introduce test-kitchen

Change-Id: I374cb4a534e260c00a3cca178ec1d03c2faebcbf
Implements: blueprint service-level-testing
This commit is contained in:
Petr Michalec 2016-07-27 09:50:37 +02:00
parent 2e4a74bc92
commit e80631d97e
12 changed files with 502 additions and 4 deletions

3
.gitignore vendored
View File

@ -1,4 +1,5 @@
tests/build/
*.swp
*.pyc
.ropeproject
.ropeproject
.kitchen

11
.kitchen.docker.yml Normal file
View File

@ -0,0 +1,11 @@
---
driver:
name: docker
hostname: horizon.ci.local
use_sudo: false
provisioner:
grains:
noservices: True

41
.kitchen.openstack.yml Normal file
View File

@ -0,0 +1,41 @@
# usage: `KITCHEN_LOCAL_YAML=.kitchen.openstack.yml kitchen test`
# https://docs.chef.io/config_yml_kitchen.html
# https://github.com/test-kitchen/kitchen-openstack
---
driver:
name: openstack
openstack_auth_url: <%= ENV['OS_AUTH_URL'] %>/tokens
openstack_username: <%= ENV['OS_USERNAME'] || 'ci' %>
openstack_api_key: <%= ENV['OS_PASSWORD'] || 'ci' %>
openstack_tenant: <%= ENV['OS_TENANT_NAME'] || 'ci_jenkins' %>
#floating_ip_pool: <%= ENV['OS_FLOATING_IP_POOL'] || 'nova' %>
key_name: <%= ENV['BOOTSTRAP_SSH_KEY_NAME'] || 'bootstrap_insecure' %>
private_key_path: <%= ENV['BOOTSTRAP_SSH_KEY_PATH'] || "#{ENV['HOME']}/.ssh/id_rsa_bootstrap_insecure" %>
platforms:
- name: ubuntu-14.04
driver:
username: <%= ENV['OS_UBUNTU_IMAGE_USER'] || 'root' %>
image_ref: <%= ENV['OS_UBUNTU_IMAGE_REF'] || 'ubuntu-14-04-x64-1455869035' %>
flavor_ref: m1.medium
network_ref:
<% if ENV['OS_NETWORK_REF'] -%>
- <% ENV['OS_NETWORK_REF'] %>
<% else -%>
- ci-net
<% end -%>
# force update apt cache on the image
run_list:
- recipe[apt]
attributes:
apt:
compile_time_update: true
transport:
username: <%= ENV['OS_UBUNTU_IMAGE_USER'] || 'root' %>
# vim: ft=yaml sw=2 ts=2 sts=2 tw=125

53
.kitchen.yml Normal file
View File

@ -0,0 +1,53 @@
---
driver:
name: vagrant
vm_hostname: horizon.ci.local
use_sudo: false
customize:
memory: 512
provisioner:
name: salt_solo
salt_install: bootstrap
salt_bootstrap_url: https://bootstrap.saltstack.com
salt_version: latest
formula: horizon
log_level: info
state_top:
base:
"*":
- horizon
pillars:
top.sls:
base:
"*":
- horizon
grains:
noservices: False
verifier:
name: inspec
sudo: true
platforms:
- name: ubuntu-14.04
- name: ubuntu-16.04
#- name: centos-7.1
suites:
- name: cluster
provisioner:
pillars-from-files:
horizon.sls: tests/pillar/cluster.sls
- name: single
provisioner:
pillars-from-files:
horizon.sls: tests/pillar/single.sls
# vim: ft=yaml sw=2 ts=2 sts=2 tw=125

114
INTEGRATION.rst Normal file
View File

@ -0,0 +1,114 @@
Continuous Integration
======================
We are using Jenkins to spin a kitchen instances in Docker or OpenStack environment.
If you would like to repeat, then you may use ``.kitchen.<backend>.yml`` configuration yaml in the main directory
to override ``.kitchen.yml`` at some points.
Usage: ``KITCHEN_LOCAL_YAML=.kitchen.<driver>.yml kitchen verify server-ubuntu-1404 -t tests/integration``.
Example: ``KITCHEN_LOCAL_YAML=.kitchen.docker.yml kitchen verify server-ubuntu-1404 -t tests/integration``.
Be aware of fundamental differences of backends. The formula verification scripts are primarily tested with
Vagrant driver.
CI uses a tuned `make kitchen` target defined in `Makefile` to perform following (Kitchen Test) actions:
1. *create*, provision an test instance (VM, container)
2. *converge*, run a provisioner (shell script, kitchen-salt)
3. *verify*, run a verification (inspec, other may be added)
4. *destroy*
Test Kitchen
------------
To install Test Kitchen is as simple as:
.. code-block:: shell
# install kitchen
gem install test-kitchen
# install required plugins
gem install kitchen-vagrant kitchen-docker kitchen-salt
# install additional plugins & tools
gem install kitchen-openstack kitchen-inspec busser-serverspec
kitchen list
kitchen test
of course you have to have installed Ruby and it's package manager `gem <https://rubygems.org/>`_ first.
One may be satisfied installing it system-wide right from OS package manager which is preferred installation method.
For advanced users or the sake of complex environments you may use `rbenv <https://github.com/rbenv/rbenv>`_ for user side ruby installation.
* https://github.com/rbenv/rbenv
* http://kitchen.ci/docs/getting-started/installing
An example steps then might be:
.. code-block:: shell
# get rbenv
git clone https://github.com/rbenv/rbenv.git ~/.rbenv
# configure
cd ~/.rbenv && src/configure && make -C src # don't worry if it fails
echo 'export PATH="$HOME/.rbenv/bin:$PATH"'>> ~/.bash_profile
# Ubuntu Desktop note: Modify your ~/.bashrc instead of ~/.bash_profile.
cd ~/.rbenv; git fetch
# install ruby-build, which provides the rbenv install command
git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
# list all available versions:
rbenv install -l
# install a Ruby version
# maybe you will need additional packages: libssl-dev, libreadline-dev, zlib1g-dev
rbenv install 2.0.0-p648
# activate
rbenv local 2.0.0-p648
# install test kitchen
gem install test-kitchen
An optional ``Gemfile`` in the main directory may contain Ruby dependencies to be required for Test Kitchen workflow.
To install them you have to install first ``gem install bundler`` and then run ``bundler install``.
Verifier
--------
The `Busser <https://github.com/test-kitchen/busser>`_ *Verifier* goes with test-kitchen by default.
It is used to setup and run tests implemented in `<repo>/test/integration`. It guess and installs the particular driver to tested instance.
By default `InSpec <https://github.com/chef/kitchen-inspec>`_ is expected.
You may avoid to install busser framework if you configure specific verifier in `.kitchen.yml` and install it kitchen plugin locally:
verifier:
name: serverspec
If you would to write another verification scripts than InSpec store them in ``<repo>/tests/integration/<suite>/<busser>/*``.
``Busser <https://github.com/test-kitchen/busser>`` is a test setup and execution framework under test kitchen.
InSpec
~~~~~~
Implement integration tests under ``<repo>/tests/integration/<suite>/<busser>/*`` directory with ``_spec.rb`` filename
suffix.
Docs:
* https://github.com/chef/inspec
* https://github.com/chef/kitchen-inspec

View File

@ -1,11 +1,28 @@
DESTDIR=/
SALTENVDIR=/usr/share/salt-formulas/env
RECLASSDIR=/usr/share/salt-formulas/reclass
FORMULANAME=$(shell grep name: metadata.yml|head -1|cut -d : -f 2|grep -Eo '[a-z0-9\-]*')
FORMULANAME=$(shell grep name: metadata.yml|head -1|cut -d : -f 2|grep -Eo '[a-z0-9\-\_]*')
MAKE_PID := $(shell echo $$PPID)
JOB_FLAG := $(filter -j%, $(subst -j ,-j,$(shell ps T | grep "^\s*$(MAKE_PID).*$(MAKE)")))
ifneq ($(subst -j,,$(JOB_FLAG)),)
JOBS := $(subst -j,,$(JOB_FLAG))
else
JOBS := 1
endif
KITCHEN_LOCAL_YAML?=.kitchen.yml
KITCHEN_OPTS?="--concurrency=$(JOBS)"
KITCHEN_OPTS_CREATE?=""
KITCHEN_OPTS_CONVERGE?=""
KITCHEN_OPTS_VERIFY?=""
KITCHEN_OPTS_TEST?=""
all:
@echo "make install - Install into DESTDIR"
@echo "make test - Run tests"
@echo "make kitchen - Run Kitchen CI tests (create, converge, verify)"
@echo "make clean - Cleanup after tests run"
install:
@ -21,6 +38,29 @@ install:
test:
[ ! -d tests ] || (cd tests; ./run_tests.sh)
kitchen: kitchen-create kitchen-converge kitchen-verify kitchen-list
kitchen-create:
kitchen create ${KITCHEN_OPTS} ${KITCHEN_OPTS_CREATE}
[ "$(shell echo $(KITCHEN_LOCAL_YAML)|grep -Eo docker)" = "docker" ] || sleep 120
kitchen-converge:
kitchen converge ${KITCHEN_OPTS} ${KITCHEN_OPTS_CONVERGE} &&\
kitchen converge ${KITCHEN_OPTS} ${KITCHEN_OPTS_CONVERGE}
kitchen-verify:
[ ! -d tests/integration ] || kitchen verify -t tests/integration ${KITCHEN_OPTS} ${KITCHEN_OPTS_VERIFY}
[ -d tests/integration ] || kitchen verify ${KITCHEN_OPTS} ${KITCHEN_OPTS_VERIFY}
kitchen-test:
[ ! -d tests/integration ] || kitchen test -t tests/integration ${KITCHEN_OPTS} ${KITCHEN_OPTS_TEST}
[ -d tests/integration ] || kitchen test ${KITCHEN_OPTS} ${KITCHEN_OPTS_TEST}
kitchen-list:
kitchen list
clean:
[ ! -x "$(shell which kitchen)" ] || kitchen destroy
[ ! -d .kitchen ] || rm -rf .kitchen
[ ! -d tests/build ] || rm -rf tests/build
[ ! -d build ] || rm -rf build

View File

@ -367,3 +367,53 @@ Things to improve
* CACHES - configure caching engine - is it not allowed by default?
* SESSION_ENGINE - change it from signed cookie to something else
* policy files - look into these files and think of further configuration/parametrisation
Development and testing
=======================
Development and test workflow with `Test Kitchen <http://kitchen.ci>`_ and
`kitchen-salt <https://github.com/simonmcc/kitchen-salt>`_ provisioner plugin.
Test Kitchen is a test harness tool to execute your configured code on one or more platforms in isolation.
There is a ``.kitchen.yml`` in main directory that defines *platforms* to be tested and *suites* to execute on them.
Kitchen CI can spin instances locally or remote, based on used *driver*.
For local development ``.kitchen.yml`` defines a `vagrant <https://github.com/test-kitchen/kitchen-vagrant>`_ or
`docker <https://github.com/test-kitchen/kitchen-docker>`_ driver.
To use backend drivers or implement your CI follow the section `INTEGRATION.rst#Continuous Integration`__.
A listing of scenarios to be executed:
.. code-block:: shell
$ kitchen list
Instance Driver Provisioner Verifier Transport Last Action
cluster-ubuntu-1404 Vagrant SaltSolo Inspec Ssh <Not Created>
cluster-ubuntu-1604 Vagrant SaltSolo Inspec Ssh <Not Created>
cluster-centos-71 Vagrant SaltSolo Inspec Ssh <Not Created>
single-ubuntu-1404 Vagrant SaltSolo Inspec Ssh <Not Created>
single-ubuntu-1604 Vagrant SaltSolo Inspec Ssh <Not Created>
single-centos-71 Vagrant SaltSolo Inspec Ssh <Not Created>
The `Busser <https://github.com/test-kitchen/busser>`_ *Verifier* is used to setup and run tests
implementated in `<repo>/test/integration`. It installs the particular driver to tested instance
(`Serverspec <https://github.com/neillturner/kitchen-verifier-serverspec>`_,
`InSpec <https://github.com/chef/kitchen-inspec>`_, Shell, Bats, ...) prior the verification is executed.
Usage:
.. code-block:: shell
# list instances and status
kitchen list
# manually execute integration tests
kitchen [test || [create|converge|verify|exec|login|destroy|...]] [instance] -t tests/integration
# use with provided Makefile (ie: within CI pipeline)
make kitchen

View File

@ -0,0 +1,87 @@
# TODO, enable helper files once resolved - https://github.com/chef/kitchen-inspec/issues/93
#require 'spec_helper'
param = {
user: 'horizon',
group: 'horizon',
}
# based on operating system we select the available service
if ['centos', 'fedora', 'freebsd', 'opensuse'].include?(os[:family])
# CentOS, Fedora
service_nm = 'httpd'
config = '/etc/openstack-dashboard/local_settings'
apache_config = '/etc/httpd/conf.d/openstack-dashboard.conf'
elsif ['debian', 'ubuntu'].include?(os[:family])
# Debian, Ubuntu
service_nm = 'apache'
config = '/etc/openstack-dashboard/local_settings.py'
apache_config = '/etc/apache2/conf-available/openstack-dashboard.conf'
end
control 'horizon' do
# TODO, check for config files and content
#debian
#'config': '/etc/openstack-dashboard/local_settings.py',
#'apache_config': '/etc/apache2/conf-available/openstack-dashboard.conf',
#'port_config_file': '/etc/apache2/ports.conf',
#'port_config_template': 'salt://horizon/files/ports.conf',
#rhel
#'config': '/etc/openstack-dashboard/local_settings',
#'apache_config': '/etc/httpd/conf.d/openstack-dashboard.conf',
#'port_config_file': '/etc/httpd/conf/httpd.conf',
#'port_config_template': 'salt://horizon/files/httpd.conf',
describe file(config) do
it { should exist }
its('content') { should match("SECRET_KEY = 'secret'") }
#it { should be_owned_by param[:user] }
#it { should be_grouped_into param[:group] }
end
end
control 'apache config' do
describe file(apache_config) do
it { should exist }
its('content') { should match("WSGIDaemonProcess horizon user=horizon group=horizon") }
end
end
return
return if ENV['DOCKER']
#control 'web server' do
#if os[:family] == 'ubuntu' && os[:release] >= '16.04'
#describe systemd_service(service_nm) do
#it { should be_enabled }
#it { should be_installed }
#it { should be_running }
#end
#else
#describe_service(service_nm) do
#it { should be_enabled }
#it { should be_installed }
#it { should be_running }
#end
#end
#if os.unix?
#describe port(80) do
#it { should be_listening }
#its('protocols') { should include('tcp') }
#end
#end
#end

View File

View File

@ -0,0 +1,87 @@
# TODO, enable helper files once resolved - https://github.com/chef/kitchen-inspec/issues/93
#require 'spec_helper'
param = {
user: 'horizon',
group: 'horizon',
}
# based on operating system we select the available service
if ['centos', 'fedora', 'freebsd', 'opensuse'].include?(os[:family])
# CentOS, Fedora
service_nm = 'httpd'
config = '/etc/openstack-dashboard/local_settings'
apache_config = '/etc/httpd/conf.d/openstack-dashboard.conf'
elsif ['debian', 'ubuntu'].include?(os[:family])
# Debian, Ubuntu
service_nm = 'apache'
config = '/etc/openstack-dashboard/local_settings.py'
apache_config = '/etc/apache2/conf-available/openstack-dashboard.conf'
end
control 'horizon' do
# TODO, check for config files and content
#debian
#'config': '/etc/openstack-dashboard/local_settings.py',
#'apache_config': '/etc/apache2/conf-available/openstack-dashboard.conf',
#'port_config_file': '/etc/apache2/ports.conf',
#'port_config_template': 'salt://horizon/files/ports.conf',
#rhel
#'config': '/etc/openstack-dashboard/local_settings',
#'apache_config': '/etc/httpd/conf.d/openstack-dashboard.conf',
#'port_config_file': '/etc/httpd/conf/httpd.conf',
#'port_config_template': 'salt://horizon/files/httpd.conf',
describe file(config) do
it { should exist }
its('content') { should match("SECRET_KEY = 'secret'") }
#it { should be_owned_by param[:user] }
#it { should be_grouped_into param[:group] }
end
end
control 'apache config' do
describe file(apache_config) do
it { should exist }
its('content') { should match("WSGIDaemonProcess horizon user=horizon group=horizon") }
end
end
return
return if ENV['DOCKER']
#control 'web server' do
#if os[:family] == 'ubuntu' && os[:release] >= '16.04'
#describe systemd_service(service_nm) do
#it { should be_enabled }
#it { should be_installed }
#it { should be_running }
#end
#else
#describe_service(service_nm) do
#it { should be_enabled }
#it { should be_installed }
#it { should be_running }
#end
#end
#if os.unix?
#describe port(80) do
#it { should be_listening }
#its('protocols') { should include('tcp') }
#end
#end
#end

View File

@ -4,6 +4,12 @@ horizon:
version: liberty
secret_key: secret
session_timeout: 43200
plugin:
horizon_theme:
theme_name: ubuntu
source:
engine: pkg
name: openstack-dashboard-ubuntu-theme
wsgi:
processes: 3
threads: 10
@ -22,6 +28,7 @@ horizon:
port: 11211
identity:
engine: keystone
encryption: encryption
host: 127.0.0.1
port: 5000
api_version: 2
@ -45,4 +52,4 @@ haproxy:
- name: ctl03
host: 127.0.0.1
port: 80
params: cookie ctl03 check inter 2000 fall 3
params: cookie ctl03 check inter 2000 fall 3

View File

@ -7,6 +7,12 @@ horizon:
bind:
address: 127.0.0.1
port: 80
plugin:
horizon_theme:
theme_name: ubuntu
source:
engine: pkg
name: openstack-dashboard-ubuntu-theme
wsgi:
processes: 3
threads: 10
@ -23,4 +29,5 @@ horizon:
port: 5000
host: 127.0.0.1
encryption: encryption
api_version: 2
api_version: 2