diff --git a/.fixtures.yml b/.fixtures.yml
index adba068..f9baf1c 100644
--- a/.fixtures.yml
+++ b/.fixtures.yml
@@ -1,12 +1,11 @@
fixtures:
repositories:
epel: 'https://github.com/voxpupuli/puppet-epel.git'
inifile: 'https://github.com/puppetlabs/puppetlabs-inifile.git'
stdlib: 'https://github.com/puppetlabs/puppetlabs-stdlib.git'
- vcsrepo: 'https://github.com/puppetlabs/puppetlabs-vcsrepo.git'
yumrepo_core:
repo: https://github.com/puppetlabs/puppetlabs-yumrepo_core.git
puppet_version: ">= 6.0.0"
cron_core:
repo: https://github.com/puppetlabs/puppetlabs-cron_core.git
puppet_version: ">= 6.0.0"
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index cace33e..887d571 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -1,283 +1,252 @@
# 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
-bundle exec rake acceptance
-```
-
-This will run the tests on the module's default nodeset. You can override the
-nodeset used, e.g.,
-
-```sh
-BEAKER_set=centos-7-x64 bundle exec rake acceptance
-```
-
-There are default rake tasks for the various acceptance test modules, e.g.,
-
-```sh
-bundle exec rake beaker:centos-7-x64
-bundle exec rake beaker:ssh:centos-7-x64
-```
-
-If you don't want to have to recreate the virtual machine every time you can
-use `BEAKER_destroy=no` and `BEAKER_provision=no`. On the first run you will at
-least need `BEAKER_provision` set to yes (the default). The Vagrantfile for the
-created virtual machines will be in `.vagrant/beaker_vagrant_files`.
-
-Beaker also supports docker containers. We also use that in our automated CI
-pipeline at [travis-ci](http://travis-ci.org). To use that instead of Vagrant:
-
-```sh
-PUPPET_INSTALL_TYPE=agent BEAKER_IS_PE=no BEAKER_PUPPET_COLLECTION=puppet6 BEAKER_debug=true BEAKER_setfile=debian10-64{hypervisor=docker} BEAKER_destroy=yes bundle exec rake beaker
+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
-* centos6
* centos7
* centos8
-The easiest way to debug in a docker container is to open a shell:
-
-```sh
-docker exec -it -u root ${container_id_or_name} bash
-```
+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/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..97ddc62
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,75 @@
+name: CI
+
+on: pull_request
+
+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
+
+ 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
+
+ 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 }}
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..68b8528
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,29 @@
+name: Release
+
+on:
+ push:
+ tags:
+ - '*'
+
+env:
+ BUNDLE_WITHOUT: development:test:system_tests
+
+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
diff --git a/.msync.yml b/.msync.yml
index 5758ace..57ff503 100644
--- a/.msync.yml
+++ b/.msync.yml
@@ -1,2 +1,2 @@
---
-modulesync_config_version: '3.1.0'
+modulesync_config_version: '4.1.0'
diff --git a/.pmtignore b/.pmtignore
index 4e6d54b..33a8c65 100644
--- a/.pmtignore
+++ b/.pmtignore
@@ -1,21 +1,34 @@
docs/
pkg/
+Gemfile
Gemfile.lock
Gemfile.local
vendor/
.vendor/
-spec/fixtures/manifests/
-spec/fixtures/modules/
+spec/
+Rakefile
.vagrant/
.bundle/
.ruby-version
coverage/
log/
.idea/
.dependencies/
+.github/
.librarian/
Puppetfile.lock
*.iml
+.editorconfig
+.fixtures.yml
+.gitignore
+.msync.yml
+.overcommit.yml
+.pmtignore
+.rspec
+.rspec_parallel
+.rubocop.yml
+.sync.yml
.*.sw?
.yardoc/
+.yardopts
Dockerfile
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index ca73a2d..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,105 +0,0 @@
----
-# yamllint disable rule:line-length rule:truthy
-os: linux
-dist: focal
-language: ruby
-cache: bundler
-before_install:
- - yes | gem update --system
- - bundle --version
-script:
- - 'bundle exec rake $CHECK'
-jobs:
- fast_finish: true
- include:
- - rvm: 2.4.4
- bundler_args: --without system_tests development release
- env: PUPPET_VERSION="~> 5.0" CHECK=test
- - rvm: 2.5.3
- bundler_args: --without system_tests development release
- env: PUPPET_VERSION="~> 6.0" CHECK=test_with_coveralls
- - rvm: 2.5.3
- bundler_args: --without system_tests development release
- env: PUPPET_VERSION="~> 6.0" CHECK=rubocop
- - rvm: 2.4.4
- bundler_args: --without system_tests development release
- env: PUPPET_VERSION="~> 5.0" CHECK=build DEPLOY_TO_FORGE=yes
- - rvm: 2.5.3
- bundler_args: --without development release
- env: BEAKER_PUPPET_COLLECTION=puppet5 BEAKER_setfile=centos7-64 CHECK=beaker
- services: docker
- - rvm: 2.5.3
- bundler_args: --without development release
- env: BEAKER_PUPPET_COLLECTION=puppet6 BEAKER_setfile=centos7-64 CHECK=beaker
- services: docker
- - rvm: 2.5.3
- bundler_args: --without development release
- env: BEAKER_PUPPET_COLLECTION=puppet5 BEAKER_setfile=debian9-64 CHECK=beaker
- services: docker
- - rvm: 2.5.3
- bundler_args: --without development release
- env: BEAKER_PUPPET_COLLECTION=puppet6 BEAKER_setfile=debian9-64 CHECK=beaker
- services: docker
- - rvm: 2.5.3
- bundler_args: --without development release
- env: BEAKER_PUPPET_COLLECTION=puppet5 BEAKER_setfile=debian10-64 CHECK=beaker
- services: docker
- - rvm: 2.5.3
- bundler_args: --without development release
- env: BEAKER_PUPPET_COLLECTION=puppet6 BEAKER_setfile=debian10-64 CHECK=beaker
- services: docker
- - rvm: 2.5.3
- bundler_args: --without development release
- env: BEAKER_PUPPET_COLLECTION=puppet5 BEAKER_setfile=fedora30-64 CHECK=beaker
- services: docker
- - rvm: 2.5.3
- bundler_args: --without development release
- env: BEAKER_PUPPET_COLLECTION=puppet6 BEAKER_setfile=fedora30-64 CHECK=beaker
- services: docker
- - rvm: 2.5.3
- bundler_args: --without development release
- env: BEAKER_PUPPET_COLLECTION=puppet5 BEAKER_setfile=fedora31-64 CHECK=beaker
- services: docker
- - rvm: 2.5.3
- bundler_args: --without development release
- env: BEAKER_PUPPET_COLLECTION=puppet6 BEAKER_setfile=fedora31-64 CHECK=beaker
- services: docker
- - rvm: 2.5.3
- bundler_args: --without development release
- env: BEAKER_PUPPET_COLLECTION=puppet5 BEAKER_setfile=ubuntu1604-64 CHECK=beaker
- services: docker
- - rvm: 2.5.3
- bundler_args: --without development release
- env: BEAKER_PUPPET_COLLECTION=puppet6 BEAKER_setfile=ubuntu1604-64 CHECK=beaker
- services: docker
- - rvm: 2.5.3
- bundler_args: --without development release
- env: BEAKER_PUPPET_COLLECTION=puppet5 BEAKER_setfile=ubuntu1804-64 CHECK=beaker
- services: docker
- - rvm: 2.5.3
- bundler_args: --without development release
- env: BEAKER_PUPPET_COLLECTION=puppet6 BEAKER_setfile=ubuntu1804-64 CHECK=beaker
- services: docker
-branches:
- only:
- - master
- - /^v\d/
-notifications:
- email: false
- webhooks: https://voxpupu.li/incoming/travis
- irc:
- on_success: always
- on_failure: always
- channels:
- - "chat.freenode.org#voxpupuli-notifications"
-deploy:
- provider: puppetforge
- username: puppet
- password:
- secure: "n/CVUS2upgv5DifCm/YsjBxR/11bdTRDYi1x9QK6ILa32+6ngVV2RQaIMXXJfJKYIPT8/O21tpc9C7fOBRGhRpNbl0usfvqsZS0C9UkpEx6AqT7lcRzj6pLrNn3IuChhZ0tQjNiKp9LxVTzltxr5uTFwKKCE4o534v/DLAzkzq5EAZuBWpRS1rcVHQA3o0767Gu3601yyYkZj9ySDH5RpbSdTCcNkTzwtFhr2NEvVb+2FI0RhchDSqPBfNWHV4Hn3dKuL42MNC2zjd2FYFpC8F27OXk/erUZIOZFfpZuIWypjSimfVC95a2Nb8kfQotTvQxUI1fwiB01ibUQGGkJj+mh7Utg/byBrbijpJnWRR7TT6oQ1NbIUHVXcqE1tfpbCBZ4Ws2Hqji0QoGc0fMrkt1NVlZlgbVrb9t+ctb1QcLaEPI+1Zf2a3AZhXOKA1EGx2W5DTQSWSPv57BUtFPICZENQi/ats30h+0FwtN7rjfx8Q6BIGO2D5JODI6eJC0nLNaL5UaPA0pjGRsNlZWoUzieuKG08G/rQtj/8jLq/3eLICv1cbfvj7lDPc0thPwXdrPIC9nLSisb/wdLufpqXsFku9TeBqrRHfQWImybcv7JRAeZZeHEo+tvFvZg5MSAEiPmz8zIOJOKhuMKEXlOmALmSjEhW6Ca5r7xuQ5Qwm0="
- on:
- tags: true
- # all_branches is required to use tags
- all_branches: true
- # Only publish the build marked with "DEPLOY_TO_FORGE"
- condition: "$DEPLOY_TO_FORGE = yes"
diff --git a/Dockerfile b/Dockerfile
index 6fd6342..a51c641 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,21 +1,21 @@
-FROM ruby:2.5.3
+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 2fce93c..f4855f6 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,48 +1,33 @@
source ENV['GEM_SOURCE'] || "https://rubygems.org"
-def location_for(place, fake_version = nil)
- if place =~ /^(git[:@][^#]*)#(.*)/
- [fake_version, { :git => $1, :branch => $2, :require => false }].compact
- elsif place =~ /^file:\/\/(.*)/
- ['>= 0', { :path => File.expand_path($1), :require => false }]
- else
- [place, { :require => false }]
- end
-end
-
group :test do
gem 'voxpupuli-test', '~> 2.1', :require => false
gem 'coveralls', :require => false
gem 'simplecov-console', :require => false
end
group :development do
- gem 'travis', :require => false
- gem 'travis-lint', :require => false
gem 'guard-rake', :require => false
gem 'overcommit', '>= 0.39.1', :require => false
end
group :system_tests do
- gem 'voxpupuli-acceptance', :require => false
+ gem 'puppet_metadata', '~> 0.3.0', :require => false
+ gem 'voxpupuli-acceptance', :require => false
end
group :release do
- gem 'github_changelog_generator', :require => false, :git => 'https://github.com/voxpupuli/github-changelog-generator', :branch => 'voxpupuli_essential_fixes'
- gem 'puppet-blacksmith', :require => false
- gem 'voxpupuli-release', :require => false
- gem 'puppet-strings', '>= 2.2', :require => false
+ 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
end
+gem 'puppetlabs_spec_helper', '~> 2.0', :require => false
+gem 'rake', :require => false
+gem 'facter', ENV['FACTER_GEM_VERSION'], :require => false, :groups => [:test]
-
-if facterversion = ENV['FACTER_GEM_VERSION']
- gem 'facter', facterversion.to_s, :require => false, :groups => [:test]
-else
- gem 'facter', :require => false, :groups => [:test]
-end
-
-ENV['PUPPET_VERSION'].nil? ? puppetversion = '~> 6.0' : puppetversion = ENV['PUPPET_VERSION'].to_s
+puppetversion = ENV['PUPPET_VERSION'] || '~> 6.0'
gem 'puppet', puppetversion, :require => false, :groups => [:test]
# vim: syntax=ruby
diff --git a/README.md b/README.md
index dbc93d4..7a841d5 100644
--- a/README.md
+++ b/README.md
@@ -1,365 +1,365 @@
# Let's Encrypt
[](https://travis-ci.org/voxpupuli/puppet-letsencrypt)
[](https://forge.puppetlabs.com/puppet/letsencrypt)
[](https://forge.puppetlabs.com/puppet/letsencrypt)
[](https://forge.puppetlabs.com/puppet/letsencrypt)
[](https://forge.puppetlabs.com/puppet/letsencrypt)
[](http://voxpupuli.github.io/puppet-letsencrypt)
This module installs the Let's Encrypt client from source and allows you to request certificates.
## Support
This module is currently only written to work on Debian and RedHat based
operating systems, although it may work on others. The supported Puppet
versions are defined in the [metadata.json](metadata.json)
## Dependencies
On EL (Red Hat, CentOS etc.) systems, the EPEL repository needs to be enabled
for the Let's Encrypt client package.
The module can integrate with [puppet/epel](https://forge.puppetlabs.com/puppet/epel)
to set up the repo by setting the `configure_epel` parameter to `true` (the default for RedHat) and
installing the module.
## Usage
### Setting up the Let's Encrypt client
To install the Let's Encrypt client with the default configuration settings you
must provide your email address to register with the Let's Encrypt servers:
```puppet
class { 'letsencrypt':
email => 'foo@example.com',
}
```
-If using Ubuntu16.04 with `install_method` to default `package`, you can enforce upgrade of package from 0.4 to 0.7 with :
+You can enforce upgrade of package to the latest available version (in your repositories):
```puppet
class { 'letsencrypt':
email => 'foo@example.com',
package_ensure => 'latest',
}
```
If using EL7 without EPEL-preconfigured, add `configure_epel`:
```puppet
class { 'letsencrypt':
configure_epel => true,
email => 'foo@example.com',
}
```
(If you manage epel some other way, disable it with `configure_epel => false`.)
This will install the Let's Encrypt client and its dependencies, agree to the
Terms of Service, initialize the client, and install a configuration file for
the client.
Alternatively, you can specify your email address in the $config hash:
```puppet
class { 'letsencrypt':
config => {
email => 'foo@example.com',
server => 'https://acme-v01.api.letsencrypt.org/directory',
}
}
```
During testing, you probably want to direct to the staging server instead with
`server => 'https://acme-staging.api.letsencrypt.org/directory'`
If you don't wish to provide your email address, you can set the
`unsafe_registration` parameter to `true` (this is not recommended):
```puppet
class { 'letsencrypt':
unsafe_registration => true,
}
```
To request a wildcard certificate, you must use the ACME v2 endpoint and use
a DNS-01 challenge. See
https://community.letsencrypt.org/t/acme-v2-production-environment-wildcards/55578
```puppet
class { 'letsencrypt':
config => {
email => 'foo@example.com',
server => 'https://acme-v02.api.letsencrypt.org/directory',
}
}
```
### Issuing certificates
#### Standalone authenticator
To request a certificate for `foo.example.com` using the `certonly` installer
and the `standalone` authenticator:
```puppet
letsencrypt::certonly { 'foo.example.com': }
```
#### Apache authenticator
To request a certificate for `foo.example.com` and `bar.example.com` with the
`certonly` installer and the `apache` authenticator:
```puppet
letsencrypt::certonly { 'foo':
domains => ['foo.example.com', 'bar.example.com'],
plugin => 'apache',
}
```
#### Webroot plugin
To request a certificate using the `webroot` plugin, the paths to the webroots
for all domains must be given through `webroot_paths`. If `domains` and
`webroot_paths` are not the same length, the last `webroot_paths` element will
be used for all subsequent domains.
```puppet
letsencrypt::certonly { 'foo':
domains => ['foo.example.com', 'bar.example.com'],
plugin => 'webroot',
webroot_paths => ['/var/www/foo', '/var/www/bar'],
}
```
#### dns-rfc2136 plugin
To request a certificate using the `dns-rfc2136` plugin, you will at a minimum
need to pass `server`, `key_name` and `key_secret` to the class
`letsencrypt::plugin::dns_rfc2136`. Ideally the key secret should be encrypted,
eg. with eyaml if using Hiera. It's also recommended to only enable access to
the specific DNS records needed by the Let's Encrypt client.
Plugin documentation and it's parameters can be found here:
https://certbot-dns-rfc2136.readthedocs.io
Parameter defaults:
- `key_algorithm` HMAC-SHA512
- `port` 53
- `propagation_seconds` 10 (the plugin defaults to 60)
Example:
```puppet
class { 'letsencrypt::plugin::dns_rfc2136':
server => '192.0.2.1',
key_name => 'certbot',
key_secret => '[...]==',
}
letsencrypt::certonly { 'foo':
domains => ['foo.example.com', 'bar.example.com'],
plugin => 'dns-rfc2136',
}
```
#### Additional arguments
If you need to pass a command line flag to the `letsencrypt-auto` command that
is not supported natively by this module, you can use the `additional_args`
parameter to pass those arguments:
```puppet
letsencrypt::certonly { 'foo':
domains => ['foo.example.com', 'bar.example.com'],
plugin => 'apache',
additional_args => ['--foo bar', '--baz quuz'],
}
```
### Renewing certificates
There are two ways to automatically renew certificates with cron using this module.
#### cron using certbot renew
All installed certificates will be renewed using `certbot renew` using their
original settings, including any not managed by Puppet.
* `renew_cron_ensure` manages the cron resource. Set to `present` to enable. Default: `absent`
* `renew_cron_minute` sets minute(s) to run the cron job. Default: Seeded random minute
* `renew_cron_hour` sets hour(s) to run the cron job. Default: Seeded random hour
* `renew_cron_monthday` sets month day(s) to run the cron job. Default: Every day
```puppet
class { 'letsencrypt':
config => {
email => 'foo@example.com',
server => 'https://acme-v01.api.letsencrypt.org/directory',
},
renew_cron_ensure => 'present',
}
```
With Hiera, at 6 AM (roughly) every other day:
```yaml
---
letsencrypt::renew_cron_ensure: 'present'
letsencrypt::renew_cron_minute: 0
letsencrypt::renew_cron_hour: 6
letsencrypt::renew_cron_monthday: '1-31/2'
```
#### cron using certbot certonly
Only specific certificates will be renewed using `certbot certonly`.
* `manage_cron` can be used to automatically renew the certificate
* `cron_success_command` can be used to run a shell command on a successful renewal
* `cron_before_command` can be used to run a shell command before a renewal
* `cron_monthday` can be used to specify one or multiple days of the month to run the cron job (defaults to every day)
* `cron_hour` can be used to specify hour(s) to run the cron job (defaults to a seeded random hour)
* `cron_minute` can be used to specify minute(s) to run the cron job (defaults to a seeded random minute)
* `suppress_cron_output` can be used to disable output (and resulting emails) generated by the cron command
```puppet
letsencrypt::certonly { 'foo':
domains => ['foo.example.com', 'bar.example.com'],
manage_cron => true,
cron_hour => [0,12],
cron_minute => '30',
cron_before_command => 'service nginx stop',
cron_success_command => '/bin/systemctl reload nginx.service',
suppress_cron_output => true,
}
```
#### Deprovisioning
If a domain needs to be removed for any reason this can be done by setting
`ensure` to 'absent', this will remove the certificates for this domain from
the server. If `manage_cron` is set to true, the certificate renewal cronjob
and shell scripts for the domain will also be removed.
```puppet
letsencrypt::certonly { 'foo':
ensure => 'absent',
domains => ['foo.example.com', 'bar.example.com'],
manage_cron => true,
}
```
## Hooks
Certbot supports hooks since certbot v0.5.0, however this module uses the newer
`--deploy-hook` replacing the deprecated `--renew-hook`. Because of this the
minimum version you will need to manage hooks with this module is v0.17.0.
All hook command parameters support both string and array.
**Note on certbot hook behavior:** Hooks created by `letsencrypt::certonly` will be
configured in the renewal config file of the certificate by certbot (stored in
CONFIG_DIR/renewal/), which means all hooks created this way are used when running
`certbot renew` without hook arguments. This allows you to easily create individual
hooks for each certificate with just one cron job for renewal. HOWEVER, when running
`certbot renew` with any of the hook arguments (setting any of the
`letsencrypt::renew_*_hook_commands` parameters), hooks of the corresponding
types in all renewal configs will be ignored by certbot. It's recommended to keep
these two ways of using hooks mutually exclusive to avoid confusion. Cron jobs
created by `letsencrypt::certonly` are unaffected as they renew certificates
directly using `certbot certonly`.
### certbot certonly
Hooks created with `letsencrypt::certonly` will behave the following way:
* `pre` hooks will be run before each certificate is attempted issued or renewed,
even if the action fails.
* `post` hooks will be run after each certificate is attempted issued or renewed,
even if the action fails.
* `deploy` hooks will be run after successfully issuing or renewing each certificate.
It will not be run if no action is taken or if the action fails.
```puppet
letsencrypt::certonly { 'foo':
domains => ['foo.example.com', 'bar.example.com'],
pre_hook_commands => ['...'],
post_hook_commands => ['...'],
deploy_hook_commands => ['...'],
}
```
### certbot renew
Hooks passed to `certbot renew` will behave the following way:
* `pre` hook will be run once total before any certificates are attempted issued
or renewed. It will not be run if no actions are taken. Overrides all pre hooks
created by `letsencrypt::certonly`.
* `post` hook will be run once total after all certificates are issued or renewed.
It will not be run if no actions are taken. Overrides all post hooks created by
`letsencrypt::certonly`.
* `deploy` hook will be run once for each successfully issued or renewed certificate.
It will not be run otherwise. Overrides all deploy hooks created by
`letsencrypt::certonly`.
```puppet
class { 'letsencrypt':
config => {
email => 'foo@example.com',
server => 'https://acme-v01.api.letsencrypt.org/directory',
},
renew_pre_hook_commands: [...],
renew_post_hook_commands: [...],
renew_deploy_hook_commands: [...],
}
```
With Hiera:
```yaml
---
letsencrypt::renew_pre_hook_commands:
- '...'
letsencrypt::renew_post_hook_commands:
- '...'
letsencrypt::renew_deploy_hook_commands:
- '...'
```
## Facts
Facts about your live certificates are available through facter. You can query the list of live certificates from puppet using `$::letsencrypt_directory` in your puppet code, hiera data or from the command line.
```
facter -p letsencrypt_directory
{
legacyfiles.ijc.org => "/etc/letsencrypt/live/legacyfiles.ijc.org",
static.ijc.org => "/etc/letsencrypt/live/static.ijc.org",
ijc.org => "/etc/letsencrypt/live/ijc.org",
new.ijc.org => "/etc/letsencrypt/live/new.ijc.org",
www.ijc.org => "/etc/letsencrypt/live/ijc.org",
training.ijc.org => "/etc/letsencrypt/live/training.ijc.org"
}
```
## Puppet Functions
This module profiles a custom puppet function `letsencrypt::letsencrypt_lookup` which allows you to load information about your certificates into puppet.
This returns the same information as in the facts but for a particular domain. It accepts a single argument for your domain or wildcard domain.
## Development
1. Fork it
2. Create a feature branch
3. Write a failing test
4. Write the code to make that test pass
5. Refactor the code
6. Submit a pull request
We politely request (demand) tests for all new features. Pull requests that contain new features without a test will not be considered. If you need help, just ask!
diff --git a/REFERENCE.md b/REFERENCE.md
index c3459d5..2bb4533 100644
--- a/REFERENCE.md
+++ b/REFERENCE.md
@@ -1,811 +1,861 @@
# Reference
+
## Table of Contents
-**Classes**
+### Classes
-_Public Classes_
+#### Public Classes
* [`letsencrypt`](#letsencrypt): Install and configure Certbot, the LetsEncrypt client
* [`letsencrypt::install`](#letsencryptinstall): Installs the Let's Encrypt client.
* [`letsencrypt::plugin::dns_rfc2136`](#letsencryptplugindns_rfc2136): Installs and configures the dns-rfc2136 plugin
+* [`letsencrypt::plugin::dns_route53`](#letsencryptplugindns_route53): Installs and configures the dns-route53 plugin
* [`letsencrypt::renew`](#letsencryptrenew): Configures renewal of Let's Encrypt certificates using Certbot
-_Private Classes_
+#### Private Classes
* `letsencrypt::config`: Configures the Let's Encrypt client.
-**Defined types**
+### Defined types
* [`letsencrypt::certonly`](#letsencryptcertonly): Request a certificate using the `certonly` installer
* [`letsencrypt::hook`](#letsencrypthook): Creates hook scripts.
-**Functions**
+### Functions
-* [`letsencrypt::letsencrypt_lookup`](#letsencryptletsencrypt_lookup):
+* [`letsencrypt::letsencrypt_lookup`](#letsencryptletsencrypt_lookup)
-**Data types**
+### Data types
* [`Letsencrypt::Cron::Hour`](#letsencryptcronhour): mimic hour setting in cron as defined in man 5 crontab
* [`Letsencrypt::Cron::Minute`](#letsencryptcronminute): mimic minute setting in cron as defined in man 5 crontab
* [`Letsencrypt::Cron::Monthday`](#letsencryptcronmonthday): mimic monthday setting in cron as defined in man 5 crontab
* [`Letsencrypt::Plugin`](#letsencryptplugin): List of accepted plugins
## Classes
-### letsencrypt
+### `letsencrypt`
Install and configure Certbot, the LetsEncrypt client
#### Examples
+#####
+
```puppet
class { 'letsencrypt' :
email => 'letsregister@example.com',
config => {
'server' => 'https://acme-staging-v02.api.letsencrypt.org/directory',
},
}
```
#### Parameters
-The following parameters are available in the `letsencrypt` class.
-
-##### `email`
+The following parameters are available in the `letsencrypt` class:
+
+* [`email`](#email)
+* [`environment`](#environment)
+* [`package_name`](#package_name)
+* [`package_ensure`](#package_ensure)
+* [`package_command`](#package_command)
+* [`config_file`](#config_file)
+* [`config`](#config)
+* [`cron_scripts_path`](#cron_scripts_path)
+* [`cron_owner_group`](#cron_owner_group)
+* [`manage_config`](#manage_config)
+* [`manage_install`](#manage_install)
+* [`manage_dependencies`](#manage_dependencies)
+* [`configure_epel`](#configure_epel)
+* [`agree_tos`](#agree_tos)
+* [`unsafe_registration`](#unsafe_registration)
+* [`config_dir`](#config_dir)
+* [`key_size`](#key_size)
+* [`renew_pre_hook_commands`](#renew_pre_hook_commands)
+* [`renew_post_hook_commands`](#renew_post_hook_commands)
+* [`renew_deploy_hook_commands`](#renew_deploy_hook_commands)
+* [`renew_additional_args`](#renew_additional_args)
+* [`renew_cron_ensure`](#renew_cron_ensure)
+* [`renew_cron_hour`](#renew_cron_hour)
+* [`renew_cron_minute`](#renew_cron_minute)
+* [`renew_cron_monthday`](#renew_cron_monthday)
+
+##### `email`
Data type: `Optional[String]`
The email address to use to register with Let's Encrypt. This takes
precedence over an 'email' setting defined in $config.
-Default value: `undef`
-
-##### `path`
-
-Data type: `String`
-
-The path to the letsencrypt installation.
-
-Default value: '/opt/letsencrypt'
-
-##### `venv_path`
-
-Data type: `Any`
-
-virtualenv path for vcs-installed Certbot
-
-Default value: '/opt/letsencrypt/.venv'
+Default value: ``undef``
-##### `environment`
+##### `environment`
Data type: `Array`
-An optional array of environment variables (in addition to VENV_PATH)
+An optional array of environment variables
-Default value: []
+Default value: `[]`
-##### `repo`
-
-Data type: `String`
-
-A Git URL to install the Let's encrypt client from.
-
-Default value: 'https://github.com/certbot/certbot.git'
-
-##### `version`
-
-Data type: `String`
-
-The Git ref (tag, sha, branch) to check out when installing the client with the `vcs` method.
-
-Default value: 'v0.39.0'
-
-##### `package_name`
+##### `package_name`
Data type: `String`
Name of package and command to use when installing the client with the `package` method.
-Default value: 'certbot'
+Default value: `'certbot'`
-##### `package_ensure`
+##### `package_ensure`
Data type: `Any`
The value passed to `ensure` when installing the client with the `package` method.
-Default value: 'installed'
+Default value: `'installed'`
-##### `package_command`
+##### `package_command`
Data type: `String`
Path or name for letsencrypt executable when installing the client with the `package` method.
-Default value: 'certbot'
+Default value: `'certbot'`
-##### `config_file`
+##### `config_file`
Data type: `String`
The path to the configuration file for the letsencrypt cli.
-Default value: "${config_dir}/cli.ini"
+Default value: `"${config_dir}/cli.ini"`
-##### `config`
+##### `config`
Data type: `Hash`
A hash representation of the letsencrypt configuration file.
-Default value: {'server' => 'https://acme-v02.api.letsencrypt.org/directory'}
+Default value: `{ 'server' => 'https://acme-v02.api.letsencrypt.org/directory' }`
-##### `cron_scripts_path`
+##### `cron_scripts_path`
Data type: `String`
The path for renewal scripts called by cron
-Default value: "${facts['puppet_vardir']}/letsencrypt"
+Default value: `"${facts['puppet_vardir']}/letsencrypt"`
-##### `cron_owner_group`
+##### `cron_owner_group`
Data type: `String`
Group owner of cron renew scripts.
-Default value: 'root'
+Default value: `'root'`
-##### `manage_config`
+##### `manage_config`
Data type: `Boolean`
A feature flag to toggle the management of the letsencrypt configuration file.
-Default value: `true`
+Default value: ``true``
-##### `manage_install`
+##### `manage_install`
Data type: `Boolean`
A feature flag to toggle the management of the letsencrypt client installation.
-Default value: `true`
+Default value: ``true``
-##### `manage_dependencies`
+##### `manage_dependencies`
Data type: `Boolean`
A feature flag to toggle the management of the letsencrypt dependencies.
-Default value: `true`
+Default value: ``true``
-##### `configure_epel`
+##### `configure_epel`
Data type: `Boolean`
A feature flag to include the 'epel' class and depend on it for package installation.
-##### `install_method`
-
-Data type: `Enum['package', 'vcs']`
-
-Method to install the letsencrypt client, either package or vcs.
-
-Default value: 'package'
-
-##### `agree_tos`
+##### `agree_tos`
Data type: `Boolean`
A flag to agree to the Let's Encrypt Terms of Service.
-Default value: `true`
+Default value: ``true``
-##### `unsafe_registration`
+##### `unsafe_registration`
Data type: `Boolean`
A flag to allow using the 'register-unsafely-without-email' flag.
-Default value: `false`
+Default value: ``false``
-##### `config_dir`
+##### `config_dir`
Data type: `Stdlib::Unixpath`
The path to the configuration directory.
-Default value: '/etc/letsencrypt'
+Default value: `'/etc/letsencrypt'`
-##### `key_size`
+##### `key_size`
Data type: `Integer[2048]`
Size for the RSA public key
-Default value: 4096
+Default value: `4096`
-##### `renew_pre_hook_commands`
+##### `renew_pre_hook_commands`
Data type: `Any`
Array of commands to run in a shell before obtaining/renewing any certificates.
-Default value: []
+Default value: `[]`
-##### `renew_post_hook_commands`
+##### `renew_post_hook_commands`
Data type: `Any`
Array of commands to run in a shell after attempting to obtain/renew certificates.
-Default value: []
+Default value: `[]`
-##### `renew_deploy_hook_commands`
+##### `renew_deploy_hook_commands`
Data type: `Any`
Array of commands to run in a shell once for each successfully issued/renewed
certificate. Two environmental variables are supplied by certbot:
- $RENEWED_LINEAGE: Points to the live directory with the cert files and key.
Example: /etc/letsencrypt/live/example.com
- $RENEWED_DOMAINS: A space-delimited list of renewed certificate domains.
Example: "example.com www.example.com"
-Default value: []
+Default value: `[]`
-##### `renew_additional_args`
+##### `renew_additional_args`
Data type: `Any`
Array of additional command line arguments to pass to 'certbot renew'.
-Default value: []
+Default value: `[]`
-##### `renew_cron_ensure`
+##### `renew_cron_ensure`
Data type: `Any`
Intended state of the cron resource running certbot renew.
-Default value: 'absent'
+Default value: `'absent'`
-##### `renew_cron_hour`
+##### `renew_cron_hour`
Data type: `Any`
Optional string, integer or array of hour(s) the renewal command should run.
E.g. '[0,12]' to execute at midnight and midday.
hour.
-Default value: fqdn_rand(24)
+Default value: `fqdn_rand(24)`
-##### `renew_cron_minute`
+##### `renew_cron_minute`
Data type: `Any`
Optional string, integer or array of minute(s) the renewal command should
run. E.g. 0 or '00' or [0,30].
-Default value: fqdn_rand(60, fqdn_rand_string(10))
+Default value: `fqdn_rand(60, fqdn_rand_string(10))`
-##### `renew_cron_monthday`
+##### `renew_cron_monthday`
Data type: `Any`
Optional string, integer or array of monthday(s) the renewal command should
run. E.g. '2-30/2' to run on even days.
-Default value: '*'
+Default value: `'*'`
-### letsencrypt::install
+### `letsencrypt::install`
Installs the Let's Encrypt client.
#### Parameters
-The following parameters are available in the `letsencrypt::install` class.
-
-##### `manage_install`
+The following parameters are available in the `letsencrypt::install` class:
-Data type: `Boolean`
+* [`configure_epel`](#configure_epel)
+* [`package_ensure`](#package_ensure)
+* [`package_name`](#package_name)
-A feature flag to toggle the management of the letsencrypt client installation.
-
-Default value: $letsencrypt::manage_install
-
-##### `manage_dependencies`
-
-Data type: `Boolean`
-
-A feature flag to toggle the management of the letsencrypt dependencies.
-
-Default value: $letsencrypt::manage_dependencies
-
-##### `configure_epel`
+##### `configure_epel`
Data type: `Boolean`
A feature flag to include the 'epel' class and depend on it for package installation.
-Default value: $letsencrypt::configure_epel
-
-##### `install_method`
-
-Data type: `Enum['package', 'vcs']`
-
-Method to install the letsencrypt client
-
-Default value: $letsencrypt::install_method
-
-##### `path`
-
-Data type: `String`
-
-The path to the letsencrypt installation.
-
-Default value: $letsencrypt::path
-
-##### `repo`
-
-Data type: `String`
-
-A Git URL to install the Let's encrypt client from.
+Default value: `$letsencrypt::configure_epel`
-Default value: $letsencrypt::repo
-
-##### `version`
-
-Data type: `String`
-
-The Git ref (tag, sha, branch) to check out when installing the client with the `vcs` method.
-
-Default value: $letsencrypt::version
-
-##### `package_ensure`
+##### `package_ensure`
Data type: `String`
The value passed to `ensure` when installing the client with the `package` method.
-Default value: $letsencrypt::package_ensure
+Default value: `$letsencrypt::package_ensure`
-##### `package_name`
+##### `package_name`
Data type: `String`
Name of package to use when installing the client with the `package` method.
-Default value: $letsencrypt::package_name
+Default value: `$letsencrypt::package_name`
-### letsencrypt::plugin::dns_rfc2136
+### `letsencrypt::plugin::dns_rfc2136`
This class installs and configures the Let's Encrypt dns-rfc2136 plugin.
https://certbot-dns-rfc2136.readthedocs.io
#### Parameters
-The following parameters are available in the `letsencrypt::plugin::dns_rfc2136` class.
+The following parameters are available in the `letsencrypt::plugin::dns_rfc2136` class:
+
+* [`server`](#server)
+* [`key_name`](#key_name)
+* [`key_secret`](#key_secret)
+* [`key_algorithm`](#key_algorithm)
+* [`port`](#port)
+* [`propagation_seconds`](#propagation_seconds)
+* [`manage_package`](#manage_package)
+* [`package_name`](#package_name)
+* [`config_dir`](#config_dir)
-##### `server`
+##### `server`
Data type: `Stdlib::Host`
Target DNS server.
-##### `key_name`
+##### `key_name`
Data type: `String[1]`
TSIG key name.
-##### `key_secret`
+##### `key_secret`
Data type: `String[1]`
TSIG key secret.
-##### `key_algorithm`
+##### `key_algorithm`
Data type: `String[1]`
TSIG key algorithm.
-Default value: 'HMAC-SHA512'
+Default value: `'HMAC-SHA512'`
-##### `port`
+##### `port`
Data type: `Stdlib::Port`
Target DNS port.
-Default value: 53
+Default value: `53`
-##### `propagation_seconds`
+##### `propagation_seconds`
Data type: `Integer`
Number of seconds to wait for the DNS server to propagate the DNS-01 challenge.
-Default value: 10
+Default value: `10`
-##### `manage_package`
+##### `manage_package`
Data type: `Boolean`
Manage the plugin package.
-Default value: `true`
+Default value: ``true``
-##### `package_name`
+##### `package_name`
Data type: `String[1]`
The name of the package to install when $manage_package is true.
-##### `config_dir`
+##### `config_dir`
Data type: `Stdlib::Absolutepath`
The path to the configuration directory.
-Default value: $letsencrypt::config_dir
+Default value: `$letsencrypt::config_dir`
+
+### `letsencrypt::plugin::dns_route53`
+
+This class installs and configures the Let's Encrypt dns-route53 plugin.
+https://certbot-dns-route53.readthedocs.io
+
+#### Parameters
+
+The following parameters are available in the `letsencrypt::plugin::dns_route53` class:
+
+* [`propagation_seconds`](#propagation_seconds)
+* [`manage_package`](#manage_package)
+* [`package_name`](#package_name)
-### letsencrypt::renew
+##### `propagation_seconds`
+
+Data type: `Integer`
+
+Number of seconds to wait for the DNS server to propagate the DNS-01 challenge.
+
+Default value: `10`
+
+##### `manage_package`
+
+Data type: `Boolean`
+
+Manage the plugin package.
+
+Default value: ``true``
+
+##### `package_name`
+
+Data type: `String[1]`
+
+The name of the package to install when $manage_package is true.
+
+### `letsencrypt::renew`
Configures renewal of Let's Encrypt certificates using the certbot renew command.
Note: Hooks set here will run before/after/for ALL certificates, including
any not managed by Puppet. If you want to create hooks for specific
certificates only, create them using letsencrypt::certonly.
#### Parameters
-The following parameters are available in the `letsencrypt::renew` class.
+The following parameters are available in the `letsencrypt::renew` class:
-##### `pre_hook_commands`
+* [`pre_hook_commands`](#pre_hook_commands)
+* [`post_hook_commands`](#post_hook_commands)
+* [`deploy_hook_commands`](#deploy_hook_commands)
+* [`additional_args`](#additional_args)
+* [`cron_ensure`](#cron_ensure)
+* [`cron_hour`](#cron_hour)
+* [`cron_minute`](#cron_minute)
+* [`cron_monthday`](#cron_monthday)
+
+##### `pre_hook_commands`
Data type: `Variant[String[1], Array[String[1]]]`
Array of commands to run in a shell before obtaining/renewing any certificates.
-Default value: $letsencrypt::renew_pre_hook_commands
+Default value: `$letsencrypt::renew_pre_hook_commands`
-##### `post_hook_commands`
+##### `post_hook_commands`
Data type: `Variant[String[1], Array[String[1]]]`
Array of commands to run in a shell after attempting to obtain/renew certificates.
-Default value: $letsencrypt::renew_post_hook_commands
+Default value: `$letsencrypt::renew_post_hook_commands`
-##### `deploy_hook_commands`
+##### `deploy_hook_commands`
Data type: `Variant[String[1], Array[String[1]]]`
Array of commands to run in a shell once for each successfully issued/renewed
certificate. Two environmental variables are supplied by certbot:
- $RENEWED_LINEAGE: Points to the live directory with the cert files and key.
Example: /etc/letsencrypt/live/example.com
- $RENEWED_DOMAINS: A space-delimited list of renewed certificate domains.
Example: "example.com www.example.com"
-Default value: $letsencrypt::renew_deploy_hook_commands
+Default value: `$letsencrypt::renew_deploy_hook_commands`
-##### `additional_args`
+##### `additional_args`
Data type: `Array[String[1]]`
Array of additional command line arguments to pass to 'certbot renew'.
-Default value: $letsencrypt::renew_additional_args
+Default value: `$letsencrypt::renew_additional_args`
-##### `cron_ensure`
+##### `cron_ensure`
Data type: `Enum['present', 'absent']`
Intended state of the cron resource running certbot renew
-Default value: $letsencrypt::renew_cron_ensure
+Default value: `$letsencrypt::renew_cron_ensure`
-##### `cron_hour`
+##### `cron_hour`
Data type: `Letsencrypt::Cron::Hour`
Optional string, integer or array of hour(s) the renewal command should run.
E.g. '[0,12]' to execute at midnight and midday. Default: fqdn-seeded random hour.
-Default value: $letsencrypt::renew_cron_hour
+Default value: `$letsencrypt::renew_cron_hour`
-##### `cron_minute`
+##### `cron_minute`
Data type: `Letsencrypt::Cron::Minute`
Optional string, integer or array of minute(s) the renewal command should
run. E.g. 0 or '00' or [0,30]. Default: fqdn-seeded random minute.
-Default value: $letsencrypt::renew_cron_minute
+Default value: `$letsencrypt::renew_cron_minute`
-##### `cron_monthday`
+##### `cron_monthday`
Data type: `Letsencrypt::Cron::Monthday`
Optional string, integer or array of monthday(s) the renewal command should
run. E.g. '2-30/2' to run on even days. Default: Every day.
-Default value: $letsencrypt::renew_cron_monthday
+Default value: `$letsencrypt::renew_cron_monthday`
## Defined types
-### letsencrypt::certonly
+### `letsencrypt::certonly`
This type can be used to request a certificate using the `certonly` installer.
#### Parameters
-The following parameters are available in the `letsencrypt::certonly` defined type.
-
-##### `ensure`
+The following parameters are available in the `letsencrypt::certonly` defined type:
+
+* [`ensure`](#ensure)
+* [`domains`](#domains)
+* [`custom_plugin`](#custom_plugin)
+* [`plugin`](#plugin)
+* [`webroot_paths`](#webroot_paths)
+* [`letsencrypt_command`](#letsencrypt_command)
+* [`additional_args`](#additional_args)
+* [`environment`](#environment)
+* [`key_size`](#key_size)
+* [`manage_cron`](#manage_cron)
+* [`suppress_cron_output`](#suppress_cron_output)
+* [`cron_before_command`](#cron_before_command)
+* [`cron_success_command`](#cron_success_command)
+* [`cron_hour`](#cron_hour)
+* [`cron_minute`](#cron_minute)
+* [`cron_monthday`](#cron_monthday)
+* [`config_dir`](#config_dir)
+* [`pre_hook_commands`](#pre_hook_commands)
+* [`post_hook_commands`](#post_hook_commands)
+* [`deploy_hook_commands`](#deploy_hook_commands)
+* [`cert_name`](#cert_name)
+
+##### `ensure`
Data type: `Enum['present','absent']`
Intended state of the resource
Will remove certificates for specified domains if set to 'absent'. Will
also remove cronjobs and renewal scripts if `manage_cron` is set to 'true'.
-Default value: 'present'
+Default value: `'present'`
-##### `domains`
+##### `domains`
Data type: `Array[String[1]]`
An array of domains to include in the CSR.
-Default value: [$title]
+Default value: `[$title]`
-##### `custom_plugin`
+##### `custom_plugin`
Data type: `Boolean`
Whether to use a custom plugin in additional_args and disable -a flag.
-Default value: `false`
+Default value: ``false``
-##### `plugin`
+##### `plugin`
Data type: `Letsencrypt::Plugin`
The authenticator plugin to use when requesting the certificate.
-Default value: 'standalone'
+Default value: `'standalone'`
-##### `webroot_paths`
+##### `webroot_paths`
Data type: `Array[Stdlib::Unixpath]`
An array of webroot paths for the domains in `domains`.
Required if using `plugin => 'webroot'`. If `domains` and
`webroot_paths` are not the same length, the last `webroot_paths`
element will be used for all subsequent domains.
-Default value: []
+Default value: `[]`
-##### `letsencrypt_command`
+##### `letsencrypt_command`
Data type: `String[1]`
Command to run letsencrypt
-Default value: $letsencrypt::command
+Default value: `$letsencrypt::command`
-##### `additional_args`
+##### `additional_args`
Data type: `Array[String[1]]`
-An array of additional command line arguments to pass to the `letsencrypt-auto` command.
+An array of additional command line arguments to pass to the `letsencrypt` command.
-Default value: []
+Default value: `[]`
-##### `environment`
+##### `environment`
Data type: `Array[String[1]]`
-An optional array of environment variables (in addition to VENV_PATH).
+An optional array of environment variables
-Default value: []
+Default value: `[]`
-##### `key_size`
+##### `key_size`
Data type: `Integer[2048]`
Size for the RSA public key
-Default value: $letsencrypt::key_size
+Default value: `$letsencrypt::key_size`
-##### `manage_cron`
+##### `manage_cron`
Data type: `Boolean`
Indicating whether or not to schedule cron job for renewal.
Runs daily but only renews if near expiration, e.g. within 10 days.
-Default value: `false`
+Default value: ``false``
-##### `suppress_cron_output`
+##### `suppress_cron_output`
Data type: `Boolean`
Redirect cron output to devnull
-Default value: `false`
+Default value: ``false``
-##### `cron_before_command`
+##### `cron_before_command`
Data type: `Optional[String[1]]`
Representation of a command that should be run before renewal command
-Default value: `undef`
+Default value: ``undef``
-##### `cron_success_command`
+##### `cron_success_command`
Data type: `Optional[String[1]]`
Representation of a command that should be run if the renewal command succeeds.
-Default value: `undef`
+Default value: ``undef``
-##### `cron_hour`
+##### `cron_hour`
Data type: `Variant[Integer[0,23], String, Array]`
Optional hour(s) that the renewal command should execute.
e.g. '[0,12]' execute at midnight and midday. Default - seeded random hour.
-Default value: fqdn_rand(24, $title)
+Default value: `fqdn_rand(24, $title)`
-##### `cron_minute`
+##### `cron_minute`
Data type: `Variant[Integer[0,59], String, Array]`
Optional minute(s) that the renewal command should execute.
e.g. 0 or '00' or [0,30]. Default - seeded random minute.
-Default value: fqdn_rand(60, fqdn_rand_string(10, $title))
+Default value: `fqdn_rand(60, fqdn_rand_string(10, $title))`
-##### `cron_monthday`
+##### `cron_monthday`
Data type: `Array[Variant[Integer[0, 59], String[1]]]`
Optional string, integer or array of monthday(s) the renewal command should
run. E.g. '2-30/2' to run on even days. Default: Every day.
-Default value: ['*']
+Default value: `['*']`
-##### `config_dir`
+##### `config_dir`
Data type: `Stdlib::Unixpath`
The path to the configuration directory.
-Default value: $letsencrypt::config_dir
+Default value: `$letsencrypt::config_dir`
-##### `pre_hook_commands`
+##### `pre_hook_commands`
Data type: `Variant[String[1], Array[String[1]]]`
Array of commands to run in a shell before attempting to obtain/renew the certificate.
-Default value: []
+Default value: `[]`
-##### `post_hook_commands`
+##### `post_hook_commands`
Data type: `Variant[String[1], Array[String[1]]]`
Array of command(s) to run in a shell after attempting to obtain/renew the certificate.
-Default value: []
+Default value: `[]`
-##### `deploy_hook_commands`
+##### `deploy_hook_commands`
Data type: `Variant[String[1], Array[String[1]]]`
Array of command(s) to run in a shell once if the certificate is successfully issued.
Two environmental variables are supplied by certbot:
- $RENEWED_LINEAGE: Points to the live directory with the cert files and key.
Example: /etc/letsencrypt/live/example.com
- $RENEWED_DOMAINS: A space-delimited list of renewed certificate domains.
Example: "example.com www.example.com"
-Default value: []
+Default value: `[]`
-### letsencrypt::hook
+##### `cert_name`
+
+Data type: `String[1]`
+
+
+
+Default value: `$title`
+
+### `letsencrypt::hook`
This type is used by letsencrypt::renew and letsencrypt::certonly to create hook scripts.
#### Parameters
-The following parameters are available in the `letsencrypt::hook` defined type.
+The following parameters are available in the `letsencrypt::hook` defined type:
+
+* [`type`](#type)
+* [`hook_file`](#hook_file)
+* [`commands`](#commands)
-##### `type`
+##### `type`
Data type: `Enum['pre', 'post', 'deploy']`
Hook type.
-##### `hook_file`
+##### `hook_file`
Data type: `String[1]`
Path to deploy hook script.
-##### `commands`
+##### `commands`
Data type: `Variant[String[1],Array[String[1]]]`
Bash commands to execute when the hook is run by certbot.
## Functions
-### letsencrypt::letsencrypt_lookup
+### `letsencrypt::letsencrypt_lookup`
Type: Ruby 4.x API
The letsencrypt::letsencrypt_lookup function.
#### `letsencrypt::letsencrypt_lookup(Any $cn)`
The letsencrypt::letsencrypt_lookup function.
Returns: `Any`
##### `cn`
Data type: `Any`
## Data types
-### Letsencrypt::Cron::Hour
+### `Letsencrypt::Cron::Hour`
mimic hour setting in cron as defined in man 5 crontab
-Alias of `Variant[Integer[0,23], String[1], Array[
+Alias of
+
+```puppet
+Variant[Integer[0,23], String[1], Array[
Variant[
Integer[0,23],
String[1],
]
- ]]`
+ ]]
+```
-### Letsencrypt::Cron::Minute
+### `Letsencrypt::Cron::Minute`
mimic minute setting in cron as defined in man 5 crontab
-Alias of `Variant[Integer[0,59], String[1], Array[
+Alias of
+
+```puppet
+Variant[Integer[0,59], String[1], Array[
Variant[
Integer[0,59],
String[1],
]
- ]]`
+ ]]
+```
-### Letsencrypt::Cron::Monthday
+### `Letsencrypt::Cron::Monthday`
mimic monthday setting in cron as defined in man 5 crontab
-Alias of `Variant[Integer[0,31], String[1], Array[
+Alias of
+
+```puppet
+Variant[Integer[0,31], String[1], Array[
Variant[
Integer[0,31],
String[1],
]
- ]]`
+ ]]
+```
-### Letsencrypt::Plugin
+### `Letsencrypt::Plugin`
List of accepted plugins
-Alias of `Enum['apache', 'standalone', 'webroot', 'nginx', 'dns-route53', 'dns-google', 'dns-cloudflare', 'dns-rfc2136']`
+Alias of
+
+```puppet
+Enum['apache', 'standalone', 'webroot', 'nginx', 'dns-route53', 'dns-google', 'dns-cloudflare', 'dns-rfc2136']
+```
diff --git a/Rakefile b/Rakefile
index b450fe7..d1bf749 100644
--- a/Rakefile
+++ b/Rakefile
@@ -1,54 +1,61 @@
-require 'voxpupuli/test/rake'
+# Attempt to load voxupuli-test (which pulls in puppetlabs_spec_helper),
+# otherwise attempt to load it directly.
+begin
+ require 'voxpupuli/test/rake'
+rescue LoadError
+ require 'puppetlabs_spec_helper/rake_tasks'
+end
# load optional tasks for releases
# only available if gem group releases is installed
begin
require 'voxpupuli/release/rake_tasks'
rescue LoadError
end
desc "Run main 'test' task and report merged results to coveralls"
task test_with_coveralls: [:test] do
if Dir.exist?(File.expand_path('../lib', __FILE__))
require 'coveralls/rake/task'
Coveralls::RakeTask.new
Rake::Task['coveralls:push'].invoke
else
puts 'Skipping reporting to coveralls. Module has no lib dir'
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/manifests/certonly.pp b/manifests/certonly.pp
index b639fc9..2b46b84 100644
--- a/manifests/certonly.pp
+++ b/manifests/certonly.pp
@@ -1,219 +1,218 @@
# @summary Request a certificate using the `certonly` installer
#
# This type can be used to request a certificate using the `certonly` installer.
#
# @param ensure
# Intended state of the resource
# Will remove certificates for specified domains if set to 'absent'. Will
# also remove cronjobs and renewal scripts if `manage_cron` is set to 'true'.
# @param domains
# An array of domains to include in the CSR.
# @param custom_plugin Whether to use a custom plugin in additional_args and disable -a flag.
# @param plugin The authenticator plugin to use when requesting the certificate.
# @param webroot_paths
# An array of webroot paths for the domains in `domains`.
# Required if using `plugin => 'webroot'`. If `domains` and
# `webroot_paths` are not the same length, the last `webroot_paths`
# element will be used for all subsequent domains.
# @param letsencrypt_command Command to run letsencrypt
-# @param additional_args An array of additional command line arguments to pass to the `letsencrypt-auto` command.
-# @param environment An optional array of environment variables (in addition to VENV_PATH).
+# @param additional_args An array of additional command line arguments to pass to the `letsencrypt` command.
+# @param environment An optional array of environment variables
# @param key_size Size for the RSA public key
# @param manage_cron
# Indicating whether or not to schedule cron job for renewal.
# Runs daily but only renews if near expiration, e.g. within 10 days.
# @param suppress_cron_output Redirect cron output to devnull
# @param cron_before_command Representation of a command that should be run before renewal command
# @param cron_success_command Representation of a command that should be run if the renewal command succeeds.
# @param cron_hour
# Optional hour(s) that the renewal command should execute.
# e.g. '[0,12]' execute at midnight and midday. Default - seeded random hour.
# @param cron_minute
# Optional minute(s) that the renewal command should execute.
# e.g. 0 or '00' or [0,30]. Default - seeded random minute.
# @param cron_monthday
# Optional string, integer or array of monthday(s) the renewal command should
# run. E.g. '2-30/2' to run on even days. Default: Every day.
# @param config_dir The path to the configuration directory.
# @param pre_hook_commands Array of commands to run in a shell before attempting to obtain/renew the certificate.
# @param post_hook_commands Array of command(s) to run in a shell after attempting to obtain/renew the certificate.
# @param deploy_hook_commands
# Array of command(s) to run in a shell once if the certificate is successfully issued.
# Two environmental variables are supplied by certbot:
# - $RENEWED_LINEAGE: Points to the live directory with the cert files and key.
# Example: /etc/letsencrypt/live/example.com
# - $RENEWED_DOMAINS: A space-delimited list of renewed certificate domains.
# Example: "example.com www.example.com"
#
define letsencrypt::certonly (
Enum['present','absent'] $ensure = 'present',
Array[String[1]] $domains = [$title],
String[1] $cert_name = $title,
Boolean $custom_plugin = false,
Letsencrypt::Plugin $plugin = 'standalone',
Array[Stdlib::Unixpath] $webroot_paths = [],
String[1] $letsencrypt_command = $letsencrypt::command,
Integer[2048] $key_size = $letsencrypt::key_size,
Array[String[1]] $additional_args = [],
Array[String[1]] $environment = [],
Boolean $manage_cron = false,
Boolean $suppress_cron_output = false,
Optional[String[1]] $cron_before_command = undef,
Optional[String[1]] $cron_success_command = undef,
Array[Variant[Integer[0, 59], String[1]]] $cron_monthday = ['*'],
Variant[Integer[0,23], String, Array] $cron_hour = fqdn_rand(24, $title),
Variant[Integer[0,59], String, Array] $cron_minute = fqdn_rand(60, fqdn_rand_string(10, $title)),
Stdlib::Unixpath $config_dir = $letsencrypt::config_dir,
Variant[String[1], Array[String[1]]] $pre_hook_commands = [],
Variant[String[1], Array[String[1]]] $post_hook_commands = [],
Variant[String[1], Array[String[1]]] $deploy_hook_commands = [],
) {
if $plugin == 'webroot' and empty($webroot_paths) {
fail("The 'webroot_paths' parameter must be specified when using the 'webroot' plugin")
}
# Wildcard-less title for use in file paths
$title_nowc = regsubst($title, '^\*\.', '')
if $ensure == 'present' {
if ($custom_plugin) {
$default_args = "--text --agree-tos --non-interactive certonly --rsa-key-size ${key_size}"
} else {
$default_args = "--text --agree-tos --non-interactive certonly --rsa-key-size ${key_size} -a ${plugin}"
}
} else {
$default_args = '--text --agree-tos --non-interactive delete'
}
case $plugin {
'webroot': {
$_plugin_args = zip($domains, $webroot_paths).map |$domain| {
if $domain[1] {
"--webroot-path ${domain[1]} -d '${domain[0]}'"
} else {
"-d '${domain[0]}'"
}
}
$plugin_args = ["--cert-name '${cert_name}'"] + $_plugin_args
}
'dns-rfc2136': {
require letsencrypt::plugin::dns_rfc2136
$_domains = join($domains, '\' -d \'')
$plugin_args = [
"--cert-name '${cert_name}' -d",
"'${_domains}'",
"--dns-rfc2136-credentials ${letsencrypt::plugin::dns_rfc2136::config_dir}/dns-rfc2136.ini",
"--dns-rfc2136-propagation-seconds ${letsencrypt::plugin::dns_rfc2136::propagation_seconds}",
]
}
'dns-route53': {
require letsencrypt::plugin::dns_route53
$_domains = join($domains, '\' -d \'')
$plugin_args = [
"--cert-name '${cert_name}' -d '${_domains}'",
"--dns-route53-propagation-seconds ${letsencrypt::plugin::dns_route53::propagation_seconds}",
]
}
default: {
if $ensure == 'present' {
$_domains = join($domains, '\' -d \'')
$plugin_args = "--cert-name '${cert_name}' -d '${_domains}'"
} else {
$plugin_args = "--cert-name '${cert_name}'"
}
}
}
$hook_args = ['pre', 'post', 'deploy'].map | String $type | {
$commands = getvar("${type}_hook_commands")
if (!empty($commands)) {
$hook_file = "${config_dir}/renewal-hooks-puppet/${title_nowc}-${type}.sh"
letsencrypt::hook { "${title}-${type}":
type => $type,
hook_file => $hook_file,
commands => $commands,
before => Exec["letsencrypt certonly ${title}"],
}
"--${type}-hook \"${hook_file}\""
}
else {
undef
}
}
# certbot uses --cert-name to generate the file path
$live_path_certname = regsubst($cert_name, '^\*\.', '')
$live_path = "${config_dir}/live/${live_path_certname}/cert.pem"
$_command = flatten([
$letsencrypt_command,
$default_args,
$plugin_args,
$hook_args,
$additional_args,
]).filter | $arg | { $arg =~ NotUndef and $arg != [] }
$command = join($_command, ' ')
- $execution_environment = ["VENV_PATH=${letsencrypt::venv_path}",] + $environment
$verify_domains = join(unique($domains), '\' \'')
if $ensure == 'present' {
$exec_ensure = { 'unless' => "/usr/local/sbin/letsencrypt-domain-validation ${live_path} '${verify_domains}'" }
} else {
$exec_ensure = { 'onlyif' => "/usr/local/sbin/letsencrypt-domain-validation ${live_path} '${verify_domains}'" }
}
exec { "letsencrypt certonly ${title}":
command => $command,
* => $exec_ensure,
path => $facts['path'],
- environment => $execution_environment,
+ environment => $environment,
provider => 'shell',
require => [
Class['letsencrypt'],
File['/usr/local/sbin/letsencrypt-domain-validation'],
],
}
if $manage_cron {
$maincommand = join(["${letsencrypt_command} --keep-until-expiring"] + $_command[1,-1], ' ')
$cron_script_ensure = $ensure ? { 'present' => 'file', default => 'absent' }
$cron_ensure = $ensure
if $suppress_cron_output {
$croncommand = "${maincommand} > /dev/null 2>&1"
} else {
$croncommand = $maincommand
}
if $cron_before_command {
$renewcommand = "(${cron_before_command}) && ${croncommand}"
} else {
$renewcommand = $croncommand
}
if $cron_success_command {
$cron_cmd = "${renewcommand} && (${cron_success_command})"
} else {
$cron_cmd = $renewcommand
}
file { "${letsencrypt::cron_scripts_path}/renew-${title}.sh":
ensure => $cron_script_ensure,
mode => '0755',
owner => 'root',
group => $letsencrypt::cron_owner_group,
content => template('letsencrypt/renew-script.sh.erb'),
}
cron { "letsencrypt renew cron ${title}":
ensure => $cron_ensure,
command => "\"${letsencrypt::cron_scripts_path}/renew-${title}.sh\"",
user => root,
hour => $cron_hour,
minute => $cron_minute,
monthday => $cron_monthday,
}
}
}
diff --git a/manifests/init.pp b/manifests/init.pp
index d72707b..261e8f7 100644
--- a/manifests/init.pp
+++ b/manifests/init.pp
@@ -1,131 +1,113 @@
# @summary Install and configure Certbot, the LetsEncrypt client
#
# Install and configure Certbot, the LetsEncrypt client
#
# @example
# class { 'letsencrypt' :
# email => 'letsregister@example.com',
# config => {
# 'server' => 'https://acme-staging-v02.api.letsencrypt.org/directory',
# },
# }
#
# @param email
# The email address to use to register with Let's Encrypt. This takes
# precedence over an 'email' setting defined in $config.
-# @param path The path to the letsencrypt installation.
-# @param venv_path virtualenv path for vcs-installed Certbot
-# @param environment An optional array of environment variables (in addition to VENV_PATH)
-# @param repo A Git URL to install the Let's encrypt client from.
-# @param version The Git ref (tag, sha, branch) to check out when installing the client with the `vcs` method.
+# @param environment An optional array of environment variables
# @param package_name Name of package and command to use when installing the client with the `package` method.
# @param package_ensure The value passed to `ensure` when installing the client with the `package` method.
# @param package_command Path or name for letsencrypt executable when installing the client with the `package` method.
# @param config_file The path to the configuration file for the letsencrypt cli.
# @param config A hash representation of the letsencrypt configuration file.
# @param cron_scripts_path The path for renewal scripts called by cron
# @param cron_owner_group Group owner of cron renew scripts.
# @param manage_config A feature flag to toggle the management of the letsencrypt configuration file.
# @param manage_install A feature flag to toggle the management of the letsencrypt client installation.
# @param manage_dependencies A feature flag to toggle the management of the letsencrypt dependencies.
# @param configure_epel A feature flag to include the 'epel' class and depend on it for package installation.
-# @param install_method Method to install the letsencrypt client, either package or vcs.
# @param agree_tos A flag to agree to the Let's Encrypt Terms of Service.
# @param unsafe_registration A flag to allow using the 'register-unsafely-without-email' flag.
# @param config_dir The path to the configuration directory.
# @param key_size Size for the RSA public key
# @param renew_pre_hook_commands Array of commands to run in a shell before obtaining/renewing any certificates.
# @param renew_post_hook_commands Array of commands to run in a shell after attempting to obtain/renew certificates.
# @param renew_deploy_hook_commands
# Array of commands to run in a shell once for each successfully issued/renewed
# certificate. Two environmental variables are supplied by certbot:
# - $RENEWED_LINEAGE: Points to the live directory with the cert files and key.
# Example: /etc/letsencrypt/live/example.com
# - $RENEWED_DOMAINS: A space-delimited list of renewed certificate domains.
# Example: "example.com www.example.com"
# @param renew_additional_args Array of additional command line arguments to pass to 'certbot renew'.
# @param renew_cron_ensure Intended state of the cron resource running certbot renew.
# @param renew_cron_hour
# Optional string, integer or array of hour(s) the renewal command should run.
# E.g. '[0,12]' to execute at midnight and midday.
# hour.
# @param renew_cron_minute
# Optional string, integer or array of minute(s) the renewal command should
# run. E.g. 0 or '00' or [0,30].
# @param renew_cron_monthday
# Optional string, integer or array of monthday(s) the renewal command should
# run. E.g. '2-30/2' to run on even days.
#
class letsencrypt (
Boolean $configure_epel,
Optional[String] $email = undef,
- String $path = '/opt/letsencrypt',
- $venv_path = '/opt/letsencrypt/.venv',
Array $environment = [],
- String $repo = 'https://github.com/certbot/certbot.git',
- String $version = 'v0.39.0',
String $package_name = 'certbot',
$package_ensure = 'installed',
String $package_command = 'certbot',
Stdlib::Unixpath $config_dir = '/etc/letsencrypt',
String $config_file = "${config_dir}/cli.ini",
Hash $config = { 'server' => 'https://acme-v02.api.letsencrypt.org/directory' },
String $cron_scripts_path = "${facts['puppet_vardir']}/letsencrypt",
String $cron_owner_group = 'root',
Boolean $manage_config = true,
Boolean $manage_install = true,
Boolean $manage_dependencies = true,
- Enum['package', 'vcs'] $install_method = 'package',
Boolean $agree_tos = true,
Boolean $unsafe_registration = false,
Integer[2048] $key_size = 4096,
# $renew_* should only be used in letsencrypt::renew (blame rspec)
$renew_pre_hook_commands = [],
$renew_post_hook_commands = [],
$renew_deploy_hook_commands = [],
$renew_additional_args = [],
$renew_cron_ensure = 'absent',
$renew_cron_hour = fqdn_rand(24),
$renew_cron_minute = fqdn_rand(60, fqdn_rand_string(10)),
$renew_cron_monthday = '*',
) {
if $manage_install {
contain letsencrypt::install # lint:ignore:relative_classname_inclusion
Class['letsencrypt::install'] ~> Exec['initialize letsencrypt']
Class['letsencrypt::install'] -> Class['letsencrypt::renew']
}
- $command = $install_method ? {
- 'package' => $package_command,
- 'vcs' => "${venv_path}/bin/letsencrypt",
- }
-
- $command_init = $install_method ? {
- 'package' => $package_command,
- 'vcs' => "${path}/letsencrypt-auto",
- }
+ $command = $package_command
if $manage_config {
contain letsencrypt::config # lint:ignore:relative_classname_inclusion
Class['letsencrypt::config'] -> Exec['initialize letsencrypt']
}
contain letsencrypt::renew
# TODO: do we need this command when installing from package?
exec { 'initialize letsencrypt':
- command => "${command_init} -h",
+ command => "${package_command} -h",
path => $facts['path'],
- environment => concat(["VENV_PATH=${venv_path}"], $environment),
+ environment => $environment,
refreshonly => true,
}
# Used in letsencrypt::certonly Exec["letsencrypt certonly ${title}"]
file { '/usr/local/sbin/letsencrypt-domain-validation':
ensure => file,
owner => 'root',
group => 'root',
mode => '0500',
source => "puppet:///modules/${module_name}/domain-validation.sh",
}
}
diff --git a/manifests/install.pp b/manifests/install.pp
index 0ba3313..c46f33c 100644
--- a/manifests/install.pp
+++ b/manifests/install.pp
@@ -1,48 +1,21 @@
# @summary Installs the Let's Encrypt client.
#
-# @param manage_install A feature flag to toggle the management of the letsencrypt client installation.
-# @param manage_dependencies A feature flag to toggle the management of the letsencrypt dependencies.
# @param configure_epel A feature flag to include the 'epel' class and depend on it for package installation.
-# @param install_method Method to install the letsencrypt client
-# @param path The path to the letsencrypt installation.
-# @param repo A Git URL to install the Let's encrypt client from.
-# @param version The Git ref (tag, sha, branch) to check out when installing the client with the `vcs` method.
# @param package_ensure The value passed to `ensure` when installing the client with the `package` method.
# @param package_name Name of package to use when installing the client with the `package` method.
#
class letsencrypt::install (
- Boolean $manage_install = $letsencrypt::manage_install,
- Boolean $manage_dependencies = $letsencrypt::manage_dependencies,
Boolean $configure_epel = $letsencrypt::configure_epel,
- Enum['package', 'vcs'] $install_method = $letsencrypt::install_method,
String $package_name = $letsencrypt::package_name,
String $package_ensure = $letsencrypt::package_ensure,
- String $path = $letsencrypt::path,
- String $repo = $letsencrypt::repo,
- String $version = $letsencrypt::version,
) {
- if $install_method == 'vcs' {
- if $manage_dependencies {
- $dependencies = ['python', 'git']
- ensure_packages($dependencies)
- Package[$dependencies] -> Vcsrepo[$path]
- }
-
- vcsrepo { $path:
- ensure => present,
- provider => git,
- source => $repo,
- revision => $version,
- }
- } else {
- package { 'letsencrypt':
- ensure => $package_ensure,
- name => $package_name,
- }
+ package { 'letsencrypt':
+ ensure => $package_ensure,
+ name => $package_name,
+ }
- if $configure_epel {
- include epel
- Class['epel'] -> Package['letsencrypt']
- }
+ if $configure_epel {
+ include epel
+ Class['epel'] -> Package['letsencrypt']
}
}
diff --git a/metadata.json b/metadata.json
index 2a00b23..09d6a3b 100644
--- a/metadata.json
+++ b/metadata.json
@@ -1,88 +1,83 @@
{
"name": "puppet-letsencrypt",
"version": "6.0.1-rc0",
"author": "Vox Pupuli",
"summary": "Manages lets-encrypt and certbot + related certs",
"license": "Apache-2.0",
"source": "https://github.com/voxpupuli/puppet-letsencrypt",
"project_page": "https://github.com/voxpupuli/puppet-letsencrypt",
"issues_url": "https://github.com/voxpupuli/puppet-letsencrypt/issues",
"tags": [
"letsencrypt",
"let's encrypt",
"certbot",
"acme"
],
"operatingsystem_support": [
{
"operatingsystem": "CentOS",
"operatingsystemrelease": [
"7"
]
},
{
"operatingsystem": "RedHat",
"operatingsystemrelease": [
"7"
]
},
{
"operatingsystem": "Fedora",
"operatingsystemrelease": [
- "30",
- "31"
+ "32"
]
},
{
"operatingsystem": "Ubuntu",
"operatingsystemrelease": [
"16.04",
"18.04"
]
},
{
"operatingsystem": "Debian",
"operatingsystemrelease": [
"9",
"10"
]
},
{
"operatingsystem": "OpenBSD",
"operatingsystemrelease": [
"6.2"
]
},
{
"operatingsystem": "FreeBSD",
"operatingsystemrelease": [
"11",
"12"
]
}
],
"requirements": [
{
"name": "puppet",
- "version_requirement": ">= 5.5.8 < 7.0.0"
+ "version_requirement": ">= 6.1.0 < 8.0.0"
}
],
"dependencies": [
{
"name": "puppetlabs/stdlib",
"version_requirement": ">= 4.13.1 < 7.0.0"
},
{
"name": "puppetlabs/inifile",
"version_requirement": ">= 2.0.0 < 5.0.0"
},
- {
- "name": "puppetlabs/vcsrepo",
- "version_requirement": ">= 2.0.0 < 4.0.0"
- },
{
"name": "puppet/epel",
"version_requirement": ">= 3.0.1 < 4.0.0"
}
]
}
diff --git a/spec/acceptance/letsencrypt_spec.rb b/spec/acceptance/letsencrypt_spec.rb
index 7c30e7b..38c0d04 100644
--- a/spec/acceptance/letsencrypt_spec.rb
+++ b/spec/acceptance/letsencrypt_spec.rb
@@ -1,65 +1,30 @@
require 'spec_helper_acceptance'
describe 'letsencrypt' do
context 'with defaults values' do
pp = %(
class { 'letsencrypt' :
email => 'letsregister@example.com',
config => {
'server' => 'https://acme-staging-v02.api.letsencrypt.org/directory',
},
}
)
it 'installs letsencrypt without error' do
apply_manifest(pp, catch_failures: true)
end
it 'installs letsencrypt idempotently' do
apply_manifest(pp, catch_changes: true)
end
describe file('/etc/letsencrypt/cli.ini') do
it { is_expected.to be_file }
it { is_expected.to be_owned_by 'root' }
it { is_expected.to be_grouped_into 'root' }
it { is_expected.to be_mode 644 }
its(:content) { is_expected.to match %r{server = https://acme-staging-v02.api.letsencrypt.org/directory} }
its(:content) { is_expected.to match %r{email = letsregister@example.com} }
end
end
-
- context 'with install_method => vcs' do
- pp = %(
- class { 'letsencrypt' :
- install_method => 'vcs',
- email => 'letsregister@example.com',
- config => {
- 'server' => 'https://acme-staging-v02.api.letsencrypt.org/directory',
- },
- }
- )
-
- it 'installs letsencrypt without error' do
- apply_manifest(pp, catch_failures: true)
- end
- it 'installs letsencrypt idempotently' do
- apply_manifest(pp, catch_changes: true)
- end
-
- describe file('/etc/letsencrypt/cli.ini') do
- it { is_expected.to be_file }
- it { is_expected.to be_owned_by 'root' }
- it { is_expected.to be_grouped_into 'root' }
- it { is_expected.to be_mode 644 }
- its(:content) { is_expected.to match %r{server = https://acme-staging-v02.api.letsencrypt.org/directory} }
- its(:content) { is_expected.to match %r{email = letsregister@example.com} }
- end
-
- describe file('/opt/letsencrypt/.venv/bin/certbot') do
- it { is_expected.to be_file }
- it { is_expected.to be_owned_by 'root' }
- it { is_expected.to be_grouped_into 'root' }
- it { is_expected.to be_mode 755 }
- end
- end
end
diff --git a/spec/classes/letsencrypt_install_spec.rb b/spec/classes/letsencrypt_install_spec.rb
index 46a807c..7510585 100644
--- a/spec/classes/letsencrypt_install_spec.rb
+++ b/spec/classes/letsencrypt_install_spec.rb
@@ -1,103 +1,48 @@
require 'spec_helper'
describe 'letsencrypt::install' do
on_supported_os.each do |os, facts|
let(:params) { default_params.merge(additional_params) }
let(:default_params) do
{
configure_epel: false,
package_ensure: 'installed',
- manage_install: true,
- manage_dependencies: true,
- path: '/opt/letsencrypt',
- repo: 'https://github.com/certbot/certbot.git',
- version: 'v0.30.2',
package_name: 'letsencrypt'
}
end
let(:additional_params) { {} }
context "on #{os} based operating systems" do
let :facts do
facts
end
- describe 'with install_method => package' do
- let(:additional_params) { { install_method: 'package' } }
+ it { is_expected.to compile.with_all_deps }
- it { is_expected.to compile }
-
- it 'contains the correct resources' do
- is_expected.not_to contain_vcsrepo('/opt/letsencrypt')
- is_expected.not_to contain_package('python')
- is_expected.not_to contain_package('git')
-
- is_expected.to contain_package('letsencrypt').with_ensure('installed')
- end
-
- describe 'with package_ensure => 0.3.0-1.el7' do
- let(:additional_params) { { install_method: 'package', package_ensure: '0.3.0-1.el7' } }
-
- it { is_expected.to compile }
- it { is_expected.to contain_package('letsencrypt').with_ensure('0.3.0-1.el7') }
- end
-
- case facts[:osfamily]
- when 'RedHat'
- describe 'with configure_epel => true' do
- let(:additional_params) { { install_method: 'package', configure_epel: true } }
-
- it { is_expected.to compile }
-
- it 'contains the correct resources' do
- is_expected.to contain_class('epel')
- is_expected.to contain_package('letsencrypt').that_requires('Class[epel]')
- end
- end
- end
+ it 'contains the correct resources' do
+ is_expected.to contain_package('letsencrypt').with_ensure('installed')
end
- describe 'with install_method => vcs' do
- let(:additional_params) { { install_method: 'vcs' } }
-
- it { is_expected.to compile }
+ describe 'with package_ensure => 0.3.0-1.el7' do
+ let(:additional_params) { { package_ensure: '0.3.0-1.el7' } }
- it 'contains the correct resources' do
- is_expected.to contain_vcsrepo('/opt/letsencrypt').with(source: 'https://github.com/certbot/certbot.git',
- revision: 'v0.30.2')
- is_expected.to contain_package('python')
- is_expected.to contain_package('git')
-
- is_expected.not_to contain_package('letsencrypt')
- end
-
- describe 'with custom path' do
- let(:additional_params) { { install_method: 'vcs', path: '/usr/lib/letsencrypt' } }
-
- it { is_expected.to contain_vcsrepo('/usr/lib/letsencrypt') }
- end
-
- describe 'with custom repo' do
- let(:additional_params) { { install_method: 'vcs', repo: 'git://foo.com/letsencrypt.git' } }
-
- it { is_expected.to contain_vcsrepo('/opt/letsencrypt').with_source('git://foo.com/letsencrypt.git') }
- end
-
- describe 'with custom version' do
- let(:additional_params) { { install_method: 'vcs', version: 'foo' } }
+ it { is_expected.to compile.with_all_deps }
+ it { is_expected.to contain_package('letsencrypt').with_ensure('0.3.0-1.el7') }
+ end
- it { is_expected.to contain_vcsrepo('/opt/letsencrypt').with_revision('foo') }
- end
+ case facts[:osfamily]
+ when 'RedHat'
+ describe 'with configure_epel => true' do
+ let(:additional_params) { { configure_epel: true } }
- describe 'with manage_dependencies set to false' do
- let(:additional_params) { { install_method: 'vcs', manage_dependencies: false } }
+ it { is_expected.to compile.with_all_deps }
- it 'does not contain the dependencies' do
- is_expected.not_to contain_package('git')
- is_expected.not_to contain_package('python')
+ it 'contains the correct resources' do
+ is_expected.to contain_class('epel')
+ is_expected.to contain_package('letsencrypt').that_requires('Class[epel]')
end
end
end
end
end
end
diff --git a/spec/classes/letsencrypt_spec.rb b/spec/classes/letsencrypt_spec.rb
index 1a5e615..1b59164 100644
--- a/spec/classes/letsencrypt_spec.rb
+++ b/spec/classes/letsencrypt_spec.rb
@@ -1,298 +1,255 @@
require 'spec_helper'
describe 'letsencrypt' do
on_supported_os.each do |os, facts|
context "on #{os}" do
let :facts do
facts
end
context 'when specifying an email address with the email parameter' do
let(:params) { additional_params.merge(default_params) }
let(:default_params) { { email: 'foo@example.com' } }
let(:additional_params) { {} }
describe 'with defaults' do
it { is_expected.to compile }
epel = facts[:osfamily] == 'RedHat' && facts[:operatingsystem] != 'Fedora'
it 'contains File[/usr/local/sbin/letsencrypt-domain-validation]' do
is_expected.to contain_file('/usr/local/sbin/letsencrypt-domain-validation').
with_ensure('file').
with_owner('root').
with_group('root').
with_mode('0500').
with_source('puppet:///modules/letsencrypt/domain-validation.sh')
end
it 'contains the correct resources' do
is_expected.to contain_class('letsencrypt::install').
- with(configure_epel: epel,
- manage_install: true,
- manage_dependencies: true,
- repo: 'https://github.com/certbot/certbot.git',
- version: 'v0.39.0').
+ with(configure_epel: epel).
that_notifies('Exec[initialize letsencrypt]').
that_comes_before('Class[letsencrypt::renew]')
- is_expected.to contain_exec('initialize letsencrypt')
+ is_expected.to contain_exec('initialize letsencrypt').with_command('certbot -h')
is_expected.to contain_class('letsencrypt::config').that_comes_before('Exec[initialize letsencrypt]')
is_expected.to contain_class('letsencrypt::renew').
with(pre_hook_commands: [],
post_hook_commands: [],
deploy_hook_commands: [],
additional_args: [],
cron_ensure: 'absent',
cron_monthday: ['*'])
is_expected.to contain_cron('letsencrypt-renew').with_ensure('absent')
if facts[:osfamily] == 'FreeBSD'
is_expected.to contain_ini_setting('/usr/local/etc/letsencrypt/cli.ini email foo@example.com')
is_expected.to contain_ini_setting('/usr/local/etc/letsencrypt/cli.ini server https://acme-v02.api.letsencrypt.org/directory')
is_expected.to contain_file('letsencrypt-renewal-hooks-puppet').
with(ensure: 'directory',
path: '/usr/local/etc/letsencrypt/renewal-hooks-puppet',
owner: 'root',
group: 'root',
mode: '0755',
recurse: true,
purge: true)
else
is_expected.to contain_ini_setting('/etc/letsencrypt/cli.ini email foo@example.com')
is_expected.to contain_ini_setting('/etc/letsencrypt/cli.ini server https://acme-v02.api.letsencrypt.org/directory')
is_expected.to contain_file('letsencrypt-renewal-hooks-puppet').with_path('/etc/letsencrypt/renewal-hooks-puppet')
end
if facts[:osfamily] == 'RedHat'
if epel
is_expected.to contain_class('epel').that_comes_before('Package[letsencrypt]')
else
is_expected.not_to contain_class('epel')
end
- is_expected.to contain_class('letsencrypt::install').with(install_method: 'package').with(package_name: 'certbot')
+ is_expected.to contain_class('letsencrypt::install').with(package_name: 'certbot')
is_expected.to contain_class('letsencrypt').with(package_command: 'certbot')
is_expected.to contain_package('letsencrypt').with(name: 'certbot')
is_expected.to contain_file('/etc/letsencrypt').with(ensure: 'directory')
elsif facts[:osfamily] == 'Debian'
- is_expected.to contain_class('letsencrypt::install').with(install_method: 'package').with(package_name: 'certbot')
+ is_expected.to contain_class('letsencrypt::install').with(package_name: 'certbot')
is_expected.to contain_file('/etc/letsencrypt').with(ensure: 'directory')
elsif facts[:operatingsystem] == 'Gentoo'
- is_expected.to contain_class('letsencrypt::install').with(install_method: 'package').with(package_name: 'app-crypt/certbot')
+ is_expected.to contain_class('letsencrypt::install').with(package_name: 'app-crypt/certbot')
is_expected.to contain_class('letsencrypt').with(package_command: 'certbot')
is_expected.to contain_package('letsencrypt').with(name: 'app-crypt/certbot')
is_expected.to contain_file('/etc/letsencrypt').with(ensure: 'directory')
elsif facts[:operatingsystem] == 'OpenBSD'
- is_expected.to contain_class('letsencrypt::install').with(install_method: 'package').with(package_name: 'certbot')
+ is_expected.to contain_class('letsencrypt::install').with(package_name: 'certbot')
is_expected.to contain_class('letsencrypt').with(package_command: 'certbot')
is_expected.to contain_package('letsencrypt').with(name: 'certbot')
is_expected.to contain_file('/etc/letsencrypt').with(ensure: 'directory')
elsif facts[:operatingsystem] == 'FreeBSD'
- is_expected.to contain_class('letsencrypt::install').with(install_method: 'package').with(package_name: 'py27-certbot')
+ is_expected.to contain_class('letsencrypt::install').with(package_name: 'py27-certbot')
is_expected.to contain_class('letsencrypt').with(package_command: 'certbot')
is_expected.to contain_package('letsencrypt').with(name: 'py27-certbot')
is_expected.to contain_file('/usr/local/etc/letsencrypt').with(ensure: 'directory')
else
- is_expected.to contain_class('letsencrypt::install').with(install_method: 'vcs')
+ is_expected.to contain_class('letsencrypt::install')
is_expected.to contain_file('/etc/letsencrypt').with(ensure: 'directory')
end
end
end # describe 'with defaults'
- describe 'with custom path' do
- let(:additional_params) { { path: '/usr/lib/letsencrypt', install_method: 'vcs' } }
-
- it { is_expected.to contain_class('letsencrypt::install').with_path('/usr/lib/letsencrypt') }
- it { is_expected.to contain_exec('initialize letsencrypt').with_command('/usr/lib/letsencrypt/letsencrypt-auto -h') }
- end
-
describe 'with custom environment variables' do
let(:additional_params) { { environment: ['FOO=bar', 'FIZZ=buzz'] } }
- it { is_expected.to contain_exec('initialize letsencrypt').with_environment(['VENV_PATH=/opt/letsencrypt/.venv', 'FOO=bar', 'FIZZ=buzz']) }
- end
-
- describe 'with custom repo' do
- let(:additional_params) { { repo: 'git://foo.com/letsencrypt.git' } }
-
- it { is_expected.to contain_class('letsencrypt::install').with_repo('git://foo.com/letsencrypt.git') }
- end
-
- describe 'with custom version' do
- let(:additional_params) { { version: 'foo' } }
-
- it { is_expected.to contain_class('letsencrypt::install').with_path('/opt/letsencrypt').with_version('foo') }
+ it { is_expected.to contain_exec('initialize letsencrypt').with_environment(['FOO=bar', 'FIZZ=buzz']) }
end
describe 'with custom package_ensure' do
let(:additional_params) { { package_ensure: '0.3.0-1.el7' } }
it { is_expected.to contain_class('letsencrypt::install').with_package_ensure('0.3.0-1.el7') }
end
describe 'with custom config file' do
let(:additional_params) { { config_file: '/etc/letsencrypt/custom_config.ini' } }
it { is_expected.to contain_ini_setting('/etc/letsencrypt/custom_config.ini server https://acme-v02.api.letsencrypt.org/directory') }
end
describe 'with custom config' do
let(:additional_params) { { config: { 'foo' => 'bar' } } }
case facts[:operatingsystem]
when 'FreeBSD'
it { is_expected.to contain_ini_setting('/usr/local/etc/letsencrypt/cli.ini foo bar') }
else
it { is_expected.to contain_ini_setting('/etc/letsencrypt/cli.ini foo bar') }
end
end
describe 'with manage_config set to false' do
let(:additional_params) { { manage_config: false } }
it { is_expected.not_to contain_class('letsencrypt::config') }
end
describe 'with manage_install set to false' do
let(:additional_params) { { manage_install: false } }
it { is_expected.not_to contain_class('letsencrypt::install') }
end
- describe 'with install_method => package' do
- let(:additional_params) { { install_method: 'package', package_command: 'letsencrypt' } }
-
- it { is_expected.to contain_class('letsencrypt::install').with_install_method('package') }
- it { is_expected.to contain_exec('initialize letsencrypt').with_command('letsencrypt -h') }
- end
-
- describe 'with install_method => vcs' do
- let(:additional_params) { { install_method: 'vcs' } }
-
- it { is_expected.to contain_class('letsencrypt::install').with_install_method('vcs') }
- it { is_expected.to contain_exec('initialize letsencrypt').with_command('/opt/letsencrypt/letsencrypt-auto -h') }
- end
-
describe 'with custom config directory' do
let(:additional_params) { { config_dir: '/foo/bar/baz' } }
it { is_expected.to contain_file('/foo/bar/baz').with(ensure: 'directory') }
end
context 'when not agreeing to the TOS' do
let(:params) { { agree_tos: false } }
it { is_expected.to raise_error Puppet::Error, %r{You must agree to the Let's Encrypt Terms of Service} }
end
context 'with renew' do
describe 'pre hook' do
let(:additional_params) { { config_dir: '/etc/letsencrypt', renew_pre_hook_commands: ['FooBar'] } }
it { is_expected.to contain_letsencrypt__hook('renew-pre').with_hook_file('/etc/letsencrypt/renewal-hooks-puppet/renew-pre.sh') }
end
describe 'post hook' do
let(:additional_params) { { config_dir: '/etc/letsencrypt', renew_post_hook_commands: ['FooBar'] } }
it { is_expected.to contain_letsencrypt__hook('renew-post').with_hook_file('/etc/letsencrypt/renewal-hooks-puppet/renew-post.sh') }
end
describe 'deploy hook' do
let(:additional_params) { { config_dir: '/etc/letsencrypt', renew_deploy_hook_commands: ['FooBar'] } }
it { is_expected.to contain_letsencrypt__hook('renew-deploy').with_hook_file('/etc/letsencrypt/renewal-hooks-puppet/renew-deploy.sh') }
end
describe 'renew_cron_ensure' do
let(:additional_params) do
- { install_method: 'package',
- package_command: 'certbot',
- renew_cron_ensure: 'present',
+ { renew_cron_ensure: 'present',
renew_cron_hour: 0,
renew_cron_minute: 0 }
end
it do
is_expected.to contain_cron('letsencrypt-renew').
with(ensure: 'present',
command: 'certbot renew -q',
hour: 0,
minute: 0,
monthday: '*')
end
end
describe 'renew_cron_ensure and renew_cron_monthday' do
let(:additional_params) { { renew_cron_ensure: 'present', renew_cron_monthday: [1, 15] } }
it { is_expected.to contain_cron('letsencrypt-renew').with_ensure('present').with_monthday([1, 15]) }
end
describe 'renew_cron_ensure and hooks' do
let(:additional_params) do
{ config_dir: '/etc/letsencrypt',
- install_method: 'package',
- package_command: 'certbot',
renew_cron_ensure: 'present',
renew_pre_hook_commands: ['PreBar'],
renew_post_hook_commands: ['PostBar'],
renew_deploy_hook_commands: ['DeployBar'] }
end
it do
is_expected.to contain_cron('letsencrypt-renew').
with(ensure: 'present',
command: 'certbot renew -q --pre-hook "/etc/letsencrypt/renewal-hooks-puppet/renew-pre.sh" --post-hook "/etc/letsencrypt/renewal-hooks-puppet/renew-post.sh" --deploy-hook "/etc/letsencrypt/renewal-hooks-puppet/renew-deploy.sh"')
end
end
describe 'renew_cron_ensure and additional args' do
let(:additional_params) do
- { install_method: 'package',
- package_command: 'certbot',
- renew_cron_ensure: 'present',
+ { renew_cron_ensure: 'present',
renew_additional_args: ['AdditionalBar'] }
end
it do
is_expected.to contain_cron('letsencrypt-renew').
with(ensure: 'present',
command: 'certbot renew -q AdditionalBar')
end
end
end # context 'with renew'
end # context 'when specifying an email address with the email parameter'
context 'when specifying an email in $config' do
let(:params) { { config: { 'email' => 'foo@example.com' } } }
it { is_expected.to compile.with_all_deps }
case facts[:operatingsystem]
when 'FreeBSD'
it { is_expected.to contain_ini_setting('/usr/local/etc/letsencrypt/cli.ini email foo@example.com') }
else
it { is_expected.to contain_ini_setting('/etc/letsencrypt/cli.ini email foo@example.com') }
end
end
context 'when not specifying the email parameter or an email key in $config' do
context 'with unsafe_registration set to false' do
it { is_expected.to raise_error Puppet::Error, %r{Please specify an email address} }
end
context 'with unsafe_registration set to true' do
let(:params) { { unsafe_registration: true } }
case facts[:operatingsystem]
when 'FreeBSD'
it { is_expected.not_to contain_ini_setting('/usr/local/etc/letsencrypt/cli.ini email foo@example.com') }
it { is_expected.to contain_ini_setting('/usr/local/etc/letsencrypt/cli.ini register-unsafely-without-email true') }
else
it { is_expected.not_to contain_ini_setting('/etc/letsencrypt/cli.ini email foo@example.com') }
it { is_expected.to contain_ini_setting('/etc/letsencrypt/cli.ini register-unsafely-without-email true') }
end
end
end
end
end
end
diff --git a/spec/defines/letsencrypt_certonly_spec.rb b/spec/defines/letsencrypt_certonly_spec.rb
index 63028b7..b4e61bc 100644
--- a/spec/defines/letsencrypt_certonly_spec.rb
+++ b/spec/defines/letsencrypt_certonly_spec.rb
@@ -1,461 +1,461 @@
require 'spec_helper'
describe 'letsencrypt::certonly' do
on_supported_os.each do |os, facts|
context "on #{os} based operating systems" do
let :facts do
facts
end
let(:pre_condition) { "class { letsencrypt: email => 'foo@example.com', package_command => 'letsencrypt' }" }
# FreeBSD uses a different filesystem path
pathprefix = facts[:kernel] == 'FreeBSD' ? '/usr/local' : ''
context 'with a single domain' do
let(:title) { 'foo.example.com' }
it { is_expected.to compile.with_all_deps }
it { is_expected.to contain_class('Letsencrypt::Install') }
it { is_expected.to contain_class('Letsencrypt::Config') }
if facts[:osfamily] == 'FreeBSD'
it { is_expected.to contain_file('/usr/local/etc/letsencrypt') }
it { is_expected.to contain_ini_setting('/usr/local/etc/letsencrypt/cli.ini email foo@example.com') }
it { is_expected.to contain_ini_setting('/usr/local/etc/letsencrypt/cli.ini server https://acme-v02.api.letsencrypt.org/directory') }
else
it { is_expected.to contain_file('/etc/letsencrypt') }
it { is_expected.to contain_package('letsencrypt') }
it { is_expected.to contain_ini_setting('/etc/letsencrypt/cli.ini email foo@example.com') }
it { is_expected.to contain_ini_setting('/etc/letsencrypt/cli.ini server https://acme-v02.api.letsencrypt.org/directory') }
end
it { is_expected.to contain_exec('initialize letsencrypt') }
it { is_expected.to contain_exec('letsencrypt certonly foo.example.com') }
it { is_expected.to contain_exec('letsencrypt certonly foo.example.com').with_unless "/usr/local/sbin/letsencrypt-domain-validation #{pathprefix}/etc/letsencrypt/live/foo.example.com/cert.pem 'foo.example.com'" }
end
context 'with ensure absent' do
let(:title) { 'foo.example.com' }
let(:params) { { ensure: 'absent' } }
it { is_expected.to contain_exec('letsencrypt certonly foo.example.com') }
it { is_expected.to contain_exec('letsencrypt certonly foo.example.com').with_command "letsencrypt --text --agree-tos --non-interactive delete --cert-name 'foo.example.com'" }
it { is_expected.to contain_exec('letsencrypt certonly foo.example.com').with_onlyif "/usr/local/sbin/letsencrypt-domain-validation #{pathprefix}/etc/letsencrypt/live/foo.example.com/cert.pem 'foo.example.com'" }
end
context 'with multiple domains' do
let(:title) { 'foo' }
let(:params) { { domains: ['foo.example.com', 'bar.example.com', '*.example.com'] } }
it { is_expected.to compile.with_all_deps }
it { is_expected.to contain_exec('letsencrypt certonly foo').with_command "letsencrypt --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a standalone --cert-name 'foo' -d 'foo.example.com' -d 'bar.example.com' -d '*.example.com'" }
end
context 'with custom cert-name' do
let(:title) { 'foo' }
let(:params) { { cert_name: 'bar.example.com' } }
it { is_expected.to compile.with_all_deps }
it { is_expected.to contain_exec('letsencrypt certonly foo').with_command "letsencrypt --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a standalone --cert-name 'bar.example.com' -d 'foo'" }
end
context 'with custom command' do
let(:title) { 'foo.example.com' }
let(:params) { { letsencrypt_command: '/usr/lib/letsencrypt/letsencrypt-auto' } }
it { is_expected.to compile.with_all_deps }
it { is_expected.to contain_exec('letsencrypt certonly foo.example.com').with_command '/usr/lib/letsencrypt/letsencrypt-auto --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a standalone --cert-name \'foo.example.com\' -d \'foo.example.com\'' }
end
context 'with webroot plugin' do
let(:title) { 'foo.example.com' }
let(:params) do
{ plugin: 'webroot',
webroot_paths: ['/var/www/foo'] }
end
it { is_expected.to compile.with_all_deps }
it { is_expected.to contain_exec('letsencrypt certonly foo.example.com').with_command "letsencrypt --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a webroot --cert-name 'foo.example.com' --webroot-path /var/www/foo -d 'foo.example.com'" }
end
context 'with webroot plugin and multiple domains' do
let(:title) { 'foo' }
let(:params) do
{ domains: ['foo.example.com', 'bar.example.com'],
plugin: 'webroot',
webroot_paths: ['/var/www/foo', '/var/www/bar'] }
end
it { is_expected.to compile.with_all_deps }
it { is_expected.to contain_exec('letsencrypt certonly foo').with_command "letsencrypt --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a webroot --cert-name 'foo' --webroot-path /var/www/foo -d 'foo.example.com' --webroot-path /var/www/bar -d 'bar.example.com'" }
end
context 'with webroot plugin, one webroot, and multiple domains' do
let(:title) { 'foo' }
let(:params) do
{ domains: ['foo.example.com', 'bar.example.com'],
plugin: 'webroot',
webroot_paths: ['/var/www/foo'] }
end
it { is_expected.to compile.with_all_deps }
it { is_expected.to contain_exec('letsencrypt certonly foo').with_command "letsencrypt --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a webroot --cert-name 'foo' --webroot-path /var/www/foo -d 'foo.example.com' -d 'bar.example.com'" }
end
context 'with webroot plugin and no webroot_paths' do
let(:title) { 'foo.example.com' }
let(:params) { { plugin: 'webroot' } }
it { is_expected.not_to compile.with_all_deps }
it { is_expected.to raise_error Puppet::Error, %r{'webroot_paths' parameter must be specified} }
end
context 'with dns-rfc2136 plugin' do
let(:title) { 'foo.example.com' }
let(:params) { { plugin: 'dns-rfc2136', letsencrypt_command: 'letsencrypt' } }
let(:pre_condition) do
<<-PUPPET
class { 'letsencrypt':
email => 'foo@example.com',
config_dir => '/etc/letsencrypt',
}
class { 'letsencrypt::plugin::dns_rfc2136':
server => '192.0.2.1',
key_name => 'certbot',
key_secret => 'secret',
package_name => 'irrelevant',
}
PUPPET
end
it { is_expected.to compile.with_all_deps }
it { is_expected.to contain_class('letsencrypt::plugin::dns_rfc2136') }
it { is_expected.to contain_exec('letsencrypt certonly foo.example.com').with_command "letsencrypt --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a dns-rfc2136 --cert-name 'foo.example.com' -d 'foo.example.com' --dns-rfc2136-credentials /etc/letsencrypt/dns-rfc2136.ini --dns-rfc2136-propagation-seconds 10" }
end
context 'with dns-route53 plugin' do
let(:title) { 'foo.example.com' }
let(:params) { { plugin: 'dns-route53', letsencrypt_command: 'letsencrypt' } }
let(:pre_condition) do
<<-PUPPET
class { 'letsencrypt':
email => 'foo@example.com',
config_dir => '/etc/letsencrypt',
}
class { 'letsencrypt::plugin::dns_route53':
package_name => 'irrelevant',
}
PUPPET
end
it { is_expected.to compile.with_all_deps }
it { is_expected.to contain_class('letsencrypt::plugin::dns_route53') }
it { is_expected.to contain_exec('letsencrypt certonly foo.example.com').with_command "letsencrypt --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a dns-route53 --cert-name 'foo.example.com' -d 'foo.example.com' --dns-route53-propagation-seconds 10" }
end
context 'with custom plugin' do
let(:title) { 'foo.example.com' }
let(:params) { { plugin: 'apache' } }
it { is_expected.to compile.with_all_deps }
it { is_expected.to contain_exec('letsencrypt certonly foo.example.com').with_command "letsencrypt --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a apache --cert-name 'foo.example.com' -d 'foo.example.com'" }
end
context 'with custom plugin and manage_cron' do
let(:title) { 'foo.example.com' }
let(:params) do
{
plugin: 'apache',
manage_cron: true
}
end
it { is_expected.to compile.with_all_deps }
it { is_expected.to contain_cron('letsencrypt renew cron foo.example.com').with_command('"/var/lib/puppet/letsencrypt/renew-foo.example.com.sh"').with_ensure('present') }
- it { is_expected.to contain_file('/var/lib/puppet/letsencrypt/renew-foo.example.com.sh').with_ensure('file').with_content("#!/bin/sh\nexport VENV_PATH=/opt/letsencrypt/.venv\nletsencrypt --keep-until-expiring --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a apache --cert-name 'foo.example.com' -d 'foo.example.com'\n") }
+ it { is_expected.to contain_file('/var/lib/puppet/letsencrypt/renew-foo.example.com.sh').with_ensure('file').with_content("#!/bin/sh\nletsencrypt --keep-until-expiring --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a apache --cert-name 'foo.example.com' -d 'foo.example.com'\n") }
end
context 'with hook' do
context 'pre' do
let(:title) { 'foo.example.com' }
let(:params) { { config_dir: '/etc/letsencrypt', pre_hook_commands: ['FooBar'] } }
it do
is_expected.to compile.with_all_deps
is_expected.to contain_letsencrypt__hook('foo.example.com-pre').with_hook_file('/etc/letsencrypt/renewal-hooks-puppet/foo.example.com-pre.sh')
end
end
context 'pre with wildcard domain' do
let(:title) { '*.example.com' }
let(:params) { { config_dir: '/etc/letsencrypt', pre_hook_commands: ['FooBar'] } }
it do
is_expected.to compile.with_all_deps
is_expected.to contain_letsencrypt__hook('*.example.com-pre').with_hook_file('/etc/letsencrypt/renewal-hooks-puppet/example.com-pre.sh')
end
end
context 'post' do
let(:title) { 'foo.example.com' }
let(:params) { { config_dir: '/etc/letsencrypt', post_hook_commands: ['FooBar'] } }
it do
is_expected.to compile.with_all_deps
is_expected.to contain_letsencrypt__hook('foo.example.com-post').with_hook_file('/etc/letsencrypt/renewal-hooks-puppet/foo.example.com-post.sh')
end
end
context 'deploy' do
let(:title) { 'foo.example.com' }
let(:params) { { config_dir: '/etc/letsencrypt', deploy_hook_commands: ['FooBar'] } }
it do
is_expected.to compile.with_all_deps
is_expected.to contain_letsencrypt__hook('foo.example.com-deploy').with_hook_file('/etc/letsencrypt/renewal-hooks-puppet/foo.example.com-deploy.sh')
end
end
end # context 'with hook'
context 'with manage_cron and defined cron_hour (integer)' do
let(:title) { 'foo.example.com' }
let(:params) do
{
cron_hour: 13,
manage_cron: true
}
end
it { is_expected.to compile.with_all_deps }
it { is_expected.to contain_cron('letsencrypt renew cron foo.example.com').with_hour(13).with_ensure('present') }
- it { is_expected.to contain_file('/var/lib/puppet/letsencrypt/renew-foo.example.com.sh').with_ensure('file').with_content("#!/bin/sh\nexport VENV_PATH=/opt/letsencrypt/.venv\nletsencrypt --keep-until-expiring --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a standalone --cert-name 'foo.example.com' -d 'foo.example.com'\n") }
+ it { is_expected.to contain_file('/var/lib/puppet/letsencrypt/renew-foo.example.com.sh').with_ensure('file').with_content("#!/bin/sh\nletsencrypt --keep-until-expiring --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a standalone --cert-name 'foo.example.com' -d 'foo.example.com'\n") }
end
context 'with manage_cron and out of range defined cron_hour (integer)' do
let(:title) { 'foo.example.com' }
let(:params) do
{
cron_hour: 24,
manage_cron: true
}
end
it { is_expected.not_to compile.with_all_deps }
it { is_expected.to raise_error Puppet::Error }
end
context 'with manage_cron and defined cron_hour (string)' do
let(:title) { 'foo.example.com' }
let(:params) do
{
cron_hour: '00',
manage_cron: true
}
end
it { is_expected.to compile.with_all_deps }
it { is_expected.to contain_cron('letsencrypt renew cron foo.example.com').with_hour('00').with_ensure('present') }
- it { is_expected.to contain_file('/var/lib/puppet/letsencrypt/renew-foo.example.com.sh').with_ensure('file').with_content("#!/bin/sh\nexport VENV_PATH=/opt/letsencrypt/.venv\nletsencrypt --keep-until-expiring --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a standalone --cert-name 'foo.example.com' -d 'foo.example.com'\n") }
+ it { is_expected.to contain_file('/var/lib/puppet/letsencrypt/renew-foo.example.com.sh').with_ensure('file').with_content("#!/bin/sh\nletsencrypt --keep-until-expiring --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a standalone --cert-name 'foo.example.com' -d 'foo.example.com'\n") }
end
context 'with manage_cron and defined cron_hour (array)' do
let(:title) { 'foo.example.com' }
let(:params) do
{
cron_hour: [1, 13],
manage_cron: true
}
end
it { is_expected.to compile.with_all_deps }
it { is_expected.to contain_cron('letsencrypt renew cron foo.example.com').with_hour([1, 13]).with_ensure('present') }
- it { is_expected.to contain_file('/var/lib/puppet/letsencrypt/renew-foo.example.com.sh').with_ensure('file').with_content("#!/bin/sh\nexport VENV_PATH=/opt/letsencrypt/.venv\nletsencrypt --keep-until-expiring --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a standalone --cert-name 'foo.example.com' -d 'foo.example.com'\n") }
+ it { is_expected.to contain_file('/var/lib/puppet/letsencrypt/renew-foo.example.com.sh').with_ensure('file').with_content("#!/bin/sh\nletsencrypt --keep-until-expiring --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a standalone --cert-name 'foo.example.com' -d 'foo.example.com'\n") }
end
context 'with manage_cron and defined cron_minute (integer)' do
let(:title) { 'foo.example.com' }
let(:params) do
{
cron_minute: 15,
manage_cron: true
}
end
it { is_expected.to compile.with_all_deps }
it { is_expected.to contain_cron('letsencrypt renew cron foo.example.com').with_minute(15).with_ensure('present') }
- it { is_expected.to contain_file('/var/lib/puppet/letsencrypt/renew-foo.example.com.sh').with_ensure('file').with_content("#!/bin/sh\nexport VENV_PATH=/opt/letsencrypt/.venv\nletsencrypt --keep-until-expiring --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a standalone --cert-name 'foo.example.com' -d 'foo.example.com'\n") }
+ it { is_expected.to contain_file('/var/lib/puppet/letsencrypt/renew-foo.example.com.sh').with_ensure('file').with_content("#!/bin/sh\nletsencrypt --keep-until-expiring --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a standalone --cert-name 'foo.example.com' -d 'foo.example.com'\n") }
end
context 'with manage_cron and out of range defined cron_hour (integer)' do
let(:title) { 'foo.example.com' }
let(:params) do
{
cron_hour: 66,
manage_cron: true
}
end
it { is_expected.not_to compile.with_all_deps }
it { is_expected.to raise_error Puppet::Error }
end
context 'with manage_cron and defined cron_minute (string)' do
let(:title) { 'foo.example.com' }
let(:params) do
{
cron_minute: '15',
manage_cron: true
}
end
it { is_expected.to compile.with_all_deps }
it { is_expected.to contain_cron('letsencrypt renew cron foo.example.com').with_minute('15').with_ensure('present') }
- it { is_expected.to contain_file('/var/lib/puppet/letsencrypt/renew-foo.example.com.sh').with_ensure('file').with_content("#!/bin/sh\nexport VENV_PATH=/opt/letsencrypt/.venv\nletsencrypt --keep-until-expiring --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a standalone --cert-name 'foo.example.com' -d 'foo.example.com'\n") }
+ it { is_expected.to contain_file('/var/lib/puppet/letsencrypt/renew-foo.example.com.sh').with_ensure('file').with_content("#!/bin/sh\nletsencrypt --keep-until-expiring --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a standalone --cert-name 'foo.example.com' -d 'foo.example.com'\n") }
end
context 'with manage_cron and defined cron_minute (array)' do
let(:title) { 'foo.example.com' }
let(:params) do
{
cron_minute: [0, 30],
manage_cron: true
}
end
it { is_expected.to compile.with_all_deps }
it { is_expected.to contain_cron('letsencrypt renew cron foo.example.com').with_minute([0, 30]).with_ensure('present') }
- it { is_expected.to contain_file('/var/lib/puppet/letsencrypt/renew-foo.example.com.sh').with_ensure('file').with_content("#!/bin/sh\nexport VENV_PATH=/opt/letsencrypt/.venv\nletsencrypt --keep-until-expiring --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a standalone --cert-name 'foo.example.com' -d 'foo.example.com'\n") }
+ it { is_expected.to contain_file('/var/lib/puppet/letsencrypt/renew-foo.example.com.sh').with_ensure('file').with_content("#!/bin/sh\nletsencrypt --keep-until-expiring --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a standalone --cert-name 'foo.example.com' -d 'foo.example.com'\n") }
end
context 'with manage_cron and ensure absent' do
let(:title) { 'foo.example.com' }
let(:params) do
{
ensure: 'absent',
manage_cron: true
}
end
it { is_expected.to compile.with_all_deps }
it { is_expected.to contain_cron('letsencrypt renew cron foo.example.com').with_ensure('absent') }
it { is_expected.to contain_file('/var/lib/puppet/letsencrypt/renew-foo.example.com.sh').with_ensure('absent') }
end
context 'with custom puppet_vardir path and manage_cron' do
let :facts do
super().merge(puppet_vardir: '/tmp/custom_vardir')
end
let(:title) { 'foo.example.com' }
let(:params) do
{ plugin: 'apache',
manage_cron: true }
end
it { is_expected.to compile.with_all_deps }
it { is_expected.to contain_file('/tmp/custom_vardir/letsencrypt').with_ensure('directory') }
it { is_expected.to contain_cron('letsencrypt renew cron foo.example.com').with_command '"/tmp/custom_vardir/letsencrypt/renew-foo.example.com.sh"' }
- it { is_expected.to contain_file('/tmp/custom_vardir/letsencrypt/renew-foo.example.com.sh').with_ensure('file').with_content("#!/bin/sh\nexport VENV_PATH=/opt/letsencrypt/.venv\nletsencrypt --keep-until-expiring --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a apache --cert-name 'foo.example.com' -d 'foo.example.com'\n") }
+ it { is_expected.to contain_file('/tmp/custom_vardir/letsencrypt/renew-foo.example.com.sh').with_ensure('file').with_content("#!/bin/sh\nletsencrypt --keep-until-expiring --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a apache --cert-name 'foo.example.com' -d 'foo.example.com'\n") }
end
context 'with custom plugin and manage cron and cron_success_command' do
let(:title) { 'foo.example.com' }
let(:params) do
{
plugin: 'apache',
manage_cron: true,
cron_before_command: 'echo before',
cron_success_command: 'echo success'
}
end
it { is_expected.to compile.with_all_deps }
it { is_expected.to contain_cron('letsencrypt renew cron foo.example.com').with_command '"/var/lib/puppet/letsencrypt/renew-foo.example.com.sh"' }
- it { is_expected.to contain_file('/var/lib/puppet/letsencrypt/renew-foo.example.com.sh').with_ensure('file').with_content("#!/bin/sh\nexport VENV_PATH=/opt/letsencrypt/.venv\n(echo before) && letsencrypt --keep-until-expiring --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a apache --cert-name 'foo.example.com' -d 'foo.example.com' && (echo success)\n") }
+ it { is_expected.to contain_file('/var/lib/puppet/letsencrypt/renew-foo.example.com.sh').with_ensure('file').with_content("#!/bin/sh\n(echo before) && letsencrypt --keep-until-expiring --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a apache --cert-name 'foo.example.com' -d 'foo.example.com' && (echo success)\n") }
end
context 'without plugin' do
let(:title) { 'foo.example.com' }
let(:params) { { custom_plugin: true } }
it { is_expected.to contain_exec('letsencrypt certonly foo.example.com').with_command "letsencrypt --text --agree-tos --non-interactive certonly --rsa-key-size 4096 --cert-name 'foo.example.com' -d 'foo.example.com'" }
end
context 'with invalid plugin' do
let(:title) { 'foo.example.com' }
let(:params) { { plugin: 'bad' } }
it { is_expected.not_to compile.with_all_deps }
it { is_expected.to raise_error Puppet::Error }
end
context 'when specifying additional arguments' do
let(:title) { 'foo.example.com' }
let(:params) { { additional_args: ['--foo bar', '--baz quux'] } }
it { is_expected.to compile.with_all_deps }
it { is_expected.to contain_exec('letsencrypt certonly foo.example.com').with_command "letsencrypt --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a standalone --cert-name 'foo.example.com' -d 'foo.example.com' --foo bar --baz quux" }
end
describe 'when specifying custom environment variables' do
let(:title) { 'foo.example.com' }
let(:params) { { environment: ['FOO=bar', 'FIZZ=buzz'] } }
it { is_expected.to compile.with_all_deps }
- it { is_expected.to contain_exec('letsencrypt certonly foo.example.com').with_environment(['VENV_PATH=/opt/letsencrypt/.venv', 'FOO=bar', 'FIZZ=buzz']) }
+ it { is_expected.to contain_exec('letsencrypt certonly foo.example.com').with_environment(['FOO=bar', 'FIZZ=buzz']) }
end
context 'with custom environment variables and manage_cron' do
let(:title) { 'foo.example.com' }
let(:params) { { environment: ['FOO=bar', 'FIZZ=buzz'], manage_cron: true } }
it { is_expected.to compile.with_all_deps }
- it { is_expected.to contain_file('/var/lib/puppet/letsencrypt/renew-foo.example.com.sh').with_content "#!/bin/sh\nexport VENV_PATH=/opt/letsencrypt/.venv\nexport FOO=bar\nexport FIZZ=buzz\nletsencrypt --keep-until-expiring --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a standalone --cert-name 'foo.example.com' -d 'foo.example.com'\n" }
+ it { is_expected.to contain_file('/var/lib/puppet/letsencrypt/renew-foo.example.com.sh').with_content "#!/bin/sh\nexport FOO=bar\nexport FIZZ=buzz\nletsencrypt --keep-until-expiring --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a standalone --cert-name 'foo.example.com' -d 'foo.example.com'\n" }
end
context 'with manage cron and suppress_cron_output' do\
let(:title) { 'foo.example.com' }
let(:params) do
{ manage_cron: true,
suppress_cron_output: true }
end
it { is_expected.to compile.with_all_deps }
it { is_expected.to contain_cron('letsencrypt renew cron foo.example.com').with_command('"/var/lib/puppet/letsencrypt/renew-foo.example.com.sh"').with_ensure('present') }
- it { is_expected.to contain_file('/var/lib/puppet/letsencrypt/renew-foo.example.com.sh').with_ensure('file').with_content("#!/bin/sh\nexport VENV_PATH=/opt/letsencrypt/.venv\nletsencrypt --keep-until-expiring --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a standalone --cert-name 'foo.example.com' -d 'foo.example.com' > /dev/null 2>&1\n") }
+ it { is_expected.to contain_file('/var/lib/puppet/letsencrypt/renew-foo.example.com.sh').with_ensure('file').with_content("#!/bin/sh\nletsencrypt --keep-until-expiring --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a standalone --cert-name 'foo.example.com' -d 'foo.example.com' > /dev/null 2>&1\n") }
end
context 'with manage cron and custom day of month' do
let(:title) { 'foo.example.com' }
let(:params) do
{ manage_cron: true,
cron_monthday: [1, 15] }
end
it { is_expected.to compile.with_all_deps }
it { is_expected.to contain_cron('letsencrypt renew cron foo.example.com').with(monthday: [1, 15]).with_ensure('present') }
- it { is_expected.to contain_file('/var/lib/puppet/letsencrypt/renew-foo.example.com.sh').with_ensure('file').with_content("#!/bin/sh\nexport VENV_PATH=/opt/letsencrypt/.venv\nletsencrypt --keep-until-expiring --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a standalone --cert-name 'foo.example.com' -d 'foo.example.com'\n") }
+ it { is_expected.to contain_file('/var/lib/puppet/letsencrypt/renew-foo.example.com.sh').with_ensure('file').with_content("#!/bin/sh\nletsencrypt --keep-until-expiring --text --agree-tos --non-interactive certonly --rsa-key-size 4096 -a standalone --cert-name 'foo.example.com' -d 'foo.example.com'\n") }
end
context 'with custom config_dir' do
let(:title) { 'foo.example.com' }
let(:pre_condition) { "class { letsencrypt: email => 'foo@example.com', config_dir => '/foo/bar/baz', package_command => 'letsencrypt'}" }
it { is_expected.to compile.with_all_deps }
it { is_expected.to contain_file('/foo/bar/baz').with_ensure('directory') }
it { is_expected.to contain_exec('letsencrypt certonly foo.example.com').with_unless '/usr/local/sbin/letsencrypt-domain-validation /foo/bar/baz/live/foo.example.com/cert.pem \'foo.example.com\'' }
end
context 'on FreeBSD', if: facts[:os]['name'] == 'FreeBSD' do
let(:title) { 'foo.example.com' }
let(:pre_condition) { "class { letsencrypt: email => 'foo@example.com'}" }
it { is_expected.to compile.with_all_deps }
it { is_expected.to contain_exec('letsencrypt certonly foo.example.com').with_command %r{^certbot} }
it { is_expected.to contain_ini_setting('/usr/local/etc/letsencrypt/cli.ini email foo@example.com') }
it { is_expected.to contain_ini_setting('/usr/local/etc/letsencrypt/cli.ini server https://acme-v02.api.letsencrypt.org/directory') }
it { is_expected.to contain_file('/usr/local/etc/letsencrypt').with_ensure('directory') }
it { is_expected.to contain_exec('letsencrypt certonly foo.example.com').with_unless '/usr/local/sbin/letsencrypt-domain-validation /usr/local/etc/letsencrypt/live/foo.example.com/cert.pem \'foo.example.com\'' }
end
end
end
end
diff --git a/templates/renew-script.sh.erb b/templates/renew-script.sh.erb
index 8ff4b80..a77cde5 100644
--- a/templates/renew-script.sh.erb
+++ b/templates/renew-script.sh.erb
@@ -1,5 +1,5 @@
#!/bin/sh
-<%- @execution_environment.each do |environment| -%>
+<%- @environment.each do |environment| -%>
export <%= environment %>
<%- end -%>
<%= @cron_cmd %>