Use bundletester for amulet test execution

Switch to using bundletester for execution of functional tests,
leveraging tox to build out test virtualenvs.

Rename amulet tests inline with gate-* and dfs-* naming standards.

Update README to refer to functional testing section of the charm
guide.

Drop deprecated tests as no longer used in any automated way.

Change-Id: I5fbfb7484ae2fdc5ed10e5a992cedc94df071ee4
This commit is contained in:
James Page 2016-07-19 09:46:58 +01:00 committed by Ryan Beisner
parent 94612bf09d
commit df8742dc74
27 changed files with 101 additions and 1214 deletions

View File

@ -30,5 +30,4 @@ test:
functional_test:
@echo Starting amulet tests...
@tests/setup/00-setup
@juju test -v -p AMULET_HTTP_PROXY,AMULET_OS_VIP --timeout 2700
@tox -e func27

View File

@ -7,3 +7,17 @@ flake8>=2.2.4,<=2.4.1
os-testr>=0.4.1
charm-tools>=2.0.0
requests==2.6.0
# BEGIN: Amulet OpenStack Charm Helper Requirements
# Liberty client lower constraints
amulet>=1.14.3,<2.0
bundletester>=0.6.1,<1.0
python-ceilometerclient>=1.5.0,<2.0
python-cinderclient>=1.4.0,<2.0
python-glanceclient>=1.1.0,<2.0
python-heatclient>=0.8.0,<1.0
python-novaclient>=2.30.1,<3.0
python-openstackclient>=1.7.0,<2.0
python-swiftclient>=2.6.0,<3.0
pika>=0.10.0,<1.0
distro-info
# END: Amulet OpenStack Charm Helper Requirements

View File

@ -1,25 +0,0 @@
#!/usr/bin/python
#
# Copyright 2016 Canonical Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Amulet tests on a basic rabbitmq-server deployment on trusty-juno."""
from basic_deployment import RmqBasicDeployment
if __name__ == '__main__':
deployment = RmqBasicDeployment(series='trusty',
openstack='cloud:trusty-juno',
source='cloud:trusty-updates/juno')
deployment.run_tests()

View File

@ -1,23 +0,0 @@
#!/usr/bin/python
#
# Copyright 2016 Canonical Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Amulet tests on a basic rabbitmq-server deployment on wily-liberty."""
from basic_deployment import RmqBasicDeployment
if __name__ == '__main__':
deployment = RmqBasicDeployment(series='wily')
deployment.run_tests()

View File

@ -1,113 +0,0 @@
This directory provides Amulet tests to verify basic deployment functionality
from the perspective of this charm, its requirements and its features, as
exercised in a subset of the full OpenStack deployment test bundle topology.
Reference: lp:openstack-charm-testing for full test bundles.
A single topology and configuration is defined and deployed, once for each of
the defined Ubuntu:OpenStack release combos. The ongoing goal is for this
charm to always possess tests and combo definitions for all currently-supported
release combinations of U:OS.
test_* methods are called in lexical sort order, as with most runners. However,
each individual test method should be idempotent and expected to pass regardless
of run order or Ubuntu:OpenStack combo. When writing or modifying tests,
ensure that every individual test is not dependent on another test_ method.
Test naming convention, purely for code organization purposes:
1xx service and endpoint checks
2xx relation checks
3xx config checks
4xx functional checks
9xx restarts, config changes, actions and other final checks
In order to run tests, charm-tools and juju must be installed:
sudo add-apt-repository ppa:juju/stable
sudo apt-get update
sudo apt-get install charm-tools juju juju-deployer amulet
Alternatively, tests may be exercised with proposed or development versions
of juju and related tools:
# juju proposed version
sudo add-apt-repository ppa:juju/proposed
sudo apt-get update
sudo apt-get install charm-tools juju juju-deployer
# juju development version
sudo add-apt-repository ppa:juju/devel
sudo apt-get update
sudo apt-get install charm-tools juju juju-deployer
Some tests may need to download files. If a web proxy server is required in
the environment, the AMULET_HTTP_PROXY environment variable must be set and
passed into the juju test command. This is unrelated to juju's http proxy
settings or behavior.
The following examples demonstrate different ways that tests can be executed.
All examples are run from the charm's root directory.
* To run all +x tests in the tests directory:
bzr branch lp:charms/trusty/foo
cd foo
make functional_test
* To run the tests against a specific release combo as defined in tests/:
bzr branch lp:charms/trusty/foo
cd foo
juju test -v -p AMULET_HTTP_PROXY 015-basic-trusty-icehouse
* To run tests and keep the juju environment deployed after a failure:
bzr branch lp:charms/trusty/foo
cd foo
juju test --set-e -v -p AMULET_HTTP_PROXY 015-basic-trusty-icehouse
* To re-run a test module against an already deployed environment (one
that was deployed by a previous call to 'juju test --set-e'):
./tests/015-basic-trusty-icehouse
* Even with --set-e, `juju test` will tear down the deployment when all
tests pass. The following work flow may be more effective when
iterating on test writing.
bzr branch lp:charms/trusty/foo
cd foo
./tests/setup/00-setup
juju bootstrap
./tests/015-basic-trusty-icehouse
# make some changes, run tests again
./tests/015-basic-trusty-icehouse
# make some changes, run tests again
./tests/015-basic-trusty-icehouse
* There may be test definitions in the tests/ dir which are not set +x
executable. This is generally true for deprecated releases, or for
upcoming releases which are not yet validated and enabled. To enable
and run these tests:
bzr branch lp:charms/trusty/foo
cd foo
ls tests
chmod +x tests/017-basic-trusty-kilo
./tests/setup/00-setup
juju bootstrap
./tests/017-basic-trusty-kilo
Additional notes:
* Use DEBUG to turn on debug logging, use ERROR otherwise.
u = OpenStackAmuletUtils(ERROR)
u = OpenStackAmuletUtils(DEBUG)
* To interact with the deployed environment:
export OS_USERNAME=admin
export OS_PASSWORD=openstack
export OS_TENANT_NAME=admin
export OS_REGION_NAME=RegionOne
export OS_AUTH_URL=${OS_AUTH_PROTOCOL:-http}://`juju-deployer -e trusty -f keystone`:5000/v2.0
keystone user-list
glance image-list

9
tests/README.md Normal file
View File

@ -0,0 +1,9 @@
# Overview
This directory provides Amulet tests to verify basic deployment functionality
from the perspective of this charm, its requirements and its features, as
exercised in a subset of the full OpenStack deployment test bundle topology.
For full details on functional testing of OpenStack charms please refer to
the [functional testing](http://docs.openstack.org/developer/charm-guide/testing.html#functional-testing)
section of the OpenStack Charm Guide.

View File

@ -130,13 +130,13 @@ class RmqBasicDeployment(OpenStackAmuletDeployment):
def _initialize_tests(self):
"""Perform final initialization before tests get run."""
# Access the sentries for inspecting service units
self.rmq0_sentry = self.d.sentry.unit['rabbitmq-server/0']
self.rmq1_sentry = self.d.sentry.unit['rabbitmq-server/1']
self.rmq2_sentry = self.d.sentry.unit['rabbitmq-server/2']
self.keystone_sentry = self.d.sentry.unit['keystone/0']
self.mysql_sentry = self.d.sentry.unit['mysql/0']
self.cinder_sentry = self.d.sentry.unit['cinder/0']
self.nrpe_sentry = self.d.sentry.unit['nrpe/0']
self.rmq0_sentry = self.d.sentry['rabbitmq-server'][0]
self.rmq1_sentry = self.d.sentry['rabbitmq-server'][1]
self.rmq2_sentry = self.d.sentry['rabbitmq-server'][2]
self.keystone_sentry = self.d.sentry['keystone'][0]
self.mysql_sentry = self.d.sentry['mysql'][0]
self.cinder_sentry = self.d.sentry['cinder'][0]
self.nrpe_sentry = self.d.sentry['nrpe'][0]
u.log.debug('openstack release val: {}'.format(
self._get_openstack_release()))
u.log.debug('openstack release str: {}'.format(
@ -592,16 +592,13 @@ class RmqBasicDeployment(OpenStackAmuletDeployment):
def test_910_pause_and_resume(self):
"""The services can be paused and resumed. """
u.log.debug('Checking pause and resume actions...')
unit_name = "rabbitmq-server/0"
unit = self.d.sentry.unit[unit_name]
assert u.status_get(self.rmq0_sentry)[0] == "active"
assert u.status_get(unit)[0] == "active"
action_id = u.run_action(self.rmq0_sentry, "pause")
assert u.wait_on_action(action_id), "Pause action failed."
assert u.status_get(self.rmq0_sentry)[0] == "maintenance"
action_id = self._run_action(unit_name, "pause")
assert self._wait_on_action(action_id), "Pause action failed."
assert u.status_get(unit)[0] == "maintenance"
action_id = self._run_action(unit_name, "resume")
assert self._wait_on_action(action_id), "Resume action failed."
assert u.status_get(unit)[0] == "active"
action_id = u.run_action(self.rmq0_sentry, "resume")
assert u.wait_on_action(action_id), "Resume action failed."
assert u.status_get(self.rmq0_sentry)[0] == "active"
u.log.debug('OK')

View File

@ -1,21 +0,0 @@
#!/bin/sh
# Install Juju Amulet and any other applications that are needed for the tests.
set -x
# Check if amulet is installed before adding repository and updating apt-get.
dpkg -s amulet
if [ $? -ne 0 ]; then
sudo add-apt-repository -y ppa:juju/stable
sudo apt-get update
sudo apt-get install -y amulet
fi
# Install any additional python packages, or software here.
sudo apt-get install -y python python-pika python3-requests python3-setuptools
# Set http proxy if amulet is using one.
[[ -n "$AMULET_HTTP_PROXY" ]] && export http_proxy="$AMULET_HTTP_PROXY" && export https_proxy="$AMULET_HTTP_PROXY"
sudo easy_install3 python3-pika

View File

@ -1,30 +0,0 @@
#!/usr/bin/python3
import amulet
import pika
d = amulet.Deployment(series='trusty')
d.add('rabbitmq-server')
d.expose('rabbitmq-server')
# Don't forget to expose using d.expose(service)
try:
d.setup(timeout=3000)
d.sentry.wait()
except amulet.helpers.TimeoutError:
amulet.raise_status(amulet.SKIP, msg="Environment wasn't stood up in time")
except:
raise
server = d.sentry.unit['rabbitmq-server/0']
host = server.info['public-address']
try:
connection = pika.BlockingConnection(pika.ConnectionParameters(host=host))
except Exception as e:
amulet.raise_status(
amulet.FAIL,
str(e)
)

View File

@ -1,92 +0,0 @@
#!/usr/bin/python3
# This Amulet test performs a basic deploy and checks if rabbitmq is running.
import amulet
import os
import socket
import ssl
from deploy_common import CA
# The number of seconds to wait for the environment to setup.
seconds = 900
# Get the directory in this way to load the files from the tests directory.
path = os.path.abspath(os.path.dirname(__file__))
ca = CA()
# Create a dictionary for the rabbitmq configuration.
rabbitmq_configuration = {
'ssl_enabled': True,
'ssl_key': ca.get_key(),
'ssl_cert': ca.get_cert(),
'ssl_port': 5671
}
d = amulet.Deployment(series='trusty')
# Add the rabbitmq-server charm to the deployment.
d.add('rabbitmq-server')
# Configure options on the rabbitmq-server.
d.configure('rabbitmq-server', rabbitmq_configuration)
# Expose the server so we can connect.
d.expose('rabbitmq-server')
try:
# Execute the deployer with the current mapping.
d.setup(timeout=seconds)
except amulet.helpers.TimeoutError:
message = 'The environment did not setup in %d seconds.' % seconds
# The SKIP status enables skip or fail the test based on configuration.
amulet.raise_status(amulet.SKIP, msg=message)
except:
raise
print('The rabbitmq-server has been successfully deployed.')
###############################################################################
# Verify that the rabbit service is running on the deployed server.
###############################################################################
rabbitmq_sentry = d.sentry.unit['rabbitmq-server/0']
# Get the public address for rabbitmq-server instance.
server_address = rabbitmq_sentry.info['public-address']
# Create the command that checks if the rabbitmq-server service is running.
command = 'rabbitmqctl status'
print(command)
# Execute the command on the deployed service.
output, code = rabbitmq_sentry.run(command)
print(output)
# Check the return code for the success and failure of this test.
if (code != 0):
message = 'The ' + command + ' did not return the expected code of 0.'
amulet.raise_status(amulet.FAIL, msg=message)
else:
print('The rabbitmq-server is running on %s' % server_address)
###############################################################################
# Test the ssl certificate.
###############################################################################
# Get the port for ssl_port instance.
server_port = rabbitmq_configuration['ssl_port']
print('Testing ssl connection to rabbitmq-server.')
try:
# Create a normal socket.
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Require a certificate from the server, since a self-signed certificate
# was used, the ca_certs must be the server certificate file itself.
ssl_sock = ssl.wrap_socket(s, ca_certs=ca.ca_cert_path(),
cert_reqs=ssl.CERT_REQUIRED)
# Connect to the rabbitmq server using ssl.
ssl_sock.connect((server_address, server_port))
# Get the certificate.
certificate = ssl_sock.getpeercert()
# SSL socket connected and got the certificate, this passes the ssl test!
print('Connected to the rabbitmq-server {0}:{1} using ssl!'.format(
server_address, server_port))
except Exception as e:
message = 'Failed to create an ssl connection to {0}:{1}\n{2}'.format(
server_address, server_port, str(e))
amulet.raise_status(amulet.FAIL, msg=message)
finally:
ssl_sock.close()
print('The rabbitmq-server passed the basic deploy test!')

View File

@ -1,94 +0,0 @@
#!/usr/bin/python3
import amulet
import os
import pika
import subprocess
import time
# The juju environment should be bootstrapped at this point, but depending
# on the test environment an http proxy may be necessary. Check to see if
# one is configured via the environment variable and set it if it is.
# The settings of these proxy variables should only survive the current
# lifetime of the bootstrapped environment and will be torn down for the
# next test.
if os.environ.get('AMULET_HTTP_PROXY', None):
proxy_setting = 'http-proxy={}'.format(os.environ['AMULET_HTTP_PROXY'])
subprocess.call(['juju', 'set-env', proxy_setting])
if os.environ.get('AMULET_HTTPS_PROXY', None):
proxy_setting = 'https-proxy={}'.format(os.environ['AMULET_HTTPS_PROXY'])
subprocess.call(['juju', 'set-env', proxy_setting])
d = amulet.Deployment(series='trusty')
d.add('rabbitmq-server')
d.configure('rabbitmq-server', {
"source": "deb http://www.rabbitmq.com/debian/ testing main",
"key": """-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1.4.6 (GNU/Linux)
mQGiBEaOQ/IRBACs/n609zN+OzlK9qDkFWwYKfPG+BlgqBj5MSy0XE2K8cE3bWSV
2WftTe/TGEfW0hknXt1PyBla0cnO9Up1xCn142vo8bvUug8WjrxLQBBiAf11FAOR
dt9roGe4IWw/Lakgb88re09ZYKmOL9H7MEpvMqtjdWjFSq4zeeGa8rGEswCgnQLb
ZD/MNlUNQwQVCs+vVRdgpzcD+QELSc2EeYl4tef0NiUaZQt+mjFTs3DjQNDTjXao
ETVAqECx4kavcshx5tSE5JbbQPIMiUgh0h9J3z3uZsBVnx6P82aW/QTw+jLhsQry
/i3Z/+pS66mk6EWhAAYF/SPVqM/06BZh0ZvUmeG9WGGJXD9CUN1Wfi2mt42L2zhT
xg3uBACoIs5/GORi0H2i+blLiFSxTroXw+TdxiP+mfjdPho0oXJQTljXBgG70VfX
XW9sWsYtekqXBsmwMcbCZTjZGul/8jAUlUoYfthRw9KpP9N8Q7wB8Flx9jEv0M0H
tV1KTrLuXNZvEAB1sECMa7RRrV1yO4wyYDsOXiZNTL6rYugOU7QwUmFiYml0TVEg
UmVsZWFzZSBTaWduaW5nIEtleSA8aW5mb0ByYWJiaXRtcS5jb20+iGAEExECACAF
AkaOQ/ICGwMGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAAKCRD3uM6mBW6OVkymAJ0R
6MwiZNRuTAttgYf1Xe7dK7HpzACfZioV/LqnDh7XvcTJEl+r4GB19by5Ag0ERo5D
+xAIAKu1ZxtAZjwlNLb0L5uwrEP7nTbRTNUYoEtE8+CNDSLLlmRIvBriKYNGicnz
Ebq2kDnAoyH38ACIMNayrkqc6I4l3BD2sv7zPZCd4qAbyFCu6gnewTANTWkVuH60
R65QQ8pM8sM+VZAMSoMkDSP4u248xOzFyGgVYuuWuR/sIRcaA02FW9TGvZQ7fNoF
rf6UbKSYkjpY767IW8q0b68vKzSLw0GQvH+dsvhaj80hjKJ06+IZ9Gdi/b4+AIT2
YWyWmrHo2QhnUmsarNdtusesQGQtiYgZw95PJJkzR0AttuPPfPNGLYZtVJenvOCC
jsK5uUL3/eEQ3UWGs+BKEyA/qLMAAwUH/2kIFCdgCw2DnL87TO+vruhGjsM7NjXf
57F4ojTdblFd6AerjRhMgICdzCF9WkFROdBSyQ/GajoNU81kbHZglxmKyKkVwWEb
G7pmSIc/sk5Z7OP/zrg4h8ZGzvMbRy0XLf86lQhbDE3AcHMeJCcShIWAHAbygnYW
j0KRhZiyqxqx4mrZQDZEWI7S1G9YNvgu1GS9EEKEpmxDEOME9nJZLi9o7mTeD1QV
TyOzWHkpQ42QcgrFuG7RMxDaQK6bdinNTl8aPmMoPamGzotSt4aMoVMiNxjatnlH
pqQ5UJlqbB5FGLnwJ0773WzgRdxIwSIxkFhL/Mq4agf4an8151kqcZCISQQYEQIA
CQUCRo5D+wIbDAAKCRD3uM6mBW6OVhLmAKCYY152B/10n7aUNKejs92NsNAnPACf
ZwbDOKBXGfkCPuRx5j/AGneASNU=
=Ry+c
-----END PGP PUBLIC KEY BLOCK-----""",
"management_plugin": True,
})
d.expose('rabbitmq-server')
# Don't forget to expose using d.expose(service)
try:
# TODO(billy-olsen), juju test --timeout fails to pass the timeout values
# into the environment and the charm isn't the best of places to select
# a viable timeout since so muc is attributed to the environment anyways.
# Need to fix this the right way, but for now we'll bump the timeout.
d.setup(timeout=2700)
d.sentry.wait()
except amulet.helpers.TimeoutError:
amulet.raise_status(amulet.SKIP, msg="Environment wasn't stood up in time")
except:
raise
server = d.sentry.unit['rabbitmq-server/0']
host = server.info['public-address']
# TODO(wolsen) It looks like in rabbitmq version 3.3.0 the guest count was
# disabled by default and therefore we need to do the following bit of code
# in order to allow guest access for the test to succeed.
server.run('echo "[{rabbit, [{loopback_users, []}]}]." > '
'/etc/rabbitmq/rabbitmq.config')
server.run('service rabbitmq-server restart')
try:
connection = pika.BlockingConnection(pika.ConnectionParameters(host=host))
except Exception as e:
amulet.raise_status(
amulet.FAIL,
str(e)
)

View File

@ -1,251 +0,0 @@
#!/usr/bin/python3
# This Amulet test deploys rabbitmq-server, and the related charms.
import amulet
import os
import subprocess
import time
# The number of seconds to wait for the environment to setup.
seconds = 2700
# The number of units to scale rabbitmq-server to.
scale = 2
# The port that amqp traffic is sent on.
amqp_port = '5672'
# The directory to use as a block devie for the ceph
devices = '/srv/osd1'
# The default version of ceph does not support directories as devices.
havana = 'cloud:precise-updates/havana'
# Create a dictionary of configuration values for ceph.
ceph_configuration = {
'fsid': 'ecbb8960-0e21-11e2-b495-83a88f44db01',
'monitor-secret': 'AQBomftSyK1LORAAhg71ukxBxN9ml90stexqEw==',
'osd-devices': devices,
'source': havana
}
# Create a dictionary of configuration values for cinder.
cinder_configuration = {
'block-device': 'None'
}
# Create a dictionary of the rabbit configuration values.
rabbit_configuration = {
'vip': '192.168.77.11',
'vip_cidr': 19,
'vip_iface': 'eth0',
'ha-bindiface': 'eth0',
'ha-mcastport': 5406,
'rbd-size': '2G',
'rbd-name': 'testrabbit1'
}
# The AMQP package is only available for python version 2.x.
python2 = '/usr/bin/python'
if not os.path.isfile(python2):
error_message = 'Error, python version 2 is required for this test.'
amulet.raise_status(amulet.FAIL, msg=error_message)
series = 'trusty'
d = amulet.Deployment(series=series)
# Add rabbitmq-server to the deployment.
d.add('rabbitmq-server', units=scale)
# TODO(billy-olsen) - Rework this following set of code to be more in-line
# with how the other openstack services are done. For now, we want to test
# the current branch with the appropriate branches of related charms in
# order to test /next with /next branches and /trunk with /trunk branches.
stable = False
def determine_charm_branches(services):
if stable:
for svc in services:
temp = 'lp:charms/{}'
svc['location'] = temp.format(svc['name'])
else:
for svc in services:
temp = 'lp:~openstack-charmers/charms/{}/{}/next'
svc['location'] = temp.format(series, svc['name'])
return services
def add_services(services):
"""
Adds services to the deployment. The input is a list of dicts with
the key of 'name' name for the service name. The branch location
will be determined automatically.
"""
services = determine_charm_branches(services)
for svc in services:
d.add(svc['name'], charm=svc['location'])
services_to_add = [
{'name': 'ceph'},
{'name': 'cinder'},
{'name': 'hacluster'},
]
# Add the services to the deployment
add_services(services_to_add)
# The ceph charm requires configuration to deploy successfully.
d.configure('ceph', ceph_configuration)
# Configure the cinder charm.
d.configure('cinder', cinder_configuration)
# Configure the rabbit charm.
d.configure('rabbitmq-server', rabbit_configuration)
# Add relation from rabbitmq-server to ceph testing the ceph relation.
d.relate('rabbitmq-server:ceph', 'ceph:client')
# Add relation from rabbitmq-server to cinder testing the amqp relation.
d.relate('rabbitmq-server:amqp', 'cinder:amqp')
# Add relation from rabibtmq-server to hacluster testing the ha relation.
d.relate('rabbitmq-server:ha', 'hacluster:ha')
# Expose the rabbitmq-server.
d.expose('rabbitmq-server')
try:
# Execute the deployer with the current mapping.
d.setup(timeout=seconds)
# Wait for the relation to finish the transations.
d.sentry.wait(seconds)
except amulet.helpers.TimeoutError:
message = 'The environment did not setup in %d seconds.' % seconds
# The SKIP status enables skip or fail the test based on configuration.
amulet.raise_status(amulet.FAIL, msg=message)
except:
raise
print('The environment successfully deployed.')
# Create a counter to make the messages unique.
counter = 1
# Get the directory in this way to load the files from the tests directory.
path = os.path.abspath(os.path.dirname(__file__))
# Create a path to the python test file to call.
amqp_tester = os.path.join(path, 'amqp_tester.py')
if not os.path.isfile(amqp_tester):
error_message = 'Unable to locate python test file %s' % amqp_tester
amulet.raise_status(amulet.FAIL, msg=error_message)
# Verify the ceph unit was created.
ceph_unit = d.sentry.unit['ceph/0']
# Verify the cinder unit was created.
cinder_unit = d.sentry.unit['cinder/0']
rabbit_units = []
for n in range(scale):
# Get each rabbitmq unit that was deployed.
rabbit_units.append(d.sentry.unit['rabbitmq-server/%d' % n])
# Iterate over every rabbitmq-unit to get the different relations.
for rabbit_unit in rabbit_units:
###########################################################################
# Test Relations
###########################################################################
# Verify the ceph relation was created for the rabbit unit.
rabbit_relation = rabbit_unit.relation('ceph', 'ceph:client')
print('rabbit relation to ceph:')
for key, value in rabbit_relation.items():
print(key, value)
# Verify the amqp relation was created for the rabbit unit.
rabbit_relation = rabbit_unit.relation('amqp', 'cinder:amqp')
print('rabbit relation to amqp:')
for key, value in rabbit_relation.items():
print(key, value)
# The hacluster charm is a subordinate, since the relation-sentry is also
# a subordinate charm no sentry is created for the hacluster relation.
# Verify the rabbit relation was created with the ceph unit.
ceph_relation = ceph_unit.relation('client', 'rabbitmq-server:ceph')
print('ceph relation to rabbitmq-server:')
for key, value in ceph_relation.items():
print(key, value)
# Verify the rabbit relation was created with the cinder unit.
cinder_relation = cinder_unit.relation('amqp', 'rabbitmq-server:amqp')
print('cinder relation to rabbitmq-server:')
for key, value in cinder_relation.items():
print(key, value)
###########################################################################
# Test AMQP
###########################################################################
# The AMQP python library is only available for python2 at this time.
# Call out a command to run the python2 code to test the AMQP protocol.
# Get the public address for rabbitmq-server instance.
server_address = rabbit_unit.info['public-address']
# Create a time stamp to help make the AMQP message unique.
time_stamp = time.strftime('%F %r')
# Create the message to send on the AMPQ protocol.
amqp_message = "Message #{0} to send using the AMPQ protocol {1}".format(
counter, time_stamp)
# Create the command with arguments that sends the message.
send_command = [python2, amqp_tester, server_address, amqp_port,
amqp_message]
print(send_command)
# Call the python command to send the AMQP message to the server.
output = subprocess.check_output(send_command)
# Create the command with arguments to receive messages.
receive_command = [python2, amqp_tester, server_address, amqp_port]
print(receive_command)
# Call the python command to receive the AMQP message from the same server.
output = subprocess.check_output(receive_command)
# The output is a byte string so convert the message to a byte string.
if output.find(amqp_message.encode()) == -1:
print('The AMQP test to {0}:{1} failed.'.format(server_address,
amqp_port))
amulet.raise_status(amulet.FAIL, msg=output)
else:
print('The AMQP test to {0}:{1} completed successfully.'.format(
server_address, amqp_port))
counter += 1
###########################################################################
# Verify that the rabbitmq cluster status is correct.
###########################################################################
# Create the command that checks if the rabbitmq-server service is running.
command = 'rabbitmqctl cluster_status'
print(command)
# Execute the command on the deployed service.
output, code = rabbit_unit.run(command)
print(output)
# Check the return code for the success and failure of this test.
if (code != 0):
message = 'The ' + command + ' did not return the expected code of 0.'
amulet.raise_status(amulet.FAIL, msg=message)
else:
print('The rabbitmq-server cluster status is OK.')
###############################################################################
# Test the AMQP messages can be sent from and read from another.
###############################################################################
# Get the public address for rabbitmq-server instance 0.
send_address = rabbit_units[0].info['public-address']
# Create a message to send from instance 0 and read it from instance 1.
amqp_message = "Message #{0} sent from {1} using the AMQP protocol.".format(
counter, send_address)
counter += 1
# Create the command that sends the message to instance 0.
send_command = [python2, amqp_tester, send_address, amqp_port, amqp_message]
print(send_command)
output = subprocess.check_output(send_command)
# Get the public address for rabbitmq-server instance 1.
receive_address = rabbit_units[1].info['public-address']
# Create the command that receives the message from instance 1.
recieve_command = [python2, amqp_tester, receive_address, amqp_port]
print(recieve_command)
output = subprocess.check_output(receive_command)
# The output is a byte string so convert the message to a byte string.
if output.find(amqp_message.encode()) == -1:
print(output)
message = 'Server {0} did not receive the AMQP message "{1}"'.format(
receive_address, amqp_message)
amulet.raise_status(amulet.FAIL, msg=message)
else:
print('Server {0} received the AMQP message sent from {1}'.format(
receive_address, send_address))
print('The rabbitmq-server charm passed this relations test.')

View File

@ -1,117 +0,0 @@
#!/usr/bin/python3
import amulet
import pika
import time
d = amulet.Deployment(series='trusty')
d.add('rabbitmq-server')
d.expose('rabbitmq-server')
# Don't forget to expose using d.expose(service)
try:
# TODO(billy-olsen), juju test --timeout fails to pass the timeout values
# into the environment and the charm isn't the best of places to select
# a viable timeout since so muc is attributed to the environment anyways.
# Need to fix this the right way, but for now we'll bump the timeout.
d.setup(timeout=2700)
d.sentry.wait()
except amulet.helpers.TimeoutError:
amulet.raise_status(amulet.SKIP, msg="Environment wasn't stood up in time")
except:
raise
server = d.sentry.unit['rabbitmq-server/0']
host = server.info['public-address']
# Connects without ssl
try:
connection = pika.BlockingConnection(pika.ConnectionParameters(host=host,
ssl=False))
except Exception as e:
amulet.raise_status(
amulet.FAIL,
"Insecure connection failed with ssl=off: {}".format(str(e))
)
# Doesn't connect with ssl
try:
connection = pika.BlockingConnection(pika.ConnectionParameters(host=host,
ssl=True))
except Exception as e:
pass
else:
amulet.raise_status(
amulet.FAIL,
'SSL enabled when it shouldn\'t.'
)
d.configure('rabbitmq-server', {
'ssl': 'on'
})
# There's a race for changing the configuration of a deployment.
# The configure from the juju client side happens fairly quickly, and the
# sentry.wait() can fire before the config-changed hooks do, which causes
# the wait to end...
time.sleep(10)
d.sentry.wait()
# Connects without ssl
try:
connection = pika.BlockingConnection(pika.ConnectionParameters(host=host,
ssl=False))
except Exception as e:
amulet.raise_status(
amulet.FAIL,
"Insecure connection fails with ssl=on: {}".format(str(e))
)
# Connects with ssl
try:
connection = pika.BlockingConnection(pika.ConnectionParameters(host=host,
port=5671,
ssl=True))
except Exception as e:
amulet.raise_status(
amulet.FAIL,
"Secure connection fails with ssl=on"
)
d.configure('rabbitmq-server', {
'ssl': 'only'
})
# There's a race for changing the configuration of a deployment.
# The configure from the juju client side happens fairly quickly, and the
# sentry.wait() can fire before the config-changed hooks do, which causes
# the wait to end...
time.sleep(10)
d.sentry.wait()
# Doesn't connect without ssl
try:
connection = pika.BlockingConnection(pika.ConnectionParameters(host=host,
ssl=False))
except Exception as e:
pass
else:
amulet.raise_status(
amulet.FAIL,
"Connects without SSL when it shouldn't"
)
# Connects with ssl
try:
connection = pika.BlockingConnection(pika.ConnectionParameters(host=host,
port=5671,
ssl=True))
except Exception as e:
amulet.raise_status(
amulet.FAIL,
"Secure connection fails with ssl=only: {}".format(str(e))
)

View File

@ -1,118 +0,0 @@
#!/usr/bin/python3
# This Amulet test exercises the configuration options for rabbitmq-server.
import amulet
import os
import socket
import ssl
from deploy_common import CA
# The number of seconds to wait for the environment to setup.
seconds = 2700
# Get the directory in this way to load the files from the tests directory.
path = os.path.abspath(os.path.dirname(__file__))
ca = CA()
privateKey = ca.get_key()
certificate = ca.get_cert()
# Create a dictionary of all the configuration values.
rabbit_configuration = {
'management_plugin': True,
'ssl_enabled': True,
'ssl_port': 5999,
'ssl_key': privateKey,
'ssl_cert': certificate,
}
d = amulet.Deployment(series='trusty')
# Add the rabbitmq-server charm to the deployment.
d.add('rabbitmq-server')
# Configure all the options on rabbitmq-server.
d.configure('rabbitmq-server', rabbit_configuration)
# Expose the rabbitmq-server.
d.expose('rabbitmq-server')
try:
# Execute the deployer with the current mapping.
d.setup(timeout=seconds)
# Wait for the relation to finish the transations.
d.sentry.wait(seconds)
except amulet.helpers.TimeoutError:
message = 'The environment did not setup in %d seconds.' % seconds
# The SKIP status enables skip or fail the test based on configuration.
amulet.raise_status(amulet.SKIP, msg=message)
except:
raise
rabbit_unit = d.sentry.unit['rabbitmq-server/0']
###############################################################################
# Verify that the rabbit service is running on the deployed server.
###############################################################################
# Create the command that checks if the rabbitmq-server service is running.
command = 'rabbitmqctl status'
print(command)
# Execute the command on the deployed service.
output, code = rabbit_unit.run(command)
print(output)
# Check the return code for the success and failure of this test.
if (code != 0):
message = 'The ' + command + ' did not return the expected code of 0.'
amulet.raise_status(amulet.FAIL, msg=message)
else:
print('The rabbitmq-server is running.')
###############################################################################
# Verify the configuration values.
###############################################################################
# Get the contents of the private key from the rabbitmq-server
contents = rabbit_unit.file_contents('/etc/rabbitmq/rabbit-server-privkey.pem')
# Verify the private key was saved on the rabbitmq server correctly.
if contents != privateKey:
message = 'The private keys did not match!'
amulet.raise_status(amulet.FAIL, msg=message)
else:
print('The private keys was configured properly on the rabbitmq server.')
# Get the contents of the certificate from the rabbitmq-server.
contents = rabbit_unit.file_contents('/etc/rabbitmq/rabbit-server-cert.pem')
# Verify the certificate was saved on the rabbitmq server correctly.
if contents != certificate:
message = 'The certificates did not match!'
amulet.raise_status(amulet.FAIL, msg=message)
else:
print('The certificate was configured properly on the rabbitmq server.')
# Get the public address for rabbitmq-server instance.
rabbit_host = rabbit_unit.info['public-address']
###############################################################################
# Verify that SSL is set up on the non-default port.
###############################################################################
# Get the port for ssl_port instance.
ssl_port = rabbit_configuration['ssl_port']
try:
# Create a normal socket.
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Require a certificate from the server, since a self-signed certificate
# was used, the ca_certs must be the server certificate file itself.
ssl_sock = ssl.wrap_socket(s, ca_certs=ca.ca_cert_path(),
cert_reqs=ssl.CERT_REQUIRED)
# Connect to the rabbitmq server using ssl.
ssl_sock.connect((rabbit_host, ssl_port))
# Get the certificate.
certificate = ssl_sock.getpeercert()
# SSL scoket connected and got the certificate, this passes the ssl test!
print('Connected to the rabbitmq-server {0}:{1} using ssl!'.format(
rabbit_host, ssl_port))
except Exception as e:
message = 'Failed to create an ssl connection to {0}:{1}\n{2}'.format(
rabbit_host, ssl_port, str(e))
amulet.raise_status(amulet.FAIL, msg=message)
finally:
ssl_sock.close()
print('The rabbitmq-server passed the configuration tests.')

View File

@ -1,85 +0,0 @@
#!/usr/bin/python
#
# This Amulet test deploys rabbitmq-server
#
# Note: We use python2, because pika doesn't support python3
import amulet
import pika
import telnetlib
# The number of seconds to wait for the environment to setup.
seconds = 2700
d = amulet.Deployment(series="trusty")
# Add the rabbitmq-server charm to the deployment.
d.add('rabbitmq-server', units=2)
# Create a configuration.
configuration = {'mirroring-queues': True,
'management_plugin': True}
d.configure('rabbitmq-server', configuration)
d.expose('rabbitmq-server')
try:
d.setup(timeout=seconds)
d.sentry.wait(seconds)
except amulet.helpers.TimeoutError:
message = 'The environment did not setup in %d seconds.' % seconds
amulet.raise_status(amulet.SKIP, msg=message)
except:
raise
rabbit_unit = d.sentry.unit['rabbitmq-server/0']
rabbit_unit2 = d.sentry.unit['rabbitmq-server/1']
commands = ['service rabbitmq-server status',
'rabbitmqctl cluster_status']
for cmd in commands:
output, code = rabbit_unit.run(cmd)
message = cmd + ' | exit code: %d.' % code
print(message)
print(output)
if code != 0:
amulet.raise_status(amulet.FAIL, msg=message)
rabbit_addr1 = rabbit_unit.info["public-address"]
rabbit_port = "5672"
rabbit_url = 'amqp://guest:guest@%s:%s/%%2F' % (rabbit_addr1, rabbit_port)
print('Connecting to %s' % rabbit_url)
conn1 = pika.BlockingConnection(pika.connection.URLParameters(rabbit_url))
channel = conn1.channel()
print('Declaring queue')
channel.queue_declare(queue='hello')
orig_msg = 'Hello World!'
print('Publishing message: %s' % orig_msg)
channel.basic_publish(exchange='',
routing_key='hello',
body=orig_msg)
print('stopping rabbit in unit 0')
rabbit_unit.run('service rabbitmq-server stop')
print('Consuming message from second unit')
rabbit_addr2 = rabbit_unit2.info["public-address"]
rabbit_url2 = 'amqp://guest:guest@%s:%s/%%2F' % (rabbit_addr2, rabbit_port)
conn2 = pika.BlockingConnection(pika.connection.URLParameters(rabbit_url2))
channel2 = conn2.channel()
method_frame, header_frame, body = channel2.basic_get('hello')
if method_frame:
print(method_frame, header_frame, body)
assert body == orig_msg, '%s != %s' % (body, orig_msg)
channel2.basic_ack(method_frame.delivery_tag)
else:
raise Exception('No message returned')
# check the management plugin is running
mgmt_port = "15672"
print('Checking management port')
telnetlib.Telnet(rabbit_addr2, mgmt_port)

View File

@ -1,71 +0,0 @@
#!/usr/bin/python3
# This Amulet test performs a basic deploy and checks if rabbitmq is running.
import amulet
import time
# The number of seconds to wait for the environment to setup.
seconds = 900
# Create a dictionary for the rabbitmq configuration.
rabbitmq_configuration = {
'stats_cron_schedule': '*/1 * * * *'
}
d = amulet.Deployment(series='trusty')
# Add the rabbitmq-server charm to the deployment.
d.add('rabbitmq-server')
# Configure options on the rabbitmq-server.
d.configure('rabbitmq-server', rabbitmq_configuration)
# Expose the server so we can connect.
d.expose('rabbitmq-server')
# XXX Remove charm= once this branch lands in the charm store
d.add('nrpe-external-master',
charm='lp:~gnuoy/charms/trusty/nrpe/services-rewrite')
d.relate('rabbitmq-server:nrpe-external-master',
'nrpe-external-master:nrpe-external-master')
try:
# Execute the deployer with the current mapping.
d.setup(timeout=seconds)
except amulet.helpers.TimeoutError:
message = 'The environment did not setup in %d seconds.' % seconds
# The SKIP status enables skip or fail the test based on configuration.
amulet.raise_status(amulet.SKIP, msg=message)
except:
raise
print('The rabbitmq-server has been successfully deployed and related '
'to nrpe-external-master.')
###############################################################################
# # Verify nagios checks
###############################################################################
rabbitmq_sentry = d.sentry.unit['rabbitmq-server/0']
command = 'bash -c "$(egrep -oh /usr/local.* ' \
'/etc/nagios/nrpe.d/check_rabbitmq.cfg)"'
print(command)
output, code = rabbitmq_sentry.run(command)
print(output)
if (code != 0):
message = 'The ' + command + ' did not return the expected code of 0.'
amulet.raise_status(amulet.FAIL, msg=message)
else:
print('The rabbitmq-server check_rabbitmq is OK')
print('Sleeping 70 seconds to make sure the monitoring cron has run')
time.sleep(70)
command = 'bash -c "$(egrep -oh /usr/local.* ' \
'/etc/nagios/nrpe.d/check_rabbitmq_queue.cfg)"'
print(command)
output, code = rabbitmq_sentry.run(command)
print(output)
if (code != 0):
message = 'The ' + command + ' did not return the expected code of 0.'
amulet.raise_status(amulet.FAIL, msg=message)
else:
print('The rabbitmq-server check_rabbitmq_queue is OK')
# Success!
print('The rabbitmq-server passed the monitoring tests!')

View File

@ -1,65 +0,0 @@
#!/usr/bin/python
# This class uses Python to make AMQP calls to send and receive messages.
# To send an AMQP message call this module with a host, port, and message.
# To receive an AMQP message call this module with a host and port only.
import logging
import pika
import sys
def send(host, port, message, queue='test'):
""" Send an AMQP message to a host and port."""
connection = None
try:
parameters = pika.ConnectionParameters(host, port)
connection = pika.BlockingConnection(parameters)
channel = connection.channel()
channel.queue_declare(queue)
channel.basic_publish(exchange='', routing_key=queue, body=message)
print('Message published to {0}:{1}'.format(host, port))
except Exception as e:
print('Unable to send message to {0}:{1}'.format(host, port))
print(e)
finally:
if connection:
connection.close()
def callback(ch, method, properties, body):
""" Handle the callback when the channel receives a message. """
print(body)
def receive(host, port, queue='test'):
""" Connects to host and port, and consumes AMQP messages. """
connection = None
try:
parameters = pika.ConnectionParameters(host, port)
connection = pika.BlockingConnection(parameters)
channel = connection.channel()
channel.queue_declare(queue)
channel.basic_consume(callback, queue, no_ack=True)
except Exception as e:
print('Unable to receive message from {0}:{1}'.format(host, port))
print(e)
finally:
if connection:
connection.close()
# Needed to disable pika complaining about logging levels not set.
logging.basicConfig(level=logging.ERROR)
if len(sys.argv) == 3:
host = sys.argv[1]
port = int(sys.argv[2])
receive(host, port)
elif len(sys.argv) > 3:
host = sys.argv[1]
port = int(sys.argv[2])
message = ' '.join(sys.argv[3:])
send(host, port, message)
else:
print('Not enough arguments, host and port are required.')

View File

@ -1,52 +0,0 @@
# Copyright 2015 Canonical Limited.
# This file provides common functions for amulet tests for the rabbitmq-server
# juju charm.
import os
from charmhelpers.contrib.ssl.service import ServiceCA
class CA(object):
"""
Represents the certificate authority for use in RabbitMQ amulet tests.
"""
# The name of the rabbit certificate authority.
CA_NAME = 'rabbit-server-ca'
# Put the certificate authority in a temporary location since
# it is rebuilt for each amulet run.
CA_PATH = '/tmp/rabbit-server-ca'
# The common name for the certificate itself.
COMMON_NAME = 'rabbitmq-server'
def __init__(self):
self.ca = ServiceCA(self.CA_NAME, self.CA_PATH)
self.ca.init()
self.ca.get_or_create_cert(self.COMMON_NAME)
def _load_file(self, path):
contents = None
with open(path) as f:
contents = f.read()
return contents
def get_key(self):
"""
Returns the contents of the rabbitmq private key.
"""
key_path = os.path.join(self.CA_PATH, 'certs', 'rabbitmq-server.key')
return self._load_file(key_path)
def get_cert(self):
"""
Returns the contents of the rabbitmq certificate.
"""
cert_path = os.path.join(self.CA_PATH, 'certs', 'rabbitmq-server.crt')
return self._load_file(cert_path)
def ca_cert_path(self):
"""
Returns the certificate authority certificate path.
"""
return os.path.join(self.CA_PATH, 'cacert.pem')

View File

@ -1,17 +0,0 @@
#!/bin/bash
set -ex
sudo add-apt-repository --yes ppa:juju/stable
sudo apt-get update --yes
sudo apt-get install --yes amulet \
distro-info-data \
python-cinderclient \
python-distro-info \
python-glanceclient \
python-heatclient \
python-keystoneclient \
python-neutronclient \
python-novaclient \
python-pika \
python-swiftclient

View File

@ -1,21 +1,17 @@
bootstrap: true
reset: false
virtualenv: true
makefile:
- lint
- test
sources:
- ppa:juju/stable
packages:
- amulet
- distro-info-data
- python-ceilometerclient
- python-cinderclient
- python-distro-info
- python-glanceclient
- python-heatclient
- python-keystoneclient
- python-neutronclient
- python-novaclient
- python-pika
- python-swiftclient
# Bootstrap the model if necessary.
bootstrap: True
# Re-use bootstrap node instead of destroying/re-bootstrapping.
reset: True
# Use tox/requirements to drive the venv instead of bundletester's venv feature.
virtualenv: False
# Leave makefile empty, otherwise unit/lint tests will rerun ahead of amulet.
makefile: []
# Do not specify juju PPA sources. Juju is presumed to be pre-installed
# and configured in all test runner environments.
#sources:
# Do not specify or rely on system packages.
#packages:
# Do not specify python packages here. Use test-requirements.txt
# and tox instead. ie. The venv is constructed before bundletester
# is invoked.
#python-packages:

46
tox.ini
View File

@ -5,6 +5,8 @@ skipsdist = True
[testenv]
setenv = VIRTUAL_ENV={envdir}
PYTHONHASHSEED=0
AMULET_SETUP_TIMEOUT=2700
passenv = AMULET_* HOME TERM
install_command =
pip install --allow-unverified python-apt {opts} {packages}
commands = ostestr {posargs}
@ -24,6 +26,50 @@ commands = flake8 {posargs} hooks unit_tests tests
[testenv:venv]
commands = {posargs}
[testenv:func27-noop]
# DRY RUN - For Debug
basepython = python2.7
deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
commands =
bundletester -vl DEBUG -r json -o func-results.json --test-pattern "gate-*" -n --no-destroy
[testenv:func27]
# Charm Functional Test
# Run all gate tests which are +x (expected to always pass)
basepython = python2.7
deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
commands =
bundletester -vl DEBUG -r json -o func-results.json --test-pattern "gate-*" --no-destroy
[testenv:func27-smoke]
# Charm Functional Test
# Run a specific test as an Amulet smoke test (expected to always pass)
basepython = python2.7
deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
commands =
bundletester -vl DEBUG -r json -o func-results.json gate-basic-xenial-mitaka --no-destroy
[testenv:func27-dfs]
# Charm Functional Test
# Run all deploy-from-source tests which are +x (may not always pass!)
basepython = python2.7
deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
commands =
bundletester -vl DEBUG -r json -o func-results.json --test-pattern "dfs-*" --no-destroy
[testenv:func27-dev]
# Charm Functional Test
# Run all development test targets which are +x (may not always pass!)
basepython = python2.7
deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
commands =
bundletester -vl DEBUG -r json -o func-results.json --test-pattern "dev-*" --no-destroy
[flake8]
ignore = E402,E226
exclude = hooks/charmhelpers