add first kitchen test

This commit is contained in:
Daniel Wallace 2017-10-11 20:40:44 -06:00
parent 662d8bd68c
commit 7410ffd92a
No known key found for this signature in database
GPG key ID: 5FA5E5544F010D48
18 changed files with 634 additions and 3 deletions

View file

@ -12,10 +12,14 @@ provisioner:
name: salt_solo
salt_install: pip
pip_pkg: <%= ENV['SALT_SDIST_PATH'] || 'salt' %>
pip_index_url: https://pypi.c7.saltstack.net/simple
pip_extra_index_url:
- https://pypi.python.org/simple
require_chef: false
formula: states
<% if File.exists?('dependencies.yml') %>
<% File.read('dependencies.yml').split(/\n/).each do |line| %>
log_level: debug
<% if File.exists?('provisioner.yml') %>
<% File.read('provisioner.yml').split(/\n/).each do |line| %>
<%= line %>
<% end %>
<% end %>
@ -50,12 +54,17 @@ platforms:
run_command: /usr/lib/systemd/systemd
provision_command:
- systemctl enable sshd.service
- zypper install -y python-pip python-devel gcc git gcc-c++
- name: ubuntu
driver_config:
run_command: /lib/systemd/systemd
provision_command:
- DEBIAN_FRONTEND=noninteractive apt-get install -y python-pip python-dev gcc git
- name: debian
driver_config:
run_command: /lib/systemd/systemd
provision_command:
- DEBIAN_FRONTEND=noninteractive apt-get install -y python-pip python-dev gcc git
<% end %>
<% if File.exists?('suites.yml') %>
@ -71,5 +80,5 @@ suites:
verifier:
name: shell
remote_exec: false
command: pytest --junitxml= -v tests/integration/$KITCHEN_SUITE
command: pytest -v tests/integration/$KITCHEN_SUITE
<% end %>

View file

@ -0,0 +1 @@
# -*- coding: utf-8 -*-

View file

@ -0,0 +1,75 @@
import os
import logging
from tests.support.unit import TestCase, skipIf
import setup
import salt.utils.path
from salt.modules import cmdmod as cmd
CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
log = logging.getLogger(__name__)
@skipIf(not salt.utils.path.which('bundle'), 'Bundler is not installed')
class KitchenTestCase(TestCase):
'''
Test kitchen environments
'''
@classmethod
def setUpClass(cls):
'''
setup kitchen tests
'''
cls.topdir = '/' + os.path.join(*CURRENT_DIR.split('/')[:-2])
cls.use_vt = int(os.environ.get('TESTS_LOG_LEVEL')) >= 3
cmd.run('python setup.py sdist', cwd=cls.topdir)
cmd.run('bundle install', cwd=CURRENT_DIR)
@classmethod
def tearDownClass(cls):
del cls.topdir
def func_builder(testdir):
def func(self):
env = {
'KITCHEN_YAML': os.path.join(CURRENT_DIR, '.kitchen.yml'),
'SALT_SDIST_PATH': os.path.join(self.topdir, 'dist', 'salt-{0}.tar.gz'.format(setup.__version__)),
}
if 'TESTS_XML_OUTPUT_DIR' in os.environ:
env['PYTEST_ADDOPTS'] = '--junit-xml {0}/kitchen.tests.{1}.xml'.format(
os.environ.get('TESTS_XML_OUTPUT_DIR'),
testdir,
)
self.assertEqual(
cmd.retcode(
'bundle exec kitchen create -c 4 all',
cwd=os.path.join(CURRENT_DIR, 'tests', testdir),
env=env,
use_vt=self.use_vt,
),
0
)
self.assertEqual(
cmd.retcode(
'bundle exec kitchen verify all',
cwd=os.path.join(CURRENT_DIR, 'tests', testdir),
env=env,
use_vt=self.use_vt,
),
0
)
self.assertEqual(
cmd.retcode(
'bundle exec kitchen destroy all',
cwd=os.path.join(CURRENT_DIR, 'tests', testdir),
env=env,
use_vt=self.use_vt,
),
0
)
return func
for testdir in os.listdir(os.path.join(CURRENT_DIR, 'tests')):
setattr(KitchenTestCase, 'test_kitchen_{0}'.format(testdir), func_builder(testdir))

View file

@ -0,0 +1,107 @@
# -*- coding: utf-8 -*-
'''
Manage wordpress plugins
'''
import re
import pprint
import collections
import salt.utils
Plugin = collections.namedtuple('Plugin', 'name status update versino')
def __virtual__():
if salt.utils.which('wp'):
return True
return False
def _get_plugins(stuff):
return Plugin(stuff)
def list_plugins(path, user):
"""
Check if plugin is activated in path
"""
ret = []
resp = __salt__['cmd.shell']((
'wp --path={0} plugin list'
).format(path), runas=user)
for line in resp.split('\n')[1:]:
ret.append(line.split('\t'))
return [plugin.__dict__ for plugin in map(_get_plugins, ret)]
def show_plugin(name, path, user):
ret = {'name': name}
resp = __salt__['cmd.shell']((
'wp --path={0} plugin status {1}'
).format(path, name), runas=user).split('\n')
for line in resp:
if 'Status' in line:
ret['status'] = line.split(' ')[-1].lower()
elif 'Version' in line:
ret['version'] = line.split(' ')[-1].lower()
return ret
def activate(name, path, user):
check = show_plugin(name, path, user)
if check['status'] == 'active':
# already active
return None
resp = __salt__['cmd.shell']((
'wp --path={0} plugin activate {1}'
).format(path, name), runas=user)
if 'Success' in resp:
return True
elif show_plugin(name, path, user)['status'] == 'active':
return True
return False
def deactivate(name, path, user):
check = show_plugin(name, path, user)
if check['status'] == 'inactive':
# already inactive
return None
resp = __salt__['cmd.shell']((
'wp --path={0} plugin deactivate {1}'
).format(path, name), runas=user)
if 'Success' in resp:
return True
elif show_plugin(name, path, user)['status'] == 'inactive':
return True
return False
def is_installed(path, user=None):
retcode = __salt__['cmd.retcode']((
'wp --path={0} core is-installed'
).format(path), runas=user)
if retcode == 0:
return True
return False
def install(path, user, admin_user, admin_password, admin_email, title, url):
retcode = __salt__['cmd.retcode']((
'wp --path={0} core install '
'--title="{1}" '
'--admin_user={2} '
"--admin_password='{3}' "
'--admin_email={4} '
'--url={5}'
).format(
path,
title,
admin_user,
admin_password,
admin_email,
url
), runas=user)
if retcode == 0:
return True
return False

View file

@ -0,0 +1,114 @@
# -*- coding: utf-8 -*-
'''
Manage wordpress plugins
'''
def __virtual__():
return 'wordpress.show_plugin' in __salt__
def installed(name, path, user, admin_user, admin_password, admin_email, title, url):
ret = {'name': name,
'changes': {},
'comment': '',
'result': False}
check = __salt__['wordpress.is_installed'](path, user)
if check:
ret['result'] = True
ret['comment'] = 'Wordpress is already installed: {0}'.format(name)
return ret
elif __opts__['test']:
ret['result'] = None
ret['comment'] = 'Wordpress will be installed: {0}'.format(name)
return ret
resp = __salt__['wordpress.install'](path, user, admin_user, admin_password, admin_email, title, url)
if resp:
ret['result'] = True
ret['comment'] = 'Wordpress Installed: {0}'.format(name)
ret['changes'] = {
'new': resp
}
else:
ret['comment'] = 'Failed to install wordpress: {0}'.format(name)
return ret
def activated(name, path, user):
ret = {'name': name,
'changes': {},
'comment': '',
'result': False}
check = __salt__['wordpress.show_plugin'](name, path, user)
if check['status'] == 'active':
ret['result'] = True
ret['comment'] = 'Plugin already activated: {0}'.format(name)
return ret
elif __opts__['test']:
ret['result'] = None
ret['comment'] = 'Plugin will be activated: {0}'.format(name)
return ret
resp = __salt__['wordpress.activate'](name, path, user)
if resp is True:
ret['result'] = True
ret['comment'] = 'Plugin activated: {0}'.format(name)
ret['changes'] = {
'old': check,
'new': __salt__['wordpress.show_plugin'](name, path, user)
}
elif resp is None:
ret['result'] = True
ret['comment'] = 'Plugin already activated: {0}'.format(name)
ret['changes'] = {
'old': check,
'new': __salt__['wordpress.show_plugin'](name, path, user)
}
else:
ret['comment'] = 'Plugin failed to activate: {0}'.format(name)
return ret
def deactivated(name, path, user):
ret = {'name': name,
'changes': {},
'comment': '',
'result': False}
check = __salt__['wordpress.show_plugin'](name, path, user)
if check['status'] == 'inactive':
ret['result'] = True
ret['comment'] = 'Plugin already deactivated: {0}'.format(name)
return ret
elif __opts__['test']:
ret['result'] = None
ret['comment'] = 'Plugin will be deactivated: {0}'.format(name)
return ret
resp = __salt__['wordpress.deactivate'](name, path, user)
if resp is True:
ret['result'] = True
ret['comment'] = 'Plugin deactivated: {0}'.format(name)
ret['changes'] = {
'old': check,
'new': __salt__['wordpress.show_plugin'](name, path, user)
}
elif resp is None:
ret['result'] = True
ret['comment'] = 'Plugin already deactivated: {0}'.format(name)
ret['changes'] = {
'old': check,
'new': __salt__['wordpress.show_plugin'](name, path, user)
}
else:
ret['comment'] = 'Plugin failed to deactivate: {0}'.format(name)
return ret

View file

@ -0,0 +1,43 @@
import functools
import os
import pytest
import subprocess
import testinfra
if os.environ.get('KITCHEN_USERNAME') == 'vagrant':
if 'windows' in os.environ.get('KITCHEN_INSTANCE'):
test_host = testinfra.get_host('winrm://{KITCHEN_USERNAME}:{KITCHEN_PASSWORD}@{KITCHEN_HOSTNAME}:{KITCHEN_PORT}'.format(**os.environ), no_ssl=True)
else:
test_host = testinfra.get_host('paramiko://{KITCHEN_USERNAME}@{KITCHEN_HOSTNAME}:{KITCHEN_PORT}'.format(**os.environ),
ssh_identity_file=os.environ.get('KITCHEN_SSH_KEY'))
else:
test_host = testinfra.get_host('docker://{KITCHEN_USERNAME}@{KITCHEN_CONTAINER_ID}'.format(**os.environ))
@pytest.fixture
def host():
return test_host
@pytest.fixture
def salt():
if 'windows' in os.environ.get('KITCHEN_INSTANCE'):
tmpconf = r'c:\Users\vagrant\AppData\Local\Temp\kitchen\etc\salt'
else:
test_host.run('sudo chown -R {0} /tmp/kitchen'.format(os.environ.get('KITCHEN_USERNAME')))
tmpconf = '/tmp/kitchen/etc/salt'
return functools.partial(test_host.salt, config=tmpconf)
@pytest.fixture
def http_port():
return subprocess.check_output(
['docker', 'inspect',
'-f', '{{(index (index .NetworkSettings.Ports "80/tcp") 0).HostPort}}',
os.environ.get('KITCHEN_CONTAINER_ID')]
).strip().decode()
@pytest.fixture
def host_ip():
return os.environ.get('KITCHEN_HOSTNAME')

View file

@ -0,0 +1,2 @@
forward:
- 80

View file

@ -0,0 +1,23 @@
pillars:
top.sls:
base:
"*":
- wordpress
wordpress.sls:
mysql:
database:
- wordpress
user:
wordpress:
password: quair9aiqueeShae4toh
host: localhost
databases:
- database: wordpress
grants:
- all privileges
wordpress:
lookup:
admin_user: gtmanfred
admin_email: daniel@gtmanfred.com
title: "GtManfred's Blog"
url: http://blog.manfred.io

View file

@ -0,0 +1,10 @@
dependencies:
- name: apache
repo: git
source: https://github.com/saltstack-formulas/apache-formula.git
- name: mysql
repo: git
source: https://github.com/saltstack-formulas/mysql-formula.git
- name: php
repo: git
source: https://github.com/saltstack-formulas/php-formula.git

View file

@ -0,0 +1,29 @@
{%- load_yaml as rawmap %}
Debian:
user: www-data
group: www-data
dir: /var/www/html/
admin_user: wordpress
admin_password: "{{salt.grains.get_or_set_hash('generated:wordpress:password')}}"
admin_email: test@example.com
title: Wordpress
url: http://localhost/
RedHat:
user: apache
group: apache
dir: /var/www/html/
admin_user: wordpress
admin_password: "{{salt.grains.get_or_set_hash('generated:wordpress:password')}}"
admin_email: test@example.com
title: Wordpress
url: http://localhost/
Suse:
user: wwwrun
group: www
dir: /srv/www/htdocs/
admin_user: wordpress
admin_password: "{{salt.grains.get_or_set_hash('generated:wordpress:password')}}"
admin_email: test@example.com
title: Wordpress
url: http://localhost/
{%- endload %}

View file

@ -0,0 +1,7 @@
include:
- php.ng
- php.ng.mysql
- php.ng.apache2
- apache
- mysql
- .setup

View file

@ -0,0 +1,78 @@
{% from "states/defaults.yaml" import rawmap with context %}
{%- set config = salt['grains.filter_by'](rawmap, grain='os_family', merge=salt['config.get']('wordpress:lookup')) %}
download wordpress archive:
pkg.latest:
- name: tar
archive.extracted:
- name: {{config.dir}}
- source_hash: a99115b3b6d6d7a1eb6c5617d4e8e704ed50f450
- source: https://wordpress.org/wordpress-4.8.2.tar.gz
- options: --strip-components=1
- enforce_toplevel: false
grains.present:
- name: wordpressdir
- value: {{config.dir}}
configure wordpress:
file.managed:
- name: {{config.dir}}/wp-config.php
- source: salt://states/wp-config.php.j2
- user: {{config.user}}
- group: {{config.group}}
- template: jinja
{%- if grains.os_family in ('Suse',) %}
suse setup:
pkg.latest:
- pkgs:
- php5-phar
- apache2-mod_php5
- listen_in:
- service: apache
file.managed:
- name: /etc/apache2/conf.d/mod_php.conf
- contents: |
LoadModule php5_module /usr/lib64/apache2/mod_php5.so
- listen_in:
- service: apache
{%- elif grains.os_family in ('Debian',) %}
remove default index.html:
file.absent:
- name: /var/www/html/index.html
{%- endif %}
get wp manager script:
file.managed:
- name: /usr/local/bin/wp
- user: root
- group: root
- mode: 755
- source: salt://states/wp-cli.phar
- source_hash: a647367c1e6c34c7357e380515d59e15fbc86fa2
- reload_modules: True
do install:
wordpress.installed:
- path: {{config.dir}}
- user: {{config.user}}
- admin_user: {{config.admin_user}}
- admin_password: "{{config.admin_password}}"
- admin_email: "{{config.admin_email}}"
- title: "{{config.title}}"
- url: "{{config.url}}"
- retry:
attempts: 5
file.directory:
- name: {{config.dir}}
- user: {{config.user}}
- group: {{config.group}}
- file_mode: 644
- dir_mode: 2775
- recurse:
- user
- group
- mode

Binary file not shown.

View file

@ -0,0 +1,82 @@
<?php
/**
* The base configuration for WordPress
*
* The wp-config.php creation script uses this file during the
* installation. You don't have to use the web site, you can
* copy this file to "wp-config.php" and fill in the values.
*
* This file contains the following configurations:
*
* * MySQL settings
* * Secret keys
* * Database table prefix
* * ABSPATH
*
* @link https://codex.wordpress.org/Editing_wp-config.php
*
* @package WordPress
*/
// ** MySQL settings - You can get this info from your web host ** //
/** The name of the database for WordPress */
define('DB_NAME', 'wordpress');
/** MySQL database username */
define('DB_USER', 'wordpress');
/** MySQL database password */
define('DB_PASSWORD', 'quair9aiqueeShae4toh');
/** MySQL hostname */
define('DB_HOST', 'localhost');
/** Database Charset to use in creating database tables. */
define('DB_CHARSET', 'utf8');
/** The Database Collate type. Don't change this if in doubt. */
define('DB_COLLATE', '');
/**#@+
* Authentication Unique Keys and Salts.
*
* Change these to different unique phrases!
* You can generate these using the {@link https://api.wordpress.org/secret-key/1.1/salt/ WordPress.org secret-key service}
* You can change these at any point in time to invalidate all existing cookies. This will force all users to have to log in again.
*
* @since 2.6.0
*/
{%- do salt['http.query']('https://api.wordpress.org/secret-key/1.1/salt/') %}
/**#@-*/
/**
* WordPress Database Table prefix.
*
* You can have multiple installations in one database if you give each
* a unique prefix. Only numbers, letters, and underscores please!
*/
$table_prefix = 'wp_';
/**
* For developers: WordPress debugging mode.
*
* Change this to true to enable the display of notices during development.
* It is strongly recommended that plugin and theme developers use WP_DEBUG
* in their development environments.
*
* For information on other constants that can be used for debugging,
* visit the Codex.
*
* @link https://codex.wordpress.org/Debugging_in_WordPress
*/
define('WP_DEBUG', false);
/* That's all, stop editing! Happy blogging. */
/** Absolute path to the WordPress directory. */
if ( !defined('ABSPATH') )
define('ABSPATH', dirname(__FILE__) . '/');
/** Sets up WordPress vars and included files. */
require_once(ABSPATH . 'wp-settings.php');

View file

@ -0,0 +1,8 @@
def test_formula(salt):
formulas = {'wordpress',}
dirs = salt('cp.list_master_dirs')
assert all([formula in dirs for formula in formulas])
def test_wordpress_module(salt):
wordpressdir = salt('grains.get', 'wordpressdir')
assert salt('wordpress.is_installed', path=wordpressdir)

View file

@ -0,0 +1,6 @@
import requests
def test_server(host_ip, http_port):
resp = requests.get('http://{0}:{1}'.format(host_ip, http_port), headers={'Host': 'blog.manfred.io'})
assert resp.status_code == 200

View file

@ -735,6 +735,39 @@ class SaltTestsuiteParser(SaltCoverageTestingParser):
status.append(results)
return status
def run_kitchen_tests(self):
'''
Execute the kitchen tests
'''
named_kitchen_test = []
if self.options.name:
for test in self.options.name:
if not test.startswith(('tests.kitchen.', 'kitchen.')):
continue
named_kitchen_test.append(test)
if not self.options.kitchen and not named_kitchen_test:
# We are not explicitly running the unit tests and none of the
# names passed to --name is a unit test.
return [True]
status = []
if self.options.kitchen:
results = self.run_suite(
os.path.join(TEST_DIR, 'kitchen'), 'Kitchen', suffix='test_*.py'
)
status.append(results)
# We executed ALL unittests, we can skip running unittests by name
# below
return status
for name in named_kitchen_test:
results = self.run_suite(
os.path.join(TEST_DIR, 'kitchen'), name, suffix='test_*.py', load_from_name=True
)
status.append(results)
return status
def main():
'''
@ -755,6 +788,8 @@ def main():
overall_status.extend(status)
status = parser.run_unit_tests()
overall_status.extend(status)
status = parser.run_kitchen_tests()
overall_status.extend(status)
false_count = overall_status.count(False)
if false_count > 0:

View file

@ -384,6 +384,7 @@ class SaltTestingParser(optparse.OptionParser):
if self.xml_output_dir is not None and self.options.xml_out:
if not os.path.isdir(self.xml_output_dir):
os.makedirs(self.xml_output_dir)
os.environ['TESTS_XML_OUTPUT_DIR'] = self.xml_output_dir
print(
' * Generated unit test XML reports will be stored '
'at {0!r}'.format(self.xml_output_dir)
@ -455,6 +456,7 @@ class SaltTestingParser(optparse.OptionParser):
logging_level = logging.INFO
else:
logging_level = logging.ERROR
os.environ['TESTS_LOG_LEVEL'] = six.text_type(self.options.verbosity)
consolehandler.setLevel(logging_level)
logging.root.addHandler(consolehandler)
log.info('Runtests logging has been setup')