From db5aca4e6a3a0c9c6f7a0f9b3e0c66caa7e64bb9 Mon Sep 17 00:00:00 2001 From: Alexander Weidinger Date: Fri, 24 May 2019 10:57:27 +0200 Subject: [PATCH] ci(travis): test based on template-formula and #98 --- .kitchen.yml | 38 ------ .travis.yml | 83 ++++++++++++++ Gemfile | 6 + commitlint.config.js | 3 + kitchen.yml | 108 ++++++++++++++++++ pillar.example | 1 + release-rules.js | 18 +++ release.config.js | 106 +++++++++++++++++ test/config/pillars.sls | 5 - test/config/testinfra/test_config.py | 15 --- test/conftest.py | 31 ----- .../default/controls/config_spec.rb | 39 +++++++ .../default/controls/packages_spec.rb | 7 ++ .../default/controls/services_spec.rb | 37 ++++++ test/integration/default/inspec.yml | 12 ++ 15 files changed, 420 insertions(+), 89 deletions(-) delete mode 100644 .kitchen.yml create mode 100644 .travis.yml create mode 100644 Gemfile create mode 100644 commitlint.config.js create mode 100644 kitchen.yml create mode 100644 release-rules.js create mode 100644 release.config.js delete mode 100644 test/config/testinfra/test_config.py delete mode 100644 test/conftest.py create mode 100644 test/integration/default/controls/config_spec.rb create mode 100644 test/integration/default/controls/packages_spec.rb create mode 100644 test/integration/default/controls/services_spec.rb create mode 100644 test/integration/default/inspec.yml diff --git a/.kitchen.yml b/.kitchen.yml deleted file mode 100644 index 6adfb59..0000000 --- a/.kitchen.yml +++ /dev/null @@ -1,38 +0,0 @@ ---- -driver: - name: vagrant - -platforms: - - name: bento/debian-9 - - name: bento/debian-8 - - name: bento/ubuntu-18.04 - - name: bento/centos-7 - -provisioner: - salt_version: latest - name: salt_solo - formula: openvpn - require_chef: false - pillars: - top.sls: - base: - '*': - - test - -suites: - - name: config - provisioner: - pillars_from_files: - test.sls: test/config/pillars.sls - state_top: - base: - '*': - - openvpn.config - -lifecycle: - post_converge: - - local: sleep 10 - -verifier: - name: shell - command: py.test -vvv --junit-xml junit-$KITCHEN_INSTANCE.xml test/$KITCHEN_SUITE diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..588016b --- /dev/null +++ b/.travis.yml @@ -0,0 +1,83 @@ +stages: + - test + - commitlint + - name: release + if: branch = master AND type != pull_request + +sudo: required +cache: bundler +language: ruby + +services: + - docker + +# Make sure the instances listed below match up with +# the `platforms` defined in `kitchen.yml` +# NOTE: Please try to select up to six instances that add some meaningful +# testing of the formula's behaviour. If possible, try to refrain from +# the classical "chosing all the instances because I want to test on +# another/all distro/s" trap: it will just add time to the testing (see +# the discussion on #121). As an example, the set chosen below covers +# the most used distros families, systemd and non-systemd and the latest +# three supported Saltstack versions with python2 and 3." +# As for `kitchen.yml`, that should still contain all of the platforms, +# to allow for comprehensive local testing +# Ref: https://github.com/saltstack-formulas/template-formula/issues/118 +# Ref: https://github.com/saltstack-formulas/template-formula/issues/121 +env: + matrix: + - INSTANCE: default-debian-9-2019-2-py3 + # - INSTANCE: default-ubuntu-1804-2019-2-py3 + - INSTANCE: default-centos-7-2019-2-py3 + # - INSTANCE: default-fedora-29-2019-2-py3 + - INSTANCE: default-opensuse-leap-15-2019-2-py3 + # - INSTANCE: default-debian-9-2018-3-py2 + - INSTANCE: default-ubuntu-1604-2018-3-py2 + # - INSTANCE: default-centos-7-2018-3-py2 + - INSTANCE: default-fedora-29-2018-3-py2 + # TODO: Use this when fixed instead of `opensuse-leap-42` + # Ref: https://github.com/netmanagers/salt-image-builder/issues/2 + # - INSTANCE: default-opensuse-leap-15-2018-3-py2 + # - INSTANCE: default-opensuse-leap-42-2018-3-py2 + # - INSTANCE: default-debian-8-2017-7-py2 + # - INSTANCE: default-ubuntu-1604-2017-7-py2 + # TODO: Enable after improving the formula to work with other than `systemd` + - INSTANCE: default-centos-6-2017-7-py2 + # - INSTANCE: default-fedora-28-2017-7-py2 + # - INSTANCE: default-opensuse-leap-42-2017-7-py2 + +script: + - bundle exec kitchen verify ${INSTANCE} + +jobs: + include: + # Define the commitlint stage + - stage: commitlint + language: node_js + node_js: lts/* + before_install: skip + script: + - npm install @commitlint/config-conventional -D + - npm install @commitlint/travis-cli -D + - commitlint-travis + # Define the release stage that runs semantic-release + - stage: release + language: node_js + node_js: lts/* + before_install: skip + script: + # Update `AUTHORS.md` + - export MAINTAINER_TOKEN=${GH_TOKEN} + - go get github.com/myii/maintainer + - maintainer contributor + + # Install all dependencies required for `semantic-release` + - npm install @semantic-release/changelog@3 -D + - npm install @semantic-release/exec@3 -D + - npm install @semantic-release/git@7 -D + deploy: + provider: script + skip_cleanup: true + script: + # Run `semantic-release` + - npx semantic-release@15 diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..3b36de3 --- /dev/null +++ b/Gemfile @@ -0,0 +1,6 @@ +source "https://rubygems.org" + +gem 'kitchen-docker', '>= 2.9' +gem 'kitchen-salt', '>= 0.6.0' +gem 'kitchen-inspec', '>= 1.1' + diff --git a/commitlint.config.js b/commitlint.config.js new file mode 100644 index 0000000..2f9d1aa --- /dev/null +++ b/commitlint.config.js @@ -0,0 +1,3 @@ +module.exports = { + extends: ['@commitlint/config-conventional'], +}; diff --git a/kitchen.yml b/kitchen.yml new file mode 100644 index 0000000..79db621 --- /dev/null +++ b/kitchen.yml @@ -0,0 +1,108 @@ +# -*- coding: utf-8 -*- +# vim: ft=yaml +--- +# For help on this file's format, see https://kitchen.ci/ +driver: + name: docker + use_sudo: false + privileged: true + run_command: /lib/systemd/systemd + +# Make sure the platforms listed below match up with +# the `env.matrix` instances defined in `.travis.yml` +platforms: + ## SALT 2019.2 + - name: debian-9-2019-2-py3 + driver: + image: netmanagers/salt-2019.2-py3:debian-9 + - name: ubuntu-1804-2019-2-py3 + driver: + image: netmanagers/salt-2019.2-py3:ubuntu-18.04 + - name: centos-7-2019-2-py3 + driver: + image: netmanagers/salt-2019.2-py3:centos-7 + - name: fedora-29-2019-2-py3 + driver: + image: netmanagers/salt-2019.2-py3:fedora-29 + - name: opensuse-leap-15-2019-2-py3 + driver: + image: netmanagers/salt-2019.2-py3:opensuse-leap-15 + run_command: /usr/lib/systemd/systemd + + ## SALT 2018.3 + - name: debian-9-2018-3-py2 + driver: + image: netmanagers/salt-2018.3-py2:debian-9 + - name: ubuntu-1604-2018-3-py2 + driver: + image: netmanagers/salt-2018.3-py2:ubuntu-16.04 + - name: centos-7-2018-3-py2 + driver: + image: netmanagers/salt-2018.3-py2:centos-7 + - name: fedora-29-2018-3-py2 + driver: + image: netmanagers/salt-2018.3-py2:fedora-29 + # TODO: Use this when fixed instead of `opensuse-leap-42` + # Ref: https://github.com/netmanagers/salt-image-builder/issues/2 + # - name: opensuse-leap-15-2018-3-py2 + # driver: + # image: netmanagers/salt-2018.3-py2:opensuse-leap-15 + # run_command: /usr/lib/systemd/systemd + - name: opensuse-leap-42-2018-3-py2 + driver: + image: netmanagers/salt-2018.3-py2:opensuse-leap-42 + run_command: /usr/lib/systemd/systemd + + ## SALT 2017.7 + - name: debian-8-2017-7-py2 + driver: + image: netmanagers/salt-2017.7-py2:debian-8 + - name: ubuntu-1604-2017-7-py2 + driver: + image: netmanagers/salt-2017.7-py2:ubuntu-16.04 + # TODO: Modify the formula to work for non-`systemd` platforms + - name: centos-6-2017-7-py2 + driver: + image: netmanagers/salt-2017.7-py2:centos-6 + run_command: /sbin/init + - name: fedora-28-2017-7-py2 + driver: + image: netmanagers/salt-2017.7-py2:fedora-28 + - name: opensuse-leap-42-2017-7-py2 + driver: + image: netmanagers/salt-2017.7-py2:opensuse-leap-42 + run_command: /usr/lib/systemd/systemd + +provisioner: + name: salt_solo + log_level: info + salt_install: none + require_chef: false + formula: openvpn + salt_copy_filter: + - .kitchen + - .git + state_top: + base: + '*': + - openvpn.config + pillars: + top.sls: + base: + '*': + - openvpn + pillars_from_files: + openvpn.sls: test/config/pillars.sls + +verifier: + # https://www.inspec.io/ + name: inspec + sudo: true + # cli, documentation, html, progress, json, json-min, json-rspec, junit + reporter: + - cli + inspec_tests: + - path: test/integration/default + +suites: + - name: default diff --git a/pillar.example b/pillar.example index b92fc82..43cb8b5 100644 --- a/pillar.example +++ b/pillar.example @@ -23,6 +23,7 @@ # # Info here: https://community.openvpn.net/openvpn/wiki/OpenvpnSoftwareRepos # # Valid options: stable (default), testing, release/2.3, release/2.4 +See also the example used in tests: test/config/pillars.sls ## # OpenVPN user and group diff --git a/release-rules.js b/release-rules.js new file mode 100644 index 0000000..c63c850 --- /dev/null +++ b/release-rules.js @@ -0,0 +1,18 @@ +// No release is triggered for the types commented out below. +// Commits using these types will be incorporated into the next release. +// +// NOTE: Any changes here must be reflected in `CONTRIBUTING.md`. +module.exports = [ + {breaking: true, release: 'major'}, + // {type: 'build', release: 'patch'}, + // {type: 'chore', release: 'patch'}, + // {type: 'ci', release: 'patch'}, + {type: 'docs', release: 'patch'}, + {type: 'feat', release: 'minor'}, + {type: 'fix', release: 'patch'}, + {type: 'perf', release: 'patch'}, + {type: 'refactor', release: 'patch'}, + {type: 'revert', release: 'patch'}, + {type: 'style', release: 'patch'}, + {type: 'test', release: 'patch'}, +]; diff --git a/release.config.js b/release.config.js new file mode 100644 index 0000000..afa0cb1 --- /dev/null +++ b/release.config.js @@ -0,0 +1,106 @@ +module.exports = { + branch: 'master', + plugins: [ + ['@semantic-release/commit-analyzer', { + preset: 'angular', + releaseRules: './release-rules.js', + }], + '@semantic-release/release-notes-generator', + ['@semantic-release/changelog', { + changelogFile: 'CHANGELOG.md', + changelogTitle: '# Changelog', + }], + ['@semantic-release/exec', { + prepareCmd: 'sh ./pre-commit_semantic-release.sh ${nextRelease.version}', + }], + ['@semantic-release/git', { + assets: ['*.md', 'docs/*.rst', 'FORMULA'], + }], + '@semantic-release/github', + ], + generateNotes: { + preset: 'angular', + writerOpts: { + // Required due to upstream bug preventing all types being displayed. + // Bug: https://github.com/conventional-changelog/conventional-changelog/issues/317 + // Fix: https://github.com/conventional-changelog/conventional-changelog/pull/410 + transform: (commit, context) => { + const issues = [] + + commit.notes.forEach(note => { + note.title = `BREAKING CHANGES` + }) + + // NOTE: Any changes here must be reflected in `CONTRIBUTING.md`. + if (commit.type === `feat`) { + commit.type = `Features` + } else if (commit.type === `fix`) { + commit.type = `Bug Fixes` + } else if (commit.type === `perf`) { + commit.type = `Performance Improvements` + } else if (commit.type === `revert`) { + commit.type = `Reverts` + } else if (commit.type === `docs`) { + commit.type = `Documentation` + } else if (commit.type === `style`) { + commit.type = `Styles` + } else if (commit.type === `refactor`) { + commit.type = `Code Refactoring` + } else if (commit.type === `test`) { + commit.type = `Tests` + } else if (commit.type === `build`) { + commit.type = `Build System` + // } else if (commit.type === `chore`) { + // commit.type = `Maintenance` + } else if (commit.type === `ci`) { + commit.type = `Continuous Integration` + } else { + return + } + + if (commit.scope === `*`) { + commit.scope = `` + } + + if (typeof commit.hash === `string`) { + commit.hash = commit.hash.substring(0, 7) + } + + if (typeof commit.subject === `string`) { + let url = context.repository + ? `${context.host}/${context.owner}/${context.repository}` + : context.repoUrl + if (url) { + url = `${url}/issues/` + // Issue URLs. + commit.subject = commit.subject.replace(/#([0-9]+)/g, (_, issue) => { + issues.push(issue) + return `[#${issue}](${url}${issue})` + }) + } + if (context.host) { + // User URLs. + commit.subject = commit.subject.replace(/\B@([a-z0-9](?:-?[a-z0-9/]){0,38})/g, (_, username) => { + if (username.includes('/')) { + return `@${username}` + } + + return `[@${username}](${context.host}/${username})` + }) + } + } + + // remove references that already appear in the subject + commit.references = commit.references.filter(reference => { + if (issues.indexOf(reference.issue) === -1) { + return true + } + + return false + }) + + return commit + }, + }, + }, +}; diff --git a/test/config/pillars.sls b/test/config/pillars.sls index b94d381..255eeba 100644 --- a/test/config/pillars.sls +++ b/test/config/pillars.sls @@ -1,12 +1,7 @@ openvpn: lookup: - {% if ( grains.os == "Debian" and grains.osmajorrelease >= 9 ) %} - user: root - group: root - {% else %} user: openvpn group: openvpn - {% endif %} manage_user: True manage_group: True external_repo_enabled: True diff --git a/test/config/testinfra/test_config.py b/test/config/testinfra/test_config.py deleted file mode 100644 index e8638d3..0000000 --- a/test/config/testinfra/test_config.py +++ /dev/null @@ -1,15 +0,0 @@ -def test_myserver1_service(host): - service = host.service("openvpn@myserver1.service") - assert service.is_running - assert service.is_enabled - -def test_myserver1_log(host): - assert host.file("/var/log/openvpn/myserver1.log").contains("Initialization Sequence Completed") - -def test_myclient1_service(host): - service = host.service("openvpn@myclient1.service") - assert service.is_running - assert service.is_enabled - -def test_myclient1_log(host): - assert host.file("/var/log/openvpn/myclient1.log").contains("Initialization Sequence Completed") diff --git a/test/conftest.py b/test/conftest.py deleted file mode 100644 index 374f7e3..0000000 --- a/test/conftest.py +++ /dev/null @@ -1,31 +0,0 @@ -import pytest -import testinfra -from testinfra.backend.base import BaseBackend -from testinfra.backend import parse_hostspec -import os - -SSH_CONFIG = '.ssh-config' -SSH_CONFIG_MAP = { - 'KITCHEN_HOSTNAME': 'Hostname', - 'KITCHEN_USERNAME': 'User', - 'KITCHEN_PORT': 'Port', - 'KITCHEN_SSH_KEY': 'IdentityFile', -} - -@pytest.fixture -def host(request, tmpdir_factory): - # Override the TestinfraBackend fixture, - # all testinfra fixtures (i.e. modules) depend on it. - tmpdir = tmpdir_factory.mktemp(str(id(request))) - image, kw = parse_hostspec(os.environ['KITCHEN_INSTANCE']) - ssh_config = ['Host {0}'.format(os.environ['KITCHEN_INSTANCE'])] - for key in SSH_CONFIG_MAP.keys(): - if key in os.environ: - ssh_config.append('{0} {1}'.format(SSH_CONFIG_MAP[key], os.environ[key])) - ssh_config_file = tmpdir.join(SSH_CONFIG) - ssh_config_file.write('\n'.join(ssh_config)) - - # Return a dynamic created backend - host = testinfra.host.get_host(os.environ['KITCHEN_INSTANCE'], ssh_config=str(ssh_config_file), sudo=True) - host.backend.get_hostname = lambda: image - return host diff --git a/test/integration/default/controls/config_spec.rb b/test/integration/default/controls/config_spec.rb new file mode 100644 index 0000000..a5b3b85 --- /dev/null +++ b/test/integration/default/controls/config_spec.rb @@ -0,0 +1,39 @@ +# Overide by OS +user = 'root' +group = 'openvpn' + +control 'OpenVPN server configuration' do + title 'should match desired lines' + + describe file('/etc/openvpn/myserver1.conf') do + it { should be_file } + it { should be_owned_by user } + it { should be_grouped_into group } + its('mode') { should cmp '0640' } + its('content') { should include '# OpenVPN server configuration' } + its('content') { should include '# Managed by Salt' } + its('content') { should include 'user' } + end + + describe command('ls -l /etc/openvpn/myserver1.conf') do + its('stdout') { should include " #{user} #{group} " } + end +end + +control 'OpenVPN client configuration' do + title 'should match desired lines' + + describe file('/etc/openvpn/myclient1.conf') do + it { should be_file } + it { should be_owned_by user } + it { should be_grouped_into group } + its('mode') { should cmp '0640' } + its('content') { should include '# OpenVPN client configuration' } + its('content') { should include '# Managed by Salt' } + its('content') { should include 'user' } + end + + describe command('ls -l /etc/openvpn/myclient1.conf') do + its('stdout') { should include " #{user} #{group} " } + end +end diff --git a/test/integration/default/controls/packages_spec.rb b/test/integration/default/controls/packages_spec.rb new file mode 100644 index 0000000..580f3de --- /dev/null +++ b/test/integration/default/controls/packages_spec.rb @@ -0,0 +1,7 @@ +control 'OpenVPN package' do + title 'should be installed' + + describe package('openvpn') do + it { should be_installed } + end +end diff --git a/test/integration/default/controls/services_spec.rb b/test/integration/default/controls/services_spec.rb new file mode 100644 index 0000000..f7091e9 --- /dev/null +++ b/test/integration/default/controls/services_spec.rb @@ -0,0 +1,37 @@ +# Temporary `if` due to `opensuse-leap-15` bug re: `service` +if os[:name] == 'suse' + puts "[Skip `service`-based tests due to `opensuse-leap-15` detection bug (see https://github.com/inspec/train/issues/377)]" +else + + control 'OpenVPN service' do + impact 0.5 + title 'should be running and enabled' + + # single service + if os[:name] == 'centos' and os[:release].start_with?('6') + describe service("openvpn") do + it { should be_enabled } + it { should be_running } + end + + # multiple services + else + %w(server client).each do |role| + describe service("openvpn@my#{role}1.service") do + it { should be_enabled } + it { should be_running } + end + end + end + + %w(server client).each do |role| + logfile = "/var/log/openvpn/my#{role}1.log" + + describe command("sh -c 'for i in $(seq 1 60); do if grep \"Initialization Sequence Completed\" #{logfile}; then exit 0; fi; echo -n '.'; sleep 1; done; cat #{logfile}; exit 1'") do + its('exit_status') { should be 0 } + its('stdout') { should include "Initialization Sequence Completed" } + end + end + end + +end diff --git a/test/integration/default/inspec.yml b/test/integration/default/inspec.yml new file mode 100644 index 0000000..622bfe6 --- /dev/null +++ b/test/integration/default/inspec.yml @@ -0,0 +1,12 @@ +name: openvpn +title: OpenVPN Formula +maintainer: alxwr +license: Apache-2.0 +summary: Verify that the openvpn formula is setup and configured correctly +supports: + - os-name: debian + - os-name: ubuntu + - os-name: centos + - os-name: fedora + - os-name: opensuse + - os-name: suse