diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..08c6abf9 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,4 @@ +{ + "name": "VoxBox", + "image": "ghcr.io/voxpupuli/voxbox:latest" +} diff --git a/.editorconfig b/.editorconfig index d77700e3..ecb10a80 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,6 +1,7 @@ # editorconfig.org -# MANAGED BY MODULESYNC +# Managed by modulesync - DO NOT EDIT +# https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ root = true diff --git a/.fixtures.yml b/.fixtures.yml index 8d63745d..4a4f5b85 100644 --- a/.fixtures.yml +++ b/.fixtures.yml @@ -1,7 +1,6 @@ +--- fixtures: repositories: - stdlib: https://github.com/puppetlabs/puppetlabs-stdlib.git epel: https://github.com/voxpupuli/puppet-epel.git - yumrepo_core: - repo: https://github.com/puppetlabs/puppetlabs-yumrepo_core.git - puppet_version: ">= 6.0.0" + stdlib: https://github.com/puppetlabs/puppetlabs-stdlib.git + yumrepo_core: https://github.com/puppetlabs/puppetlabs-yumrepo_core.git diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md deleted file mode 100644 index 887d571a..00000000 --- a/.github/CONTRIBUTING.md +++ /dev/null @@ -1,252 +0,0 @@ -# Contribution guidelines - -## Table of contents - -* [Contributing](#contributing) -* [Writing proper commits - short version](#writing-proper-commits-short-version) -* [Writing proper commits - long version](#writing-proper-commits-long-version) -* [Dependencies](#dependencies) - * [Note for OS X users](#note-for-os-x-users) -* [The test matrix](#the-test-matrix) -* [Syntax and style](#syntax-and-style) -* [Running the unit tests](#running-the-unit-tests) -* [Unit tests in docker](#unit-tests-in-docker) -* [Integration tests](#integration-tests) - -This module has grown over time based on a range of contributions from -people using it. If you follow these contributing guidelines your patch -will likely make it into a release a little more quickly. - -## Contributing - -Please note that this project is released with a Contributor Code of Conduct. -By participating in this project you agree to abide by its terms. -[Contributor Code of Conduct](https://voxpupuli.org/coc/). - -* Fork the repo. -* Create a separate branch for your change. -* We only take pull requests with passing tests, and documentation. [travis-ci](http://travis-ci.org) runs the tests for us. You can also execute them locally. This is explained [in a later section](#the-test-matrix). -* Checkout [our docs](https://voxpupuli.org/docs/reviewing_pr/) we use to review a module and the [official styleguide](https://puppet.com/docs/puppet/6.0/style_guide.html). They provide some guidance for new code that might help you before you submit a pull request. -* Add a test for your change. Only refactoring and documentation changes require no new tests. If you are adding functionality or fixing a bug, please add a test. -* Squash your commits down into logical components. Make sure to rebase against our current master. -* Push the branch to your fork and submit a pull request. - -Please be prepared to repeat some of these steps as our contributors review your code. - -Also consider sending in your profile code that calls this component module as an acceptance test or provide it via an issue. This helps reviewers a lot to test your use case and prevents future regressions! - -## Writing proper commits - short version - -* Make commits of logical units. -* Check for unnecessary whitespace with "git diff --check" before committing. -* Commit using Unix line endings (check the settings around "crlf" in git-config(1)). -* Do not check in commented out code or unneeded files. -* The first line of the commit message should be a short description (50 characters is the soft limit, excluding ticket number(s)), and should skip the full stop. -* Associate the issue in the message. The first line should include the issue number in the form "(#XXXX) Rest of message". -* The body should provide a meaningful commit message, which: - *uses the imperative, present tense: `change`, not `changed` or `changes`. - * includes motivation for the change, and contrasts its implementation with the previous behavior. - * Make sure that you have tests for the bug you are fixing, or feature you are adding. - * Make sure the test suites passes after your commit: - * When introducing a new feature, make sure it is properly documented in the README.md - -## Writing proper commits - long version - - 1. Make separate commits for logically separate changes. - - Please break your commits down into logically consistent units - which include new or changed tests relevant to the rest of the - change. The goal of doing this is to make the diff easier to - read for whoever is reviewing your code. In general, the easier - your diff is to read, the more likely someone will be happy to - review it and get it into the code base. - - If you are going to refactor a piece of code, please do so as a - separate commit from your feature or bug fix changes. - - We also really appreciate changes that include tests to make - sure the bug is not re-introduced, and that the feature is not - accidentally broken. - - Describe the technical detail of the change(s). If your - description starts to get too long, that is a good sign that you - probably need to split up your commit into more finely grained - pieces. - - Commits which plainly describe the things which help - reviewers check the patch and future developers understand the - code are much more likely to be merged in with a minimum of - bike-shedding or requested changes. Ideally, the commit message - would include information, and be in a form suitable for - inclusion in the release notes for the version of Puppet that - includes them. - - Please also check that you are not introducing any trailing - whitespace or other "whitespace errors". You can do this by - running "git diff --check" on your changes before you commit. - - 2. Sending your patches - - To submit your changes via a GitHub pull request, we _highly_ - recommend that you have them on a topic branch, instead of - directly on `master`. - It makes things much easier to keep track of, especially if - you decide to work on another thing before your first change - is merged in. - - GitHub has some pretty good - [general documentation](http://help.github.com/) on using - their site. They also have documentation on - [creating pull requests](http://help.github.com/send-pull-requests/). - - In general, after pushing your topic branch up to your - repository on GitHub, you can switch to the branch in the - GitHub UI and click "Pull Request" towards the top of the page - in order to open a pull request. - - - 3. Update the related GitHub issue. - - If there is a GitHub issue associated with the change you - submitted, then you should update the ticket to include the - location of your branch, along with any other commentary you - may wish to make. - -## Dependencies - -The testing and development tools have a bunch of dependencies, -all managed by [bundler](http://bundler.io/) according to the -[Puppet support matrix](http://docs.puppetlabs.com/guides/platforms.html#ruby-versions). - -By default the tests use a baseline version of Puppet. - -If you have Ruby 2.x or want a specific version of Puppet, -you must set an environment variable such as: - -```sh -export PUPPET_VERSION="~> 5.5.6" -``` - -You can install all needed gems for spec tests into the modules directory by -running: - -```sh -bundle install --path .vendor/ --without development system_tests release --jobs "$(nproc)" -``` - -If you also want to run acceptance tests: - -```sh -bundle install --path .vendor/ --with system_tests --without development release --jobs "$(nproc)" -``` - -Our all in one solution if you don't know if you need to install or update gems: - -```sh -bundle install --path .vendor/ --with system_tests --without development release --jobs "$(nproc)"; bundle update; bundle clean -``` - -As an alternative to the `--jobs "$(nproc)` parameter, you can set an -environment variable: - -```sh -BUNDLE_JOBS="$(nproc)" -``` - -### Note for OS X users - -`nproc` isn't a valid command under OS x. As an alternative, you can do: - -```sh ---jobs "$(sysctl -n hw.ncpu)" -``` - -## The test matrix - -### Syntax and style - -The test suite will run [Puppet Lint](http://puppet-lint.com/) and -[Puppet Syntax](https://github.com/gds-operations/puppet-syntax) to -check various syntax and style things. You can run these locally with: - -```sh -bundle exec rake lint -bundle exec rake validate -``` - -It will also run some [Rubocop](http://batsov.com/rubocop/) tests -against it. You can run those locally ahead of time with: - -```sh -bundle exec rake rubocop -``` - -### Running the unit tests - -The unit test suite covers most of the code, as mentioned above please -add tests if you're adding new functionality. If you've not used -[rspec-puppet](http://rspec-puppet.com/) before then feel free to ask -about how best to test your new feature. - -To run the linter, the syntax checker and the unit tests: - -```sh -bundle exec rake test -``` - -To run your all the unit tests - -```sh -bundle exec rake spec -``` - -To run a specific spec test set the `SPEC` variable: - -```sh -bundle exec rake spec SPEC=spec/foo_spec.rb -``` - -#### Unit tests in docker - -Some people don't want to run the dependencies locally or don't want to install -ruby. We ship a Dockerfile that enables you to run all unit tests and linting. -You only need to run: - -```sh -docker build . -``` - -Please ensure that a docker daemon is running and that your user has the -permission to talk to it. You can specify a remote docker host by setting the -`DOCKER_HOST` environment variable. it will copy the content of the module into -the docker image. So it will not work if a Gemfile.lock exists. - -### Integration tests - -The unit tests just check the code runs, not that it does exactly what -we want on a real machine. For that we're using -[beaker](https://github.com/puppetlabs/beaker). - -This fires up a new virtual machine (using vagrant) and runs a series of -simple tests against it after applying the module. You can run this -with: - -```sh -BEAKER_setfile=debian10-x64 bundle exec rake beaker -``` - -You can replace the string `debian10` with any common operating system. -The following strings are known to work: - -* ubuntu1604 -* ubuntu1804 -* ubuntu2004 -* debian9 -* debian10 -* centos7 -* centos8 - -For more information and tips & tricks, see [voxpupuli-acceptance's documentation](https://github.com/voxpupuli/voxpupuli-acceptance#running-tests). - -The source of this file is in our [modulesync_config](https://github.com/voxpupuli/modulesync_config/blob/master/moduleroot/.github/CONTRIBUTING.md.erb) -repository. diff --git a/.github/SECURITY.md b/.github/SECURITY.md deleted file mode 100644 index cacadf22..00000000 --- a/.github/SECURITY.md +++ /dev/null @@ -1,3 +0,0 @@ -# Vox Pupuli Security Policy - -Our vulnerabilities reporting process is at https://voxpupuli.org/security/ diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 00000000..f2d08d6b --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,6 @@ +--- +# Managed by modulesync - DO NOT EDIT +# https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ + +skip-changelog: + - head-branch: ['^release-*', 'release'] diff --git a/.github/release.yml b/.github/release.yml new file mode 100644 index 00000000..f5b5d7a9 --- /dev/null +++ b/.github/release.yml @@ -0,0 +1,42 @@ +--- +# Managed by modulesync - DO NOT EDIT +# https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ + +# https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes + +changelog: + exclude: + labels: + - duplicate + - invalid + - modulesync + - question + - skip-changelog + - wont-fix + - wontfix + + categories: + - title: Breaking Changes 🛠 + labels: + - backwards-incompatible + + - title: New Features 🎉 + labels: + - enhancement + + - title: Bug Fixes 🐛 + labels: + - bug + + - title: Documentation Updates 📚 + labels: + - documentation + - docs + + - title: Dependency Updates ⬆️ + labels: + - dependencies + + - title: Other Changes + labels: + - "*" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 97ddc620..e9744066 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,75 +1,52 @@ +--- +# Managed by modulesync - DO NOT EDIT +# https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ + name: CI -on: pull_request +# yamllint disable-line rule:truthy +on: + pull_request: {} + push: + branches: + - main + - master + workflow_dispatch: + inputs: + beaker_staging_url: + description: |- + URL to a staging Server to test unreleased packages. + We will append the version to the Server and assume all packages are in the same directory. + Only supported for AIO packages. + required: false + type: string + default: 'https://artifacts.voxpupuli.org/openvox-agent' + beaker_collection: + description: |- + When set to staging, we will download the packages from staging_url. + Otherwise we will use the official repos. Supported values: puppet7, puppet8, openvox7, openvox8, staging. + When unset, we will generate a list of supported collections based on metadata.json. + required: false + type: string + beaker_staging_version: + description: |- + The package version we want to test. + Only used for beaker_collection = staging + required: false + type: string -jobs: - setup_matrix: - name: 'Setup Test Matrix' - runs-on: ubuntu-latest - timeout-minutes: 40 - outputs: - beaker_setfiles: ${{ steps.get-outputs.outputs.beaker_setfiles }} - puppet_major_versions: ${{ steps.get-outputs.outputs.puppet_major_versions }} - puppet_unit_test_matrix: ${{ steps.get-outputs.outputs.puppet_unit_test_matrix }} - env: - BUNDLE_WITHOUT: development:release - steps: - - uses: actions/checkout@v2 - - name: Setup ruby - uses: ruby/setup-ruby@v1 - with: - ruby-version: '2.7' - bundler-cache: true - - name: Run rake validate - run: bundle exec rake validate - - name: Run rake rubocop - run: bundle exec rake rubocop - - name: Setup Test Matrix - id: get-outputs - run: bundle exec metadata2gha --use-fqdn --pidfile-workaround false +concurrency: + group: ${{ github.ref_name }} + cancel-in-progress: true - unit: - needs: setup_matrix - runs-on: ubuntu-latest - timeout-minutes: 40 - strategy: - fail-fast: false - matrix: - include: ${{fromJson(needs.setup_matrix.outputs.puppet_unit_test_matrix)}} - env: - BUNDLE_WITHOUT: development:system_tests:release - PUPPET_VERSION: "~> ${{ matrix.puppet }}.0" - name: Puppet ${{ matrix.puppet }} (Ruby ${{ matrix.ruby }}) - steps: - - uses: actions/checkout@v2 - - name: Setup ruby - uses: ruby/setup-ruby@v1 - with: - ruby-version: ${{ matrix.ruby }} - bundler-cache: true - - name: Run tests - run: bundle exec rake +permissions: + contents: read - acceptance: - needs: setup_matrix - runs-on: ubuntu-latest - env: - BUNDLE_WITHOUT: development:test:release - strategy: - fail-fast: false - matrix: - setfile: ${{fromJson(needs.setup_matrix.outputs.beaker_setfiles)}} - puppet: ${{fromJson(needs.setup_matrix.outputs.puppet_major_versions)}} - name: ${{ matrix.puppet.name }} - ${{ matrix.setfile.name }} - steps: - - uses: actions/checkout@v2 - - name: Setup ruby - uses: ruby/setup-ruby@v1 - with: - ruby-version: '2.7' - bundler-cache: true - - name: Run tests - run: bundle exec rake beaker - env: - BEAKER_PUPPET_COLLECTION: ${{ matrix.puppet.collection }} - BEAKER_setfile: ${{ matrix.setfile.value }} +jobs: + puppet: + name: Puppet + uses: voxpupuli/gha-puppet/.github/workflows/beaker.yml@v4 + with: + beaker_staging_url: ${{ inputs.beaker_staging_url }} + beaker_collection: ${{ inputs.beaker_collection }} + beaker_staging_version: ${{ inputs.beaker_staging_version }} diff --git a/.github/workflows/create_tag.yml b/.github/workflows/create_tag.yml new file mode 100644 index 00000000..c835e75a --- /dev/null +++ b/.github/workflows/create_tag.yml @@ -0,0 +1,22 @@ +--- +# Managed by modulesync - DO NOT EDIT +# https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ + +name: Create Git tag + +on: + workflow_dispatch: + +permissions: {} + +jobs: + create_tag: + uses: 'voxpupuli/gha-puppet/.github/workflows/create_tag.yml@v4' + with: + allowed_owner: 'voxpupuli' + git_name: 'pccibot' + git_email: '12855858+pccibot@users.noreply.github.com' + secrets: + # Configure secrets here: + # https://docs.github.com/en/actions/security-guides/encrypted-secrets + ssh_private_key: ${{ secrets.PCCI_SSH_PRIVATE_KEY }} diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml new file mode 100644 index 00000000..eacd0b33 --- /dev/null +++ b/.github/workflows/labeler.yml @@ -0,0 +1,22 @@ +--- +# Managed by modulesync - DO NOT EDIT +# https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ + +name: "Pull Request Labeler" + +# yamllint disable-line rule:truthy +on: + pull_request_target: {} + +permissions: + contents: read + pull-requests: write + +jobs: + labeler: + permissions: + contents: read + pull-requests: write + runs-on: ubuntu-latest + steps: + - uses: actions/labeler@v5 diff --git a/.github/workflows/prepare_release.yml b/.github/workflows/prepare_release.yml new file mode 100644 index 00000000..b3725319 --- /dev/null +++ b/.github/workflows/prepare_release.yml @@ -0,0 +1,30 @@ +--- +# Managed by modulesync - DO NOT EDIT +# https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ + +name: 'Prepare Release' + +on: + workflow_dispatch: + inputs: + version: + description: 'Module version to be released. Must be a valid semver string without leading v. (1.2.3)' + required: false + +permissions: + contents: write + pull-requests: write + +jobs: + release_prep: + uses: 'voxpupuli/gha-puppet/.github/workflows/prepare_release.yml@v4' + with: + version: ${{ github.event.inputs.version }} + allowed_owner: 'voxpupuli' + git_name: 'pccibot' + git_email: '12855858+pccibot@users.noreply.github.com' + secrets: + # Configure secrets here: + # https://docs.github.com/en/actions/security-guides/encrypted-secrets + github_pat: '${{ secrets.PCCI_PAT_RELEASE_PREP }}' + ssh_private_key: '${{ secrets.PCCI_SSH_PRIVATE_KEY }}' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 68b85284..9062a93c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,29 +1,28 @@ +--- +# Managed by modulesync - DO NOT EDIT +# https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ + name: Release +# yamllint disable-line rule:truthy on: push: tags: - - '*' + # https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax#onpushbranchestagsbranches-ignoretags-ignore + # https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax#filter-pattern-cheat-sheet + - 'v[0-9]+.[0-9]+.[0-9]+' -env: - BUNDLE_WITHOUT: development:test:system_tests +permissions: + contents: write jobs: - deploy: - name: 'deploy to forge' - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v2 - - name: Setup Ruby - uses: ruby/setup-ruby@v1 - with: - ruby-version: '2.7' - bundler-cache: true - - name: Build and Deploy - env: - # Configure secrets here: - # https://docs.github.com/en/free-pro-team@latest/actions/reference/encrypted-secrets - BLACKSMITH_FORGE_USERNAME: '${{ secrets.PUPPET_FORGE_USERNAME }}' - BLACKSMITH_FORGE_API_KEY: '${{ secrets.PUPPET_FORGE_API_KEY }}' - run: bundle exec rake module:push + release: + name: Release + uses: voxpupuli/gha-puppet/.github/workflows/release.yml@v4 + with: + allowed_owner: 'voxpupuli' + secrets: + # Configure secrets here: + # https://docs.github.com/en/actions/security-guides/encrypted-secrets + username: ${{ secrets.PUPPET_FORGE_USERNAME }} + api_key: ${{ secrets.PUPPET_FORGE_API_KEY }} diff --git a/.gitignore b/.gitignore index e9b3cf4b..adea1b01 100644 --- a/.gitignore +++ b/.gitignore @@ -1,20 +1,25 @@ -pkg/ -Gemfile.lock -Gemfile.local -vendor/ -.vendor/ -spec/fixtures/manifests/ -spec/fixtures/modules/ -.vagrant/ -.bundle/ -.ruby-version -coverage/ -log/ -.idea/ -.dependencies/ -.librarian/ -Puppetfile.lock +# Managed by modulesync - DO NOT EDIT +# https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ + +/pkg/ +/Gemfile.lock +/Gemfile.local +/vendor/ +/.vendor/ +/spec/fixtures/manifests/ +/spec/fixtures/modules/ +/.vagrant/ +/.bundle/ +/.ruby-version +/coverage/ +/log/ +/.idea/ +/.dependencies/ +/.librarian/ +/Puppetfile.lock *.iml .*.sw? -.yardoc/ -Guardfile +/.yardoc/ +/Guardfile +bolt-debug.log +.rerun.json diff --git a/.msync.yml b/.msync.yml index 57ff5038..bfb9bee8 100644 --- a/.msync.yml +++ b/.msync.yml @@ -1,2 +1,5 @@ --- -modulesync_config_version: '4.1.0' +# Managed by modulesync - DO NOT EDIT +# https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ + +modulesync_config_version: '10.8.0' diff --git a/.overcommit.yml b/.overcommit.yml index 0af0fdc0..4ed994cc 100644 --- a/.overcommit.yml +++ b/.overcommit.yml @@ -1,4 +1,5 @@ -# Managed by https://github.com/voxpupuli/modulesync_configs +# Managed by modulesync - DO NOT EDIT +# https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ # # Hooks are only enabled if you take action. # @@ -42,10 +43,12 @@ PreCommit: enabled: true description: 'Runs rubocop on modified files only' command: ['bundle', 'exec', 'rubocop'] - PuppetLint: + RakeTarget: enabled: true - description: 'Runs puppet-lint on modified files only' - command: ['bundle', 'exec', 'puppet-lint'] + description: 'Runs lint on modified files only' + targets: + - 'lint' + command: ['bundle', 'exec', 'rake'] YamlSyntax: enabled: true JsonSyntax: diff --git a/.pmtignore b/.pmtignore index 33a8c65d..a9d37aa0 100644 --- a/.pmtignore +++ b/.pmtignore @@ -1,34 +1,39 @@ -docs/ -pkg/ -Gemfile -Gemfile.lock -Gemfile.local -vendor/ -.vendor/ -spec/ -Rakefile -.vagrant/ -.bundle/ -.ruby-version -coverage/ -log/ -.idea/ -.dependencies/ -.github/ -.librarian/ -Puppetfile.lock +# Managed by modulesync - DO NOT EDIT +# https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ + +/docs/ +/pkg/ +/Gemfile +/Gemfile.lock +/Gemfile.local +/vendor/ +/.vendor/ +/spec/ +/Rakefile +/.vagrant/ +/.bundle/ +/.ruby-version +/coverage/ +/log/ +/.idea/ +/.dependencies/ +/.github/ +/.librarian/ +/Puppetfile.lock +/Puppetfile *.iml -.editorconfig -.fixtures.yml -.gitignore -.msync.yml -.overcommit.yml -.pmtignore -.rspec -.rspec_parallel -.rubocop.yml -.sync.yml +/.editorconfig +/.fixtures.yml +/.gitignore +/.msync.yml +/.overcommit.yml +/.pmtignore +/.rspec +/.rspec_parallel +/.rubocop.yml +/.sync.yml .*.sw? -.yardoc/ -.yardopts -Dockerfile +/.yardoc/ +/.yardopts +/Dockerfile +/HISTORY.md diff --git a/.puppet-lint.rc b/.puppet-lint.rc new file mode 100644 index 00000000..420e819f --- /dev/null +++ b/.puppet-lint.rc @@ -0,0 +1,5 @@ +# Managed by modulesync - DO NOT EDIT +# https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ + +--fail-on-warnings +--no-parameter_documentation-check diff --git a/.rspec b/.rspec deleted file mode 100644 index 8c18f1ab..00000000 --- a/.rspec +++ /dev/null @@ -1,2 +0,0 @@ ---format documentation ---color diff --git a/.rspec_parallel b/.rspec_parallel deleted file mode 100644 index e4d136b7..00000000 --- a/.rspec_parallel +++ /dev/null @@ -1 +0,0 @@ ---format progress diff --git a/.rubocop.yml b/.rubocop.yml index 198a3599..fded90cf 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,3 +1,7 @@ --- +# Managed by modulesync - DO NOT EDIT +# https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ + +inherit_from: .rubocop_todo.yml inherit_gem: voxpupuli-test: rubocop.yml diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml new file mode 100644 index 00000000..f8daacee --- /dev/null +++ b/.rubocop_todo.yml @@ -0,0 +1,13 @@ +# This configuration was generated by +# `rubocop --auto-gen-config --no-auto-gen-timestamp` +# using RuboCop version 1.85.1. +# The point is for the user to remove these configuration records +# one by one as the offenses are removed from the code base. +# Note that changes in the inspected code, or installation of new +# versions of RuboCop, may require this file to be generated again. + +# Offense count: 3 +RSpec/LeakyLocalVariable: + Exclude: + - 'spec/acceptance/class_spec.rb' + - 'spec/acceptance/facts_test_spec.rb' diff --git a/.sync.yml b/.sync.yml index 5d21319c..4fa6b004 100644 --- a/.sync.yml +++ b/.sync.yml @@ -1,3 +1,6 @@ --- spec/spec_helper_acceptance.rb: unmanaged: false +.puppet-lint.rc: + enabled_lint_checks: + - parameter_types diff --git a/.yardopts b/.yardopts deleted file mode 100644 index 3687f518..00000000 --- a/.yardopts +++ /dev/null @@ -1,2 +0,0 @@ ---markup markdown ---output-dir docs/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 87e485ea..2c8c0db3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,203 @@ All notable changes to this project will be documented in this file. Each new release typically also includes the latest modulesync defaults. These should not affect the functionality of the module. -## [v6.0.1](https://github.com/voxpupuli/puppet-python/tree/v6.0.1) (2021-04-28) +## [v9.0.0](https://github.com/voxpupuli/puppet-python/tree/v9.0.0) (2026-05-05) + +[Full Changelog](https://github.com/voxpupuli/puppet-python/compare/v8.0.0...v9.0.0) + +**Breaking changes:** + +- `init.pp`: remove useless `exec` with `default` title that was trying to set a default `umask` [\#735](https://github.com/voxpupuli/puppet-python/pull/735) ([kenyon](https://github.com/kenyon)) +- Drop puppet, update openvox minimum version to 8.19 [\#729](https://github.com/voxpupuli/puppet-python/pull/729) ([TheMeier](https://github.com/TheMeier)) + +**Implemented enhancements:** + +- puppet/epel: Allow 6.x [\#751](https://github.com/voxpupuli/puppet-python/pull/751) ([bastelfreak](https://github.com/bastelfreak)) +- Add support for Debian 13 [\#740](https://github.com/voxpupuli/puppet-python/pull/740) ([smortex](https://github.com/smortex)) +- Do not pass `--log` to `pip install` [\#736](https://github.com/voxpupuli/puppet-python/pull/736) ([smortex](https://github.com/smortex)) +- Allow a version to start with "v" in `python::pip` [\#727](https://github.com/voxpupuli/puppet-python/pull/727) ([techsk8](https://github.com/techsk8)) + +**Fixed bugs:** + +- Fix group ownership for `pip.conf` on FreeBSD [\#739](https://github.com/voxpupuli/puppet-python/pull/739) ([Szparki](https://github.com/Szparki)) +- Replace legacy fact in gunicorn config templating [\#724](https://github.com/voxpupuli/puppet-python/pull/724) ([notCalle](https://github.com/notCalle)) + +**Closed issues:** + +- "Error: Could not find group root" on FreeBSD [\#738](https://github.com/voxpupuli/puppet-python/issues/738) +- Define pip\_package in hieradata [\#731](https://github.com/voxpupuli/puppet-python/issues/731) + +**Merged pull requests:** + +- Add section in README that simulates `python3 -m pip install pandas --user` [\#725](https://github.com/voxpupuli/puppet-python/pull/725) ([bschonec](https://github.com/bschonec)) + +## [v8.0.0](https://github.com/voxpupuli/puppet-python/tree/v8.0.0) (2025-04-28) + +[Full Changelog](https://github.com/voxpupuli/puppet-python/compare/v7.4.0...v8.0.0) + +**Breaking changes:** + +- Drop EoL Ubuntu 20.04 support [\#719](https://github.com/voxpupuli/puppet-python/pull/719) ([smortex](https://github.com/smortex)) +- Drop EoL FreeBSD 11/12 support [\#718](https://github.com/voxpupuli/puppet-python/pull/718) ([smortex](https://github.com/smortex)) +- Drop EoL Ubuntu 18.04 support [\#710](https://github.com/voxpupuli/puppet-python/pull/710) ([bastelfreak](https://github.com/bastelfreak)) +- Drop EoL CentOS 7/8 support [\#709](https://github.com/voxpupuli/puppet-python/pull/709) ([bastelfreak](https://github.com/bastelfreak)) +- Drop EoL Debian 10 support [\#708](https://github.com/voxpupuli/puppet-python/pull/708) ([bastelfreak](https://github.com/bastelfreak)) + +**Fixed bugs:** + +- Fix bootstrapping `python::pyvenv` when Python is not installed [\#716](https://github.com/voxpupuli/puppet-python/pull/716) ([smortex](https://github.com/smortex)) + +## [v7.4.0](https://github.com/voxpupuli/puppet-python/tree/v7.4.0) (2024-11-27) + +[Full Changelog](https://github.com/voxpupuli/puppet-python/compare/v7.3.0...v7.4.0) + +**Implemented enhancements:** + +- Add Ubuntu 24.04 support [\#707](https://github.com/voxpupuli/puppet-python/pull/707) ([bastelfreak](https://github.com/bastelfreak)) +- pyvenv: Harden datatype for `$environment` [\#704](https://github.com/voxpupuli/puppet-python/pull/704) ([bastelfreak](https://github.com/bastelfreak)) +- simplify packages version detection [\#703](https://github.com/voxpupuli/puppet-python/pull/703) ([maxadamo](https://github.com/maxadamo)) +- Add support for FreeBSD 14 [\#694](https://github.com/voxpupuli/puppet-python/pull/694) ([smortex](https://github.com/smortex)) + +**Fixed bugs:** + +- `python::pip`'s `notreallyaversion` is not compatible with latest `pip` \(\>= 24.1\) due to changed output \(again\) [\#695](https://github.com/voxpupuli/puppet-python/issues/695) +- Fix `python::pip` - use valid, but highly unlikely package version [\#696](https://github.com/voxpupuli/puppet-python/pull/696) ([acojocariu1-godaddy](https://github.com/acojocariu1-godaddy)) + +**Merged pull requests:** + +- CI: Dont pin pip to an outdated version and don't force python3 package installation [\#706](https://github.com/voxpupuli/puppet-python/pull/706) ([bastelfreak](https://github.com/bastelfreak)) + +## [v7.3.0](https://github.com/voxpupuli/puppet-python/tree/v7.3.0) (2024-02-08) + +[Full Changelog](https://github.com/voxpupuli/puppet-python/compare/v7.2.0...v7.3.0) + +**Implemented enhancements:** + +- Add python\_path to pyvenv class [\#686](https://github.com/voxpupuli/puppet-python/pull/686) ([wmellema](https://github.com/wmellema)) + +## [v7.2.0](https://github.com/voxpupuli/puppet-python/tree/v7.2.0) (2024-01-01) + +[Full Changelog](https://github.com/voxpupuli/puppet-python/compare/v7.1.0...v7.2.0) + +**Implemented enhancements:** + +- Add Support for RedHat/CentOS 9 [\#676](https://github.com/voxpupuli/puppet-python/pull/676) ([ValdrinLushaj](https://github.com/ValdrinLushaj)) + +**Fixed bugs:** + +- Make latest version detection portable [\#682](https://github.com/voxpupuli/puppet-python/pull/682) ([smortex](https://github.com/smortex)) + +## [v7.1.0](https://github.com/voxpupuli/puppet-python/tree/v7.1.0) (2023-11-29) + +[Full Changelog](https://github.com/voxpupuli/puppet-python/compare/v7.0.0...v7.1.0) + +**Implemented enhancements:** + +- Add Ubuntu 22.04 support [\#679](https://github.com/voxpupuli/puppet-python/pull/679) ([bastelfreak](https://github.com/bastelfreak)) +- Add Debian 12 support [\#678](https://github.com/voxpupuli/puppet-python/pull/678) ([bastelfreak](https://github.com/bastelfreak)) +- Add OracleLinux/AlmaLinux/Rocky support [\#677](https://github.com/voxpupuli/puppet-python/pull/677) ([JakeTRogers](https://github.com/JakeTRogers)) +- Bump puppet-epel to allow 5.x [\#674](https://github.com/voxpupuli/puppet-python/pull/674) ([dandunckelman](https://github.com/dandunckelman)) +- Allow to skip management of python dev package [\#669](https://github.com/voxpupuli/puppet-python/pull/669) ([smortex](https://github.com/smortex)) + +**Fixed bugs:** + +- Use the wheel group by default on FreeBSD [\#672](https://github.com/voxpupuli/puppet-python/pull/672) ([smortex](https://github.com/smortex)) +- If user declares their requirements.txt in Puppet, don't skip pip installation in python::requirements [\#619](https://github.com/voxpupuli/puppet-python/pull/619) ([acullenn](https://github.com/acullenn)) + +## [v7.0.0](https://github.com/voxpupuli/puppet-python/tree/v7.0.0) (2023-07-27) + +[Full Changelog](https://github.com/voxpupuli/puppet-python/compare/v6.4.0...v7.0.0) + +**Breaking changes:** + +- Drop Ubuntu 16.04 \(EOL\) [\#659](https://github.com/voxpupuli/puppet-python/pull/659) ([smortex](https://github.com/smortex)) +- Drop Debian 9 \(EOL\) [\#658](https://github.com/voxpupuli/puppet-python/pull/658) ([smortex](https://github.com/smortex)) +- Drop Puppet 6 support [\#656](https://github.com/voxpupuli/puppet-python/pull/656) ([bastelfreak](https://github.com/bastelfreak)) + +**Implemented enhancements:** + +- Add Puppet 8 support [\#664](https://github.com/voxpupuli/puppet-python/pull/664) ([bastelfreak](https://github.com/bastelfreak)) +- puppetlabs/stdlib: Allow 9.x [\#663](https://github.com/voxpupuli/puppet-python/pull/663) ([bastelfreak](https://github.com/bastelfreak)) + +**Fixed bugs:** + +- Fix pip installation on Gentoo [\#651](https://github.com/voxpupuli/puppet-python/pull/651) ([puppetjoy](https://github.com/puppetjoy)) + +## [v6.4.0](https://github.com/voxpupuli/puppet-python/tree/v6.4.0) (2022-11-06) + +[Full Changelog](https://github.com/voxpupuli/puppet-python/compare/v6.3.0...v6.4.0) + +**Implemented enhancements:** + +- move static data from params.pp to init.pp [\#648](https://github.com/voxpupuli/puppet-python/pull/648) ([bastelfreak](https://github.com/bastelfreak)) +- cleanup acceptance tests [\#647](https://github.com/voxpupuli/puppet-python/pull/647) ([bastelfreak](https://github.com/bastelfreak)) +- Implement Arch Linux support [\#642](https://github.com/voxpupuli/puppet-python/pull/642) ([bastelfreak](https://github.com/bastelfreak)) +- Add prompt parameter to python::pyvenv [\#641](https://github.com/voxpupuli/puppet-python/pull/641) ([ookisan](https://github.com/ookisan)) +- Add extra\_index parameter to python::pip [\#640](https://github.com/voxpupuli/puppet-python/pull/640) ([ookisan](https://github.com/ookisan)) + +**Fixed bugs:** + +- use legacy pip resolver for pip versions \< 21.1 \> 20.2.4 [\#639](https://github.com/voxpupuli/puppet-python/pull/639) ([saz](https://github.com/saz)) + +## [v6.3.0](https://github.com/voxpupuli/puppet-python/tree/v6.3.0) (2022-07-18) + +[Full Changelog](https://github.com/voxpupuli/puppet-python/compare/v6.2.1...v6.3.0) + +**Implemented enhancements:** + +- allow puppet/epel v4 [\#634](https://github.com/voxpupuli/puppet-python/pull/634) ([vchepkov](https://github.com/vchepkov)) +- cleanup references to obsolete virtualenv parameter/command [\#633](https://github.com/voxpupuli/puppet-python/pull/633) ([vchepkov](https://github.com/vchepkov)) + +**Fixed bugs:** + +- Add python-venv installation [\#579](https://github.com/voxpupuli/puppet-python/pull/579) ([crazymind1337](https://github.com/crazymind1337)) + +## [v6.2.1](https://github.com/voxpupuli/puppet-python/tree/v6.2.1) (2021-12-10) + +[Full Changelog](https://github.com/voxpupuli/puppet-python/compare/v6.2.0...v6.2.1) + +**Fixed bugs:** + +- Correct python::pip::environment parameter example [\#629](https://github.com/voxpupuli/puppet-python/pull/629) ([traylenator](https://github.com/traylenator)) +- gunicorn: RHEL8 has a different package name [\#628](https://github.com/voxpupuli/puppet-python/pull/628) ([yakatz](https://github.com/yakatz)) +- Remove duplicate arguments in the pip install command [\#627](https://github.com/voxpupuli/puppet-python/pull/627) ([zanyou](https://github.com/zanyou)) + +**Closed issues:** + +- Package with provider pip3 tries installing every run. [\#626](https://github.com/voxpupuli/puppet-python/issues/626) + +**Merged pull requests:** + +- modulesync 5.1.0 & puppet-lint: fix params\_empty\_string\_assignment [\#631](https://github.com/voxpupuli/puppet-python/pull/631) ([bastelfreak](https://github.com/bastelfreak)) + +## [v6.2.0](https://github.com/voxpupuli/puppet-python/tree/v6.2.0) (2021-08-26) + +[Full Changelog](https://github.com/voxpupuli/puppet-python/compare/v6.1.0...v6.2.0) + +**Implemented enhancements:** + +- Add support for Debian 11 [\#621](https://github.com/voxpupuli/puppet-python/pull/621) ([smortex](https://github.com/smortex)) + +**Merged pull requests:** + +- Allow stdlib 8.0.0 [\#622](https://github.com/voxpupuli/puppet-python/pull/622) ([smortex](https://github.com/smortex)) + +## [v6.1.0](https://github.com/voxpupuli/puppet-python/tree/v6.1.0) (2021-06-05) + +[Full Changelog](https://github.com/voxpupuli/puppet-python/compare/v6.0.1...v6.1.0) + +Due to a bug in the release pipeline, release v6.0.1 didn't make it to the forge. v6.0.2 just contains [modulesync 4.1.0](https://github.com/voxpupuli/modulesync_config/blob/master/CHANGELOG.md#410-2021-04-03) patch. + +**Implemented enhancements:** + +- Add support for FreeBSD [\#612](https://github.com/voxpupuli/puppet-python/pull/612) ([smortex](https://github.com/smortex)) + +**Closed issues:** + +- Can't uninstall pip package because of duplicate variable name [\#532](https://github.com/voxpupuli/puppet-python/issues/532) +- audit metaparameter is deprecated [\#375](https://github.com/voxpupuli/puppet-python/issues/375) + +## [v6.0.1](https://github.com/voxpupuli/puppet-python/tree/v6.0.1) (2021-04-29) [Full Changelog](https://github.com/voxpupuli/puppet-python/compare/v6.0.0...v6.0.1) @@ -163,7 +359,7 @@ These should not affect the functionality of the module. - Allow HTTP\_PROXY on bootstrap. [\#488](https://github.com/voxpupuli/puppet-python/pull/488) ([pillarsdotnet](https://github.com/pillarsdotnet)) - Modern pip can install wheels without wheel installed [\#483](https://github.com/voxpupuli/puppet-python/pull/483) ([asottile](https://github.com/asottile)) -- Allow arbitrary pip providers [\#480](https://github.com/voxpupuli/puppet-python/pull/480) ([ethanhs](https://github.com/ethanhs)) +- Allow arbitrary pip providers [\#480](https://github.com/voxpupuli/puppet-python/pull/480) ([emmatyping](https://github.com/emmatyping)) - Add manage\_scl boolean to control managing SCL [\#464](https://github.com/voxpupuli/puppet-python/pull/464) ([bodgit](https://github.com/bodgit)) - Allow pip to work in AIX systems [\#461](https://github.com/voxpupuli/puppet-python/pull/461) ([feltra](https://github.com/feltra)) - move pip bootstrap into a seperate class [\#460](https://github.com/voxpupuli/puppet-python/pull/460) ([feltra](https://github.com/feltra)) @@ -304,7 +500,7 @@ These should not affect the functionality of the module. - Fix Python version regex in install.pp [\#410](https://github.com/voxpupuli/puppet-python/pull/410) ([fklajn](https://github.com/fklajn)) - Remove docker nodesets [\#408](https://github.com/voxpupuli/puppet-python/pull/408) ([bastelfreak](https://github.com/bastelfreak)) -- Update README compatibility section [\#405](https://github.com/voxpupuli/puppet-python/pull/405) ([rkcpi](https://github.com/rkcpi)) +- Update README compatibility section [\#405](https://github.com/voxpupuli/puppet-python/pull/405) ([sandra-thieme](https://github.com/sandra-thieme)) - add secret for forge deployment via travis [\#404](https://github.com/voxpupuli/puppet-python/pull/404) ([bastelfreak](https://github.com/bastelfreak)) - Add deprecation notice for the old repository [\#403](https://github.com/voxpupuli/puppet-python/pull/403) ([stankevich](https://github.com/stankevich)) - virtualenv.pp: make creation of $venv\_dir optional [\#391](https://github.com/voxpupuli/puppet-python/pull/391) ([daylicron](https://github.com/daylicron)) @@ -329,7 +525,7 @@ These should not affect the functionality of the module. - Fix tests: Pin rake for ruby 1.9.3 [\#387](https://github.com/voxpupuli/puppet-python/pull/387) ([waipeng](https://github.com/waipeng)) - Support virtualenv for Ubuntu 16.04 [\#386](https://github.com/voxpupuli/puppet-python/pull/386) ([waipeng](https://github.com/waipeng)) - Set virtualenv package name for Debian stretch [\#383](https://github.com/voxpupuli/puppet-python/pull/383) ([sergiik](https://github.com/sergiik)) -- Update gunicorn.pp - Add manage\_config\_dir [\#382](https://github.com/voxpupuli/puppet-python/pull/382) ([bc-bjoern](https://github.com/bc-bjoern)) +- Update gunicorn.pp - Add manage\_config\_dir [\#382](https://github.com/voxpupuli/puppet-python/pull/382) ([epik0r](https://github.com/epik0r)) - Support latest puppet versions [\#376](https://github.com/voxpupuli/puppet-python/pull/376) ([ghoneycutt](https://github.com/ghoneycutt)) - Add python release as available facts [\#355](https://github.com/voxpupuli/puppet-python/pull/355) ([jcpunk](https://github.com/jcpunk)) - Allow hiera config for dotfiles [\#344](https://github.com/voxpupuli/puppet-python/pull/344) ([puppetninja](https://github.com/puppetninja)) @@ -501,7 +697,7 @@ These should not affect the functionality of the module. - Revert "Update virtualenv\_version.rb" [\#267](https://github.com/voxpupuli/puppet-python/pull/267) ([shivapoudel](https://github.com/shivapoudel)) - Update virtualenv\_version.rb [\#265](https://github.com/voxpupuli/puppet-python/pull/265) ([shivapoudel](https://github.com/shivapoudel)) - Update params.pp [\#263](https://github.com/voxpupuli/puppet-python/pull/263) ([philippeback](https://github.com/philippeback)) -- Bug virtualenv instead of virtualenv-$version [\#261](https://github.com/voxpupuli/puppet-python/pull/261) ([Asher256](https://github.com/Asher256)) +- Bug virtualenv instead of virtualenv-$version [\#261](https://github.com/voxpupuli/puppet-python/pull/261) ([ghost](https://github.com/ghost)) - Addressing stankevich/puppet-python issue \#258. [\#260](https://github.com/voxpupuli/puppet-python/pull/260) ([rpocase](https://github.com/rpocase)) ## [1.10.0](https://github.com/voxpupuli/puppet-python/tree/1.10.0) (2015-10-29) @@ -538,7 +734,7 @@ These should not affect the functionality of the module. - Bootstrap pip installation [\#244](https://github.com/voxpupuli/puppet-python/pull/244) ([joshuaspence](https://github.com/joshuaspence)) - Various tidying up [\#242](https://github.com/voxpupuli/puppet-python/pull/242) ([joshuaspence](https://github.com/joshuaspence)) - Allow custom versions to be installed [\#241](https://github.com/voxpupuli/puppet-python/pull/241) ([joshuaspence](https://github.com/joshuaspence)) -- Check that we have results before returning a value [\#238](https://github.com/voxpupuli/puppet-python/pull/238) ([xaque208](https://github.com/xaque208)) +- Check that we have results before returning a value [\#238](https://github.com/voxpupuli/puppet-python/pull/238) ([zachfi](https://github.com/zachfi)) - Adjust test code to pass syntax checker [\#237](https://github.com/voxpupuli/puppet-python/pull/237) ([fluential](https://github.com/fluential)) ## [1.9.7](https://github.com/voxpupuli/puppet-python/tree/1.9.7) (2015-08-21) @@ -703,7 +899,7 @@ These should not affect the functionality of the module. **Closed issues:** - 'system' or any other version of python doesn't work, doesn't get validated [\#129](https://github.com/voxpupuli/puppet-python/issues/129) -- Could not look up qualified variable '::python::install::valid\_versions' [\#126](https://github.com/voxpupuli/puppet-python/issues/126) +- Could not look up qualified variable `python::install::valid_versions` [\#126](https://github.com/voxpupuli/puppet-python/issues/126) ## [1.7.11](https://github.com/voxpupuli/puppet-python/tree/1.7.11) (2014-10-11) diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index a51c6416..00000000 --- a/Dockerfile +++ /dev/null @@ -1,21 +0,0 @@ -FROM ruby:2.7 - -WORKDIR /opt/puppet - -# https://github.com/puppetlabs/puppet/blob/06ad255754a38f22fb3a22c7c4f1e2ce453d01cb/lib/puppet/provider/service/runit.rb#L39 -RUN mkdir -p /etc/sv - -ARG PUPPET_VERSION="~> 6.0" -ARG PARALLEL_TEST_PROCESSORS=4 - -# Cache gems -COPY Gemfile . -RUN bundle install --without system_tests development release --path=${BUNDLE_PATH:-vendor/bundle} - -COPY . . - -RUN bundle install -RUN bundle exec rake release_checks - -# Container should not saved -RUN exit 1 diff --git a/Gemfile b/Gemfile index f4855f64..12839821 100644 --- a/Gemfile +++ b/Gemfile @@ -1,9 +1,11 @@ -source ENV['GEM_SOURCE'] || "https://rubygems.org" +# Managed by modulesync - DO NOT EDIT +# https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ + +source ENV['GEM_SOURCE'] || 'https://rubygems.org' group :test do - gem 'voxpupuli-test', '~> 2.1', :require => false - gem 'coveralls', :require => false - gem 'simplecov-console', :require => false + gem 'voxpupuli-test', '~> 14.0', :require => false + gem 'puppet_metadata', '~> 6.1', :require => false end group :development do @@ -12,22 +14,15 @@ group :development do end group :system_tests do - gem 'puppet_metadata', '~> 0.3.0', :require => false - gem 'voxpupuli-acceptance', :require => false + gem 'voxpupuli-acceptance', '~> 4.4', :require => false end group :release do - gem 'github_changelog_generator', '>= 1.16.1', :require => false - gem 'puppet-blacksmith', :require => false - gem 'voxpupuli-release', :require => false - gem 'puppet-strings', '>= 2.2', :require => false + gem 'voxpupuli-release', '~> 5.3', :require => false end -gem 'puppetlabs_spec_helper', '~> 2.0', :require => false gem 'rake', :require => false -gem 'facter', ENV['FACTER_GEM_VERSION'], :require => false, :groups => [:test] -puppetversion = ENV['PUPPET_VERSION'] || '~> 6.0' -gem 'puppet', puppetversion, :require => false, :groups => [:test] +gem 'openvox', ENV.fetch('OPENVOX_GEM_VERSION', [">= 7", "< 9"]), :require => false, :groups => [:test] # vim: syntax=ruby diff --git a/README.md b/README.md index dcfafdb2..59d95fd3 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,13 @@ # puppet-python -[![License](https://img.shields.io/github/license/voxpupuli/puppet-python.svg)](https://github.com/voxpupuli/puppet-python/blob/master/LICENSE) -[![Build Status](https://travis-ci.org/voxpupuli/puppet-python.png?branch=master)](https://travis-ci.org/voxpupuli/puppet-python) +[![Build Status](https://github.com/voxpupuli/puppet-python/workflows/CI/badge.svg)](https://github.com/voxpupuli/puppet-python/actions?query=workflow%3ACI) +[![Release](https://github.com/voxpupuli/puppet-python/actions/workflows/release.yml/badge.svg)](https://github.com/voxpupuli/puppet-python/actions/workflows/release.yml) [![Puppet Forge](https://img.shields.io/puppetforge/v/puppet/python.svg)](https://forge.puppetlabs.com/puppet/python) [![Puppet Forge - downloads](https://img.shields.io/puppetforge/dt/puppet/python.svg)](https://forge.puppetlabs.com/puppet/python) [![Puppet Forge - endorsement](https://img.shields.io/puppetforge/e/puppet/python.svg)](https://forge.puppetlabs.com/puppet/python) [![Puppet Forge - scores](https://img.shields.io/puppetforge/f/puppet/python.svg)](https://forge.puppetlabs.com/puppet/python) +[![puppetmodule.info docs](http://www.puppetmodule.info/images/badge.png)](http://www.puppetmodule.info/m/puppet-python) +[![License](https://img.shields.io/github/license/voxpupuli/puppet-python.svg)](https://github.com/voxpupuli/puppet-python/blob/master/LICENSE) Puppet module for installing and managing python, pip, virtualenvs and Gunicorn virtual hosts. @@ -17,6 +19,35 @@ For class usage refer to the [Reference]("https://github.com/voxpupuli/puppet-py bundle exec rake strings:generate\[',,,,false,true'] ``` +### Install Python package to a user's default install directory + +The following code simulates + +```shell +python3 -m pip install pandas --user +``` +where pip installs packages to a user's default install directory -- +typically `~/.local/` on Linux. + +```puppet +# Somewhat hackishly, install Python PIP module PANDAS for Oracle Cloud API queries. +python::pyvenv { 'user_python_venv': + ensure => present, + version => 'system', + systempkgs => true, + venv_dir => '/home/example/.local', + owner => 'example', + group => 'example', + mode => '0750', +} + +python::pip { 'pandas': + virtualenv => '/home/example/.local', + owner => 'example', + group => 'example', +} +``` + ### hiera configuration This module supports configuration through hiera. The following example diff --git a/REFERENCE.md b/REFERENCE.md index a12a230c..9f19f1d8 100644 --- a/REFERENCE.md +++ b/REFERENCE.md @@ -9,7 +9,7 @@ #### Public Classes * [`python`](#python): Installs and manages python, python-dev and gunicorn. -* [`python::pip::bootstrap`](#pythonpipbootstrap): allow to bootstrap pip when python is managed from other module +* [`python::pip::bootstrap`](#python--pip--bootstrap): allow to bootstrap pip when python is managed from other module #### Private Classes @@ -19,23 +19,24 @@ ### Defined types -* [`python::dotfile`](#pythondotfile): Manages any python dotfiles with a simple config hash. -* [`python::gunicorn`](#pythongunicorn): Manages Gunicorn virtual hosts. -* [`python::pip`](#pythonpip): Installs and manages packages from pip. -* [`python::pyvenv`](#pythonpyvenv): Create a Python3 virtualenv using pyvenv. -* [`python::requirements`](#pythonrequirements): Installs and manages Python packages from requirements file. +* [`python::dotfile`](#python--dotfile): Manages any python dotfiles with a simple config hash. +* [`python::gunicorn`](#python--gunicorn): Manages Gunicorn virtual hosts. +* [`python::pip`](#python--pip): Installs and manages packages from pip. +* [`python::pyvenv`](#python--pyvenv): Create a Python3 virtualenv using pyvenv. +* [`python::requirements`](#python--requirements): Installs and manages Python packages from requirements file. ### Data types -* [`Python::Loglevel`](#pythonloglevel): A version type to match all valid loglevels for python -* [`Python::Package::Ensure`](#pythonpackageensure): A version type to match all valid package ensures for python -* [`Python::Provider`](#pythonprovider): A version type to match all valid provider for python -* [`Python::Umask`](#pythonumask): A version type to match valid umask for python -* [`Python::Version`](#pythonversion): A version type to match all valid versions for python +* [`Python::Loglevel`](#Python--Loglevel): Match all valid loglevels for python +* [`Python::Package::Ensure`](#Python--Package--Ensure): Match all valid package ensures for python +* [`Python::Provider`](#Python--Provider): Match all valid provider for python +* [`Python::Umask`](#Python--Umask): Match valid umask for python +* [`Python::Venv::PipVersion`](#Python--Venv--PipVersion): A version type to ensure a specific Pip version in a virtual env. +* [`Python::Version`](#Python--Version): Match all valid versions for python ## Classes -### `python` +### `python` Installs and manages python, python-dev and gunicorn. @@ -64,17 +65,51 @@ class { 'python' : #### Parameters -The following parameters are available in the `python` class. +The following parameters are available in the `python` class: + +* [`default_system_version`](#-python--default_system_version) +* [`ensure`](#-python--ensure) +* [`version`](#-python--version) +* [`pip`](#-python--pip) +* [`dev`](#-python--dev) +* [`gunicorn`](#-python--gunicorn) +* [`manage_gunicorn`](#-python--manage_gunicorn) +* [`provider`](#-python--provider) +* [`use_epel`](#-python--use_epel) +* [`manage_scl`](#-python--manage_scl) +* [`umask`](#-python--umask) +* [`manage_gunicorn`](#-python--manage_gunicorn) +* [`manage_python_package`](#-python--manage_python_package) +* [`manage_dev_package`](#-python--manage_dev_package) +* [`manage_venv_package`](#-python--manage_venv_package) +* [`manage_pip_package`](#-python--manage_pip_package) +* [`venv`](#-python--venv) +* [`gunicorn_package_name`](#-python--gunicorn_package_name) +* [`python_pips`](#-python--python_pips) +* [`python_pyvenvs`](#-python--python_pyvenvs) +* [`python_requirements`](#-python--python_requirements) +* [`python_dotfiles`](#-python--python_dotfiles) +* [`rhscl_use_public_repository`](#-python--rhscl_use_public_repository) +* [`anaconda_installer_url`](#-python--anaconda_installer_url) +* [`anaconda_install_path`](#-python--anaconda_install_path) + +##### `default_system_version` -##### `ensure` +Data type: `Python::Version` + +The default version of Python provided by the operating system. Only used as a fallback if Python is not installed yet to determine how to handle some actions that vary depending on the Python version used. + +Default value: `'3.11'` + +##### `ensure` Data type: `Python::Package::Ensure` Desired installation state for the Python package. -Default value: `$python::params::ensure` +Default value: `'present'` -##### `version` +##### `version` Data type: `Python::Version` @@ -87,49 +122,49 @@ Allowed values: - 3/3.3/... means you are going to install the python3/python3.3/... package, if available on your osfamily. -Default value: `'3'` +Default value: `$facts['os']['family'] ? { 'Archlinux' => 'system', default => '3'` -##### `pip` +##### `pip` Data type: `Python::Package::Ensure` Desired installation state for the python-pip package. -Default value: `$python::params::pip` +Default value: `'present'` -##### `dev` +##### `dev` Data type: `Python::Package::Ensure` Desired installation state for the python-dev package. -Default value: `$python::params::dev` +Default value: `'absent'` -##### `gunicorn` +##### `gunicorn` Data type: `Python::Package::Ensure` Desired installation state for Gunicorn. -Default value: `$python::params::gunicorn` +Default value: `'absent'` -##### `manage_gunicorn` +##### `manage_gunicorn` Data type: `Boolean` Allow Installation / Removal of Gunicorn. -Default value: `$python::params::manage_gunicorn` +Default value: `true` -##### `provider` +##### `provider` Data type: `Optional[Python::Provider]` What provider to use for installation of the packages, except gunicorn and Python itself. -Default value: `$python::params::provider` +Default value: `undef` -##### `use_epel` +##### `use_epel` Data type: `Boolean` @@ -137,39 +172,65 @@ to determine if the epel class is used. Default value: `$python::params::use_epel` -##### `manage_scl` +##### `manage_scl` Data type: `Boolean` Whether to manage core SCL packages or not. -Default value: `$python::params::manage_scl` +Default value: `true` -##### `umask` - -Data type: `Optional[Python::Umask]` +##### `umask` The default umask for invoked exec calls. -Default value: ``undef`` +##### `manage_gunicorn` -##### `manage_python_package` +manage the state for package gunicorn + +Default value: `true` + +##### `manage_python_package` Data type: `Boolean` +manage the state for package python +Default value: `true` -Default value: `$python::params::manage_python_package` +##### `manage_dev_package` + +Data type: `Boolean` -##### `manage_pip_package` +manage the state of the python development package + +Default value: `true` + +##### `manage_venv_package` Data type: `Boolean` +manage the state for package venv + +Default value: `$python::params::manage_venv_package` +##### `manage_pip_package` + +Data type: `Boolean` + +manage the state for package pip Default value: `$python::params::manage_pip_package` -##### `gunicorn_package_name` +##### `venv` + +Data type: `Python::Package::Ensure` + + + +Default value: `'absent'` + +##### `gunicorn_package_name` Data type: `String[1]` @@ -177,7 +238,7 @@ Data type: `String[1]` Default value: `$python::params::gunicorn_package_name` -##### `python_pips` +##### `python_pips` Data type: `Hash` @@ -185,7 +246,7 @@ Data type: `Hash` Default value: `{}` -##### `python_pyvenvs` +##### `python_pyvenvs` Data type: `Hash` @@ -193,7 +254,7 @@ Data type: `Hash` Default value: `{}` -##### `python_requirements` +##### `python_requirements` Data type: `Hash` @@ -201,7 +262,7 @@ Data type: `Hash` Default value: `{}` -##### `python_dotfiles` +##### `python_dotfiles` Data type: `Hash` @@ -209,31 +270,31 @@ Data type: `Hash` Default value: `{}` -##### `rhscl_use_public_repository` +##### `rhscl_use_public_repository` Data type: `Boolean` -Default value: `$python::params::rhscl_use_public_repository` +Default value: `true` -##### `anaconda_installer_url` +##### `anaconda_installer_url` Data type: `Stdlib::Httpurl` -Default value: `$python::params::anaconda_installer_url` +Default value: `'https://repo.anaconda.com/archive/Anaconda3-5.2.0-Linux-x86_64.sh'` -##### `anaconda_install_path` +##### `anaconda_install_path` Data type: `Stdlib::Absolutepath` -Default value: `$python::params::anaconda_install_path` +Default value: `'/opt/python'` -### `python::pip::bootstrap` +### `python::pip::bootstrap` allow to bootstrap pip when python is managed from other module @@ -249,9 +310,14 @@ class { 'python::pip::bootstrap': #### Parameters -The following parameters are available in the `python::pip::bootstrap` class. +The following parameters are available in the `python::pip::bootstrap` class: + +* [`version`](#-python--pip--bootstrap--version) +* [`manage_python`](#-python--pip--bootstrap--manage_python) +* [`http_proxy`](#-python--pip--bootstrap--http_proxy) +* [`exec_provider`](#-python--pip--bootstrap--exec_provider) -##### `version` +##### `version` Data type: `Enum['pip', 'pip3']` @@ -259,23 +325,23 @@ should be pip or pip3 Default value: `'pip'` -##### `manage_python` +##### `manage_python` Data type: `Variant[Boolean, String]` if python module will manage deps -Default value: ``false`` +Default value: `false` -##### `http_proxy` +##### `http_proxy` Data type: `Optional[Stdlib::HTTPUrl]` Proxy server to use for outbound connections. -Default value: ``undef`` +Default value: `undef` -##### `exec_provider` +##### `exec_provider` Data type: `String[1]` @@ -285,7 +351,7 @@ Default value: `'shell'` ## Defined types -### `python::dotfile` +### `python::dotfile` Manages any python dotfiles with a simple config hash. @@ -309,9 +375,16 @@ python::dotfile { '/var/lib/jenkins/.pip/pip.conf': #### Parameters -The following parameters are available in the `python::dotfile` defined type. +The following parameters are available in the `python::dotfile` defined type: -##### `ensure` +* [`ensure`](#-python--dotfile--ensure) +* [`filename`](#-python--dotfile--filename) +* [`mode`](#-python--dotfile--mode) +* [`owner`](#-python--dotfile--owner) +* [`group`](#-python--dotfile--group) +* [`config`](#-python--dotfile--config) + +##### `ensure` Data type: `Enum['absent', 'present']` @@ -319,7 +392,7 @@ Data type: `Enum['absent', 'present']` Default value: `'present'` -##### `filename` +##### `filename` Data type: `Stdlib::Absolutepath` @@ -327,7 +400,7 @@ Filename. Default value: `$title` -##### `mode` +##### `mode` Data type: `Stdlib::Filemode` @@ -335,7 +408,7 @@ File mode. Default value: `'0644'` -##### `owner` +##### `owner` Data type: `String[1]` @@ -343,15 +416,15 @@ user owner of dotfile Default value: `'root'` -##### `group` +##### `group` Data type: `String[1]` group owner of dotfile -Default value: `'root'` +Default value: `getvar('python::params::group')` -##### `config` +##### `config` Data type: `Hash` @@ -359,7 +432,7 @@ Config hash. This will be expanded to an ini-file. Default value: `{}` -### `python::gunicorn` +### `python::gunicorn` Manages Gunicorn virtual hosts. @@ -386,9 +459,30 @@ python::gunicorn { 'vhost': #### Parameters -The following parameters are available in the `python::gunicorn` defined type. - -##### `ensure` +The following parameters are available in the `python::gunicorn` defined type: + +* [`ensure`](#-python--gunicorn--ensure) +* [`config_dir`](#-python--gunicorn--config_dir) +* [`manage_config_dir`](#-python--gunicorn--manage_config_dir) +* [`virtualenv`](#-python--gunicorn--virtualenv) +* [`mode`](#-python--gunicorn--mode) +* [`dir`](#-python--gunicorn--dir) +* [`bind`](#-python--gunicorn--bind) +* [`environment`](#-python--gunicorn--environment) +* [`appmodule`](#-python--gunicorn--appmodule) +* [`osenv`](#-python--gunicorn--osenv) +* [`timeout`](#-python--gunicorn--timeout) +* [`template`](#-python--gunicorn--template) +* [`args`](#-python--gunicorn--args) +* [`owner`](#-python--gunicorn--owner) +* [`group`](#-python--gunicorn--group) +* [`workers`](#-python--gunicorn--workers) +* [`access_log_format`](#-python--gunicorn--access_log_format) +* [`accesslog`](#-python--gunicorn--accesslog) +* [`errorlog`](#-python--gunicorn--errorlog) +* [`log_level`](#-python--gunicorn--log_level) + +##### `ensure` Data type: `Enum['present', 'absent']` @@ -396,7 +490,7 @@ Data type: `Enum['present', 'absent']` Default value: `present` -##### `config_dir` +##### `config_dir` Data type: `Stdlib::Absolutepath` @@ -404,23 +498,23 @@ Configure the gunicorn config directory path. Default value: `'/etc/gunicorn.d'` -##### `manage_config_dir` +##### `manage_config_dir` Data type: `Boolean` Set if the gunicorn config directory should be created. -Default value: ``false`` +Default value: `false` -##### `virtualenv` +##### `virtualenv` Data type: `Variant[Boolean,Stdlib::Absolutepath]` Run in virtualenv, specify directory. -Default value: ``false`` +Default value: `false` -##### `mode` +##### `mode` Data type: `Enum['wsgi', 'django']` @@ -428,13 +522,13 @@ Gunicorn mode. Default value: `'wsgi'` -##### `dir` +##### `dir` Data type: `Stdlib::Absolutepath` Application directory. -##### `bind` +##### `bind` Data type: `Variant[String[1],Boolean]` @@ -442,17 +536,17 @@ Bind on: 'HOST', 'HOST:PORT', 'unix:PATH'. Default: system-wide: unix:/tmp/gunicorn-$name.socket virtualenv: unix:${virtualenv}/${name}.socket -Default value: ``false`` +Default value: `false` -##### `environment` +##### `environment` Data type: `Variant[String[1],Boolean]` Set ENVIRONMENT variable. -Default value: ``false`` +Default value: `false` -##### `appmodule` +##### `appmodule` Data type: `String[1]` @@ -460,15 +554,15 @@ Set the application module name for gunicorn to load when not using Django. Default value: `'app:app'` -##### `osenv` +##### `osenv` Data type: `Variant[Boolean,Hash]` Allows setting environment variables for the gunicorn service. Accepts a hash of 'key': 'value' pairs. -Default value: ``false`` +Default value: `false` -##### `timeout` +##### `timeout` Data type: `Integer` @@ -476,7 +570,7 @@ Allows setting the gunicorn idle worker process time before being killed. The un Default value: `30` -##### `template` +##### `template` Data type: `String[1]` @@ -484,7 +578,7 @@ Which ERB template to use. Default value: `'python/gunicorn.erb'` -##### `args` +##### `args` Data type: `Array` @@ -492,7 +586,7 @@ Custom arguments to add in gunicorn config file. Default value: `[]` -##### `owner` +##### `owner` Data type: `String[1]` @@ -500,7 +594,7 @@ Data type: `String[1]` Default value: `'www-data'` -##### `group` +##### `group` Data type: `String[1]` @@ -508,39 +602,39 @@ Data type: `String[1]` Default value: `'www-data'` -##### `workers` +##### `workers` Data type: `Variant[Boolean,Integer]` -Default value: ``false`` +Default value: `false` -##### `access_log_format` +##### `access_log_format` Data type: `Variant[Boolean,String[1]]` -Default value: ``false`` +Default value: `false` -##### `accesslog` +##### `accesslog` Data type: `Variant[Boolean,Stdlib::Absolutepath]` -Default value: ``false`` +Default value: `false` -##### `errorlog` +##### `errorlog` Data type: `Variant[Boolean,Stdlib::Absolutepath]` -Default value: ``false`` +Default value: `false` -##### `log_level` +##### `log_level` Data type: `Python::Loglevel` @@ -548,7 +642,7 @@ Data type: `Python::Loglevel` Default value: `'error'` -### `python::pip` +### `python::pip` Installs and manages packages from pip. @@ -573,7 +667,7 @@ python::pip { 'cx_Oracle' : virtualenv => '/var/www/project1', owner => 'appuser', proxy => 'http://proxy.domain.com:3128', - environment => 'ORACLE_HOME=/usr/lib/oracle/11.2/client64', + environment => ['ORACLE_HOME=/usr/lib/oracle/11.2/client64'], install_args => '-e', timeout => 1800, } @@ -594,13 +688,36 @@ python::pip { 'requests' : #### Parameters -The following parameters are available in the `python::pip` defined type. - -##### `name` +The following parameters are available in the `python::pip` defined type: + +* [`name`](#-python--pip--name) +* [`pkgname`](#-python--pip--pkgname) +* [`ensure`](#-python--pip--ensure) +* [`virtualenv`](#-python--pip--virtualenv) +* [`pip_provider`](#-python--pip--pip_provider) +* [`url`](#-python--pip--url) +* [`owner`](#-python--pip--owner) +* [`group`](#-python--pip--group) +* [`index`](#-python--pip--index) +* [`extra_index`](#-python--pip--extra_index) +* [`proxy`](#-python--pip--proxy) +* [`editable`](#-python--pip--editable) +* [`environment`](#-python--pip--environment) +* [`extras`](#-python--pip--extras) +* [`timeout`](#-python--pip--timeout) +* [`install_args`](#-python--pip--install_args) +* [`uninstall_args`](#-python--pip--uninstall_args) +* [`log_dir`](#-python--pip--log_dir) +* [`egg`](#-python--pip--egg) +* [`umask`](#-python--pip--umask) +* [`path`](#-python--pip--path) +* [`exec_provider`](#-python--pip--exec_provider) + +##### `name` must be unique -##### `pkgname` +##### `pkgname` Data type: `String[1]` @@ -608,7 +725,7 @@ the name of the package. Default value: `$name` -##### `ensure` +##### `ensure` Data type: `Variant[Enum[present, absent, latest], String[1]]` @@ -616,7 +733,7 @@ Require pip to be available. Default value: `present` -##### `virtualenv` +##### `virtualenv` Data type: `Variant[Enum['system'], Stdlib::Absolutepath]` @@ -624,7 +741,7 @@ virtualenv to run pip in. Default value: `'system'` -##### `pip_provider` +##### `pip_provider` Data type: `String[1]` @@ -632,15 +749,15 @@ version of pip you wish to use. Default value: `'pip'` -##### `url` +##### `url` Data type: `Variant[Boolean, String]` URL to install from. -Default value: ``false`` +Default value: `false` -##### `owner` +##### `owner` Data type: `String[1]` @@ -648,7 +765,7 @@ The owner of the virtualenv being manipulated. Default value: `'root'` -##### `group` +##### `group` Data type: `Optional[String[1]]` @@ -656,31 +773,39 @@ The group of the virtualenv being manipulated. Default value: `getvar('python::params::group')` -##### `index` +##### `index` Data type: `Variant[Boolean,String[1]]` Base URL of Python package index. -Default value: ``false`` +Default value: `false` -##### `proxy` +##### `extra_index` + +Data type: `Variant[Boolean,String[1]]` + +Base URL of extra Python package index. + +Default value: `false` + +##### `proxy` Data type: `Optional[Stdlib::HTTPUrl]` Proxy server to use for outbound connections. -Default value: ``undef`` +Default value: `undef` -##### `editable` +##### `editable` Data type: `Boolean` If true the package is installed as an editable resource. -Default value: ``false`` +Default value: `false` -##### `environment` +##### `environment` Data type: `Array` @@ -688,7 +813,7 @@ Additional environment variables required to install the packages. Default value: `[]` -##### `extras` +##### `extras` Data type: `Array` @@ -696,7 +821,7 @@ Extra features provided by the package which should be installed. Default value: `[]` -##### `timeout` +##### `timeout` Data type: `Numeric` @@ -704,23 +829,23 @@ The maximum time in seconds the "pip install" command should take. Default value: `1800` -##### `install_args` +##### `install_args` -Data type: `String` +Data type: `Optional[String[1]]` Any additional installation arguments that will be supplied when running pip install. -Default value: `''` +Default value: `undef` -##### `uninstall_args` +##### `uninstall_args` -Data type: `String` +Data type: `Optional[String[1]]` Any additional arguments that will be supplied when running pip uninstall. -Default value: `''` +Default value: `undef` -##### `log_dir` +##### `log_dir` Data type: `String[1]` @@ -728,23 +853,23 @@ Log directory Default value: `'/tmp'` -##### `egg` +##### `egg` Data type: `Any` The egg name to use -Default value: ``false`` +Default value: `false` -##### `umask` +##### `umask` Data type: `Optional[Python::Umask]` -Default value: ``undef`` +Default value: `undef` -##### `path` +##### `path` Data type: `Array[String]` @@ -752,7 +877,7 @@ Data type: `Array[String]` Default value: `['/usr/local/bin','/usr/bin','/bin', '/usr/sbin']` -##### `exec_provider` +##### `exec_provider` Data type: `String[1]` @@ -760,7 +885,7 @@ Data type: `String[1]` Default value: `'shell'` -### `python::pyvenv` +### `python::pyvenv` Create a Python3 virtualenv using pyvenv. @@ -781,9 +906,22 @@ python::pyvenv { '/var/www/project1' : #### Parameters -The following parameters are available in the `python::pyvenv` defined type. +The following parameters are available in the `python::pyvenv` defined type: + +* [`ensure`](#-python--pyvenv--ensure) +* [`version`](#-python--pyvenv--version) +* [`systempkgs`](#-python--pyvenv--systempkgs) +* [`venv_dir`](#-python--pyvenv--venv_dir) +* [`owner`](#-python--pyvenv--owner) +* [`group`](#-python--pyvenv--group) +* [`mode`](#-python--pyvenv--mode) +* [`path`](#-python--pyvenv--path) +* [`environment`](#-python--pyvenv--environment) +* [`prompt`](#-python--pyvenv--prompt) +* [`python_path`](#-python--pyvenv--python_path) +* [`pip_version`](#-python--pyvenv--pip_version) -##### `ensure` +##### `ensure` Data type: `Python::Package::Ensure` @@ -791,7 +929,7 @@ Data type: `Python::Package::Ensure` Default value: `present` -##### `version` +##### `version` Data type: `Python::Version` @@ -799,15 +937,15 @@ Python version to use. Default value: `'system'` -##### `systempkgs` +##### `systempkgs` Data type: `Boolean` Copy system site-packages into virtualenv -Default value: ``false`` +Default value: `false` -##### `venv_dir` +##### `venv_dir` Data type: `Stdlib::Absolutepath` @@ -815,7 +953,7 @@ Directory to install virtualenv to Default value: `$name` -##### `owner` +##### `owner` Data type: `String[1]` @@ -823,7 +961,7 @@ The owner of the virtualenv being manipulated Default value: `'root'` -##### `group` +##### `group` Data type: `String[1]` @@ -831,7 +969,7 @@ The group relating to the virtualenv being manipulated Default value: `'root'` -##### `mode` +##### `mode` Data type: `Stdlib::Filemode` @@ -839,7 +977,7 @@ Optionally specify directory mode Default value: `'0755'` -##### `path` +##### `path` Data type: `Array[Stdlib::Absolutepath]` @@ -847,15 +985,39 @@ Specifies the PATH variable. Default value: `['/bin', '/usr/bin', '/usr/sbin', '/usr/local/bin',]` -##### `environment` +##### `environment` -Data type: `Array` +Data type: `Array[String[1]]` Optionally specify environment variables for pyvenv Default value: `[]` -### `python::requirements` +##### `prompt` + +Data type: `Optional[String[1]]` + +Optionally specify the virtualenv prompt (python >= 3.6) + +Default value: `undef` + +##### `python_path` + +Data type: `Optional[Stdlib::Absolutepath]` + +Optionally specify python path for creation of virtualenv + +Default value: `undef` + +##### `pip_version` + +Data type: `Python::Venv::PipVersion` + + + +Default value: `'latest'` + +### `python::requirements` Installs and manages Python packages from requirements file. @@ -874,9 +1036,25 @@ python::requirements { '/var/www/project1/requirements.txt' : #### Parameters -The following parameters are available in the `python::requirements` defined type. - -##### `requirements` +The following parameters are available in the `python::requirements` defined type: + +* [`requirements`](#-python--requirements--requirements) +* [`virtualenv`](#-python--requirements--virtualenv) +* [`pip_provider`](#-python--requirements--pip_provider) +* [`owner`](#-python--requirements--owner) +* [`group`](#-python--requirements--group) +* [`proxy`](#-python--requirements--proxy) +* [`src`](#-python--requirements--src) +* [`environment`](#-python--requirements--environment) +* [`forceupdate`](#-python--requirements--forceupdate) +* [`cwd`](#-python--requirements--cwd) +* [`extra_pip_args`](#-python--requirements--extra_pip_args) +* [`manage_requirements`](#-python--requirements--manage_requirements) +* [`fix_requirements_owner`](#-python--requirements--fix_requirements_owner) +* [`log_dir`](#-python--requirements--log_dir) +* [`timeout`](#-python--requirements--timeout) + +##### `requirements` Data type: `Stdlib::Absolutepath` @@ -884,7 +1062,7 @@ Path to the requirements file. Default value: `$name` -##### `virtualenv` +##### `virtualenv` Data type: `Variant[Enum['system'],Stdlib::Absolutepath]` @@ -892,7 +1070,7 @@ virtualenv to run pip in. Default value: `'system'` -##### `pip_provider` +##### `pip_provider` Data type: `Enum['pip', 'pip3']` @@ -900,7 +1078,7 @@ version of pip you wish to use. Default value: `'pip'` -##### `owner` +##### `owner` Data type: `String[1]` @@ -908,7 +1086,7 @@ The owner of the virtualenv being manipulated. Default value: `'root'` -##### `group` +##### `group` Data type: `String[1]` @@ -916,23 +1094,23 @@ The group relating to the virtualenv being manipulated. Default value: `'root'` -##### `proxy` +##### `proxy` Data type: `Optional[Stdlib::HTTPUrl]` Proxy server to use for outbound connections. -Default value: ``undef`` +Default value: `undef` -##### `src` +##### `src` Data type: `Any` Pip --src parameter to; if the requirements file contains --editable resources, this parameter specifies where they will be installed. See the pip documentation for more. -Default value: ``false`` +Default value: `false` -##### `environment` +##### `environment` Data type: `Array` @@ -940,47 +1118,47 @@ Additional environment variables required to install the packages. Default value: `[]` -##### `forceupdate` +##### `forceupdate` Data type: `Boolean` Run a pip install requirements even if we don't receive an event from the requirements file - Useful for when the requirements file is written as part of a resource other than file (E.g vcsrepo) -Default value: ``false`` +Default value: `false` -##### `cwd` +##### `cwd` Data type: `Optional[Stdlib::Absolutepath]` The directory from which to run the "pip install" command. -Default value: ``undef`` +Default value: `undef` -##### `extra_pip_args` +##### `extra_pip_args` -Data type: `String` +Data type: `Optional[String[1]]` Extra arguments to pass to pip after the requirements file -Default value: `''` +Default value: `undef` -##### `manage_requirements` +##### `manage_requirements` Data type: `Boolean` Create the requirements file if it doesn't exist. -Default value: ``true`` +Default value: `true` -##### `fix_requirements_owner` +##### `fix_requirements_owner` Data type: `Boolean` Change owner and group of requirements file. -Default value: ``true`` +Default value: `true` -##### `log_dir` +##### `log_dir` Data type: `Stdlib::Absolutepath` @@ -988,7 +1166,7 @@ Log directory. Default value: `'/tmp'` -##### `timeout` +##### `timeout` Data type: `Integer` @@ -998,33 +1176,39 @@ Default value: `1800` ## Data types -### `Python::Loglevel` +### `Python::Loglevel` -A version type to match all valid loglevels for python +Match all valid loglevels for python Alias of `Enum['debug', 'info', 'warning', 'error', 'critical']` -### `Python::Package::Ensure` +### `Python::Package::Ensure` -A version type to match all valid package ensures for python +Match all valid package ensures for python -Alias of `Enum['absent', 'present', 'latest']` +Alias of `Enum['absent', 'present', 'installed', 'latest']` -### `Python::Provider` +### `Python::Provider` -A version type to match all valid provider for python +Match all valid provider for python Alias of `Enum['pip', 'scl', 'rhscl', 'anaconda', '']` -### `Python::Umask` +### `Python::Umask` -A version type to match valid umask for python +Match valid umask for python Alias of `Pattern[/[0-7]{1,4}/]` -### `Python::Version` +### `Python::Venv::PipVersion` + +A version type to ensure a specific Pip version in a virtual env. + +Alias of `Pattern[/^(<|>|<=|>=|==) [0-9]*(\.[0-9]+)*$/, /\Alatest\Z/]` + +### `Python::Version` -A version type to match all valid versions for python +Match all valid versions for python Alias of `Pattern[/\A(python)?[0-9](\.?[0-9])*/, /\Apypy\Z/, /\Asystem\Z/, /\Arh-python[0-9]{2}(?:-python)?\Z/]` diff --git a/Rakefile b/Rakefile index d1bf7491..fab1b62a 100644 --- a/Rakefile +++ b/Rakefile @@ -1,16 +1,25 @@ -# Attempt to load voxupuli-test (which pulls in puppetlabs_spec_helper), -# otherwise attempt to load it directly. +# Managed by modulesync - DO NOT EDIT +# https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ + begin require 'voxpupuli/test/rake' rescue LoadError - require 'puppetlabs_spec_helper/rake_tasks' + # only available if gem group test is installed +end + +begin + require 'voxpupuli/acceptance/rake' +rescue LoadError + # only available if gem group acceptance is installed end -# load optional tasks for releases -# only available if gem group releases is installed begin require 'voxpupuli/release/rake_tasks' rescue LoadError + # only available if gem group releases is installed +else + GCGConfig.user = 'voxpupuli' + GCGConfig.project = 'puppet-python' end desc "Run main 'test' task and report merged results to coveralls" @@ -24,38 +33,4 @@ task test_with_coveralls: [:test] do end end -desc 'Generate REFERENCE.md' -task :reference, [:debug, :backtrace] do |t, args| - patterns = '' - Rake::Task['strings:generate:reference'].invoke(patterns, args[:debug], args[:backtrace]) -end - -begin - require 'github_changelog_generator/task' - require 'puppet_blacksmith' - GitHubChangelogGenerator::RakeTask.new :changelog do |config| - version = (Blacksmith::Modulefile.new).version - config.future_release = "v#{version}" if version =~ /^\d+\.\d+.\d+$/ - config.header = "# Changelog\n\nAll notable changes to this project will be documented in this file.\nEach new release typically also includes the latest modulesync defaults.\nThese should not affect the functionality of the module." - config.exclude_labels = %w{duplicate question invalid wontfix wont-fix modulesync skip-changelog} - config.user = 'voxpupuli' - metadata_json = File.join(File.dirname(__FILE__), 'metadata.json') - metadata = JSON.load(File.read(metadata_json)) - config.project = metadata['name'] - end - - # Workaround for https://github.com/github-changelog-generator/github-changelog-generator/issues/715 - require 'rbconfig' - if RbConfig::CONFIG['host_os'] =~ /linux/ - task :changelog do - puts 'Fixing line endings...' - changelog_file = File.join(__dir__, 'CHANGELOG.md') - changelog_txt = File.read(changelog_file) - new_contents = changelog_txt.gsub(%r{\r\n}, "\n") - File.open(changelog_file, "w") {|file| file.puts new_contents } - end - end - -rescue LoadError -end # vim: syntax=ruby diff --git a/data/common.yaml b/data/common.yaml new file mode 100644 index 00000000..8d945465 --- /dev/null +++ b/data/common.yaml @@ -0,0 +1 @@ +python::default_system_version: "3.11" diff --git a/data/os/Archlinux.yaml b/data/os/Archlinux.yaml new file mode 100644 index 00000000..3bd58ed5 --- /dev/null +++ b/data/os/Archlinux.yaml @@ -0,0 +1 @@ +python::default_system_version: "3.13" diff --git a/data/os/Debian/11.yaml b/data/os/Debian/11.yaml new file mode 100644 index 00000000..c3b18ece --- /dev/null +++ b/data/os/Debian/11.yaml @@ -0,0 +1 @@ +python::default_system_version: "3.9" diff --git a/data/os/Debian/12.yaml b/data/os/Debian/12.yaml new file mode 100644 index 00000000..8d945465 --- /dev/null +++ b/data/os/Debian/12.yaml @@ -0,0 +1 @@ +python::default_system_version: "3.11" diff --git a/data/os/Debian/13.yaml b/data/os/Debian/13.yaml new file mode 100644 index 00000000..3bd58ed5 --- /dev/null +++ b/data/os/Debian/13.yaml @@ -0,0 +1 @@ +python::default_system_version: "3.13" diff --git a/data/os/FreeBSD.yaml b/data/os/FreeBSD.yaml new file mode 100644 index 00000000..8d945465 --- /dev/null +++ b/data/os/FreeBSD.yaml @@ -0,0 +1 @@ +python::default_system_version: "3.11" diff --git a/data/os/Gentoo.yaml b/data/os/Gentoo.yaml new file mode 100644 index 00000000..68b52939 --- /dev/null +++ b/data/os/Gentoo.yaml @@ -0,0 +1 @@ +python::default_system_version: "3.12" diff --git a/data/os/RedHat/8.yaml b/data/os/RedHat/8.yaml new file mode 100644 index 00000000..5de86ac2 --- /dev/null +++ b/data/os/RedHat/8.yaml @@ -0,0 +1 @@ +python::default_system_version: "3.6" diff --git a/data/os/RedHat/9.yaml b/data/os/RedHat/9.yaml new file mode 100644 index 00000000..c3b18ece --- /dev/null +++ b/data/os/RedHat/9.yaml @@ -0,0 +1 @@ +python::default_system_version: "3.9" diff --git a/data/os/Ubuntu/22.04.yaml b/data/os/Ubuntu/22.04.yaml new file mode 100644 index 00000000..383744c0 --- /dev/null +++ b/data/os/Ubuntu/22.04.yaml @@ -0,0 +1 @@ +python::default_system_version: "3.10" diff --git a/data/os/Ubuntu/24.04.yaml b/data/os/Ubuntu/24.04.yaml new file mode 100644 index 00000000..68b52939 --- /dev/null +++ b/data/os/Ubuntu/24.04.yaml @@ -0,0 +1 @@ +python::default_system_version: "3.12" diff --git a/examples/gunicorn.pp b/examples/gunicorn.pp index 9308f000..c83a8224 100644 --- a/examples/gunicorn.pp +++ b/examples/gunicorn.pp @@ -1,7 +1,6 @@ class { 'python': - version => 'system', - dev => true, - virtualenv => true, + version => 'system', + dev => true, } python::gunicorn { 'vhost': diff --git a/examples/init.pp b/examples/init.pp index 4c51cc80..ab934fa9 100644 --- a/examples/init.pp +++ b/examples/init.pp @@ -1,5 +1,4 @@ class { 'python': - version => 'system', - dev => true, - virtualenv => true, + version => 'system', + dev => true, } diff --git a/examples/pip.pp b/examples/pip.pp index f0db1728..a65be945 100644 --- a/examples/pip.pp +++ b/examples/pip.pp @@ -1,7 +1,6 @@ class { 'python': - version => 'system', - dev => true, - virtualenv => true, + version => 'system', + dev => true, } python::pip { 'flask': diff --git a/examples/requirements.pp b/examples/requirements.pp index 90dccd8b..4dcf289e 100644 --- a/examples/requirements.pp +++ b/examples/requirements.pp @@ -1,7 +1,6 @@ class { 'python': - version => 'system', - dev => true, - virtualenv => true, + version => 'system', + dev => true, } python::requirements { '/var/www/project1/requirements.txt': diff --git a/examples/virtualenv.pp b/examples/virtualenv.pp deleted file mode 100644 index da5ab57c..00000000 --- a/examples/virtualenv.pp +++ /dev/null @@ -1,13 +0,0 @@ -class { 'python': - version => 'system', - dev => true, - virtualenv => true, -} - -python::virtualenv { '/var/www/project1': - ensure => present, - version => 'system', - requirements => '/var/www/project1/requirements.txt', - proxy => 'http://proxy.domain.com:3128', - systempkgs => true, -} diff --git a/hiera.yaml b/hiera.yaml new file mode 100644 index 00000000..9cea3e4d --- /dev/null +++ b/hiera.yaml @@ -0,0 +1,23 @@ +--- +version: 5 + +defaults: # Used for any hierarchy level that omits these keys. + datadir: data # This path is relative to hiera.yaml's directory. + data_hash: yaml_data # Use the built-in YAML backend. + +hierarchy: + - name: "archicture" + paths: + - "architecture/%{facts.os.architecture}.yaml" + - "architecture/common.yaml" + - name: "osfamily/major release" + paths: + - "os/%{facts.os.name}/%{facts.os.release.major}.yaml" # Used to distinguish between Debian and Ubuntu + - "os/%{facts.os.family}/%{facts.os.release.major}.yaml" # + - "os/%{facts.os.family}/%{facts.kernelrelease}.yaml" # Used for Solaris + - name: "osfamily" + paths: + - "os/%{facts.os.name}.yaml" + - "os/%{facts.os.family}.yaml" + - name: 'common' + path: 'common.yaml' diff --git a/lib/facter/pip_version.rb b/lib/facter/pip_version.rb index 11db4d60..3303b237 100644 --- a/lib/facter/pip_version.rb +++ b/lib/facter/pip_version.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Make pip version available as a fact def get_pip_version(executable) diff --git a/lib/facter/python_release.rb b/lib/facter/python_release.rb index 5013b5c3..fab88dbd 100644 --- a/lib/facter/python_release.rb +++ b/lib/facter/python_release.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Make python release available as facts def get_python_release(executable) diff --git a/lib/facter/python_version.rb b/lib/facter/python_version.rb index e21723bb..fe70684e 100644 --- a/lib/facter/python_version.rb +++ b/lib/facter/python_version.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Make python versions available as facts def get_python_version(executable) diff --git a/lib/facter/virtualenv_version.rb b/lib/facter/virtualenv_version.rb deleted file mode 100644 index dbaba059..00000000 --- a/lib/facter/virtualenv_version.rb +++ /dev/null @@ -1,10 +0,0 @@ -# Make virtualenv version available as a fact - -Facter.add('virtualenv_version') do - setcode do - if Facter::Util::Resolution.which('virtualenv') - results = Facter::Util::Resolution.exec('virtualenv --version 2>&1').match(%r{(\d+\.\d+\.?\d*).*$}) - results[1] if results - end - end -end diff --git a/manifests/dotfile.pp b/manifests/dotfile.pp index 4961f12f..66d59b1e 100644 --- a/manifests/dotfile.pp +++ b/manifests/dotfile.pp @@ -26,7 +26,7 @@ Enum['absent', 'present'] $ensure = 'present', Stdlib::Absolutepath $filename = $title, String[1] $owner = 'root', - String[1] $group = 'root', + String[1] $group = getvar('python::params::group'), Stdlib::Filemode $mode = '0644', Hash $config = {}, ) { diff --git a/manifests/gunicorn.pp b/manifests/gunicorn.pp index 7670f394..5393ac33 100644 --- a/manifests/gunicorn.pp +++ b/manifests/gunicorn.pp @@ -55,6 +55,8 @@ String[1] $template = 'python/gunicorn.erb', Array $args = [], ) { + $processor_count = fact('processors.count') + if $manage_config_dir { file { $config_dir: ensure => directory, diff --git a/manifests/init.pp b/manifests/init.pp index 74da7eb6..31061daf 100644 --- a/manifests/init.pp +++ b/manifests/init.pp @@ -1,5 +1,6 @@ # @summary Installs and manages python, python-dev and gunicorn. # +# @param default_system_version The default version of Python provided by the operating system. Only used as a fallback if Python is not installed yet to determine how to handle some actions that vary depending on the Python version used. # @param ensure Desired installation state for the Python package. # @param version Python version to install. Beware that valid values for this differ a) by the provider you choose and b) by the osfamily/operatingsystem you are using. # Allowed values: @@ -17,6 +18,11 @@ # @param use_epel to determine if the epel class is used. # @param manage_scl Whether to manage core SCL packages or not. # @param umask The default umask for invoked exec calls. +# @param manage_gunicorn manage the state for package gunicorn +# @param manage_python_package manage the state for package python +# @param manage_dev_package manage the state of the python development package +# @param manage_venv_package manage the state for package venv +# @param manage_pip_package manage the state for package pip # # @example install python from system python # class { 'python': @@ -33,26 +39,29 @@ # } # class python ( - Python::Package::Ensure $ensure = $python::params::ensure, - Python::Version $version = '3', - Python::Package::Ensure $pip = $python::params::pip, - Python::Package::Ensure $dev = $python::params::dev, - Python::Package::Ensure $gunicorn = $python::params::gunicorn, - Boolean $manage_gunicorn = $python::params::manage_gunicorn, - Boolean $manage_python_package = $python::params::manage_python_package, + Python::Version $default_system_version, + Python::Package::Ensure $ensure = 'present', + Python::Version $version = $facts['os']['family'] ? { 'Archlinux' => 'system', default => '3' }, + Python::Package::Ensure $pip = 'present', + Python::Package::Ensure $dev = 'absent', + Python::Package::Ensure $venv = 'absent', + Python::Package::Ensure $gunicorn = 'absent', + Boolean $manage_gunicorn = true, + Boolean $manage_python_package = true, + Boolean $manage_dev_package = true, + Boolean $manage_venv_package = $python::params::manage_venv_package, Boolean $manage_pip_package = $python::params::manage_pip_package, String[1] $gunicorn_package_name = $python::params::gunicorn_package_name, - Optional[Python::Provider] $provider = $python::params::provider, + Optional[Python::Provider] $provider = undef, Hash $python_pips = {}, Hash $python_pyvenvs = {}, Hash $python_requirements = {}, Hash $python_dotfiles = {}, Boolean $use_epel = $python::params::use_epel, - Boolean $rhscl_use_public_repository = $python::params::rhscl_use_public_repository, - Stdlib::Httpurl $anaconda_installer_url = $python::params::anaconda_installer_url, - Stdlib::Absolutepath $anaconda_install_path = $python::params::anaconda_install_path, - Boolean $manage_scl = $python::params::manage_scl, - Optional[Python::Umask] $umask = undef, + Boolean $rhscl_use_public_repository = true, + Stdlib::Httpurl $anaconda_installer_url = 'https://repo.anaconda.com/archive/Anaconda3-5.2.0-Linux-x86_64.sh', + Stdlib::Absolutepath $anaconda_install_path = '/opt/python', + Boolean $manage_scl = true, ) inherits python::params { $exec_prefix = $provider ? { 'scl' => "/usr/bin/scl enable ${version} -- ", @@ -66,11 +75,6 @@ Class['python::install'] -> Class['python::config'] - # Set default umask. - exec { default: - umask => $umask, - } - # Allow hiera configuration of python resources create_resources('python::pip', $python_pips) create_resources('python::pyvenv', $python_pyvenvs) diff --git a/manifests/install.pp b/manifests/install.pp index 64f9350e..60adfc05 100644 --- a/manifests/install.pp +++ b/manifests/install.pp @@ -15,23 +15,13 @@ } $pythondev = $facts['os']['family'] ? { - 'AIX' => "${python}-devel", - 'RedHat' => "${python}-devel", - 'Debian' => "${python}-dev", - 'Suse' => "${python}-devel", - 'Gentoo' => undef, - } - - $pip_ensure = $python::pip ? { - true => 'present', - false => 'absent', - default => $python::pip, - } - - $dev_ensure = $python::dev ? { - true => 'present', - false => 'absent', - default => $python::dev, + 'AIX' => "${python}-devel", + 'Debian' => "${python}-dev", + 'FreeBSD' => undef, + 'Gentoo' => undef, + 'Archlinux' => undef, + 'RedHat' => "${python}-devel", + 'Suse' => "${python}-devel", } if $python::manage_python_package { @@ -41,24 +31,37 @@ } } + if $python::manage_venv_package { + ## + ## CentOS has no extra package for venv + ## + unless $facts['os']['family'] == 'RedHat' { + package { 'python-venv': + ensure => $python::venv, + name => "${python}-venv", + require => Package['python'], + } + } + } + case $python::provider { 'pip': { if $python::manage_pip_package { package { 'pip': - ensure => $pip_ensure, + ensure => $python::pip, require => Package['python'], } } - if $pythondev { + if $python::manage_dev_package and $pythondev { package { 'python-dev': - ensure => $dev_ensure, + ensure => $python::dev, name => $pythondev, } } - # Respect the $pip_ensure setting - unless $pip_ensure == 'absent' { + # Respect the $python::pip setting + unless $python::pip == 'absent' { # Install pip without pip, see https://pip.pypa.io/en/stable/installing/. include python::pip::bootstrap @@ -92,16 +95,16 @@ Package['scl-utils'] -> Package["${python}-scldevel"] - if $pip_ensure != 'absent' { + if $python::pip != 'absent' { Package['scl-utils'] -> Exec['python-scl-pip-install'] } } package { "${python}-scldevel": - ensure => $dev_ensure, + ensure => $python::dev, } - if $pip_ensure != 'absent' { + if $python::pip != 'absent' { exec { 'python-scl-pip-install': command => "${python::exec_prefix}easy_install pip", path => ['/usr/bin', '/bin'], @@ -125,17 +128,13 @@ tag => 'python-scl-package', } - Package <| title == 'virtualenv' |> { - name => "${python}-python-virtualenv", - } - package { "${python}-scldevel": - ensure => $dev_ensure, + ensure => $python::dev, tag => 'python-scl-package', } package { "${python}-python-pip": - ensure => $pip_ensure, + ensure => $python::pip, tag => 'python-pip-package', } @@ -173,16 +172,16 @@ } else { if $python::manage_pip_package { package { 'python-pip': - ensure => $pip_ensure, + ensure => $python::pip, require => Package['python'], provider => 'yum', } } } - if $pythondev { + if $python::manage_dev_package and $pythondev { package { 'python-dev': - ensure => $dev_ensure, + ensure => $python::dev, name => $pythondev, alias => $pythondev, provider => 'yum', @@ -192,14 +191,14 @@ default: { if $python::manage_pip_package { package { 'pip': - ensure => $pip_ensure, + ensure => $python::pip, require => Package['python'], } } - if $pythondev { + if $python::manage_dev_package and $pythondev { package { 'python-dev': - ensure => $dev_ensure, + ensure => $python::dev, name => $pythondev, alias => $pythondev, } @@ -208,39 +207,36 @@ } if $facts['os']['family'] == 'RedHat' { - if $pip_ensure != 'absent' and $python::use_epel and ($python::manage_pip_package or $python::manage_python_package) { + if $python::pip != 'absent' and $python::use_epel and ($python::manage_pip_package or $python::manage_python_package) { require epel } } if String($python::version) =~ /^python3/ { - $pip_category = undef $pip_package = "${python}-pip" $pip_provider = $python.regsubst(/^.*python3\.?/,'pip3.').regsubst(/\.$/,'') - } elsif ($facts['os']['family'] == 'RedHat') and (versioncmp($facts['os']['release']['major'], '8') >= 0) { - $pip_category = undef + } elsif $facts['os']['family'] == 'RedHat' { $pip_package = 'python3-pip' $pip_provider = pip3 - } elsif ($facts['os']['family'] == 'RedHat') and (versioncmp($facts['os']['release']['major'], '7') >= 0) { - $pip_category = undef - $pip_package = 'python2-pip' - $pip_provider = pip2 + } elsif $facts['os']['family'] == 'FreeBSD' { + $pip_package = "py${python::version}-pip" + $pip_provider = 'pip' } elsif $facts['os']['family'] == 'Gentoo' { - $pip_category = 'dev-python' - $pip_package = 'pip' + $pip_package = 'dev-python/pip' $pip_provider = 'pip' + } elsif $facts['os']['name'] == 'Ubuntu' { + $pip_package = 'python3-pip' + $pip_provider = 'pip3' + } elsif $facts['os']['name'] == 'Debian' { + $pip_package = 'python3-pip' + $pip_provider = 'pip3' } else { - $pip_package = $facts['os']['release']['major'] ? { - '20.04' => 'python3-pip', - default => 'python-pip', - } - $pip_category = undef + $pip_package = 'python-pip' $pip_provider = 'pip' } Package <| title == 'pip' |> { - name => $pip_package, - category => $pip_category, + name => $pip_package, } } } diff --git a/manifests/params.pp b/manifests/params.pp index e1ed7c21..49bfdeac 100644 --- a/manifests/params.pp +++ b/manifests/params.pp @@ -5,26 +5,10 @@ # class python::params { # Module compatibility check - unless $facts['os']['family'] in ['Debian', 'RedHat', 'Suse', 'Gentoo', 'AIX',] { + unless $facts['os']['family'] in ['AIX', 'Debian', 'FreeBSD', 'Gentoo', 'RedHat', 'Suse', 'Archlinux'] { fail("Module is not compatible with ${facts['os']['name']}") } - $ensure = 'present' - $pip = 'present' - $dev = 'absent' - $virtualenv = 'absent' - $gunicorn = 'absent' - $manage_gunicorn = true - $manage_python_package = true - $manage_virtualenv_package = true - $manage_pip_package = true - $provider = undef - $valid_versions = undef - $manage_scl = true - $rhscl_use_public_repository = true - $anaconda_installer_url = 'https://repo.anaconda.com/archive/Anaconda3-5.2.0-Linux-x86_64.sh' - $anaconda_install_path = '/opt/python' - if $facts['os']['family'] == 'RedHat' and $facts['os']['name'] != 'Fedora' { $use_epel = true } else { @@ -32,8 +16,9 @@ } $group = $facts['os']['family'] ? { - 'AIX' => 'system', - default => 'root' + 'AIX' => 'system', + 'FreeBSD' => 'wheel', + default => 'root' } $pip_lookup_path = $facts['os']['family'] ? { @@ -42,7 +27,19 @@ } $gunicorn_package_name = $facts['os']['family'] ? { - 'RedHat' => 'python-gunicorn', + 'RedHat' => $facts['os']['release']['major'] ? { + '7' => 'python-gunicorn', + default => 'python3-gunicorn', + }, default => 'gunicorn', } + + $manage_pip_package = $facts['os']['family'] ? { + 'Archlinux' => false, + default => true, + } + $manage_venv_package = $facts['os']['family'] ? { + 'Archlinux' => false, + default => true, + } } diff --git a/manifests/pip.pp b/manifests/pip.pp index 3935df72..9d568207 100644 --- a/manifests/pip.pp +++ b/manifests/pip.pp @@ -9,6 +9,7 @@ # @param owner The owner of the virtualenv being manipulated. # @param group The group of the virtualenv being manipulated. # @param index Base URL of Python package index. +# @param extra_index Base URL of extra Python package index. # @param proxy Proxy server to use for outbound connections. # @param editable If true the package is installed as an editable resource. # @param environment Additional environment variables required to install the packages. @@ -33,7 +34,7 @@ # virtualenv => '/var/www/project1', # owner => 'appuser', # proxy => 'http://proxy.domain.com:3128', -# environment => 'ORACLE_HOME=/usr/lib/oracle/11.2/client64', +# environment => ['ORACLE_HOME=/usr/lib/oracle/11.2/client64'], # install_args => '-e', # timeout => 1800, # } @@ -57,13 +58,14 @@ Optional[String[1]] $group = getvar('python::params::group'), Optional[Python::Umask] $umask = undef, Variant[Boolean,String[1]] $index = false, + Variant[Boolean,String[1]] $extra_index = false, Optional[Stdlib::HTTPUrl] $proxy = undef, Any $egg = false, Boolean $editable = false, Array $environment = [], Array $extras = [], - String $install_args = '', - String $uninstall_args = '', + Optional[String[1]] $install_args = undef, + Optional[String[1]] $uninstall_args = undef, Numeric $timeout = 1800, String[1] $log_dir = '/tmp', Array[String] $path = ['/usr/local/bin','/usr/bin','/bin', '/usr/sbin'], @@ -114,6 +116,11 @@ default => "--index-url=${index}", } + $pypi_extra_index = $extra_index ? { + false => '', + default => "--extra-index-url=${extra_index}", + } + $proxy_flag = $proxy ? { undef => '', default => "--proxy=${proxy}", @@ -127,11 +134,11 @@ } # TODO: Do more robust argument checking, but below is a start - if ($ensure == absent) and ($install_args != '') { + if ($ensure == absent) and $install_args { fail('python::pip cannot provide install_args with ensure => absent') } - if ($ensure == present) and ($uninstall_args != '') { + if ($ensure == present) and $uninstall_args { fail('python::pip cannot provide uninstall_args with ensure => present') } @@ -148,11 +155,13 @@ $_ensure = $ensure } - # Check if searching by explicit version. - if $_ensure =~ /^((19|20)[0-9][0-9]-(0[1-9]|1[1-2])-([0-2][1-9]|3[0-1])|[0-9]+\.\w+\+?\w*(\.\w+)*)$/ { - $grep_regex = "^${real_pkgname}[[:space:]]\\+(\\?${_ensure}\\()$\\|$\\|, \\|[[:space:]]\\)" - } else { - $grep_regex = "^${real_pkgname}[[:space:]].*$" + # We do not try to mimic a version scheme validation which is already implemented by the package manager. + # If it starts with a number it is probably a version. + # If it wasn't or if there is any error, the package manager will trigger a failure. + $grep_regex = $_ensure ? { + /^(present|absent|latest)$/ => "^${real_pkgname}[[:space:]].*$", + /^v?[0-9].*$/ => "^${real_pkgname}[[:space:]]\\+(\\?${_ensure}\\()$\\|$\\|, \\|[[:space:]]\\)", + default => fail('ensure can be a version number (e.g. 1.7.0 or v1.7.0) or one of: present, absent, latest') } $extras_string = empty($extras) ? { @@ -172,8 +181,8 @@ default => "'${url}#egg=${egg_name}'", } - $pip_install = "${pip_env} --log ${log}/pip.log install" - $pip_common_args = "${pypi_index} ${proxy_flag} ${install_args} ${install_editable} ${source}" + $pip_install = "${pip_env} install" + $pip_common_args = "${pypi_index} ${pypi_extra_index} ${proxy_flag} ${install_editable} ${source}" # Explicit version out of VCS when PIP supported URL is provided if $source =~ /^'(git\+|hg\+|bzr\+|svn\+)(http|https|ssh|svn|sftp|ftp|lp|git)(:\/\/).+'$/ { @@ -186,35 +195,44 @@ } } else { case $_ensure { - /^((19|20)[0-9][0-9]-(0[1-9]|1[1-2])-([0-2][1-9]|3[0-1])|[0-9]+\.\w+\+?\w*(\.\w+)*)$/: { - # Version formats as per http://guide.python-distribute.org/specification.html#standard-versioning-schemes - # Explicit version. + /^v?[0-9].*$/: { + # Specific version $command = "${pip_install} ${install_args} ${pip_common_args}==${_ensure}" $unless_command = "${pip_env} list | grep -i -e '${grep_regex}'" } 'present': { # Whatever version is available. - $command = "${pip_install} ${pip_common_args}" + $command = "${pip_install} ${install_args} ${pip_common_args}" $unless_command = "${pip_env} list | grep -i -e '${grep_regex}'" } 'latest': { + $pip_version = $facts['pip_version'] + if $pip_version and versioncmp($pip_version, '21.1') == -1 and versioncmp($pip_version, '20.2.4') == 1 { + $legacy_resolver = '--use-deprecated=legacy-resolver' + } else { + $legacy_resolver = '' + } + # Unfortunately this is the smartest way of getting the latest available package version with pip as of now # Note: we DO need to repeat ourselves with "from version" in both grep and sed as on some systems pip returns # more than one line with paretheses. - $latest_version = join( [ - "${pip_install} ${pypi_index} ${proxy_flag} ${install_args} ${install_editable} ${real_pkgname}==notreallyaversion 2>&1", - ' | grep -oP "\(from versions: .*\)" | sed -E "s/\(from versions: (.*?, )*(.*)\)/\2/g"', + $latest_version = join( + [ + "${pip_install} ${legacy_resolver} ${pypi_index} ${pypi_extra_index} ${proxy_flag}", + " ${install_args} ${install_editable} '${real_pkgname}==9!0dev0+x' 2>&1", + " | sed -nE 's/.*\\(from versions: (.*, )*(.*)\\)/\\2/p'", ' | tr -d "[:space:]"', - ]) + ], + ) # Packages with underscores in their names are listed with dashes in their place in `pip freeze` output $pkgname_with_dashes = regsubst($real_pkgname, '_', '-', 'G') $grep_regex_pkgname_with_dashes = "^${pkgname_with_dashes}==" - $installed_version = join( ["${pip_env} freeze --all", " | grep -i -e ${grep_regex_pkgname_with_dashes} | cut -d= -f3", " | tr -d '[:space:]'",]) + $installed_version = join(["${pip_env} freeze --all", " | grep -i -e ${grep_regex_pkgname_with_dashes} | cut -d= -f3", " | tr -d '[:space:]'",]) - $command = "${pip_install} --upgrade ${pip_common_args}" + $command = "${pip_install} --upgrade ${install_args} ${pip_common_args}" $unless_command = "[ \$(${latest_version}) = \$(${installed_version}) ]" } diff --git a/manifests/pyvenv.pp b/manifests/pyvenv.pp index ca710015..7aa7202f 100644 --- a/manifests/pyvenv.pp +++ b/manifests/pyvenv.pp @@ -10,6 +10,8 @@ # @param mode Optionally specify directory mode # @param path Specifies the PATH variable. # @param environment Optionally specify environment variables for pyvenv +# @param prompt Optionally specify the virtualenv prompt (python >= 3.6) +# @param python_path Optionally specify python path for creation of virtualenv # # @example # python::pyvenv { '/var/www/project1' : @@ -30,33 +32,33 @@ String[1] $group = 'root', Stdlib::Filemode $mode = '0755', Array[Stdlib::Absolutepath] $path = ['/bin', '/usr/bin', '/usr/sbin', '/usr/local/bin',], - Array $environment = [], + Array[String[1]] $environment = [], + Optional[String[1]] $prompt = undef, Python::Venv::PipVersion $pip_version = 'latest', + Optional[Stdlib::Absolutepath] $python_path = undef, ) { include python if $ensure == 'present' { $python_version = $version ? { - 'system' => $facts['python3_version'], + 'system' => $facts['python3_version'].lest || { $python::default_system_version }, default => $version, } $python_version_parts = split($python_version, '[.]') $normalized_python_version = sprintf('%s.%s', $python_version_parts[0], $python_version_parts[1]) - # Debian splits the venv module into a seperate package - if ( $facts['os']['family'] == 'Debian') { - $python3_venv_package = "python${normalized_python_version}-venv" - ensure_packages($python3_venv_package) - - Package[$python3_venv_package] -> File[$venv_dir] + $local_exec_prefix = $python_path ? { + undef => $python::exec_prefix, + default => $python_path, } - # pyvenv is deprecated since 3.6 and will be removed in 3.8 - if versioncmp($normalized_python_version, '3.6') >=0 { - $virtualenv_cmd = "${python::exec_prefix}python${normalized_python_version} -m venv" + if $python_path != undef { + $virtualenv_cmd = "${python_path} -m venv" + } elsif versioncmp($normalized_python_version, '3.6') >=0 { + $virtualenv_cmd = "${local_exec_prefix}python${normalized_python_version} -m venv" } else { - $virtualenv_cmd = "${python::exec_prefix}pyvenv-${normalized_python_version}" + $virtualenv_cmd = "${local_exec_prefix}pyvenv-${normalized_python_version}" } $_path = $python::provider ? { @@ -70,11 +72,18 @@ $system_pkgs_flag = '' } + if versioncmp($normalized_python_version, '3.6') >=0 and $prompt { + $prompt_arg = "--prompt ${shell_escape($prompt)}" + } else { + $prompt_arg = '' + } + file { $venv_dir: - ensure => directory, - owner => $owner, - group => $group, - mode => $mode, + ensure => directory, + owner => $owner, + group => $group, + mode => $mode, + require => Class['python::install'], } $pip_cmd = "${python::exec_prefix}${venv_dir}/bin/pip" @@ -85,7 +94,7 @@ } exec { "python_virtualenv_${venv_dir}": - command => "${virtualenv_cmd} --clear ${system_pkgs_flag} ${venv_dir} && ${pip_cmd} --log ${venv_dir}/pip.log install ${pip_upgrade} && ${pip_cmd} --log ${venv_dir}/pip.log install --upgrade setuptools", + command => "${virtualenv_cmd} --clear ${system_pkgs_flag} ${prompt_arg} ${venv_dir} && ${pip_cmd} install ${pip_upgrade} && ${pip_cmd} install --upgrade setuptools", user => $owner, creates => "${venv_dir}/bin/activate", path => $_path, diff --git a/manifests/requirements.pp b/manifests/requirements.pp index 9d245be3..374a70ae 100644 --- a/manifests/requirements.pp +++ b/manifests/requirements.pp @@ -36,7 +36,7 @@ Array $environment = [], Boolean $forceupdate = false, Optional[Stdlib::Absolutepath] $cwd = undef, - String $extra_pip_args = '', + Optional[String[1]] $extra_pip_args = undef, Boolean $manage_requirements = true, Boolean $fix_requirements_owner = true, Stdlib::Absolutepath $log_dir = '/tmp', @@ -89,6 +89,8 @@ content => '# Puppet will install and/or update pip packages listed here', } + $local_subscribe = File[$requirements] + } elsif File[$requirements] and $manage_requirements == true { $local_subscribe = File[$requirements] } else { $local_subscribe = undef @@ -96,7 +98,7 @@ exec { "python_requirements${name}": provider => shell, - command => "${pip_env} --log ${log}/pip.log install ${proxy_flag} ${src_flag} -r ${requirements} ${extra_pip_args}", + command => "${pip_env} install ${proxy_flag} ${src_flag} -r ${requirements} ${extra_pip_args}", refreshonly => !$forceupdate, timeout => $timeout, cwd => $cwd, diff --git a/metadata.json b/metadata.json index 9bba9594..9cd44130 100644 --- a/metadata.json +++ b/metadata.json @@ -1,17 +1,15 @@ { "name": "puppet-python", - "version": "6.0.2-rc0", + "version": "9.0.0", "author": "Vox Pupuli", - "summary": "Python Module", + "summary": "Puppet module for Python", "license": "Apache-2.0", "source": "https://github.com/voxpupuli/puppet-python.git", "project_page": "https://github.com/voxpupuli/puppet-python", "issues_url": "https://github.com/voxpupuli/puppet-python/issues", - "description": "Puppet module for Python", "tags": [ "python", "pip", - "virtualenv", "gunicorn" ], "operatingsystem_support": [ @@ -22,18 +20,32 @@ "7100-05-03-1837" ] }, + { + "operatingsystem": "AlmaLinux", + "operatingsystemrelease": [ + "8", + "9" + ] + }, { "operatingsystem": "CentOS", "operatingsystemrelease": [ - "7", - "8" + "9" ] }, { "operatingsystem": "Debian", "operatingsystemrelease": [ - "9", - "10" + "11", + "12", + "13" + ] + }, + { + "operatingsystem": "FreeBSD", + "operatingsystemrelease": [ + "13", + "14" ] }, { @@ -42,9 +54,29 @@ { "operatingsystem": "Ubuntu", "operatingsystemrelease": [ - "16.04", - "18.04", - "20.04" + "22.04", + "24.04" + ] + }, + { + "operatingsystem": "OracleLinux", + "operatingsystemrelease": [ + "8", + "9" + ] + }, + { + "operatingsystem": "RedHat", + "operatingsystemrelease": [ + "8", + "9" + ] + }, + { + "operatingsystem": "Rocky", + "operatingsystemrelease": [ + "8", + "9" ] }, { @@ -52,22 +84,25 @@ "operatingsystemrelease": [ "11.3" ] + }, + { + "operatingsystem": "Archlinux" } ], "requirements": [ { - "name": "puppet", - "version_requirement": ">= 6.1.0 < 8.0.0" + "name": "openvox", + "version_requirement": ">= 8.19.0 < 9.0.0" } ], "dependencies": [ { "name": "puppetlabs/stdlib", - "version_requirement": ">= 4.19.0 < 8.0.0" + "version_requirement": ">= 4.19.0 < 10.0.0" }, { "name": "puppet/epel", - "version_requirement": ">= 3.0.0 < 4.0.0" + "version_requirement": ">= 3.0.0 < 7.0.0" } ] } diff --git a/spec/acceptance/class_spec.rb b/spec/acceptance/class_spec.rb index 5caac95e..70c573b0 100644 --- a/spec/acceptance/class_spec.rb +++ b/spec/acceptance/class_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper_acceptance' describe 'python class' do @@ -18,9 +20,9 @@ pp = <<-EOS class { 'python': ensure => 'present', - version => '3', pip => 'present', dev => 'present', + venv => 'present', } EOS @@ -28,10 +30,11 @@ class { 'python': apply_manifest(pp, catch_failures: true) apply_manifest(pp, catch_changes: true) end + fact_notices = <<-EOS notify{"pip_version: ${facts['pip3_version']}":} notify{"python_version: ${facts['python3_version']}":} - EOS + EOS it 'outputs python facts when not installed' do apply_manifest(fact_notices, catch_failures: true) do |r| expect(r.stdout).to match(%r{python_version: 3\.\S+}) diff --git a/spec/acceptance/declared_requirements_install_spec.rb b/spec/acceptance/declared_requirements_install_spec.rb new file mode 100644 index 00000000..bc9c07d4 --- /dev/null +++ b/spec/acceptance/declared_requirements_install_spec.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +require 'spec_helper_acceptance' + +describe 'requirements' do + it 'checks declared requirements file is installed to venv' do + pp = <<-EOS + file { '/tmp/requirements.txt': + ensure => 'present', + content => 'requests', + } + + python::pyvenv { '/tmp/pyvenv': + ensure => 'present', + } + + python::requirements { '/tmp/requirements.txt': + virtualenv => '/tmp/pyvenv' + } + EOS + + apply_manifest(pp, catch_failures: true) + + expect(shell('/tmp/pyvenv/bin/pip3 list --no-index | grep requests').stdout).to match(%r{requests +\d+.\d+.\d+}) + end +end diff --git a/spec/acceptance/facts_test_spec.rb b/spec/acceptance/facts_test_spec.rb index 20f3c54a..b6393a79 100644 --- a/spec/acceptance/facts_test_spec.rb +++ b/spec/acceptance/facts_test_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper_acceptance' describe 'python class' do @@ -6,13 +8,13 @@ class { 'python' : pip => 'present', } - EOS + EOS fact_notices = <<-EOS notify{"pip_version: ${facts['pip_version']}":} notify{"system_python_version: ${facts['system_python_version']}":} notify{"python_version: ${facts['python_version']}":} - EOS + EOS # rubocop:disable RSpec/RepeatedExample it 'outputs python facts when not installed' do diff --git a/spec/acceptance/pip_spec.rb b/spec/acceptance/pip_spec.rb index f2da5ecd..83a639fa 100644 --- a/spec/acceptance/pip_spec.rb +++ b/spec/acceptance/pip_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper_acceptance' describe 'python::pip defined resource' do @@ -5,15 +7,13 @@ it 'works with no errors' do pp = <<-PUPPET class { 'python': - version => '3', - dev => 'present', + dev => 'present', } python::pyvenv { '/opt/test-venv': ensure => 'present', systempkgs => false, mode => '0755', - pip_version => '<= 20.3.4', } python::pip { 'agent package': @@ -28,6 +28,8 @@ class { 'python': end end + # rubocop:disable RSpec/RepeatedExampleGroupDescription + # rubocop:disable RSpec/RepeatedExampleGroupBody describe command('/opt/test-venv/bin/pip list') do its(:exit_status) { is_expected.to eq 0 } its(:stdout) { is_expected.to match %r{agent.* 0\.1\.2} } @@ -37,15 +39,13 @@ class { 'python': it 'works with no errors' do pp = <<-PUPPET class { 'python': - version => '3', - dev => 'present', + dev => 'present', } python::pyvenv { '/opt/test-venv': ensure => 'present', systempkgs => false, mode => '0755', - pip_version => '<= 20.3.4', } python::pip { 'agent package install': @@ -71,4 +71,67 @@ class { 'python': its(:exit_status) { is_expected.to eq 0 } its(:stdout) { is_expected.not_to match %r{agent.* 0\.1\.2} } end + + context 'fails to install package with wrong version' do + it 'throws an error' do + pp = <<-PUPPET + class { 'python': + dev => 'present', + } + + python::pyvenv { '/opt/test-venv': + ensure => 'present', + systempkgs => false, + mode => '0755', + } + + python::pip { 'agent package': + virtualenv => '/opt/test-venv', + pkgname => 'agent', + ensure => '0.1.33+2020-this_is_something-fun', + } + PUPPET + + result = apply_manifest(pp, expect_failures: true) + expect(result.stderr).to contain(%r{returned 1 instead of one of}) + end + end + + describe command('/opt/test-venv/bin/pip show agent') do + its(:exit_status) { is_expected.to eq 1 } + end + + context 'install package via extra_index' do + it 'works with no errors' do + pp = <<-PUPPET + class { 'python': + dev => 'present', + } + + python::pyvenv { '/opt/test-venv': + ensure => 'present', + systempkgs => false, + mode => '0755', + } + + python::pip { 'agent package via extra_index': + virtualenv => '/opt/test-venv', + pkgname => 'agent', + index => 'invalid', + extra_index => 'https://pypi.org/simple', + ensure => '0.1.2', + } + PUPPET + + apply_manifest(pp, catch_failures: true) + apply_manifest(pp, catch_changes: true) + end + end + + describe command('/opt/test-venv/bin/pip list') do + its(:exit_status) { is_expected.to eq 0 } + its(:stdout) { is_expected.to match %r{agent.* 0\.1\.2} } + end + # rubocop:enable RSpec/RepeatedExampleGroupBody + # rubocop:enable RSpec/RepeatedExampleGroupDescription end diff --git a/spec/acceptance/pyvenv_spec.rb b/spec/acceptance/pyvenv_spec.rb index a6234637..263e4a40 100644 --- a/spec/acceptance/pyvenv_spec.rb +++ b/spec/acceptance/pyvenv_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper_acceptance' describe 'python::pyvenv defined resource with python 3' do @@ -6,8 +8,8 @@ it 'works with no errors' do pp = <<-PUPPET class { 'python': - version => '3', - dev => 'present', + dev => 'present', + venv => 'present', } user { 'agent': ensure => 'present', @@ -24,7 +26,6 @@ class { 'python': owner => 'agent', group => 'agent', mode => '0755', - pip_version => '<= 20.3.4', } PUPPET @@ -38,8 +39,8 @@ class { 'python': it 'works with no errors' do pp = <<-PUPPET class { 'python': - version => '3', - dev => 'present', + dev => 'present', + venv => 'present', } user { 'agent': ensure => 'present', @@ -56,7 +57,6 @@ class { 'python': owner => 'agent', group => 'agent', mode => '0755', - pip_version => '<= 20.3.4', } python::pip { 'agent' : ensure => 'latest', @@ -78,8 +78,8 @@ class { 'python': it 'works with no errors' do pp = <<-PUPPET class { 'python': - version => '3', - dev => 'present', + dev => 'present', + venv => 'present', } user { 'agent': ensure => 'present', @@ -96,7 +96,6 @@ class { 'python': owner => 'agent', group => 'agent', mode => '0755', - pip_version => '<= 20.3.4', } python::pip { 'agent' : virtualenv => '/opt/agent/venv', @@ -115,8 +114,8 @@ class { 'python': it 'works with no errors' do pp = <<-PUPPET class { 'python': - version => '3', - dev => 'present', + dev => 'present', + venv => 'present', } user { 'agent': ensure => 'present', @@ -133,7 +132,6 @@ class { 'python': owner => 'agent', group => 'agent', mode => '0755', - pip_version => '<= 20.3.4', } python::pip { 'agent' : virtualenv => '/opt/agent/venv', @@ -152,8 +150,55 @@ class { 'python': it 'works with no errors' do pp = <<-PUPPET class { 'python': - version => '3', - dev => 'present', + dev => 'present', + venv => 'present', + } + user { 'agent': + ensure => 'present', + managehome => true, + home => '/opt/agent', + } + group { 'agent': + ensure => 'present', + system => true, + } + python::pyvenv { '/opt/agent/venv': + ensure => 'present', + systempkgs => false, + owner => 'agent', + group => 'agent', + mode => '0755', + } + python::pip { 'agent' : + ensure => '0.1.2', + virtualenv => '/opt/agent/venv', + owner => 'agent', + group => 'agent', + } + PUPPET + + # Run it twice and test for idempotency + apply_manifest(pp, catch_failures: true) + apply_manifest(pp, catch_changes: true) + end + + describe command('/opt/agent/venv/bin/pip list') do + its(:exit_status) { is_expected.to eq 0 } + its(:stdout) { is_expected.to match %r{agent.* 0\.1\.2} } + end + end + + context 'with versioned minimal python::pip and without systempkgs using custom python path' do + it 'works with no errors' do + pp = <<-PUPPET + + class { 'python': + dev => 'present', + venv => 'present', + } + file { '/usr/bin/mycustompython': + ensure => link, + target => '/usr/bin/python', } user { 'agent': ensure => 'present', @@ -170,7 +215,7 @@ class { 'python': owner => 'agent', group => 'agent', mode => '0755', - pip_version => '<= 20.3.4', + python_path => '/usr/bin/mycustompython', } python::pip { 'agent' : ensure => '0.1.2', diff --git a/spec/classes/python_spec.rb b/spec/classes/python_spec.rb index 4e2bc1ac..f1c2d9a1 100644 --- a/spec/classes/python_spec.rb +++ b/spec/classes/python_spec.rb @@ -1,8 +1,10 @@ -require 'spec_helper' +# frozen_string_literal: true +require 'spec_helper' describe 'python' do on_supported_os.each do |os, facts| next if os == 'gentoo-3-x86_64' + context "on #{os}" do let :facts do facts @@ -14,20 +16,51 @@ it { is_expected.to contain_class('python::params') } it { is_expected.to contain_class('python::config') } it { is_expected.to contain_package('python') } - it { is_expected.to contain_package('pip') } + + if facts[:os]['family'] == 'Archlinux' + it { is_expected.not_to contain_package('pip') } + else + it { is_expected.to contain_package('pip') } + end + + if %w[Archlinux RedHat].include?(facts[:os]['family']) + it { is_expected.not_to contain_package('python-venv') } + else + it { is_expected.to contain_package('python-venv') } + end end context 'without managing things' do let :params do { manage_python_package: false, - manage_pip_package: false + manage_dev_package: false, + manage_pip_package: false, + manage_venv_package: false, } end it { is_expected.to compile.with_all_deps } it { is_expected.not_to contain_package('python') } + it { is_expected.not_to contain_package('python-dev') } it { is_expected.not_to contain_package('pip') } + it { is_expected.not_to contain_package('python-venv') } + end + + context 'with packages present' do + let :params do + { + manage_pip_package: true, + manage_venv_package: true, + pip: 'present', + venv: 'present', + } + end + + it { is_expected.to compile.with_all_deps } + it { is_expected.to contain_package('pip').with(ensure: 'present') } + + it { is_expected.to contain_package('python-venv').with(ensure: 'present') } unless facts[:os]['family'] == 'RedHat' end case facts[:os]['family'] @@ -52,6 +85,7 @@ end end + # rubocop:disable RSpec/RepeatedExampleGroupDescription describe 'with python::dev' do context 'true' do let(:params) { { dev: 'present' } } @@ -59,6 +93,7 @@ it { is_expected.to compile.with_all_deps } it { is_expected.to contain_package('python-dev').with_ensure('present') } end + context 'empty/default' do it { is_expected.to compile.with_all_deps } it { is_expected.to contain_package('python-dev').with_ensure('absent') } @@ -77,12 +112,12 @@ { python_pyvenvs: { '/opt/env1' => { - version: '3.8' + version: '3.8', }, '/opt/env2' => { - version: '3.8' - } - } + version: '3.8', + }, + }, } end @@ -104,13 +139,13 @@ python_pyvenvs: { '/opt/env1' => { version: '3.8', - pip_version: 'latest' + pip_version: 'latest', }, '/opt/env2' => { version: '3.8', - pip_version: '<= 20.3.4' - } - } + pip_version: '<= 20.3.4', + }, + }, } end @@ -118,44 +153,47 @@ it { is_expected.to contain_python__pyvenv('/opt/env1').with_ensure('present') } it { is_expected.to contain_python__pyvenv('/opt/env2').with_ensure('present') } + it { - is_expected.to contain_exec('python_virtualenv_/opt/env1'). - with( - command: 'python3.8 -m venv --clear /opt/env1 && /opt/env1/bin/pip --log /opt/env1/pip.log install --upgrade pip && /opt/env1/bin/pip --log /opt/env1/pip.log install --upgrade setuptools', + expect(subject).to contain_exec('python_virtualenv_/opt/env1') + .with( + command: 'python3.8 -m venv --clear /opt/env1 && /opt/env1/bin/pip install --upgrade pip && /opt/env1/bin/pip install --upgrade setuptools', user: 'root', creates: '/opt/env1/bin/activate', path: [ '/bin', '/usr/bin', '/usr/sbin', - '/usr/local/bin' + '/usr/local/bin', ], cwd: '/tmp', environment: [], timeout: 600, - unless: %r{^grep '\^\[\\t \]\*VIRTUAL_ENV=\[\\\\'\\\"\]\*/opt/env1\[\\\"\\\\'\]\[\\t \]\*\$' /opt/env1/bin/activate$} - ). - that_requires('File[/opt/env1]') + unless: %r{^grep '\^\[\\t \]\*VIRTUAL_ENV=\[\\\\'\\"\]\*/opt/env1\[\\"\\\\'\]\[\\t \]\*\$' /opt/env1/bin/activate$}, + ) + .that_requires('File[/opt/env1]') } + it { - is_expected.to contain_exec('python_virtualenv_/opt/env2'). - with( - command: 'python3.8 -m venv --clear /opt/env2 && /opt/env2/bin/pip --log /opt/env2/pip.log install --upgrade \'pip <= 20.3.4\' && /opt/env2/bin/pip --log /opt/env2/pip.log install --upgrade setuptools', + expect(subject).to contain_exec('python_virtualenv_/opt/env2') + .with( + command: 'python3.8 -m venv --clear /opt/env2 && /opt/env2/bin/pip install --upgrade \'pip <= 20.3.4\' && /opt/env2/bin/pip install --upgrade setuptools', user: 'root', creates: '/opt/env2/bin/activate', path: [ '/bin', '/usr/bin', '/usr/sbin', - '/usr/local/bin' + '/usr/local/bin', ], cwd: '/tmp', environment: [], timeout: 600, - unless: %r{^grep '\^\[\\t \]\*VIRTUAL_ENV=\[\\\\'\\\"\]\*/opt/env2\[\\\"\\\\'\]\[\\t \]\*\$' /opt/env2/bin/activate$} - ). - that_requires('File[/opt/env2]') + unless: %r{^grep '\^\[\\t \]\*VIRTUAL_ENV=\[\\\\'\\"\]\*/opt/env2\[\\"\\\\'\]\[\\t \]\*\$' /opt/env2/bin/activate$}, + ) + .that_requires('File[/opt/env2]') } + it { is_expected.to contain_file('/opt/env1') } it { is_expected.to contain_file('/opt/env2') } end @@ -167,10 +205,12 @@ it { is_expected.to contain_package('gunicorn') } end + context 'empty args' do # let(:params) {{ :manage_gunicorn => '' }} it { is_expected.to contain_package('gunicorn') } end + context 'false' do let(:params) { { manage_gunicorn: false } } @@ -199,6 +239,7 @@ it { is_expected.to contain_package('python-dev').with_ensure('present') } end + context 'default/empty' do it { is_expected.to contain_package('python-dev').with_ensure('absent') } end @@ -224,11 +265,10 @@ end when 'RedHat', 'CentOS' case facts[:os]['release']['major'] - when '7' - - context 'on a Redhat 7 OS' do + when '8' + context 'on a Redhat 8 OS' do it { is_expected.to contain_class('python::install') } - it { is_expected.to contain_package('pip').with_name('python2-pip') } + it { is_expected.to contain_package('pip').with_name('python3-pip') } describe 'with python::version' do context 'python36' do @@ -240,6 +280,26 @@ it { is_expected.to contain_package('python-dev').with_name('python36-devel') } end end + + describe 'with manage_gunicorn' do + context 'true' do + let(:params) { { manage_gunicorn: true } } + + it { is_expected.to contain_package('gunicorn').with_name('python3-gunicorn') } + end + + context 'empty args' do + # let(:params) {{ :manage_gunicorn => '' }} + it { is_expected.to contain_package('gunicorn').with_name('python3-gunicorn') } + end + + context 'false' do + let(:params) { { manage_gunicorn: false } } + + it { is_expected.not_to contain_package('gunicorn').with_name('python3-gunicorn') } + end + end + describe 'with python::provider' do context 'scl' do describe 'with version' do @@ -248,12 +308,14 @@ it { is_expected.to compile.with_all_deps } end + context '3.6 SCL python package' do let(:params) { { version: 'rh-python36-python' } } it { is_expected.to compile.with_all_deps } end end + describe 'with manage_scl' do context 'true' do let(:params) { { provider: 'scl', manage_scl: true } } @@ -261,6 +323,7 @@ it { is_expected.to contain_package('centos-release-scl') } it { is_expected.to contain_package('scl-utils') } end + context 'false' do let(:params) { { provider: 'scl', manage_scl: false } } @@ -290,6 +353,7 @@ it { is_expected.to contain_package('python-dev').with_ensure('present') } end + context 'empty/default' do it { is_expected.to contain_package('python-dev').with_ensure('absent') } end @@ -301,10 +365,12 @@ it { is_expected.to contain_package('gunicorn') } end + context 'empty args' do # let(:params) {{ :manage_gunicorn => '' }} it { is_expected.to contain_package('gunicorn') } end + context 'false' do let(:params) { { manage_gunicorn: false } } @@ -317,8 +383,8 @@ let(:params) { { provider: 'pip' } } it { - is_expected.to contain_package('pip').with( - 'provider' => 'pip' + expect(subject).to contain_package('pip').with( + 'provider' => 'pip', ) } end @@ -337,6 +403,7 @@ it { is_expected.to contain_package('python-dev').with_ensure('present') } end + context 'default/empty' do it { is_expected.to contain_package('python-dev').with_ensure('absent') } end @@ -354,7 +421,7 @@ it { is_expected.to contain_class('python::install') } # Base debian packages. it { is_expected.to contain_package('python') } - it { is_expected.to contain_package('pip').with('category' => 'dev-python') } + it { is_expected.to contain_package('pip').with('name' => 'dev-python/pip') } # Python::Dev it { is_expected.not_to contain_package('python-dev') } @@ -364,10 +431,12 @@ it { is_expected.to contain_package('gunicorn') } end + context 'empty args' do # let(:params) {{ :manage_gunicorn => '' }} it { is_expected.to contain_package('gunicorn') } end + context 'false' do let(:params) { { manage_gunicorn: false } } @@ -387,3 +456,4 @@ end end end +# rubocop:enable RSpec/RepeatedExampleGroupDescription diff --git a/spec/defines/dotfile_spec.rb b/spec/defines/dotfile_spec.rb index d7d469e4..a44426f7 100644 --- a/spec/defines/dotfile_spec.rb +++ b/spec/defines/dotfile_spec.rb @@ -1,42 +1,59 @@ +# frozen_string_literal: true + require 'spec_helper' describe 'python::dotfile', type: :define do on_supported_os.each do |os, facts| - context("on #{os} ") do + context("on #{os}") do let :facts do facts end + let(:root_group) do + if facts[:os]['family'] == 'FreeBSD' + 'wheel' + else + 'root' + end + end + + let(:pre_condition) { 'include python' } + describe 'dotfile as' do context 'fails with empty string filename' do let(:title) { '' } it { is_expected.to raise_error(%r{Evaluation Error: Empty string title at 0. Title strings must have a length greater than zero.}) } end + context 'fails with incorrect mode' do let(:title) { '/etc/pip.conf' } let(:params) { { mode: 'not-a-mode' } } it { is_expected.to raise_error(%r{Evaluation Error: Error while evaluating a Resource}) } end + context 'succeeds with filename in existing path' do let(:title) { '/etc/pip.conf' } it { is_expected.to contain_file('/etc/pip.conf').with_mode('0644') } end + context 'succeeds with filename in a non-existing path' do let(:title) { '/home/someuser/.pip/pip.conf' } - it { is_expected.to contain_exec('create /home/someuser/.pip/pip.conf\'s parent dir').with_command('install -o root -g root -d /home/someuser/.pip') } + it { is_expected.to contain_exec('create /home/someuser/.pip/pip.conf\'s parent dir').with_command("install -o root -g #{root_group} -d /home/someuser/.pip") } it { is_expected.to contain_file('/home/someuser/.pip/pip.conf').with_mode('0644') } end + context 'succeeds when set owner' do let(:title) { '/home/someuser/.pip/pip.conf' } let(:params) { { owner: 'someuser' } } - it { is_expected.to contain_exec('create /home/someuser/.pip/pip.conf\'s parent dir').with_command('install -o someuser -g root -d /home/someuser/.pip') } + it { is_expected.to contain_exec('create /home/someuser/.pip/pip.conf\'s parent dir').with_command("install -o someuser -g #{root_group} -d /home/someuser/.pip") } it { is_expected.to contain_file('/home/someuser/.pip/pip.conf').with_owner('someuser') } end + context 'succeeds when set group set' do let(:title) { '/home/someuser/.pip/pip.conf' } let(:params) { { group: 'somegroup' } } diff --git a/spec/defines/gunicorn_spec.rb b/spec/defines/gunicorn_spec.rb index d2563054..de36c580 100644 --- a/spec/defines/gunicorn_spec.rb +++ b/spec/defines/gunicorn_spec.rb @@ -1,39 +1,46 @@ +# frozen_string_literal: true + require 'spec_helper' describe 'python::gunicorn', type: :define do let(:title) { 'test-app' } - context 'on Debian OS' do - let :facts do - { - id: 'root', - kernel: 'Linux', - lsbdistcodename: 'squeeze', - osfamily: 'Debian', - operatingsystem: 'Debian', - operatingsystemrelease: '6', - path: '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', - concat_basedir: '/dne' - } - end + on_supported_os.each do |os, facts| + context "on #{os}" do + let(:facts) { facts } - describe 'test-app with default parameter values' do - context 'configures test app with default parameter values' do - let(:params) { { dir: '/srv/testapp' } } + describe 'test-app with default parameter values' do + context 'configures test app with default parameter values' do + let(:params) { { dir: '/srv/testapp' } } + let(:expected_workers) do + # manifests/gunicorn.pp + processor_count = facts.dig(:processors, 'count') - it { is_expected.to contain_file('/etc/gunicorn.d/test-app').with_mode('0644').with_content(%r{--log-level=error}) } - end + # templates/gunicorn.erb + (processor_count.to_i * 2) + 1 + end - context 'test-app with custom log level' do - let(:params) { { dir: '/srv/testapp', log_level: 'info' } } + it { is_expected.to contain_file('/etc/gunicorn.d/test-app').with_mode('0644').with_content(%r{--log-level=error}) } + it { is_expected.to contain_file('/etc/gunicorn.d/test-app').with_mode('0644').with_content(%r{--workers=#{expected_workers}}) } + end - it { is_expected.to contain_file('/etc/gunicorn.d/test-app').with_mode('0644').with_content(%r{--log-level=info}) } - end + context 'test-app with custom log level' do + let(:params) { { dir: '/srv/testapp', log_level: 'info' } } + + it { is_expected.to contain_file('/etc/gunicorn.d/test-app').with_mode('0644').with_content(%r{--log-level=info}) } + end + + context 'test-app with custom gunicorn preload arguments' do + let(:params) { { dir: '/srv/testapp', args: ['--preload'] } } + + it { is_expected.to contain_file('/etc/gunicorn.d/test-app').with_mode('0644').with_content(%r{--preload}) } + end - context 'test-app with custom gunicorn preload arguments' do - let(:params) { { dir: '/srv/testapp', args: ['--preload'] } } + context 'test-app with custom workers count' do + let(:params) { { dir: '/srv/testapp', workers: 42 } } - it { is_expected.to contain_file('/etc/gunicorn.d/test-app').with_mode('0644').with_content(%r{--preload}) } + it { is_expected.to contain_file('/etc/gunicorn.d/test-app').with_mode('0644').with_content(%r{--workers=#{params[:workers]}}) } + end end end end diff --git a/spec/defines/pip_spec.rb b/spec/defines/pip_spec.rb index a51e916a..a00a7792 100644 --- a/spec/defines/pip_spec.rb +++ b/spec/defines/pip_spec.rb @@ -1,5 +1,8 @@ +# frozen_string_literal: true + require 'spec_helper' # rubocop:disable RSpec/MultipleDescribes +# rubocop:disable RSpec/RepeatedExampleGroupDescription describe 'python::pip', type: :define do let(:title) { 'rpyc' } @@ -17,7 +20,8 @@ operatingsystem: 'Debian', operatingsystemrelease: '6', path: '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', - concat_basedir: '/dne' + concat_basedir: '/dne', + pip_version: '18.1', } end @@ -25,13 +29,15 @@ context 'fails with non qualified path' do let(:params) { { virtualenv: 'venv' } } - it { is_expected.to raise_error(%r{expects a match for Variant\[Enum\['system'\].*Stdlib::Windowspath = Pattern\[\/.*\/\], Stdlib::Unixpath = Pattern\[\/.*\/\]\]}) } + it { is_expected.to raise_error(%r{expects a match for Variant\[Enum\['system'\].*Stdlib::Windowspath = Pattern\[/.*/\], Stdlib::Unixpath = Pattern\[/.*/\]\]}) } end + context 'suceeds with qualified path' do let(:params) { { virtualenv: '/opt/venv' } } it { is_expected.to contain_exec('pip_install_rpyc').with_cwd('/opt/venv') } end + context 'defaults to system' do let(:params) { {} } @@ -46,12 +52,14 @@ it { is_expected.to contain_exec('pip_install_rpyc').with_command(%r{pip}) } it { is_expected.not_to contain_exec('pip_install_rpyc').with_command(%r{pip3}) } end + context 'use pip instead of pip3 when specified' do let(:params) { { pip_provider: 'pip' } } it { is_expected.to contain_exec('pip_install_rpyc').with_command(%r{pip}) } it { is_expected.not_to contain_exec('pip_install_rpyc').with_command(%r{pip3}) } end + context 'use pip3 instead of pip when specified' do let(:params) { { pip_provider: 'pip3' } } @@ -65,10 +73,11 @@ it { is_expected.not_to contain_exec('pip_install_rpyc').with_command(%r{--proxy}) } end + context 'adds proxy to install command if proxy set' do let(:params) { { proxy: 'http://my.proxy:3128' } } - it { is_expected.to contain_exec('pip_install_rpyc').with_command('pip --log /tmp/pip.log install --proxy=http://my.proxy:3128 rpyc') } + it { is_expected.to contain_exec('pip_install_rpyc').with_command('pip install --proxy=http://my.proxy:3128 rpyc') } end end @@ -78,10 +87,25 @@ it { is_expected.not_to contain_exec('pip_install_rpyc').with_command(%r{--index-url}) } end + context 'adds index to install command if index set' do let(:params) { { index: 'http://www.example.com/simple/' } } - it { is_expected.to contain_exec('pip_install_rpyc').with_command('pip --log /tmp/pip.log install --index-url=http://www.example.com/simple/ rpyc') } + it { is_expected.to contain_exec('pip_install_rpyc').with_command('pip install --index-url=http://www.example.com/simple/ rpyc') } + end + end + + describe 'extra_index as' do + context 'defaults to empty' do + let(:params) { {} } + + it { is_expected.not_to contain_exec('pip_install_rpyc').with_command(%r{--extra-index-url}) } + end + + context 'adds extra_index to install command if extra_index set' do + let(:params) { { extra_index: 'http://www.example.com/extra/simple/' } } + + it { is_expected.to contain_exec('pip_install_rpyc').with_command('pip install --extra-index-url=http://www.example.com/extra/simple/ rpyc') } end end @@ -94,12 +118,27 @@ end end + describe 'install_args as' do + context 'adds install_args to install command if install_args set' do + let(:params) { { install_args: '--pre' } } + + it { is_expected.to contain_exec('pip_install_rpyc').with_command('pip install --pre rpyc') } + end + end + describe 'install latest' do + context 'does not use legacy resolver in unless' do + let(:params) { { ensure: 'latest' } } + + it { is_expected.not_to contain_exec('pip_install_rpyc').with_unless(%r{--use-deprecated=legacy-resolver}) } + end + context 'does not use pip search in unless' do let(:params) { { ensure: 'latest' } } it { is_expected.not_to contain_exec('pip_install_rpyc').with_unless(%r{search}) } end + context 'checks installed version of a package by converting underscores in its name to dashes' do let(:params) { { ensure: 'latest', pkgname: 'wordpress_json' } } @@ -108,6 +147,20 @@ end end + describe 'install specific version' do + context 'supports v-prefixed version string' do + let(:params) { { ensure: 'v1.7.0' } } + + it { is_expected.to contain_exec('pip_install_rpyc').with_command('pip install rpyc==v1.7.0') } + end + + context 'supports version string without v-prefix' do + let(:params) { { ensure: '1.7.0' } } + + it { is_expected.to contain_exec('pip_install_rpyc').with_command('pip install rpyc==1.7.0') } + end + end + describe 'uninstall' do context 'adds correct title' do let(:params) { { ensure: 'absent' } } @@ -126,9 +179,39 @@ end end end + + context 'on Debian OS with pip_version 20.3.4' do + let :facts do + { + id: 'root', + kernel: 'Linux', + lsbdistcodename: 'buster', + os: { + family: 'Debian', + release: { major: '10' }, + }, + osfamily: 'Debian', + operatingsystem: 'Debian', + operatingsystemrelease: '10.12', + path: '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', + concat_basedir: '/dne', + pip_version: '20.3.4', + } + end + + describe 'install latest' do + context 'with legacy resolver in unless cmd' do + let(:params) { { ensure: 'latest' } } + + it { is_expected.to compile.with_all_deps } + it { is_expected.to contain_exec('pip_install_rpyc').with_unless(%r{--use-deprecated=legacy-resolver}) } + end + end + end end describe 'python::pip', type: :define do + # rubocop:enable RSpec/RepeatedExampleGroupDescription let(:title) { 'requests' } context 'on Debian OS' do @@ -138,13 +221,13 @@ kernel: 'Linux', lsbdistcodename: 'squeeze', os: { - family: 'Debian' + family: 'Debian', }, osfamily: 'Debian', operatingsystem: 'Debian', operatingsystemrelease: '6', path: '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', - concat_basedir: '/dne' + concat_basedir: '/dne', } end @@ -152,12 +235,13 @@ context 'suceeds with no extras' do let(:params) { {} } - it { is_expected.to contain_exec('pip_install_requests').with_command('pip --log /tmp/pip.log install requests') } + it { is_expected.to contain_exec('pip_install_requests').with_command('pip install requests') } end + context 'succeeds with extras' do let(:params) { { extras: ['security'] } } - it { is_expected.to contain_exec('pip_install_requests').with_command('pip --log /tmp/pip.log install requests[security]') } + it { is_expected.to contain_exec('pip_install_requests').with_command('pip install requests[security]') } end end end diff --git a/spec/defines/pyvenv_spec.rb b/spec/defines/pyvenv_spec.rb index 36452839..6ce6646b 100644 --- a/spec/defines/pyvenv_spec.rb +++ b/spec/defines/pyvenv_spec.rb @@ -1,41 +1,100 @@ +# frozen_string_literal: true + require 'spec_helper' describe 'python::pyvenv', type: :define do on_supported_os.each do |os, facts| next if os == 'gentoo-3-x86_64' + + let :title do + '/opt/env' + end + context "on #{os}" do - let :facts do - # python3 is required to use pyvenv - facts.merge( - python3_version: '3.5.1' - ) + context 'with default parameters' do + let :facts do + facts + end + + it { is_expected.to compile } end - let :title do - '/opt/env' + + context 'with a specific python3 version' do + let :facts do + # python3 is required to use pyvenv + facts.merge( + python3_version: '3.5.1', + ) + end + + context 'with default parameters' do + it { is_expected.to contain_file('/opt/env').that_requires('Class[python::install]') } + it { is_expected.to contain_exec('python_virtualenv_/opt/env').with_command('pyvenv-3.5 --clear /opt/env && /opt/env/bin/pip install --upgrade pip && /opt/env/bin/pip install --upgrade setuptools') } + end + + describe 'when ensure' do + context 'is absent' do + let :params do + { + ensure: 'absent', + } + end + + it { + expect(subject).to contain_file('/opt/env').with_ensure('absent').with_purge(true) + } + end + end end - context 'with default parameters' do - it { is_expected.to contain_file('/opt/env') } - it { is_expected.to contain_exec('python_virtualenv_/opt/env').with_command('pyvenv-3.5 --clear /opt/env && /opt/env/bin/pip --log /opt/env/pip.log install --upgrade pip && /opt/env/bin/pip --log /opt/env/pip.log install --upgrade setuptools') } + context "prompt on #{os} with python 3.6" do + let :facts do + # python 3.6 is required for venv and prompt + facts.merge( + python3_version: '3.6.1', + ) + end + let :title do + '/opt/env' + end + + context 'with prompt' do + let :params do + { + prompt: 'custom prompt', + } + end - if %w[xenial bionic cosmic disco stretch buster].include?(facts[:lsbdistcodename]) - it { is_expected.to contain_package('python3.5-venv').that_comes_before('File[/opt/env]') } + it { + is_expected.to contain_file('/opt/env').that_requires('Class[python::install]') + is_expected.to contain_exec('python_virtualenv_/opt/env').with_command('python3.6 -m venv --clear --prompt custom\\ prompt /opt/env && /opt/env/bin/pip install --upgrade pip && /opt/env/bin/pip install --upgrade setuptools') + } end end - describe 'when ensure' do - context 'is absent' do + context "prompt on #{os} with python 3.5" do + let :facts do + facts.merge( + python3_version: '3.5.1', + ) + end + let :title do + '/opt/env' + end + + context 'with prompt' do let :params do { - ensure: 'absent' + prompt: 'custom prompt', } end it { - is_expected.to contain_file('/opt/env').with_ensure('absent').with_purge(true) + is_expected.to contain_file('/opt/env').that_requires('Class[python::install]') + is_expected.to contain_exec('python_virtualenv_/opt/env').with_command('pyvenv-3.5 --clear /opt/env && /opt/env/bin/pip install --upgrade pip && /opt/env/bin/pip install --upgrade setuptools') } end end - end # context + end end end diff --git a/spec/defines/requirements_spec.rb b/spec/defines/requirements_spec.rb index 17db0595..82dc0b37 100644 --- a/spec/defines/requirements_spec.rb +++ b/spec/defines/requirements_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe 'python::requirements', type: :define do @@ -14,7 +16,7 @@ context 'with /requirements.txt' do let :params do { - requirements: '/requirements.txt' + requirements: '/requirements.txt', } end @@ -33,7 +35,7 @@ let :params do { owner: 'bob', - group: 'bob' + group: 'bob', } end @@ -48,12 +50,17 @@ it { is_expected.to contain_class('python::params') } it { is_expected.to contain_class('python') } it { is_expected.to contain_exec('python_requirements/requirements.txt') } - it { is_expected.to contain_package('pip') } + + if facts[:os]['family'] == 'Archlinux' + it { is_expected.not_to contain_package('pip') } + else + it { is_expected.to contain_package('pip') } + end it { is_expected.to contain_package('python') } it { is_expected.to contain_package('gunicorn') } it { is_expected.to contain_file('/requirements.txt').with_owner('root').with_group('root') } - if facts[:os]['name'] == 'Gentoo' + if %w[Archlinux FreeBSD Gentoo].include?(facts[:os]['name']) it { is_expected.not_to contain_package('python-dev') } else it { is_expected.to contain_package('python-dev') } diff --git a/spec/spec.opts b/spec/spec.opts deleted file mode 100644 index 91cd6427..00000000 --- a/spec/spec.opts +++ /dev/null @@ -1,6 +0,0 @@ ---format -s ---colour ---loadby -mtime ---backtrace diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index d266f6b4..58c9b66a 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,18 +1,24 @@ -# This file is managed via modulesync -# https://github.com/voxpupuli/modulesync -# https://github.com/voxpupuli/modulesync_config +# frozen_string_literal: true + +# Managed by modulesync - DO NOT EDIT +# https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ # puppetlabs_spec_helper will set up coverage if the env variable is set. # We want to do this if lib exists and it hasn't been explicitly set. -ENV['COVERAGE'] ||= 'yes' if Dir.exist?(File.expand_path('../../lib', __FILE__)) +ENV['COVERAGE'] ||= 'yes' if Dir.exist?(File.expand_path('../lib', __dir__)) require 'voxpupuli/test/spec_helper' +RSpec.configure do |c| + c.facterdb_string_keys = false +end + +add_mocked_facts! + if File.exist?(File.join(__dir__, 'default_module_facts.yml')) facts = YAML.safe_load(File.read(File.join(__dir__, 'default_module_facts.yml'))) - if facts - facts.each do |name, value| - add_custom_fact name.to_sym, value - end + facts&.each do |name, value| + add_custom_fact name.to_sym, value end end +Dir['./spec/support/spec/**/*.rb'].sort.each { |f| require f } diff --git a/spec/spec_helper_acceptance.rb b/spec/spec_helper_acceptance.rb index bec34fdd..2681792e 100644 --- a/spec/spec_helper_acceptance.rb +++ b/spec/spec_helper_acceptance.rb @@ -1,6 +1,10 @@ -# This file is completely managed via modulesync +# frozen_string_literal: true + +# Managed by modulesync - DO NOT EDIT +# https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ + require 'voxpupuli/acceptance/spec_helper_acceptance' -configure_beaker +configure_beaker(modules: :metadata) Dir['./spec/support/acceptance/**/*.rb'].sort.each { |f| require f } diff --git a/spec/type_aliases/venv/pipversion_spec.rb b/spec/type_aliases/venv/pipversion_spec.rb index 986eeb1e..920cf061 100644 --- a/spec/type_aliases/venv/pipversion_spec.rb +++ b/spec/type_aliases/venv/pipversion_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe 'Python::Venv::PipVersion' do @@ -26,7 +28,7 @@ ].each do |value| describe value.inspect do it { - is_expected.to allow_value(value) + expect(subject).to allow_value(value) } end end @@ -50,7 +52,7 @@ ].each do |value| describe value.inspect do it { - is_expected.not_to allow_value(value) + expect(subject).not_to allow_value(value) } end end diff --git a/spec/unit/facter/pip_version_spec.rb b/spec/unit/facter/pip_version_spec.rb index 379323ed..3e6cd29f 100644 --- a/spec/unit/facter/pip_version_spec.rb +++ b/spec/unit/facter/pip_version_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Facter::Util::Fact do @@ -6,21 +8,21 @@ end let(:pip_version_output) do - <<-EOS -pip 6.0.6 from /opt/boxen/homebrew/Cellar/python/2.7.9/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/pip-6.0.6-py2.7.egg (python 2.7) -EOS + <<~EOS + pip 6.0.6 from /opt/boxen/homebrew/Cellar/python/2.7.9/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/pip-6.0.6-py2.7.egg (python 2.7) + EOS end let(:pip2_version_output) do - <<-EOS -pip 9.0.1 from /usr/lib/python2.7/dist-packages/pip (python 2.7) -EOS + <<~EOS + pip 9.0.1 from /usr/lib/python2.7/dist-packages/pip (python 2.7) + EOS end let(:pip3_version_output) do - <<-EOS -pip 18.1 from /usr/lib/python3/dist-packages/pip (python 3.7) -EOS + <<~EOS + pip 18.1 from /usr/lib/python3/dist-packages/pip (python 3.7) + EOS end describe 'pip_version' do @@ -35,7 +37,7 @@ context 'returns nil when pip not present' do it do allow(Facter::Util::Resolution).to receive(:which).with('pip').and_return(false) - expect(Facter.value(:pip_version)).to eq(nil) + expect(Facter.value(:pip_version)).to be_nil end end end @@ -52,7 +54,7 @@ context 'returns nil when pip2 not present' do it do allow(Facter::Util::Resolution).to receive(:which).with('pip2').and_return(false) - expect(Facter.value(:pip2_version)).to eq(nil) + expect(Facter.value(:pip2_version)).to be_nil end end end @@ -69,7 +71,7 @@ context 'returns nil when pip3 not present' do it do allow(Facter::Util::Resolution).to receive(:which).with('pip3').and_return(false) - expect(Facter.value(:pip3_version)).to eq(nil) + expect(Facter.value(:pip3_version)).to be_nil end end end diff --git a/spec/unit/facter/python_release_spec.rb b/spec/unit/facter/python_release_spec.rb index e6870b9f..6026c1e3 100644 --- a/spec/unit/facter/python_release_spec.rb +++ b/spec/unit/facter/python_release_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Facter::Util::Fact do @@ -6,14 +8,14 @@ end let(:python2_version_output) do - <<-EOS -Python 2.7.9 -EOS + <<~EOS + Python 2.7.9 + EOS end let(:python3_version_output) do - <<-EOS -Python 3.3.0 -EOS + <<~EOS + Python 3.3.0 + EOS end describe 'python_release' do @@ -29,7 +31,7 @@ it do allow(Facter::Util::Resolution).to receive(:exec).and_return(false) allow(Facter::Util::Resolution).to receive(:which).with('python').and_return(false) - expect(Facter.value(:python_release)).to eq(nil) + expect(Facter.value(:python_release)).to be_nil end end end @@ -58,7 +60,7 @@ allow(Facter::Util::Resolution).to receive(:which).with('python').and_return(true) allow(Facter::Util::Resolution).to receive(:exec).with('python -V 2>&1').and_return(python3_version_output) allow(Facter::Util::Resolution).to receive(:which).with('python2').and_return(false) - expect(Facter.value(:python2_release)).to eq(nil) + expect(Facter.value(:python2_release)).to be_nil end end @@ -66,7 +68,7 @@ it do allow(Facter::Util::Resolution).to receive(:which).with('python').and_return(false) allow(Facter::Util::Resolution).to receive(:which).with('python2').and_return(false) - expect(Facter.value(:python2_release)).to eq(nil) + expect(Facter.value(:python2_release)).to be_nil end end end @@ -83,7 +85,7 @@ context 'returns nil when `python3` not present' do it do allow(Facter::Util::Resolution).to receive(:which).with('python3').and_return(false) - expect(Facter.value(:python3_release)).to eq(nil) + expect(Facter.value(:python3_release)).to be_nil end end end diff --git a/spec/unit/facter/python_version_spec.rb b/spec/unit/facter/python_version_spec.rb index db302b27..d66d280b 100644 --- a/spec/unit/facter/python_version_spec.rb +++ b/spec/unit/facter/python_version_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Facter::Util::Fact do @@ -6,14 +8,14 @@ end let(:python2_version_output) do - <<-EOS -Python 2.7.9 -EOS + <<~EOS + Python 2.7.9 + EOS end let(:python3_version_output) do - <<-EOS -Python 3.3.0 -EOS + <<~EOS + Python 3.3.0 + EOS end describe 'python_version' do @@ -28,7 +30,7 @@ context 'returns nil when `python` not present' do it do allow(Facter::Util::Resolution).to receive(:which).with('python').and_return(false) - expect(Facter.value(:python_version)).to eq(nil) + expect(Facter.value(:python_version)).to be_nil end end end @@ -57,7 +59,7 @@ allow(Facter::Util::Resolution).to receive(:which).with('python').and_return(true) allow(Facter::Util::Resolution).to receive(:exec).with('python -V 2>&1').and_return(python3_version_output) allow(Facter::Util::Resolution).to receive(:which).with('python2').and_return(false) - expect(Facter.value(:python2_version)).to eq(nil) + expect(Facter.value(:python2_version)).to be_nil end end @@ -65,7 +67,7 @@ it do allow(Facter::Util::Resolution).to receive(:which).with('python2').and_return(false) allow(Facter::Util::Resolution).to receive(:which).with('python').and_return(false) - expect(Facter.value(:python2_version)).to eq(nil) + expect(Facter.value(:python2_version)).to be_nil end end end @@ -82,7 +84,7 @@ context 'returns nil when `python3` not present' do it do allow(Facter::Util::Resolution).to receive(:which).with('python3').and_return(false) - expect(Facter.value(:python3_version)).to eq(nil) + expect(Facter.value(:python3_version)).to be_nil end end end diff --git a/spec/unit/facter/virtualenv_version_spec.rb b/spec/unit/facter/virtualenv_version_spec.rb deleted file mode 100644 index 3cfd4c99..00000000 --- a/spec/unit/facter/virtualenv_version_spec.rb +++ /dev/null @@ -1,46 +0,0 @@ -require 'spec_helper' - -describe Facter::Util::Fact do - before do - Facter.clear - end - - let(:virtualenv_old_version_output) do - <<-EOS -12.0.7 -EOS - end - - let(:virtualenv_new_version_output) do - <<-EOS -virtualenv 20.0.17 from /opt/python/lib/python3.5/site-packages/virtualenv/__init__.py -EOS - end - - describe 'virtualenv_version old' do - context 'returns virtualenv version when virtualenv present' do - it do - allow(Facter::Util::Resolution).to receive(:which).with('virtualenv').and_return(true) - allow(Facter::Util::Resolution).to receive(:exec).with('virtualenv --version 2>&1').and_return(virtualenv_old_version_output) - expect(Facter.value(:virtualenv_version)).to eq('12.0.7') - end - end - - context 'returns nil when virtualenv not present' do - it do - allow(Facter::Util::Resolution).to receive(:which).with('virtualenv').and_return(false) - expect(Facter.value(:virtualenv_version)).to eq(nil) - end - end - end - - describe 'virtualenv_version new' do - context 'returns virtualenv version when virtualenv present' do - it do - allow(Facter::Util::Resolution).to receive(:which).with('virtualenv').and_return(true) - allow(Facter::Util::Resolution).to receive(:exec).with('virtualenv --version 2>&1').and_return(virtualenv_new_version_output) - expect(Facter.value(:virtualenv_version)).to eq('20.0.17') - end - end - end -end diff --git a/templates/gunicorn.erb b/templates/gunicorn.erb index 07605ade..57dd4748 100644 --- a/templates/gunicorn.erb +++ b/templates/gunicorn.erb @@ -39,7 +39,7 @@ CONFIG = { <% if @workers -%> '--workers=<%= @workers %>', <% else -%> - '--workers=<%= @processorcount.to_i*2 + 1 %>', + '--workers=<%= @processor_count.to_i*2 + 1 %>', <% end -%> '--timeout=<%= @timeout %>', <% if @access_log_format -%> diff --git a/types/loglevel.pp b/types/loglevel.pp index 3cdb653d..849c2236 100644 --- a/types/loglevel.pp +++ b/types/loglevel.pp @@ -1,3 +1,3 @@ -# @summary A version type to match all valid loglevels for python +# @summary Match all valid loglevels for python # type Python::Loglevel = Enum['debug', 'info', 'warning', 'error', 'critical'] diff --git a/types/package/ensure.pp b/types/package/ensure.pp index 1eb13b82..9fa54968 100644 --- a/types/package/ensure.pp +++ b/types/package/ensure.pp @@ -1,3 +1,3 @@ -# @summary A version type to match all valid package ensures for python +# @summary Match all valid package ensures for python # -type Python::Package::Ensure = Enum['absent', 'present', 'latest'] +type Python::Package::Ensure = Enum['absent', 'present', 'installed', 'latest'] diff --git a/types/provider.pp b/types/provider.pp index 8c7dfd65..68fd7ff6 100644 --- a/types/provider.pp +++ b/types/provider.pp @@ -1,3 +1,3 @@ -# @summary A version type to match all valid provider for python +# @summary Match all valid provider for python # type Python::Provider = Enum['pip', 'scl', 'rhscl', 'anaconda', ''] diff --git a/types/umask.pp b/types/umask.pp index 11296cd0..39038093 100644 --- a/types/umask.pp +++ b/types/umask.pp @@ -1,3 +1,3 @@ -# @summary A version type to match valid umask for python +# @summary Match valid umask for python # type Python::Umask = Pattern[/[0-7]{1,4}/] diff --git a/types/version.pp b/types/version.pp index e0023aef..5bbcaaa8 100644 --- a/types/version.pp +++ b/types/version.pp @@ -1,4 +1,4 @@ -# @summary A version type to match all valid versions for python +# @summary Match all valid versions for python # type Python::Version = Pattern[ /\A(python)?[0-9](\.?[0-9])*/,