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
This commit is contained in:
parent
92b8e69942
commit
8a0e22fb60
|
@ -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()
|
|
@ -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()
|
|
@ -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(',')
|
|
@ -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
|
|
@ -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
|
|
@ -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] }
|
|
@ -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 <id> tag
|
||||
test_locations=Wellington:Chrome, Indore:Firefox, Stockholm:Safari, Dulles:Firefox, Miami:Chrome
|
|
@ -8,3 +8,5 @@ openstack.nose_plugin
|
|||
oslotest
|
||||
requests
|
||||
testtools
|
||||
python-heatclient
|
||||
beautifulsoup4
|
||||
|
|
2
tox.ini
2
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
|
||||
|
|
Loading…
Reference in New Issue