poppy/tests/unit/manager/default/test_services.py

1719 lines
65 KiB
Python

# 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 random
import uuid
import ddt
import mock
from oslo_config import cfg
from oslo_context import context
import requests
import six
import testtools
from poppy.common import errors
from poppy.distributed_task.taskflow.task import common
from poppy.distributed_task.taskflow.task import create_service_tasks
from poppy.distributed_task.taskflow.task import delete_service_tasks
from poppy.distributed_task.taskflow.task import purge_service_tasks
from poppy.distributed_task.taskflow.task import update_service_tasks
from poppy.distributed_task.utils import memoized_controllers
from poppy.manager.default import driver
from poppy.manager.default import services
from poppy.model import flavor
from poppy.model.helpers import provider_details
from poppy.model import ssl_certificate
from poppy.transport.pecan.models.request import service
from tests.unit import base
class Elapsed(object):
def __init__(self, time):
self.time = time
def total_seconds(self):
return self.time
class Response(object):
def __init__(self, resp_status, resp_json=None):
self.resp_status = resp_status
self.resp_json = resp_json
def headers(self):
return {}
@property
def ok(self):
return self.resp_status
def json(self):
return self.resp_json
@property
def elapsed(self):
return Elapsed(0.1)
class MonkeyPatchControllers(object):
def __init__(self, service_controller,
dns_controller,
storage_controller, ssl_cert_controller, func):
self.service_controller = service_controller
self.dns_controller = dns_controller
self.storage_controller = storage_controller
self.ssl_cert_controller = ssl_cert_controller
self.func = func
def __enter__(self):
def monkey_task_controllers(program, controller=None):
if controller == 'storage':
return self.service_controller, self.storage_controller
if controller == 'dns':
return self.service_controller, self.dns_controller
if controller == 'ssl_certificate':
return self.service_controller, self.ssl_cert_controller
else:
return self.service_controller
memoized_controllers.task_controllers = monkey_task_controllers
def __exit__(self, exc_type, exc_val, exc_tb):
memoized_controllers.task_controllers = self.func
@ddt.ddt
class DefaultManagerServiceTests(base.TestCase):
@mock.patch('poppy.bootstrap.Bootstrap')
@mock.patch('poppy.notification.base.driver.NotificationDriverBase')
@mock.patch('poppy.dns.base.driver.DNSDriverBase')
@mock.patch('poppy.storage.base.driver.StorageDriverBase')
@mock.patch('poppy.distributed_task.base.driver.DistributedTaskDriverBase')
@mock.patch('poppy.metrics.base.driver.MetricsDriverBase')
def setUp(self, mock_metrics, mock_distributed_task, mock_storage,
mock_dns, mock_notification, mock_bootstrap):
# NOTE(TheSriram): the mock.patch decorator applies mocks
# in the reverse order of the arguments present
super(DefaultManagerServiceTests, self).setUp()
self.context = context.RequestContext()
# create mocked config and driver
conf = cfg.ConfigOpts()
_DRIVER_DNS_OPTIONS = [
cfg.IntOpt(
'retries',
default=5,
help='Total number of Retries after '
'Exponentially Backing Off'),
cfg.IntOpt(
'min_backoff_range',
default=20,
help='Minimum Number of seconds to sleep between retries'),
cfg.IntOpt(
'max_backoff_range',
default=30,
help='Maximum Number of seconds to sleep between retries'),
]
_PROVIDER_OPTIONS = [
cfg.IntOpt(
'default_cache_ttl',
default=86400,
help='Default ttl to be set, when no caching '
'rules are specified'),
]
_MAX_SERVICE_OPTIONS = [
cfg.IntOpt('max_services_per_project', default=20,
help='Default max service per project_id')
]
_DRIVER_DNS_GROUP = 'driver:dns'
_PROVIDER_GROUP = 'drivers:provider'
_MAX_SERVICE_GROUP = 'drivers:storage'
conf.register_opts(_PROVIDER_OPTIONS, group=_PROVIDER_GROUP)
conf.register_opts(_DRIVER_DNS_OPTIONS, group=_DRIVER_DNS_GROUP)
conf.register_opts(_MAX_SERVICE_OPTIONS, group=_MAX_SERVICE_GROUP)
self.max_services_per_project = \
conf[_MAX_SERVICE_GROUP].max_services_per_project
self.bootstrap_obj = mock_bootstrap(conf)
# mock a stevedore provider extension
def get_provider_by_name(name):
name_p_name_mapping = {
'maxcdn': 'MaxCDN',
'cloudfront': 'CloudFront',
'fastly': 'Fastly',
'mock': 'Mock'
}
return mock.Mock(obj=mock.Mock(provider_name=(
name_p_name_mapping[name])))
mock_providers = mock.MagicMock()
mock_providers.__getitem__.side_effect = get_provider_by_name
manager_driver = driver.DefaultManagerDriver(conf,
mock_storage,
mock_providers,
mock_dns,
mock_distributed_task,
mock_notification,
mock_metrics)
# stubbed driver
self.sc = services.DefaultServicesController(manager_driver)
self.bootstrap_obj.manager = manager_driver
self.bootstrap_obj.manager.services_controller = self.sc
self.project_id = str(uuid.uuid4())
self.service_name = str(uuid.uuid4())
self.service_id = str(uuid.uuid4())
self.auth_token = str(uuid.uuid4())
self.service_json = {
"name": self.service_name,
"domains": [
{"domain": "www.mywebsite.com"},
{"domain": "blog.mywebsite.com"},
],
"origins": [
{
"origin": "mywebsite.com",
"port": 80,
"ssl": False
}
],
"caching": [
{"name": "default",
"ttl": 3600},
{"name": "home",
"ttl": 17200,
"rules": [
{"name": "index", "request_url": "/index.htm"}
]
},
{"name": "images",
"ttl": 12800,
"rules": [
{"name": "pictures",
"request_url": "/pictures.htm"}
]
}
],
"flavor_id": "standard",
"log_delivery": {
"enabled": False
},
}
self.service_obj = service.load_from_json(self.service_json)
self.mock_storage = mock_storage
self.mock_distributed_task = mock_distributed_task
@mock.patch('poppy.bootstrap.Bootstrap')
def mock_purge_service(self, mock_bootstrap, hard=False):
mock_bootstrap.return_value = self.bootstrap_obj
purge_provider = purge_service_tasks.PurgeProviderServicesTask()
provider_details = json.dumps(
dict([(k, v.to_dict()) for k, v
in self.provider_details.items()]))
responders = \
purge_provider.execute(json.dumps(self.service_obj.to_dict()),
json.dumps(hard),
provider_details,
str(None))
error_update = common.UpdateProviderDetailErrorTask()
changed_provider_details_dict = error_update.execute(responders,
self.service_id,
provider_details,
hard)
not_empty_update = common.UpdateProviderDetailIfNotEmptyTask()
not_empty_update.execute(changed_provider_details_dict,
self.project_id,
self.service_id)
@mock.patch('poppy.bootstrap.Bootstrap')
def mock_delete_service(self, mock_bootstrap):
mock_bootstrap.return_value = self.bootstrap_obj
delete_provider = delete_service_tasks.DeleteProviderServicesTask()
provider_details = json.dumps(
dict([(k, v.to_dict()) for k, v
in self.provider_details.items()]))
responders = delete_provider.execute(provider_details, self.project_id)
delete_dns = delete_service_tasks.DeleteServiceDNSMappingTask()
dns_responders = delete_dns.execute(provider_details, 0, responders,
self.project_id, self.service_id)
gather_provider = delete_service_tasks.GatherProviderDetailsTask()
changed_provider_dict = gather_provider.execute(responders,
dns_responders,
provider_details)
update_provider = common.UpdateProviderDetailIfNotEmptyTask()
update_provider.execute(changed_provider_dict, self.project_id,
self.service_id)
delete_service = delete_service_tasks.DeleteStorageServiceTask()
delete_service.execute(self.project_id, self.service_id)
def mock_create_service(self, provider_details_json):
@mock.patch('poppy.bootstrap.Bootstrap')
def bootstrap_mock_create(mock_bootstrap):
mock_bootstrap.return_value = self.bootstrap_obj
create_provider = create_service_tasks.CreateProviderServicesTask()
responders = create_provider.execute(
json.dumps(provider_details_json),
self.project_id,
self.service_id)
create_dns = create_service_tasks.CreateServiceDNSMappingTask()
dns_responder = create_dns.execute(responders, 0, self.project_id,
self.service_id)
gather_provider = create_service_tasks.GatherProviderDetailsTask()
log_responder = \
create_service_tasks.CreateLogDeliveryContainerTask()
provider_details_dict = \
gather_provider.execute(responders,
dns_responder,
log_responder)
update_provider_details = common.UpdateProviderDetailTask()
update_provider_details.execute(provider_details_dict,
self.project_id, self.service_id)
bootstrap_mock_create()
def mock_update_service(self, provider_details_json):
@mock.patch('poppy.bootstrap.Bootstrap')
def bootstrap_mock_update(mock_bootstrap):
mock_bootstrap.return_value = self.bootstrap_obj
service_old = json.dumps(self.service_json)
update_provider = update_service_tasks.UpdateProviderServicesTask()
service_updates = self.service_json.copy()
service_updates['log_delivery'] = {
'enabled': True
}
service_updates_json = json.dumps(service_updates)
responders = update_provider.execute(
service_old,
service_updates_json
)
update_dns = update_service_tasks.UpdateServiceDNSMappingTask()
dns_responder = update_dns.execute(responders, 0, service_old,
service_updates_json,
self.project_id,
self.service_id)
log_delivery_update = \
update_service_tasks.UpdateLogDeliveryContainerTask()
log_responder = log_delivery_update.execute(self.project_id,
self.auth_token,
service_old,
service_updates_json)
gather_provider = update_service_tasks.GatherProviderDetailsTask()
provider_details_dict = \
gather_provider.execute(responders,
dns_responder,
log_responder,
self.project_id,
self.service_id,
service_old)
update_provider_details = \
update_service_tasks.UpdateProviderDetailsTask_Errors()
update_provider_details.execute(provider_details_dict,
self.project_id,
self.service_id,
service_old,
service_updates_json)
bootstrap_mock_update()
def test_defaults_caching(self):
service_json = {
'caching': []
}
self.sc._append_defaults(service_json, operation='create')
changed_service_json = {
'caching': [
{
'name': 'default',
'rules': [
{
'name': 'default',
'request_url': '/*'
}
]
}
]
}
self.assertTrue(isinstance(service_json['caching'][0]['ttl'],
six.integer_types))
del service_json['caching'][0]['ttl']
self.assertEqual(service_json, changed_service_json)
def test_create(self):
# fake one return value
self.sc.flavor_controller.get.return_value = flavor.Flavor(
"standard",
[flavor.Provider("cloudfront", "www.cloudfront.com"),
flavor.Provider("fastly", "www.fastly.com"),
flavor.Provider("mock", "www.mock_provider.com")]
)
providers = self.sc._driver.providers
# mock responses from provider_wrapper.create call
# to get code coverage
def get_provider_extension_by_name(name):
if name == "cloudfront":
return_mock = mock.Mock(
return_value={
'Cloudfront': {
'id':
'08d2e326-377e-11e4-b531-3c15c2b8d2d6',
'links': [
{
'href': 'www.mysite.com',
'rel': 'access_url'}],
'status': "deploy_in_progress"}})
service_controller = mock.Mock(
create=return_mock)
return mock.Mock(obj=mock.Mock(
provider_name='CloudFront',
service_controller=service_controller)
)
elif name == "fastly":
return_mock = mock.Mock(
return_value={
'Fastly': {'error': "fail to create service",
'error_detail': 'Fastly Create failed'
' because of XYZ'}})
service_controller = mock.Mock(
create=return_mock)
return mock.Mock(obj=mock.Mock(
provider_name=name.title(),
service_controller=service_controller)
)
else:
return_mock = mock.Mock(
return_value={
name.title(): {
'id':
'08d2e326-377e-11e4-b531-3c15c2b8d2d6',
'links': [
{
'href': 'www.mysite.com',
'rel': 'access_url'}]}})
service_controller = mock.Mock(
create=return_mock)
return mock.Mock(obj=mock.Mock(
provider_name=name.title(),
service_controller=service_controller)
)
providers.__getitem__.side_effect = get_provider_extension_by_name
self.sc.storage_controller.get_service_limit = mock.Mock(
return_value=self.max_services_per_project)
self.sc.storage_controller.get_service_count = mock.Mock(
return_value=1)
service_obj = self.sc.create_service(
self.project_id,
self.auth_token,
self.service_json
)
# ensure the manager calls the storage driver with the appropriate data
self.sc.storage_controller.create_service.assert_called_once_with(
self.project_id,
service_obj
)
@ddt.file_data('data_provider_details.json')
def test_create_service_worker(self, provider_details_json):
self.provider_details = {}
for provider_name in provider_details_json:
provider_detail_dict = json.loads(
provider_details_json[provider_name]
)
provider_service_id = provider_detail_dict.get('id', None)
access_urls = provider_detail_dict.get('access_urls', None)
status = provider_detail_dict.get('status', u'deployed')
provider_detail_obj = provider_details.ProviderDetail(
provider_service_id=provider_service_id,
access_urls=access_urls,
status=status)
self.provider_details[provider_name] = provider_detail_obj
providers = self.sc._driver.providers
def get_provider_extension_by_name(name):
if name == 'cloudfront':
return_mock = {
'CloudFront': {
'id':
'08d2e326-377e-11e4-b531-3c15c2b8d2d6',
'links': [{'href': 'www.mysite.com',
'rel': 'access_url'}],
'status': 'deploy_in_progress'
}
}
service_controller = mock.Mock(
create=mock.Mock(return_value=return_mock)
)
return mock.Mock(obj=mock.Mock(
provider_name='CloudFront',
service_controller=service_controller)
)
elif name == 'fastly':
return_mock = {
'Fastly': {'error': "fail to create service",
'error_detail': 'Fastly Create failed'
' because of XYZ'}
}
service_controller = mock.Mock(
create=mock.Mock(return_value=return_mock)
)
return mock.Mock(obj=mock.Mock(
provider_name='MaxCDN',
service_controller=service_controller)
)
else:
return_mock = {
name.title(): {
'id':
'08d2e326-377e-11e4-b531-3c15c2b8d2d6',
'links': [
{'href': 'www.mysite.com',
'rel': 'access_url'}]
}
}
service_controller = mock.Mock(
create=mock.Mock(return_value=return_mock)
)
return mock.Mock(obj=mock.Mock(
provider_name=name.title(),
service_controller=service_controller)
)
providers.__getitem__.side_effect = get_provider_extension_by_name
with MonkeyPatchControllers(self.sc,
self.sc.dns_controller,
self.sc.storage_controller,
self.sc.ssl_certificate_storage,
memoized_controllers.task_controllers):
self.mock_create_service(provider_details_json)
def test_create_service_flavor_get_error(self):
# fake one return value
self.sc.flavor_controller.get.side_effect = LookupError(
"Mock -- Invalid flavor!"
)
with testtools.ExpectedException(LookupError):
self.sc.create_service('project_id', 'auth_token', {})
self.assertFalse(self.sc.storage_controller.create_service.called)
@ddt.file_data('data_provider_details.json')
def test_update_service_worker_success_and_failure(self,
provider_details_json):
self.provider_details = {}
for provider_name in provider_details_json:
provider_detail_dict = json.loads(
provider_details_json[provider_name]
)
provider_service_id = provider_detail_dict.get('id', None)
access_urls = provider_detail_dict.get('access_urls', None)
status = provider_detail_dict.get('status', u'deployed')
provider_detail_obj = provider_details.ProviderDetail(
provider_service_id=provider_service_id,
access_urls=access_urls,
status=status)
self.provider_details[provider_name] = provider_detail_obj
providers = self.sc._driver.providers
def get_provider_extension_by_name(name):
if name == 'cloudfront':
return_mock = {
'CloudFront': {
'id':
'08d2e326-377e-11e4-b531-3c15c2b8d2d6',
'links': [{'href': 'www.mysite.com',
'rel': 'access_url'}],
'status': 'deploy_in_progress'
}
}
service_controller = mock.Mock(
create=mock.Mock(return_value=return_mock)
)
return mock.Mock(obj=mock.Mock(
provider_name='CloudFront',
service_controller=service_controller)
)
elif name == 'fastly':
return_mock = {
'Fastly': {'error': "fail to create servcice",
'error_detail': 'Fastly Create failed'
' because of XYZ'}
}
service_controller = mock.Mock(
create=mock.Mock(return_value=return_mock)
)
return mock.Mock(obj=mock.Mock(
provider_name='MaxCDN',
service_controller=service_controller)
)
else:
return_mock = {
name.title(): {
'id':
'08d2e326-377e-11e4-b531-3c15c2b8d2d6',
'links': [
{'href': 'www.mysite.com',
'rel': 'access_url'}]
}
}
service_controller = mock.Mock(
create=mock.Mock(return_value=return_mock)
)
return mock.Mock(obj=mock.Mock(
provider_name=name.title(),
service_controller=service_controller)
)
providers.__getitem__.side_effect = get_provider_extension_by_name
conf = cfg.CONF
conf(project='poppy', prog='poppy', args=[])
LOG_DELIVERY_OPTIONS = [
cfg.ListOpt('preferred_dcs', default=['DC1', 'DC2'],
help='Preferred DCs to create container'),
]
LOG_DELIVERY_GROUP = 'log_delivery'
conf.register_opts(LOG_DELIVERY_OPTIONS, group=LOG_DELIVERY_GROUP)
region = random.choice(conf['log_delivery']['preferred_dcs'])
catalog_json = {
'access': {
'serviceCatalog': [
{
'type': 'object-store',
'endpoints': [
{
'region': region,
'publicURL': 'public',
'internalURL': 'private'
}
]
}
]
}
}
with MonkeyPatchControllers(self.sc,
self.sc.dns_controller,
self.sc.storage_controller,
self.sc.ssl_certificate_storage,
memoized_controllers.task_controllers):
# NOTE(TheSriram): Successful update
with mock.patch.object(requests,
'post',
return_value=Response(True, catalog_json)):
with mock.patch.object(requests,
'put',
return_value=Response(True)):
self.mock_update_service(provider_details_json)
# NOTE(TheSriram): Unsuccessful update due to keystone
with mock.patch.object(requests,
'post',
return_value=Response(False, catalog_json)):
with mock.patch.object(requests,
'put',
return_value=Response(True)):
self.mock_update_service(provider_details_json)
# NOTE(TheSriram): Unsuccessful update due to swift
with mock.patch.object(requests,
'post',
return_value=Response(True, catalog_json)):
with mock.patch.object(requests,
'put',
return_value=Response(False)):
self.mock_update_service(provider_details_json)
def test_update(self):
provider_details_dict = {
"MaxCDN": {"id": 11942, "access_urls": ["mypullzone.netdata.com"]},
"Mock": {"id": 73242, "access_urls": ["mycdn.mock.com"]},
"CloudFront": {
"id": "5ABC892", "access_urls": ["cf123.cloudcf.com"]},
"Fastly": {
"id": 3488, "access_urls": ["mockcf123.fastly.prod.com"]}
}
providers_details = {}
for name in provider_details_dict:
details = provider_details_dict[name]
provider_detail_obj = provider_details.ProviderDetail(
provider_service_id=details['id'],
access_urls=details['access_urls'],
status=details.get('status', u'unknown'))
providers_details[name] = provider_detail_obj
self.sc.storage_controller.get_provider_details.return_value = (
providers_details
)
service_obj = service.load_from_json(self.service_json)
service_obj.status = u'deployed'
self.sc.storage_controller.get_service.return_value = service_obj
service_updates = json.dumps([
{
"op": "replace",
"path": "/domains/0",
"value": {"domain": "added.mocksite4.com"}
}
])
self.sc.update_service(
self.project_id,
self.service_id,
self.auth_token,
service_updates
)
# ensure the manager calls the storage driver with the appropriate data
self.sc.storage_controller.update_service.assert_called_once()
def test_upgrade_http_to_https_san(self):
provider_details_dict = {
"MaxCDN": {
"id": 11942,
"access_urls": [{
"domain": "www.mywebsite.com",
"access_url": "mypullzone.netdata.com",
"provider_url": 'maxcdn.provider.com'
}]
},
"Mock": {
"id": 73242,
"access_urls": [{
"domain": "www.mywebsite.com",
"access_url": "mycdn.mock.com",
"provider_url": 'mock.provider.com'
}]
},
"CloudFront": {
"id": "5ABC892",
"access_urls": [{
"access_url": "cf123.cloudcf.com",
"domain": "www.mywebsite.com",
"provider_url": 'cf.provider.com'
}]
},
"Fastly": {
"id": 3488,
"access_urls": [{
"access_url": "mockcf123.fastly.prod.com",
"domain": "www.mywebsite.com",
"provider_url": 'fastly.provider.com'
}]
}
}
providers_details_dict = {}
for name in provider_details_dict:
details = provider_details_dict[name]
provider_detail_obj = provider_details.ProviderDetail(
provider_service_id=details['id'],
access_urls=details['access_urls'],
status=details.get('status', u'unknown'))
providers_details_dict[name] = provider_detail_obj
self.sc.storage_controller.get_provider_details.return_value = (
providers_details_dict
)
service_obj = service.load_from_json(self.service_json)
service_obj.provider_details = providers_details_dict
service_obj.status = u'deployed'
self.sc.storage_controller.get_service.return_value = service_obj
self.sc.ssl_certificate_storage.get_certs_by_domain.return_value = []
self.sc.flavor_controller.get.return_value = flavor.Flavor(
'standard',
providers=[
flavor.Provider('MaxCDN', 'http://maxcdn.com'),
flavor.Provider('Mock', 'http://www.mock.com'),
flavor.Provider('CloudFront', 'http://www.cloudfront.com'),
flavor.Provider('Fastly', 'http://www.fastly.com')
]
)
service_updates = json.dumps([
{
"op": "replace",
"path": "/domains/0",
"value": {
"domain": "www.mywebsite.com",
"protocol": "https",
"certificate": "san"
}
}
])
self.sc.update_service(
self.project_id,
self.service_id,
self.auth_token,
service_updates
)
# ensure the manager calls the storage driver with the appropriate data
self.sc.storage_controller.update_service.assert_called_once()
def test_update_service_operator_status_disabled_error(self):
provider_details_dict = {
"MaxCDN": {"id": 11942, "access_urls": ["mypullzone.netdata.com"]},
"Mock": {"id": 73242, "access_urls": ["mycdn.mock.com"]},
"CloudFront": {
"id": "5ABC892", "access_urls": ["cf123.cloudcf.com"]},
"Fastly": {
"id": 3488, "access_urls": ["mockcf123.fastly.prod.com"]}
}
providers_details = {}
for name in provider_details_dict:
details = provider_details_dict[name]
provider_detail_obj = provider_details.ProviderDetail(
provider_service_id=details['id'],
access_urls=details['access_urls'],
status=details.get('status', u'unknown'))
providers_details[name] = provider_detail_obj
self.sc.storage_controller.get_provider_details.return_value = (
providers_details
)
service_obj = service.load_from_json(self.service_json)
service_obj.status = u'deployed'
service_obj.operator_status = 'disabled'
self.sc.storage_controller.get_service.return_value = service_obj
service_updates = json.dumps([
{
"op": "replace",
"path": "/domains/0",
"value": {"domain": "added.mocksite4.com"}
}
])
with testtools.ExpectedException(errors.ServiceStatusDisabled):
self.sc.update_service(
self.project_id,
self.service_id,
self.auth_token,
service_updates
)
def test_update_service_status_not_failed_or_deployed_error(self):
provider_details_dict = {
"MaxCDN": {"id": 11942, "access_urls": ["mypullzone.netdata.com"]},
"Mock": {"id": 73242, "access_urls": ["mycdn.mock.com"]},
"CloudFront": {
"id": "5ABC892", "access_urls": ["cf123.cloudcf.com"]},
"Fastly": {
"id": 3488, "access_urls": ["mockcf123.fastly.prod.com"]}
}
providers_details = {}
for name in provider_details_dict:
details = provider_details_dict[name]
provider_detail_obj = provider_details.ProviderDetail(
provider_service_id=details['id'],
access_urls=details['access_urls'],
status=details.get('status', u'unknown'))
providers_details[name] = provider_detail_obj
self.sc.storage_controller.get_provider_details.return_value = (
providers_details
)
service_obj = service.load_from_json(self.service_json)
service_obj.status = u'create_in_progress'
self.sc.storage_controller.get_service.return_value = service_obj
service_updates = json.dumps([
{
"op": "replace",
"path": "/domains/0",
"value": {"domain": "added.mocksite4.com"}
}
])
with testtools.ExpectedException(
errors.ServiceStatusNeitherDeployedNorFailed
):
self.sc.update_service(
self.project_id,
self.service_id,
self.auth_token,
service_updates
)
@ddt.file_data('data_provider_details.json')
def test_delete(self, provider_details_json):
self.provider_details = {}
for provider_name in provider_details_json:
provider_detail_dict = json.loads(
provider_details_json[provider_name]
)
provider_service_id = provider_detail_dict.get('id', None)
access_urls = provider_detail_dict.get('access_urls', [])
status = provider_detail_dict.get('status', u'unknown')
provider_detail_obj = provider_details.ProviderDetail(
provider_service_id=provider_service_id,
access_urls=access_urls,
status=status)
self.provider_details[provider_name] = provider_detail_obj
self.service_obj.provider_details = self.provider_details
sc = self.sc.storage_controller
sc.get_service.return_value = self.service_obj
self.sc.delete_service(self.project_id, self.service_id)
# ensure the manager calls the storage driver with the appropriate data
sc.get_service.assert_called_once_with(
self.project_id,
self.service_id
)
sc.update_service.assert_called_once_with(
self.project_id,
self.service_id,
self.service_obj
)
# break into 2 lines.
sc = self.sc.storage_controller
sc.get_provider_details.assert_called_once_with(
self.project_id,
self.service_id)
@ddt.file_data('data_provider_details.json')
def test_delete_service_worker_success(self, provider_details_json):
self.provider_details = {}
for provider_name in provider_details_json:
provider_detail_dict = json.loads(
provider_details_json[provider_name]
)
provider_service_id = provider_detail_dict.get("id", None)
access_urls = provider_detail_dict.get("access_urls", None)
status = provider_detail_dict.get("status", u'deployed')
provider_detail_obj = provider_details.ProviderDetail(
provider_service_id=provider_service_id,
access_urls=access_urls,
status=status)
self.provider_details[provider_name] = provider_detail_obj
providers = self.sc._driver.providers
def get_provider_extension_by_name(name):
if name == 'cloudfront':
return_mock = {
'CloudFront': {
'id':
'08d2e326-377e-11e4-b531-3c15c2b8d2d6',
}
}
service_controller = mock.Mock(
delete=mock.Mock(return_value=return_mock)
)
return mock.Mock(obj=mock.Mock(
provider_name='CloudFront',
service_controller=service_controller)
)
elif name == 'maxcdn':
return_mock = {
'MaxCDN': {'id': "pullzone345"}
}
service_controller = mock.Mock(
delete=mock.Mock(return_value=return_mock)
)
return mock.Mock(obj=mock.Mock(
provider_name='MaxCDN',
service_controller=service_controller)
)
else:
return_mock = {
name.title(): {
'id':
'08d2e326-377e-11e4-b531-3c15c2b8d2d6',
}
}
service_controller = mock.Mock(
delete=mock.Mock(return_value=return_mock)
)
return mock.Mock(obj=mock.Mock(
provider_name=name.title(),
service_controller=service_controller)
)
providers.__getitem__.side_effect = get_provider_extension_by_name
with MonkeyPatchControllers(self.sc,
self.sc.dns_controller,
self.sc.storage_controller,
self.sc.ssl_certificate_storage,
memoized_controllers.task_controllers):
self.mock_delete_service()
@ddt.file_data('data_provider_details.json')
def test_delete_service_worker_with_error(self, provider_details_json):
self.provider_details = {}
for provider_name in provider_details_json:
provider_detail_dict = json.loads(
provider_details_json[provider_name]
)
provider_service_id = provider_detail_dict.get("id", None)
access_urls = provider_detail_dict.get("access_urls", None)
status = provider_detail_dict.get("status", u'deployed')
provider_detail_obj = provider_details.ProviderDetail(
provider_service_id=provider_service_id,
access_urls=access_urls,
status=status)
self.provider_details[provider_name] = provider_detail_obj
providers = self.sc._driver.providers
def get_provider_extension_by_name(name):
if name == 'cloudfront':
return mock.Mock(
obj=mock.Mock(
provider_name='CloudFront',
service_controller=mock.Mock(
delete=mock.Mock(
return_value={
'CloudFront': {
'id':
'08d2e326-377e-11e4-b531-3c15c2b8d2d6',
}}),
)))
elif name == 'maxcdn':
return mock.Mock(obj=mock.Mock(
provider_name='MaxCDN',
service_controller=mock.Mock(
delete=mock.Mock(return_value={
'MaxCDN': {'error': "fail to create servcice",
'error_detail':
'MaxCDN delete service'
' failed because of XYZ'}})
)
))
else:
return mock.Mock(
obj=mock.Mock(
provider_name=name.title(),
service_controller=mock.Mock(
delete=mock.Mock(
return_value={
name.title(): {
'id':
'08d2e326-377e-11e4-b531-3c15c2b8d2d6',
}}),
)))
providers.__getitem__.side_effect = get_provider_extension_by_name
with MonkeyPatchControllers(self.sc,
self.sc.dns_controller,
self.sc.storage_controller,
self.sc.ssl_certificate_storage,
memoized_controllers.task_controllers):
self.mock_delete_service()
def test_delete_get_provider_details_error(self):
self.sc.storage_controller.get_service.return_value = (
self.service_obj
)
self.sc.storage_controller.get_provider_details.\
side_effect = Exception(
"Mock -- Error during get provider details!"
)
with testtools.ExpectedException(LookupError):
self.sc.delete_service('project_id', 'service_id')
@ddt.file_data('data_provider_details.json')
def test_purge(self, provider_details_json):
self.provider_details = {}
for provider_name in provider_details_json:
provider_detail_dict = json.loads(
provider_details_json[provider_name]
)
provider_service_id = provider_detail_dict.get('id', None)
access_urls = provider_detail_dict.get('access_urls', [])
status = provider_detail_dict.get('status', u'deployed')
provider_detail_obj = provider_details.ProviderDetail(
provider_service_id=provider_service_id,
access_urls=access_urls,
status=status)
self.provider_details[provider_name] = provider_detail_obj
self.sc.storage_controller.get_provider_details.return_value = (
self.provider_details
)
self.service_obj.provider_details = self.provider_details
self.sc.storage_controller.get_service.return_value = (
self.service_obj
)
self.sc.purge(self.project_id, self.service_id)
# ensure the manager calls the storage driver with the appropriate data
sc = self.sc.storage_controller
sc.get_provider_details.assert_called_once_with(
self.project_id,
self.service_id,
)
@ddt.file_data('data_provider_details.json')
def test_purge_not_deployed_exception(self, provider_details_json):
self.provider_details = {}
for provider_name in provider_details_json:
provider_detail_dict = json.loads(
provider_details_json[provider_name]
)
provider_service_id = provider_detail_dict.get('id', None)
access_urls = provider_detail_dict.get('access_urls', [])
status = provider_detail_dict.get('status', u'update_in_progress')
provider_detail_obj = provider_details.ProviderDetail(
provider_service_id=provider_service_id,
access_urls=access_urls,
status=status)
self.provider_details[provider_name] = provider_detail_obj
self.sc.storage_controller.get_provider_details.return_value = (
self.provider_details
)
self.service_obj.provider_details = self.provider_details
self.sc.storage_controller.get_service.return_value = (
self.service_obj
)
with testtools.ExpectedException(errors.ServiceStatusNotDeployed):
self.sc.purge(self.project_id, self.service_id)
# ensure the manager never called the storage driver
# ensure the manager never submitted a task
sc = self.sc.storage_controller
self.assertEqual(False, sc.get_provider_details.called)
self.assertEqual(False, sc.distributed_task_controller.called)
@ddt.file_data('data_provider_details.json')
def test_purge_service_worker_success(self, provider_details_json):
self.provider_details = {}
for provider_name in provider_details_json:
provider_detail_dict = json.loads(
provider_details_json[provider_name]
)
provider_service_id = provider_detail_dict.get("id", None)
access_urls = provider_detail_dict.get("access_urls", None)
status = provider_detail_dict.get("status", u'deployed')
provider_detail_obj = provider_details.ProviderDetail(
provider_service_id=provider_service_id,
access_urls=access_urls,
status=status)
self.provider_details[provider_name] = provider_detail_obj
providers = self.sc._driver.providers
def get_provider_extension_by_name(name):
if name == 'cloudfront':
return mock.Mock(
obj=mock.Mock(
provider_name='CloudFront',
service_controller=mock.Mock(
purge=mock.Mock(
return_value={
'CloudFront': {
'id':
'08d2e326-377e-11e4-b531-3c15c2b8d2d6',
}}),
)))
elif name == 'maxcdn':
return mock.Mock(obj=mock.Mock(
provider_name='MaxCDN',
service_controller=mock.Mock(
purge=mock.Mock(return_value={
'MaxCDN': {
'id':
'08d2e326-377e-11e4-b531-3c15c2b8d2d6'
}
})
)
))
else:
return mock.Mock(
obj=mock.Mock(
provider_name=name.title(),
service_controller=mock.Mock(
purge=mock.Mock(
return_value={
name.title(): {
'id':
'08d2e326-377e-11e4-b531-3c15c2b8d2d6',
}}),
)))
providers.__getitem__.side_effect = get_provider_extension_by_name
with MonkeyPatchControllers(self.sc,
self.sc.dns_controller,
self.sc.storage_controller,
self.sc.ssl_certificate_storage,
memoized_controllers.task_controllers):
self.mock_purge_service(hard=True)
self.mock_purge_service(hard=False)
@ddt.file_data('data_provider_details.json')
def test_purge_service_worker_with_error(self, provider_details_json):
self.provider_details = {}
for provider_name in provider_details_json:
provider_detail_dict = json.loads(
provider_details_json[provider_name]
)
provider_service_id = provider_detail_dict.get("id", None)
access_urls = provider_detail_dict.get("access_urls", None)
status = provider_detail_dict.get("status", u'deployed')
provider_detail_obj = provider_details.ProviderDetail(
provider_service_id=provider_service_id,
access_urls=access_urls,
status=status)
self.provider_details[provider_name] = provider_detail_obj
providers = self.sc._driver.providers
def get_provider_extension_by_name(name):
if name == 'cloudfront':
return mock.Mock(
obj=mock.Mock(
provider_name='CloudFront',
service_controller=mock.Mock(
purge=mock.Mock(
return_value={
'CloudFront': {
'id':
'08d2e326-377e-11e4-b531-3c15c2b8d2d6',
}}),
)))
else:
return mock.Mock(
obj=mock.Mock(
provider_name=name.title(),
service_controller=mock.Mock(
purge=mock.Mock(
return_value={
name.title(): {
'id':
'08d2e326-377e-11e4-b531-3c15c2b8d2d6',
}}),
)))
providers.__getitem__.side_effect = get_provider_extension_by_name
with MonkeyPatchControllers(self.sc,
self.sc.dns_controller,
self.sc.storage_controller,
self.sc.ssl_certificate_storage,
memoized_controllers.task_controllers):
self.mock_purge_service(hard=True)
self.mock_purge_service(hard=False)
def test_set_service_provider_details_missing_provider_details(self):
context.RequestContext(overwrite=True)
mock_service_obj = mock.Mock()
mock_service_obj.to_dict.return_value = {
'name': 'name',
'flavor_id': 'flavor_id',
'service_id': 'service_id',
'status': 'status',
'operator_status': 'operator_status',
'provider_details': 'provider_details',
'domains': [
{'domain': 'www.test.com'}
],
'origins': [
{
"origin": "www.tester.com",
"port": 80,
"ssl": False,
"rules": [
{
"name": "default",
"request_url": "/*"
}
],
"hostheadertype": "domain"
}
]
}
type(mock_service_obj).service_id = mock.PropertyMock(
return_value='service_id'
)
type(mock_service_obj).status = mock.PropertyMock(
return_value="create_in_progress"
)
type(mock_service_obj).provider_details = mock.PropertyMock(
return_value=dict()
)
type(mock_service_obj).domains = mock.PropertyMock(
return_value=list()
)
self.mock_storage.services_controller.get_service.return_value = (
mock_service_obj
)
self.sc.set_service_provider_details(
"project_id", "service_id", "auth_token", "deployed"
)
self.assertTrue(
self.mock_storage.services_controller.get_service.called
)
self.assertTrue(
self.mock_storage.services_controller.update_service.called
)
self.assertTrue(
self.mock_distributed_task.services_controller.submit_task.called
)
def test_migrate_domain_positive_existing_domain(self):
provider_detail_obj = provider_details.ProviderDetail(
provider_service_id='service_id',
access_urls=[
{
'domain': 'domain.com',
'operator_url': 'operator.domain.com'
}
],
status='deployed',
domains_certificate_status={
"domain.com": "deployed"
}
)
mock_provider_details = {
'Akamai': provider_detail_obj
}
ssl_cert_obj = ssl_certificate.SSLCertificate(
"flavor_id",
"domain.com",
"san",
project_id="project_id",
cert_details={
'Akamai': {
'extra_info': {
'status': 'deployed'
}
}
}
)
self.mock_storage.services_controller.\
get_provider_details.return_value = mock_provider_details
self.mock_storage.certificates_controller.\
get_certs_by_domain.return_value = ssl_cert_obj
self.sc.migrate_domain(
"project_id",
"service_id",
"domain.com",
"new_san_cert",
cert_status='create_in_progress'
)
self.mock_storage.services_controller.\
update_cert_info.assert_called_once_with(
"domain.com",
"san",
"flavor_id",
{
'Akamai': json.dumps({
'extra_info': {
'status': 'create_in_progress'
}
})
}
)
(
project_id_arg,
service_id_arg,
provider_details_arg
), _kwargs = self.mock_storage.services_controller.\
update_provider_details.call_args
self.assertEqual("project_id", project_id_arg)
self.assertEqual("service_id", service_id_arg)
self.assertEqual(
"create_in_progress",
provider_details_arg['Akamai'].domains_certificate_status.
get_domain_certificate_status(
"domain.com"
)
)
def test_migrate_domain_not_in_provider_details(self):
provider_detail_obj = provider_details.ProviderDetail(
provider_service_id='service_id',
access_urls=[
{
'domain': 'domain.com',
'operator_url': 'operator.domain.com'
}
],
status='deployed',
domains_certificate_status={
"domain.com": "deployed"
}
)
mock_provider_details = {
'Akamai': provider_detail_obj
}
ssl_cert_obj = ssl_certificate.SSLCertificate(
"flavor_id",
"new-domain.com",
"san",
project_id="project_id",
cert_details={
'Akamai': {
'extra_info': {
'status': 'deployed'
}
}
}
)
self.mock_storage.services_controller.\
get_provider_details.return_value = mock_provider_details
self.mock_storage.certificates_controller.\
get_certs_by_domain.return_value = ssl_cert_obj
self.sc.migrate_domain(
"project_id",
"service_id",
"new-domain.com",
"new_san_cert",
cert_status='create_in_progress'
)
self.mock_storage.services_controller.\
update_cert_info.assert_called_once_with(
"new-domain.com",
"san",
"flavor_id",
{
'Akamai': json.dumps({
'extra_info': {
'status': 'create_in_progress'
}
})
}
)
(
project_id_arg,
service_id_arg,
provider_details_arg
), _kwargs = self.mock_storage.services_controller.\
update_provider_details.call_args
self.assertEqual("project_id", project_id_arg)
self.assertEqual("service_id", service_id_arg)
self.assertEqual(
"create_in_progress",
provider_details_arg['Akamai'].domains_certificate_status.
get_domain_certificate_status(
"new-domain.com"
)
)
def test_migrate_domain_missing_operator_url(self):
provider_detail_obj = provider_details.ProviderDetail(
provider_service_id='service_id',
access_urls=[
{
'domain': 'domain.com',
}
],
status='deployed',
domains_certificate_status={
"domain.com": "deployed"
}
)
mock_provider_details = {
'Akamai': provider_detail_obj
}
ssl_cert_obj = ssl_certificate.SSLCertificate(
"flavor_id",
"domain.com",
"san",
project_id="project_id",
cert_details={
'Akamai': {
'extra_info': {
'status': 'deployed'
}
}
}
)
self.mock_storage.services_controller.\
get_provider_details.return_value = mock_provider_details
self.mock_storage.certificates_controller.\
get_certs_by_domain.return_value = ssl_cert_obj
self.sc.migrate_domain(
"project_id",
"service_id",
"domain.com",
"new_san_cert",
cert_status='create_in_progress'
)
self.mock_storage.services_controller.\
update_cert_info.assert_called_once_with(
"domain.com",
"san",
"flavor_id",
{
'Akamai': json.dumps({
'extra_info': {
'status': 'create_in_progress'
}
})
}
)
(
project_id_arg,
service_id_arg,
provider_details_arg
), _kwargs = self.mock_storage.services_controller.\
update_provider_details.call_args
self.assertEqual("project_id", project_id_arg)
self.assertEqual("service_id", service_id_arg)
self.assertEqual(
"create_in_progress",
provider_details_arg['Akamai'].domains_certificate_status.
get_domain_certificate_status(
"domain.com"
)
)
def test_migrate_domain_cert_not_found(self):
provider_detail_obj = provider_details.ProviderDetail(
provider_service_id='service_id',
access_urls=[
{
'domain': 'domain.com',
'operator_url': 'operator.domain.com'
}
],
status='deployed',
domains_certificate_status={
"domain.com": "deployed"
}
)
mock_provider_details = {
'Akamai': provider_detail_obj
}
self.mock_storage.services_controller.\
get_provider_details.return_value = mock_provider_details
self.mock_storage.certificates_controller.\
get_certs_by_domain.return_value = []
self.sc.migrate_domain(
"project_id",
"service_id",
"domain.com",
"new_san_cert",
cert_status='create_in_progress'
)
self.assertFalse(
self.mock_storage.services_controller.update_cert_info.called
)
(
project_id_arg,
service_id_arg,
provider_details_arg
), _kwargs = self.mock_storage.services_controller.\
update_provider_details.call_args
self.assertEqual("project_id", project_id_arg)
self.assertEqual("service_id", service_id_arg)
self.assertEqual(
"create_in_progress",
provider_details_arg['Akamai'].domains_certificate_status.
get_domain_certificate_status(
"domain.com"
)
)
def test_migrate_domain_service_not_found(self):
self.mock_storage.services_controller.\
get_provider_details.side_effect = ValueError
with testtools.ExpectedException(errors.ServiceNotFound):
self.sc.migrate_domain(
"project_id",
"service_id",
"new-domain.com",
"new_san_cert",
cert_status='create_in_progress'
)
self.assertFalse(
self.mock_storage.services_controller.update_cert_info.called
)
self.assertFalse(
self.mock_storage.services_controller.
update_provider_details.called
)
@ddt.data('delete', 'enable', 'disable')
def test_services_action_positive(self, action):
self.mock_storage.services_controller.get_services.side_effect = [
[self.service_obj],
[]
]
self.sc.services_action('project_id', action)
if action == 'delete':
self.assertTrue(self.sc.storage_controller.update_service.called)
self.assertTrue(self.sc.distributed_task_controller.submit_task.called)
def test_services_action_negative_unsupported_action(self):
self.mock_storage.services_controller.get_services.side_effect = [
[self.service_obj],
[]
]
self.sc.services_action('project_id', 'invalid-action')
self.assertFalse(
self.sc.distributed_task_controller.submit_task.called)
def test_services_action_negative_distributed_task_exception(self):
self.mock_storage.services_controller.get_services.side_effect = [
[self.service_obj],
[]
]
self.sc.distributed_task_controller.submit_task.\
side_effect = Exception(
"Mock -- Something went wrong with distributed_task driver!"
)
with mock.patch(
'oslo_log.log.KeywordArgumentAdapter.warning') as logger:
self.sc.services_action('project_id', 'enable')
self.assertTrue(logger.called)
self.assertTrue(
self.sc.distributed_task_controller.submit_task.called)
def test_get_services_limit_positive(self):
limit = 1
self.sc.storage_controller.get_service_limit.return_value = limit
self.assertEqual({'limit': limit},
self.sc.get_services_limit('project_id'))
def test_set_services_limit_positive(self):
limit = 1
self.sc.storage_controller.get_services_limit.return_value = None
try:
self.sc.services_limit('project_id', limit)
except Exception as e:
self.fail(
"Unable to set services limit in unit test: {0}".format(e))
def test_get_service_by_domain_name_positive(self):
self.sc.storage_controller.get_service_details_by_domain_name.\
return_value = self.service_obj
self.assertEqual(self.service_obj,
self.sc.get_service_by_domain_name('domain_name'))
def test_get_service_by_domain_name_not_found_error(self):
self.sc.storage_controller.get_service_details_by_domain_name.\
return_value = None
with testtools.ExpectedException(LookupError):
self.sc.get_service_by_domain_name('domain_name')
def test_get_services_by_status_positive(self):
self.sc.storage_controller.get_services_by_status.\
return_value = [self.service_obj.service_id]
self.assertEqual([self.service_obj.service_id],
self.sc.get_services_by_status('deployed'))
def test_get_domains_by_provider_url_positive(self):
domains = [
'domain-a.com',
'domain-b.com',
'domain-c.com',
]
self.sc.storage_controller.get_domains_by_provider_url.\
return_value = domains
self.assertEqual(domains,
self.sc.get_domains_by_provider_url('provider_url'))