diff --git a/tests/kitchen/.kitchen.yml b/tests/kitchen/.kitchen.yml index 1b7550fdbe8..c8e42f594d8 100644 --- a/tests/kitchen/.kitchen.yml +++ b/tests/kitchen/.kitchen.yml @@ -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 %> diff --git a/tests/kitchen/__init__.py b/tests/kitchen/__init__.py new file mode 100644 index 00000000000..40a96afc6ff --- /dev/null +++ b/tests/kitchen/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/tests/kitchen/test_kitchen.py b/tests/kitchen/test_kitchen.py new file mode 100644 index 00000000000..1db721774aa --- /dev/null +++ b/tests/kitchen/test_kitchen.py @@ -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)) diff --git a/tests/kitchen/tests/wordpress/_modules/wordpress.py b/tests/kitchen/tests/wordpress/_modules/wordpress.py new file mode 100644 index 00000000000..7da57e273b9 --- /dev/null +++ b/tests/kitchen/tests/wordpress/_modules/wordpress.py @@ -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 diff --git a/tests/kitchen/tests/wordpress/_states/wordpress.py b/tests/kitchen/tests/wordpress/_states/wordpress.py new file mode 100644 index 00000000000..e730e183fcf --- /dev/null +++ b/tests/kitchen/tests/wordpress/_states/wordpress.py @@ -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 diff --git a/tests/kitchen/tests/wordpress/conftest.py b/tests/kitchen/tests/wordpress/conftest.py new file mode 100644 index 00000000000..27d163557b7 --- /dev/null +++ b/tests/kitchen/tests/wordpress/conftest.py @@ -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') diff --git a/tests/kitchen/tests/wordpress/driver.yml b/tests/kitchen/tests/wordpress/driver.yml new file mode 100644 index 00000000000..208831b94d4 --- /dev/null +++ b/tests/kitchen/tests/wordpress/driver.yml @@ -0,0 +1,2 @@ +forward: + - 80 diff --git a/tests/kitchen/tests/wordpress/pillars.yml b/tests/kitchen/tests/wordpress/pillars.yml new file mode 100644 index 00000000000..48b6b5a2661 --- /dev/null +++ b/tests/kitchen/tests/wordpress/pillars.yml @@ -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 diff --git a/tests/kitchen/tests/wordpress/provisioner.yml b/tests/kitchen/tests/wordpress/provisioner.yml new file mode 100644 index 00000000000..76f46ce2f5d --- /dev/null +++ b/tests/kitchen/tests/wordpress/provisioner.yml @@ -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 diff --git a/tests/kitchen/tests/wordpress/states/defaults.yaml b/tests/kitchen/tests/wordpress/states/defaults.yaml new file mode 100644 index 00000000000..39efc622999 --- /dev/null +++ b/tests/kitchen/tests/wordpress/states/defaults.yaml @@ -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 %} diff --git a/tests/kitchen/tests/wordpress/states/init.sls b/tests/kitchen/tests/wordpress/states/init.sls new file mode 100644 index 00000000000..70b7087efac --- /dev/null +++ b/tests/kitchen/tests/wordpress/states/init.sls @@ -0,0 +1,7 @@ +include: + - php.ng + - php.ng.mysql + - php.ng.apache2 + - apache + - mysql + - .setup diff --git a/tests/kitchen/tests/wordpress/states/setup.sls b/tests/kitchen/tests/wordpress/states/setup.sls new file mode 100644 index 00000000000..2240bc2bd11 --- /dev/null +++ b/tests/kitchen/tests/wordpress/states/setup.sls @@ -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 diff --git a/tests/kitchen/tests/wordpress/states/wp-cli.phar b/tests/kitchen/tests/wordpress/states/wp-cli.phar new file mode 100644 index 00000000000..ff8c7b6a39b Binary files /dev/null and b/tests/kitchen/tests/wordpress/states/wp-cli.phar differ diff --git a/tests/kitchen/tests/wordpress/states/wp-config.php.j2 b/tests/kitchen/tests/wordpress/states/wp-config.php.j2 new file mode 100644 index 00000000000..928dbddc335 --- /dev/null +++ b/tests/kitchen/tests/wordpress/states/wp-config.php.j2 @@ -0,0 +1,82 @@ + 0: diff --git a/tests/support/parser/__init__.py b/tests/support/parser/__init__.py index 429ad4f0cf3..eebef3f5484 100644 --- a/tests/support/parser/__init__.py +++ b/tests/support/parser/__init__.py @@ -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')