From 8a0e22fb60a1580781e9f32a1d2ce596a8db0e93 Mon Sep 17 00:00:00 2001 From: Malini Kamalambal Date: Tue, 25 Nov 2014 16:27:07 -0500 Subject: [PATCH] Adds End To End Tests This patchset adds the end to end tests for Poppy. The test steps include, 1) Deploying a WP site on a cloud server 2) Creating a Poppy Service for the WP site 3) Reporting page load metrics using webpagtest Change-Id: I5f96cd21ac59806310f314e27da4f07642d3cfb0 --- tests/endtoend/__init__.py | 0 tests/endtoend/base.py | 91 +++++++ tests/endtoend/test_cdn_website.py | 99 +++++++ tests/endtoend/utils/__init__.py | 0 tests/endtoend/utils/config.py | 106 ++++++++ tests/endtoend/utils/heatclient.py | 116 ++++++++ tests/endtoend/utils/wptclient.py | 76 ++++++ tests/endtoend/wordpress-single.yaml | 390 +++++++++++++++++++++++++++ tests/etc/endtoend.conf | 27 ++ tests/test-requirements.txt | 2 + tox.ini | 2 +- 11 files changed, 908 insertions(+), 1 deletion(-) create mode 100644 tests/endtoend/__init__.py create mode 100644 tests/endtoend/base.py create mode 100644 tests/endtoend/test_cdn_website.py create mode 100644 tests/endtoend/utils/__init__.py create mode 100644 tests/endtoend/utils/config.py create mode 100644 tests/endtoend/utils/heatclient.py create mode 100644 tests/endtoend/utils/wptclient.py create mode 100644 tests/endtoend/wordpress-single.yaml create mode 100644 tests/etc/endtoend.conf diff --git a/tests/endtoend/__init__.py b/tests/endtoend/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/endtoend/base.py b/tests/endtoend/base.py new file mode 100644 index 00000000..94ff746f --- /dev/null +++ b/tests/endtoend/base.py @@ -0,0 +1,91 @@ +# Copyright (c) 2014 Rackspace, Inc. +# +# 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. + +import BeautifulSoup +from cafe.drivers.unittest import fixtures +import requests + +from tests.api.utils import client +from tests.endtoend.utils import config +from tests.endtoend.utils import heatclient +from tests.endtoend.utils import wptclient + + +class TestBase(fixtures.BaseTestFixture): + """Base class for End To End CDN Tests + + The tests do the following, + 1. Spins up a wordpress site on a cloud server. + 2. Create a Poppy service via API call using the origin & domain + feom Step 1. + 3. Measures the pageload performance of the CDN enabled website. + """ + + @classmethod + def setUpClass(cls): + + super(TestBase, cls).setUpClass() + + cls.auth_config = config.AuthConfig() + cls.auth_client = client.AuthClient() + auth_token, project_id = cls.auth_client.authenticate_user( + cls.auth_config.base_url, + cls.auth_config.user_name, + cls.auth_config.api_key) + + cls.poppy_config = config.PoppyConfig() + cls.url = cls.poppy_config.base_url + + cls.poppy_client = client.PoppyClient( + cls.url, auth_token, project_id, + serialize_format='json', + deserialize_format='json') + + cls.test_config = config.TestConfig() + + cls.heat_config = config.OrchestrationConfig() + heat_url = cls.heat_config.base_url + '/' + project_id + cls.heat_client = heatclient.HeatClient(heat_url=heat_url, + token=auth_token) + + cls.wpt_config = config.WebPageTestConfig() + cls.wpt_client = wptclient.WebpageTestClient( + wpt_url=cls.wpt_config.base_url, api_key=cls.wpt_config.api_key) + + def get_content(self, url): + """Get content from the url + + :param url: url to get content from + :returns: content fetched from the url + """ + response = requests.get(url) + content = BeautifulSoup.BeautifulSoup(response.text) + return content.findAll() + + def assertSameContent(self, origin_url, access_url): + """Asserts that the origin & access_url serve the same content + + :param origin: Origin website + :param access_url: CDN enabled url of the origin website + :returns: True/False + """ + origin_content = self.get_content(url=origin_url) + cdn_content = self.get_content(url=access_url) + self.assertEqual(origin_content, cdn_content) + + @classmethod + def tearDownClass(cls): + """Deletes the added resources.""" + super(TestBase, cls).tearDownClass() diff --git a/tests/endtoend/test_cdn_website.py b/tests/endtoend/test_cdn_website.py new file mode 100644 index 00000000..7d7552ca --- /dev/null +++ b/tests/endtoend/test_cdn_website.py @@ -0,0 +1,99 @@ +# coding= utf-8 + +# Copyright (c) 2014 Rackspace, Inc. +# +# 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. + +import random +import string +import uuid + +from tests.endtoend import base + + +class TestWebsiteCDN(base.TestBase): + + """Tests for CDN enabling a website.""" + + def setUp(self): + super(TestWebsiteCDN, self).setUp() + + def _random_string(length=12): + return ''.join([random.choice(string.ascii_letters) + for _ in range(length)]) + + self.stack_name = _random_string() + + self.domain_name = 'TestCDN-' + _random_string() + '.org' + + # Deploys a test website to a cloud server + self.heat_client.create_stack(yaml_path=self.heat_config.yaml_path, + stack_name=self.stack_name, + domain_name=self.domain_name) + print('Stack Name', self.stack_name) + print('Domain Name', self.domain_name) + + self.heat_client.wait_for_stack_status(stack_name=self.stack_name) + self.origin = self.heat_client.get_server_ip( + stack_name=self.stack_name) + print('Origin', self.origin) + + def test_enable_cdn(self): + + # Create a Poppy Service for the test website + domain_list = [{"domain": self.domain_name}] + origin_list = [{"origin": self.origin, + "port": 80, + "ssl": False}] + caching_list = [] + self.service_name = str(uuid.uuid1()) + + resp = self.poppy_client.create_service( + service_name=self.service_name, + domain_list=domain_list, + origin_list=origin_list, + caching_list=caching_list, + flavor_id=self.poppy_config.flavor) + + self.assertEqual(resp.status_code, 202) + self.poppy_client.wait_for_service_status( + service_name=self.service_name, + status='DEPLOYED') + + resp = self.poppy_client.get_service(service_name=self.service_name) + links = resp.json()['links'] + access_url = [link['href'] for link in links if + link['rel'] == 'access_url'] + access_url = 'http://' + access_url[0] + origin_url = 'http://' + self.origin + self.assertSameContent(origin_url=origin_url, access_url=access_url) + + # Benchmark page load metrics for the CDN enabled website + wpt_test_results = {} + for location in self.wpt_config.test_locations: + wpt_test_url = self.wpt_client.start_test(access_url=access_url, + test_location=location, + runs=2) + wpt_test_results[location] = wpt_test_url + '''self.wpt_client.wait_for_test_status(status='COMPLETE', + test_url=wpt_test_url) + wpt_test_results[location] = self.wpt_client.get_test_details( + test_url=wpt_test_url) + ''' + print(wpt_test_results) + + def tearDown(self): + self.heat_client.delete_stack(stack_name=self.stack_name) + self.poppy_client.delete_service(service_name=self.service_name) + super(TestWebsiteCDN, self).tearDown() diff --git a/tests/endtoend/utils/__init__.py b/tests/endtoend/utils/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/endtoend/utils/config.py b/tests/endtoend/utils/config.py new file mode 100644 index 00000000..c9d42c2c --- /dev/null +++ b/tests/endtoend/utils/config.py @@ -0,0 +1,106 @@ +# Copyright (c) 2014 Rackspace, Inc. +# +# 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. + +from cafe.engine.models import data_interfaces + + +class PoppyConfig(data_interfaces.ConfigSectionInterface): + """Defines the config values for poppy.""" + SECTION_NAME = 'poppy' + + @property + def base_url(self): + """poppy endpoint.""" + return self.get('base_url') + + @property + def flavor(self): + """poppy flavor to use in create service call.""" + return self.get('flavor') + + +class TestConfig(data_interfaces.ConfigSectionInterface): + """Defines the config values specific to test execution.""" + SECTION_NAME = 'test_configuration' + + @property + def provider_validation(self): + """Boolean value indicating if tests verify provider side details.""" + return self.get_boolean('provider_validation') + + @property + def status_check_retry_interval(self): + """Int value to set retry intervals for status check.""" + return int(self.get('status_check_retry_interval')) + + @property + def status_check_retry_timeout(self): + """Int value to set timeout for status check.""" + return int(self.get('status_check_retry_timeout')) + + +class AuthConfig(data_interfaces.ConfigSectionInterface): + """Defines the auth config values.""" + SECTION_NAME = 'auth' + + @property + def base_url(self): + """Auth endpoint.""" + return self.get('base_url') + + @property + def user_name(self): + """The name of the user, if applicable.""" + return self.get('user_name') + + @property + def api_key(self): + """The user's api key, if applicable.""" + return self.get_raw('api_key') + + +class OrchestrationConfig(data_interfaces.ConfigSectionInterface): + """Defines the Rackspace orchestration config values.""" + SECTION_NAME = 'orchestration' + + @property + def base_url(self): + """Orchestration base url.""" + return self.get('base_url') + + @property + def yaml_path(self): + """path to the heat yaml file.""" + return self.get('yaml_path') + + +class WebPageTestConfig(data_interfaces.ConfigSectionInterface): + """Defines the webpage test config values.""" + SECTION_NAME = 'webpagetest' + + @property + def base_url(self): + """Auth endpoint.""" + return self.get('base_url') + + @property + def api_key(self): + """The user's api key.""" + return self.get_raw('api_key') + + @property + def test_locations(self): + """Locations from which to test page load.""" + return self.get('test_locations').split(',') diff --git a/tests/endtoend/utils/heatclient.py b/tests/endtoend/utils/heatclient.py new file mode 100644 index 00000000..f8691606 --- /dev/null +++ b/tests/endtoend/utils/heatclient.py @@ -0,0 +1,116 @@ +# Copyright (c) 2014 Rackspace, Inc. +# +# 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. + +import json +import time + +import requests + + +class HeatClient(object): + + def __init__(self, heat_url, token): + self.heat_url = heat_url + + self.headers = { + 'content-type': 'application/json', + 'Accept': 'application/json', + 'X-Auth-Token': token + } + + def create_stack(self, yaml_path, stack_name, domain_name): + """Creates a HEAT stack + + :param yaml_path: path to the uaml file relative to endtoend directory + (eg. endtoend/heat_file.yaml) + :param stack_name: name of the heat stack containing the test site + :param domain_name: domain name to use for the deployed website + :returns: response to HEAT API call + """ + url = self.heat_url + '/stacks' + + template_file = open(yaml_path, 'r+') + template = template_file.read() + template = template.replace('example.com', domain_name) + request_data = { + "stack_name": stack_name, + "disable_rollback": True, + "parameters": {}, + "template": template, + "timeout_mins": 60 + } + + response = requests.post(url, + data=json.dumps(request_data), + headers=self.headers) + return response + + def get_stack_details(self, stack_name): + """Gets details of the stack + + :param stack_name: name of the stack to be queried + :returns: response containing stack details + """ + url = self.heat_url + '/stacks/' + stack_name + response = requests.get(url, headers=self.headers) + return response + + def wait_for_stack_status(self, stack_name, status='CREATE_COMPLETE', + retry_timeout=6000, retry_interval=10): + """Wait for the stack to reach the specified status + + :param stack_name: name of the stack to be queried + :param status: desired stack status + :param retry_interval: how frequently to ping for status change(sec) + :param retry_interval: max number of seconds to wait for status change + :returns: None + """ + current_status = '' + start_time = int(time.time()) + stop_time = start_time + retry_timeout + while current_status != status: + time.sleep(retry_interval) + stack_details = self.get_stack_details(stack_name=stack_name) + body = stack_details.json() + current_status = body['stack']['stack_status'] + if (current_status == status): + return + current_time = int(time.time()) + if current_time > stop_time: + return + + def get_server_ip(self, stack_name): + """Get the cloud server ip for the stack_name + + :param stack_name: name of the stack + :returns: ip of the server that is part of the stack + """ + stack_details = self.get_stack_details(stack_name) + stack_details = stack_details.json() + outputs = stack_details['stack']['outputs'] + server_ip = [output['output_value'] for + output in outputs if + output['output_key'] == 'server_ip'] + return server_ip[0] + + def delete_stack(self, stack_name): + """Delete specified stack + + :param stack_name: name of the stack + :returns: DELETE response + """ + url = self.heat_url + '/stacks/' + stack_name + response = requests.delete(url, headers=self.headers) + return response diff --git a/tests/endtoend/utils/wptclient.py b/tests/endtoend/utils/wptclient.py new file mode 100644 index 00000000..b5b4eda6 --- /dev/null +++ b/tests/endtoend/utils/wptclient.py @@ -0,0 +1,76 @@ +# Copyright (c) 2014 Rackspace, Inc. +# +# 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. + +import time + +import requests + + +class WebpageTestClient(object): + + def __init__(self, wpt_url, api_key): + self.wpt_url = wpt_url + self.api_key = api_key + + def start_test(self, access_url, test_location, runs=5): + """Starts webpage test + + :param access_url: the url which needs performance metrics + :param test_location: location to run the test on + :param runs: number of test runs + :returns: url to access the test details + """ + start_url = (self.wpt_url + '/runtest.php?url=' + access_url + + '&k=' + self.api_key + '&location=' + test_location + + '&runs=' + str(runs) + '&f=json') + response = requests.post(start_url) + test = response.json() + return test['data']['jsonUrl'] + + def get_test_details(self, test_url): + """Gets test results in json format + + :param test_url: the url pointing to json test results + :returns: test details json + """ + response = requests.get(test_url) + return response.json() + + def wait_for_test_status(self, test_url, status='COMPLETE', + retry_timeout=6000, retry_interval=10): + + """Waits for the wpt test to be completed. + + :param test_url: the url pointing to json test results + :param status: expected status from WPT server + 200 (COMPLETE) indicates test is completed. + 1XX means the test is still in progress. + And 4XX indicates some error. + """ + current_status = '' + start_time = int(time.time()) + stop_time = start_time + retry_timeout + if status == 'COMPLETE': + status_code = 200 + while current_status != status: + time.sleep(retry_interval) + test_details = self.get_test_details(test_url=test_url) + current_status = test_details['statusCode'] + print('status', test_details) + if (current_status == status_code): + return + current_time = int(time.time()) + if current_time > stop_time: + return diff --git a/tests/endtoend/wordpress-single.yaml b/tests/endtoend/wordpress-single.yaml new file mode 100644 index 00000000..59002272 --- /dev/null +++ b/tests/endtoend/wordpress-single.yaml @@ -0,0 +1,390 @@ +heat_template_version: 2013-05-23 + +description: | + This is a Heat template to deploy a single Linux server running WordPress. + +parameter_groups: + +- label: Server Settings + parameters: + - server_hostname + - image + - flavor + +- label: WordPress Settings + parameters: + - domain + - username + +- label: rax-dev-params + # These are parameters that will not be displayed in the portal. The purpose + # of these parameters are for users who are developing or testing newer or + # different setups. If any of these parameters are changed, there is a good + # chance this stack will fail to properly deploy. + parameters: + - kitchen + - chef_version + - version + - prefix + +parameters: + + # Server settings + server_hostname: + label: Server Name + description: Hostname to use for the server that's built. + type: string + default: CDN-Test-WordPress + constraints: + - length: + min: 1 + max: 64 + - allowed_pattern: "^[a-zA-Z][a-zA-Z0-9-]*$" + description: | + Must begin with a letter and contain only alphanumeric characters. + + image: + label: Operating System + description: | + Required: Server image used for all servers that are created as a part of + this deployment. + type: string + default: Ubuntu 12.04 LTS (Precise Pangolin) (PVHVM) + constraints: + - allowed_values: + - Ubuntu 12.04 LTS (Precise Pangolin) (PVHVM) + description: Must be a supported operating system. + + flavor: + label: Server Size + description: | + Required: Rackspace Cloud Server flavor to use. The size is based on the + amount of RAM for the provisioned server. + type: string + default: 4 GB General Purpose v1 + constraints: + - allowed_values: + - 1 GB General Purpose v1 + - 2 GB General Purpose v1 + - 4 GB General Purpose v1 + - 8 GB General Purpose v1 + - 15 GB I/O v1 + - 30 GB I/O v1 + - 1GB Standard Instance + - 2GB Standard Instance + - 4GB Standard Instance + - 8GB Standard Instance + - 15GB Standard Instance + - 30GB Standard Instance + description: | + Must be a valid Rackspace Cloud Server flavor for the region you have + selected to deploy into. + + # WordPress settings + domain: + label: Site Domain + description: Domain to be used with WordPress site + type: string + default: "example.com" + constraints: + - allowed_pattern: "^[a-zA-Z0-9.-]{1,255}.[a-zA-Z]{2,15}$" + description: Must be a valid domain name + + version: + label: WordPress Version + description: Version of WordPress to install + type: string + default: "3.9.2" + constraints: + - allowed_values: + - "3.9.2" + + # Optional Apache settings (SSL certs) + # ssl_private_key: + # description: Private SSL key + # type: string + # default: "" + # constraints: + # - allowed_pattern: "^(.){0,5000}$" + # description: "Key values must be under 5,000 characters" + + # ssl_certificate: + # description: Public SSL key + # type: string + # default: "" + # constraints: + # - allowed_pattern: "^(.){0,5000}$" + # description: "Certificate values must be under 5,000 characters" + + # ssl_intermediate_key: + # description: Intermediate SSL key + # type: string + # default: "" + # constraints: + # - allowed_pattern: "^(.){0,5000}$" + # description: "Intermediate values must be under 5,000 characters." + + # Database and system user configuration + prefix: + label: Database Prefix + description: Prefix to use for WordPress database tables + type: string + default: wp_ + constraints: + - allowed_pattern: "^[0-9a-zA-Z$_]{0,10}$" + description: | + Prefix must be shorter than 10 characters, and can only include + letters, numbers, $, and/or underscores. + + database_name: + label: Database Name + description: WordPress database name + type: string + default: wordpress + constraints: + - allowed_pattern: "^[0-9a-zA-Z$_]{1,64}$" + description: | + Maximum length of 64 characters, may only contain letters, numbers, and + underscores. + + username: + label: Username + description: "Username for system, database, and WordPress logins." + type: string + default: wp_user + constraints: + - allowed_pattern: "^[a-zA-Z0-9 _.@-]{1,16}$" + description: | + Must be shorter than 16 characters and may only contain alphanumeric + characters, ' ', '_', '.', '@', and/or '-'. + + kitchen: + label: Kitchen URL + description: "URL for a git repo containing required cookbooks" + type: string + default: https://github.com/rackspace-orchestration-templates/wordpress-single.git + + chef_version: + label: Chef Version + description: Version of chef client to use + type: string + default: 11.16.2 + +resources: + # Random password generation + database_password: + type: "OS::Heat::RandomString" + properties: + length: 16 + sequence: lettersdigits + + mysql_root_password: + type: "OS::Heat::RandomString" + properties: + length: 16 + sequence: lettersdigits + + mysql_repl_password: + type: "OS::Heat::RandomString" + properties: + length: 16 + sequence: lettersdigits + + mysql_debian_password: + type: "OS::Heat::RandomString" + properties: + length: 16 + sequence: lettersdigits + + # Random strings for WP salting + wp_auth: + type: "OS::Heat::RandomString" + properties: + length: 32 + sequence: hexdigits + + wp_logged_in: + type: "OS::Heat::RandomString" + properties: + length: 32 + sequence: hexdigits + + wp_nonce: + type: "OS::Heat::RandomString" + properties: + length: 32 + sequence: hexdigits + + wp_secure_auth: + type: "OS::Heat::RandomString" + properties: + length: 32 + sequence: hexdigits + + # SSH KEYS + sync_key: + type: "OS::Nova::KeyPair" + properties: + name: + str_replace: + template: "%stack_id%-sync" + params: + "%stack_id%": { get_param: "OS::stack_id" } + save_private_key: true + + ssh_key: + type: "OS::Nova::KeyPair" + properties: + name: { get_param: "OS::stack_id" } + save_private_key: true + + # Server resources + wordpress_server: + type: "Rackspace::Cloud::Server" + properties: + name: { get_param: server_hostname } + flavor: { get_param: flavor } + image: { get_param: image } + key_name: { get_resource: ssh_key } + metadata: + rax-heat: { get_param: "OS::stack_id" } + + # Chef resources + wordpress_setup: + type: "OS::Heat::ChefSolo" + depends_on: wordpress_server + properties: + username: root + private_key: { get_attr: [ssh_key, private_key] } + host: { get_attr: [wordpress_server, accessIPv4] } + kitchen: { get_param: kitchen } + chef_version: { get_param: chef_version } + node: + apache: + listen_ports: [8080] + timeout: 30 + serversignature: "Off" + traceenable: "Off" + hollandbackup: + main: + backup_directory: "/var/lib/mysqlbackup" + mysqldump: + user: "root" + host: "localhost" + password: { get_attr: [mysql_root_password, value] } + lsyncd: + interval: 5 + memcached: + listen: "127.0.0.1" + monit: + notify_email: 'root@localhost' + mail_format: + from: 'monit@localhost' + mysql: + bind_address: "127.0.0.1" + server_root_password: { get_attr: [mysql_root_password, value] } + server_repl_password: { get_attr: [mysql_repl_password, value] } + server_debian_password: { get_attr: [mysql_debian_password, value] } + remove_test_database: true + remove_anonymous_users: true + sysctl: + values: + fs.inotify.max_user_watches: 1000000 + varnish: + version: "3.0" + listen_port: "80" + vsftpd: + ipaddress: '' + write_enable: true + local_umask: "002" + chroot_local_user: false + hide_ids: false + ssl_enable: true + ssl_ciphers: "AES256-SHA" + wordpress: + version: { get_param: version } + server_aliases: [{ get_param: domain }] + dir: + str_replace: + template: "/var/www/vhosts/%domain%" + params: + "%domain%": { get_param: domain } + db: + name: { get_param: database_name } + user: { get_param: username } + pass: { get_attr: [database_password, value] } + host: "127.0.0.1" + keys: + auth: { get_attr: [wp_auth, value] } + logged_in: { get_attr: [wp_logged_in, value] } + nonce_key: { get_attr: [wp_nonce, value] } + secure_auth_key: { get_attr: [wp_secure_auth, value] } + rax: + apache: + # ssl_private_key: { get_param: ssl_private_key } + # ssl_certificate: { get_param: ssl_certificate } + # ssl_intermediate_certs: { get_param: ssl_intermediate_certs } + domain: { get_param: domain } + lsyncd: + ssh: + private_key: { get_attr: [sync_key, private_key] } + packages: + - php5-imagick + varnish: + master_backend: "localhost" + wordpress: + admin_user: { get_param: username } + admin_pass: { get_attr: [database_password, value] } + user: + name: { get_param: username } + group: { get_param: username } + run_list: ["recipe[apt]", + "recipe[build-essential]", + "recipe[rax-wordpress::apache-prep]", + "recipe[sysctl::attribute_driver]", + "recipe[mysql::server]", + "recipe[rax-wordpress::mysql]", + "recipe[hollandbackup]", + "recipe[hollandbackup::mysqldump]", + "recipe[hollandbackup::main]", + "recipe[hollandbackup::backupsets]", + "recipe[hollandbackup::cron]", + "recipe[rax-wordpress::x509]", + "recipe[memcached]", + "recipe[php]", + "recipe[rax-install-packages]", + "recipe[wordpress]", + "recipe[rax-wordpress::wp-setup]", + "recipe[rax-wordpress::user]", + "recipe[rax-wordpress::memcache]", + "recipe[lsyncd]", + "recipe[vsftpd]", + "recipe[rax-wordpress::vsftpd]", + "recipe[varnish::repo]", + "recipe[varnish]", + "recipe[rax-wordpress::apache]", + "recipe[rax-wordpress::varnish]", + "recipe[rax-wordpress::firewall]", + "recipe[rax-wordpress::vsftpd-firewall]", + "recipe[rax-wordpress::lsyncd]"] + +outputs: + private_key: + description: SSH Private Key + value: { get_attr: [ssh_key, private_key] } + + server_ip: + description: Server IP + value: { get_attr: [wordpress_server, accessIPv4] } + + wordpress_user: + description: WordPress User + value: { get_param: username } + + wordpress_password: + description: WordPress Password + value: { get_attr: [database_password, value] } + + mysql_root_password: + description: MySQL Root Password + value: { get_attr: [mysql_root_password, value] } diff --git a/tests/etc/endtoend.conf b/tests/etc/endtoend.conf new file mode 100644 index 00000000..294ca58a --- /dev/null +++ b/tests/etc/endtoend.conf @@ -0,0 +1,27 @@ +#============================================================================= +# Configuration file to execute End To End tests. +#============================================================================= + +[auth] +user_name={user name of the cloud account} +api_key={api key for this user name} +base_url=https://identity.api.rackspacecloud.com/v2.0 + +[test_configuration] +provider_validation=False +status_check_retry_interval=2 +status_check_retry_timeout=30 + +[orchestration] +base_url=https://iad.orchestration.api.rackspacecloud.com/v1/{project_id}/ +yaml_path=endtoend/wordpress-single.yaml + +[poppy] +base_url=http://0.0.0.0:8888 +flavor=standard + +[webpagetest] +base_url=http://www.webpagetest.org/ +api_key={api key for webpagetest instance} +# GET http://www.webpagetest.org/getLocations.php - Use tag +test_locations=Wellington:Chrome, Indore:Firefox, Stockholm:Safari, Dulles:Firefox, Miami:Chrome diff --git a/tests/test-requirements.txt b/tests/test-requirements.txt index 36e80931..07c67e3e 100644 --- a/tests/test-requirements.txt +++ b/tests/test-requirements.txt @@ -8,3 +8,5 @@ openstack.nose_plugin oslotest requests testtools +python-heatclient +beautifulsoup4 diff --git a/tox.ini b/tox.ini index d44a5eb2..06e3b2e3 100644 --- a/tox.ini +++ b/tox.ini @@ -18,7 +18,7 @@ deps = -r{toxinidir}/requirements/requirements.txt -r{toxinidir}/tests/test-requirements.txt commands = pip install git+https://github.com/stackforge/opencafe.git#egg=cafe pip install git+https://github.com/tonytan4ever/python-maxcdn.git#egg=maxcdn - nosetests {posargs:--exclude=api --nologcapture} + nosetests {posargs:--exclude=api --exclude=endtoend --nologcapture} [tox:jenkins] downloadcache = ~/cache/pip