diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 4e115c9..4021da7 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -1,217 +1,217 @@ name: "nightly" on: schedule: - cron: '0 0 * * *' env: HONEYCOMB_WRITEKEY: 7f3c63a70eecc61d635917de46bea4e6 HONEYCOMB_DATASET: litmus tests jobs: setup_matrix: name: "Setup Test Matrix" runs-on: ubuntu-20.04 outputs: matrix: ${{ steps.get-matrix.outputs.matrix }} steps: - name: "Honeycomb: Start recording" - uses: kvrhdn/gha-buildevents@v1.0.2 + uses: kvrhdn/gha-buildevents@5be4636b81803713c94d7cb7e3a4b85d759df112 # pin@v1.0.2 with: apikey: ${{ env.HONEYCOMB_WRITEKEY }} dataset: ${{ env.HONEYCOMB_DATASET }} job-status: ${{ job.status }} - name: "Honeycomb: Start first step" run: | echo STEP_ID=0 >> $GITHUB_ENV echo STEP_START=$(date +%s) >> $GITHUB_ENV - name: Checkout Source uses: actions/checkout@v2 if: ${{ github.repository_owner == 'puppetlabs' }} - name: Activate Ruby 2.7 uses: actions/setup-ruby@v1 if: ${{ github.repository_owner == 'puppetlabs' }} with: ruby-version: "2.7" - name: Cache gems uses: actions/cache@v2 if: ${{ github.repository_owner == 'puppetlabs' }} with: path: vendor/gems key: ${{ runner.os }}-${{ github.event_name }}-${{ hashFiles('**/Gemfile') }} restore-keys: | ${{ runner.os }}-${{ github.event_name }}- ${{ runner.os }}- - name: Install gems if: ${{ github.repository_owner == 'puppetlabs' }} run: | buildevents cmd $TRACE_ID $STEP_ID 'bundle config path vendor/gems' -- bundle config path vendor/gems buildevents cmd $TRACE_ID $STEP_ID 'bundle config jobs 8' -- bundle config jobs 8 buildevents cmd $TRACE_ID $STEP_ID 'bundle config retry 3' -- bundle config retry 3 buildevents cmd $TRACE_ID $STEP_ID 'bundle install' -- bundle install buildevents cmd $TRACE_ID $STEP_ID 'bundle clean' -- bundle clean - name: Setup Acceptance Test Matrix id: get-matrix if: ${{ github.repository_owner == 'puppetlabs' }} run: | if [ '${{ github.repository_owner }}' == 'puppetlabs' ]; then buildevents cmd $TRACE_ID $STEP_ID matrix_from_metadata -- bundle exec matrix_from_metadata else echo "::set-output name=matrix::{}" fi - name: "Honeycomb: Record setup time" if: ${{ always() }} run: | buildevents step $TRACE_ID $STEP_ID $STEP_START 'Setup Test Matrix' Acceptance: needs: - setup_matrix runs-on: ubuntu-20.04 strategy: fail-fast: false matrix: ${{fromJson(needs.setup_matrix.outputs.matrix)}} env: BUILDEVENT_FILE: '../buildevents.txt' steps: - run: | echo 'platform=${{ matrix.platform }}' >> $BUILDEVENT_FILE echo 'collection=${{ matrix.collection }}' >> $BUILDEVENT_FILE - name: "Honeycomb: Start recording" - uses: kvrhdn/gha-buildevents@v1.0.2 + uses: kvrhdn/gha-buildevents@5be4636b81803713c94d7cb7e3a4b85d759df112 # pin@v1.0.2 with: apikey: ${{ env.HONEYCOMB_WRITEKEY }} dataset: ${{ env.HONEYCOMB_DATASET }} job-status: ${{ job.status }} matrix-key: ${{ matrix.platform }}-${{ matrix.collection }} - name: "Honeycomb: start first step" run: | echo STEP_ID=${{ matrix.platform }}-${{ matrix.collection }}-1 >> $GITHUB_ENV echo STEP_START=$(date +%s) >> $GITHUB_ENV - name: Checkout Source uses: actions/checkout@v2 - name: Activate Ruby 2.7 uses: actions/setup-ruby@v1 with: ruby-version: "2.7" - name: Cache gems uses: actions/cache@v2 with: path: vendor/gems key: ${{ runner.os }}-${{ github.event_name }}-${{ hashFiles('**/Gemfile') }} restore-keys: | ${{ runner.os }}-${{ github.event_name }}- ${{ runner.os }}- - name: "Honeycomb: Record cache setup time" if: ${{ always() }} run: | buildevents step $TRACE_ID $STEP_ID $STEP_START 'Cache retrieval' echo STEP_ID=${{ matrix.platform }}-${{ matrix.collection }}-2 >> $GITHUB_ENV echo STEP_START=$(date +%s) >> $GITHUB_ENV - name: Bundler Setup run: | buildevents cmd $TRACE_ID $STEP_ID 'bundle config path vendor/gems' -- bundle config path vendor/gems buildevents cmd $TRACE_ID $STEP_ID 'bundle config jobs 8' -- bundle config jobs 8 buildevents cmd $TRACE_ID $STEP_ID 'bundle config retry 3' -- bundle config retry 3 buildevents cmd $TRACE_ID $STEP_ID 'bundle install' -- bundle install buildevents cmd $TRACE_ID $STEP_ID 'bundle clean' -- bundle clean echo ::group::bundler environment buildevents cmd $TRACE_ID $STEP_ID 'bundle env' -- bundle env echo ::endgroup:: - name: "Honeycomb: Record Bundler Setup time" if: ${{ always() }} run: | buildevents step $TRACE_ID $STEP_ID $STEP_START 'Bundler Setup' echo STEP_ID=${{ matrix.platform }}-${{ matrix.collection }}-3 >> $GITHUB_ENV echo STEP_START=$(date +%s) >> $GITHUB_ENV - name: Provision test environment run: | buildevents cmd $TRACE_ID $STEP_ID 'rake litmus:provision ${{ matrix.platform }}' -- bundle exec rake 'litmus:provision[provision::provision_service,${{ matrix.platform }}]' echo ::group::=== REQUEST === cat request.json || true echo echo ::endgroup:: echo ::group::=== INVENTORY === sed -e 's/password: .*/password: "[redacted]"/' < inventory.yaml || true echo ::endgroup:: - name: Install agent run: | buildevents cmd $TRACE_ID $STEP_ID 'rake litmus:install_agent ${{ matrix.collection }}' -- bundle exec rake 'litmus:install_agent[${{ matrix.collection }}]' - name: Install module run: | buildevents cmd $TRACE_ID $STEP_ID 'rake litmus:install_module' -- bundle exec rake 'litmus:install_module' - name: "Honeycomb: Record deployment times" if: ${{ always() }} run: | echo ::group::honeycomb step buildevents step $TRACE_ID $STEP_ID $STEP_START 'Deploy test system' echo STEP_ID=${{ matrix.platform }}-${{ matrix.collection }}-4 >> $GITHUB_ENV echo STEP_START=$(date +%s) >> $GITHUB_ENV echo ::endgroup:: - name: Run acceptance tests run: | buildevents cmd $TRACE_ID $STEP_ID 'rake litmus:acceptance:parallel' -- bundle exec rake 'litmus:acceptance:parallel' - name: "Honeycomb: Record acceptance testing times" if: ${{ always() }} run: | buildevents step $TRACE_ID $STEP_ID $STEP_START 'Run acceptance tests' echo STEP_ID=${{ matrix.platform }}-${{ matrix.collection }}-5 >> $GITHUB_ENV echo STEP_START=$(date +%s) >> $GITHUB_ENV - name: Remove test environment if: ${{ always() }} run: | if [ -f inventory.yaml ]; then buildevents cmd $TRACE_ID $STEP_ID 'rake litmus:tear_down' -- bundle exec rake 'litmus:tear_down' echo ::group::=== REQUEST === cat request.json || true echo echo ::endgroup:: fi - name: "Honeycomb: Record removal times" if: ${{ always() }} run: | buildevents step $TRACE_ID $STEP_ID $STEP_START 'Remove test environment' slack-workflow-status: if: always() name: Post Workflow Status To Slack needs: - Acceptance runs-on: ubuntu-20.04 steps: - name: Slack Workflow Notification - uses: Gamesight/slack-workflow-status@master + uses: Gamesight/slack-workflow-status@88ee95b73b4669825883ddf22747966204663e58 # pin@master with: # Required Input repo_token: ${{ secrets.GITHUB_TOKEN }} slack_webhook_url: ${{ secrets.SLACK_WEBHOOK }} # Optional Input channel: '#team-ia-bots' name: 'GABot' diff --git a/.github/workflows/pr_test.yml b/.github/workflows/pr_test.yml index b8f49c7..2b5ab1f 100644 --- a/.github/workflows/pr_test.yml +++ b/.github/workflows/pr_test.yml @@ -1,198 +1,198 @@ name: "PR Testing" on: [pull_request] env: HONEYCOMB_WRITEKEY: 7f3c63a70eecc61d635917de46bea4e6 HONEYCOMB_DATASET: litmus tests jobs: setup_matrix: name: "Setup Test Matrix" runs-on: ubuntu-20.04 outputs: matrix: ${{ steps.get-matrix.outputs.matrix }} steps: - name: "Honeycomb: Start recording" - uses: kvrhdn/gha-buildevents@v1.0.2 + uses: kvrhdn/gha-buildevents@5be4636b81803713c94d7cb7e3a4b85d759df112 # pin@v1.0.2 with: apikey: ${{ env.HONEYCOMB_WRITEKEY }} dataset: ${{ env.HONEYCOMB_DATASET }} job-status: ${{ job.status }} - name: "Honeycomb: Start first step" run: | echo STEP_ID=0 >> $GITHUB_ENV echo STEP_START=$(date +%s) >> $GITHUB_ENV - name: Checkout Source uses: actions/checkout@v2 if: ${{ github.repository_owner == 'puppetlabs' }} - name: Activate Ruby 2.7 uses: actions/setup-ruby@v1 if: ${{ github.repository_owner == 'puppetlabs' }} with: ruby-version: "2.7" - name: Cache gems uses: actions/cache@v2 if: ${{ github.repository_owner == 'puppetlabs' }} with: path: vendor/gems key: ${{ runner.os }}-${{ github.event_name }}-${{ hashFiles('**/Gemfile') }} restore-keys: | ${{ runner.os }}-${{ github.event_name }}- ${{ runner.os }}- - name: Install gems if: ${{ github.repository_owner == 'puppetlabs' }} run: | buildevents cmd $TRACE_ID $STEP_ID 'bundle config path vendor/gems' -- bundle config path vendor/gems buildevents cmd $TRACE_ID $STEP_ID 'bundle config jobs 8' -- bundle config jobs 8 buildevents cmd $TRACE_ID $STEP_ID 'bundle config retry 3' -- bundle config retry 3 buildevents cmd $TRACE_ID $STEP_ID 'bundle install' -- bundle install buildevents cmd $TRACE_ID $STEP_ID 'bundle clean' -- bundle clean - name: Setup Acceptance Test Matrix id: get-matrix if: ${{ github.repository_owner == 'puppetlabs' }} run: | if [ '${{ github.repository_owner }}' == 'puppetlabs' ]; then buildevents cmd $TRACE_ID $STEP_ID matrix_from_metadata -- bundle exec matrix_from_metadata else echo "::set-output name=matrix::{}" fi - name: "Honeycomb: Record setup time" if: ${{ always() }} run: | buildevents step $TRACE_ID $STEP_ID $STEP_START 'Setup Test Matrix' Acceptance: needs: - setup_matrix runs-on: ubuntu-20.04 strategy: fail-fast: false matrix: ${{fromJson(needs.setup_matrix.outputs.matrix)}} env: BUILDEVENT_FILE: '../buildevents.txt' steps: - run: | echo 'platform=${{ matrix.platform }}' >> $BUILDEVENT_FILE echo 'collection=${{ matrix.collection }}' >> $BUILDEVENT_FILE - name: "Honeycomb: Start recording" - uses: kvrhdn/gha-buildevents@v1.0.2 + uses: kvrhdn/gha-buildevents@5be4636b81803713c94d7cb7e3a4b85d759df112 # pin@v1.0.2 with: apikey: ${{ env.HONEYCOMB_WRITEKEY }} dataset: ${{ env.HONEYCOMB_DATASET }} job-status: ${{ job.status }} matrix-key: ${{ matrix.platform }}-${{ matrix.collection }} - name: "Honeycomb: start first step" run: | echo STEP_ID=${{ matrix.platform }}-${{ matrix.collection }}-1 >> $GITHUB_ENV echo STEP_START=$(date +%s) >> $GITHUB_ENV - name: Checkout Source uses: actions/checkout@v2 - name: Activate Ruby 2.7 uses: actions/setup-ruby@v1 with: ruby-version: "2.7" - name: Cache gems uses: actions/cache@v2 with: path: vendor/gems key: ${{ runner.os }}-${{ github.event_name }}-${{ hashFiles('**/Gemfile') }} restore-keys: | ${{ runner.os }}-${{ github.event_name }}- ${{ runner.os }}- - name: "Honeycomb: Record cache setup time" if: ${{ always() }} run: | buildevents step $TRACE_ID $STEP_ID $STEP_START 'Cache retrieval' echo STEP_ID=${{ matrix.platform }}-${{ matrix.collection }}-2 >> $GITHUB_ENV echo STEP_START=$(date +%s) >> $GITHUB_ENV - name: Bundler Setup run: | buildevents cmd $TRACE_ID $STEP_ID 'bundle config path vendor/gems' -- bundle config path vendor/gems buildevents cmd $TRACE_ID $STEP_ID 'bundle config jobs 8' -- bundle config jobs 8 buildevents cmd $TRACE_ID $STEP_ID 'bundle config retry 3' -- bundle config retry 3 buildevents cmd $TRACE_ID $STEP_ID 'bundle install' -- bundle install buildevents cmd $TRACE_ID $STEP_ID 'bundle clean' -- bundle clean echo ::group::bundler environment buildevents cmd $TRACE_ID $STEP_ID 'bundle env' -- bundle env echo ::endgroup:: - name: "Honeycomb: Record Bundler Setup time" if: ${{ always() }} run: | buildevents step $TRACE_ID $STEP_ID $STEP_START 'Bundler Setup' echo STEP_ID=${{ matrix.platform }}-${{ matrix.collection }}-3 >> $GITHUB_ENV echo STEP_START=$(date +%s) >> $GITHUB_ENV - name: Provision test environment run: | buildevents cmd $TRACE_ID $STEP_ID 'rake litmus:provision ${{ matrix.platform }}' -- bundle exec rake 'litmus:provision[provision::provision_service,${{ matrix.platform }}]' echo ::group::=== REQUEST === cat request.json || true echo echo ::endgroup:: echo ::group::=== INVENTORY === sed -e 's/password: .*/password: "[redacted]"/' < inventory.yaml || true echo ::endgroup:: - name: Install agent run: | buildevents cmd $TRACE_ID $STEP_ID 'rake litmus:install_agent ${{ matrix.collection }}' -- bundle exec rake 'litmus:install_agent[${{ matrix.collection }}]' - name: Install module run: | buildevents cmd $TRACE_ID $STEP_ID 'rake litmus:install_module' -- bundle exec rake 'litmus:install_module' - name: "Honeycomb: Record deployment times" if: ${{ always() }} run: | echo ::group::honeycomb step buildevents step $TRACE_ID $STEP_ID $STEP_START 'Deploy test system' echo STEP_ID=${{ matrix.platform }}-${{ matrix.collection }}-4 >> $GITHUB_ENV echo STEP_START=$(date +%s) >> $GITHUB_ENV echo ::endgroup:: - name: Run acceptance tests run: | buildevents cmd $TRACE_ID $STEP_ID 'rake litmus:acceptance:parallel' -- bundle exec rake 'litmus:acceptance:parallel' - name: "Honeycomb: Record acceptance testing times" if: ${{ always() }} run: | buildevents step $TRACE_ID $STEP_ID $STEP_START 'Run acceptance tests' echo STEP_ID=${{ matrix.platform }}-${{ matrix.collection }}-5 >> $GITHUB_ENV echo STEP_START=$(date +%s) >> $GITHUB_ENV - name: Remove test environment if: ${{ always() }} run: | if [ -f inventory.yaml ]; then buildevents cmd $TRACE_ID $STEP_ID 'rake litmus:tear_down' -- bundle exec rake 'litmus:tear_down' echo ::group::=== REQUEST === cat request.json || true echo echo ::endgroup:: fi - name: "Honeycomb: Record removal times" if: ${{ always() }} run: | buildevents step $TRACE_ID $STEP_ID $STEP_START 'Remove test environment' diff --git a/.rubocop.yml b/.rubocop.yml index b3a8dee..07b68b8 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,138 +1,540 @@ --- require: +- rubocop-performance - rubocop-rspec -- rubocop-i18n AllCops: DisplayCopNames: true TargetRubyVersion: '2.4' Include: - - "./**/*.rb" + - "**/*.rb" Exclude: - bin/* - ".vendor/**/*" - "**/Gemfile" - "**/Rakefile" - pkg/**/* - spec/fixtures/**/* - vendor/**/* - "**/Puppetfile" - "**/Vagrantfile" - "**/Guardfile" -Metrics/LineLength: +Layout/LineLength: Description: People have wide screens, use them. Max: 200 -GetText: - Enabled: false -GetText/DecorateString: - Description: We don't want to decorate test output. - Exclude: - - spec/**/* - Enabled: false RSpec/BeforeAfterAll: Description: Beware of using after(:all) as it may cause state to leak between tests. A necessary evil in acceptance testing. Exclude: - spec/acceptance/**/*.rb RSpec/HookArgument: Description: Prefer explicit :each argument, matching existing module's style EnforcedStyle: each Style/BlockDelimiters: Description: Prefer braces for chaining. Mostly an aesthetical choice. Better to be consistent then. EnforcedStyle: braces_for_chaining -Style/BracesAroundHashParameters: - Description: Braces are required by Ruby 2.7. Cop removed from RuboCop v0.80.0. - See https://github.com/rubocop-hq/rubocop/pull/7643 - Enabled: false Style/ClassAndModuleChildren: Description: Compact style reduces the required amount of indentation. EnforcedStyle: compact Style/EmptyElse: Description: Enforce against empty else clauses, but allow `nil` for clarity. EnforcedStyle: empty Style/FormatString: Description: Following the main puppet project's style, prefer the % format format. EnforcedStyle: percent Style/FormatStringToken: Description: Following the main puppet project's style, prefer the simpler template tokens over annotated ones. EnforcedStyle: template Style/Lambda: Description: Prefer the keyword for easier discoverability. EnforcedStyle: literal Style/RegexpLiteral: Description: Community preference. See https://github.com/voxpupuli/modulesync_config/issues/168 EnforcedStyle: percent_r Style/TernaryParentheses: Description: Checks for use of parentheses around ternary conditions. Enforce parentheses on complex expressions for better readability, but seriously consider breaking it up. EnforcedStyle: require_parentheses_when_complex Style/TrailingCommaInArguments: Description: Prefer always trailing comma on multiline argument lists. This makes diffs, and re-ordering nicer. EnforcedStyleForMultiline: comma -Style/TrailingCommaInLiteral: +Style/TrailingCommaInArrayLiteral: Description: Prefer always trailing comma on multiline literals. This makes diffs, and re-ordering nicer. EnforcedStyleForMultiline: comma Style/SymbolArray: Description: Using percent style obscures symbolic intent of array's contents. EnforcedStyle: brackets -inherit_from: ".rubocop_todo.yml" RSpec/MessageSpies: EnforcedStyle: receive Style/Documentation: Exclude: - lib/puppet/parser/functions/**/* - spec/**/* Style/WordArray: EnforcedStyle: brackets +Performance/AncestorsInclude: + Enabled: true +Performance/BigDecimalWithNumericArgument: + Enabled: true +Performance/BlockGivenWithExplicitBlock: + Enabled: true +Performance/Caller: + Enabled: true +Performance/CaseWhenSplat: + Enabled: true +Performance/Casecmp: + Enabled: true +Performance/CollectionLiteralInLoop: + Exclude: + - spec/**/* + Enabled: true +Performance/CompareWithBlock: + Enabled: true +Performance/ConstantRegexp: + Enabled: true +Performance/Count: + Enabled: true +Performance/Detect: + Enabled: true +Performance/DoubleStartEndWith: + Enabled: true +Performance/EndWith: + Enabled: true +Performance/FixedSize: + Enabled: true +Performance/FlatMap: + Enabled: true +Performance/MethodObjectAsBlock: + Enabled: true +Performance/RangeInclude: + Enabled: true +Performance/RedundantBlockCall: + Enabled: true +Performance/RedundantMatch: + Enabled: true +Performance/RedundantMerge: + Enabled: true +Performance/RedundantSortBlock: + Enabled: true +Performance/RedundantStringChars: + Enabled: true +Performance/RegexpMatch: + Enabled: true +Performance/ReverseEach: + Enabled: true +Performance/ReverseFirst: + Enabled: true +Performance/Size: + Enabled: true +Performance/SortReverse: + Enabled: true +Performance/Squeeze: + Enabled: true +Performance/StartWith: + Enabled: true +Performance/StringInclude: + Enabled: true +Performance/StringReplacement: + Enabled: true +Performance/Sum: + Enabled: true +Performance/TimesMap: + Enabled: true Style/CollectionMethods: Enabled: true Style/MethodCalledOnDoEndBlock: Enabled: true Style/StringMethods: Enabled: true -GetText/DecorateFunctionMessage: +Bundler/InsecureProtocolSource: + Enabled: false +Gemspec/DuplicatedAssignment: Enabled: false -GetText/DecorateStringFormattingUsingInterpolation: +Gemspec/OrderedDependencies: Enabled: false -GetText/DecorateStringFormattingUsingPercent: +Gemspec/RequiredRubyVersion: + Enabled: false +Gemspec/RubyVersionGlobalsUsage: + Enabled: false +Layout/ArgumentAlignment: + Enabled: false +Layout/BeginEndAlignment: + Enabled: false +Layout/ClosingHeredocIndentation: + Enabled: false +Layout/EmptyComment: + Enabled: false +Layout/EmptyLineAfterGuardClause: + Enabled: false +Layout/EmptyLinesAroundArguments: + Enabled: false +Layout/EmptyLinesAroundAttributeAccessor: Enabled: false Layout/EndOfLine: Enabled: false -Layout/IndentHeredoc: +Layout/FirstArgumentIndentation: + Enabled: false +Layout/HashAlignment: + Enabled: false +Layout/HeredocIndentation: + Enabled: false +Layout/LeadingEmptyLines: + Enabled: false +Layout/SpaceAroundMethodCallOperator: + Enabled: false +Layout/SpaceInsideArrayLiteralBrackets: + Enabled: false +Layout/SpaceInsideReferenceBrackets: + Enabled: false +Lint/BigDecimalNew: + Enabled: false +Lint/BooleanSymbol: + Enabled: false +Lint/ConstantDefinitionInBlock: + Enabled: false +Lint/DeprecatedOpenSSLConstant: + Enabled: false +Lint/DisjunctiveAssignmentInConstructor: + Enabled: false +Lint/DuplicateBranch: + Enabled: false +Lint/DuplicateElsifCondition: + Enabled: false +Lint/DuplicateRegexpCharacterClassElement: + Enabled: false +Lint/DuplicateRequire: + Enabled: false +Lint/DuplicateRescueException: + Enabled: false +Lint/EmptyBlock: + Enabled: false +Lint/EmptyClass: + Enabled: false +Lint/EmptyConditionalBody: + Enabled: false +Lint/EmptyFile: + Enabled: false +Lint/ErbNewArguments: + Enabled: false +Lint/FloatComparison: + Enabled: false +Lint/HashCompareByIdentity: + Enabled: false +Lint/IdentityComparison: + Enabled: false +Lint/InterpolationCheck: + Enabled: false +Lint/MissingCopEnableDirective: + Enabled: false +Lint/MixedRegexpCaptureTypes: + Enabled: false +Lint/NestedPercentLiteral: + Enabled: false +Lint/NoReturnInBeginEndBlocks: + Enabled: false +Lint/NonDeterministicRequireOrder: + Enabled: false +Lint/OrderedMagicComments: + Enabled: false +Lint/OutOfRangeRegexpRef: + Enabled: false +Lint/RaiseException: + Enabled: false +Lint/RedundantCopEnableDirective: + Enabled: false +Lint/RedundantRequireStatement: + Enabled: false +Lint/RedundantSafeNavigation: + Enabled: false +Lint/RedundantWithIndex: + Enabled: false +Lint/RedundantWithObject: + Enabled: false +Lint/RegexpAsCondition: + Enabled: false +Lint/ReturnInVoidContext: + Enabled: false +Lint/SafeNavigationConsistency: + Enabled: false +Lint/SafeNavigationWithEmpty: + Enabled: false +Lint/SelfAssignment: + Enabled: false +Lint/SendWithMixinArgument: + Enabled: false +Lint/ShadowedArgument: + Enabled: false +Lint/StructNewOverride: + Enabled: false +Lint/ToEnumArguments: + Enabled: false +Lint/ToJSON: + Enabled: false +Lint/TopLevelReturnWithArgument: + Enabled: false +Lint/TrailingCommaInAttributeDeclaration: + Enabled: false +Lint/UnexpectedBlockArity: + Enabled: false +Lint/UnmodifiedReduceAccumulator: + Enabled: false +Lint/UnreachableLoop: + Enabled: false +Lint/UriEscapeUnescape: + Enabled: false +Lint/UriRegexp: + Enabled: false +Lint/UselessMethodDefinition: + Enabled: false +Lint/UselessTimes: Enabled: false Metrics/AbcSize: Enabled: false Metrics/BlockLength: Enabled: false +Metrics/BlockNesting: + Enabled: false Metrics/ClassLength: Enabled: false Metrics/CyclomaticComplexity: Enabled: false Metrics/MethodLength: Enabled: false Metrics/ModuleLength: Enabled: false Metrics/ParameterLists: Enabled: false Metrics/PerceivedComplexity: Enabled: false +Migration/DepartmentName: + Enabled: false +Naming/AccessorMethodName: + Enabled: false +Naming/BlockParameterName: + Enabled: false +Naming/HeredocDelimiterCase: + Enabled: false +Naming/HeredocDelimiterNaming: + Enabled: false +Naming/MemoizedInstanceVariableName: + Enabled: false +Naming/MethodParameterName: + Enabled: false +Naming/RescuedExceptionsVariableName: + Enabled: false +RSpec/Be: + Enabled: false +RSpec/Capybara/CurrentPathExpectation: + Enabled: false +RSpec/Capybara/FeatureMethods: + Enabled: false +RSpec/Capybara/VisibilityMatcher: + Enabled: false +RSpec/ContextMethod: + Enabled: false +RSpec/ContextWording: + Enabled: false RSpec/DescribeClass: Enabled: false +RSpec/EmptyHook: + Enabled: false +RSpec/EmptyLineAfterExample: + Enabled: false +RSpec/EmptyLineAfterExampleGroup: + Enabled: false +RSpec/EmptyLineAfterHook: + Enabled: false RSpec/ExampleLength: Enabled: false -RSpec/MessageExpectation: +RSpec/ExampleWithoutDescription: + Enabled: false +RSpec/ExpectChange: + Enabled: false +RSpec/ExpectInHook: + Enabled: false +RSpec/FactoryBot/AttributeDefinedStatically: + Enabled: false +RSpec/FactoryBot/CreateList: + Enabled: false +RSpec/FactoryBot/FactoryClassName: + Enabled: false +RSpec/HooksBeforeExamples: + Enabled: false +RSpec/ImplicitBlockExpectation: + Enabled: false +RSpec/ImplicitSubject: + Enabled: false +RSpec/LeakyConstantDeclaration: + Enabled: false +RSpec/LetBeforeExamples: + Enabled: false +RSpec/MissingExampleGroupArgument: Enabled: false RSpec/MultipleExpectations: Enabled: false +RSpec/MultipleMemoizedHelpers: + Enabled: false +RSpec/MultipleSubjects: + Enabled: false RSpec/NestedGroups: Enabled: false +RSpec/PredicateMatcher: + Enabled: false +RSpec/ReceiveCounts: + Enabled: false +RSpec/ReceiveNever: + Enabled: false +RSpec/RepeatedExampleGroupBody: + Enabled: false +RSpec/RepeatedExampleGroupDescription: + Enabled: false +RSpec/RepeatedIncludeExample: + Enabled: false +RSpec/ReturnFromStub: + Enabled: false +RSpec/SharedExamples: + Enabled: false +RSpec/StubbedMock: + Enabled: false +RSpec/UnspecifiedException: + Enabled: false +RSpec/VariableDefinition: + Enabled: false +RSpec/VoidExpect: + Enabled: false +RSpec/Yield: + Enabled: false +Security/Open: + Enabled: false +Style/AccessModifierDeclarations: + Enabled: false +Style/AccessorGrouping: + Enabled: false +Style/ArgumentsForwarding: + Enabled: false Style/AsciiComments: Enabled: false +Style/BisectedAttrAccessor: + Enabled: false +Style/CaseLikeIf: + Enabled: false +Style/ClassEqualityComparison: + Enabled: false +Style/CollectionCompact: + Enabled: false +Style/ColonMethodDefinition: + Enabled: false +Style/CombinableLoops: + Enabled: false +Style/CommentedKeyword: + Enabled: false +Style/Dir: + Enabled: false +Style/DocumentDynamicEvalDefinition: + Enabled: false +Style/DoubleCopDisableDirective: + Enabled: false +Style/EmptyBlockParameter: + Enabled: false +Style/EmptyLambdaParameter: + Enabled: false +Style/Encoding: + Enabled: false +Style/EvalWithLocation: + Enabled: false +Style/ExpandPathArguments: + Enabled: false +Style/ExplicitBlockArgument: + Enabled: false +Style/ExponentialNotation: + Enabled: false +Style/FloatDivision: + Enabled: false +Style/GlobalStdStream: + Enabled: false +Style/HashAsLastArrayItem: + Enabled: false +Style/HashLikeCase: + Enabled: false +Style/HashTransformKeys: + Enabled: false +Style/HashTransformValues: + Enabled: false Style/IfUnlessModifier: Enabled: false +Style/KeywordParametersOrder: + Enabled: false +Style/MinMax: + Enabled: false +Style/MixinUsage: + Enabled: false +Style/MultilineWhenThen: + Enabled: false +Style/NegatedIfElseCondition: + Enabled: false +Style/NegatedUnless: + Enabled: false +Style/NilLambda: + Enabled: false +Style/NumericPredicate: + Enabled: false +Style/OptionalBooleanParameter: + Enabled: false +Style/OrAssignment: + Enabled: false +Style/RandomWithOffset: + Enabled: false +Style/RedundantArgument: + Enabled: false +Style/RedundantAssignment: + Enabled: false +Style/RedundantCondition: + Enabled: false +Style/RedundantConditional: + Enabled: false +Style/RedundantFetchBlock: + Enabled: false +Style/RedundantFileExtensionInRequire: + Enabled: false +Style/RedundantRegexpCharacterClass: + Enabled: false +Style/RedundantRegexpEscape: + Enabled: false +Style/RedundantSelfAssignment: + Enabled: false +Style/RedundantSort: + Enabled: false +Style/RescueStandardError: + Enabled: false +Style/SingleArgumentDig: + Enabled: false +Style/SlicingWithRange: + Enabled: false +Style/SoleNestedConditional: + Enabled: false +Style/StderrPuts: + Enabled: false +Style/StringConcatenation: + Enabled: false +Style/Strip: + Enabled: false +Style/SwapValues: + Enabled: false Style/SymbolProc: Enabled: false +Style/TrailingBodyOnClass: + Enabled: false +Style/TrailingBodyOnMethodDefinition: + Enabled: false +Style/TrailingBodyOnModule: + Enabled: false +Style/TrailingCommaInHashLiteral: + Enabled: false +Style/TrailingMethodEndStatement: + Enabled: false +Style/UnpackFirst: + Enabled: false diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml deleted file mode 100644 index 53400b4..0000000 --- a/.rubocop_todo.yml +++ /dev/null @@ -1,2 +0,0 @@ -Style/FrozenStringLiteralComment: - Enabled: false \ No newline at end of file diff --git a/.sync.yml b/.sync.yml index 32c53c4..d5f4618 100644 --- a/.sync.yml +++ b/.sync.yml @@ -1,79 +1,64 @@ --- ".gitlab-ci.yml": delete: true -".rubocop.yml": - default_configs: - inherit_from: ".rubocop_todo.yml" - require: - - rubocop-i18n - - rubocop-rspec ".travis.yml": - global_env: + global_env: - HONEYCOMB_WRITEKEY="7f3c63a70eecc61d635917de46bea4e6",HONEYCOMB_DATASET="litmus tests" dist: trusty deploy_to_forge: enabled: false user: puppet secure: '' branches: - release use_litmus: true litmus: provision_list: - ---travis_el - travis_deb - travis_el7 - travis_el8 complex: - collection: puppet_collection: - puppet6 provision_list: - travis_ub_6 - collection: puppet_collection: - puppet5 provision_list: - travis_ub_5 simplecov: true notifications: slack: secure: f7XbE9eVRGVVLoq1BYsib9T+elBhFoAs/Vojg1OaxFAixZSMAbHq+6egsAZuToSXwwo2XIYeBYbnyomvoKDrMw1UDQ93vc85AwZBvk4pKsPpF+jfgX+az56pu7LeZmEcDk+eWvvH2PPhOJmJpYcWR/gRKQciGSGHBiMgD8UmenU= appveyor.yml: environment: HONEYCOMB_WRITEKEY: 7f3c63a70eecc61d635917de46bea4e6 HONEYCOMB_DATASET: litmus tests use_litmus: true matrix_extras: - RUBY_VERSION: 25-x64 ACCEPTANCE: 'yes' TARGET_HOST: localhost - RUBY_VERSION: 25-x64 ACCEPTANCE: 'yes' TARGET_HOST: localhost APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 simplecov: true Gemfile: optional: ":development": - gem: github_changelog_generator - git: https://github.com/skywinder/github-changelog-generator - ref: 20ee04ba1234e9e83eb2ffb5056e23d641c7a018 - condition: Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new('2.2.2') - required: - ":development": - - gem: puppet-lint-i18n -Rakefile: - requires: - - puppet_pot_generator/rake_tasks spec/spec_helper.rb: mock_with: ":rspec" coverage_report: true .gitpod.Dockerfile: unmanaged: false .gitpod.yml: unmanaged: false .github/workflows/nightly.yml: unmanaged: false .github/workflows/pr_test.yml: unmanaged: false diff --git a/.travis.yml b/.travis.yml index d6ed1d9..75d3b7c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,146 +1,138 @@ --- os: linux dist: trusty language: ruby cache: bundler before_install: - bundle -v - rm -f Gemfile.lock - "# Update system gems if requested. This is useful to temporarily workaround troubles in the test runner" - "# See https://github.com/puppetlabs/pdk-templates/commit/705154d5c437796b821691b707156e1b056d244f for an example of how this was used" - "# Ignore exit code of SIGPIPE'd yes to not fail with shell's pipefail set" - '[ -z "$RUBYGEMS_VERSION" ] || (yes || true) | gem update --system $RUBYGEMS_VERSION' - gem --version - bundle -v script: - 'SIMPLECOV=yes bundle exec rake $CHECK' bundler_args: --without system_tests rvm: - 2.5.7 env: global: - HONEYCOMB_WRITEKEY="7f3c63a70eecc61d635917de46bea4e6",HONEYCOMB_DATASET="litmus tests" stages: - static - spec - acceptance jobs: fast_finish: true include: - - + - bundler_args: --with system_tests before_script: - "bundle exec rake 'litmus:provision_list[travis_ub_6]'" - "bundle exec rake 'litmus:install_agent[puppet6]'" - "bundle exec rake litmus:install_module" - bundler_args: env: PLATFORMS=travis_ub_6_puppet6 rvm: 2.5.7 script: ["travis_wait 45 bundle exec rake litmus:acceptance:parallel"] services: docker stage: acceptance - - + - bundler_args: --with system_tests before_script: - "bundle exec rake 'litmus:provision_list[travis_ub_5]'" - "bundle exec rake 'litmus:install_agent[puppet5]'" - "bundle exec rake litmus:install_module" - bundler_args: env: PLATFORMS=travis_ub_5_puppet5 rvm: 2.5.7 script: ["travis_wait 45 bundle exec rake litmus:acceptance:parallel"] services: docker stage: acceptance - - + - bundler_args: --with system_tests before_script: - "bundle exec rake 'litmus:provision_list[travis_deb]'" - "bundle exec rake 'litmus:install_agent[puppet5]'" - "bundle exec rake litmus:install_module" - bundler_args: env: PLATFORMS=travis_deb_puppet5 rvm: 2.5.7 script: ["travis_wait 45 bundle exec rake litmus:acceptance:parallel"] services: docker stage: acceptance - - + - bundler_args: --with system_tests before_script: - "bundle exec rake 'litmus:provision_list[travis_el7]'" - "bundle exec rake 'litmus:install_agent[puppet5]'" - "bundle exec rake litmus:install_module" - bundler_args: env: PLATFORMS=travis_el7_puppet5 rvm: 2.5.7 script: ["travis_wait 45 bundle exec rake litmus:acceptance:parallel"] services: docker stage: acceptance - - + - bundler_args: --with system_tests before_script: - "bundle exec rake 'litmus:provision_list[travis_el8]'" - "bundle exec rake 'litmus:install_agent[puppet5]'" - "bundle exec rake litmus:install_module" - bundler_args: env: PLATFORMS=travis_el8_puppet5 rvm: 2.5.7 script: ["travis_wait 45 bundle exec rake litmus:acceptance:parallel"] services: docker stage: acceptance - - + - bundler_args: --with system_tests before_script: - "bundle exec rake 'litmus:provision_list[travis_deb]'" - "bundle exec rake 'litmus:install_agent[puppet6]'" - "bundle exec rake litmus:install_module" - bundler_args: env: PLATFORMS=travis_deb_puppet6 rvm: 2.5.7 script: ["travis_wait 45 bundle exec rake litmus:acceptance:parallel"] services: docker stage: acceptance - - + - bundler_args: --with system_tests before_script: - "bundle exec rake 'litmus:provision_list[travis_el7]'" - "bundle exec rake 'litmus:install_agent[puppet6]'" - "bundle exec rake litmus:install_module" - bundler_args: env: PLATFORMS=travis_el7_puppet6 rvm: 2.5.7 script: ["travis_wait 45 bundle exec rake litmus:acceptance:parallel"] services: docker stage: acceptance - - + - bundler_args: --with system_tests before_script: - "bundle exec rake 'litmus:provision_list[travis_el8]'" - "bundle exec rake 'litmus:install_agent[puppet6]'" - "bundle exec rake litmus:install_module" - bundler_args: env: PLATFORMS=travis_el8_puppet6 rvm: 2.5.7 script: ["travis_wait 45 bundle exec rake litmus:acceptance:parallel"] services: docker stage: acceptance - env: CHECK="check:symlinks check:git_ignore check:dot_underscore check:test_file rubocop syntax lint metadata_lint" stage: static - env: PUPPET_GEM_VERSION="~> 5.0" CHECK=parallel_spec rvm: 2.4.5 stage: spec - env: PUPPET_GEM_VERSION="~> 6.0" CHECK=parallel_spec rvm: 2.5.7 stage: spec branches: only: - main - /^v\d/ - release notifications: email: false slack: secure: f7XbE9eVRGVVLoq1BYsib9T+elBhFoAs/Vojg1OaxFAixZSMAbHq+6egsAZuToSXwwo2XIYeBYbnyomvoKDrMw1UDQ93vc85AwZBvk4pKsPpF+jfgX+az56pu7LeZmEcDk+eWvvH2PPhOJmJpYcWR/gRKQciGSGHBiMgD8UmenU= deploy: provider: puppetforge username: puppet password: secure: "" on: tags: true all_branches: true condition: "$DEPLOY_TO_FORGE = yes" diff --git a/Gemfile b/Gemfile index 815331e..ae2b430 100644 --- a/Gemfile +++ b/Gemfile @@ -1,75 +1,73 @@ source ENV['GEM_SOURCE'] || 'https://rubygems.org' def location_for(place_or_version, fake_version = nil) git_url_regex = %r{\A(?(https?|git)[:@][^#]*)(#(?.*))?} file_url_regex = %r{\Afile:\/\/(?.*)} if place_or_version && (git_url = place_or_version.match(git_url_regex)) [fake_version, { git: git_url[:url], branch: git_url[:branch], require: false }].compact elsif place_or_version && (file_url = place_or_version.match(file_url_regex)) ['>= 0', { path: File.expand_path(file_url[:path]), require: false }] else [place_or_version, { require: false }] end end ruby_version_segments = Gem::Version.new(RUBY_VERSION.dup).segments minor_version = ruby_version_segments[0..1].join('.') group :development do - gem "fast_gettext", '1.1.0', require: false if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new('2.1.0') - gem "fast_gettext", require: false if Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new('2.1.0') - gem "json_pure", '<= 2.0.1', require: false if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new('2.0.0') - gem "json", '= 1.8.1', require: false if Gem::Version.new(RUBY_VERSION.dup) == Gem::Version.new('2.1.9') gem "json", '= 2.0.4', require: false if Gem::Requirement.create('~> 2.4.2').satisfied_by?(Gem::Version.new(RUBY_VERSION.dup)) gem "json", '= 2.1.0', require: false if Gem::Requirement.create(['>= 2.5.0', '< 2.7.0']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup)) gem "json", '= 2.3.0', require: false if Gem::Requirement.create(['>= 2.7.0', '< 2.8.0']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup)) - gem "rb-readline", '= 0.5.5', require: false, platforms: [:mswin, :mingw, :x64_mingw] - gem "puppet-module-posix-default-r#{minor_version}", '~> 0.4', require: false, platforms: [:ruby] - gem "puppet-module-posix-dev-r#{minor_version}", '~> 0.4', require: false, platforms: [:ruby] - gem "puppet-module-win-default-r#{minor_version}", '~> 0.4', require: false, platforms: [:mswin, :mingw, :x64_mingw] - gem "puppet-module-win-dev-r#{minor_version}", '~> 0.4', require: false, platforms: [:mswin, :mingw, :x64_mingw] - gem "puppet-lint-i18n", require: false - gem "github_changelog_generator", require: false, git: 'https://github.com/skywinder/github-changelog-generator', ref: '20ee04ba1234e9e83eb2ffb5056e23d641c7a018' if Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new('2.2.2') + gem "puppet-module-posix-default-r#{minor_version}", '~> 1.0', require: false, platforms: [:ruby] + gem "puppet-module-posix-dev-r#{minor_version}", '~> 1.0', require: false, platforms: [:ruby] + gem "puppet-module-win-default-r#{minor_version}", '~> 1.0', require: false, platforms: [:mswin, :mingw, :x64_mingw] + gem "puppet-module-win-dev-r#{minor_version}", '~> 1.0', require: false, platforms: [:mswin, :mingw, :x64_mingw] + gem "github_changelog_generator", require: false +end +group :system_tests do + gem "puppet-module-posix-system-r#{minor_version}", '~> 1.0', require: false, platforms: [:ruby] + gem "puppet-module-win-system-r#{minor_version}", '~> 1.0', require: false, platforms: [:mswin, :mingw, :x64_mingw] end puppet_version = ENV['PUPPET_GEM_VERSION'] facter_version = ENV['FACTER_GEM_VERSION'] hiera_version = ENV['HIERA_GEM_VERSION'] gems = {} gems['puppet'] = location_for(puppet_version) # If facter or hiera versions have been specified via the environment # variables gems['facter'] = location_for(facter_version) if facter_version gems['hiera'] = location_for(hiera_version) if hiera_version if Gem.win_platform? && puppet_version =~ %r{^(file:///|git://)} # If we're using a Puppet gem on Windows which handles its own win32-xxx gem # dependencies (>= 3.5.0), set the maximum versions (see PUP-6445). gems['win32-dir'] = ['<= 0.4.9', require: false] gems['win32-eventlog'] = ['<= 0.6.5', require: false] gems['win32-process'] = ['<= 0.7.5', require: false] gems['win32-security'] = ['<= 0.2.5', require: false] gems['win32-service'] = ['0.8.8', require: false] end gems.each do |gem_name, gem_params| gem gem_name, *gem_params end # Evaluate Gemfile.local and ~/.gemfile if they exist extra_gemfiles = [ "#{__FILE__}.local", File.join(Dir.home, '.gemfile'), ] extra_gemfiles.each do |gemfile| if File.file?(gemfile) && File.readable?(gemfile) eval(File.read(gemfile), binding) end end # vim: syntax=ruby diff --git a/Rakefile b/Rakefile index f8ec754..0a5093b 100644 --- a/Rakefile +++ b/Rakefile @@ -1,88 +1,87 @@ # frozen_string_literal: true require 'puppet_litmus/rake_tasks' if Bundler.rubygems.find_name('puppet_litmus').any? require 'puppetlabs_spec_helper/rake_tasks' require 'puppet-syntax/tasks/puppet-syntax' require 'puppet_blacksmith/rake_tasks' if Bundler.rubygems.find_name('puppet-blacksmith').any? require 'github_changelog_generator/task' if Bundler.rubygems.find_name('github_changelog_generator').any? require 'puppet-strings/tasks' if Bundler.rubygems.find_name('puppet-strings').any? -require 'puppet_pot_generator/rake_tasks' def changelog_user return unless Rake.application.top_level_tasks.include? "changelog" returnVal = nil || JSON.load(File.read('metadata.json'))['author'] raise "unable to find the changelog_user in .sync.yml, or the author in metadata.json" if returnVal.nil? puts "GitHubChangelogGenerator user:#{returnVal}" returnVal end def changelog_project return unless Rake.application.top_level_tasks.include? "changelog" returnVal = nil returnVal ||= begin metadata_source = JSON.load(File.read('metadata.json'))['source'] metadata_source_match = metadata_source && metadata_source.match(%r{.*\/([^\/]*?)(?:\.git)?\Z}) metadata_source_match && metadata_source_match[1] end raise "unable to find the changelog_project in .sync.yml or calculate it from the source in metadata.json" if returnVal.nil? puts "GitHubChangelogGenerator project:#{returnVal}" returnVal end def changelog_future_release return unless Rake.application.top_level_tasks.include? "changelog" returnVal = "v%s" % JSON.load(File.read('metadata.json'))['version'] raise "unable to find the future_release (version) in metadata.json" if returnVal.nil? puts "GitHubChangelogGenerator future_release:#{returnVal}" returnVal end PuppetLint.configuration.send('disable_relative') if Bundler.rubygems.find_name('github_changelog_generator').any? GitHubChangelogGenerator::RakeTask.new :changelog do |config| raise "Set CHANGELOG_GITHUB_TOKEN environment variable eg 'export CHANGELOG_GITHUB_TOKEN=valid_token_here'" if Rake.application.top_level_tasks.include? "changelog" and ENV['CHANGELOG_GITHUB_TOKEN'].nil? config.user = "#{changelog_user}" config.project = "#{changelog_project}" config.future_release = "#{changelog_future_release}" config.exclude_labels = ['maintenance'] config.header = "# Change log\n\nAll notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org)." config.add_pr_wo_labels = true config.issues = false config.merge_prefix = "### UNCATEGORIZED PRS; LABEL THEM ON GITHUB" config.configure_sections = { "Changed" => { "prefix" => "### Changed", "labels" => ["backwards-incompatible"], }, "Added" => { "prefix" => "### Added", "labels" => ["enhancement", "feature"], }, "Fixed" => { "prefix" => "### Fixed", "labels" => ["bug", "documentation", "bugfix"], }, } end else desc 'Generate a Changelog from GitHub' task :changelog do raise < 1.15' condition: "Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new('2.3.0')" EOM end end diff --git a/appveyor.yml b/appveyor.yml index eb3dcfb..d69b461 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,84 +1,84 @@ --- version: 1.1.x.{build} branches: only: - main - release skip_commits: message: /^\(?doc\)?.*/ clone_depth: 10 init: - SET - 'mkdir C:\ProgramData\PuppetLabs\code && exit 0' - 'mkdir C:\ProgramData\PuppetLabs\facter && exit 0' - 'mkdir C:\ProgramData\PuppetLabs\hiera && exit 0' - 'mkdir C:\ProgramData\PuppetLabs\puppet\var && exit 0' environment: HONEYCOMB_WRITEKEY: 7f3c63a70eecc61d635917de46bea4e6 HONEYCOMB_DATASET: litmus tests SIMPLECOV: yes matrix: - - RUBY_VERSION: 24-x64 + RUBY_VERSION: 25-x64 CHECK: syntax lint metadata_lint check:symlinks check:git_ignore check:dot_underscore check:test_file rubocop - PUPPET_GEM_VERSION: ~> 5.0 RUBY_VERSION: 24 CHECK: parallel_spec - PUPPET_GEM_VERSION: ~> 5.0 RUBY_VERSION: 24-x64 CHECK: parallel_spec - PUPPET_GEM_VERSION: ~> 6.0 RUBY_VERSION: 25 CHECK: parallel_spec - PUPPET_GEM_VERSION: ~> 6.0 RUBY_VERSION: 25-x64 CHECK: parallel_spec - RUBY_VERSION: 25-x64 ACCEPTANCE: yes TARGET_HOST: localhost - RUBY_VERSION: 25-x64 ACCEPTANCE: yes TARGET_HOST: localhost APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 for: - matrix: only: - ACCEPTANCE: yes install: - set PATH=C:\Ruby%RUBY_VERSION%\bin;%PATH% - bundle install --jobs 4 --retry 2 - type Gemfile.lock test_script: - bundle exec puppet -V - ruby -v - gem -v - bundle -v - bundle exec rake spec_prep - bundle exec rake litmus:acceptance:localhost matrix: fast_finish: true install: - set PATH=C:\Ruby%RUBY_VERSION%\bin;%PATH% - bundle install --jobs 4 --retry 2 --without system_tests - type Gemfile.lock build: off test_script: - bundle exec puppet -V - ruby -v - gem -v - bundle -v - bundle exec rake %CHECK% notifications: - provider: Email to: - nobody@nowhere.com on_build_success: false on_build_failure: false on_build_status_changed: false diff --git a/lib/puppet/functions/create_ini_settings.rb b/lib/puppet/functions/create_ini_settings.rb index 8cd5ab3..ba6b156 100644 --- a/lib/puppet/functions/create_ini_settings.rb +++ b/lib/puppet/functions/create_ini_settings.rb @@ -1,10 +1,12 @@ +# frozen_string_literal: true + # @summary DEPRECATED. Use the namespaced function [`inifile::create_ini_settings`](#inifilecreate_ini_settings) instead. Puppet::Functions.create_function(:create_ini_settings) do dispatch :deprecation_gen do repeated_param 'Any', :args end def deprecation_gen(*args) call_function('deprecation', 'create_ini_settings', 'This method is deprecated, please use inifile::create_ini_settings instead.') call_function('inifile::create_ini_settings', *args) end end diff --git a/lib/puppet/functions/inifile/create_ini_settings.rb b/lib/puppet/functions/inifile/create_ini_settings.rb index 7a045fc..072e3a7 100644 --- a/lib/puppet/functions/inifile/create_ini_settings.rb +++ b/lib/puppet/functions/inifile/create_ini_settings.rb @@ -1,37 +1,39 @@ +# frozen_string_literal: true + # @summary This function is used to create a set of ini_setting resources from a hash Puppet::Functions.create_function(:'inifile::create_ini_settings') do # @param settings # A hash of settings you want to create ini_setting resources from # @param defaults # A hash of defaults you would like to use in the ini_setting resources dispatch :default_impl do param 'Hash', :settings optional_param 'Hash', :defaults end def default_impl(settings, defaults = {}) resources = settings.keys.each_with_object({}) do |section, res| unless settings[section].is_a?(Hash) raise(Puppet::ParseError, _('create_ini_settings(): Section %{section} must contain a Hash') % { section: section }) end path = defaults.merge(settings)['path'] raise Puppet::ParseError, _('create_ini_settings(): must pass the path parameter to the Ini_setting resource!') unless path settings[section].each do |setting, value| res["#{path} [#{section}] #{setting}"] = { 'ensure' => 'present', 'section' => section, 'setting' => setting, }.merge(if value.is_a?(Hash) value else { 'value' => value } end) end end call_function('create_resources', 'ini_setting', resources, defaults) end end diff --git a/lib/puppet/provider/ini_setting/ruby.rb b/lib/puppet/provider/ini_setting/ruby.rb index 08c51ff..1d3206b 100644 --- a/lib/puppet/provider/ini_setting/ruby.rb +++ b/lib/puppet/provider/ini_setting/ruby.rb @@ -1,150 +1,152 @@ +# frozen_string_literal: true + require File.expand_path('../../../util/ini_file', __FILE__) Puppet::Type.type(:ini_setting).provide(:ruby) do def self.instances desc ' Creates new ini_setting file, a specific config file with a provider that uses this as its parent and implements the method self.file_path, and that will provide the value for the path to the ini file.' raise(Puppet::Error, 'Ini_settings only support collecting instances when a file path is hard coded') unless respond_to?(:file_path) # figure out what to do about the seperator ini_file = Puppet::Util::IniFile.new(file_path, '=') resources = [] ini_file.section_names.each do |section_name| ini_file.get_settings(section_name).each do |setting, value| resources.push( new( name: namevar(section_name, setting), value: value, ensure: :present, ), ) end end resources end def self.namevar(section_name, setting) setting.nil? ? section_name : "#{section_name}/#{setting}" end def exists? setting.nil? && ini_file.section_names.include?(section) || !ini_file.get_value(section, setting).nil? if ini_file.section?(section) !ini_file.get_value(section, setting).nil? - elsif resource.parameters.keys.include?(:force_new_section_creation) && !resource[:force_new_section_creation] + elsif resource.parameters.key?(:force_new_section_creation) && !resource[:force_new_section_creation] # for backwards compatibility, if a user is using their own ini_setting # types but does not have this parameter, we need to fall back to the # previous functionality which was to create the section. Anyone # wishing to leverage this setting must define it in their provider # type. See comments on # https://github.com/puppetlabs/puppetlabs-inifile/pull/286 resource[:ensure] = :absent resource[:force_new_section_creation] - elsif resource.parameters.keys.include?(:force_new_section_creation) && resource[:force_new_section_creation] + elsif resource.parameters.key?(:force_new_section_creation) && resource[:force_new_section_creation] !resource[:force_new_section_creation] else false end end def create if setting.nil? && resource[:value].nil? ini_file.set_value(section) else ini_file.set_value(section, setting, separator, resource[:value]) end ini_file.save @ini_file = nil end def destroy ini_file.remove_setting(section, setting) ini_file.save @ini_file = nil end def value ini_file.get_value(section, setting) end def value=(_value) if setting.nil? && resource[:value].nil? ini_file.set_value(section) else ini_file.set_value(section, setting, separator, resource[:value]) end ini_file.save end def section # this method is here so that it can be overridden by a child provider resource[:section] end def setting # this method is here so that it can be overridden by a child provider resource[:setting] end def file_path # this method is here to support purging and sub-classing. # if a user creates a type and subclasses our provider and provides a # 'file_path' method, then they don't have to specify the # path as a parameter for every ini_setting declaration. # This implementation allows us to support that while still # falling back to the parameter value when necessary. if self.class.respond_to?(:file_path) self.class.file_path else resource[:path] end end def separator if resource.class.validattr?(:key_val_separator) resource[:key_val_separator] || '=' else '=' end end def section_prefix if resource.class.validattr?(:section_prefix) resource[:section_prefix] || '[' else '[' end end def section_suffix if resource.class.validattr?(:section_suffix) resource[:section_suffix] || ']' else ']' end end def indent_char if resource.class.validattr?(:indent_char) resource[:indent_char] || ' ' else ' ' end end def indent_width if resource.class.validattr?(:indent_width) resource[:indent_width] || nil else nil end end private def ini_file @ini_file ||= Puppet::Util::IniFile.new(file_path, separator, section_prefix, section_suffix, indent_char, indent_width) end end diff --git a/lib/puppet/provider/ini_subsetting/ruby.rb b/lib/puppet/provider/ini_subsetting/ruby.rb index 3c32a29..0f6b772 100644 --- a/lib/puppet/provider/ini_subsetting/ruby.rb +++ b/lib/puppet/provider/ini_subsetting/ruby.rb @@ -1,94 +1,96 @@ +# frozen_string_literal: true + require File.expand_path('../../../util/ini_file', __FILE__) require File.expand_path('../../../util/setting_value', __FILE__) Puppet::Type.type(:ini_subsetting).provide(:ruby) do desc ' Creates new ini_subsetting file, a specific config file with a provider that uses this as its parent and implements the method self.file_path, and that will provide the value for the path to the ini file.' def exists? setting_value.get_subsetting_value(subsetting, resource[:use_exact_match]) end def create setting_value.add_subsetting( subsetting, resource[:value], resource[:use_exact_match], resource[:insert_type], resource[:insert_value] ) ini_file.set_value(section, setting, key_val_separator, setting_value.get_value) ini_file.save @ini_file = nil @setting_value = nil end def destroy setting_value.remove_subsetting(subsetting, resource[:use_exact_match]) if setting_value.get_value.empty? && resource[:delete_if_empty] ini_file.remove_setting(section, setting) else ini_file.set_value(section, setting, key_val_separator, setting_value.get_value) end ini_file.save @ini_file = nil @setting_value = nil end def value setting_value.get_subsetting_value(subsetting) end def value=(value) setting_value.add_subsetting( subsetting, value, resource[:use_exact_match], resource[:insert_type], resource[:insert_value] ) ini_file.set_value(section, setting, key_val_separator, setting_value.get_value) ini_file.save end def section resource[:section] end def setting resource[:setting] end def subsetting resource[:subsetting] end def subsetting_separator resource[:subsetting_separator] end def file_path resource[:path] end def key_val_separator resource[:key_val_separator] || '=' end def subsetting_key_val_separator resource[:subsetting_key_val_separator] || '' end def quote_char resource[:quote_char] end private def ini_file @ini_file ||= Puppet::Util::IniFile.new(file_path, key_val_separator) end def setting_value @setting_value ||= Puppet::Util::SettingValue.new( ini_file.get_value(section, setting), subsetting_separator, quote_char, subsetting_key_val_separator ) end end diff --git a/lib/puppet/type/ini_setting.rb b/lib/puppet/type/ini_setting.rb index 8eb8280..c27406b 100644 --- a/lib/puppet/type/ini_setting.rb +++ b/lib/puppet/type/ini_setting.rb @@ -1,155 +1,157 @@ +# frozen_string_literal: true + require 'digest/md5' require 'puppet/parameter/boolean' Puppet::Type.newtype(:ini_setting) do desc 'ini_settings is used to manage a single setting in an INI file' ensurable do desc 'Ensurable method handles modeling creation. It creates an ensure property' newvalue(:present) do provider.create end newvalue(:absent) do provider.destroy end def insync?(current) if @resource[:refreshonly] true else current == should end end defaultto :present end def munge_boolean_md5(value) case value when true, :true, 'true', :yes, 'yes' :true when false, :false, 'false', :no, 'no' :false when :md5, 'md5' :md5 else raise(_('expected a boolean value or :md5')) end end newparam(:name, namevar: true) do desc 'An arbitrary name used as the identity of the resource.' end newparam(:section) do desc 'The name of the section in the ini file in which the setting should be defined.' defaultto('') end newparam(:setting) do desc 'The name of the setting to be defined.' munge do |value| if value.match?(%r{(^\s|\s$)}) Puppet.warn('Settings should not have spaces in the value, we are going to strip the whitespace') end value.strip end end newparam(:force_new_section_creation, boolean: true, parent: Puppet::Parameter::Boolean) do desc 'Create setting only if the section exists' defaultto(true) end newparam(:path) do desc 'The ini file Puppet will ensure contains the specified setting.' validate do |value| unless Puppet::Util.absolute_path?(value) raise(Puppet::Error, _("File paths must be fully qualified, not '%{value}'") % { value: value }) end end end newparam(:show_diff) do desc 'Whether to display differences when the setting changes.' defaultto :true newvalues(:true, :md5, :false) munge do |value| @resource.munge_boolean_md5(value) end end newparam(:key_val_separator) do desc 'The separator string to use between each setting name and value.' defaultto(' = ') end newproperty(:value) do desc 'The value of the setting to be defined.' munge do |value| if ([true, false].include? value) || value.is_a?(Numeric) value.to_s else value.strip.to_s end end def should_to_s(newvalue) if @resource[:show_diff] == :true && Puppet[:show_diff] newvalue elsif @resource[:show_diff] == :md5 && Puppet[:show_diff] '{md5}' + Digest::MD5.hexdigest(newvalue.to_s) else '[redacted sensitive information]' end end - def is_to_s(value) # rubocop:disable Style/PredicateName : Changing breaks the code (./.bundle/gems/gems/puppet-5.3.3-universal-darwin/lib/puppet/parameter.rb:525:in `to_s') + def is_to_s(value) # rubocop:disable Naming/PredicateName : Changing breaks the code (./.bundle/gems/gems/puppet-5.3.3-universal-darwin/lib/puppet/parameter.rb:525:in `to_s') should_to_s(value) end def insync?(current) if @resource[:refreshonly] true else current == should end end end newparam(:section_prefix) do desc 'The prefix to the section name\'s header.' defaultto('[') end newparam(:section_suffix) do desc 'The suffix to the section name\'s header.' defaultto(']') end newparam(:indent_char) do desc 'The character to indent new settings with.' defaultto(' ') end newparam(:indent_width) do desc 'The number of indent_chars to use to indent a new setting.' end newparam(:refreshonly, boolean: true, parent: Puppet::Parameter::Boolean) do desc 'A flag indicating whether or not the ini_setting should be updated only when called as part of a refresh event' defaultto false end def refresh if self[:ensure] == :absent && self[:refreshonly] return provider.destroy end # update the value in the provider, which will save the value to the ini file provider.value = self[:value] if self[:refreshonly] end autorequire(:file) do Pathname.new(self[:path]).parent.to_s end end diff --git a/lib/puppet/type/ini_subsetting.rb b/lib/puppet/type/ini_subsetting.rb index 596607b..403e047 100644 --- a/lib/puppet/type/ini_subsetting.rb +++ b/lib/puppet/type/ini_subsetting.rb @@ -1,132 +1,134 @@ +# frozen_string_literal: true + require 'digest/md5' Puppet::Type.newtype(:ini_subsetting) do desc 'ini_subsettings is used to manage multiple values in a setting in an INI file' ensurable do desc 'Ensurable method handles modeling creation. It creates an ensure property' defaultvalues defaultto :present end def munge_boolean_md5(value) case value when true, :true, 'true', :yes, 'yes' :true when false, :false, 'false', :no, 'no' :false when :md5, 'md5' :md5 else raise(_('expected a boolean value or :md5')) end end newparam(:name, namevar: true) do desc 'An arbitrary name used as the identity of the resource.' end newparam(:section) do desc 'The name of the section in the ini file in which the setting should be defined.' defaultto('') end newparam(:setting) do desc 'The name of the setting to be defined.' end newparam(:subsetting) do desc 'The name of the subsetting to be defined.' end newparam(:subsetting_separator) do desc 'The separator string between subsettings. Defaults to the empty string.' defaultto(' ') end newparam(:subsetting_key_val_separator) do desc 'The separator string between the subsetting name and its value. Defaults to the empty string.' defaultto('') end newparam(:path) do desc 'The ini file Puppet will ensure contains the specified setting.' validate do |value| unless Puppet::Util.absolute_path?(value) raise(Puppet::Error, _("File paths must be fully qualified, not '%{value}'") % { value: value }) end end end newparam(:show_diff) do desc 'Whether to display differences when the setting changes.' defaultto :true newvalues(:true, :md5, :false) munge do |value| @resource.munge_boolean_md5(value) end end newparam(:key_val_separator) do desc 'The separator string to use between each setting name and value.' defaultto(' = ') end newparam(:quote_char) do desc "The character used to quote the entire value of the setting. Valid values are '', '\"' and \"'\"" defaultto('') validate do |value| unless value.match?(%r{^["']?$}) raise Puppet::Error, _(%q(:quote_char valid values are '', '"' and "'")) end end end newparam(:use_exact_match) do desc 'Set to true if your subsettings don\'t have values and you want to use exact matches to determine if the subsetting exists.' newvalues(:true, :false) defaultto(:false) end newproperty(:value) do desc 'The value of the subsetting to be defined.' def should_to_s(newvalue) if @resource[:show_diff] == :true && Puppet[:show_diff] newvalue elsif @resource[:show_diff] == :md5 && Puppet[:show_diff] '{md5}' + Digest::MD5.hexdigest(newvalue.to_s) else '[redacted sensitive information]' end end - def is_to_s(value) # rubocop:disable Style/PredicateName : Changing breaks the code (./.bundle/gems/gems/puppet-5.3.3-universal-darwin/lib/puppet/parameter.rb:525:in `to_s') + def is_to_s(value) # rubocop:disable Naming/PredicateName : Changing breaks the code (./.bundle/gems/gems/puppet-5.3.3-universal-darwin/lib/puppet/parameter.rb:525:in `to_s') should_to_s(value) end end newparam(:insert_type) do desc <<-eof Where the new subsetting item should be inserted * :start - insert at the beginning of the line. * :end - insert at the end of the line (default). * :before - insert before the specified element if possible. * :after - insert after the specified element if possible. * :index - insert at the specified index number. eof newvalues(:start, :end, :before, :after, :index) defaultto(:end) end newparam(:insert_value) do desc 'The value for the insert types which require one.' end newparam(:delete_if_empty) do desc 'Set to true to delete the parent setting when the subsetting is empty instead of writing an empty string' newvalues(:true, :false) defaultto(:false) end end diff --git a/lib/puppet/util/external_iterator.rb b/lib/puppet/util/external_iterator.rb index 812f70b..0a6b235 100644 --- a/lib/puppet/util/external_iterator.rb +++ b/lib/puppet/util/external_iterator.rb @@ -1,30 +1,32 @@ +# frozen_string_literal: true + module Puppet::Util # # external_iterator.rb # class ExternalIterator def initialize(coll) @coll = coll @cur_index = -1 end def next @cur_index += 1 item_at(@cur_index) end def peek item_at(@cur_index + 1) end private def item_at(index) if @coll.length > index [@coll[index], index] else [nil, nil] end end end end diff --git a/lib/puppet/util/ini_file.rb b/lib/puppet/util/ini_file.rb index 7a5321b..15c2dbe 100644 --- a/lib/puppet/util/ini_file.rb +++ b/lib/puppet/util/ini_file.rb @@ -1,347 +1,349 @@ +# frozen_string_literal: true + require File.expand_path('../external_iterator', __FILE__) require File.expand_path('../ini_file/section', __FILE__) module Puppet::Util # # ini_file.rb # class IniFile def initialize(path, key_val_separator = ' = ', section_prefix = '[', section_suffix = ']', indent_char = ' ', indent_width = nil) k_v_s = (key_val_separator =~ %r{^\s+$}) ? ' ' : key_val_separator.strip @section_prefix = section_prefix @section_suffix = section_suffix @indent_char = indent_char @indent_width = indent_width ? indent_width.to_i : nil @section_regex = section_regex @setting_regex = %r{^(\s*)([^#;\s]|[^#;\s].*?[^\s#{k_v_s}])(\s*#{k_v_s}[ \t]*)(.*)\s*$} @commented_setting_regex = %r{^(\s*)[#;]+(\s*)(.*?[^\s#{k_v_s}])(\s*#{k_v_s}[ \t]*)(.*)\s*$} @path = path @key_val_separator = key_val_separator @section_names = [] @sections_hash = {} parse_file end def section_regex # Only put in prefix/suffix if they exist # Also, if the prefix is '', the negated # set match should be a match all instead. r_string = '^\s*' r_string += Regexp.escape(@section_prefix) r_string += '(' if @section_prefix != '' r_string += '[^' r_string += Regexp.escape(@section_prefix) r_string += ']' else r_string += '.' end r_string += '*)' r_string += Regexp.escape(@section_suffix) r_string += '\s*$' %r{#{r_string}} end attr_reader :section_names def get_settings(section_name) section = @sections_hash[section_name] section.setting_names.each_with_object({}) do |setting, result| result[setting] = section.get_value(setting) end end def section?(section_name) @sections_hash.key?(section_name) end def get_value(section_name, setting) @sections_hash[section_name].get_value(setting) if @sections_hash.key?(section_name) end - def set_value(*args) # rubocop:disable Style/AccessorMethodName : Recomended alternative is a common value name + def set_value(*args) case args.size when 1 section_name = args[0] when 3 # Backwards compatible set_value function, See MODULES-5172 (section_name, setting, value) = args when 4 (section_name, setting, separator, value) = args end complete_setting = { setting: setting, separator: separator, value: value, } unless @sections_hash.key?(section_name) add_section(Section.new(section_name, nil, nil, nil, nil)) end section = @sections_hash[section_name] if section.existing_setting?(setting) update_line(section, setting, value) section.update_existing_setting(setting, value) elsif find_commented_setting(section, setting) # So, this stanza is a bit of a hack. What we're trying # to do here is this: for settings that don't already # exist, we want to take a quick peek to see if there # is a commented-out version of them in the section. # If so, we'd prefer to add the setting directly after # the commented line, rather than at the end of the section. # If we get here then we found a commented line, so we # call "insert_inline_setting_line" to update the lines array insert_inline_setting_line(find_commented_setting(section, setting), section, complete_setting) # Then, we need to tell the setting object that we hacked # in an inline setting section.insert_inline_setting(setting, value) # Finally, we need to update all of the start/end line # numbers for all of the sections *after* the one that # was modified. section_index = @section_names.index(section_name) increment_section_line_numbers(section_index + 1) elsif !setting.nil? || !value.nil? section.set_additional_setting(setting, value) end end def remove_setting(section_name, setting) section = @sections_hash[section_name] return unless section.existing_setting?(setting) # If the setting is found, we have some work to do. # First, we remove the line from our array of lines: remove_line(section, setting) # Then, we need to tell the setting object to remove # the setting from its state: section.remove_existing_setting(setting) # Finally, we need to update all of the start/end line # numbers for all of the sections *after* the one that # was modified. section_index = @section_names.index(section_name) decrement_section_line_numbers(section_index + 1) return unless section.empty? # By convention, it's time to remove this newly emptied out section lines.delete_at(section.start_line) decrement_section_line_numbers(section_index + 1) @section_names.delete_at(section_index) @sections_hash.delete(section.name) end def save global_empty = @sections_hash[''].empty? && @sections_hash[''].additional_settings.empty? File.open(@path, 'w') do |fh| @section_names.each_index do |index| name = @section_names[index] section = @sections_hash[name] # We need a buffer to cache lines that are only whitespace whitespace_buffer = [] if section.new_section? && !section.global? if index == 1 && !global_empty || index > 1 fh.puts('') end fh.puts("#{@section_prefix}#{section.name}#{@section_suffix}") end unless section.new_section? # write all of the pre-existing lines (section.start_line..section.end_line).each do |line_num| line = lines[line_num] # We buffer any lines that are only whitespace so that # if they are at the end of a section, we can insert # any new settings *before* the final chunk of whitespace # lines. if line.match?(%r{^\s*$}) whitespace_buffer << line else # If we get here, we've found a non-whitespace line. # We'll flush any cached whitespace lines before we # write it. flush_buffer_to_file(whitespace_buffer, fh) fh.puts(line) end end end # write new settings, if there are any section.additional_settings.each_pair do |key, value| fh.puts("#{@indent_char * (@indent_width || section.indentation || 0)}#{key}#{@key_val_separator}#{value}") end if !whitespace_buffer.empty? flush_buffer_to_file(whitespace_buffer, fh) elsif section.new_section? && !section.additional_settings.empty? && (index < @section_names.length - 1) # We get here if there were no blank lines at the end of the # section. # # If we are adding a new section with a new setting, # and if there are more sections that come after this one, # we'll write one blank line just so that there is a little # whitespace between the sections. # if (section.end_line.nil? && fh.puts('') end end end end private def add_section(section) @sections_hash[section.name] = section @section_names << section.name end def parse_file line_iter = create_line_iter # We always create a "global" section at the beginning of the file, for # anything that appears before the first named section. section = read_section('', 0, line_iter) add_section(section) line, line_num = line_iter.next while line if (match = @section_regex.match(line)) section = read_section(match[1], line_num, line_iter) add_section(section) end line, line_num = line_iter.next end end def read_section(name, start_line, line_iter) settings = {} end_line_num = start_line min_indentation = nil empty = true loop do line, line_num = line_iter.peek if line_num.nil? || @section_regex.match(line) # the global section always exists, even when it's empty; # when it's empty, we must be sure it's thought of as new, # which is signalled with a nil ending line end_line_num = nil if name == '' && empty return Section.new(name, start_line, end_line_num, settings, min_indentation) end if (match = @setting_regex.match(line)) settings[match[2]] = match[4] indentation = match[1].length min_indentation = [indentation, min_indentation || indentation].min end end_line_num = line_num empty = false line_iter.next end end def update_line(section, setting, value) (section.start_line..section.end_line).each do |line_num| next unless (match = @setting_regex.match(lines[line_num])) if match[2] == setting lines[line_num] = "#{match[1]}#{match[2]}#{match[3]}#{value}" end end end def remove_line(section, setting) (section.start_line..section.end_line).each do |line_num| next unless (match = @setting_regex.match(lines[line_num])) if match[2] == setting lines.delete_at(line_num) end end end def create_line_iter ExternalIterator.new(lines) end def lines @lines ||= IniFile.readlines(@path) end # This is mostly here because it makes testing easier--we don't have # to try to stub any methods on File. def self.readlines(path) # rubocop:disable Lint/IneffectiveAccessModifier : Attempting to change breaks tests # If this type is ever used with very large files, we should # write this in a different way, using a temp # file; for now assuming that this type is only used on # small-ish config files that can fit into memory without # too much trouble. File.file?(path) ? File.readlines(path) : [] end # This utility method scans through the lines for a section looking for # commented-out versions of a setting. It returns `nil` if it doesn't # find one. If it does find one, then it returns a hash containing # two keys: # # :line_num - the line number that contains the commented version # of the setting # :match - the ruby regular expression match object, which can # be used to mimic the whitespace from the comment line def find_commented_setting(section, setting) return nil if section.new_section? (section.start_line..section.end_line).each do |line_num| next unless (match = @commented_setting_regex.match(lines[line_num])) if match[3] == setting return { match: match, line_num: line_num } end end nil end # This utility method is for inserting a line into the existing # lines array. The `result` argument is expected to be in the # format of the return value of `find_commented_setting`. def insert_inline_setting_line(result, section, complete_setting) line_num = result[:line_num] s = complete_setting lines.insert(line_num + 1, "#{@indent_char * (@indent_width || section.indentation || 0)}#{s[:setting]}#{s[:separator]}#{s[:value]}") end # Utility method; given a section index (index into the @section_names # array), decrement the start/end line numbers for that section and all # all of the other sections that appear *after* the specified section. def decrement_section_line_numbers(section_index) @section_names[section_index..(@section_names.length - 1)].each do |name| section = @sections_hash[name] section.decrement_line_nums end end # Utility method; given a section index (index into the @section_names # array), increment the start/end line numbers for that section and all # all of the other sections that appear *after* the specified section. def increment_section_line_numbers(section_index) @section_names[section_index..(@section_names.length - 1)].each do |name| section = @sections_hash[name] section.increment_line_nums end end def flush_buffer_to_file(buffer, fh) return if buffer.empty? buffer.each { |l| fh.puts(l) } buffer.clear end end end diff --git a/lib/puppet/util/ini_file/section.rb b/lib/puppet/util/ini_file/section.rb index 9da0e9c..75ea544 100644 --- a/lib/puppet/util/ini_file/section.rb +++ b/lib/puppet/util/ini_file/section.rb @@ -1,93 +1,95 @@ +# frozen_string_literal: true + class Puppet::Util::IniFile # # section.rb # class Section # Some implementation details: # # * `name` will be set to the empty string for the 'global' section. # * there will always be a 'global' section, with a `start_line` of 0, # but if the file actually begins with a real section header on # the first line, then the 'global' section will have an # `end_line` of `nil`. # * `start_line` and `end_line` will be set to `nil` for a new non-global # section. def initialize(name, start_line, end_line, settings, indentation) @name = name @start_line = start_line @end_line = end_line @existing_settings = settings.nil? ? {} : settings @additional_settings = {} @indentation = indentation end attr_reader :name, :start_line, :end_line, :additional_settings, :indentation def global? @name == '' end def new_section? # a new section (global or named) will always have `end_line` # set to `nil` @end_line.nil? end def setting_names @existing_settings.keys | @additional_settings.keys end def get_value(setting_name) @existing_settings[setting_name] || @additional_settings[setting_name] end def existing_setting?(setting_name) @existing_settings.key?(setting_name) end # the global section is empty whenever it's new; # other sections are empty when they have no lines def empty? global? ? new_section? : start_line == end_line end def update_existing_setting(setting_name, value) @existing_settings[setting_name] = value end def remove_existing_setting(setting_name) @end_line -= 1 if @existing_settings.delete(setting_name) && @end_line end # This is a hacky method; it's basically called when we need to insert # a new setting but we don't want it to appear at the very end of the # section. Instead we hack it into the existing settings list and # increment our end_line number--this assumes that the caller (`ini_file`) # is doing some babysitting w/rt the other sections and the actual data # of the lines. def insert_inline_setting(setting_name, value) @existing_settings[setting_name] = value @end_line += 1 if @end_line end def set_additional_setting(setting_name, value) @additional_settings[setting_name] = value end # Decrement the start and end line numbers for the section (if they are # defined); this is intended to be called when a setting is removed # from a section that comes before this section in the ini file. def decrement_line_nums @start_line -= 1 if @start_line @end_line -= 1 if @end_line end # Increment the start and end line numbers for the section (if they are # defined); this is intended to be called when an inline setting is added # to a section that comes before this section in the ini file. def increment_line_nums @start_line += 1 if @start_line @end_line += 1 if @end_line end end end diff --git a/lib/puppet/util/setting_value.rb b/lib/puppet/util/setting_value.rb index 26f5517..51e349d 100644 --- a/lib/puppet/util/setting_value.rb +++ b/lib/puppet/util/setting_value.rb @@ -1,182 +1,184 @@ +# frozen_string_literal: true + module Puppet::Util # This class can work with a list of subsettings inside # an ini file setting string to add, remove, extract and set their values. class SettingValue # The constructor method # @param setting_value [String] The initial setting value # @param subsetting_separator [String] The character is used to separate # subsettings in the setting_value string. # @param default_quote_char [String] Quote the setting string with this character. def initialize(setting_value, subsetting_separator = ' ', default_quote_char = '', key_val_separator = '') @setting_value = setting_value @subsetting_separator = subsetting_separator @quote_char = default_quote_char @key_val_separator = key_val_separator @subsetting_items = [] return unless @setting_value unquoted, quote_char = unquote_setting_value(setting_value) @quote_char = quote_char unless quote_char.empty? # an item can contain escaped separator @subsetting_items = unquoted.scan(Regexp.new("(?:(?:[^\\#{@subsetting_separator}]|\\.)+)")) @subsetting_items.map! { |item| item.strip } end # If the setting value is quoted, the quotes are # removed and the unquoted string and the quoting # character are returned. # @param setting_value [String] The input value # @return [Array] The unquoted string and the quoting character def unquote_setting_value(setting_value) quote_char = '' if setting_value.start_with?('"') && setting_value.end_with?('"') quote_char = '"' elsif setting_value.start_with?("'") && setting_value.end_with?("'") quote_char = "'" end unquoted = if quote_char != '' setting_value[1, setting_value.length - 2] else setting_value end [unquoted, quote_char] end # Get the resulting setting value by joining all the # subsettings, separator and quote characters. # @return [String] - def get_value # rubocop:disable Style/AccessorMethodName : Unable to alter without breaking code + def get_value value = @subsetting_items.join @subsetting_separator @quote_char + value + @quote_char end # Get the value of the given subsetting item. # If the exact match is used the value will be true # if the item is found. # @param subsetting [String] The name of the subsetting to add. # @param use_exact_match [:true,:false] Should the full name match be used? # @return [nil,true,String] def get_subsetting_value(subsetting, use_exact_match = :false) index = find_subsetting(subsetting, use_exact_match) # the item is not found in the list return nil unless index # the exact match is set and the item is found, the value should be true return true if use_exact_match == :true item = @subsetting_items[index] item[(subsetting.length + @key_val_separator.length)..-1] end # Add a new subsetting item to the list of existing items # if such item is not already there. # @param subsetting [String] The name of the subsetting to add. # @param subsetting_value [String] The value of the subsetting. # It will be appended to the name. # @param use_exact_match [:true,:false] Should the full name match be used? # @param [Symbol] insert_type # @param [String,Integer] insert_value # @return [Array] The resulting subsettings list. def add_subsetting(subsetting, subsetting_value, use_exact_match = :false, insert_type = :end, insert_value = nil) index = find_subsetting(subsetting, use_exact_match) # update the existing values if the subsetting is found in the list return update_subsetting(subsetting, subsetting_value, use_exact_match) if index new_item = item_value(subsetting, subsetting_value) case insert_type when :start @subsetting_items.unshift(new_item) when :end @subsetting_items.push(new_item) when :before before_index = find_subsetting(insert_value, use_exact_match) if before_index @subsetting_items.insert(before_index, new_item) else @subsetting_items.push(new_item) end when :after after_index = find_subsetting(insert_value, use_exact_match) if after_index @subsetting_items.insert(after_index + 1, new_item) else @subsetting_items.push(new_item) end when :index before_index = insert_value.to_i before_index = @subsetting_items.length if before_index > @subsetting_items.length @subsetting_items.insert(before_index, new_item) else @subsetting_items.push(new_item) end @subsetting_items end # Update all matching items in the settings list to the new values. # @param subsetting [String] The name of the subsetting to add. # @param subsetting_value [String] The value of the subsetting. # @param use_exact_match [:true,:false] Should the full name match be used? # @return [Array] The resulting subsettings list. def update_subsetting(subsetting, subsetting_value, use_exact_match = :false) new_item = item_value(subsetting, subsetting_value) @subsetting_items.map! do |item| if match_subsetting?(item, subsetting, use_exact_match) new_item else item end end end # Find the first subsetting item matching the given name, # or, if the exact match is set, equal to the given name # and return its array index value. Returns nil if not found. # @param subsetting [String] The name of the subsetting to search. # @param use_exact_match [:true,:false] Look for the full string match? # @return [Integer, nil] def find_subsetting(subsetting, use_exact_match = :false) @subsetting_items.index do |item| match_subsetting?(item, subsetting, use_exact_match) end end # Check if the subsetting item matches the given name. # If the exact match is set the entire item is matched, # and only the item name and separator string if not. # @param item [String] The item value to check against the subsetting name. # @param subsetting [String] The subsetting name. # @param use_exact_match [:true,:false] Look for the full string match? # @return [true,false] def match_subsetting?(item, subsetting, use_exact_match = :false) if use_exact_match == :true item.eql?(subsetting) else item.start_with?(subsetting + @key_val_separator) end end # Remove all the subsetting items that match # the given subsetting name. # @param subsetting [String] The subsetting name to remove. # @param use_exact_match [:true,:false] Look for the full string match? # @return [Array] The resulting subsettings list. def remove_subsetting(subsetting, use_exact_match = :false) @subsetting_items.delete_if do |item| match_subsetting?(item, subsetting, use_exact_match) end end # The actual value of the subsetting item. # It's built from the subsetting name, its value and the separator # string if present. # @param subsetting [String] The subsetting name # @param subsetting_value [String] The value of the subsetting # @return [String] def item_value(subsetting, subsetting_value) (subsetting || '') + (@key_val_separator || '') + (subsetting_value || '') end end end diff --git a/metadata.json b/metadata.json index b3ff00f..ccb48fb 100644 --- a/metadata.json +++ b/metadata.json @@ -1,114 +1,114 @@ { "name": "puppetlabs-inifile", "version": "4.4.0", "author": "puppetlabs", "summary": "Resource types for managing settings in INI files", "license": "Apache-2.0", "source": "https://github.com/puppetlabs/puppetlabs-inifile", "project_page": "https://github.com/puppetlabs/puppetlabs-inifile", "issues_url": "https://tickets.puppetlabs.com/browse/MODULES", "dependencies": [ { "name": "puppetlabs/translate", "version_requirement": ">= 1.0.0 < 3.0.0" }, { "name": "puppetlabs/stdlib", "version_requirement": ">= 4.13.0 < 7.0.0" } ], "operatingsystem_support": [ { "operatingsystem": "RedHat", "operatingsystemrelease": [ "5", "6", "7", "8" ] }, { "operatingsystem": "CentOS", "operatingsystemrelease": [ "5", "6", "7", "8" ] }, { "operatingsystem": "OracleLinux", "operatingsystemrelease": [ "5", "6", "7" ] }, { "operatingsystem": "Scientific", "operatingsystemrelease": [ "6", "7" ] }, { "operatingsystem": "SLES", "operatingsystemrelease": [ "11 SP1", "12", "15" ] }, { "operatingsystem": "Debian", "operatingsystemrelease": [ "8", "9", "10" ] }, { "operatingsystem": "Ubuntu", "operatingsystemrelease": [ "14.04", "16.04", "18.04", "20.04" ] }, { "operatingsystem": "Solaris", "operatingsystemrelease": [ "10", "11" ] }, { "operatingsystem": "Windows", "operatingsystemrelease": [ "2008 R2", "2012 R2", "2016", "2019", "10" ] }, { "operatingsystem": "AIX", "operatingsystemrelease": [ "5.3", "6.1", "7.1" ] } ], "requirements": [ { "name": "puppet", "version_requirement": ">= 5.5.10 < 8.0.0" } ], "template-url": "https://github.com/puppetlabs/pdk-templates#main", - "template-ref": "heads/main-0-g62126e1", + "template-ref": "heads/main-0-g4543421", "pdk-version": "1.18.1" } diff --git a/spec/acceptance/ini_setting_spec.rb b/spec/acceptance/ini_setting_spec.rb index 2dd5857..28e077f 100644 --- a/spec/acceptance/ini_setting_spec.rb +++ b/spec/acceptance/ini_setting_spec.rb @@ -1,356 +1,358 @@ +# frozen_string_literal: true + require 'spec_helper_acceptance' describe 'ini_setting resource' do basedir = setup_test_directory after :all do run_shell("rm #{basedir}/*.ini", expect_failures: true) end shared_examples 'has_content' do |path, pp, content| before :all do run_shell("rm #{path}", expect_failures: true) end it 'applies the manifest twice' do idempotent_apply(pp) end describe file(path) do it { is_expected.to be_file } describe '#content' do subject { super().content } it { is_expected.to match content } end end end shared_examples 'has_error' do |path, pp, error| before :all do run_shell("rm #{path}", expect_failures: true) end it 'applies the manifest and gets a failure message' do expect(apply_manifest(pp, expect_failures: true).stderr).to match(error) end describe file(path) do it { is_expected.not_to be_file } end end context 'ensure parameter => present for global and section' do pp = <<-EOS ini_setting { 'ensure => present for section': ensure => present, path => "#{basedir}/ini_setting.ini", section => 'one', setting => 'two', value => 'three', } ini_setting { 'ensure => present for global': ensure => present, path => "#{basedir}/ini_setting.ini", section => '', setting => 'four', value => 'five', } EOS it_behaves_like 'has_content', "#{basedir}/ini_setting.ini", pp, %r{four = five\R\R\[one\]\Rtwo = three} end context 'ensure parameter => absent for key/value' do before :all do ipp = <<-MANIFEST file { '#{basedir}/ini_setting.ini': content => "four = five \n [one] \n two = three", force => true, } MANIFEST apply_manifest(ipp) end pp = <<-EOS ini_setting { 'ensure => absent for key/value': ensure => absent, path => "#{basedir}/ini_setting.ini", section => 'one', setting => 'two', value => 'three', } EOS it 'applies the manifest twice' do idempotent_apply(pp) end describe file("#{basedir}/ini_setting.ini") do it { is_expected.to be_file } describe '#content' do subject { super().content } it { is_expected.to match %r{four = five} } it { is_expected.not_to match %r{\[one\]} } it { is_expected.not_to match %r{two = three} } end end end context 'ensure parameter => absent for global' do before :all do ipp = <<-MANIFEST file { '#{basedir}/ini_setting.ini': content => "four = five\n [one]\n two = three", force => true, } MANIFEST apply_manifest(ipp) end after :all do run_shell("rm #{basedir}/ini_setting.ini", expect_failures: true) end pp = <<-EOS ini_setting { 'ensure => absent for global': ensure => absent, path => "#{basedir}/ini_setting.ini", section => '', setting => 'four', value => 'five', } EOS it 'applies the manifest twice' do idempotent_apply(pp) end describe file("#{basedir}/ini_setting.ini") do it { is_expected.to be_file } describe '#content' do subject { super().content } it { is_expected.not_to match %r{four = five} } it { is_expected.to match %r{\[one\]} } it { is_expected.to match %r{two = three} } end end end describe 'path parameter' do context 'path => foo' do pp = <<-EOS ini_setting { 'path => foo': ensure => present, section => 'one', setting => 'two', value => 'three', path => 'foo', } EOS it_behaves_like 'has_error', 'foo', pp, %r{must be fully qualified} end end describe 'show_diff parameter and logging:' do setup_puppet_config_file [{ value: 'initial_value', matcher: 'created', show_diff: true }, { value: 'public_value', matcher: %r{initial_value.*public_value}, show_diff: true }, { value: 'secret_value', matcher: %r{redacted sensitive information.*redacted sensitive information}, show_diff: false }, { value: 'md5_value', matcher: %r{\{md5\}881671aa2bbc680bc530c4353125052b.*\{md5\}ed0903a7fa5de7886ca1a7a9ad06cf51}, show_diff: :md5 }].each do |i| pp = <<-EOS ini_setting { 'test_show_diff': ensure => present, section => 'test', setting => 'something', value => '#{i[:value]}', path => "#{basedir}/test_show_diff.ini", show_diff => #{i[:show_diff]} } EOS context "show_diff => #{i[:show_diff]}" do res = apply_manifest(pp, expect_changes: true) it 'applies manifest and expects changed value to be logged in proper form' do expect(res.stdout).to match(i[:matcher]) end it 'applies manifest and expects changed value to be logged in proper form #optional test' do expect(res.stdout).not_to match(i[:value]) unless i[:show_diff] == true end end end end describe 'values with spaces in the value part at the beginning or at the end' do pp = <<-EOS ini_setting { 'path => foo': ensure => present, section => 'one', setting => 'two', value => ' 123', path => '#{basedir}/ini_setting.ini', } EOS it_behaves_like 'has_content', "#{basedir}/ini_setting.ini", pp, %r{\[one\]\Rtwo = 123} end describe 'refreshonly' do before :each do ipp = <<-MANIFEST file { '#{basedir}/ini_setting.ini': content => "[section1]\n valueinsection1 = 123\", force => true, } MANIFEST apply_manifest(ipp) end after :each do run_shell("rm #{basedir}/ini_setting.ini", expect_failures: true) end context 'when event is triggered' do context 'update setting value' do let(:update_value_manifest) do <<-EOS notify { foo: notify => Ini_Setting['updateSetting'], } ini_setting { "updateSetting": ensure => present, path => "#{basedir}/ini_setting.ini", section => 'section1', setting => 'valueinsection1', value => "newValue", refreshonly => true, } EOS end before(:each) do apply_manifest(update_value_manifest, expect_changes: true) end describe file("#{basedir}/ini_setting.ini") do it { is_expected.to be_file } describe '#content' do subject { super().content } it { is_expected.to match %r{valueinsection1 = newValue} } end end end context 'remove setting value' do let(:remove_setting_manifest) do <<-EOS notify { foo: notify => Ini_Setting['removeSetting'], } ini_setting { "removeSetting": ensure => absent, path => "#{basedir}/ini_setting.ini", section => 'section1', setting => 'valueinsection1', refreshonly => true, } EOS end before(:each) do apply_manifest(remove_setting_manifest, expect_changes: true) end describe file("#{basedir}/ini_setting.ini") do it { is_expected.to be_file } describe '#content' do subject { super().content } it { is_expected.to be_empty } end end end end context 'when not receiving an event' do context 'does not update setting' do let(:does_not_update_value_manifest) do <<-EOS file { "#{basedir}/ini_setting.ini": ensure => present, notify => Ini_Setting['updateSetting'], } ini_setting { "updateSetting": ensure => present, path => "#{basedir}/ini_setting.ini", section => 'section1', setting => 'valueinsection1', value => "newValue", refreshonly => true, } EOS end before(:each) do apply_manifest(does_not_update_value_manifest, expect_changes: false) end describe file("#{basedir}/ini_setting.ini") do it { is_expected.to be_file } describe '#content' do subject { super().content } it { is_expected.to match %r{valueinsection1 = 123} } it { is_expected.to match %r{\[section1\]} } end end end context 'does not remove setting' do let(:does_not_remove_setting_manifest) do <<-EOS file { "#{basedir}/ini_setting.ini": ensure => present, notify => Ini_Setting['removeSetting'], } ini_setting { "removeSetting": ensure => absent, path => "#{basedir}/ini_setting.ini", section => 'section1', setting => 'valueinsection1', refreshonly => true, } EOS end before(:each) do apply_manifest(does_not_remove_setting_manifest, expect_changes: false) end describe file("#{basedir}/ini_setting.ini") do it { is_expected.to be_file } describe '#content' do subject { super().content } it { is_expected.not_to be_empty } it { is_expected.to match %r{valueinsection1 = 123} } it { is_expected.to match %r{\[section1\]} } end end end end end end diff --git a/spec/acceptance/ini_subsetting_spec.rb b/spec/acceptance/ini_subsetting_spec.rb index 2465d4d..aec0b7a 100644 --- a/spec/acceptance/ini_subsetting_spec.rb +++ b/spec/acceptance/ini_subsetting_spec.rb @@ -1,289 +1,290 @@ +# frozen_string_literal: true + require 'spec_helper_acceptance' describe 'ini_subsetting resource' do basedir = setup_test_directory after :all do run_shell("rm #{basedir}/*.ini", expect_failures: true) end shared_examples 'has_content' do |path, pp, content| before :all do run_shell("rm #{path}", expect_failures: true) end it 'applies the manifest twice' do idempotent_apply(pp) end describe file(path) do it { is_expected.to be_file } describe '#content' do subject { super().content } it { is_expected.to match content } end end end shared_examples 'has_error' do |path, pp, error| before :all do run_shell("rm #{path}", expect_failures: true) end it 'applies the manifest and gets a failure message' do expect(apply_manifest(pp, expect_failures: true).stderr).to match(error) end describe file(path) do it { is_expected.not_to be_file } end end describe 'ensure, section, setting, subsetting, & value parameters => present with subsections' do pp = <<-EOS ini_subsetting { 'ensure => present for alpha': ensure => present, path => "#{basedir}/ini_subsetting.ini", section => 'one', setting => 'key', subsetting => 'alpha', value => 'bet', } ini_subsetting { 'ensure => present for beta': ensure => present, path => "#{basedir}/ini_subsetting.ini", section => 'one', setting => 'key', subsetting => 'beta', value => 'trons', require => Ini_subsetting['ensure => present for alpha'], } EOS describe file("#{basedir}/ini_subsetting.ini") do it_behaves_like 'has_content', "#{basedir}/ini_subsetting.ini", pp, %r{\[one\]\Rkey = alphabet betatrons} end end describe 'ensure => absent' do pp = <<-EOS ini_subsetting { 'ensure => absent for subsetting': ensure => absent, path => "#{basedir}/ini_subsetting.ini", section => 'one', setting => 'key', subsetting => 'alpha', } EOS it 'applies the manifest twice' do idempotent_apply(pp) end describe file("#{basedir}/ini_subsetting.ini") do it { is_expected.to be_file } describe '#content' do subject { super().content } it { is_expected.to match %r{\[one\]} } it { is_expected.to match %r{key = betatrons} } it { is_expected.not_to match %r{alphabet} } end end end describe 'delete_if_empty => true' do pp = <<-EOS ini_subsetting { 'ensure => absent for alpha': ensure => absent, path => "#{basedir}/ini_subsetting.ini", section => 'one', setting => 'key', subsetting => 'alpha', delete_if_empty => true, } ini_subsetting { 'ensure => absent for beta': ensure => absent, path => "#{basedir}/ini_subsetting.ini", section => 'one', setting => 'key', subsetting => 'beta', delete_if_empty => true, } EOS it 'applies the manifest twice' do run_shell("echo -e [one]\\\\nkey = alphabet betatrons > #{basedir}/ini_subsetting.ini", expect_failures: true) idempotent_apply(pp) end describe file("#{basedir}/ini_subsetting.ini") do it { is_expected.to be_file } describe '#content' do subject { super().content } it { is_expected.not_to match %r{\[one\]} } it { is_expected.not_to match %r{key =} } it { is_expected.not_to match %r{alphabet} } it { is_expected.not_to match %r{betatrons} } end end end describe 'quote_char' do { ['-Xmx'] => %r{args=""}, ['-Xmx', '256m'] => %r{args=-Xmx256m}, ['-Xmx', '512m'] => %r{args="-Xmx512m"}, ['-Xms', '256m'] => %r{args="-Xmx256m -Xms256m"}, }.each do |parameter, content| - context %(with '#{parameter.first}' #{(parameter.length > 1) ? '=> \'' << parameter[1] << '\'' : 'absent'} makes '#{content}') do + context %(with '#{parameter.first}' #{(parameter.length > 1) ? '=> \'' + parameter[1] + '\'' : 'absent'} makes '#{content}') do path = File.join(basedir, 'ini_subsetting.ini') before :all do ipp = <<-MANIFEST file { '#{path}': content => "[java]\nargs=-Xmx256m", force => true, } MANIFEST apply_manifest(ipp) end after :all do run_shell("cat #{path}", expect_failures: true) run_shell("rm #{path}", expect_failures: true) end pp = <<-EOS ini_subsetting { '#{parameter.first}': ensure => #{(parameter.length > 1) ? 'present' : 'absent'}, path => '#{path}', section => 'java', setting => 'args', quote_char => '"', subsetting => '#{parameter.first}', value => '#{(parameter.length > 1) ? parameter[1] : ''}' } EOS it 'applies the manifest twice' do idempotent_apply(pp) end describe file("#{basedir}/ini_subsetting.ini") do it { is_expected.to be_file } describe '#content' do subject { super().content } it { is_expected.to match content } end end end end end describe 'show_diff parameter and logging:' do setup_puppet_config_file [{ value: 'initial_value', matcher: 'created', show_diff: true }, { value: 'public_value', matcher: %r{initial_value.*public_value}, show_diff: true }, { value: 'secret_value', matcher: %r{redacted sensitive information.*redacted sensitive information}, show_diff: false }, { value: 'md5_value', matcher: %r{\{md5\}881671aa2bbc680bc530c4353125052b.*\{md5\}ed0903a7fa5de7886ca1a7a9ad06cf51}, show_diff: :md5 }].each do |i| - pp = <<-EOS ini_subsetting { 'test_show_diff': ensure => present, section => 'test', setting => 'something', subsetting => 'xxx', value => '#{i[:value]}', path => "#{basedir}/test_show_diff.ini", show_diff => #{i[:show_diff]} } EOS context "show_diff => #{i[:show_diff]}" do res = apply_manifest(pp, expect_changes: true) it 'applies manifest and expects changed value to be logged in proper form' do expect(res.stdout).to match(i[:matcher]) end it 'applies manifest and expects changed value to be logged in proper form #optional test' do expect(res.stdout).not_to match(i[:value]) unless i[:show_diff] == true end end end end describe 'insert types:' do [ { insert_type: :start, content: %r{d a b c}, }, { insert_type: :end, content: %r{a b c d}, }, { insert_type: :before, insert_value: 'c', content: %r{a b d c}, }, { insert_type: :after, insert_value: 'a', content: %r{a d b c}, }, { insert_type: :index, insert_value: 2, content: %r{a b d c}, }, ].each do |params| context "with '#{params[:insert_type]}' makes '#{params[:content]}'" do pp = <<-EOS ini_subsetting { "a": ensure => present, section => 'one', setting => 'two', subsetting => 'a', path => "#{basedir}/insert_types.ini", } -> ini_subsetting { "b": ensure => present, section => 'one', setting => 'two', subsetting => 'b', path => "#{basedir}/insert_types.ini", } -> ini_subsetting { "c": ensure => present, section => 'one', setting => 'two', subsetting => 'c', path => "#{basedir}/insert_types.ini", } -> ini_subsetting { "insert makes #{params[:content]}": ensure => present, section => 'one', setting => 'two', subsetting => 'd', path => "#{basedir}/insert_types.ini", insert_type => '#{params[:insert_type]}', insert_value => '#{params[:insert_value]}', } EOS it_behaves_like 'has_content', "#{basedir}/insert_types.ini", pp, params[:content] end end end end diff --git a/spec/classes/create_ini_settings_test_spec.rb b/spec/classes/create_ini_settings_test_spec.rb index c008044..5f5555b 100644 --- a/spec/classes/create_ini_settings_test_spec.rb +++ b/spec/classes/create_ini_settings_test_spec.rb @@ -1,25 +1,27 @@ +# frozen_string_literal: true + require 'spec_helper' # end-to-end test of the create_init_settings function describe 'create_ini_settings_test' do it { is_expected.to have_ini_setting_resource_count(3) } it { is_expected.to contain_ini_setting('/tmp/foo.ini [section1] setting1').with( ensure: 'present', section: 'section1', setting: 'setting1', value: 'val1', path: '/tmp/foo.ini' ) } it { is_expected.to contain_ini_setting('/tmp/foo.ini [section2] setting2').with( ensure: 'present', section: 'section2', setting: 'setting2', value: 'val2', path: '/tmp/foo.ini' ) } it { is_expected.to contain_ini_setting('/tmp/foo.ini [section2] setting3').with( ensure: 'absent', section: 'section2', setting: 'setting3', path: '/tmp/foo.ini' ) } end diff --git a/spec/classes/create_multiple_ini_settings_spec.rb b/spec/classes/create_multiple_ini_settings_spec.rb index 6ed90fa..db79744 100644 --- a/spec/classes/create_multiple_ini_settings_spec.rb +++ b/spec/classes/create_multiple_ini_settings_spec.rb @@ -1,41 +1,43 @@ +# frozen_string_literal: true + require 'spec_helper' describe 'create_multiple_ini_settings' do context 'on a non-Windows platform', if: !Puppet::Util::Platform.windows? do let(:facts) do { 'os' => { 'family' => 'RedHat', 'release' => { 'major' => '7', 'minor' => '1', 'full' => '7.1.1503' } } } end it { is_expected.to compile } it { is_expected.to have_ini_setting_resource_count(2) } it { is_expected.to contain_ini_setting('/tmp/foo.ini [section1] setting1').with( ensure: 'present', section: 'section1', setting: 'setting1', value: 'value1', path: '/tmp/foo.ini', ) } it { is_expected.to contain_ini_setting('/tmp/foo.ini [section1] setting2').with( ensure: 'absent', section: 'section1', setting: 'setting2', path: '/tmp/foo.ini', ) } end context 'on a Windows platform', if: Puppet::Util::Platform.windows? do let(:facts) do { 'os' => { 'family' => 'windows' } } end it { is_expected.to compile } end end diff --git a/spec/classes/inherit_test1_spec.rb b/spec/classes/inherit_test1_spec.rb index f6f3244..500810b 100644 --- a/spec/classes/inherit_test1_spec.rb +++ b/spec/classes/inherit_test1_spec.rb @@ -1,8 +1,10 @@ +# frozen_string_literal: true + require 'spec_helper' # We can't really test much here, apart from the type roundtrips though the # parser OK. describe 'inherit_test1' do it { is_expected.to contain_inherit_ini_setting('valid_type').with('value' => 'true') } end diff --git a/spec/functions/create_ini_settings_spec.rb b/spec/functions/create_ini_settings_spec.rb index 9140e0b..9431911 100644 --- a/spec/functions/create_ini_settings_spec.rb +++ b/spec/functions/create_ini_settings_spec.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + require 'spec_helper' describe 'create_ini_settings' do it_behaves_like 'create_ini_settings function' end diff --git a/spec/functions/inifile_create_ini_settings_spec.rb b/spec/functions/inifile_create_ini_settings_spec.rb index 174a691..5916998 100644 --- a/spec/functions/inifile_create_ini_settings_spec.rb +++ b/spec/functions/inifile_create_ini_settings_spec.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + require 'spec_helper' describe 'inifile::create_ini_settings' do it_behaves_like 'create_ini_settings function' end diff --git a/spec/spec_helper_local.rb b/spec/spec_helper_local.rb index c726167..0d9188c 100644 --- a/spec/spec_helper_local.rb +++ b/spec/spec_helper_local.rb @@ -1,37 +1,39 @@ +# frozen_string_literal: true + if ENV['COVERAGE'] == 'yes' require 'simplecov' require 'simplecov-console' require 'codecov' SimpleCov.formatters = [ SimpleCov::Formatter::HTMLFormatter, SimpleCov::Formatter::Console, SimpleCov::Formatter::Codecov, ] SimpleCov.start do track_files 'lib/**/*.rb' add_filter '/spec' # do not track vendored files add_filter '/vendor' add_filter '/.vendor' # do not track gitignored files # this adds about 4 seconds to the coverage check # this could definitely be optimized add_filter do |f| # system returns true if exit status is 0, which with git-check-ignore means file is ignored system("git check-ignore --quiet #{f.filename}") end end end shared_examples 'create_ini_settings function' do it { is_expected.not_to eq(nil) } it { is_expected.to run.with_params({}) } it { is_expected.to run.with_params({}, {}) } it { is_expected.to run.with_params('section' => { 'setting' => 'value' }).and_raise_error(Puppet::ParseError, %r{must pass the path parameter}) } it { is_expected.to run.with_params(1 => 2).and_raise_error(Puppet::ParseError, %r{Section 1 must contain a Hash}) } end diff --git a/spec/unit/puppet/provider/ini_setting/inheritance_spec.rb b/spec/unit/puppet/provider/ini_setting/inheritance_spec.rb index d3b8760..0f22485 100644 --- a/spec/unit/puppet/provider/ini_setting/inheritance_spec.rb +++ b/spec/unit/puppet/provider/ini_setting/inheritance_spec.rb @@ -1,61 +1,63 @@ +# frozen_string_literal: true + require 'spec_helper' # This is a reduced version of ruby_spec.rb just to ensure we can subclass as # documented $LOAD_PATH << './spec/fixtures/inherit_ini_setting/lib' describe Puppet::Type.type(:inherit_ini_setting).provider(:ini_setting) do include PuppetlabsSpec::Files let(:tmpfile) { tmpfilename('inherit_ini_setting_test') } def validate_file(expected_content, tmpfile) expect(File.read(tmpfile)).to eq(expected_content) end before :each do File.open(tmpfile, 'w') do |fh| fh.write(orig_content) end end context 'when calling instances' do let(:orig_content) { '' } it 'parses nothing when the file is empty' do allow(described_class).to receive(:file_path).and_return(tmpfile) expect(described_class.instances).to eq([]) end context 'when the file has contents' do let(:orig_content) do <<-EOS # A comment red = blue green = purple EOS end it 'parses the results' do allow(described_class).to receive(:file_path).and_return(tmpfile) instances = described_class.instances expect(instances.size).to eq(2) # inherited version of namevar flattens the names names = instances.map do |instance| instance.instance_variable_get(:@property_hash)[:name] end # rubocop:disable Style/BlockDelimiters expect(names.sort).to eq(['green', 'red']) end end end context 'when ensuring that a setting is present' do let(:orig_content) { '' } it 'adds a value to the file' do allow(described_class).to receive(:file_path).and_return(tmpfile) resource = Puppet::Type::Inherit_ini_setting.new(setting: 'set_this', value: 'to_that') provider = described_class.new(resource) provider.create validate_file("set_this=to_that\n", tmpfile) end end end diff --git a/spec/unit/puppet/provider/ini_setting/ruby_spec.rb b/spec/unit/puppet/provider/ini_setting/ruby_spec.rb index 414eb85..d1f448e 100644 --- a/spec/unit/puppet/provider/ini_setting/ruby_spec.rb +++ b/spec/unit/puppet/provider/ini_setting/ruby_spec.rb @@ -1,1588 +1,1590 @@ +# frozen_string_literal: true + require 'spec_helper' require 'puppet' provider_class = Puppet::Type.type(:ini_setting).provider(:ruby) describe provider_class do include PuppetlabsSpec::Files let(:tmpfile) { tmpfilename('ini_setting_test') } let(:emptyfile) { tmpfilename('ini_setting_test_empty') } let(:common_params) do { title: 'ini_setting_ensure_present_test', path: tmpfile, section: 'section2', } end def validate_file(expected_content, tmpfile) expect(File.read(tmpfile)).to eq(expected_content) end before :each do File.open(tmpfile, 'w') do |fh| fh.write(orig_content) end File.open(emptyfile, 'w') do |fh| fh.write('') end end context 'when calling instances' do let :orig_content do '' end it 'fails when file path is not set' do expect { provider_class.instances }.to raise_error(Puppet::Error, 'Ini_settings only support collecting instances when a file path is hard coded') end context 'when file path is set by a child class' do child_one = Class.new(provider_class) do def self.file_path emptyfile end end it 'returns [] when file is empty' do expect(child_one).to receive(:file_path).and_return(emptyfile) expect(child_one.instances).to eq([]) end child_two = Class.new(provider_class) do def self.file_path '/some/file/path' end end it 'overrides the provider instances file_path' do resource = Puppet::Type::Ini_setting.new(common_params) provider = child_two.new(resource) expect(provider.file_path).to eq('/some/file/path') end end context 'when file has contecnts' do let(:orig_content) do <<-EOS # This is a comment [section1] ; This is also a comment foo=foovalue bar = barvalue main = true [section2] foo= foovalue2 baz=bazvalue url = http://192.168.1.1:8080 [section:sub] subby=bar #another comment ; yet another comment EOS end it 'is able to parse the results' do child_three = Class.new(provider_class) do def self.file_path '/some/file/path' end end expect(child_three).to receive(:file_path).exactly(2).times.and_return(tmpfile) expect(child_three.instances.size).to eq(7) expected_array = [ { name: 'section1/foo', value: 'foovalue' }, { name: 'section1/bar', value: 'barvalue' }, { name: 'section1/main', value: 'true' }, { name: 'section2/foo', value: 'foovalue2' }, { name: 'section2/baz', value: 'bazvalue' }, { name: 'section2/url', value: 'http://192.168.1.1:8080' }, { name: 'section:sub/subby', value: 'bar' }, ] real_array = [] ensure_array = [] child_three.instances.each do |x| prop_hash = x.instance_variable_get(:@property_hash) ensure_value = prop_hash.delete(:ensure) ensure_array.push(ensure_value) real_array.push(prop_hash) end expect(ensure_array.uniq).to eq([:present]) expect((real_array - expected_array) && (expected_array - real_array)).to eq([]) end # rubocop:enable RSpec/ExampleLength # rubocop:enable RSpec/MultipleExpectations end end context 'when ensuring that a setting is present' do let(:orig_content) do <<-EOS # This is a comment [section1] ; This is also a comment foo=foovalue bar = barvalue main = true [section2] foo= foovalue2 baz=bazvalue url = http://192.168.1.1:8080 [section:sub] subby=bar #another comment ; yet another comment -nonstandard- shoes = purple EOS end expected_content_one = <<-EOS # This is a comment [section1] ; This is also a comment foo=foovalue bar = barvalue main = true [section2] foo= foovalue2 baz=bazvalue url = http://192.168.1.1:8080 yahoo = yippee [section:sub] subby=bar #another comment ; yet another comment -nonstandard- shoes = purple EOS it 'adds a missing setting to the correct section' do resource = Puppet::Type::Ini_setting.new(common_params.merge(setting: 'yahoo', value: 'yippee')) provider = described_class.new(resource) expect(provider.exists?).to be false provider.create validate_file(expected_content_one, tmpfile) end expected_content_two = <<-EOS # This is a comment [section1] ; This is also a comment foo=foovalue bar = barvalue main = true [section2] foo= foovalue2 baz=bazvalue url = http://192.168.1.1:8080 [section:sub] subby=bar #another comment ; yet another comment -nonstandard- shoes = purple yahoo = yippee EOS it 'adds a missing setting to the correct section with pre/suffix' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'nonstandard', setting: 'yahoo', value: 'yippee', section_prefix: '-', section_suffix: '-')) provider = described_class.new(resource) expect(provider.exists?).to be false provider.create validate_file(expected_content_two, tmpfile) end expected_content_three = <<-EOS # This is a comment [section1] ; This is also a comment foo=foovalue bar = barvalue main = true [section2] foo= foovalue2 baz=bazvalue url = http://192.168.1.1:8080 [section:sub] subby=bar #another comment ; yet another comment -nonstandard- shoes = purple indented = weirdly EOS it 'adds a missing setting to the correct section with indent_char' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'nonstandard', setting: 'indented', value: 'weirdly', section_prefix: '-', section_suffix: '-', indent_char: "\t")) provider = described_class.new(resource) expect(provider.exists?).to be false provider.create validate_file(expected_content_three, tmpfile) end expected_content_four = <<-EOS # This is a comment [section1] ; This is also a comment foo=foovalue bar = barvalue main = true [section2] foo= foovalue2 baz=bazvalue url = http://192.168.1.1:8080 [section:sub] subby=bar #another comment ; yet another comment -nonstandard- shoes = purple indented = weirdly EOS it 'adds a missing setting to the correct section indented by indent_char * indent_width' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'nonstandard', setting: 'indented', value: 'weirdly', section_prefix: '-', section_suffix: '-', indent_char: "\t", indent_width: 4)) provider = described_class.new(resource) expect(provider.exists?).to be false provider.create validate_file(expected_content_four, tmpfile) end expected_content_five = <<-EOS # This is a comment [section1] ; This is also a comment foo=foovalue bar = barvalue main = true [section2] foo= foovalue2 baz=bazvalue url = http://192.168.1.1:8080 [section:sub] subby=bar #another comment ; yet another comment -nonstandard- shoes = purple indented = weirdly EOS it 'treats a string indent_width as an integer' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'nonstandard', setting: 'indented', value: 'weirdly', section_prefix: '-', section_suffix: '-', indent_char: "\t", indent_width: '4')) provider = described_class.new(resource) expect(provider.exists?).to be false provider.create validate_file(expected_content_five, tmpfile) end expected_content_six = <<-EOS # This is a comment [section1] ; This is also a comment foo=foovalue bar = barvalue main = true [section2] foo= foovalue2 baz=bazvalue url = http://192.168.1.1:8080 [section:sub] subby=bar #another comment ; yet another comment -nonstandard- shoes = purple yahoo = yippee EOS it 'adds a missing setting to the correct section with colon' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'section:sub', setting: 'yahoo', value: 'yippee')) provider = described_class.new(resource) expect(provider.exists?).to be false provider.create validate_file(expected_content_six, tmpfile) end expected_content_seven = <<-EOS # This is a comment [section1] ; This is also a comment foo=foovalue bar = barvalue main = true [section2] foo= foovalue2 baz=bazvalue2 url = http://192.168.1.1:8080 [section:sub] subby=bar #another comment ; yet another comment -nonstandard- shoes = purple EOS it 'modifies an existing setting with a different value' do resource = Puppet::Type::Ini_setting.new(common_params.merge(setting: 'baz', value: 'bazvalue2')) provider = described_class.new(resource) expect(provider).to be_exists provider.value = 'bazvalue2' validate_file(expected_content_seven, tmpfile) end expected_content_eight = <<-EOS # This is a comment [section1] ; This is also a comment foo=foovalue bar = barvalue main = false [section2] foo= foovalue2 baz=bazvalue url = http://192.168.1.1:8080 [section:sub] subby=bar #another comment ; yet another comment -nonstandard- shoes = purple EOS it 'modifies an existing setting with a different boolean value' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'section1', setting: 'main', value: false)) provider = described_class.new(resource) expect(provider.exists?).to be true transaction = instance_double('transaction', persistence: true) expect(Puppet::Transaction::ResourceHarness.new(transaction).evaluate(provider.resource).out_of_sync).to eq(true) validate_file(expected_content_eight, tmpfile) end # rubocop:enable RSpec/MultipleExpectations : Unable to reduce without altering test expected_content_nine = <<-EOS # This is a comment [section1] ; This is also a comment foo=foovalue bar = barvalue main = true [section2] foo= foovalue2 baz=bazvalue url = http://192.168.1.1:8080 [section:sub] subby=bar #another comment ; yet another comment -nonstandard- shoes = orange EOS it 'modifies an existing setting with pre/suffix with a different value' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'nonstandard', setting: 'shoes', value: 'orange', section_prefix: '-', section_suffix: '-')) provider = described_class.new(resource) expect(provider.exists?).to be true provider.value = 'orange' validate_file(expected_content_nine, tmpfile) end expected_content_ten = <<-EOS # This is a comment [section1] ; This is also a comment foo=foovalue bar = barvalue main = true [section2] foo= foovalue2 baz=bazvalue url = http://192.168.1.1:8080 [section:sub] subby=foo #another comment ; yet another comment -nonstandard- shoes = purple EOS it 'modifies an existing setting with a different value - with colon in section' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'section:sub', setting: 'subby', value: 'foo')) provider = described_class.new(resource) expect(provider.value).to eq('bar') provider.value = 'foo' validate_file(expected_content_ten, tmpfile) end expected_content_eleven = <<-EOS # This is a comment [section1] ; This is also a comment foo=foovalue bar = barvalue main = true [section2] foo= foovalue2 baz=bazvalue url = http://192.168.0.1:8080 [section:sub] subby=bar #another comment ; yet another comment -nonstandard- shoes = purple EOS it 'is able to handle settings with non alphanumbering settings' do resource = Puppet::Type::Ini_setting.new(common_params.merge(setting: 'url', value: 'http://192.168.0.1:8080')) provider = described_class.new(resource) expect(provider.value).to eq('http://192.168.1.1:8080') provider.value = 'http://192.168.0.1:8080' validate_file(expected_content_eleven, tmpfile) end expected_content_twelve = <<-EOS # This is a comment [section1] ; This is also a comment foo=foovalue bar = barvalue main = true [section2] foo= foovalue2 baz=bazvalue url = http://192.168.1.1:8080 [section:sub] subby=bar #another comment ; yet another comment -nonstandard- shoes = http://192.168.0.1:8080 EOS it 'is able to handle settings with pre/suffix with non alphanumbering settings' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'nonstandard', setting: 'shoes', value: 'http://192.168.0.1:8080', section_prefix: '-', section_suffix: '-')) provider = described_class.new(resource) expect(provider.value).to eq('purple') provider.value = 'http://192.168.0.1:8080' validate_file(expected_content_twelve, tmpfile) end it 'recognizes an existing setting with the specified value' do resource = Puppet::Type::Ini_setting.new(common_params.merge(setting: 'baz', value: 'bazvalue')) provider = described_class.new(resource) expect(provider.exists?).to be true end it 'recognizes an existing setting with pre/suffix with the specified value' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'nonstandard', setting: 'shoes', value: 'purple', section_prefix: '-', section_suffix: '-')) provider = described_class.new(resource) expect(provider.exists?).to be true end expected_content_thirteen = <<-EOS # This is a comment [section1] ; This is also a comment foo=foovalue bar = barvalue main = true [section2] foo= foovalue2 baz=bazvalue url = http://192.168.1.1:8080 [section:sub] subby=bar #another comment ; yet another comment -nonstandard- shoes = purple [section3] huzzah = shazaam EOS it 'adds a new section if the section does not exist' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'section3', setting: 'huzzah', value: 'shazaam')) provider = described_class.new(resource) expect(provider.exists?).to be false provider.create validate_file(expected_content_thirteen, tmpfile) end expected_content_fourteen = <<-EOS # This is a comment [section1] ; This is also a comment foo=foovalue bar = barvalue main = true [section2] foo= foovalue2 baz=bazvalue url = http://192.168.1.1:8080 [section:sub] subby=bar #another comment ; yet another comment -nonstandard- shoes = purple -section3- huzzah = shazaam EOS it 'adds a new section with pre/suffix if the section does not exist' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'section3', setting: 'huzzah', value: 'shazaam', section_prefix: '-', section_suffix: '-')) provider = described_class.new(resource) expect(provider).not_to be_exists provider.create validate_file(expected_content_fourteen, tmpfile) end expected_content_fifteen = <<-EOS # This is a comment [section1] ; This is also a comment foo=foovalue bar = barvalue main = true [section2] foo= foovalue2 baz=bazvalue url = http://192.168.1.1:8080 [section:sub] subby=bar #another comment ; yet another comment -nonstandard- shoes = purple [section:subsection] huzzah = shazaam EOS it 'adds a new section if the section does not exist - with colon' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'section:subsection', setting: 'huzzah', value: 'shazaam')) provider = described_class.new(resource) expect(provider.exists?).to be false provider.create validate_file(expected_content_fifteen, tmpfile) end expected_content_sixteen = <<-EOS # This is a comment [section1] ; This is also a comment foo=foovalue bar = barvalue main = true [section2] foo= foovalue2 baz=bazvalue url = http://192.168.1.1:8080 [section:sub] subby=bar #another comment ; yet another comment -nonstandard- shoes = purple -section:subsection- huzzah = shazaam EOS it 'adds a new section with pre/suffix if the section does not exist - with colon' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'section:subsection', setting: 'huzzah', value: 'shazaam', section_prefix: '-', section_suffix: '-')) provider = described_class.new(resource) expect(provider.exists?).to be false provider.create validate_file(expected_content_sixteen, tmpfile) end it 'adds a new empty section' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'section')) provider = described_class.new(resource) expect(provider.exists?).to be false provider.create validate_file(orig_content + "\n[section]\n", tmpfile) end it 'is able to handle variables of any type' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'section1', setting: 'main', value: true)) provider = described_class.new(resource) expect(provider.value).to eq('true') end end context 'when no sections exist' do let(:orig_content) do '' end validate_zero = "[section]\n" it 'adds an empty section' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'section', path: emptyfile)) provider = described_class.new(resource) expect(provider.exists?).to be false provider.create validate_file(validate_zero, emptyfile) end validate_one = '[section1] setting1 = hellowworld ' it 'adds a new section' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'section1', setting: 'setting1', value: 'hellowworld', path: emptyfile)) provider = described_class.new(resource) expect(provider.exists?).to be false provider.create validate_file(validate_one, emptyfile) end validate_two = '-section1- setting1 = hellowworld ' it 'adds a new section with pre/suffix' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'section1', setting: 'setting1', value: 'hellowworld', path: emptyfile, section_prefix: '-', section_suffix: '-')) provider = described_class.new(resource) expect(provider.exists?).to be false provider.create validate_file(validate_two, emptyfile) end validate_three = '[section:subsection] setting1 = hellowworld ' it 'adds a new section with colon' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'section:subsection', setting: 'setting1', value: 'hellowworld', path: emptyfile)) provider = described_class.new(resource) expect(provider.exists?).to be false provider.create validate_file(validate_three, emptyfile) end validate_four = '-section:subsection- setting1 = hellowworld ' it 'adds a new section with pre/suffix with colon' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'section:subsection', setting: 'setting1', value: 'hellowworld', path: emptyfile, section_prefix: '-', section_suffix: '-')) provider = described_class.new(resource) expect(provider.exists?).to be false provider.create validate_file(validate_four, emptyfile) end end context 'when only an empty section exists' do let(:orig_content) do "[section]\n" end it 'adds a new setting' do expected = orig_content { 'section' => { 'first' => 1 } }.each_pair do |section, settings| settings.each_pair do |setting, value| resource = Puppet::Type::Ini_setting.new(common_params.merge(section: section, setting: setting, value: value)) provider = described_class.new(resource) expect(provider.exists?).to be false # byebug provider.create expected += "#{setting} = #{value}\n" end end validate_file(expected, tmpfile) end end context 'when dealing with a global section' do let(:orig_content) do <<-EOS # This is a comment foo=blah [section2] foo = http://192.168.1.1:8080 ; yet another comment EOS end expected_content_one = <<-EOS # This is a comment foo=blah bar = yippee [section2] foo = http://192.168.1.1:8080 ; yet another comment EOS it "adds a missing setting if it doesn't exist" do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: '', setting: 'bar', value: 'yippee')) provider = described_class.new(resource) expect(provider.exists?).to be false provider.create validate_file(expected_content_one, tmpfile) end expected_content_two = <<-EOS # This is a comment foo=yippee [section2] foo = http://192.168.1.1:8080 ; yet another comment EOS it 'modifies an existing setting with a different value' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: '', setting: 'foo', value: 'yippee')) provider = described_class.new(resource) expect(provider.value).to eq('blah') provider.value = 'yippee' validate_file(expected_content_two, tmpfile) end it 'recognizes an existing setting with the specified value' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: '', setting: 'foo', value: 'blah')) provider = described_class.new(resource) expect(provider.exists?).to be true end end context 'when the first line of the file is a section' do let(:orig_content) do <<-EOS [section2] foo = http://192.168.1.1:8080 EOS end expected_content_one = <<-EOS foo = yippee [section2] foo = http://192.168.1.1:8080 EOS it 'is able to add a global setting' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: '', setting: 'foo', value: 'yippee')) provider = described_class.new(resource) expect(provider.exists?).to be false provider.create validate_file(expected_content_one, tmpfile) end expected_content_two = <<-EOS [section2] foo = yippee EOS it 'modifies an existing setting' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'section2', setting: 'foo', value: 'yippee')) provider = described_class.new(resource) expect(provider.value).to eq('http://192.168.1.1:8080') provider.value = 'yippee' validate_file(expected_content_two, tmpfile) end expected_content_three = <<-EOS [section2] foo = http://192.168.1.1:8080 bar = baz EOS it 'adds a new setting' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'section2', setting: 'bar', value: 'baz')) provider = described_class.new(resource) expect(provider.exists?).to be false provider.create validate_file(expected_content_three, tmpfile) end end context 'when overriding the separator' do let(:orig_content) do <<-EOS [section2] foo=bar EOS end expected_content_one = <<-EOS [section2] foo=yippee EOS it 'modifies an existing setting' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'section2', setting: 'foo', value: 'yippee', key_val_separator: '=')) provider = described_class.new(resource) expect(provider.value).to eq('bar') provider.value = 'yippee' validate_file(expected_content_one, tmpfile) end end context 'when overriding the separator to something other than =' do let(:orig_content) do <<-EOS [section2] foo: bar EOS end expected_content_one = <<-EOS [section2] foo: yippee EOS it 'modifies an existing setting' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'section2', setting: 'foo', value: 'yippee', key_val_separator: ': ')) provider = described_class.new(resource) expect(provider.value).to eq('bar') provider.value = 'yippee' validate_file(expected_content_one, tmpfile) end expected_content_two = <<-EOS [section2] foo: bar bar: baz EOS it 'adds a new setting' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'section2', setting: 'bar', value: 'baz', key_val_separator: ': ')) provider = described_class.new(resource) expect(provider.exists?).to be false provider.create validate_file(expected_content_two, tmpfile) end end context 'when overriding the separator to a space' do let(:orig_content) do <<-EOS [section2] foo bar EOS end expected_content_one = <<-EOS [section2] foo yippee EOS it 'modifies an existing setting' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'section2', setting: 'foo', value: 'yippee', key_val_separator: ' ')) provider = described_class.new(resource) expect(provider.value).to eq('bar') provider.value = 'yippee' validate_file(expected_content_one, tmpfile) end expected_content_two = <<-EOS [section2] foo bar bar baz EOS it 'adds a new setting' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'section2', setting: 'bar', value: 'baz', key_val_separator: ' ')) provider = described_class.new(resource) expect(provider.exists?).to be false provider.create validate_file(expected_content_two, tmpfile) end end context 'when ensuring that a setting is absent' do let(:orig_content) do <<-EOS [section1] ; This is also a comment foo=foovalue bar = barvalue main = true [section2] foo= foovalue2 baz=bazvalue url = http://192.168.1.1:8080 [section3] # com = ment uncom = ment [section4] uncom = ment [section:sub] subby=bar #another comment ; yet another comment -nonstandard- shoes = purple EOS end expected_content_one = <<-EOS [section1] ; This is also a comment bar = barvalue main = true [section2] foo= foovalue2 baz=bazvalue url = http://192.168.1.1:8080 [section3] # com = ment uncom = ment [section4] uncom = ment [section:sub] subby=bar #another comment ; yet another comment -nonstandard- shoes = purple EOS it 'removes a setting that exists' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'section1', setting: 'foo', ensure: 'absent')) provider = described_class.new(resource) expect(provider.exists?).to be true provider.destroy validate_file(expected_content_one, tmpfile) end expected_content_two = <<-EOS [section1] ; This is also a comment foo=foovalue bar = barvalue main = true [section2] foo= foovalue2 baz=bazvalue url = http://192.168.1.1:8080 [section3] # com = ment uncom = ment [section4] uncom = ment [section:sub] subby=bar #another comment ; yet another comment EOS it 'removes a setting with pre/suffix that exists' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'nonstandard', setting: 'shoes', ensure: 'absent', section_prefix: '-', section_suffix: '-')) provider = described_class.new(resource) expect(provider.exists?).to be true provider.destroy validate_file(expected_content_two, tmpfile) end expected_content_three = <<-EOS [section1] ; This is also a comment foo=foovalue bar = barvalue main = true [section2] foo= foovalue2 baz=bazvalue url = http://192.168.1.1:8080 [section3] # com = ment uncom = ment [section4] uncom = ment [section:sub] subby=bar #another comment ; yet another comment -nonstandard- shoes = purple EOS it 'does nothing for a setting that does not exist' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'section:sub', setting: 'foo', ensure: 'absent')) provider = described_class.new(resource) expect(provider.exists?).to be false provider.destroy validate_file(expected_content_three, tmpfile) end expected_content_four = <<-EOS [section1] ; This is also a comment foo=foovalue bar = barvalue main = true [section2] foo= foovalue2 baz=bazvalue url = http://192.168.1.1:8080 [section3] # com = ment uncom = ment [section4] uncom = ment [section:sub] subby=bar #another comment ; yet another comment -nonstandard- shoes = purple EOS it 'does nothing for a setting with pre/suffix that does not exist' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'nonstandard', setting: 'foo', ensure: 'absent', section_prefix: '-', section_suffix: '-')) provider = described_class.new(resource) expect(provider.exists?).to be false provider.destroy validate_file(expected_content_four, tmpfile) end expected_content_five = <<-EOS [section1] ; This is also a comment foo=foovalue bar = barvalue main = true [section2] foo= foovalue2 baz=bazvalue url = http://192.168.1.1:8080 [section3] # com = ment [section4] uncom = ment [section:sub] subby=bar #another comment ; yet another comment -nonstandard- shoes = purple EOS it 'does not remove a section when the last uncommented setting is removed if there are comments' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'section3', setting: 'uncom', ensure: 'absent')) provider = described_class.new(resource) expect(provider.exists?).to be true provider.destroy validate_file(expected_content_five, tmpfile) end expected_content_six = <<-EOS [section1] ; This is also a comment foo=foovalue bar = barvalue main = true [section2] foo= foovalue2 baz=bazvalue url = http://192.168.1.1:8080 [section3] # com = ment uncom = ment [section:sub] subby=bar #another comment ; yet another comment -nonstandard- shoes = purple EOS it 'removes the section when removing the last line in the section' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'section4', setting: 'uncom', ensure: 'absent')) provider = described_class.new(resource) expect(provider.exists?).to be true provider.destroy validate_file(expected_content_six, tmpfile) end end context 'when dealing with indentation in sections' do let(:orig_content) do <<-EOS # This is a comment [section1] ; This is also a comment foo=foovalue bar = barvalue main = true [section2] foo= foovalue2 baz=bazvalue url = http://192.168.1.1:8080 [section:sub] subby=bar #another comment fleezy = flam ; yet another comment EOS end expected_content_one = <<-EOS # This is a comment [section1] ; This is also a comment foo=foovalue bar = barvalue main = true yahoo = yippee [section2] foo= foovalue2 baz=bazvalue url = http://192.168.1.1:8080 [section:sub] subby=bar #another comment fleezy = flam ; yet another comment EOS it 'adds a missing setting at the correct indentation when the header is aligned' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'section1', setting: 'yahoo', value: 'yippee')) provider = described_class.new(resource) expect(provider.exists?).to be false provider.create validate_file(expected_content_one, tmpfile) end expected_content_two = <<-EOS # This is a comment [section1] ; This is also a comment foo=foovalue bar = barvalue2 main = true [section2] foo= foovalue2 baz=bazvalue url = http://192.168.1.1:8080 [section:sub] subby=bar #another comment fleezy = flam ; yet another comment EOS it 'updates an existing setting at the correct indentation when the header is aligned' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'section1', setting: 'bar', value: 'barvalue2')) provider = described_class.new(resource) expect(provider.exists?).to be true provider.create validate_file(expected_content_two, tmpfile) end expected_content_three = <<-EOS # This is a comment [section1] ; This is also a comment foo=foovalue bar = barvalue main = true [section2] foo= foovalue2 baz=bazvalue url = http://192.168.1.1:8080 yahoo = yippee [section:sub] subby=bar #another comment fleezy = flam ; yet another comment EOS it 'adds a missing setting at the correct indentation when the header is not aligned' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'section2', setting: 'yahoo', value: 'yippee')) provider = described_class.new(resource) expect(provider.exists?).to be false provider.create validate_file(expected_content_three, tmpfile) end expected_content_four = <<-EOS # This is a comment [section1] ; This is also a comment foo=foovalue bar = barvalue main = true [section2] foo= foovalue2 baz=bazvalue2 url = http://192.168.1.1:8080 [section:sub] subby=bar #another comment fleezy = flam ; yet another comment EOS it 'updates an existing setting at the correct indentation when the header is not aligned' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'section2', setting: 'baz', value: 'bazvalue2')) provider = described_class.new(resource) expect(provider.exists?).to be true provider.create validate_file(expected_content_four, tmpfile) end expected_content_five = <<-EOS # This is a comment [section1] ; This is also a comment foo=foovalue bar = barvalue main = true [section2] foo= foovalue2 baz=bazvalue url = http://192.168.1.1:8080 [section:sub] subby=bar #another comment fleezy = flam ; yet another comment yahoo = yippee EOS it 'adds a missing setting at the min indentation when the section is not aligned' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'section:sub', setting: 'yahoo', value: 'yippee')) provider = described_class.new(resource) expect(provider.exists?).to be false provider.create validate_file(expected_content_five, tmpfile) end expected_content_six = <<-EOS # This is a comment [section1] ; This is also a comment foo=foovalue bar = barvalue main = true [section2] foo= foovalue2 baz=bazvalue url = http://192.168.1.1:8080 [section:sub] subby=bar #another comment fleezy = flam2 ; yet another comment EOS it 'updates an existing setting at the previous indentation when the section is not aligned' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'section:sub', setting: 'fleezy', value: 'flam2')) provider = described_class.new(resource) expect(provider.exists?).to be true provider.create validate_file(expected_content_six, tmpfile) end expected_content_seven = <<-EOS # This is a comment [section1] ; This is also a comment foo=foovalue bar = barvalue main = true [section2] foo= foovalue2 baz=bazvalue url = http://192.168.1.1:8080 [section:sub] subby=bar #another comment fleezy = flam2 ; yet another comment EOS it 'updates an existing setting at the previous indentation regardless of indent_char and indent_width settings' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'section:sub', setting: 'fleezy', value: 'flam2', indent_char: 'ignore this', indent_width: 10)) provider = described_class.new(resource) expect(provider.exists?).to be true provider.create validate_file(expected_content_seven, tmpfile) end end context 'when dealing settings that have a commented version present' do let(:orig_content) do <<-EOS [section1] # foo=foovalue bar=barvalue foo = foovalue2 [section2] # foo = foovalue ;bar=barvalue blah = blah #baz= EOS end expected_content_eight = <<-EOS [section1] # foo=foovalue bar=barvalue foo = foovalue2 [section2] # foo = foovalue foo = foo3 ;bar=barvalue blah = blah #baz= EOS it 'adds a new setting below a commented version of that setting' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'section2', setting: 'foo', value: 'foo3')) provider = described_class.new(resource) expect(provider.exists?).to be false provider.create validate_file(expected_content_eight, tmpfile) end expected_content_nine = <<-EOS [section1] # foo=foovalue bar=barvalue foo = foo3 [section2] # foo = foovalue ;bar=barvalue blah = blah #baz= EOS it 'updates an existing setting in place, even if there is a commented version of that setting' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'section1', setting: 'foo', value: 'foo3')) provider = described_class.new(resource) expect(provider.exists?).to be true provider.create validate_file(expected_content_nine, tmpfile) end expected_content_ten = <<-EOS [section1] # foo=foovalue bar=barvalue foo = foovalue2 [section2] # foo = foovalue ;bar=barvalue bar = bar3 blah = blah #baz= EOS it 'adds a new setting below a commented version of that setting, respecting semicolons as comments' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'section2', setting: 'bar', value: 'bar3')) provider = described_class.new(resource) expect(provider.exists?).to be false provider.create validate_file(expected_content_ten, tmpfile) end expected_content_four = <<-EOS [section1] # foo=foovalue bar=barvalue foo = foovalue2 [section2] # foo = foovalue ;bar=barvalue blah = blah #baz= baz = bazvalue EOS it 'adds a new setting below an empty commented version of that setting' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'section2', setting: 'baz', value: 'bazvalue')) provider = described_class.new(resource) expect(provider.exists?).to be false provider.create validate_file(expected_content_four, tmpfile) end context 'when a section only contains comments' do let(:orig_content) do <<-EOS [section1] # foo=foovalue # bar=bar2 EOS end expected_content_one = <<-EOS [section1] # foo=foovalue foo = foovalue2 # bar=bar2 EOS it 'is able to add a new setting when a section contains only comments' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'section1', setting: 'foo', value: 'foovalue2')) provider = described_class.new(resource) expect(provider.exists?).to be false provider.create validate_file(expected_content_one, tmpfile) end expected_content_two = <<-EOS [section1] # foo=foovalue # bar=bar2 bar = barvalue2 EOS it 'is able to add a new setting when it matches a commented out line other than the first one' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'section1', setting: 'bar', value: 'barvalue2')) provider = described_class.new(resource) expect(provider.exists?).to be false provider.create validate_file(expected_content_two, tmpfile) end end context 'when sections have spaces and dashes' do let(:orig_content) do <<-EOS # This is a comment [section - one] ; This is also a comment foo=foovalue bar = barvalue main = true [section - two] foo= foovalue2 baz=bazvalue url = http://192.168.1.1:8080 [section:sub] subby=bar #another comment ; yet another comment EOS end expected_content_one = <<-EOS # This is a comment [section - one] ; This is also a comment foo=foovalue bar = barvalue main = true [section - two] foo= foovalue2 baz=bazvalue url = http://192.168.1.1:8080 yahoo = yippee [section:sub] subby=bar #another comment ; yet another comment EOS it 'adds a missing setting to the correct section' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'section - two', setting: 'yahoo', value: 'yippee')) provider = described_class.new(resource) expect(provider.exists?).to be false provider.create validate_file(expected_content_one, tmpfile) end end end context 'when sections have spaces and quotations' do let(:orig_content) do <<-EOS [branch "main"] remote = origin merge = refs/heads/main [alias] to-deploy = log --merges --grep='pull request' --format='%s (%cN)' origin/production..origin/main [branch "production"] remote = origin merge = refs/heads/production EOS end expected_content_one = <<-EOS [branch "main"] remote = origin merge = refs/heads/main [alias] to-deploy = log --merges --grep='pull request' --format='%s (%cN)' origin/production..origin/main foo = bar [branch "production"] remote = origin merge = refs/heads/production EOS it 'adds a missing setting to the correct section' do resource = Puppet::Type::Ini_setting.new(common_params.merge(section: 'alias', setting: 'foo', value: 'bar')) provider = described_class.new(resource) expect(provider.exists?).to be false provider.create validate_file(expected_content_one, tmpfile) end end # rubocop:enable Layout/IndentHeredoc end diff --git a/spec/unit/puppet/provider/ini_subsetting/ruby_spec.rb b/spec/unit/puppet/provider/ini_subsetting/ruby_spec.rb index 18f22f7..8c732ca 100644 --- a/spec/unit/puppet/provider/ini_subsetting/ruby_spec.rb +++ b/spec/unit/puppet/provider/ini_subsetting/ruby_spec.rb @@ -1,361 +1,363 @@ +# frozen_string_literal: true + require 'spec_helper' require 'puppet' provider_class = Puppet::Type.type(:ini_subsetting).provider(:ruby) describe provider_class do include PuppetlabsSpec::Files let(:tmpfile) { tmpfilename('ini_setting_test') } def validate_file(expected_content, tmpfile) expect(File.read(tmpfile)).to eq expected_content end before :each do File.open(tmpfile, 'w') do |fh| fh.write(orig_content) end end context 'when ensuring that a subsetting is present' do let(:common_params) do { title: 'ini_setting_ensure_present_test', path: tmpfile, section: '', key_val_separator: '=', setting: 'JAVA_ARGS', } end let(:orig_content) do <<-EOS JAVA_ARGS="-Xmx192m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/pe-puppetdb/puppetdb-oom.hprof" EOS end expected_content_one = <<-EOS JAVA_ARGS="-Xmx192m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/pe-puppetdb/puppetdb-oom.hprof -Xms128m" EOS it 'adds a missing subsetting' do resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: '-Xms', value: '128m')) provider = described_class.new(resource) expect(provider.exists?).to be_nil provider.create validate_file(expected_content_one, tmpfile) end expected_content_two = <<-EOS JAVA_ARGS="-Xms128m -Xmx192m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/pe-puppetdb/puppetdb-oom.hprof" EOS it 'adds a missing subsetting element at the beginning of the line' do resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: '-Xms', value: '128m', insert_type: :start)) provider = described_class.new(resource) expect(provider.exists?).to be_nil provider.create validate_file(expected_content_two, tmpfile) end expected_content_three = <<-EOS JAVA_ARGS="-Xmx192m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/pe-puppetdb/puppetdb-oom.hprof -Xms128m" EOS it 'adds a missing subsetting element at the end of the line' do resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: '-Xms', value: '128m', insert_type: :end)) provider = described_class.new(resource) expect(provider.exists?).to be_nil provider.create validate_file(expected_content_three, tmpfile) end expected_content_four = <<-EOS JAVA_ARGS="-Xmx192m -Xms128m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/pe-puppetdb/puppetdb-oom.hprof" EOS it 'adds a missing subsetting element after the given item' do resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: '-Xms', value: '128m', insert_type: :after, insert_value: '-Xmx')) provider = described_class.new(resource) expect(provider.exists?).to be_nil provider.create validate_file(expected_content_four, tmpfile) end expected_content_five = <<-EOS JAVA_ARGS="-Xms128m -Xmx192m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/pe-puppetdb/puppetdb-oom.hprof" EOS it 'adds a missing subsetting element before the given item' do resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: '-Xms', value: '128m', insert_type: :before, insert_value: '-Xmx')) provider = described_class.new(resource) expect(provider.exists?).to be_nil provider.create validate_file(expected_content_five, tmpfile) end expected_content_six = <<-EOS JAVA_ARGS="-Xmx192m -Xms128m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/pe-puppetdb/puppetdb-oom.hprof" EOS it 'adds a missing subsetting element at the given index' do resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: '-Xms', value: '128m', insert_type: :index, insert_value: '1')) provider = described_class.new(resource) expect(provider.exists?).to be_nil provider.create validate_file(expected_content_six, tmpfile) end expected_content_seven = <<-EOS JAVA_ARGS="-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/pe-puppetdb/puppetdb-oom.hprof" EOS it 'removes an existing subsetting' do resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: '-Xmx')) provider = described_class.new(resource) expect(provider.exists?).to eq '192m' provider.destroy validate_file(expected_content_seven, tmpfile) end expected_content_eight = <<-EOS JAVA_ARGS="-Xmx192m" EOS it 'is able to remove several subsettings with the same name' do resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: '-XX')) provider = described_class.new(resource) expect(provider.exists?).to eq ':+HeapDumpOnOutOfMemoryError' provider.destroy validate_file(expected_content_eight, tmpfile) end expected_content_nine = <<-EOS JAVA_ARGS="-Xmx256m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/pe-puppetdb/puppetdb-oom.hprof" EOS it 'modifies an existing subsetting' do resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: '-Xmx', value: '256m')) provider = described_class.new(resource) expect(provider.exists?).to eq '192m' provider.value = '256m' validate_file(expected_content_nine, tmpfile) end expected_content_ten = <<-EOS JAVA_ARGS="-Xmx192m -XXtest -XXtest" EOS it 'is able to modify several subsettings with the same name' do resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: '-XX', value: 'test')) provider = described_class.new(resource) expect(provider.exists?).to eq ':+HeapDumpOnOutOfMemoryError' provider.value = 'test' validate_file(expected_content_ten, tmpfile) end end context 'when working with subsettings in files with unquoted settings values' do let(:common_params) do { title: 'ini_setting_ensure_present_test', path: tmpfile, section: 'main', setting: 'reports', } end let(:orig_content) do <<-EOS [main] reports = http,foo EOS end expected_content_one = <<-EOS [main] reports = foo EOS it 'removes an existing subsetting' do resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: 'http', subsetting_separator: ',')) provider = described_class.new(resource) expect(provider.exists?).to eq '' provider.destroy validate_file(expected_content_one, tmpfile) end expected_content_two = <<-EOS [main] reports = http,foo,puppetdb EOS it "adds a new subsetting when the 'parent' setting already exists" do resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: 'puppetdb', subsetting_separator: ',')) provider = described_class.new(resource) expect(provider.exists?).to be_nil provider.value = '' validate_file(expected_content_two, tmpfile) end expected_content_three = <<-EOS [main] reports = http,foo somenewsetting = puppetdb EOS it "adds a new subsetting when the 'parent' setting does not already exist" do resource = Puppet::Type::Ini_subsetting.new(common_params.merge(setting: 'somenewsetting', subsetting: 'puppetdb', subsetting_separator: ',')) provider = described_class.new(resource) expect(provider.exists?).to be_nil provider.value = '' validate_file(expected_content_three, tmpfile) end end context 'when working with subsettings in files with use_exact_match' do let(:common_params) do { title: 'ini_setting_ensure_present_test', path: tmpfile, section: 'main', setting: 'reports', use_exact_match: true, } end let(:orig_content) do <<-EOS [main] reports = http,foo EOS end expected_content_one = <<-EOS [main] reports = http,foo,fo EOS it "adds a new subsetting when the 'parent' setting already exists" do resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: 'fo', subsetting_separator: ',')) provider = described_class.new(resource) provider.value = '' validate_file(expected_content_one, tmpfile) end expected_content_two = <<-EOS [main] reports = http,foo EOS it 'does not remove substring subsettings' do resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: 'fo', subsetting_separator: ',')) provider = described_class.new(resource) provider.value = '' provider.destroy validate_file(expected_content_two, tmpfile) end end context 'when working with subsettings in files with subsetting_key_val_separator' do let(:common_params) do { title: 'ini_setting_ensure_present_test', path: tmpfile, section: 'main', setting: 'reports', subsetting_separator: ',', subsetting_key_val_separator: ':', } end let(:orig_content) do <<-EOS [main] reports = a:1,b:2 EOS end expected_content_one = <<-EOS [main] reports = a:1,b:2,c:3 EOS it "adds a new subsetting when the 'parent' setting already exists" do resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: 'c', value: '3')) provider = described_class.new(resource) provider.value = '3' validate_file(expected_content_one, tmpfile) end expected_content_two = <<-EOS [main] reports = a:1,b:2 somenewsetting = c:3 EOS it "adds a new subsetting when the 'parent' setting does not already exist" do resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: 'c', value: '3', setting: 'somenewsetting')) provider = described_class.new(resource) expect(provider.exists?).to be_nil provider.value = '3' validate_file(expected_content_two, tmpfile) end expected_content_three = <<-EOS [main] reports = a:1 EOS it 'is able to remove the existing subsetting' do resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: 'b')) provider = described_class.new(resource) expect(provider.exists?).to eq '2' provider.destroy validate_file(expected_content_three, tmpfile) end expected_content_four = <<-EOS [main] reports = a:1,b:5 EOS it 'is able to modify the existing subsetting' do resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: 'b', value: '5')) provider = described_class.new(resource) expect(provider.exists?).to eq '2' provider.value = '5' validate_file(expected_content_four, tmpfile) end end context 'when delete_if_empty is toggled to true' do let(:common_params) do { title: 'ini_setting_delete_if_empty_test', path: tmpfile, section: 'main', delete_if_empty: true, } end let(:orig_content) do <<-EOS [main] reports = http something = else EOS end expected_content_one = <<-EOS [main] something = else EOS expected_content_two = '' it 'removes the subsetting when the it is empty' do resource = Puppet::Type::Ini_subsetting.new(common_params.merge(setting: 'reports', subsetting: 'http', subsetting_separator: ',')) provider = described_class.new(resource) provider.destroy validate_file(expected_content_one, tmpfile) resource = Puppet::Type::Ini_subsetting.new(common_params.merge(setting: 'something', subsetting: 'else', subsetting_separator: ',')) provider = described_class.new(resource) provider.destroy validate_file(expected_content_two, tmpfile) end end end diff --git a/spec/unit/puppet/type/ini_setting_spec.rb b/spec/unit/puppet/type/ini_setting_spec.rb index c4a717a..00b3adf 100644 --- a/spec/unit/puppet/type/ini_setting_spec.rb +++ b/spec/unit/puppet/type/ini_setting_spec.rb @@ -1,171 +1,173 @@ +# frozen_string_literal: true + require 'spec_helper' ini_setting = Puppet::Type.type(:ini_setting) describe ini_setting do describe 'path validation' do subject { -> { described_class.new(name: 'foo', path: path) } } context 'on posix platforms' do before(:each) do Puppet.features.stub(:posix?) { true } Puppet.features.stub(:microsoft_windows?) { false } Puppet::Util::Platform.stub(:windows?) { false } end context 'with an absolute path' do let(:path) { '/absolute/path' } it { is_expected.not_to raise_exception } end context 'with a relative path' do let(:path) { 'relative/path' } it { is_expected.to raise_exception } end end context 'on windows platforms' do before(:each) do Puppet.features.stub(:posix?) { false } Puppet.features.stub(:microsoft_windows?) { true } Puppet::Util::Platform.stub(:windows?) { true } end context 'with an absolute path with front slashes' do let(:path) { 'c:/absolute/path' } it { is_expected.not_to raise_exception } end context 'with an absolute path with backslashes' do let(:path) { 'c:\absolute\path' } it { is_expected.not_to raise_exception } end context 'with an absolute path with mixed slashes' do let(:path) { 'c:/absolute\path' } it { is_expected.not_to raise_exception } end context 'with a relative path with front slashes' do let(:path) { 'relative/path' } it { is_expected.to raise_exception } end context 'with a relative path with back slashes' do let(:path) { 'relative\path' } it { is_expected.to raise_exception } end end # rubocop:enable RSpec/NestedGroups end [true, false].product([true, false, 'true', 'false', 'md5', :md5]).each do |cfg, param| describe "when Puppet[:show_diff] is #{cfg} and show_diff => #{param}" do before(:each) do Puppet[:show_diff] = cfg end let(:value) { described_class.new(name: 'foo', value: 'whatever', show_diff: param).property(:value) } if cfg && [true, 'true'].include?(param) it 'displays diff' do expect(value.change_to_s('not_secret', 'at_all')).to include('not_secret', 'at_all') end it 'tells current value' do expect(value.is_to_s('not_secret_at_all')).to eq('not_secret_at_all') end it 'tells new value' do expect(value.should_to_s('not_secret_at_all')).to eq('not_secret_at_all') end elsif cfg && ['md5', :md5].include?(param) it 'tells correct md5 hashes for multiple values' do expect(value.change_to_s('not_at', 'all_secret')).to include('6edef0c4f5ec664feff6ca6fbc290970', '1660308ab156754fa09af0e8dc2c6629') end it 'does not tell singular value one' do expect(value.change_to_s('not_at #', 'all_secret')).not_to include('not_at') end it 'does not tell singular value two' do expect(value.change_to_s('not_at', 'all_secret')).not_to include('all_secret') end it 'tells md5 of current value' do expect(value.is_to_s('not_at_all_secret')).to eq('{md5}858b46aee11b780b8f5c8853668efc05') end it 'does not tell the current value' do expect(value.is_to_s('not_at_all_secret')).not_to include('not_secret_at_all') end it 'tells md5 of new value' do expect(value.should_to_s('not_at_all_secret')).to eq('{md5}858b46aee11b780b8f5c8853668efc05') end it 'does not tell the new value' do expect(value.should_to_s('not_at_all_secret')).not_to include('not_secret_at_all') end else it 'tells redaction warning in place of actual values' do expect(value.change_to_s('at_all', 'not_secret')).to include('[redacted sensitive information]') end it 'does not tell actual value one' do expect(value.change_to_s('at_all', 'not_secret')).not_to include('not_secret') end it 'does not tell actual value two' do expect(value.change_to_s('at_all', 'not_secret')).not_to include('at_all') end it 'tells redaction warning in place of current value' do expect(value.is_to_s('not_secret_at_all')).to eq('[redacted sensitive information]') end it 'does not tell current value' do expect(value.is_to_s('not_secret_at_all')).not_to include('not_secret_at_all') end it 'tells redaction warning in place of new value' do expect(value.should_to_s('not_secret_at_all')).to eq('[redacted sensitive information]') end it 'does not tell new value' do expect(value.should_to_s('not_secret_at_all')).not_to include('not_secret_at_all') end end end end describe 'when parent of :path is in the catalog' do ['posix', 'windows'].each do |platform| context "on #{platform} platforms" do before(:each) do Puppet.features.stub(:posix?) { platform == 'posix' } Puppet.features.stub(:microsoft_windows?) { platform == 'windows' } Puppet::Util::Platform.stub(:windows?) { platform == 'windows' } end let(:file_path) { (platform == 'posix') ? '/tmp' : 'c:/tmp' } let(:file_resource) { Puppet::Type.type(:file).new(name: file_path) } let(:ini_setting_resource) { described_class.new(name: 'foo', path: "#{file_path}/foo.ini") } let(:auto_req) do catalog = Puppet::Resource::Catalog.new catalog.add_resource(file_resource) catalog.add_resource(ini_setting_resource) ini_setting_resource.autorequire end it 'creates relationship' do expect(auto_req.size).to be 1 end it 'links to ini_setting resource' do expect(auto_req[0].target).to eq(ini_setting_resource) end it 'autorequires parent directory' do expect(auto_req[0].source).to eq(file_resource) end end end end end diff --git a/spec/unit/puppet/type/ini_subetting_spec.rb b/spec/unit/puppet/type/ini_subetting_spec.rb index 088d775..d32de89 100644 --- a/spec/unit/puppet/type/ini_subetting_spec.rb +++ b/spec/unit/puppet/type/ini_subetting_spec.rb @@ -1,131 +1,133 @@ +# frozen_string_literal: true + require 'spec_helper' ini_subsetting = Puppet::Type.type(:ini_subsetting) describe ini_subsetting do describe 'quote_char validation' do subject { -> { described_class.new(name: 'foo', path: path, quote_char: quote_char) } } context 'on posix platforms' do let(:path) { '/absolute/path' } let(:quote_char) { '\”' } it { is_expected.to raise_exception } end end describe 'path validation' do subject { -> { described_class.new(name: 'foo', path: path) } } context 'on posix platforms' do before(:each) do Puppet.features.stub(:posix?) { true } Puppet.features.stub(:microsoft_windows?) { false } Puppet::Util::Platform.stub(:windows?) { false } end context 'with an absolute path' do let(:path) { '/absolute/path' } it { is_expected.not_to raise_exception } end context 'with a relative path' do let(:path) { 'relative/path' } it { is_expected.to raise_exception } end end context 'on windows platforms' do before(:each) do Puppet.features.stub(:posix?) { false } Puppet.features.stub(:microsoft_windows?) { true } Puppet::Util::Platform.stub(:windows?) { true } end context 'with an absolute path with front slashes' do let(:path) { 'c:/absolute/path' } it { is_expected.not_to raise_exception } end context 'with a relative path with back slashes' do let(:path) { 'relative\path' } it { is_expected.to raise_exception } end end end [true, false].product([true, false, :md5]).each do |cfg, param| describe "when Puppet[:show_diff] is #{cfg} and show_diff => #{param}" do before(:each) do Puppet[:show_diff] = cfg end let(:value) { described_class.new(name: 'foo', value: 'whatever', show_diff: param).property(:value) } if cfg && param == true it 'displays diff' do expect(value.change_to_s('not_secret', 'at_all')).to include('not_secret', 'at_all') end it 'tells current value' do expect(value.is_to_s('not_secret_at_all')).to eq('not_secret_at_all') end it 'tells new value' do expect(value.should_to_s('not_secret_at_all')).to eq('not_secret_at_all') end elsif cfg && param == :md5 it 'tells correct md5 hashes for multiple values' do expect(value.change_to_s('not_secret', 'at_all')).to include('e9e8db547f8960ef32dbc34029735564', '46cd73a9509ba78c39f05faf078a8cbe') end it 'does not tell singular value one' do expect(value.change_to_s('not_secret', 'at_all')).not_to include('not_secret') end it 'does not tell singular value two' do expect(value.change_to_s('not_secret', 'at_all')).not_to include('at_all') end it 'tells md5 of current value' do expect(value.is_to_s('not_secret_at_all')).to eq('{md5}218fde79f501b8ab8d212f1059bb857f') end it 'does not tell the current value' do expect(value.is_to_s('not_secret_at_all')).not_to include('not_secret_at_all') end it 'tells md5 of new value' do expect(value.should_to_s('not_secret_at_all')).not_to include('not_secret_at_all') end it 'does not tell the new value' do expect(value.should_to_s('not_secret_at_all')).to eq('{md5}218fde79f501b8ab8d212f1059bb857f') end else it 'tells redaction warning in place of actual values' do expect(value.change_to_s('not_at', 'all_secret')).to include('[redacted sensitive information]') end it 'does not tell actual value one' do expect(value.change_to_s('not_at', 'all_secret')).not_to include('not_at') end it 'does not tell actual value two' do expect(value.change_to_s('not_at', 'all_secret')).not_to include('all_secret') end it 'tells redaction warning in place of current value' do expect(value.is_to_s('not_at_all_secret')).to eq('[redacted sensitive information]') end it 'does not tell current value' do expect(value.is_to_s('not_at_all_secret')).not_to include('not_at_all_secret') end it 'tells redaction warning in place of new value' do expect(value.should_to_s('not_at_all_secret')).to eq('[redacted sensitive information]') end it 'does not tell new value' do expect(value.should_to_s('not_at_all_secret')).not_to include('not_at_all_secret') end end end end end diff --git a/spec/unit/puppet/util/external_iterator_spec.rb b/spec/unit/puppet/util/external_iterator_spec.rb index e19e8a3..4773d83 100644 --- a/spec/unit/puppet/util/external_iterator_spec.rb +++ b/spec/unit/puppet/util/external_iterator_spec.rb @@ -1,36 +1,38 @@ +# frozen_string_literal: true + require 'spec_helper' require 'puppet/util/external_iterator' describe Puppet::Util::ExternalIterator do subject_class = nil expected_values = nil before(:each) do subject_class = described_class.new(['a', 'b', 'c']) expected_values = [['a', 0], ['b', 1], ['c', 2]] end context '#next' do it 'iterates over the items' do expected_values.each do |expected_pair| expect(subject_class.next).to eq(expected_pair) end end end context '#peek' do it 'returns the 0th item repeatedly' do (0..2).each do |_i| expect(subject_class.peek).to eq(expected_values[0]) end end it 'does not advance the iterator, but should reflect calls to #next' do expected_values.each do |expected_pair| expect(subject_class.peek).to eq(expected_pair) expect(subject_class.peek).to eq(expected_pair) expect(subject_class.next).to eq(expected_pair) end end end end diff --git a/spec/unit/puppet/util/ini_file_spec.rb b/spec/unit/puppet/util/ini_file_spec.rb index bb4ceda..abf4a25 100644 --- a/spec/unit/puppet/util/ini_file_spec.rb +++ b/spec/unit/puppet/util/ini_file_spec.rb @@ -1,316 +1,317 @@ +# frozen_string_literal: true + require 'spec_helper' require 'stringio' require 'puppet/util/ini_file' describe Puppet::Util::IniFile do subject(:ini_sub) { described_class.new('/my/ini/file/path') } before :each do allow(File).to receive(:file?).with('/my/ini/file/path') { true } allow(described_class).to receive(:readlines).once.with('/my/ini/file/path') do sample_content end end context 'when parsing a file' do let(:sample_content) do template = <<-EOS # This is a comment [section1] ; This is also a comment foo=foovalue bar = barvalue baz = [section2] foo= foovalue2 baz=bazvalue ; commented = out setting #another comment ; yet another comment zot = multi word value xyzzy['thing1']['thing2']=xyzzyvalue l=git log EOS template.split("\n") end it 'parses the correct number of sections' do # there is always a "global" section, so our count should be 3. expect(ini_sub.section_names.length).to eq(3) end it 'parses the correct section_names' do # there should always be a "global" section named "" at the beginning of the list expect(ini_sub.section_names).to eq(['', 'section1', 'section2']) end it 'exposes settings for sections #section1' do expect(ini_sub.get_settings('section1')).to eq('bar' => 'barvalue', 'baz' => '', 'foo' => 'foovalue') end it 'exposes settings for sections #section2' do expect(ini_sub.get_settings('section2')).to eq('baz' => 'bazvalue', 'foo' => 'foovalue2', 'l' => 'git log', "xyzzy['thing1']['thing2']" => 'xyzzyvalue', 'zot' => 'multi word value') end end context 'when parsing a file whose first line is a section' do let(:sample_content) do template = <<-EOS [section1] ; This is a comment foo=foovalue EOS template.split("\n") end it 'parses the correct number of sections' do # there is always a "global" section, so our count should be 2. expect(ini_sub.section_names.length).to eq(2) end it 'parses the correct section_names' do # there should always be a "global" section named "" at the beginning of the list expect(ini_sub.section_names).to eq(['', 'section1']) end it 'exposes settings for sections' do expect(ini_sub.get_value('section1', 'foo')).to eq('foovalue') end end context "when parsing a file with a 'global' section" do let(:sample_content) do template = <<-EOS foo = bar [section1] ; This is a comment foo=foovalue EOS template.split("\n") end it 'parses the correct number of sections' do # there is always a "global" section, so our count should be 2. expect(ini_sub.section_names.length).to eq(2) end it 'parses the correct section_names' do # there should always be a "global" section named "" at the beginning of the list expect(ini_sub.section_names).to eq(['', 'section1']) end it 'exposes settings for sections #bar' do expect(ini_sub.get_value('', 'foo')).to eq('bar') end it 'exposes settings for sections #foovalue' do expect(ini_sub.get_value('section1', 'foo')).to eq('foovalue') end end context 'when updating a file with existing empty values' do let(:sample_content) do template = <<-EOS [section1] foo= #bar= #xyzzy['thing1']['thing2']='xyzzyvalue' EOS template.split("\n") end - # rubocop:disable RSpec/ExpectInHook before :each do expect(ini_sub.get_value('section1', 'far')).to eq(nil) expect(ini_sub.get_value('section1', 'bar')).to eq(nil) expect(ini_sub.get_value('section1', "xyzzy['thing1']['thing2']")).to eq(nil) end # rubocop:enable RSpec/ExpectInHook it 'properlies update uncommented values' do ini_sub.set_value('section1', 'foo', ' = ', 'foovalue') expect(ini_sub.get_value('section1', 'foo')).to eq('foovalue') end it 'properlies update uncommented values without separator' do ini_sub.set_value('section1', 'foo', 'foovalue') expect(ini_sub.get_value('section1', 'foo')).to eq('foovalue') end it 'properlies update commented value' do ini_sub.set_value('section1', 'bar', ' = ', 'barvalue') expect(ini_sub.get_value('section1', 'bar')).to eq('barvalue') end it 'properlies update commented values' do ini_sub.set_value('section1', "xyzzy['thing1']['thing2']", ' = ', 'xyzzyvalue') expect(ini_sub.get_value('section1', "xyzzy['thing1']['thing2']")).to eq('xyzzyvalue') end it 'properlies update commented value without separator' do ini_sub.set_value('section1', 'bar', 'barvalue') expect(ini_sub.get_value('section1', 'bar')).to eq('barvalue') end it 'properlies update commented values without separator' do ini_sub.set_value('section1', "xyzzy['thing1']['thing2']", 'xyzzyvalue') expect(ini_sub.get_value('section1', "xyzzy['thing1']['thing2']")).to eq('xyzzyvalue') end it 'properlies add new empty values' do ini_sub.set_value('section1', 'baz', ' = ', 'bazvalue') expect(ini_sub.get_value('section1', 'baz')).to eq('bazvalue') end it 'adds new empty values without separator' do ini_sub.set_value('section1', 'baz', 'bazvalue') expect(ini_sub.get_value('section1', 'baz')).to eq('bazvalue') end end context 'the file has quotation marks in its section names' do let(:sample_content) do template = <<-EOS [branch "main"] remote = origin merge = refs/heads/main [alias] to-deploy = log --merges --grep='pull request' --format='%s (%cN)' origin/production..origin/main [branch "production"] remote = origin merge = refs/heads/production EOS template.split("\n") end it 'parses the sections' do expect(ini_sub.section_names).to match_array ['', 'branch "main"', 'alias', 'branch "production"'] end end context 'Samba INI file with dollars in section names' do let(:sample_content) do template = <<-EOS [global] workgroup = FELLOWSHIP ; ... idmap config * : backend = tdb [printers] comment = All Printers ; ... browseable = No [print$] comment = Printer Drivers path = /var/lib/samba/printers [Shares] path = /home/shares read only = No guest ok = Yes EOS template.split("\n") end it 'parses the correct section_names' do expect(ini_sub.section_names).to match_array ['', 'global', 'printers', 'print$', 'Shares'] end end context 'section names with forward slashes in them' do let(:sample_content) do template = <<-EOS [monitor:///var/log/*.log] disabled = test_value EOS template.split("\n") end it 'parses the correct section_names' do expect(ini_sub.section_names).to match_array [ '', 'monitor:///var/log/*.log', ] end end context 'KDE Configuration with braces in setting names' do let(:sample_content) do template = <<-EOS [khotkeys] _k_friendly_name=khotkeys {5465e8c7-d608-4493-a48f-b99d99fdb508}=Print,none,PrintScreen {d03619b6-9b3c-48cc-9d9c-a2aadb485550}=Search,none,Search EOS template.split("\n") end it 'exposes settings for sections #print' do expect(ini_sub.get_value('khotkeys', '{5465e8c7-d608-4493-a48f-b99d99fdb508}')).to eq('Print,none,PrintScreen') end it 'exposes settings for sections #search' do expect(ini_sub.get_value('khotkeys', '{d03619b6-9b3c-48cc-9d9c-a2aadb485550}')).to eq('Search,none,Search') end end context 'Configuration with colons in setting names' do let(:sample_content) do template = <<-EOS [Drive names] A:=5.25" Floppy B:=3.5" Floppy C:=Winchester EOS template.split("\n") end it 'exposes settings for sections #A' do expect(ini_sub.get_value('Drive names', 'A:')).to eq '5.25" Floppy' end it 'exposes settings for sections #B' do expect(ini_sub.get_value('Drive names', 'B:')).to eq '3.5" Floppy' end it 'exposes settings for sections #C' do expect(ini_sub.get_value('Drive names', 'C:')).to eq 'Winchester' end end context 'Configuration with spaces in setting names' do let(:sample_content) do template = <<-EOS [global] # log files split per-machine: log file = /var/log/samba/log.%m kerberos method = system keytab passdb backend = tdbsam security = ads EOS template.split("\n") end it 'exposes settings for sections #log' do expect(ini_sub.get_value('global', 'log file')).to eq '/var/log/samba/log.%m' end it 'exposes settings for sections #kerberos' do expect(ini_sub.get_value('global', 'kerberos method')).to eq 'system keytab' end it 'exposes settings for sections #passdb' do expect(ini_sub.get_value('global', 'passdb backend')).to eq 'tdbsam' end it 'exposes settings for sections #security' do expect(ini_sub.get_value('global', 'security')).to eq 'ads' end end end diff --git a/spec/unit/puppet/util/setting_value_spec.rb b/spec/unit/puppet/util/setting_value_spec.rb index e2937db..ce3847e 100644 --- a/spec/unit/puppet/util/setting_value_spec.rb +++ b/spec/unit/puppet/util/setting_value_spec.rb @@ -1,120 +1,122 @@ +# frozen_string_literal: true + require 'spec_helper' require 'puppet/util/setting_value' describe Puppet::Util::SettingValue do describe 'space subsetting separator' do - INIT_VALUE_SPACE = '"-Xmx192m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/pe-puppetdb/puppetdb-oom.hprof"'.freeze + INIT_VALUE_SPACE = '"-Xmx192m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/pe-puppetdb/puppetdb-oom.hprof"' let(:setting_value) { described_class.new(INIT_VALUE_SPACE, ' ') } it 'gets the original value' do expect(setting_value.get_value).to eq(INIT_VALUE_SPACE) end it 'gets the correct value' do expect(setting_value.get_subsetting_value('-Xmx')).to eq('192m') end it 'adds a new value #correct' do setting_value.add_subsetting('-Xms', '256m') expect(setting_value.get_subsetting_value('-Xms')).to eq('256m') end it 'adds a new value #original' do setting_value.add_subsetting('-Xms', '256m') expect(setting_value.get_value).to eq(INIT_VALUE_SPACE[0, INIT_VALUE_SPACE.length - 1] + ' -Xms256m"') end it 'changes existing value' do setting_value.add_subsetting('-Xmx', '512m') expect(setting_value.get_subsetting_value('-Xmx')).to eq('512m') end it 'removes existing value' do setting_value.remove_subsetting('-Xmx') expect(setting_value.get_subsetting_value('-Xmx')).to eq(nil) end end describe 'comma subsetting separator' do - INIT_VALUE_COMMA = '"-Xmx192m,-XX:+HeapDumpOnOutOfMemoryError,-XX:HeapDumpPath=/var/log/pe-puppetdb/puppetdb-oom.hprof"'.freeze + INIT_VALUE_COMMA = '"-Xmx192m,-XX:+HeapDumpOnOutOfMemoryError,-XX:HeapDumpPath=/var/log/pe-puppetdb/puppetdb-oom.hprof"' let(:setting_value) { described_class.new(INIT_VALUE_COMMA, ',') } it 'gets the original value' do expect(setting_value.get_value).to eq(INIT_VALUE_COMMA) end it 'gets the correct value' do expect(setting_value.get_subsetting_value('-Xmx')).to eq('192m') end it 'adds a new value #actual' do setting_value.add_subsetting('-Xms', '256m') expect(setting_value.get_subsetting_value('-Xms')).to eq('256m') end it 'adds a new value #original' do setting_value.add_subsetting('-Xms', '256m') expect(setting_value.get_value).to eq(INIT_VALUE_COMMA[0, INIT_VALUE_COMMA.length - 1] + ',-Xms256m"') end it 'changes existing value' do setting_value.add_subsetting('-Xmx', '512m') expect(setting_value.get_subsetting_value('-Xmx')).to eq('512m') end it 'removes existing value' do setting_value.remove_subsetting('-Xmx') expect(setting_value.get_subsetting_value('-Xmx')).to eq(nil) end end describe 'quote_char parameter' do - QUOTE_CHAR = '"'.freeze - INIT_VALUE_UNQUOTED = '-Xmx192m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/pe-puppetdb/puppetdb-oom.hprof'.freeze + QUOTE_CHAR = '"' + INIT_VALUE_UNQUOTED = '-Xmx192m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/pe-puppetdb/puppetdb-oom.hprof' it 'gets quoted empty string if original value was empty' do setting_value = described_class.new(nil, ' ', QUOTE_CHAR) expect(setting_value.get_value).to eq(QUOTE_CHAR * 2) end it 'quotes the setting when adding a value #actual' do setting_value = described_class.new(INIT_VALUE_UNQUOTED, ' ', QUOTE_CHAR) setting_value.add_subsetting('-Xms', '256m') expect(setting_value.get_subsetting_value('-Xms')).to eq('256m') end it 'quotes the setting when adding a value #original' do setting_value = described_class.new(INIT_VALUE_UNQUOTED, ' ', QUOTE_CHAR) setting_value.add_subsetting('-Xms', '256m') expect(setting_value.get_value).to eq(QUOTE_CHAR + INIT_VALUE_UNQUOTED + ' -Xms256m' + QUOTE_CHAR) end it 'quotes the setting when changing an existing value #value' do setting_value = described_class.new(INIT_VALUE_UNQUOTED, ' ', QUOTE_CHAR) setting_value.add_subsetting('-Xmx', '512m') expect(setting_value.get_subsetting_value('-Xmx')).to eq('512m') end it 'quotes the setting when changing an existing value #quotes' do setting_value = described_class.new(INIT_VALUE_UNQUOTED, ' ', QUOTE_CHAR) setting_value.add_subsetting('-Xmx', '512m') expect(setting_value.get_value).to match(%r{^#{Regexp.quote(QUOTE_CHAR)}.*#{Regexp.quote(QUOTE_CHAR)}$}) end it 'quotes the setting when removing an existing value #value' do setting_value = described_class.new(INIT_VALUE_UNQUOTED, ' ', QUOTE_CHAR) setting_value.remove_subsetting('-Xmx') expect(setting_value.get_subsetting_value('-Xmx')).to eq(nil) end end it 'quotes the setting when removing an existing value #quotes' do setting_value = described_class.new(INIT_VALUE_UNQUOTED, ' ', QUOTE_CHAR) setting_value.remove_subsetting('-Xmx') expect(setting_value.get_value).to match(%r{^#{Regexp.quote(QUOTE_CHAR)}.*#{Regexp.quote(QUOTE_CHAR)}$}) end end