diff --git a/.github/workflows/auto_release.yml b/.github/workflows/auto_release.yml index a88b99c..d15d148 100644 --- a/.github/workflows/auto_release.yml +++ b/.github/workflows/auto_release.yml @@ -1,81 +1,86 @@ name: "Auto release" on: schedule: - cron: '0 3 * * 6' workflow_dispatch: env: HONEYCOMB_WRITEKEY: 7f3c63a70eecc61d635917de46bea4e6 HONEYCOMB_DATASET: litmus tests CHANGELOG_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} jobs: auto_release: name: "Automatic release prep" runs-on: ubuntu-20.04 steps: - name: "Honeycomb: Start recording" uses: puppetlabs/kvrhdn-gha-buildevents@pdk-templates-v1 with: apikey: ${{ env.HONEYCOMB_WRITEKEY }} dataset: ${{ env.HONEYCOMB_DATASET }} job-status: ${{ job.status }} - name: "Honeycomb: start first step" run: | echo STEP_ID="auto-release" >> $GITHUB_ENV echo STEP_START=$(date +%s) >> $GITHUB_ENV - name: "Checkout Source" if: ${{ github.repository_owner == 'puppetlabs' }} uses: actions/checkout@v2 with: fetch-depth: 0 persist-credentials: false - name: "PDK Release prep" - uses: docker://puppet/pdk:nightly + uses: docker://puppet/iac_release:ci with: args: 'release prep --force' env: CHANGELOG_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: "Get Version" if: ${{ github.repository_owner == 'puppetlabs' }} id: gv run: | - echo "::set-output name=ver::$(cat metadata.json | jq .version | tr -d \")" + echo "::set-output name=ver::$(jq --raw-output .version metadata.json)" - name: "Commit changes" if: ${{ github.repository_owner == 'puppetlabs' }} run: | - git config --local user.email "action@github.com" + git config --local user.email "${{ github.repository_owner }}@users.noreply.github.com" git config --local user.name "GitHub Action" git add . git commit -m "Release prep v${{ steps.gv.outputs.ver }}" - name: Create Pull Request id: cpr uses: puppetlabs/peter-evans-create-pull-request@v3 if: ${{ github.repository_owner == 'puppetlabs' }} with: token: ${{ secrets.GITHUB_TOKEN }} commit-message: "Release prep v${{ steps.gv.outputs.ver }}" branch: "release-prep" delete-branch: true title: "Release prep v${{ steps.gv.outputs.ver }}" - body: "Automated release-prep through [pdk-templates](https://github.com/puppetlabs/pdk-templates/blob/main/moduleroot/.github/workflows/auto_release.yml.erb)" + body: | + Automated release-prep through [pdk-templates](https://github.com/puppetlabs/pdk-templates/blob/main/moduleroot/.github/workflows/auto_release.yml.erb) from commit ${{ github.sha }}. + Please verify before merging: + - [ ] last [nightly](https://github.com/${{ github.repository }}/actions/workflows/nightly.yml) run is green + - [ ] [Changelog](https://github.com/${{ github.repository }}/blob/release-prep/CHANGELOG.md) is readable and has no unlabeled pull requests + - [ ] Ensure the [changelog](https://github.com/${{ github.repository }}/blob/release-prep/CHANGELOG.md) version and [metadata](https://github.com/${{ github.repository }}/blob/release-prep/metadata.json) version match labels: "maintenance" - name: PR outputs if: ${{ github.repository_owner == 'puppetlabs' }} run: | echo "Pull Request Number - ${{ steps.cpr.outputs.pull-request-number }}" echo "Pull Request URL - ${{ steps.cpr.outputs.pull-request-url }}" - name: "Honeycomb: Record finish step" if: ${{ always() }} run: | buildevents step $TRACE_ID $STEP_ID $STEP_START 'Finished auto release workflow' diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 0c1fd28..865578c 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -1,194 +1,204 @@ 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: puppetlabs/kvrhdn-gha-buildevents@pdk-templates-v1 with: apikey: ${{ env.HONEYCOMB_WRITEKEY }} dataset: ${{ env.HONEYCOMB_DATASET }} job-status: ${{ job.status }} - name: "Honeycomb: Start first step" run: | echo STEP_ID=setup-environment >> $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: ruby/setup-ruby@v1 if: ${{ github.repository_owner == 'puppetlabs' }} with: ruby-version: "2.7" bundler-cache: true - name: Print bundle environment if: ${{ github.repository_owner == 'puppetlabs' }} run: | echo ::group::bundler environment buildevents cmd $TRACE_ID $STEP_ID 'bundle env' -- bundle env echo ::endgroup:: - name: "Honeycomb: Record Setup Environment time" if: ${{ github.repository_owner == 'puppetlabs' }} run: | buildevents step $TRACE_ID $STEP_ID $STEP_START 'Setup Environment' echo STEP_ID=Setup-Acceptance-Test-Matrix >> $GITHUB_ENV echo STEP_START=$(date +%s) >> $GITHUB_ENV - 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 + buildevents cmd $TRACE_ID $STEP_ID matrix_from_metadata -- bundle exec matrix_from_metadata_v2 else echo "::set-output name=matrix::{}" fi - name: "Honeycomb: Record Setup Test Matrix time" if: ${{ always() }} run: | buildevents step $TRACE_ID $STEP_ID $STEP_START 'Setup Test Matrix' Acceptance: + name: "${{matrix.platforms.label}}, ${{matrix.collection}}" 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 'platform=${{ matrix.platforms.image }}' >> $BUILDEVENT_FILE echo 'collection=${{ matrix.collection }}' >> $BUILDEVENT_FILE + echo 'label=${{ matrix.platforms.label }}' >> $BUILDEVENT_FILE + - name: "Honeycomb: Start recording" uses: puppetlabs/kvrhdn-gha-buildevents@pdk-templates-v1 with: apikey: ${{ env.HONEYCOMB_WRITEKEY }} dataset: ${{ env.HONEYCOMB_DATASET }} job-status: ${{ job.status }} - matrix-key: ${{ matrix.platform }}-${{ matrix.collection }} + matrix-key: ${{ matrix.platforms.label }}-${{ matrix.collection }} - name: "Honeycomb: start first step" run: | - echo STEP_ID=${{ matrix.platform }}-${{ matrix.collection }}-1 >> $GITHUB_ENV + echo STEP_ID=${{ matrix.platforms.image }}-${{ 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: ruby/setup-ruby@v1 with: ruby-version: "2.7" bundler-cache: true - name: Print bundle environment run: | echo ::group::bundler environment buildevents cmd $TRACE_ID $STEP_ID 'bundle env' -- bundle env echo ::endgroup:: - name: "Honeycomb: Record Setup Environment time" if: ${{ always() }} run: | buildevents step $TRACE_ID $STEP_ID $STEP_START 'Setup Environment' - echo STEP_ID=${{ matrix.platform }}-${{ matrix.collection }}-2 >> $GITHUB_ENV + echo STEP_ID=${{ matrix.platforms.image }}-${{ matrix.collection }}-2 >> $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 }}]' + buildevents cmd $TRACE_ID $STEP_ID 'rake litmus:provision ${{ matrix.platforms.image }}' -- bundle exec rake 'litmus:provision[${{matrix.platforms.provider}},${{ matrix.platforms.image }}]' echo ::group::=== REQUEST === cat request.json || true echo echo ::endgroup:: echo ::group::=== INVENTORY === - sed -e 's/password: .*/password: "[redacted]"/' < inventory.yaml || true + if [ -f 'spec/fixtures/litmus_inventory.yaml' ]; + then + FILE='spec/fixtures/litmus_inventory.yaml' + elif [ -f 'inventory.yaml' ]; + then + FILE='inventory.yaml' + fi + sed -e 's/password: .*/password: "[redacted]"/' < $FILE || 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 }}-3 >> $GITHUB_ENV + echo STEP_ID=${{ matrix.platforms.image }}-${{ matrix.collection }}-3 >> $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 }}-4 >> $GITHUB_ENV + echo STEP_ID=${{ matrix.platforms.image }}-${{ matrix.collection }}-4 >> $GITHUB_ENV echo STEP_START=$(date +%s) >> $GITHUB_ENV - name: Remove test environment if: ${{ always() }} continue-on-error: true run: | - if [ -f inventory.yaml ]; then + if [[ -f inventory.yaml || -f spec/fixtures/litmus_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: puppetlabs/Gamesight-slack-workflow-status@pdk-templates-v1 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 207dfca..69e414b 100644 --- a/.github/workflows/pr_test.yml +++ b/.github/workflows/pr_test.yml @@ -1,175 +1,184 @@ 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: puppetlabs/kvrhdn-gha-buildevents@pdk-templates-v1 with: apikey: ${{ env.HONEYCOMB_WRITEKEY }} dataset: ${{ env.HONEYCOMB_DATASET }} job-status: ${{ job.status }} - name: "Honeycomb: Start first step" run: | echo STEP_ID=setup-environment >> $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: ruby/setup-ruby@v1 if: ${{ github.repository_owner == 'puppetlabs' }} with: ruby-version: "2.7" bundler-cache: true - name: Print bundle environment if: ${{ github.repository_owner == 'puppetlabs' }} run: | echo ::group::bundler environment buildevents cmd $TRACE_ID $STEP_ID 'bundle env' -- bundle env echo ::endgroup:: - name: "Honeycomb: Record Setup Environment time" if: ${{ github.repository_owner == 'puppetlabs' }} run: | buildevents step $TRACE_ID $STEP_ID $STEP_START 'Setup Environment' echo STEP_ID=Setup-Acceptance-Test-Matrix >> $GITHUB_ENV echo STEP_START=$(date +%s) >> $GITHUB_ENV - name: Setup Acceptance Test Matrix id: get-matrix run: | if [ '${{ github.repository_owner }}' == 'puppetlabs' ]; then - buildevents cmd $TRACE_ID $STEP_ID matrix_from_metadata -- bundle exec matrix_from_metadata + buildevents cmd $TRACE_ID $STEP_ID matrix_from_metadata -- bundle exec matrix_from_metadata_v2 else echo "::set-output name=matrix::{}" fi - name: "Honeycomb: Record Setup Test Matrix time" if: ${{ always() }} run: | buildevents step $TRACE_ID $STEP_ID $STEP_START 'Setup Test Matrix' Acceptance: + name: "${{matrix.platforms.label}}, ${{matrix.collection}}" needs: - setup_matrix if: ${{ needs.setup_matrix.outputs.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 'platform=${{ matrix.platforms.image }}' >> $BUILDEVENT_FILE echo 'collection=${{ matrix.collection }}' >> $BUILDEVENT_FILE + echo 'label=${{ matrix.platforms.label }}' >> $BUILDEVENT_FILE - name: "Honeycomb: Start recording" uses: puppetlabs/kvrhdn-gha-buildevents@pdk-templates-v1 with: apikey: ${{ env.HONEYCOMB_WRITEKEY }} dataset: ${{ env.HONEYCOMB_DATASET }} job-status: ${{ job.status }} - matrix-key: ${{ matrix.platform }}-${{ matrix.collection }} + matrix-key: ${{ matrix.platforms.label }}-${{ matrix.collection }} - name: "Honeycomb: start first step" run: | - echo STEP_ID=${{ matrix.platform }}-${{ matrix.collection }}-1 >> $GITHUB_ENV + echo STEP_ID=${{ matrix.platforms.image }}-${{ 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: ruby/setup-ruby@v1 with: ruby-version: "2.7" bundler-cache: true - name: Print bundle environment run: | echo ::group::bundler environment buildevents cmd $TRACE_ID $STEP_ID 'bundle env' -- bundle env echo ::endgroup:: - name: "Honeycomb: Record Setup Environment time" if: ${{ always() }} run: | buildevents step $TRACE_ID $STEP_ID $STEP_START 'Setup Environment' - echo STEP_ID=${{ matrix.platform }}-${{ matrix.collection }}-2 >> $GITHUB_ENV + echo STEP_ID=${{ matrix.platforms.image }}-${{ matrix.collection }}-2 >> $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 }}]' + buildevents cmd $TRACE_ID $STEP_ID 'rake litmus:provision ${{ matrix.platforms.image }}' -- bundle exec rake 'litmus:provision[${{matrix.platforms.provider}},${{ matrix.platforms.image }}]' echo ::group::=== REQUEST === cat request.json || true echo echo ::endgroup:: echo ::group::=== INVENTORY === - sed -e 's/password: .*/password: "[redacted]"/' < inventory.yaml || true + if [ -f 'spec/fixtures/litmus_inventory.yaml' ]; + then + FILE='spec/fixtures/litmus_inventory.yaml' + elif [ -f 'inventory.yaml' ]; + then + FILE='inventory.yaml' + fi + sed -e 's/password: .*/password: "[redacted]"/' < $FILE || 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 }}-3 >> $GITHUB_ENV + echo STEP_ID=${{ matrix.platforms.image }}-${{ matrix.collection }}-3 >> $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 }}-4 >> $GITHUB_ENV + echo STEP_ID=${{ matrix.platforms.image }}-${{ matrix.collection }}-4 >> $GITHUB_ENV echo STEP_START=$(date +%s) >> $GITHUB_ENV - name: Remove test environment if: ${{ always() }} continue-on-error: true run: | - if [ -f inventory.yaml ]; then + if [[ -f inventory.yaml || -f spec/fixtures/litmus_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/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..76cf31c --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,47 @@ +name: "Publish module" + +on: + workflow_dispatch: + +jobs: + create-github-release: + name: Deploy GitHub Release + runs-on: ubuntu-20.04 + steps: + - name: Checkout code + uses: actions/checkout@v2 + with: + ref: ${{ github.ref }} + clean: true + fetch-depth: 0 + - name: Get Version + id: gv + run: | + echo "::set-output name=ver::$(jq --raw-output .version metadata.json)" + - name: Create Release + uses: actions/create-release@v1 + id: create_release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: "v${{ steps.gv.outputs.ver }}" + draft: false + prerelease: false + + deploy-forge: + name: Deploy to Forge + runs-on: ubuntu-20.04 + steps: + - name: Checkout code + uses: actions/checkout@v2 + with: + ref: ${{ github.ref }} + clean: true + - name: "PDK Build" + uses: docker://puppet/pdk:2.1.0.0 + with: + args: 'build' + - name: "Push to Forge" + uses: docker://puppet/pdk:2.1.0.0 + with: + args: 'release publish --forge-token ${{ secrets.FORGE_API_KEY }} --force' diff --git a/.github/workflows/spec.yml b/.github/workflows/spec.yml new file mode 100644 index 0000000..03e2cb8 --- /dev/null +++ b/.github/workflows/spec.yml @@ -0,0 +1,128 @@ +name: "Spec Tests" + +on: + schedule: + - cron: '0 0 * * *' + workflow_dispatch: + pull_request: + +env: + HONEYCOMB_WRITEKEY: 7f3c63a70eecc61d635917de46bea4e6 + HONEYCOMB_DATASET: litmus tests + +jobs: + setup_matrix: + name: "Setup Test Matrix" + runs-on: ubuntu-20.04 + outputs: + spec_matrix: ${{ steps.get-matrix.outputs.spec_matrix }} + + steps: + - name: "Honeycomb: Start recording" + uses: puppetlabs/kvrhdn-gha-buildevents@pdk-templates-v1 + with: + apikey: ${{ env.HONEYCOMB_WRITEKEY }} + dataset: ${{ env.HONEYCOMB_DATASET }} + job-status: ${{ job.status }} + + - name: "Honeycomb: Start first step" + run: | + echo STEP_ID=setup-environment >> $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: ruby/setup-ruby@v1 + if: ${{ github.repository_owner == 'puppetlabs' }} + with: + ruby-version: "2.7" + bundler-cache: true + + - name: Print bundle environment + if: ${{ github.repository_owner == 'puppetlabs' }} + run: | + echo ::group::bundler environment + buildevents cmd $TRACE_ID $STEP_ID 'bundle env' -- bundle env + echo ::endgroup:: + + - name: "Honeycomb: Record Setup Environment time" + if: ${{ github.repository_owner == 'puppetlabs' }} + run: | + buildevents step $TRACE_ID $STEP_ID $STEP_START 'Setup Environment' + echo STEP_ID=Setup-Acceptance-Test-Matrix >> $GITHUB_ENV + echo STEP_START=$(date +%s) >> $GITHUB_ENV + + - name: Setup Spec Test Matrix + id: get-matrix + run: | + if [ '${{ github.repository_owner }}' == 'puppetlabs' ]; then + buildevents cmd $TRACE_ID $STEP_ID matrix_from_metadata -- bundle exec matrix_from_metadata_v2 + else + echo "::set-output name=spec_matrix::{}" + fi + + - name: "Honeycomb: Record Setup Test Matrix time" + if: ${{ always() }} + run: | + buildevents step $TRACE_ID $STEP_ID $STEP_START 'Setup Test Matrix' + + Spec: + name: "Spec Tests (Puppet: ${{matrix.puppet_version}}, Ruby Ver: ${{matrix.ruby_version}})" + needs: + - setup_matrix + if: ${{ needs.setup_matrix.outputs.spec_matrix != '{}' }} + + runs-on: ubuntu-20.04 + strategy: + fail-fast: false + matrix: ${{fromJson(needs.setup_matrix.outputs.spec_matrix)}} + + env: + BUILDEVENT_FILE: '../buildevents.txt' + PUPPET_GEM_VERSION: ${{ matrix.puppet_version }} + + steps: + - run: | + echo "SANITIZED_PUPPET_VERSION=$(echo '${{ matrix.puppet_version }}' | sed 's/~> //g')" >> $GITHUB_ENV + + - run: | + echo 'puppet_version=${{ env.SANITIZED_PUPPET_VERSION }}' >> $BUILDEVENT_FILE + + - name: "Honeycomb: Start first step" + run: | + echo "STEP_ID=${{ env.SANITIZED_PUPPET_VERSION }}-spec" >> $GITHUB_ENV + echo STEP_START=$(date +%s) >> $GITHUB_ENV + + - name: "Honeycomb: Start recording" + uses: puppetlabs/kvrhdn-gha-buildevents@pdk-templates-v1 + with: + apikey: ${{ env.HONEYCOMB_WRITEKEY }} + dataset: ${{ env.HONEYCOMB_DATASET }} + job-status: ${{ job.status }} + matrix-key: ${{ env.SANITIZED_PUPPET_VERSION }} + + - name: Checkout Source + uses: actions/checkout@v2 + + - name: "Activate Ruby ${{ matrix.ruby_version }}" + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{matrix.ruby_version}} + bundler-cache: true + + - name: Print bundle environment + run: | + echo ::group::bundler environment + buildevents cmd $TRACE_ID $STEP_ID 'bundle env' -- bundle env + echo ::endgroup:: + + - name: Run Static & Syntax Tests + run: | + buildevents cmd $TRACE_ID $STEP_ID 'static_syntax_checks Puppet ${{ matrix.puppet_version }}, Ruby ${{ matrix.ruby_version }}' -- bundle exec rake syntax lint metadata_lint check:symlinks check:git_ignore check:dot_underscore check:test_file rubocop + + - name: Run parallel_spec tests + run: | + buildevents cmd $TRACE_ID $STEP_ID 'rake parallel_spec Puppet ${{ matrix.puppet_version }}, Ruby ${{ matrix.ruby_version }}' -- bundle exec rake parallel_spec diff --git a/.gitignore b/.gitignore index 2767022..988dcbb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,27 +1,28 @@ .git/ .*.sw[op] .metadata .yardoc .yardwarns *.iml /.bundle/ /.idea/ /.vagrant/ /coverage/ /bin/ /doc/ /Gemfile.local /Gemfile.lock /junit/ /log/ /pkg/ /spec/fixtures/manifests/ /spec/fixtures/modules/ /tmp/ /vendor/ /convert_report.txt /update_report.txt .DS_Store .project .envrc /inventory.yaml +/spec/fixtures/litmus_inventory.yaml diff --git a/.gitpod.yml b/.gitpod.yml index 18406c5..9d89d9f 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -1,9 +1,9 @@ image: file: .gitpod.Dockerfile tasks: - init: pdk bundle install vscode: extensions: - - puppet.puppet-vscode@1.0.0:oSzfTkDf6Cmc1jOjgW33VA== + - puppet.puppet-vscode@1.2.0:f5iEPbmOj6FoFTOV6q8LTg== diff --git a/.pdkignore b/.pdkignore index 254808c..33a1347 100644 --- a/.pdkignore +++ b/.pdkignore @@ -1,44 +1,46 @@ .git/ .*.sw[op] .metadata .yardoc .yardwarns *.iml /.bundle/ /.idea/ /.vagrant/ /coverage/ /bin/ /doc/ /Gemfile.local /Gemfile.lock /junit/ /log/ /pkg/ /spec/fixtures/manifests/ /spec/fixtures/modules/ /tmp/ /vendor/ /convert_report.txt /update_report.txt .DS_Store .project .envrc /inventory.yaml +/spec/fixtures/litmus_inventory.yaml /appveyor.yml /.fixtures.yml /Gemfile /.gitattributes /.gitignore /.gitlab-ci.yml /.pdkignore /.puppet-lint.rc /Rakefile /rakelib/ /.rspec /.rubocop.yml /.travis.yml /.yardopts /spec/ /.vscode/ /.sync.yml +/.devcontainer/ diff --git a/.sync.yml b/.sync.yml index 1a3cff4..6d40ffb 100644 --- a/.sync.yml +++ b/.sync.yml @@ -1,48 +1,32 @@ --- ".gitlab-ci.yml": delete: true -".travis.yml": - global_env: - - HONEYCOMB_WRITEKEY="7f3c63a70eecc61d635917de46bea4e6",HONEYCOMB_DATASET="litmus tests" - deploy_to_forge: - enabled: false - 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 - simplecov: true - notifications: - slack: - secure: XpBD602OXRZHSTDylzzx/OqpfThEJPbx0PLhXctWuES4GpW1EHWnyPgrliNOaJOh0Zb7qMrdaKWLOltfqPT5IanPd0XF7GbT8RrNeLTmLXqvHmC6dDqWxnvFvdSrGwqpj7s7Dbwl79nmszONRj1OlolPmJgY/2kGw88c71biaas= appveyor.yml: delete: true + Gemfile: optional: ":development": - gem: github_changelog_generator spec/spec_helper.rb: spec_overrides: - require 'spec_helper_local' coverage_report: true "  changelog_user": puppetlabs .gitpod.Dockerfile: unmanaged: false .gitpod.yml: unmanaged: false .github/workflows/nightly.yml: unmanaged: false .github/workflows/pr_test.yml: unmanaged: false .github/workflows/auto_release.yml: unmanaged: false +.github/workflows/spec.yml: + checks: 'syntax lint metadata_lint check:symlinks check:git_ignore check:dot_underscore check:test_file rubocop' + unmanaged: false +.github/workflows/release.yml: + unmanaged: false +.travis.yml: + delete: true diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index baab976..0000000 --- a/.travis.yml +++ /dev/null @@ -1,89 +0,0 @@ ---- -os: linux -dist: xenial -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: - - before_script: - - "bundle exec rake 'litmus:provision_list[travis_ub_6]'" - - "bundle exec rake 'litmus:install_agent[puppet6]'" - - "bundle exec rake litmus:install_module" - env: - PLATFORMS: travis_ub_6_puppet6 - BUNDLE_WITH: system_tests - rvm: 2.5.7 - script: ["travis_wait 45 bundle exec rake litmus:acceptance:parallel"] - services: docker - stage: acceptance - - before_script: - - "bundle exec rake 'litmus:provision_list[travis_deb]'" - - "bundle exec rake 'litmus:install_agent[puppet6]'" - - "bundle exec rake litmus:install_module" - env: - PLATFORMS: travis_deb_puppet6 - BUNDLE_WITH: system_tests - rvm: 2.5.7 - script: ["travis_wait 45 bundle exec rake litmus:acceptance:parallel"] - services: docker - stage: acceptance - - before_script: - - "bundle exec rake 'litmus:provision_list[travis_el7]'" - - "bundle exec rake 'litmus:install_agent[puppet6]'" - - "bundle exec rake litmus:install_module" - env: - PLATFORMS: travis_el7_puppet6 - BUNDLE_WITH: system_tests - rvm: 2.5.7 - script: ["travis_wait 45 bundle exec rake litmus:acceptance:parallel"] - services: docker - stage: acceptance - - before_script: - - "bundle exec rake 'litmus:provision_list[travis_el8]'" - - "bundle exec rake 'litmus:install_agent[puppet6]'" - - "bundle exec rake litmus:install_module" - env: - PLATFORMS: travis_el8_puppet6 - BUNDLE_WITH: system_tests - 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="~> 6.0" CHECK=parallel_spec - rvm: 2.5.7 - stage: spec -branches: - only: - - main - - /^v\d/ - - release -notifications: - email: false - slack: - secure: XpBD602OXRZHSTDylzzx/OqpfThEJPbx0PLhXctWuES4GpW1EHWnyPgrliNOaJOh0Zb7qMrdaKWLOltfqPT5IanPd0XF7GbT8RrNeLTmLXqvHmC6dDqWxnvFvdSrGwqpj7s7Dbwl79nmszONRj1OlolPmJgY/2kGw88c71biaas= diff --git a/Gemfile b/Gemfile index ae2b430..135373d 100644 --- a/Gemfile +++ b/Gemfile @@ -1,73 +1,63 @@ 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 "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 "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/manifests/params.pp b/manifests/params.pp index 0bb1755..5d5247a 100644 --- a/manifests/params.pp +++ b/manifests/params.pp @@ -1,582 +1,582 @@ # @summary # Params class. # # @api private # class mysql::params { $manage_config_file = true $config_file_mode = '0644' $purge_conf_dir = false $restart = false $root_password = 'UNSET' $install_secret_file = '/.mysql_secret' $server_package_ensure = 'present' $server_package_manage = true $server_service_manage = true $server_service_enabled = true $client_package_ensure = 'present' $client_package_manage = true $create_root_user = true $create_root_my_cnf = true $create_root_login_file = false $login_file = undef $exec_path = '' # mysql::bindings $bindings_enable = false $java_package_ensure = 'present' $java_package_provider = undef $perl_package_ensure = 'present' $perl_package_provider = undef $php_package_ensure = 'present' $php_package_provider = undef $python_package_ensure = 'present' $python_package_provider = undef $ruby_package_ensure = 'present' $ruby_package_provider = undef $client_dev_package_ensure = 'present' $client_dev_package_provider = undef $daemon_dev_package_ensure = 'present' $daemon_dev_package_provider = undef $xtrabackup_package_name_default = 'percona-xtrabackup' case $::osfamily { 'RedHat': { case $::operatingsystem { 'Fedora': { if versioncmp($::operatingsystemrelease, '19') >= 0 or $::operatingsystemrelease == 'Rawhide' { $provider = 'mariadb' } else { $provider = 'mysql' } $python_package_name = 'MySQL-python' } 'Amazon': { if versioncmp($::operatingsystemrelease, '2') >= 0 { $provider = 'mariadb' } else { $provider = 'mysql' } } /^(RedHat|CentOS|Scientific|OracleLinux)$/: { if versioncmp($::operatingsystemmajrelease, '7') >= 0 { $provider = 'mariadb' if versioncmp($::operatingsystemmajrelease, '8') >= 0 { $xtrabackup_package_name_override = 'percona-xtrabackup-24' } } else { $provider = 'mysql' $xtrabackup_package_name_override = 'percona-xtrabackup-20' } if versioncmp($::operatingsystemmajrelease, '8') >= 0 { $java_package_name = 'mariadb-java-client' $python_package_name = 'python3-PyMySQL' } else { $java_package_name = 'mysql-connector-java' $python_package_name = 'MySQL-python' } } default: { $provider = 'mysql' } } if $provider == 'mariadb' { $client_package_name = 'mariadb' $server_package_name = 'mariadb-server' $server_service_name = 'mariadb' $log_error = '/var/log/mariadb/mariadb.log' $config_file = '/etc/my.cnf.d/server.cnf' # mariadb package by default has !includedir set in my.cnf to /etc/my.cnf.d $includedir = undef $pidfile = '/var/run/mariadb/mariadb.pid' $daemon_dev_package_name = 'mariadb-devel' } else { $client_package_name = 'mysql' $server_package_name = 'mysql-server' $server_service_name = 'mysqld' $log_error = '/var/log/mysqld.log' $config_file = '/etc/my.cnf' $includedir = '/etc/my.cnf.d' $pidfile = '/var/run/mysqld/mysqld.pid' $daemon_dev_package_name = 'mysql-devel' } $basedir = '/usr' $datadir = '/var/lib/mysql' $root_group = 'root' $mysql_group = 'mysql' $mycnf_owner = undef $mycnf_group = undef $socket = '/var/lib/mysql/mysql.sock' $ssl_ca = '/etc/mysql/cacert.pem' $ssl_cert = '/etc/mysql/server-cert.pem' $ssl_key = '/etc/mysql/server-key.pem' $tmpdir = '/tmp' $managed_dirs = undef # mysql::bindings $perl_package_name = 'perl-DBD-MySQL' $php_package_name = 'php-mysql' $ruby_package_name = 'ruby-mysql' $client_dev_package_name = undef } 'Suse': { case $::operatingsystem { 'OpenSuSE': { if versioncmp( $::operatingsystemmajrelease, '12' ) >= 0 { $client_package_name = 'mariadb-client' $server_package_name = 'mariadb' # First service start fails if this is set. Runs fine without # it being set, in any case. Leaving it as-is for the mysql. $basedir = undef } else { $client_package_name = 'mysql-community-server-client' $server_package_name = 'mysql-community-server' $basedir = '/usr' } } 'SLES','SLED': { if versioncmp($::operatingsystemrelease, '12') >= 0 { $client_package_name = 'mariadb-client' $server_package_name = 'mariadb' $basedir = undef } else { $client_package_name = 'mysql-client' $server_package_name = 'mysql' $basedir = '/usr' } } default: { fail("Unsupported platform: puppetlabs-${module_name} currently doesn\'t support ${::operatingsystem}.") } } $config_file = '/etc/my.cnf' $includedir = '/etc/my.cnf.d' $datadir = '/var/lib/mysql' $log_error = $::operatingsystem ? { /OpenSuSE/ => '/var/log/mysql/mysqld.log', /(SLES|SLED)/ => '/var/log/mysqld.log', } $pidfile = $::operatingsystem ? { /OpenSuSE/ => '/var/run/mysql/mysqld.pid', /(SLES|SLED)/ => '/var/lib/mysql/mysqld.pid', } $root_group = 'root' $mysql_group = 'mysql' $mycnf_owner = undef $mycnf_group = undef $server_service_name = 'mysql' $xtrabackup_package_name_override = 'xtrabackup' if $::operatingsystem =~ /(SLES|SLED)/ { if versioncmp( $::operatingsystemmajrelease, '12' ) >= 0 { $socket = '/run/mysql/mysql.sock' } else { $socket = '/var/lib/mysql/mysql.sock' } } else { $socket = '/var/run/mysql/mysql.sock' } $ssl_ca = '/etc/mysql/cacert.pem' $ssl_cert = '/etc/mysql/server-cert.pem' $ssl_key = '/etc/mysql/server-key.pem' $tmpdir = '/tmp' $managed_dirs = undef # mysql::bindings $java_package_name = 'mysql-connector-java' $perl_package_name = 'perl-DBD-mysql' $php_package_name = 'apache2-mod_php53' $python_package_name = 'python-mysql' $ruby_package_name = $::operatingsystem ? { /OpenSuSE/ => 'rubygem-mysql', /(SLES|SLED)/ => 'ruby-mysql', } $client_dev_package_name = 'libmysqlclient-devel' $daemon_dev_package_name = 'mysql-devel' } 'Debian': { if $::operatingsystem == 'Debian' and versioncmp($::operatingsystemrelease, '9') >= 0 { $provider = 'mariadb' } else { $provider = 'mysql' } if $provider == 'mariadb' { $client_package_name = 'mariadb-client' $server_package_name = 'mariadb-server' $server_service_name = 'mariadb' $client_dev_package_name = 'libmariadbclient-dev' $daemon_dev_package_name = 'libmariadbd-dev' } else { $client_package_name = 'mysql-client' $server_package_name = 'mysql-server' $server_service_name = 'mysql' $client_dev_package_name = 'libmysqlclient-dev' $daemon_dev_package_name = 'libmysqld-dev' } $basedir = '/usr' $config_file = '/etc/mysql/my.cnf' $includedir = '/etc/mysql/conf.d' $datadir = '/var/lib/mysql' $log_error = '/var/log/mysql/error.log' $pidfile = '/var/run/mysqld/mysqld.pid' $root_group = 'root' $mysql_group = 'adm' $mycnf_owner = undef $mycnf_group = undef $socket = '/var/run/mysqld/mysqld.sock' $ssl_ca = '/etc/mysql/cacert.pem' $ssl_cert = '/etc/mysql/server-cert.pem' $ssl_key = '/etc/mysql/server-key.pem' $tmpdir = '/tmp' $managed_dirs = ['tmpdir','basedir','datadir','innodb_data_home_dir','innodb_log_group_home_dir','innodb_undo_directory','innodb_tmpdir'] # mysql::bindings if ($::operatingsystem == 'Debian' and versioncmp($::operatingsystemrelease, '10') >= 0) or ($::operatingsystem == 'Ubuntu' and versioncmp($::operatingsystemrelease, '20.04') >= 0) { $java_package_name = 'libmariadb-java' } else { $java_package_name = 'libmysql-java' } $perl_package_name = 'libdbd-mysql-perl' if ($::operatingsystem == 'Ubuntu' and versioncmp($::operatingsystemrelease, '16.04') >= 0) or ($::operatingsystem == 'Debian' and versioncmp($::operatingsystemrelease, '9') >= 0) { $php_package_name = 'php-mysql' } else { $php_package_name = 'php5-mysql' } if ($::operatingsystem == 'Ubuntu' and versioncmp($::operatingsystemrelease, '16.04') < 0) or ($::operatingsystem == 'Debian') { $xtrabackup_package_name_override = 'percona-xtrabackup-24' } if ($::operatingsystem == 'Ubuntu' and versioncmp($::operatingsystemrelease, '20.04') >= 0) { $python_package_name = 'python3-mysqldb' } else { $python_package_name = 'python-mysqldb' } - $ruby_package_name = $::lsbdistcodename ? { - 'jessie' => 'ruby-mysql', - 'stretch' => 'ruby-mysql2', - 'buster' => 'ruby-mysql2', - 'trusty' => 'ruby-mysql', - 'xenial' => 'ruby-mysql', - 'bionic' => 'ruby-mysql2', - 'focal' => 'ruby-mysql2', - default => 'libmysql-ruby', + $ruby_package_name = $facts['operatingsystemmajrelease'] ? { + '8' => 'ruby-mysql', # jessie + '9' => 'ruby-mysql2', # stretch + '10' => 'ruby-mysql2', # buster + '14.04' => 'ruby-mysql', # trusty + '16.04' => 'ruby-mysql', # xenial + '18.04' => 'ruby-mysql2', # bionic + '20.04' => 'ruby-mysql2', # focal + default => 'libmysql-ruby', } } 'Archlinux': { $daemon_dev_package_name = undef $client_dev_package_name = undef $includedir = undef $client_package_name = 'mariadb-clients' $server_package_name = 'mariadb' $basedir = '/usr' $config_file = '/etc/mysql/my.cnf' $datadir = '/var/lib/mysql' $log_error = '/var/log/mysqld.log' $pidfile = '/var/run/mysqld/mysqld.pid' $root_group = 'root' $mysql_group = 'mysql' $mycnf_owner = undef $mycnf_group = undef $server_service_name = 'mysqld' $socket = '/var/lib/mysql/mysql.sock' $ssl_ca = '/etc/mysql/cacert.pem' $ssl_cert = '/etc/mysql/server-cert.pem' $ssl_key = '/etc/mysql/server-key.pem' $tmpdir = '/tmp' $managed_dirs = undef # mysql::bindings $java_package_name = 'mysql-connector-java' $perl_package_name = 'perl-dbd-mysql' $php_package_name = undef $python_package_name = 'mysql-python' $ruby_package_name = 'mysql-ruby' } 'Gentoo': { $client_package_name = 'virtual/mysql' $includedir = undef $server_package_name = 'virtual/mysql' $basedir = '/usr' $config_file = '/etc/mysql/my.cnf' $datadir = '/var/lib/mysql' $log_error = '/var/log/mysql/mysqld.err' $pidfile = '/run/mysqld/mysqld.pid' $root_group = 'root' $mysql_group = 'mysql' $mycnf_owner = undef $mycnf_group = undef $server_service_name = 'mysql' $socket = '/run/mysqld/mysqld.sock' $ssl_ca = '/etc/mysql/cacert.pem' $ssl_cert = '/etc/mysql/server-cert.pem' $ssl_key = '/etc/mysql/server-key.pem' $tmpdir = '/tmp' $managed_dirs = undef # mysql::bindings $java_package_name = 'dev-java/jdbc-mysql' $perl_package_name = 'dev-perl/DBD-mysql' $php_package_name = undef $python_package_name = 'dev-python/mysql-python' $ruby_package_name = 'dev-ruby/mysql-ruby' } 'FreeBSD': { $client_package_name = 'databases/mysql57-client' $server_package_name = 'databases/mysql57-server' $basedir = '/usr/local' $config_file = '/usr/local/etc/my.cnf' $includedir = '/usr/local/etc/my.cnf.d' $datadir = '/var/db/mysql' $log_error = '/var/log/mysqld.log' $pidfile = '/var/run/mysql.pid' $root_group = 'wheel' $mysql_group = 'mysql' $mycnf_owner = undef $mycnf_group = undef $server_service_name = 'mysql-server' $socket = '/var/db/mysql/mysql.sock' $ssl_ca = undef $ssl_cert = undef $ssl_key = undef $tmpdir = '/tmp' $managed_dirs = undef # mysql::bindings $java_package_name = 'databases/mysql-connector-java' $perl_package_name = 'p5-DBD-mysql' $php_package_name = 'php5-mysql' $python_package_name = 'databases/py-MySQLdb' $ruby_package_name = 'databases/ruby-mysql' # The libraries installed by these packages are included in client and server packages, no installation required. $client_dev_package_name = undef $daemon_dev_package_name = undef } 'OpenBSD': { $client_package_name = 'mariadb-client' $server_package_name = 'mariadb-server' $basedir = '/usr/local' $config_file = '/etc/my.cnf' $includedir = undef $datadir = '/var/mysql' $log_error = "/var/mysql/${::hostname}.err" $pidfile = '/var/mysql/mysql.pid' $root_group = 'wheel' $mysql_group = '_mysql' $mycnf_owner = undef $mycnf_group = undef $server_service_name = 'mysqld' $socket = '/var/run/mysql/mysql.sock' $ssl_ca = undef $ssl_cert = undef $ssl_key = undef $tmpdir = '/tmp' $managed_dirs = undef # mysql::bindings $java_package_name = undef $perl_package_name = 'p5-DBD-mysql' $php_package_name = 'php-mysql' $python_package_name = 'py-mysql' $ruby_package_name = 'ruby-mysql' # The libraries installed by these packages are included in client and server packages, no installation required. $client_dev_package_name = undef $daemon_dev_package_name = undef } 'Solaris': { $client_package_name = 'database/mysql-55/client' $server_package_name = 'database/mysql-55' $basedir = undef $config_file = '/etc/mysql/5.5/my.cnf' $datadir = '/var/mysql/5.5/data' $log_error = "/var/mysql/5.5/data/${::hostname}.err" $pidfile = "/var/mysql/5.5/data/${::hostname}.pid" $root_group = 'bin' $server_service_name = 'application/database/mysql:version_55' $socket = '/tmp/mysql.sock' $ssl_ca = undef $ssl_cert = undef $ssl_key = undef $tmpdir = '/tmp' $managed_dirs = undef # mysql::bindings $java_package_name = undef $perl_package_name = undef $php_package_name = 'web/php-53/extension/php-mysql' $python_package_name = 'library/python/python-mysql' $ruby_package_name = undef # The libraries installed by these packages are included in client and server packages, no installation required. $client_dev_package_name = undef $daemon_dev_package_name = undef } default: { case $::operatingsystem { 'Alpine': { $client_package_name = 'mariadb-client' $server_package_name = 'mariadb' $basedir = '/usr' $config_file = '/etc/mysql/my.cnf' $datadir = '/var/lib/mysql' $log_error = '/var/log/mysqld.log' $pidfile = '/run/mysqld/mysqld.pid' $root_group = 'root' $mysql_group = 'mysql' $mycnf_owner = undef $mycnf_group = undef $server_service_name = 'mariadb' $socket = '/run/mysqld/mysqld.sock' $ssl_ca = '/etc/mysql/cacert.pem' $ssl_cert = '/etc/mysql/server-cert.pem' $ssl_key = '/etc/mysql/server-key.pem' $tmpdir = '/tmp' $managed_dirs = undef $java_package_name = undef $perl_package_name = 'perl-dbd-mysql' $php_package_name = 'php7-mysqlnd' $python_package_name = 'py-mysqldb' $ruby_package_name = undef $client_dev_package_name = undef $daemon_dev_package_name = undef } 'Amazon': { $client_package_name = 'mysql' $server_package_name = 'mysql-server' $basedir = '/usr' $config_file = '/etc/my.cnf' $includedir = '/etc/my.cnf.d' $datadir = '/var/lib/mysql' $log_error = '/var/log/mysqld.log' $pidfile = '/var/run/mysqld/mysqld.pid' $root_group = 'root' $mysql_group = 'mysql' $mycnf_owner = undef $mycnf_group = undef $server_service_name = 'mysqld' $socket = '/var/lib/mysql/mysql.sock' $ssl_ca = '/etc/mysql/cacert.pem' $ssl_cert = '/etc/mysql/server-cert.pem' $ssl_key = '/etc/mysql/server-key.pem' $tmpdir = '/tmp' $managed_dirs = undef # mysql::bindings $java_package_name = 'mysql-connector-java' $perl_package_name = 'perl-DBD-MySQL' $php_package_name = 'php-mysql' $python_package_name = 'MySQL-python' $ruby_package_name = 'ruby-mysql' # The libraries installed by these packages are included in client and server packages, no installation required. $client_dev_package_name = undef $daemon_dev_package_name = undef } default: { fail("Unsupported platform: puppetlabs-${module_name} currently doesn\'t support ${::osfamily} or ${::operatingsystem}.") } } } } case $::operatingsystem { 'Ubuntu': { # lint:ignore:only_variable_string if versioncmp("${::operatingsystemmajrelease}", '14.10') > 0 { # lint:endignore $server_service_provider = 'systemd' } else { $server_service_provider = 'upstart' } } 'Alpine': { $server_service_provider = 'rc-service' } 'FreeBSD': { $server_service_provider = 'freebsd' } default: { $server_service_provider = undef } } $default_options = { 'client' => { 'port' => '3306', 'socket' => $mysql::params::socket, }, 'mysqld_safe' => { 'nice' => '0', 'log-error' => $mysql::params::log_error, 'socket' => $mysql::params::socket, }, 'mysqld-5.0' => { 'myisam-recover' => 'BACKUP', }, 'mysqld-5.1' => { 'myisam-recover' => 'BACKUP', }, 'mysqld-5.5' => { 'myisam-recover' => 'BACKUP', 'query_cache_limit' => '1M', 'query_cache_size' => '16M', }, 'mysqld-5.6' => { 'myisam-recover-options' => 'BACKUP', 'query_cache_limit' => '1M', 'query_cache_size' => '16M', }, 'mysqld-5.7' => { 'myisam-recover-options' => 'BACKUP', 'query_cache_limit' => '1M', 'query_cache_size' => '16M', }, 'mysqld' => { 'basedir' => $mysql::params::basedir, 'bind-address' => '127.0.0.1', 'datadir' => $mysql::params::datadir, 'expire_logs_days' => '10', 'key_buffer_size' => '16M', 'log-error' => $mysql::params::log_error, 'max_allowed_packet' => '16M', 'max_binlog_size' => '100M', 'max_connections' => '151', 'pid-file' => $mysql::params::pidfile, 'port' => '3306', 'skip-external-locking' => true, 'socket' => $mysql::params::socket, 'ssl' => false, 'ssl-ca' => $mysql::params::ssl_ca, 'ssl-cert' => $mysql::params::ssl_cert, 'ssl-key' => $mysql::params::ssl_key, 'ssl-disable' => false, 'thread_cache_size' => '8', 'thread_stack' => '256K', 'tmpdir' => $mysql::params::tmpdir, 'user' => 'mysql', }, 'mysqldump' => { 'max_allowed_packet' => '16M', 'quick' => true, 'quote-names' => true, }, 'isamchk' => { 'key_buffer_size' => '16M', }, } if defined('$xtrabackup_package_name_override') { $xtrabackup_package_name = pick($xtrabackup_package_name_override, $xtrabackup_package_name_default) } else { $xtrabackup_package_name = $xtrabackup_package_name_default } ## Additional graceful failures if $::osfamily == 'RedHat' and $::operatingsystemmajrelease == '4' and $::operatingsystem != 'Amazon' { fail("Unsupported platform: puppetlabs-${module_name} only supports RedHat 5.0 and beyond.") } } diff --git a/metadata.json b/metadata.json index dc6a426..5707cd9 100644 --- a/metadata.json +++ b/metadata.json @@ -1,82 +1,74 @@ { "name": "puppetlabs-mysql", "version": "11.0.1", "author": "puppetlabs", "summary": "Installs, configures, and manages the MySQL service.", "license": "Apache-2.0", "source": "git://github.com/puppetlabs/puppetlabs-mysql", "project_page": "http://github.com/puppetlabs/puppetlabs-mysql", "issues_url": "https://tickets.puppetlabs.com/browse/MODULES", "dependencies": [ { "name": "puppetlabs/stdlib", "version_requirement": ">= 3.2.0 < 8.0.0" } ], "operatingsystem_support": [ { "operatingsystem": "RedHat", "operatingsystemrelease": [ "6", "7", "8" ] }, { "operatingsystem": "CentOS", "operatingsystemrelease": [ "6", "7", "8" ] }, { "operatingsystem": "OracleLinux", "operatingsystemrelease": [ "6", "7" ] }, { "operatingsystem": "Scientific", "operatingsystemrelease": [ "6", "7" ] }, { "operatingsystem": "SLES", "operatingsystemrelease": [ "12", "15" ] }, - { - "operatingsystem": "Debian", - "operatingsystemrelease": [ - "8", - "9", - "10" - ] - }, { "operatingsystem": "Ubuntu", "operatingsystemrelease": [ "14.04", "16.04", "18.04", "20.04" ] } ], "requirements": [ { "name": "puppet", "version_requirement": ">= 6.0.0 < 8.0.0" } ], "description": "MySQL module", "template-url": "https://github.com/puppetlabs/pdk-templates#main", - "template-ref": "heads/main-0-g44cc7ed", - "pdk-version": "1.18.1" + "template-ref": "heads/main-0-ge04486b", + "pdk-version": "2.1.0" } diff --git a/spec/acceptance/mysql_mariadb_spec.rb b/spec/acceptance/mysql_mariadb_spec.rb index 1b628b1..03a25bd 100644 --- a/spec/acceptance/mysql_mariadb_spec.rb +++ b/spec/acceptance/mysql_mariadb_spec.rb @@ -1,46 +1,46 @@ # frozen_string_literal: true require 'spec_helper_acceptance' describe 'mysql server class', if: ((os[:family] == 'debian' && os[:release].to_i > 8) || (os[:family] == 'redhat' && os[:release].to_i > 6)) do describe 'mariadb' do let(:pp) do <<-MANIFEST $osname = $facts['os']['name'].downcase yumrepo {'mariadb': baseurl => "http://yum.mariadb.org/10.4/$osname${facts['os']['release']['major']}-aarch64/", gpgkey => 'https://yum.mariadb.org/RPM-GPG-KEY-MariaDB', descr => "MariaDB 10.4", - enabled => 1, - gpgcheck => 1, + enabled => 0, + gpgcheck => 0, }-> class { '::mysql::server': require => Yumrepo['mariadb'], package_name => 'mariadb-server', service_name => 'mariadb', root_password => 'strongpassword', remove_default_accounts => true, managed_dirs => ['/var/log','/var/run/mysql'], override_options => { mysqld => { log-error => '/var/log/mariadb.log', pid-file => '/var/run/mysql/mysqld.pid', }, mysqld_safe => { log-error => '/var/log/mariadb.log', }, }, } MANIFEST end it 'apply manifest' do apply_manifest(pp) end it 'mariadb connection' do result = run_shell('mysql --user="root" --password="strongpassword" -e "status"') expect(result.stdout).to match(%r{MariaDB}) expect(result.stderr).to be_empty end end end diff --git a/spec/acceptance/types/mysql_grant_spec.rb b/spec/acceptance/types/mysql_grant_spec.rb index 76face4..bb7d60a 100644 --- a/spec/acceptance/types/mysql_grant_spec.rb +++ b/spec/acceptance/types/mysql_grant_spec.rb @@ -1,741 +1,741 @@ # frozen_string_literal: true require 'spec_helper_acceptance' describe 'mysql_grant' do before(:all) do pp = <<-MANIFEST class { 'mysql::server': root_password => 'password', } MANIFEST apply_manifest(pp, catch_failures: true) end describe 'missing privileges for user' do pp = <<-MANIFEST mysql_user { 'test1@tester': ensure => present, } mysql_grant { 'test1@tester/test.*': ensure => 'present', table => 'test.*', user => 'test1@tester', require => Mysql_user['test1@tester'], } MANIFEST it 'fails' do result = apply_manifest(pp, expect_failures: true) expect(result.stderr).to contain(%r{`privileges` `parameter` is required}) end it 'does not find the user' do result = run_shell('mysql -NBe "SHOW GRANTS FOR test1@tester"', expect_failures: true) expect(result.stderr).to contain(%r{There is no such grant defined for user 'test1' on host 'tester'}) end end describe 'missing table for user' do pp = <<-MANIFEST mysql_user { 'atest@tester': ensure => present, } mysql_grant { 'atest@tester/test.*': ensure => 'present', user => 'atest@tester', privileges => ['ALL'], require => Mysql_user['atest@tester'], } MANIFEST it 'fails' do apply_manifest(pp, expect_failures: true) end it 'does not find the user' do result = run_shell('mysql -NBe "SHOW GRANTS FOR atest@tester"', expect_failures: true) expect(result.stderr).to contain(%r{There is no such grant defined for user 'atest' on host 'tester'}) end end describe 'adding privileges' do pp = <<-MANIFEST mysql_user { 'test2@tester': ensure => present, } mysql_grant { 'test2@tester/test.*': ensure => 'present', table => 'test.*', user => 'test2@tester', privileges => ['SELECT', 'UPDATE'], require => Mysql_user['test2@tester'], } MANIFEST it 'works without errors' do apply_manifest(pp, catch_failures: true) end it 'finds the user #stdout' do result = run_shell('mysql -NBe "SHOW GRANTS FOR test2@tester"') expect(result.stdout).to contain(%r{GRANT SELECT, UPDATE.*TO ['|`]test2['|`]@['|`]tester['|`]}) expect(result.stderr).to be_empty end end describe 'adding privileges with special character in name' do pp = <<-MANIFEST mysql_user { 'test-2@tester': ensure => present, } mysql_grant { 'test-2@tester/test.*': ensure => 'present', table => 'test.*', user => 'test-2@tester', privileges => ['SELECT', 'UPDATE'], require => Mysql_user['test-2@tester'], } MANIFEST it 'works without errors' do apply_manifest(pp, catch_failures: true) end it 'finds the user #stdout' do result = run_shell("mysql -NBe \"SHOW GRANTS FOR 'test-2'@tester\"") expect(result.stdout).to contain(%r{GRANT SELECT, UPDATE.*TO ['|`]test-2['|`]@['|`]tester['|`]}) expect(result.stderr).to be_empty end end describe 'adding option' do pp = <<-MANIFEST mysql_user { 'test3@tester': ensure => present, } mysql_grant { 'test3@tester/test.*': ensure => 'present', table => 'test.*', user => 'test3@tester', options => ['GRANT'], privileges => ['SELECT', 'UPDATE'], require => Mysql_user['test3@tester'], } MANIFEST it 'works without errors' do apply_manifest(pp, catch_failures: true) end it 'finds the user #stdout' do result = run_shell('mysql -NBe "SHOW GRANTS FOR test3@tester"') expect(result.stdout).to contain(%r{GRANT SELECT, UPDATE ON `test`.* TO ['|`]test3['|`]@['|`]tester['|`] WITH GRANT OPTION$}) expect(result.stderr).to be_empty end end describe 'adding all privileges without table' do pp = <<-MANIFEST mysql_user { 'test4@tester': ensure => present, } mysql_grant { 'test4@tester/test.*': ensure => 'present', user => 'test4@tester', options => ['GRANT'], privileges => ['SELECT', 'UPDATE', 'ALL'], require => Mysql_user['test4@tester'], } MANIFEST it 'fails' do result = apply_manifest(pp, expect_failures: true) expect(result.stderr).to contain(%r{`table` `parameter` is required.}) end end describe 'adding all privileges' do pp = <<-MANIFEST mysql_user { 'test4@tester': ensure => present, } mysql_grant { 'test4@tester/test.*': ensure => 'present', table => 'test.*', user => 'test4@tester', options => ['GRANT'], privileges => ['SELECT', 'UPDATE', 'ALL'], require => Mysql_user['test4@tester'], } MANIFEST it 'onlies try to apply ALL' do apply_manifest(pp, catch_failures: true) end it 'finds the user #stdout' do result = run_shell('mysql -NBe "SHOW GRANTS FOR test4@tester"') expect(result.stdout).to contain(%r{GRANT ALL PRIVILEGES ON `test`.* TO ['|`]test4['|`]@['|`]tester['|`] WITH GRANT OPTION}) expect(result.stderr).to be_empty end end # Test combinations of user@host to ensure all cases work. describe 'short hostname' do pp = <<-MANIFEST mysql_user { 'test@short': ensure => present, } mysql_grant { 'test@short/test.*': ensure => 'present', table => 'test.*', user => 'test@short', privileges => 'ALL', require => Mysql_user['test@short'], } mysql_user { 'test@long.hostname.com': ensure => present, } mysql_grant { 'test@long.hostname.com/test.*': ensure => 'present', table => 'test.*', user => 'test@long.hostname.com', privileges => 'ALL', require => Mysql_user['test@long.hostname.com'], } mysql_user { 'test@192.168.5.6': ensure => present, } mysql_grant { 'test@192.168.5.6/test.*': ensure => 'present', table => 'test.*', user => 'test@192.168.5.6', privileges => 'ALL', require => Mysql_user['test@192.168.5.6'], } mysql_user { 'test@2607:f0d0:1002:0051:0000:0000:0000:0004': ensure => present, } mysql_grant { 'test@2607:f0d0:1002:0051:0000:0000:0000:0004/test.*': ensure => 'present', table => 'test.*', user => 'test@2607:f0d0:1002:0051:0000:0000:0000:0004', privileges => 'ALL', require => Mysql_user['test@2607:f0d0:1002:0051:0000:0000:0000:0004'], } mysql_user { 'test@::1/128': ensure => present, } mysql_grant { 'test@::1/128/test.*': ensure => 'present', table => 'test.*', user => 'test@::1/128', privileges => 'ALL', require => Mysql_user['test@::1/128'], } MANIFEST it 'applies' do apply_manifest(pp, catch_failures: true) end it 'finds short hostname #stdout' do result = run_shell('mysql -NBe "SHOW GRANTS FOR test@short"') expect(result.stdout).to contain(%r{GRANT ALL PRIVILEGES ON ['|`]test['|`].* TO ['|`]test['|`]@['|`]short['|`]}) expect(result.stderr).to be_empty end it 'finds long hostname #stdout' do run_shell("mysql -NBe \"SHOW GRANTS FOR 'test'@'long.hostname.com'\"") do |r| expect(r.stdout).to match(%r{GRANT ALL PRIVILEGES ON ['|`]test['|`].* TO ['|`]test['|`]@['|`]long.hostname.com['|`]}) expect(r.stderr).to be_empty end end it 'finds ipv4 #stdout' do run_shell("mysql -NBe \"SHOW GRANTS FOR 'test'@'192.168.5.6'\"") do |r| expect(r.stdout).to match(%r{GRANT ALL PRIVILEGES ON ['|`]test['|`].* TO ['|`]test['|`]@['|`]192.168.5.6['|`]}) expect(r.stderr).to be_empty end end it 'finds ipv6 #stdout' do run_shell("mysql -NBe \"SHOW GRANTS FOR 'test'@'2607:f0d0:1002:0051:0000:0000:0000:0004'\"") do |r| expect(r.stdout).to match(%r{GRANT ALL PRIVILEGES ON ['|`]test['|`].* TO ['|`]test['|`]@['|`]2607:f0d0:1002:0051:0000:0000:0000:0004['|`]}) expect(r.stderr).to be_empty end end it 'finds short ipv6 #stdout' do run_shell("mysql -NBe \"SHOW GRANTS FOR 'test'@'::1/128'\"") do |r| expect(r.stdout).to match(%r{GRANT ALL PRIVILEGES ON ['|`]test['|`].* TO ['|`]test['|`]@['|`]::1\/128['|`]}) expect(r.stderr).to be_empty end end end # On Ubuntu 20.04 'ALL' now returns as the sum of it's constitute parts and so require a specific test describe 'ALL privilege on newer MySQL versions', if: os[:family] == 'ubuntu' && os[:release] =~ %r{^20\.04} do pp_one = <<-MANIFEST mysql_user { 'all@localhost': ensure => present, } mysql_grant { 'all@localhost/*.*': user => 'all@localhost', privileges => ['ALL'], table => '*.*', require => Mysql_user['all@localhost'], } MANIFEST it "create ['ALL'] privs" do apply_manifest(pp_one, catch_failures: true) end pp_two = <<-MANIFEST mysql_user { 'all@localhost': ensure => present, } mysql_grant { 'all@localhost/*.*': user => 'all@localhost', privileges => ['ALTER', 'ALTER ROUTINE', 'CREATE', 'CREATE ROLE', 'CREATE ROUTINE', 'CREATE TABLESPACE', 'CREATE TEMPORARY TABLES', 'CREATE USER', 'CREATE VIEW', 'DELETE', 'DROP', 'DROP ROLE', 'EVENT', 'EXECUTE', 'FILE', 'INDEX', 'INSERT', 'LOCK TABLES', 'PROCESS', 'REFERENCES', 'RELOAD', 'REPLICATION CLIENT', 'REPLICATION SLAVE', 'SELECT', 'SHOW DATABASES', 'SHOW VIEW', 'SHUTDOWN', 'SUPER', 'TRIGGER', 'UPDATE'], table => '*.*', require => Mysql_user['all@localhost'], } MANIFEST it "create ['ALL'] constitute parts privs" do apply_manifest(pp_two, catch_changes: true) end end describe 'complex test' do # On Ubuntu 20.04 'ALL' now returns as the sum of it's constitute parts and so is no longer idempotent when set privileges = if os[:family] == 'ubuntu' && os[:release] =~ %r{^20\.04} "['SELECT', 'INSERT', 'UPDATE']" else "['ALL']" end pp = <<-MANIFEST $dbSubnet = '10.10.10.%' mysql_database { 'foo': ensure => present, } exec { 'mysql-create-table': command => '/usr/bin/mysql -NBe "CREATE TABLE foo.bar (name VARCHAR(20))"', environment => "HOME=${::root_home}", unless => '/usr/bin/mysql -NBe "SELECT 1 FROM foo.bar LIMIT 1;"', require => Mysql_database['foo'], } Mysql_grant { ensure => present, options => ['GRANT'], privileges => #{privileges}, table => '*.*', require => [ Mysql_database['foo'], Exec['mysql-create-table'] ], } mysql_user { "user1@${dbSubnet}": ensure => present, } mysql_grant { "user1@${dbSubnet}/*.*": user => "user1@${dbSubnet}", require => Mysql_user["user1@${dbSubnet}"], } mysql_user { "user2@${dbSubnet}": ensure => present, } mysql_grant { "user2@${dbSubnet}/foo.bar": privileges => ['SELECT', 'INSERT', 'UPDATE'], user => "user2@${dbSubnet}", table => 'foo.bar', require => Mysql_user["user2@${dbSubnet}"], } mysql_user { "user3@${dbSubnet}": ensure => present, } mysql_grant { "user3@${dbSubnet}/foo.*": privileges => ['SELECT', 'INSERT', 'UPDATE'], user => "user3@${dbSubnet}", table => 'foo.*', require => Mysql_user["user3@${dbSubnet}"], } mysql_user { 'web@%': ensure => present, } mysql_grant { 'web@%/*.*': user => 'web@%', require => Mysql_user['web@%'], } mysql_user { "web@${dbSubnet}": ensure => present, } mysql_grant { "web@${dbSubnet}/*.*": user => "web@${dbSubnet}", require => Mysql_user["web@${dbSubnet}"], } - mysql_user { "web@${fqdn}": + mysql_user { "web@${::networking['ip']}": ensure => present, } - mysql_grant { "web@${fqdn}/*.*": - user => "web@${fqdn}", - require => Mysql_user["web@${fqdn}"], + mysql_grant { "web@${::networking['ip']}/*.*": + user => "web@${::networking['ip']}", + require => Mysql_user["web@${::networking['ip']}"], } mysql_user { 'web@localhost': ensure => present, } mysql_grant { 'web@localhost/*.*': user => 'web@localhost', require => Mysql_user['web@localhost'], } MANIFEST it 'setup mysql::server' do idempotent_apply(pp) end end describe 'lower case privileges' do pp_one = <<-MANIFEST mysql_user { 'lowercase@localhost': ensure => present, } mysql_grant { 'lowercase@localhost/*.*': user => 'lowercase@localhost', privileges => ['SELECT', 'INSERT', 'UPDATE'], table => '*.*', require => Mysql_user['lowercase@localhost'], } MANIFEST it "create ['SELECT', 'INSERT', 'UPDATE'] privs" do apply_manifest(pp_one, catch_failures: true) end pp_two = <<-MANIFEST mysql_user { 'lowercase@localhost': ensure => present, } mysql_grant { 'lowercase@localhost/*.*': user => 'lowercase@localhost', privileges => ['select', 'insert', 'update'], table => '*.*', require => Mysql_user['lowercase@localhost'], } MANIFEST it "create lowercase ['select', 'insert', 'update'] privs" do apply_manifest(pp_two, catch_changes: true) end end describe 'adding procedure privileges' do pp = <<-MANIFEST exec { 'simpleproc-create': command => 'mysql --user="root" --password="password" --database=mysql --delimiter="//" -NBe "CREATE PROCEDURE simpleproc (OUT param1 INT) BEGIN SELECT COUNT(*) INTO param1 FROM t; end//"', path => '/usr/bin/', before => Mysql_user['test2@tester'], } mysql_user { 'test2@tester': ensure => present, } mysql_grant { 'test2@tester/PROCEDURE mysql.simpleproc': ensure => 'present', table => 'PROCEDURE mysql.simpleproc', user => 'test2@tester', privileges => ['EXECUTE'], require => Mysql_user['test2@tester'], } MANIFEST it 'works without errors' do apply_manifest(pp, catch_failures: true) end it 'finds the user #stdout' do result = run_shell('mysql -NBe "SHOW GRANTS FOR test2@tester"') expect(result.stdout).to match(%r{GRANT EXECUTE ON PROCEDURE `mysql`.`simpleproc` TO ['|`]test2['|`]@['|`]tester['|`]}) expect(result.stderr).to be_empty end end describe 'adding function privileges' do it 'works without errors' do pp = <<-EOS exec { 'simplefunc-create': command => '/usr/bin/mysql --user="root" --password="password" --database=mysql -NBe "CREATE FUNCTION simplefunc (s CHAR(20)) RETURNS CHAR(50) DETERMINISTIC RETURN CONCAT(\\'Hello, \\', s, \\'!\\')"', before => Mysql_user['test3@tester'], } mysql_user { 'test3@tester': ensure => 'present', } mysql_grant { 'test3@tester/FUNCTION mysql.simplefunc': ensure => 'present', table => 'FUNCTION mysql.simplefunc', user => 'test3@tester', privileges => ['EXECUTE'], require => Mysql_user['test3@tester'], } EOS apply_manifest(pp, catch_failures: true) end # rubocop:enable RSpec/ExampleLength it 'finds the user' do result = run_shell('mysql -NBe "SHOW GRANTS FOR test3@tester"') expect(result.stdout).to match(%r{GRANT EXECUTE ON FUNCTION `mysql`.`simplefunc` TO ['|`]test3['|`]@['|`]tester['|`]}) expect(result.stderr).to be_empty end # rubocop:enable RSpec/MultipleExpectations end describe 'proxy privilieges' do pre_run describe 'adding proxy privileges', if: Gem::Version.new(mysql_version) > Gem::Version.new('5.5.0') do pp = <<-MANIFEST mysql_user { 'proxy1@tester': ensure => present, } mysql_grant { 'proxy1@tester/proxy_user@proxy_host': ensure => 'present', table => 'proxy_user@proxy_host', user => 'proxy1@tester', privileges => ['PROXY'], require => Mysql_user['proxy1@tester'], } MANIFEST it 'works without errors when version greater than 5.5.0' do apply_manifest(pp, catch_failures: true) end it 'finds the user #stdout' do run_shell('mysql -NBe "SHOW GRANTS FOR proxy1@tester"') do |r| expect(r.stdout).to match(%r{GRANT PROXY ON 'proxy_user'@'proxy_host' TO ['|`]proxy1['|`]@['|`]tester['|`]}) expect(r.stderr).to be_empty end end end describe 'removing proxy privileges', if: Gem::Version.new(mysql_version) > Gem::Version.new('5.5.0') do pp = <<-MANIFEST mysql_user { 'proxy1@tester': ensure => present, } mysql_grant { 'proxy1@tester/proxy_user@proxy_host': ensure => 'absent', table => 'proxy_user@proxy_host', user => 'proxy1@tester', privileges => ['PROXY'], require => Mysql_user['proxy1@tester'], } MANIFEST it 'works without errors' do apply_manifest(pp, catch_failures: true) end it 'finds the user #stdout' do run_shell('mysql -NBe "SHOW GRANTS FOR proxy1@tester"') do |r| expect(r.stdout).not_to match(%r{GRANT PROXY ON 'proxy_user'@'proxy_host' TO ['|`]proxy1['|`]@['|`]tester['|`]}) expect(r.stderr).to be_empty end end end describe 'adding proxy privileges with other privileges', if: Gem::Version.new(mysql_version) > Gem::Version.new('5.5.0') do pp = <<-MANIFEST mysql_user { 'proxy2@tester': ensure => present, } mysql_grant { 'proxy2@tester/proxy_user@proxy_host': ensure => 'present', table => 'proxy_user@proxy_host', user => 'proxy2@tester', privileges => ['PROXY', 'SELECT'], require => Mysql_user['proxy2@tester'], } MANIFEST it 'fails' do result = apply_manifest(pp, expect_failures: true) expect(result.stderr).to match(%r{`privileges` `parameter`: PROXY can only be specified by itself}) end it 'does not find the user' do result = run_shell('mysql -NBe "SHOW GRANTS FOR proxy2@tester"', expect_failures: true) expect(result.stderr).to match(%r{There is no such grant defined for user 'proxy2' on host 'tester'}) end end describe 'adding proxy privileges with mysql version less than 5.5.0', unless: Gem::Version.new(mysql_version) > Gem::Version.new('5.5.0') do pp = <<-MANIFEST mysql_user { 'proxy3@tester': ensure => present, } mysql_grant { 'proxy3@tester/proxy_user@proxy_host': ensure => 'present', table => 'proxy_user@proxy_host', user => 'proxy3@tester', privileges => ['PROXY', 'SELECT'], require => Mysql_user['proxy3@tester'], } MANIFEST it 'fails' do result = apply_manifest(pp, expect_failures: true) expect(result.stderr).to match(%r{PROXY user not supported on mysql versions < 5\.5\.0}i) end it 'does not find the user' do result = run_shell('mysql -NBe "SHOW GRANTS FOR proxy2@tester"', expect_failures: true) expect(result.stderr).to match(%r{There is no such grant defined for user 'proxy2' on host 'tester'}) end end describe 'adding proxy privileges with invalid proxy user', if: Gem::Version.new(mysql_version) > Gem::Version.new('5.5.0') do pp = <<-MANIFEST mysql_user { 'proxy3@tester': ensure => present, } mysql_grant { 'proxy3@tester/invalid_proxy_user': ensure => 'present', table => 'invalid_proxy_user', user => 'proxy3@tester', privileges => ['PROXY'], require => Mysql_user['proxy3@tester'], } MANIFEST it 'fails' do result = apply_manifest(pp, expect_failures: true) expect(result.stderr).to match(%r{`table` `property` for PROXY should be specified as proxy_user@proxy_host.}) end it 'does not find the user' do result = run_shell('mysql -NBe "SHOW GRANTS FOR proxy3@tester"', expect_failures: true) expect(result.stderr).to contain(%r{There is no such grant defined for user 'proxy3' on host 'tester'}) end end end describe 'grants with skip-name-resolve specified' do pp_one = <<-MANIFEST class { 'mysql::server': override_options => { 'mysqld' => {'skip-name-resolve' => true} }, restart => true, } MANIFEST it 'setup mysql::server' do apply_manifest(pp_one, catch_failures: true) end pp_two = <<-MANIFEST mysql_user { 'test@fqdn.com': ensure => present, } mysql_grant { 'test@fqdn.com/test.*': ensure => 'present', table => 'test.*', user => 'test@fqdn.com', privileges => 'ALL', require => Mysql_user['test@fqdn.com'], } mysql_user { 'test@192.168.5.7': ensure => present, } mysql_grant { 'test@192.168.5.7/test.*': ensure => 'present', table => 'test.*', user => 'test@192.168.5.7', privileges => 'ALL', require => Mysql_user['test@192.168.5.7'], } MANIFEST it 'applies' do apply_manifest(pp_two, catch_failures: true) end it 'fails with fqdn' do pre_run unless Gem::Version.new(mysql_version) > Gem::Version.new('5.7.0') result = run_shell('mysql -NBe "SHOW GRANTS FOR test@fqdn.com"', expect_failures: true) expect(result.stderr).to contain(%r{There is no such grant defined for user 'test' on host 'fqdn.com'}) end end it 'finds ipv4 #stdout' do run_shell("mysql -NBe \"SHOW GRANTS FOR 'test'@'192.168.5.7'\"") do |r| expect(r.stdout).to match(%r{GRANT ALL PRIVILEGES ON `test`.* TO ['|`]test['|`]@['|`]192.168.5.7['|`]}) expect(r.stderr).to be_empty end end pp_three = <<-MANIFEST mysql_user { 'test@fqdn.com': ensure => present, } mysql_grant { 'test@fqdn.com/test.*': ensure => 'present', table => 'test.*', user => 'test@fqdn.com', privileges => 'ALL', require => Mysql_user['test@fqdn.com'], } MANIFEST it 'fails to execute while applying' do mysql_cmd = run_shell('which mysql').stdout.chomp run_shell("mv #{mysql_cmd} #{mysql_cmd}.bak") result = apply_manifest(pp_three, expect_failures: true) expect(result.stderr).to match(%r{Could not find a suitable provider for mysql_grant}) run_shell("mv #{mysql_cmd}.bak #{mysql_cmd}") end pp_four = <<-MANIFEST class { 'mysql::server': restart => true, } MANIFEST it 'reset mysql::server config' do apply_manifest(pp_four, catch_failures: true) end end describe 'adding privileges to specific table' do # Using puppet_apply as a helper pp_one = <<-MANIFEST class { 'mysql::server': override_options => { 'root_password' => 'password' } } MANIFEST it 'setup mysql server' do apply_manifest(pp_one, catch_failures: true) end pp_two = <<-MANIFEST mysql_user { 'test@localhost': ensure => present, } mysql_grant { 'test@localhost/grant_spec_db.grant_spec_table_doesnt_exist': user => 'test@localhost', privileges => ['SELECT'], table => 'grant_spec_db.grant_spec_table_doesnt_exist', require => Mysql_user['test@localhost'], } MANIFEST it 'creates grant on missing table will fail' do result = apply_manifest(pp_two, expect_failures: true) expect(result.stderr).to match(%r{Table 'grant_spec_db\.grant_spec_table_doesnt_exist' doesn't exist}) end pp_three = <<-MANIFEST file { '/tmp/grant_spec_table.sql': ensure => file, content => 'CREATE TABLE grant_spec_table (id int);', before => Mysql::Db['grant_spec_db'], } mysql::db { 'grant_spec_db': user => 'root1', password => 'password', sql => '/tmp/grant_spec_table.sql', } MANIFEST it 'creates table' do apply_manifest(pp_three, catch_failures: true) end it 'has the table' do result = run_shell("mysql -e 'show tables;' grant_spec_db|grep grant_spec_table") expect(result.exit_code).to be_zero end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 4ee263f..ce79d90 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,59 +1,63 @@ # frozen_string_literal: true +RSpec.configure do |c| + c.mock_with :rspec +end + require 'puppetlabs_spec_helper/module_spec_helper' require 'rspec-puppet-facts' require 'spec_helper_local' if File.file?(File.join(File.dirname(__FILE__), 'spec_helper_local.rb')) include RspecPuppetFacts default_facts = { puppetversion: Puppet.version, facterversion: Facter.version, } default_fact_files = [ File.expand_path(File.join(File.dirname(__FILE__), 'default_facts.yml')), File.expand_path(File.join(File.dirname(__FILE__), 'default_module_facts.yml')), ] default_fact_files.each do |f| next unless File.exist?(f) && File.readable?(f) && File.size?(f) begin default_facts.merge!(YAML.safe_load(File.read(f), [], [], true)) rescue => e RSpec.configuration.reporter.message "WARNING: Unable to load #{f}: #{e}" end end # read default_facts and merge them over what is provided by facterdb default_facts.each do |fact, value| add_custom_fact fact, value end RSpec.configure do |c| c.default_facts = default_facts c.before :each do # set to strictest setting for testing # by default Puppet runs at warning level Puppet.settings[:strict] = :warning Puppet.settings[:strict_variables] = true end c.filter_run_excluding(bolt: true) unless ENV['GEM_BOLT'] c.after(:suite) do RSpec::Puppet::Coverage.report!(0) end end # Ensures that a module is defined # @param module_name Name of the module def ensure_module_defined(module_name) module_name.split('::').reduce(Object) do |last_module, next_module| last_module.const_set(next_module, Module.new) unless last_module.const_defined?(next_module, false) last_module.const_get(next_module, false) end end # 'spec_overrides' from sync.yml will appear below this line require 'spec_helper_local' diff --git a/spec/spec_helper_acceptance_local.rb b/spec/spec_helper_acceptance_local.rb index 9604b11..2ac5a08 100644 --- a/spec/spec_helper_acceptance_local.rb +++ b/spec/spec_helper_acceptance_local.rb @@ -1,47 +1,48 @@ # frozen_string_literal: true require 'singleton' class LitmusHelper include Singleton include PuppetLitmus end def pre_run LitmusHelper.instance.apply_manifest("class { 'mysql::server': root_password => 'password' }", catch_failures: true) end def mysql_version shell_output = LitmusHelper.instance.run_shell('mysql --version', expect_failures: true) if shell_output.stdout.match(%r{\d+\.\d+\.\d+}).nil? pre_run shell_output = LitmusHelper.instance.run_shell('mysql --version') raise _('unable to get mysql version') if shell_output.stdout.match(%r{\d+\.\d+\.\d+}).nil? end mysql_version = shell_output.stdout.match(%r{\d+\.\d+\.\d+})[0] mysql_version end def export_locales LitmusHelper.instance.run_shell('echo export PATH="/opt/puppetlabs/bin:$PATH" > ~/.bashrc') LitmusHelper.instance.run_shell('echo export LC_ALL="C" > /etc/profile.d/my-custom.lang.sh') LitmusHelper.instance.run_shell('echo "## US English ##" >> /etc/profile.d/my-custom.lang.sh') LitmusHelper.instance.run_shell('echo export LANG=en_US.UTF-8 >> /etc/profile.d/my-custom.lang.sh') LitmusHelper.instance.run_shell('echo export LANGUAGE=en_US.UTF-8 >> /etc/profile.d/my-custom.lang.sh') LitmusHelper.instance.run_shell('echo export LC_COLLATE=C >> /etc/profile.d/my-custom.lang.sh') LitmusHelper.instance.run_shell('echo export LC_CTYPE=en_US.UTF-8 >> /etc/profile.d/my-custom.lang.sh') LitmusHelper.instance.run_shell('source /etc/profile.d/my-custom.lang.sh') LitmusHelper.instance.run_shell('echo export LC_ALL="C" >> ~/.bashrc') LitmusHelper.instance.run_shell('source ~/.bashrc') end RSpec.configure do |c| c.before :suite do if os[:family] == 'debian' || os[:family] == 'ubuntu' # needed for the puppet fact LitmusHelper.instance.apply_manifest("package { 'lsb-release': ensure => installed, }", expect_failures: false) + LitmusHelper.instance.apply_manifest("package { 'ap': ensure => installed, }", expect_failures: false) end # needed for the grant tests, not installed on el7 docker images LitmusHelper.instance.apply_manifest("package { 'which': ensure => installed, }", expect_failures: false) end end diff --git a/spec/unit/facter/mysql_server_id_spec.rb b/spec/unit/facter/mysql_server_id_spec.rb index f09de60..ee8b75c 100644 --- a/spec/unit/facter/mysql_server_id_spec.rb +++ b/spec/unit/facter/mysql_server_id_spec.rb @@ -1,38 +1,38 @@ # frozen_string_literal: true require 'spec_helper' describe Facter::Util::Fact.to_s do before(:each) do Facter.clear end describe 'mysql_server_id' do context "igalic's laptop" do before :each do - Facter.fact(:macaddress).stubs(:value).returns('3c:97:0e:69:fb:e1') + allow(Facter.fact(:macaddress)).to receive(:value).and_return('3c:97:0e:69:fb:e1') end it do Facter.fact(:mysql_server_id).value.to_s.should == '241857808' end end context 'node with lo only' do before :each do - Facter.fact(:macaddress).stubs(:value).returns('00:00:00:00:00:00') + allow(Facter.fact(:macaddress)).to receive(:value).and_return('00:00:00:00:00:00') end it do Facter.fact(:mysql_server_id).value.to_s.should == '1' end end context 'test nil case' do before :each do - Facter.fact(:macaddress).stubs(:value).returns(nil) + allow(Facter.fact(:macaddress)).to receive(:value).and_return(nil) end it do Facter.fact(:mysql_server_id).value.to_s.should == '' end end end end diff --git a/spec/unit/facter/mysql_version_spec.rb b/spec/unit/facter/mysql_version_spec.rb index abcc6fc..825fb48 100644 --- a/spec/unit/facter/mysql_version_spec.rb +++ b/spec/unit/facter/mysql_version_spec.rb @@ -1,21 +1,21 @@ # frozen_string_literal: true require 'spec_helper' describe Facter::Util::Fact.to_s do before(:each) do Facter.clear end describe 'mysql_version' do context 'with value' do before :each do - Facter::Core::Execution.stubs(:which).returns('fake_mysql_path') - Facter::Util::Resolution.stubs(:exec).with('mysql --version').returns('mysql Ver 14.12 Distrib 5.0.95, for redhat-linux-gnu (x86_64) using readline 5.1') + allow(Facter::Core::Execution).to receive(:which).and_return('fake_mysql_path') + allow(Facter::Util::Resolution).to receive(:exec).with('mysql --version').and_return('mysql Ver 14.12 Distrib 5.0.95, for redhat-linux-gnu (x86_64) using readline 5.1') end it { expect(Facter.fact(:mysql_version).value).to eq('5.0.95') } end end end diff --git a/spec/unit/facter/mysqld_version_spec.rb b/spec/unit/facter/mysqld_version_spec.rb index 2be7108..fafc197 100644 --- a/spec/unit/facter/mysqld_version_spec.rb +++ b/spec/unit/facter/mysqld_version_spec.rb @@ -1,21 +1,22 @@ # frozen_string_literal: true require 'spec_helper' describe Facter::Util::Fact.to_s do before(:each) do Facter.clear end describe 'mysqld_version' do context 'with value' do before :each do - Facter::Core::Execution.stubs(:which).with('mysqld').returns('/usr/sbin/mysqld') - Facter::Util::Resolution.stubs(:exec).with('mysqld --no-defaults -V 2>/dev/null').returns('mysqld Ver 5.5.49-37.9 for Linux on x86_64 (Percona Server (GPL), Release 37.9, Revision efa0073)') + allow(Facter::Core::Execution).to receive(:which).with('mysqld').and_return('/usr/sbin/mysqld') + allow(Facter::Util::Resolution).to receive(:exec).with('mysqld --no-defaults -V 2>/dev/null') + .and_return('mysqld Ver 5.5.49-37.9 for Linux on x86_64 (Percona Server (GPL), Release 37.9, Revision efa0073)') end it { expect(Facter.fact(:mysqld_version).value).to eq('mysqld Ver 5.5.49-37.9 for Linux on x86_64 (Percona Server (GPL), Release 37.9, Revision efa0073)') } end end end diff --git a/spec/unit/puppet/provider/mysql_database/mysql_spec.rb b/spec/unit/puppet/provider/mysql_database/mysql_spec.rb index 5147398..cd974c4 100644 --- a/spec/unit/puppet/provider/mysql_database/mysql_spec.rb +++ b/spec/unit/puppet/provider/mysql_database/mysql_spec.rb @@ -1,114 +1,115 @@ # frozen_string_literal: true require 'spec_helper' describe Puppet::Type.type(:mysql_database).provider(:mysql) do let(:defaults_file) { '--defaults-extra-file=/root/.my.cnf' } let(:parsed_databases) { ['information_schema', 'mydb', 'mysql', 'performance_schema', 'test'] } let(:provider) { resource.provider } let(:instance) { provider.class.instances.first } let(:resource) do Puppet::Type.type(:mysql_database).new( ensure: :present, charset: 'latin1', collate: 'latin1_swedish_ci', name: 'new_database', provider: described_class.name ) end let(:raw_databases) do <<-SQL_OUTPUT information_schema mydb mysql performance_schema test SQL_OUTPUT # rubocop:enable Layout/IndentHeredoc end before :each do - Facter.stubs(:value).with(:root_home).returns('/root') - Puppet::Util.stubs(:which).with('mysql').returns('/usr/bin/mysql') - File.stubs(:file?).with('/root/.my.cnf').returns(true) - provider.class.stubs(:mysql_caller).with('show databases', 'regular').returns('new_database') - provider.class.stubs(:mysql_caller).with(["show variables like '%_database'", 'new_database'], 'regular').returns("character_set_database latin1\ncollation_database latin1_swedish_ci\nskip_show_database OFF") # rubocop:disable Layout/LineLength + allow(Facter.fact(:value)).to receive(:root_home).and_return('/root') + allow(Puppet::Util).to receive(:which).with('mysql').and_return('/usr/bin/mysql') + allow(File).to receive(:file?).with('/root/.my.cnf').and_return(true) + allow(provider.class).to receive(:mysql_caller).with('show databases', 'regular').and_return('new_database') + allow(provider.class).to receive(:mysql_caller).with(["show variables like '%_database'", 'new_database'], 'regular').and_return("character_set_database latin1\ncollation_database latin1_swedish_ci\nskip_show_database OFF") # rubocop:disable Layout/LineLength end describe 'self.instances' do it 'returns an array of databases' do - provider.class.stubs(:mysql_caller).with('show databases', 'regular').returns(raw_databases) + allow(provider.class).to receive(:mysql_caller).with('show databases', 'regular').and_return(raw_databases) raw_databases.each_line do |db| - provider.class.stubs(:mysql_caller).with(["show variables like '%_database'", db.chomp], 'regular').returns("character_set_database latin1\ncollation_database latin1_swedish_ci\nskip_show_database OFF") # rubocop:disable Layout/LineLength + allow(provider.class).to receive(:mysql_caller).with(["show variables like '%_database'", db.chomp], 'regular').and_return("character_set_database latin1\ncollation_database latin1_swedish_ci\nskip_show_database OFF") # rubocop:disable Layout/LineLength end databases = provider.class.instances.map { |x| x.name } expect(parsed_databases).to match_array(databases) end end describe 'self.prefetch' do it 'exists' do provider.class.instances provider.class.prefetch({}) end end describe 'create' do it 'makes a database' do - provider.class.expects(:mysql_caller).with("create database if not exists `#{resource[:name]}` character set `#{resource[:charset]}` collate `#{resource[:collate]}`", 'regular') - provider.expects(:exists?).returns(true) + expect(provider.class).to receive(:mysql_caller).with("create database if not exists `#{resource[:name]}` character set `#{resource[:charset]}` collate `#{resource[:collate]}`", 'regular') + expect(provider).to receive(:exists?).and_return(true) expect(provider.create).to be_truthy end end describe 'destroy' do it 'removes a database if present' do - provider.class.expects(:mysql_caller).with("drop database if exists `#{resource[:name]}`", 'regular') - provider.expects(:exists?).returns(false) + expect(provider.class).to receive(:mysql_caller).with("drop database if exists `#{resource[:name]}`", 'regular') + expect(provider).to receive(:exists?).and_return(false) expect(provider.destroy).to be_truthy end end describe 'exists?' do it 'checks if database exists' do expect(instance).to be_exists end end describe 'self.defaults_file' do + before :each do + allow(Facter).to receive(:value).with(:root_home).and_return('/root') + end it 'sets --defaults-extra-file' do - File.stubs(:file?).with('/root/.my.cnf').returns(true) + allow(File).to receive(:file?).with('/root/.my.cnf').and_return(true) expect(provider.defaults_file).to eq '--defaults-extra-file=/root/.my.cnf' end it 'fails if file missing' do - File.stubs(:file?).with('/root/.my.cnf').returns(false) + allow(File).to receive(:file?).with('/root/.my.cnf').and_return(false) expect(provider.defaults_file).to be_nil end end describe 'charset' do it 'returns a charset' do expect(instance.charset).to eq('latin1') end end describe 'charset=' do it 'changes the charset' do - provider.class.expects(:mysql_caller).with("alter database `#{resource[:name]}` CHARACTER SET blah", 'regular').returns('0') - + expect(provider.class).to receive(:mysql_caller).with("alter database `#{resource[:name]}` CHARACTER SET blah", 'regular').and_return('0') provider.charset = 'blah' end end describe 'collate' do it 'returns a collate' do expect(instance.collate).to eq('latin1_swedish_ci') end end describe 'collate=' do it 'changes the collate' do - provider.class.expects(:mysql_caller).with("alter database `#{resource[:name]}` COLLATE blah", 'regular').returns('0') - + expect(provider.class).to receive(:mysql_caller).with("alter database `#{resource[:name]}` COLLATE blah", 'regular').and_return('0') provider.collate = 'blah' end end end diff --git a/spec/unit/puppet/provider/mysql_login_path/mysql_login_path_spec.rb b/spec/unit/puppet/provider/mysql_login_path/mysql_login_path_spec.rb index 92e83aa..97927d3 100644 --- a/spec/unit/puppet/provider/mysql_login_path/mysql_login_path_spec.rb +++ b/spec/unit/puppet/provider/mysql_login_path/mysql_login_path_spec.rb @@ -1,91 +1,90 @@ # frozen_string_literal: true require 'spec_helper' ensure_module_defined('Puppet::Provider::MysqlLoginPath') require 'puppet/provider/mysql_login_path/mysql_login_path' RSpec.describe Puppet::Provider::MysqlLoginPath::MysqlLoginPath do subject(:provider) { described_class.new } - let(:context) { mock('Puppet::ResourceApi::BaseContext') } - let(:wait_thr) { mock('wait_thr') } - let(:wait_thr_value) { mock('wait_thr_value') } + let(:context) { instance_double('Puppet::ResourceApi::BaseContext') } + let(:wait_thr) { instance_double('wait_thr') } + let(:wait_thr_value) { instance_double('wait_thr_value') } let(:sensitive_secure) { Puppet::Provider::MysqlLoginPath::Sensitive.new('secure') } let(:sensitive_more_secure) { Puppet::Provider::MysqlLoginPath::Sensitive.new('more_secure') } before :each do - Puppet::Util::Execution.stubs(:execute).with(['/usr/bin/getent', 'passwd', 'root'], failonfail: true).returns('root:x:0:0:root:/root:/bin/bash') + # Puppet::Util::Execution.stubs(:execute).with(['/usr/bin/getent', 'passwd', 'root'], failonfail: true).returns('root:x:0:0:root:/root:/bin/bash') + allow(Puppet::Util::Execution).to receive(:execute).with(['/usr/bin/getent', 'passwd', 'root'], failonfail: true).and_return('root:x:0:0:root:/root:/bin/bash') + allow(Puppet::Util::Execution).to receive(:execute).with(['/usr/bin/mysql_config_editor', 'print', '--all'], failonfail: true, uid: 'root', + custom_environment: { 'HOME' => '/root' }).and_return("[local_tcp]\nuser = root\npassword = *****\nhost = 127.0.0.1\nport = 3306") + allow(Puppet::Util::Execution).to receive(:execute).with(['/usr/bin/mysql_config_editor', 'remove', '-G', 'local_socket'], failonfail: true, uid: 'root', custom_environment: { 'HOME' => '/root' }) + allow(Puppet::Util::Execution).to receive(:execute).with(['/usr/bin/my_print_defaults', '-s', 'local_tcp'], failonfail: true, uid: 'root', + custom_environment: { 'HOME' => '/root' }).and_return("--user=root\n--password=secure\n--host=127.0.0.1\n--port=3306") + allow(Puppet::Util::Execution).to receive(:execute).with(['/usr/bin/my_print_defaults', '-s', 'local_socket'], failonfail: true, uid: 'root', custom_environment: { 'HOME' => '/root' }) + .and_return("--user=root\n--password=more_secure\n--host=localhost\n--socket=/var/run/mysql.sock") - Puppet::Util::Execution.stubs(:execute).with(['/usr/bin/mysql_config_editor', 'print', '--all'], failonfail: true, uid: 'root', custom_environment: { 'HOME' => '/root' }) - .returns("[local_tcp]\nuser = root\npassword = *****\nhost = 127.0.0.1\nport = 3306") - Puppet::Util::Execution.stubs(:execute).with(['/usr/bin/mysql_config_editor', 'remove', '-G', 'local_socket'], failonfail: true, uid: 'root', custom_environment: { 'HOME' => '/root' }) - - Puppet::Util::Execution.stubs(:execute).with(['/usr/bin/my_print_defaults', '-s', 'local_tcp'], failonfail: true, uid: 'root', custom_environment: { 'HOME' => '/root' }) - .returns("--user=root\n--password=secure\n--host=127.0.0.1\n--port=3306") - Puppet::Util::Execution.stubs(:execute).with(['/usr/bin/my_print_defaults', '-s', 'local_socket'], failonfail: true, uid: 'root', custom_environment: { 'HOME' => '/root' }) - .returns("--user=root\n--password=more_secure\n--host=localhost\n--socket=/var/run/mysql.sock") - - Puppet::Util::SUIDManager.stubs(:asuser).with('root').returns(`(exit 0)`) - PTY.stubs(:spawn) - .with({ 'HOME' => '/root' }, + allow(Puppet::Util::SUIDManager).to receive(:asuser).with('root').and_return(`(exit 0)`) + allow(PTY).to receive(:spawn) + .with({ 'HOME' => '/root' }, '/usr/bin/mysql_config_editor set --skip-warn -G local_socket -h localhost -u root ' \ '-S /var/run/mysql/mysql.sock -p') - .returns(`(exit 0)`) + .and_return(`(exit 0)`) - PTY.stubs(:spawn) - .with({ 'HOME' => '/root' }, + allow(PTY).to receive(:spawn) + .with({ 'HOME' => '/root' }, '/usr/bin/mysql_config_editor set --skip-warn -G local_socket -h 127.0.0.1 -u root -P 3306 -p') - .returns(`(exit 0)`) + .and_return(`(exit 0)`) end describe '#get' do it 'processes resources' do expect(provider.get(context, [{ owner: 'root' }])).to eq [ { ensure: 'present', host: '127.0.0.1', name: 'local_tcp', owner: 'root', password: sensitive_secure, port: 3306, socket: nil, title: 'local_tcp-root', user: 'root', }, ] end end describe 'create(context, name, should)' do it 'creates the resource' do provider.create(context, { name: 'local_socket', owner: 'root' }, name: 'local_socket', owner: 'root', host: 'localhost', user: 'root', password: sensitive_more_secure, socket: '/var/run/mysql/mysql.sock', ensure: 'present') end end describe 'update(context, name, should)' do it 'updates the resource' do provider.update(context, { name: 'local_socket', owner: 'root' }, name: 'local_socket', owner: 'root', host: '127.0.0.1', user: 'root', password: sensitive_more_secure, port: 3306, ensure: 'present') end end describe 'delete(context, name)' do it 'deletes the resource' do provider.delete(context, name: 'local_socket', owner: 'root') end end end diff --git a/spec/unit/puppet/provider/mysql_plugin/mysql_spec.rb b/spec/unit/puppet/provider/mysql_plugin/mysql_spec.rb index 2a4bad6..a7ad0eb 100644 --- a/spec/unit/puppet/provider/mysql_plugin/mysql_spec.rb +++ b/spec/unit/puppet/provider/mysql_plugin/mysql_spec.rb @@ -1,70 +1,70 @@ # frozen_string_literal: true require 'spec_helper' describe Puppet::Type.type(:mysql_plugin).provider(:mysql) do let(:defaults_file) { '--defaults-extra-file=/root/.my.cnf' } let(:provider) { resource.provider } let(:instance) { provider.class.instances.first } let(:resource) do Puppet::Type.type(:mysql_plugin).new( ensure: :present, soname: 'auth_socket.so', name: 'auth_socket', provider: described_class.name, ) end before :each do - Facter.stubs(:value).with(:root_home).returns('/root') - Puppet::Util.stubs(:which).with('mysql').returns('/usr/bin/mysql') - File.stubs(:file?).with('/root/.my.cnf').returns(true) - provider.class.stubs(:mysql_caller).with('show plugins', 'regular').returns('auth_socket ACTIVE AUTHENTICATION auth_socket.so GPL') + allow(Facter).to receive(:value).with(:root_home).and_return('/root') + allow(Puppet::Util).to receive(:which).with('mysql').and_return('/usr/bin/mysql') + allow(File).to receive(:file?).with('/root/.my.cnf').and_return(true) + allow(provider.class).to receive(:mysql_caller).with('show plugins', 'regular').and_return('auth_socket ACTIVE AUTHENTICATION auth_socket.so GPL') end describe 'self.prefetch' do it 'exists' do provider.class.instances provider.class.prefetch({}) end end describe 'create' do it 'loads a plugin' do - provider.class.expects(:mysql_caller).with("install plugin #{resource[:name]} soname '#{resource[:soname]}'", 'regular') - provider.expects(:exists?).returns(true) + expect(provider.class).to receive(:mysql_caller).with("install plugin #{resource[:name]} soname '#{resource[:soname]}'", 'regular') + expect(provider).to receive(:exists?).and_return(true) expect(provider.create).to be_truthy end end describe 'destroy' do it 'unloads a plugin if present' do - provider.class.expects(:mysql_caller).with("uninstall plugin #{resource[:name]}", 'regular') - provider.expects(:exists?).returns(false) + expect(provider.class).to receive(:mysql_caller).with("uninstall plugin #{resource[:name]}", 'regular') + expect(provider).to receive(:exists?).and_return(false) expect(provider.destroy).to be_truthy end end describe 'exists?' do it 'checks if plugin exists' do expect(instance).to be_exists end end describe 'self.defaults_file' do it 'sets --defaults-extra-file' do - File.stubs(:file?).with('/root/.my.cnf').returns(true) + allow(File).to receive(:file?).with('/root/.my.cnf').and_return(true) expect(provider.defaults_file).to eq '--defaults-extra-file=/root/.my.cnf' end it 'fails if file missing' do - File.stubs(:file?).with('/root/.my.cnf').returns(false) + allow(File).to receive(:file?).with('/root/.my.cnf').and_return(false) expect(provider.defaults_file).to be_nil end end describe 'soname' do it 'returns a soname' do expect(instance.soname).to eq('auth_socket.so') end end end diff --git a/spec/unit/puppet/provider/mysql_user/mysql_spec.rb b/spec/unit/puppet/provider/mysql_user/mysql_spec.rb index ba72cc1..36a5471 100644 --- a/spec/unit/puppet/provider/mysql_user/mysql_spec.rb +++ b/spec/unit/puppet/provider/mysql_user/mysql_spec.rb @@ -1,497 +1,505 @@ # frozen_string_literal: true require 'spec_helper' describe Puppet::Type.type(:mysql_user).provider(:mysql) do # Output of mysqld -V mysql_version_string_hash = { 'mysql-5.5' => { version: '5.5.46', string: '/usr/sbin/mysqld Ver 5.5.46-log for Linux on x86_64 (MySQL Community Server (GPL))', mysql_type: 'mysql', }, 'mysql-5.6' => { version: '5.6.27', string: '/usr/sbin/mysqld Ver 5.6.27 for Linux on x86_64 (MySQL Community Server (GPL))', mysql_type: 'mysql', }, 'mysql-5.7.1' => { version: '5.7.1', string: '/usr/sbin/mysqld Ver 5.7.1 for Linux on x86_64 (MySQL Community Server (GPL))', mysql_type: 'mysql', }, 'mysql-5.7.6' => { version: '5.7.8', string: '/usr/sbin/mysqld Ver 5.7.8-rc for Linux on x86_64 (MySQL Community Server (GPL))', mysql_type: 'mysql', }, 'mariadb-10.0' => { version: '10.0.21', string: '/usr/sbin/mysqld Ver 10.0.21-MariaDB for Linux on x86_64 (MariaDB Server)', mysql_type: 'mariadb', }, 'mariadb-10.0-deb8' => { version: '10.0.23', string: '/usr/sbin/mysqld (mysqld 10.0.23-MariaDB-0+deb8u1)', mysql_type: 'mariadb', }, 'mariadb-10.1.44' => { version: '10.1.44', string: '/usr/sbin/mysqld (mysqld 10.1.44-MariaDB-1~bionic)', mysql_type: 'mariadb', }, 'mariadb-10.3.22' => { version: '10.3.22', string: '/usr/sbin/mysqld (mysqld 10.3.22-MariaDB-0+deb10u1)', mysql_type: 'mariadb', }, 'percona-5.5' => { version: '5.5.39', string: 'mysqld Ver 5.5.39-36.0-55 for Linux on x86_64 (Percona XtraDB Cluster (GPL), Release rel36.0, Revision 824, WSREP version 25.11, wsrep_25.11.r4023)', mysql_type: 'percona', }, } let(:defaults_file) { '--defaults-extra-file=/root/.my.cnf' } let(:system_database) { '--database=mysql' } let(:newhash) { '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF5' } let(:raw_users) do <<-SQL_OUTPUT root@127.0.0.1 root@::1 @localhost debian-sys-maint@localhost root@localhost usvn_user@localhost @vagrant-ubuntu-raring-64 SQL_OUTPUT # rubocop:enable Layout/IndentHeredoc end let(:parsed_users) { ['root@127.0.0.1', 'root@::1', '@localhost', 'debian-sys-maint@localhost', 'root@localhost', 'usvn_user@localhost', '@vagrant-ubuntu-raring-64'] } let(:provider) { resource.provider } let(:instance) { provider.class.instances.first } let(:resource) do Puppet::Type.type(:mysql_user).new( ensure: :present, password_hash: '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4', name: 'joe@localhost', max_user_connections: '10', max_connections_per_hour: '10', max_queries_per_hour: '10', max_updates_per_hour: '10', provider: described_class.name, ) end before :each do # Set up the stubs for an instances call. - Facter.stubs(:value).with(:root_home).returns('/root') - Facter.stubs(:value).with(:mysql_version).returns('5.6.24') + allow(Facter).to receive(:value).with(:root_home).and_return('/root') + allow(Facter).to receive(:value).with(:mysql_version).and_return('5.6.24') provider.class.instance_variable_set(:@mysqld_version_string, '5.6.24') - Puppet::Util.stubs(:which).with('mysql').returns('/usr/bin/mysql') - Puppet::Util.stubs(:which).with('mysqld').returns('/usr/sbin/mysqld') - File.stubs(:file?).with('/root/.my.cnf').returns(true) - provider.class.stubs(:mysql_caller).with("SELECT CONCAT(User, '@',Host) AS User FROM mysql.user", 'regular').returns('joe@localhost') - provider.class.stubs(:mysql_caller).with("SELECT MAX_USER_CONNECTIONS, MAX_CONNECTIONS, MAX_QUESTIONS, MAX_UPDATES, SSL_TYPE, SSL_CIPHER, X509_ISSUER, X509_SUBJECT, PASSWORD /*!50508 , PLUGIN */ FROM mysql.user WHERE CONCAT(user, '@', host) = 'joe@localhost'", 'regular').returns('10 10 10 10 *6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4') # rubocop:disable Layout/LineLength + allow(Puppet::Util).to receive(:which).with('mysql').and_return('/usr/bin/mysql') + allow(Puppet::Util).to receive(:which).with('mysqld').and_return('/usr/sbin/mysqld') + allow(File).to receive(:file?).with('/root/.my.cnf').and_return(true) + allow(provider.class).to receive(:mysql_caller).with("SELECT CONCAT(User, '@',Host) AS User FROM mysql.user", 'regular').and_return('joe@localhost') + allow(provider.class).to receive(:mysql_caller).with("SELECT MAX_USER_CONNECTIONS, MAX_CONNECTIONS, MAX_QUESTIONS, MAX_UPDATES, SSL_TYPE, SSL_CIPHER, X509_ISSUER, X509_SUBJECT, PASSWORD /*!50508 , PLUGIN */ FROM mysql.user WHERE CONCAT(user, '@', host) = 'joe@localhost'", 'regular').and_return('10 10 10 10 *6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4') # rubocop:disable Layout/LineLength end describe 'self.instances' do it 'returns an array of users MySQL 5.5' do provider.class.instance_variable_set(:@mysqld_version_string, mysql_version_string_hash['mysql-5.5'][:string]) - provider.class.stubs(:mysql_caller).with("SELECT CONCAT(User, '@',Host) AS User FROM mysql.user", 'regular').returns(raw_users) - parsed_users.each { |user| provider.class.stubs(:mysql_caller).with("SELECT MAX_USER_CONNECTIONS, MAX_CONNECTIONS, MAX_QUESTIONS, MAX_UPDATES, SSL_TYPE, SSL_CIPHER, X509_ISSUER, X509_SUBJECT, PASSWORD /*!50508 , PLUGIN */ FROM mysql.user WHERE CONCAT(user, '@', host) = '#{user}'", 'regular').returns('10 10 10 10 ') } # rubocop:disable Layout/LineLength + allow(provider.class).to receive(:mysql_caller).with("SELECT CONCAT(User, '@',Host) AS User FROM mysql.user", 'regular').and_return(raw_users) + parsed_users.each { |user| allow(provider.class).to receive(:mysql_caller).with("SELECT MAX_USER_CONNECTIONS, MAX_CONNECTIONS, MAX_QUESTIONS, MAX_UPDATES, SSL_TYPE, SSL_CIPHER, X509_ISSUER, X509_SUBJECT, PASSWORD /*!50508 , PLUGIN */ FROM mysql.user WHERE CONCAT(user, '@', host) = '#{user}'", 'regular').and_return('10 10 10 10 ') } # rubocop:disable Layout/LineLength usernames = provider.class.instances.map { |x| x.name } expect(parsed_users).to match_array(usernames) end it 'returns an array of users MySQL 5.6' do provider.class.instance_variable_set(:@mysqld_version_string, mysql_version_string_hash['mysql-5.6'][:string]) - provider.class.stubs(:mysql_caller).with("SELECT CONCAT(User, '@',Host) AS User FROM mysql.user", 'regular').returns(raw_users) - parsed_users.each { |user| provider.class.stubs(:mysql_caller).with("SELECT MAX_USER_CONNECTIONS, MAX_CONNECTIONS, MAX_QUESTIONS, MAX_UPDATES, SSL_TYPE, SSL_CIPHER, X509_ISSUER, X509_SUBJECT, PASSWORD /*!50508 , PLUGIN */ FROM mysql.user WHERE CONCAT(user, '@', host) = '#{user}'", 'regular').returns('10 10 10 10 ') } # rubocop:disable Layout/LineLength + allow(provider.class).to receive(:mysql_caller).with("SELECT CONCAT(User, '@',Host) AS User FROM mysql.user", 'regular').and_return(raw_users) + parsed_users.each { |user| allow(provider.class).to receive(:mysql_caller).with("SELECT MAX_USER_CONNECTIONS, MAX_CONNECTIONS, MAX_QUESTIONS, MAX_UPDATES, SSL_TYPE, SSL_CIPHER, X509_ISSUER, X509_SUBJECT, PASSWORD /*!50508 , PLUGIN */ FROM mysql.user WHERE CONCAT(user, '@', host) = '#{user}'", 'regular').and_return('10 10 10 10 ') } # rubocop:disable Layout/LineLength usernames = provider.class.instances.map { |x| x.name } expect(parsed_users).to match_array(usernames) end it 'returns an array of users MySQL >= 5.7.0 < 5.7.6' do provider.class.instance_variable_set(:@mysqld_version_string, mysql_version_string_hash['mysql-5.7.1'][:string]) - provider.class.stubs(:mysql_caller).with("SELECT CONCAT(User, '@',Host) AS User FROM mysql.user", 'regular').returns(raw_users) - parsed_users.each { |user| provider.class.stubs(:mysql_caller).with("SELECT MAX_USER_CONNECTIONS, MAX_CONNECTIONS, MAX_QUESTIONS, MAX_UPDATES, SSL_TYPE, SSL_CIPHER, X509_ISSUER, X509_SUBJECT, PASSWORD /*!50508 , PLUGIN */ FROM mysql.user WHERE CONCAT(user, '@', host) = '#{user}'", 'regular').returns('10 10 10 10 ') } # rubocop:disable Layout/LineLength + allow(provider.class).to receive(:mysql_caller).with("SELECT CONCAT(User, '@',Host) AS User FROM mysql.user", 'regular').and_return(raw_users) + parsed_users.each { |user| allow(provider.class).to receive(:mysql_caller).with("SELECT MAX_USER_CONNECTIONS, MAX_CONNECTIONS, MAX_QUESTIONS, MAX_UPDATES, SSL_TYPE, SSL_CIPHER, X509_ISSUER, X509_SUBJECT, PASSWORD /*!50508 , PLUGIN */ FROM mysql.user WHERE CONCAT(user, '@', host) = '#{user}'", 'regular').and_return('10 10 10 10 ') } # rubocop:disable Layout/LineLength usernames = provider.class.instances.map { |x| x.name } expect(parsed_users).to match_array(usernames) end it 'returns an array of users MySQL >= 5.7.6' do provider.class.instance_variable_set(:@mysqld_version_string, mysql_version_string_hash['mysql-5.7.6'][:string]) - provider.class.stubs(:mysql_caller).with("SELECT CONCAT(User, '@',Host) AS User FROM mysql.user", 'regular').returns(raw_users) - parsed_users.each { |user| provider.class.stubs(:mysql_caller).with("SELECT MAX_USER_CONNECTIONS, MAX_CONNECTIONS, MAX_QUESTIONS, MAX_UPDATES, SSL_TYPE, SSL_CIPHER, X509_ISSUER, X509_SUBJECT, AUTHENTICATION_STRING, PLUGIN FROM mysql.user WHERE CONCAT(user, '@', host) = '#{user}'", 'regular').returns('10 10 10 10 ') } # rubocop:disable Layout/LineLength + allow(provider.class).to receive(:mysql_caller).with("SELECT CONCAT(User, '@',Host) AS User FROM mysql.user", 'regular').and_return(raw_users) + parsed_users.each { |user| allow(provider.class).to receive(:mysql_caller).with("SELECT MAX_USER_CONNECTIONS, MAX_CONNECTIONS, MAX_QUESTIONS, MAX_UPDATES, SSL_TYPE, SSL_CIPHER, X509_ISSUER, X509_SUBJECT, AUTHENTICATION_STRING, PLUGIN FROM mysql.user WHERE CONCAT(user, '@', host) = '#{user}'", 'regular').and_return('10 10 10 10 ') } # rubocop:disable Layout/LineLength usernames = provider.class.instances.map { |x| x.name } expect(parsed_users).to match_array(usernames) end it 'returns an array of users mariadb 10.0' do provider.class.instance_variable_set(:@mysqld_version_string, mysql_version_string_hash['mariadb-10.0'][:string]) - provider.class.stubs(:mysql_caller).with("SELECT CONCAT(User, '@',Host) AS User FROM mysql.user", 'regular').returns(raw_users) - parsed_users.each { |user| provider.class.stubs(:mysql_caller).with("SELECT MAX_USER_CONNECTIONS, MAX_CONNECTIONS, MAX_QUESTIONS, MAX_UPDATES, SSL_TYPE, SSL_CIPHER, X509_ISSUER, X509_SUBJECT, PASSWORD /*!50508 , PLUGIN */ FROM mysql.user WHERE CONCAT(user, '@', host) = '#{user}'", 'regular').returns('10 10 10 10 ') } # rubocop:disable Layout/LineLength + allow(provider.class).to receive(:mysql_caller).with("SELECT CONCAT(User, '@',Host) AS User FROM mysql.user", 'regular').and_return(raw_users) + parsed_users.each { |user| allow(provider.class).to receive(:mysql_caller).with("SELECT MAX_USER_CONNECTIONS, MAX_CONNECTIONS, MAX_QUESTIONS, MAX_UPDATES, SSL_TYPE, SSL_CIPHER, X509_ISSUER, X509_SUBJECT, PASSWORD /*!50508 , PLUGIN */ FROM mysql.user WHERE CONCAT(user, '@', host) = '#{user}'", 'regular').and_return('10 10 10 10 ') } # rubocop:disable Layout/LineLength usernames = provider.class.instances.map { |x| x.name } expect(parsed_users).to match_array(usernames) end it 'returns an array of users mariadb >= 10.1.21' do provider.class.instance_variable_set(:@mysqld_version_string, mysql_version_string_hash['mariadb-10.1.44'][:string]) - provider.class.stubs(:mysql_caller).with("SELECT CONCAT(User, '@',Host) AS User FROM mysql.user", 'regular').returns(raw_users) - parsed_users.each { |user| provider.class.stubs(:mysql_caller).with("SELECT MAX_USER_CONNECTIONS, MAX_CONNECTIONS, MAX_QUESTIONS, MAX_UPDATES, SSL_TYPE, SSL_CIPHER, X509_ISSUER, X509_SUBJECT, PASSWORD, PLUGIN, AUTHENTICATION_STRING FROM mysql.user WHERE CONCAT(user, '@', host) = '#{user}'", 'regular').returns('10 10 10 10 ') } # rubocop:disable Layout/LineLength + allow(provider.class).to receive(:mysql_caller).with("SELECT CONCAT(User, '@',Host) AS User FROM mysql.user", 'regular').and_return(raw_users) + parsed_users.each { |user| allow(provider.class).to receive(:mysql_caller).with("SELECT MAX_USER_CONNECTIONS, MAX_CONNECTIONS, MAX_QUESTIONS, MAX_UPDATES, SSL_TYPE, SSL_CIPHER, X509_ISSUER, X509_SUBJECT, PASSWORD, PLUGIN, AUTHENTICATION_STRING FROM mysql.user WHERE CONCAT(user, '@', host) = '#{user}'", 'regular').and_return('10 10 10 10 ') } # rubocop:disable Layout/LineLength usernames = provider.class.instances.map { |x| x.name } expect(parsed_users).to match_array(usernames) end it 'returns an array of users percona 5.5' do provider.class.instance_variable_set(:@mysqld_version_string, mysql_version_string_hash['percona-5.5'][:string]) - provider.class.stubs(:mysql_caller).with("SELECT CONCAT(User, '@',Host) AS User FROM mysql.user", 'regular').returns(raw_users) - parsed_users.each { |user| provider.class.stubs(:mysql_caller).with("SELECT MAX_USER_CONNECTIONS, MAX_CONNECTIONS, MAX_QUESTIONS, MAX_UPDATES, SSL_TYPE, SSL_CIPHER, X509_ISSUER, X509_SUBJECT, PASSWORD /*!50508 , PLUGIN */ FROM mysql.user WHERE CONCAT(user, '@', host) = '#{user}'", 'regular').returns('10 10 10 10 ') } # rubocop:disable Layout/LineLength + allow(provider.class).to receive(:mysql_caller).with("SELECT CONCAT(User, '@',Host) AS User FROM mysql.user", 'regular').and_return(raw_users) + parsed_users.each { |user| allow(provider.class).to receive(:mysql_caller).with("SELECT MAX_USER_CONNECTIONS, MAX_CONNECTIONS, MAX_QUESTIONS, MAX_UPDATES, SSL_TYPE, SSL_CIPHER, X509_ISSUER, X509_SUBJECT, PASSWORD /*!50508 , PLUGIN */ FROM mysql.user WHERE CONCAT(user, '@', host) = '#{user}'", 'regular').and_return('10 10 10 10 ') } # rubocop:disable Layout/LineLength usernames = provider.class.instances.map { |x| x.name } expect(parsed_users).to match_array(usernames) end end describe 'mysql version and type detection' do mysql_version_string_hash.each do |_name, line| version = line[:version] string = line[:string] mysql_type = line[:mysql_type] it "detects version '#{version}'" do provider.class.instance_variable_set(:@mysqld_version_string, string) expect(provider.mysqld_version).to eq(version) end it "detects type '#{mysql_type}'" do provider.class.instance_variable_set(:@mysqld_version_string, string) expect(provider.mysqld_type).to eq(mysql_type) end end end describe 'self.prefetch' do it 'exists' do provider.class.instances provider.class.prefetch({}) end end describe 'create' do it 'makes a user' do - provider.class.expects(:mysql_caller).with("CREATE USER 'joe'@'localhost' IDENTIFIED BY PASSWORD '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4'", 'system') - provider.class.expects(:mysql_caller).with("GRANT USAGE ON *.* TO 'joe'@'localhost' WITH MAX_USER_CONNECTIONS 10 MAX_CONNECTIONS_PER_HOUR 10 MAX_QUERIES_PER_HOUR 10 MAX_UPDATES_PER_HOUR 10", 'system') # rubocop:disable Layout/LineLength - provider.class.expects(:mysql_caller).with("GRANT USAGE ON *.* TO 'joe'@'localhost' REQUIRE NONE", 'system') - provider.expects(:exists?).returns(true) + expect(provider.class).to receive(:mysql_caller).with("CREATE USER 'joe'@'localhost' IDENTIFIED BY PASSWORD '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4'", 'system') + expect(provider.class).to receive(:mysql_caller).with("GRANT USAGE ON *.* TO 'joe'@'localhost' WITH MAX_USER_CONNECTIONS 10 MAX_CONNECTIONS_PER_HOUR 10 MAX_QUERIES_PER_HOUR 10 MAX_UPDATES_PER_HOUR 10", 'system') # rubocop:disable Layout/LineLength + expect(provider.class).to receive(:mysql_caller).with("GRANT USAGE ON *.* TO 'joe'@'localhost' REQUIRE NONE", 'system') + expect(provider).to receive(:exists?).and_return(true) expect(provider.create).to be_truthy end it 'creates a user using IF NOT EXISTS' do provider.class.instance_variable_set(:@mysqld_version_string, '5.7.6') - provider.class.expects(:mysql_caller).with("CREATE USER IF NOT EXISTS 'joe'@'localhost' IDENTIFIED WITH 'mysql_native_password' AS '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4'", 'system') - provider.class.expects(:mysql_caller).with("ALTER USER IF EXISTS 'joe'@'localhost' WITH MAX_USER_CONNECTIONS 10 MAX_CONNECTIONS_PER_HOUR 10 MAX_QUERIES_PER_HOUR 10 MAX_UPDATES_PER_HOUR 10", 'system') # rubocop:disable Layout/LineLength - provider.class.expects(:mysql_caller).with("ALTER USER 'joe'@'localhost' REQUIRE NONE", 'system') - provider.expects(:exists?).returns(true) + expect(provider.class).to receive(:mysql_caller).with("CREATE USER IF NOT EXISTS 'joe'@'localhost' IDENTIFIED WITH 'mysql_native_password' AS '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4'", +'system') + expect(provider.class).to receive(:mysql_caller).with("ALTER USER IF EXISTS 'joe'@'localhost' WITH MAX_USER_CONNECTIONS 10 MAX_CONNECTIONS_PER_HOUR 10 MAX_QUERIES_PER_HOUR 10 MAX_UPDATES_PER_HOUR 10", 'system') # rubocop:disable Layout/LineLength + expect(provider.class).to receive(:mysql_caller).with("ALTER USER 'joe'@'localhost' REQUIRE NONE", 'system') + expect(provider).to receive(:exists?).and_return(true) expect(provider.create).to be_truthy end end describe 'destroy' do it 'removes a user if present' do - provider.class.expects(:mysql_caller).with("DROP USER 'joe'@'localhost'", 'system') - provider.expects(:exists?).returns(false) + expect(provider.class).to receive(:mysql_caller).with("DROP USER 'joe'@'localhost'", 'system') + expect(provider).to receive(:exists?).and_return(false) expect(provider.destroy).to be_truthy end it 'removes a user using IF EXISTS' do provider.class.instance_variable_set(:@mysqld_version_string, '5.7.1') - provider.class.expects(:mysql_caller).with("DROP USER IF EXISTS 'joe'@'localhost'", 'system') + expect(provider.class).to receive(:mysql_caller).with("DROP USER IF EXISTS 'joe'@'localhost'", 'system') expect(provider.destroy).to be_truthy end end describe 'exists?' do it 'checks if user exists' do expect(instance).to be_exists end end describe 'self.mysqld_version' do it 'uses the mysqld_version fact if unset' do provider.class.instance_variable_set(:@mysqld_version_string, nil) - Facter.stubs(:value).with(:mysqld_version).returns('5.6.24') + allow(Facter).to receive(:value).with(:mysqld_version).and_return('5.6.24') expect(provider.mysqld_version).to eq '5.6.24' end it 'returns 5.7.6 for "mysqld Ver 5.7.6 for Linux on x86_64 (MySQL Community Server (GPL))"' do provider.class.instance_variable_set(:@mysqld_version_string, 'mysqld Ver 5.7.6 for Linux on x86_64 (MySQL Community Server (GPL))') expect(provider.mysqld_version).to eq '5.7.6' end it 'returns 5.7.6 for "mysqld Ver 5.7.6-rc for Linux on x86_64 (MySQL Community Server (GPL))"' do provider.class.instance_variable_set(:@mysqld_version_string, 'mysqld Ver 5.7.6-rc for Linux on x86_64 (MySQL Community Server (GPL))') expect(provider.mysqld_version).to eq '5.7.6' end it 'detects >= 5.7.6 for 5.7.7-log' do provider.class.instance_variable_set(:@mysqld_version_string, 'mysqld Ver 5.7.7-log for Linux on x86_64 (MySQL Community Server (GPL))') expect(Puppet::Util::Package.versioncmp(provider.mysqld_version, '5.7.6')).to be >= 0 end it 'detects < 5.7.6 for 5.7.5-log' do provider.class.instance_variable_set(:@mysqld_version_string, 'mysqld Ver 5.7.5-log for Linux on x86_64 (MySQL Community Server (GPL))') expect(Puppet::Util::Package.versioncmp(provider.mysqld_version, '5.7.6')).to be < 0 end end describe 'self.defaults_file' do it 'sets --defaults-extra-file' do - File.stubs(:file?).with('/root/.my.cnf').returns(true) + allow(File).to receive(:file?).with('/root/.my.cnf').and_return(true) expect(provider.defaults_file).to eq '--defaults-extra-file=/root/.my.cnf' end it 'fails if file missing' do - File.expects(:file?).with('/root/.my.cnf').returns(false) + expect(File).to receive(:file?).with('/root/.my.cnf').and_return(false) expect(provider.defaults_file).to be_nil end end describe 'password_hash' do it 'returns a hash' do expect(instance.password_hash).to eq('*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4') end end describe 'password_hash=' do it 'changes the hash mysql 5.5' do provider.class.instance_variable_set(:@mysqld_version_string, mysql_version_string_hash['mysql-5.5'][:string]) - provider.class.expects(:mysql_caller).with("SET PASSWORD FOR 'joe'@'localhost' = '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF5'", 'system').returns('0') + expect(provider.class).to receive(:mysql_caller).with("SET PASSWORD FOR 'joe'@'localhost' = '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF5'", 'system').and_return('0') - provider.expects(:password_hash).returns('*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF5') + expect(provider).to receive(:password_hash).and_return('*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF5') provider.password_hash = '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF5' end it 'changes the hash mysql 5.6' do provider.class.instance_variable_set(:@mysqld_version_string, mysql_version_string_hash['mysql-5.6'][:string]) - provider.class.expects(:mysql_caller).with("SET PASSWORD FOR 'joe'@'localhost' = '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF5'", 'system').returns('0') + expect(provider.class).to receive(:mysql_caller).with("SET PASSWORD FOR 'joe'@'localhost' = '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF5'", 'system').and_return('0') - provider.expects(:password_hash).returns('*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF5') + expect(provider).to receive(:password_hash).and_return('*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF5') provider.password_hash = '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF5' end it 'changes the hash mysql < 5.7.6' do provider.class.instance_variable_set(:@mysqld_version_string, mysql_version_string_hash['mysql-5.7.1'][:string]) - provider.class.expects(:mysql_caller).with("SET PASSWORD FOR 'joe'@'localhost' = '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF5'", 'system').returns('0') + expect(provider.class).to receive(:mysql_caller).with("SET PASSWORD FOR 'joe'@'localhost' = '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF5'", 'system').and_return('0') - provider.expects(:password_hash).returns('*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF5') + expect(provider).to receive(:password_hash).and_return('*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF5') provider.password_hash = '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF5' end it 'changes the hash MySQL >= 5.7.6' do provider.class.instance_variable_set(:@mysqld_version_string, mysql_version_string_hash['mysql-5.7.6'][:string]) - provider.class.expects(:mysql_caller).with("ALTER USER 'joe'@'localhost' IDENTIFIED WITH mysql_native_password AS '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF5'", 'system').returns('0') + expect(provider.class).to receive(:mysql_caller).with("ALTER USER 'joe'@'localhost' IDENTIFIED WITH mysql_native_password AS '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF5'", +'system').and_return('0') - provider.expects(:password_hash).returns('*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF5') + expect(provider).to receive(:password_hash).and_return('*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF5') provider.password_hash = '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF5' end it 'changes the hash mariadb-10.0' do provider.class.instance_variable_set(:@mysqld_version_string, mysql_version_string_hash['mariadb-10.0'][:string]) - provider.class.expects(:mysql_caller).with("SET PASSWORD FOR 'joe'@'localhost' = '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF5'", 'system').returns('0') + expect(provider.class).to receive(:mysql_caller).with("SET PASSWORD FOR 'joe'@'localhost' = '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF5'", 'system').and_return('0') - provider.expects(:password_hash).returns('*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF5') + expect(provider).to receive(:password_hash).and_return('*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF5') provider.password_hash = '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF5' end it 'changes the hash to an ed25519 hash mariadb >= 10.1.21 and < 10.2.0' do provider.class.instance_variable_set(:@mysqld_version_string, mysql_version_string_hash['mariadb-10.1.44'][:string]) - resource.stubs(:value).with(:plugin).returns('ed25519') - provider.class.expects(:mysql_caller).with("UPDATE mysql.user SET password = '', plugin = 'ed25519', authentication_string = 'z0pjExBYbzbupUByZRrQvC6kRCcE8n/tC7kUdUD11fU' where CONCAT(user, '@', host) = 'joe@localhost'; FLUSH PRIVILEGES", 'system').returns('0') # rubocop:disable Layout/LineLength - provider.expects(:password_hash).returns('z0pjExBYbzbupUByZRrQvC6kRCcE8n/tC7kUdUD11fU') + allow(resource).to receive(:value).with(:plugin).and_return('ed25519') + expect(provider.class).to receive(:mysql_caller).with("UPDATE mysql.user SET password = '', plugin = 'ed25519', authentication_string = 'z0pjExBYbzbupUByZRrQvC6kRCcE8n/tC7kUdUD11fU' where CONCAT(user, '@', host) = 'joe@localhost'; FLUSH PRIVILEGES", 'system').and_return('0') # rubocop:disable Layout/LineLength + expect(provider).to receive(:password_hash).and_return('z0pjExBYbzbupUByZRrQvC6kRCcE8n/tC7kUdUD11fU') provider.password_hash = 'z0pjExBYbzbupUByZRrQvC6kRCcE8n/tC7kUdUD11fU' end it 'changes the hash to an ed25519 hash mariadb >= 10.2.0' do provider.class.instance_variable_set(:@mysqld_version_string, mysql_version_string_hash['mariadb-10.3.22'][:string]) - resource.stubs(:value).with(:plugin).returns('ed25519') - provider.class.expects(:mysql_caller).with("ALTER USER 'joe'@'localhost' IDENTIFIED WITH ed25519 AS 'z0pjExBYbzbupUByZRrQvC6kRCcE8n/tC7kUdUD11fU'", 'system').returns('0') - provider.expects(:password_hash).returns('z0pjExBYbzbupUByZRrQvC6kRCcE8n/tC7kUdUD11fU') + allow(resource).to receive(:value).with(:plugin).and_return('ed25519') + expect(provider.class).to receive(:mysql_caller).with("ALTER USER 'joe'@'localhost' IDENTIFIED WITH ed25519 AS 'z0pjExBYbzbupUByZRrQvC6kRCcE8n/tC7kUdUD11fU'", 'system').and_return('0') + expect(provider).to receive(:password_hash).and_return('z0pjExBYbzbupUByZRrQvC6kRCcE8n/tC7kUdUD11fU') provider.password_hash = 'z0pjExBYbzbupUByZRrQvC6kRCcE8n/tC7kUdUD11fU' end it 'changes the hash to an invalid ed25519 hash mariadb >= 10.1.21' do provider.class.instance_variable_set(:@mysqld_version_string, mysql_version_string_hash['mariadb-10.1.44'][:string]) - resource.stubs(:value).with(:plugin).returns('ed25519') + allow(resource).to receive(:value).with(:plugin).and_return('ed25519') expect { provider.password_hash = 'invalid' }.to raise_error(ArgumentError, 'ed25519 hash should be 43 bytes long.') end it 'changes the hash percona-5.5' do provider.class.instance_variable_set(:@mysqld_version_string, mysql_version_string_hash['percona-5.5'][:string]) - provider.class.expects(:mysql_caller).with("SET PASSWORD FOR 'joe'@'localhost' = '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF5'", 'system').returns('0') + expect(provider.class).to receive(:mysql_caller).with("SET PASSWORD FOR 'joe'@'localhost' = '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF5'", 'system').and_return('0') - provider.expects(:password_hash).returns('*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF5') + expect(provider).to receive(:password_hash).and_return('*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF5') provider.password_hash = '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF5' end end describe 'plugin=' do context 'auth_socket' do context 'MySQL < 5.7.6' do it 'changes the authentication plugin' do provider.class.instance_variable_set(:@mysqld_version_string, mysql_version_string_hash['mysql-5.7.1'][:string]) - provider.class.expects(:mysql_caller).with("UPDATE mysql.user SET plugin = 'auth_socket', password = '' WHERE CONCAT(user, '@', host) = 'joe@localhost'", 'system').returns('0') + expect(provider.class).to receive(:mysql_caller).with("UPDATE mysql.user SET plugin = 'auth_socket', password = '' WHERE CONCAT(user, '@', host) = 'joe@localhost'", 'system').and_return('0') - provider.expects(:plugin).returns('auth_socket') + expect(provider).to receive(:plugin).and_return('auth_socket') provider.plugin = 'auth_socket' end end context 'MySQL >= 5.7.6' do it 'changes the authentication plugin' do provider.class.instance_variable_set(:@mysqld_version_string, mysql_version_string_hash['mysql-5.7.6'][:string]) - provider.class.expects(:mysql_caller).with("ALTER USER 'joe'@'localhost' IDENTIFIED WITH 'auth_socket'", 'system').returns('0') + expect(provider.class).to receive(:mysql_caller).with("ALTER USER 'joe'@'localhost' IDENTIFIED WITH 'auth_socket'", 'system').and_return('0') - provider.expects(:plugin).returns('auth_socket') + expect(provider).to receive(:plugin).and_return('auth_socket') provider.plugin = 'auth_socket' end end end context 'mysql_native_password' do context 'MySQL < 5.7.6' do it 'changes the authentication plugin' do provider.class.instance_variable_set(:@mysqld_version_string, mysql_version_string_hash['mysql-5.7.1'][:string]) - provider.class.expects(:mysql_caller).with("UPDATE mysql.user SET plugin = 'mysql_native_password', password = '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4' WHERE CONCAT(user, '@', host) = 'joe@localhost'", 'system').returns('0') # rubocop:disable Layout/LineLength + expect(provider.class).to receive(:mysql_caller).with("UPDATE mysql.user SET plugin = 'mysql_native_password', password = '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4' WHERE CONCAT(user, '@', host) = 'joe@localhost'", 'system').and_return('0') # rubocop:disable Layout/LineLength - provider.expects(:plugin).returns('mysql_native_password') + expect(provider).to receive(:plugin).and_return('mysql_native_password') provider.plugin = 'mysql_native_password' end end context 'MySQL >= 5.7.6' do it 'changes the authentication plugin' do provider.class.instance_variable_set(:@mysqld_version_string, mysql_version_string_hash['mysql-5.7.6'][:string]) - provider.class.expects(:mysql_caller).with("ALTER USER 'joe'@'localhost' IDENTIFIED WITH 'mysql_native_password' AS '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4'", 'system').returns('0') + expect(provider.class).to receive(:mysql_caller).with("ALTER USER 'joe'@'localhost' IDENTIFIED WITH 'mysql_native_password' AS '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4'", +'system').and_return('0') - provider.expects(:plugin).returns('mysql_native_password') + expect(provider).to receive(:plugin).and_return('mysql_native_password') provider.plugin = 'mysql_native_password' end end end context 'ed25519' do context 'mariadb >= 10.1.21 and < 10.2.0' do it 'changes the authentication plugin' do provider.class.instance_variable_set(:@mysqld_version_string, mysql_version_string_hash['mariadb-10.1.44'][:string]) - resource.stubs('[]').with(:name).returns('joe@localhost') - resource.stubs('[]').with(:password_hash).returns('z0pjExBYbzbupUByZRrQvC6kRCcE8n/tC7kUdUD11fU') - provider.class.expects(:mysql_caller).with("UPDATE mysql.user SET password = '', plugin = 'ed25519', authentication_string = 'z0pjExBYbzbupUByZRrQvC6kRCcE8n/tC7kUdUD11fU' where CONCAT(user, '@', host) = 'joe@localhost'; FLUSH PRIVILEGES", 'system').returns('0') # rubocop:disable Layout/LineLength - provider.expects(:plugin).returns('ed25519') + allow(resource).to receive('[]').with(:name).and_return('joe@localhost') + allow(resource).to receive('[]').with(:password_hash).and_return('z0pjExBYbzbupUByZRrQvC6kRCcE8n/tC7kUdUD11fU') + expect(provider.class).to receive(:mysql_caller).with("UPDATE mysql.user SET password = '', plugin = 'ed25519', authentication_string = 'z0pjExBYbzbupUByZRrQvC6kRCcE8n/tC7kUdUD11fU' where CONCAT(user, '@', host) = 'joe@localhost'; FLUSH PRIVILEGES", 'system').and_return('0') # rubocop:disable Layout/LineLength + expect(provider).to receive(:plugin).and_return('ed25519') provider.plugin = 'ed25519' end end context 'mariadb >= 10.2.0' do it 'changes the authentication plugin' do provider.class.instance_variable_set(:@mysqld_version_string, mysql_version_string_hash['mariadb-10.3.22'][:string]) - resource.stubs('[]').with(:name).returns('joe@localhost') - resource.stubs('[]').with(:password_hash).returns('z0pjExBYbzbupUByZRrQvC6kRCcE8n/tC7kUdUD11fU') - provider.class.expects(:mysql_caller).with("ALTER USER 'joe'@'localhost' IDENTIFIED WITH 'ed25519' AS 'z0pjExBYbzbupUByZRrQvC6kRCcE8n/tC7kUdUD11fU'", 'system').returns('0') - provider.expects(:plugin).returns('ed25519') + allow(resource).to receive('[]').with(:name).and_return('joe@localhost') + expect(resource).to receive('[]').with(:password_hash).and_return('z0pjExBYbzbupUByZRrQvC6kRCcE8n/tC7kUdUD11fU') + expect(provider.class).to receive(:mysql_caller).with("ALTER USER 'joe'@'localhost' IDENTIFIED WITH 'ed25519' AS 'z0pjExBYbzbupUByZRrQvC6kRCcE8n/tC7kUdUD11fU'", 'system').and_return('0') + expect(provider).to receive(:plugin).and_return('ed25519') provider.plugin = 'ed25519' end end end # rubocop:enable RSpec/NestedGroups end describe 'tls_options=' do it 'adds SSL option grant in mysql 5.5' do provider.class.instance_variable_set(:@mysqld_version_string, mysql_version_string_hash['mysql-5.5'][:string]) - provider.class.expects(:mysql_caller).with("GRANT USAGE ON *.* TO 'joe'@'localhost' REQUIRE NONE", 'system').returns('0') + expect(provider.class).to receive(:mysql_caller).with("GRANT USAGE ON *.* TO 'joe'@'localhost' REQUIRE NONE", 'system').and_return('0') - provider.expects(:tls_options).returns(['NONE']) + expect(provider).to receive(:tls_options).and_return(['NONE']) provider.tls_options = ['NONE'] end it 'adds SSL option grant in mysql 5.6' do provider.class.instance_variable_set(:@mysqld_version_string, mysql_version_string_hash['mysql-5.6'][:string]) - provider.class.expects(:mysql_caller).with("GRANT USAGE ON *.* TO 'joe'@'localhost' REQUIRE NONE", 'system').returns('0') + expect(provider.class).to receive(:mysql_caller).with("GRANT USAGE ON *.* TO 'joe'@'localhost' REQUIRE NONE", 'system').and_return('0') - provider.expects(:tls_options).returns(['NONE']) + expect(provider).to receive(:tls_options).and_return(['NONE']) provider.tls_options = ['NONE'] end it 'adds SSL option grant in mysql < 5.7.6' do provider.class.instance_variable_set(:@mysqld_version_string, mysql_version_string_hash['mysql-5.7.1'][:string]) - provider.class.expects(:mysql_caller).with("GRANT USAGE ON *.* TO 'joe'@'localhost' REQUIRE NONE", 'system').returns('0') + expect(provider.class).to receive(:mysql_caller).with("GRANT USAGE ON *.* TO 'joe'@'localhost' REQUIRE NONE", 'system').and_return('0') - provider.expects(:tls_options).returns(['NONE']) + expect(provider).to receive(:tls_options).and_return(['NONE']) provider.tls_options = ['NONE'] end it 'adds SSL option grant in mysql >= 5.7.6' do provider.class.instance_variable_set(:@mysqld_version_string, mysql_version_string_hash['mysql-5.7.6'][:string]) - provider.class.expects(:mysql_caller).with("ALTER USER 'joe'@'localhost' REQUIRE NONE", 'system').returns('0') + expect(provider.class).to receive(:mysql_caller).with("ALTER USER 'joe'@'localhost' REQUIRE NONE", 'system').and_return('0') - provider.expects(:tls_options).returns(['NONE']) + expect(provider).to receive(:tls_options).and_return(['NONE']) provider.tls_options = ['NONE'] end it 'adds SSL option grant in mariadb-10.0' do provider.class.instance_variable_set(:@mysqld_version_string, mysql_version_string_hash['mariadb-10.0'][:string]) - provider.class.expects(:mysql_caller).with("GRANT USAGE ON *.* TO 'joe'@'localhost' REQUIRE NONE", 'system').returns('0') + expect(provider.class).to receive(:mysql_caller).with("GRANT USAGE ON *.* TO 'joe'@'localhost' REQUIRE NONE", 'system').and_return('0') - provider.expects(:tls_options).returns(['NONE']) + expect(provider).to receive(:tls_options).and_return(['NONE']) provider.tls_options = ['NONE'] end end describe 'tls_options=required' do it 'adds mTLS option grant in mysql 5.5' do provider.class.instance_variable_set(:@mysqld_version_string, mysql_version_string_hash['mysql-5.5'][:string]) - provider.class.expects(:mysql_caller).with("GRANT USAGE ON *.* TO 'joe'@'localhost' REQUIRE ISSUER '/CN=Certificate Authority' AND SUBJECT '/OU=MySQL Users/CN=Username'", 'system').returns('0') + expect(provider.class).to receive(:mysql_caller).with("GRANT USAGE ON *.* TO 'joe'@'localhost' REQUIRE ISSUER '/CN=Certificate Authority' AND SUBJECT '/OU=MySQL Users/CN=Username'", +'system').and_return('0') - provider.expects(:tls_options).returns(['ISSUER \'/CN=Certificate Authority\'', 'SUBJECT \'/OU=MySQL Users/CN=Username\'']) + expect(provider).to receive(:tls_options).and_return(['ISSUER \'/CN=Certificate Authority\'', 'SUBJECT \'/OU=MySQL Users/CN=Username\'']) provider.tls_options = ['ISSUER \'/CN=Certificate Authority\'', 'SUBJECT \'/OU=MySQL Users/CN=Username\''] end it 'adds mTLS option grant in mysql 5.6' do provider.class.instance_variable_set(:@mysqld_version_string, mysql_version_string_hash['mysql-5.6'][:string]) - provider.class.expects(:mysql_caller).with("GRANT USAGE ON *.* TO 'joe'@'localhost' REQUIRE ISSUER '/CN=Certificate Authority' AND SUBJECT '/OU=MySQL Users/CN=Username'", 'system').returns('0') + expect(provider.class).to receive(:mysql_caller).with("GRANT USAGE ON *.* TO 'joe'@'localhost' REQUIRE ISSUER '/CN=Certificate Authority' AND SUBJECT '/OU=MySQL Users/CN=Username'", +'system').and_return('0') - provider.expects(:tls_options).returns(['ISSUER \'/CN=Certificate Authority\'', 'SUBJECT \'/OU=MySQL Users/CN=Username\'']) + expect(provider).to receive(:tls_options).and_return(['ISSUER \'/CN=Certificate Authority\'', 'SUBJECT \'/OU=MySQL Users/CN=Username\'']) provider.tls_options = ['ISSUER \'/CN=Certificate Authority\'', 'SUBJECT \'/OU=MySQL Users/CN=Username\''] end it 'adds mTLS option grant in mysql < 5.7.6' do provider.class.instance_variable_set(:@mysqld_version_string, mysql_version_string_hash['mysql-5.7.1'][:string]) - provider.class.expects(:mysql_caller).with("GRANT USAGE ON *.* TO 'joe'@'localhost' REQUIRE ISSUER '/CN=Certificate Authority' AND SUBJECT '/OU=MySQL Users/CN=Username'", 'system').returns('0') + expect(provider.class).to receive(:mysql_caller).with("GRANT USAGE ON *.* TO 'joe'@'localhost' REQUIRE ISSUER '/CN=Certificate Authority' AND SUBJECT '/OU=MySQL Users/CN=Username'", +'system').and_return('0') - provider.expects(:tls_options).returns(['ISSUER \'/CN=Certificate Authority\'', 'SUBJECT \'/OU=MySQL Users/CN=Username\'']) + expect(provider).to receive(:tls_options).and_return(['ISSUER \'/CN=Certificate Authority\'', 'SUBJECT \'/OU=MySQL Users/CN=Username\'']) provider.tls_options = ['ISSUER \'/CN=Certificate Authority\'', 'SUBJECT \'/OU=MySQL Users/CN=Username\''] end it 'adds mTLS option grant in mysql >= 5.7.6' do provider.class.instance_variable_set(:@mysqld_version_string, mysql_version_string_hash['mysql-5.7.6'][:string]) - provider.class.expects(:mysql_caller).with("ALTER USER 'joe'@'localhost' REQUIRE ISSUER '/CN=Certificate Authority' AND SUBJECT '/OU=MySQL Users/CN=Username'", 'system').returns('0') + expect(provider.class).to receive(:mysql_caller).with("ALTER USER 'joe'@'localhost' REQUIRE ISSUER '/CN=Certificate Authority' AND SUBJECT '/OU=MySQL Users/CN=Username'", +'system').and_return('0') - provider.expects(:tls_options).returns(['ISSUER \'/CN=Certificate Authority\'', 'SUBJECT \'/OU=MySQL Users/CN=Username\'']) + expect(provider).to receive(:tls_options).and_return(['ISSUER \'/CN=Certificate Authority\'', 'SUBJECT \'/OU=MySQL Users/CN=Username\'']) provider.tls_options = ['ISSUER \'/CN=Certificate Authority\'', 'SUBJECT \'/OU=MySQL Users/CN=Username\''] end it 'adds mTLS option grant in mariadb-10.0' do provider.class.instance_variable_set(:@mysqld_version_string, mysql_version_string_hash['mariadb-10.0'][:string]) - provider.class.expects(:mysql_caller).with("GRANT USAGE ON *.* TO 'joe'@'localhost' REQUIRE ISSUER '/CN=Certificate Authority' AND SUBJECT '/OU=MySQL Users/CN=Username'", 'system').returns('0') + expect(provider.class).to receive(:mysql_caller).with("GRANT USAGE ON *.* TO 'joe'@'localhost' REQUIRE ISSUER '/CN=Certificate Authority' AND SUBJECT '/OU=MySQL Users/CN=Username'", +'system').and_return('0') - provider.expects(:tls_options).returns(['ISSUER \'/CN=Certificate Authority\'', 'SUBJECT \'/OU=MySQL Users/CN=Username\'']) + expect(provider).to receive(:tls_options).and_return(['ISSUER \'/CN=Certificate Authority\'', 'SUBJECT \'/OU=MySQL Users/CN=Username\'']) provider.tls_options = ['ISSUER \'/CN=Certificate Authority\'', 'SUBJECT \'/OU=MySQL Users/CN=Username\''] end end ['max_user_connections', 'max_connections_per_hour', 'max_queries_per_hour', 'max_updates_per_hour'].each do |property| describe property do it "returns #{property}" do expect(instance.send(property.to_s.to_sym)).to eq('10') end end describe "#{property}=" do it "changes #{property}" do - provider.class.expects(:mysql_caller).with("GRANT USAGE ON *.* TO 'joe'@'localhost' WITH #{property.upcase} 42", 'system').returns('0') - provider.expects(property.to_sym).returns('42') + expect(provider.class).to receive(:mysql_caller).with("GRANT USAGE ON *.* TO 'joe'@'localhost' WITH #{property.upcase} 42", 'system').and_return('0') + expect(provider).to receive(property.to_sym).and_return('42') provider.send("#{property}=".to_sym, '42') end end end end diff --git a/spec/unit/puppet/type/mysql_grant_spec.rb b/spec/unit/puppet/type/mysql_grant_spec.rb index 602c22a..c90d660 100644 --- a/spec/unit/puppet/type/mysql_grant_spec.rb +++ b/spec/unit/puppet/type/mysql_grant_spec.rb @@ -1,104 +1,104 @@ # frozen_string_literal: true require 'puppet' require 'puppet/type/mysql_grant' require 'spec_helper' describe Puppet::Type.type(:mysql_grant) do let(:user) { Puppet::Type.type(:mysql_grant).new(name: 'foo@localhost/*.*', privileges: ['ALL'], table: ['*.*'], user: 'foo@localhost') } it 'accepts a grant name' do expect(user[:name]).to eq('foo@localhost/*.*') end it 'accepts ALL privileges' do user[:privileges] = 'ALL' expect(user[:privileges]).to eq(['ALL']) end context 'PROXY privilege with mysql greater than or equal to 5.5.0' do before :each do - Facter.stubs(:value).with(:mysql_version).returns('5.5.0') + allow(Facter).to receive(:value).with(:mysql_version).and_return('5.5.0') end it 'does not raise error' do user[:privileges] = 'PROXY' user[:table] = 'proxy_user@proxy_host' expect(user[:privileges]).to eq(['PROXY']) end end context 'PROXY privilege with mysql greater than or equal to 5.4.0' do before :each do - Facter.stubs(:value).with(:mysql_version).returns('5.4.0') + allow(Facter).to receive(:value).with(:mysql_version).and_return('5.4.0') end it 'raises error' do expect { user[:privileges] = 'PROXY' }.to raise_error(Puppet::ResourceError, %r{PROXY user not supported on mysql versions < 5.5.0}) end end it 'accepts a table' do user[:table] = '*.*' expect(user[:table]).to eq('*.*') end it 'accepts @ for table' do user[:table] = '@' expect(user[:table]).to eq('@') end it 'accepts proxy user for table' do user[:table] = 'proxy_user@proxy_host' expect(user[:table]).to eq('proxy_user@proxy_host') end it 'accepts a user' do user[:user] = 'foo@localhost' expect(user[:user]).to eq('foo@localhost') end it 'requires a name' do expect { Puppet::Type.type(:mysql_grant).new({}) }.to raise_error(Puppet::Error, 'Title or name must be provided') end it 'requires the name to match the user and table #general' do expect { Puppet::Type.type(:mysql_grant).new(name: 'foo@localhost/*.*', privileges: ['ALL'], table: ['*.*'], user: 'foo@localhost') }.not_to raise_error end it 'requires the name to match the user and table #specific' do expect { Puppet::Type.type(:mysql_grant).new(name: 'foo', privileges: ['ALL'], table: ['*.*'], user: 'foo@localhost') }.to raise_error %r{mysql_grant: `name` `parameter` must match user@host\/table format} end describe 'it should munge privileges' do it 'to just ALL' do user = Puppet::Type.type(:mysql_grant).new( name: 'foo@localhost/*.*', table: ['*.*'], user: 'foo@localhost', privileges: ['ALL'] ) expect(user[:privileges]).to eq(['ALL']) end it 'to upcase and ordered' do user = Puppet::Type.type(:mysql_grant).new( name: 'foo@localhost/*.*', table: ['*.*'], user: 'foo@localhost', privileges: ['select', 'Insert'] ) expect(user[:privileges]).to eq(['INSERT', 'SELECT']) end it 'ordered including column privileges' do user = Puppet::Type.type(:mysql_grant).new( name: 'foo@localhost/*.*', table: ['*.*'], user: 'foo@localhost', privileges: ['SELECT(Host,Address)', 'Insert'] ) expect(user[:privileges]).to eq(['INSERT', 'SELECT (Address, Host)']) end end end diff --git a/spec/unit/puppet/type/mysql_user_spec.rb b/spec/unit/puppet/type/mysql_user_spec.rb index 09e1ada..a193596 100644 --- a/spec/unit/puppet/type/mysql_user_spec.rb +++ b/spec/unit/puppet/type/mysql_user_spec.rb @@ -1,138 +1,138 @@ # frozen_string_literal: true require 'puppet' require 'puppet/type/mysql_user' require 'spec_helper' describe Puppet::Type.type(:mysql_user) do context 'On MySQL 5.x' do before :each do - Facter.stubs(:value).with(:mysql_version).returns('5.6.24') + allow(Facter).to receive(:value).with(:mysql_version).and_return('5.6.24') end it 'fails with a long user name' do expect { Puppet::Type.type(:mysql_user).new(name: '12345678901234567@localhost', password_hash: 'pass') }.to raise_error %r{MySQL usernames are limited to a maximum of 16 characters} end end context 'On MariaDB 10.0.0+' do let(:user) { Puppet::Type.type(:mysql_user).new(name: '12345678901234567@localhost', password_hash: 'pass') } before :each do - Facter.stubs(:value).with(:mysql_version).returns('10.0.19') + allow(Facter).to receive(:value).with(:mysql_version).and_return('10.0.19') end it 'succeeds with a long user name on MariaDB' do expect(user[:name]).to eq('12345678901234567@localhost') end end it 'requires a name' do expect { Puppet::Type.type(:mysql_user).new({}) }.to raise_error(Puppet::Error, 'Title or name must be provided') end context 'using foo@localhost' do let(:user) { Puppet::Type.type(:mysql_user).new(name: 'foo@localhost', password_hash: 'pass') } it 'accepts a user name' do expect(user[:name]).to eq('foo@localhost') end it 'accepts a password' do user[:password_hash] = 'foo' expect(user[:password_hash]).to eq('foo') end it 'accepts an empty password' do user[:password_hash] = '' expect(user[:password_hash]).to eq('') end end context 'using foo@LocalHost' do let(:user) { Puppet::Type.type(:mysql_user).new(name: 'foo@LocalHost', password_hash: 'pass') } it 'lowercases the user name' do expect(user[:name]).to eq('foo@localhost') end end context 'using foo@192.168.1.0/255.255.255.0' do let(:user) { Puppet::Type.type(:mysql_user).new(name: 'foo@192.168.1.0/255.255.255.0', password_hash: 'pass') } it 'creates the user with the netmask' do expect(user[:name]).to eq('foo@192.168.1.0/255.255.255.0') end end context 'using allo_wed$char@localhost' do let(:user) { Puppet::Type.type(:mysql_user).new(name: 'allo_wed$char@localhost', password_hash: 'pass') } it 'accepts a user name' do expect(user[:name]).to eq('allo_wed$char@localhost') end end context 'ensure the default \'debian-sys-main\'@localhost user can be parsed' do let(:user) { Puppet::Type.type(:mysql_user).new(name: '\'debian-sys-maint\'@localhost', password_hash: 'pass') } it 'accepts a user name' do expect(user[:name]).to eq('\'debian-sys-maint\'@localhost') end end context 'using a quoted 16 char username' do let(:user) { Puppet::Type.type(:mysql_user).new(name: '"debian-sys-maint"@localhost', password_hash: 'pass') } it 'accepts a user name' do expect(user[:name]).to eq('"debian-sys-maint"@localhost') end end context 'using a quoted username that is too long ' do before :each do - Facter.stubs(:value).with(:mysql_version).returns('5.6.24') + allow(Facter).to receive(:value).with(:mysql_version).and_return('5.6.24') end it 'fails with a size error' do expect { Puppet::Type.type(:mysql_user).new(name: '"debian-sys-maint2"@localhost', password_hash: 'pass') }.to raise_error %r{MySQL usernames are limited to a maximum of 16 characters} end end context 'using `speci!al#`@localhost' do let(:user) { Puppet::Type.type(:mysql_user).new(name: '`speci!al#`@localhost', password_hash: 'pass') } it 'accepts a quoted user name with special chatracters' do expect(user[:name]).to eq('`speci!al#`@localhost') end end context 'using in-valid@localhost' do let(:user) { Puppet::Type.type(:mysql_user).new(name: 'in-valid@localhost', password_hash: 'pass') } it 'accepts a user name with special chatracters' do expect(user[:name]).to eq('in-valid@localhost') end end context 'using "misquoted@localhost' do it 'fails with a misquoted username is used' do expect { Puppet::Type.type(:mysql_user).new(name: '"misquoted@localhost', password_hash: 'pass') }.to raise_error %r{Invalid database user "misquoted@localhost} end end context 'using invalid options' do it 'fails with an invalid option' do expect { Puppet::Type.type(:mysql_user).new(name: 'misquoted@localhost', password_hash: 'pass', tls_options: ['SOMETHING_ELSE']) }.to raise_error %r{Invalid tls option} end end end