Create Service
Change-Id: Id5c8da6b3b3bfc8aad6495b441e294a64598c260
This commit is contained in:
parent
3bed3f2ec2
commit
4735284a41
@ -3,5 +3,6 @@ omit = *poppy/openstack*
|
||||
|
||||
[report]
|
||||
exclude_lines =
|
||||
pragma: no cover
|
||||
# Don't complain if tests don't hit defensive assertion code
|
||||
raise NotImplementedError
|
||||
|
@ -52,4 +52,13 @@ keyspace = poppy
|
||||
database = poppy
|
||||
|
||||
[drivers:provider:fastly]
|
||||
apikey = "MYAPIKEY"
|
||||
apikey = "MYAPIKEY"
|
||||
|
||||
[drivers:provider:maxcdn]
|
||||
alias = "MYALIAS"
|
||||
consumer_secret = "MYCONSUMER_SECRET"
|
||||
consumer_key = "MYCONSUMERKEY"
|
||||
|
||||
[drivers:provider:cloudfront]
|
||||
aws_access_key_id = "MY_AWS_ACCESS_KEY_ID"
|
||||
aws_secret_access_key = "MY_AWS_SECRET_ACCESS_KEY"
|
||||
|
@ -18,8 +18,8 @@ from poppy.common import errors
|
||||
|
||||
class ProviderWrapper(object):
|
||||
|
||||
def create(self, ext, service_name, service_json):
|
||||
return ext.obj.service_controller.create(service_name, service_json)
|
||||
def create(self, ext, service_obj):
|
||||
return ext.obj.service_controller.create(service_obj)
|
||||
|
||||
def update(self, ext, provider_details, service_json):
|
||||
try:
|
||||
@ -28,7 +28,7 @@ class ProviderWrapper(object):
|
||||
raise errors.BadProviderDetail(
|
||||
"No provider detail information."
|
||||
"Perhaps service has not been created")
|
||||
provider_service_id = provider_detail.id
|
||||
provider_service_id = provider_detail.provider_service_id
|
||||
return ext.obj.service_controller.update(
|
||||
provider_service_id,
|
||||
service_json)
|
||||
@ -40,5 +40,5 @@ class ProviderWrapper(object):
|
||||
raise errors.BadProviderDetail(
|
||||
"No provider detail information."
|
||||
"Perhaps service has not been created")
|
||||
provider_service_id = provider_detail.id
|
||||
provider_service_id = provider_detail.provider_service_id
|
||||
return ext.obj.service_controller.delete(provider_service_id)
|
||||
|
@ -38,11 +38,11 @@ class ServicesControllerBase(controller.ManagerControllerBase):
|
||||
raise NotImplementedError
|
||||
|
||||
@abc.abstractmethod
|
||||
def create(self, project_id, service_name, service_json):
|
||||
def create(self, project_id, service_obj):
|
||||
raise NotImplementedError
|
||||
|
||||
@abc.abstractmethod
|
||||
def update(self, project_id, service_name, service_json):
|
||||
def update(self, project_id, service_name, service_obj):
|
||||
raise NotImplementedError
|
||||
|
||||
@abc.abstractmethod
|
||||
|
@ -14,6 +14,7 @@
|
||||
# limitations under the License.
|
||||
|
||||
from poppy.manager import base
|
||||
from poppy.model.helpers import provider_details
|
||||
|
||||
|
||||
class DefaultServicesController(base.ServicesController):
|
||||
@ -21,45 +22,91 @@ class DefaultServicesController(base.ServicesController):
|
||||
def __init__(self, manager):
|
||||
super(DefaultServicesController, self).__init__(manager)
|
||||
|
||||
self.storage = self._driver.storage.services_controller
|
||||
self.storage_controller = self._driver.storage.services_controller
|
||||
self.flavor_controller = self._driver.storage.flavors_controller
|
||||
|
||||
def list(self, project_id, marker=None, limit=None):
|
||||
return self.storage.list(project_id, marker, limit)
|
||||
return self.storage_controller.list(project_id, marker, limit)
|
||||
|
||||
def get(self, project_id, service_name):
|
||||
return self.storage.get(project_id, service_name)
|
||||
return self.storage_controller.get(project_id, service_name)
|
||||
|
||||
def create(self, project_id, service_name, service_obj):
|
||||
self.storage.create(
|
||||
project_id,
|
||||
service_name,
|
||||
service_obj)
|
||||
def create(self, project_id, service_obj):
|
||||
try:
|
||||
flavor = self.flavor_controller.get(service_obj.flavorRef)
|
||||
# raise a lookup error if the flavor is not found
|
||||
except LookupError as e:
|
||||
raise e
|
||||
|
||||
# TODO(tonytan4ever): need to update provider_detail info in storage
|
||||
return self._driver.providers.map(
|
||||
self.provider_wrapper.create,
|
||||
service_name,
|
||||
service_obj)
|
||||
providers = [p.provider_id for p in flavor.providers]
|
||||
service_name = service_obj.name
|
||||
|
||||
try:
|
||||
self.storage_controller.create(
|
||||
project_id,
|
||||
service_obj)
|
||||
# ValueError will be raised if the service has already existed
|
||||
except ValueError as e:
|
||||
raise e
|
||||
|
||||
responders = []
|
||||
for provider in providers:
|
||||
responder = self.provider_wrapper.create(
|
||||
self._driver.providers[provider],
|
||||
service_obj)
|
||||
responders.append(responder)
|
||||
|
||||
provider_details_dict = {}
|
||||
for responder in responders:
|
||||
for provider_name in responder:
|
||||
if 'error' not in responder[provider_name]:
|
||||
provider_details_dict[provider_name] = (
|
||||
provider_details.ProviderDetail(
|
||||
provider_service_id=responder[provider_name]['id'],
|
||||
access_urls=[link['href'] for link in
|
||||
responder[provider_name]['links']])
|
||||
)
|
||||
if 'status' in responder[provider_name]:
|
||||
provider_details_dict[provider_name].status = (
|
||||
responder[provider_name]['status'])
|
||||
else:
|
||||
provider_details_dict[provider_name].status = (
|
||||
'deployed')
|
||||
else:
|
||||
provider_details_dict[provider_name] = (
|
||||
provider_details.ProviderDetail(
|
||||
error_info=responder[provider_name]['error_detail']
|
||||
)
|
||||
)
|
||||
provider_details_dict[provider_name].status = 'failed'
|
||||
|
||||
self.storage_controller.update_provider_details(project_id,
|
||||
service_name,
|
||||
provider_details_dict)
|
||||
|
||||
return responders
|
||||
|
||||
def update(self, project_id, service_name, service_obj):
|
||||
self.storage.update(
|
||||
self.storage_controller.update(
|
||||
project_id,
|
||||
service_name,
|
||||
service_obj
|
||||
)
|
||||
|
||||
provider_details = self.storage.get_provider_details(project_id,
|
||||
service_name)
|
||||
provider_details = self.storage_controller.get_provider_details(
|
||||
project_id,
|
||||
service_name)
|
||||
return self._driver.providers.map(
|
||||
self.provider_wrapper.update,
|
||||
provider_details,
|
||||
service_obj)
|
||||
|
||||
def delete(self, project_id, service_name):
|
||||
self.storage.delete(project_id, service_name)
|
||||
self.storage_controller.delete(project_id, service_name)
|
||||
|
||||
provider_details = self.storage.get_provider_details(project_id,
|
||||
service_name)
|
||||
provider_details = self.storage_controller.get_provider_details(
|
||||
project_id,
|
||||
service_name)
|
||||
return self._driver.providers.map(
|
||||
self.provider_wrapper.delete,
|
||||
provider_details)
|
||||
|
@ -16,8 +16,8 @@
|
||||
import inspect
|
||||
try:
|
||||
import ordereddict as collections
|
||||
except ImportError:
|
||||
import collections
|
||||
except ImportError: # pragma: no cover
|
||||
import collections # pragma: no cover
|
||||
|
||||
|
||||
class DictSerializableModel(object):
|
||||
@ -48,7 +48,7 @@ class DictSerializableModel(object):
|
||||
return self
|
||||
|
||||
@classmethod
|
||||
def init_from_dict(cls, dict):
|
||||
def init_from_dict(cls, input_dict):
|
||||
"""Construct a model instance from a dictionary.
|
||||
|
||||
This is only meant to be used for converting a
|
||||
|
@ -21,27 +21,33 @@ class ProviderDetail(object):
|
||||
|
||||
'''ProviderDetail object for each provider.'''
|
||||
|
||||
def __init__(self, id=None, access_url=None, status=u"unknown", name=None):
|
||||
self._id = id
|
||||
self._access_url = access_url
|
||||
def __init__(self, provider_service_id=None, access_urls=[],
|
||||
status=u"unknown", name=None, error_info=None):
|
||||
self._provider_service_id = provider_service_id
|
||||
self._access_urls = access_urls
|
||||
self._status = status
|
||||
self._name = name
|
||||
self._error_info = error_info
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self._id
|
||||
def provider_service_id(self):
|
||||
return self._provider_service_id
|
||||
|
||||
@id.setter
|
||||
def id(self, value):
|
||||
self._id = value
|
||||
@provider_service_id.setter
|
||||
def provider_service_id(self, value):
|
||||
self._provider_service_id = value
|
||||
|
||||
@property
|
||||
def access_url(self):
|
||||
return self._access_url
|
||||
def access_urls(self):
|
||||
return self._access_urls
|
||||
|
||||
@access_url.setter
|
||||
def access_url(self, value):
|
||||
self._access_url = value
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
@access_urls.setter
|
||||
def access_urls(self, value):
|
||||
self._access_urls = value
|
||||
|
||||
@property
|
||||
def status(self):
|
||||
@ -57,3 +63,11 @@ class ProviderDetail(object):
|
||||
value,
|
||||
VALID_STATUSES)
|
||||
)
|
||||
|
||||
@property
|
||||
def error_info(self):
|
||||
return self._error_info
|
||||
|
||||
@error_info.setter
|
||||
def error_info(self, value):
|
||||
self._error_info = value
|
||||
|
@ -25,11 +25,13 @@ class Service(common.DictSerializableModel):
|
||||
name,
|
||||
domains,
|
||||
origins,
|
||||
flavorRef,
|
||||
caching=[],
|
||||
restrictions=[]):
|
||||
self._name = name
|
||||
self._domains = domains
|
||||
self._origins = origins
|
||||
self._flavorRef = flavorRef
|
||||
self._caching = caching
|
||||
self._restrictions = restrictions
|
||||
self._status = u'unknown'
|
||||
@ -58,6 +60,14 @@ class Service(common.DictSerializableModel):
|
||||
def origins(self, value):
|
||||
self._origins = value
|
||||
|
||||
@property
|
||||
def flavorRef(self):
|
||||
return self._flavorRef
|
||||
|
||||
@flavorRef.setter
|
||||
def flavorRef(self, value):
|
||||
self._flavorRef = value
|
||||
|
||||
@property
|
||||
def caching(self):
|
||||
return self._caching
|
||||
@ -92,7 +102,7 @@ class Service(common.DictSerializableModel):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def init_from_dict(cls, dict):
|
||||
def init_from_dict(cls, input_dict):
|
||||
"""Construct a model instance from a dictionary.
|
||||
|
||||
This is only meant to be used for converting a
|
||||
@ -100,6 +110,6 @@ class Service(common.DictSerializableModel):
|
||||
When converting a model into a request model,
|
||||
use to_dict.
|
||||
"""
|
||||
o = cls("unnamed", [], [])
|
||||
o.from_dict(dict)
|
||||
o = cls('unnamed', [], [], 'unnamed')
|
||||
o.from_dict(input_dict)
|
||||
return o
|
||||
|
@ -31,16 +31,19 @@ class Responder(object):
|
||||
|
||||
return {
|
||||
self.provider: {
|
||||
"error": msg
|
||||
"error": msg,
|
||||
"error_detail": traceback.format_exc()
|
||||
}
|
||||
}
|
||||
|
||||
def created(self, provider_service_id, links):
|
||||
def created(self, provider_service_id, links, **extras):
|
||||
provider_response = {
|
||||
"id": provider_service_id,
|
||||
"links": links
|
||||
}
|
||||
provider_response.update(extras)
|
||||
return {
|
||||
self.provider: {
|
||||
"id": provider_service_id,
|
||||
"links": links
|
||||
}
|
||||
self.provider: provider_response
|
||||
}
|
||||
|
||||
def updated(self, provider_service_id):
|
||||
|
@ -45,3 +45,21 @@ class ServicesControllerBase(controller.ProviderControllerBase):
|
||||
def get(self, service_name):
|
||||
"""Get details of the service, as stored by the provider."""
|
||||
raise NotImplementedError
|
||||
|
||||
def _map_service_name(self, service_name):
|
||||
"""Map poppy service name to provider's specific service name.
|
||||
|
||||
Map a poppy service name to a provider's service name so it
|
||||
can comply provider's naming convention.
|
||||
"""
|
||||
return service_name
|
||||
|
||||
@abc.abstractmethod
|
||||
def current_customer(self):
|
||||
"""Return the current customer for a provider.
|
||||
|
||||
This will needed call each provider's customer API,
|
||||
useful for certain providers ( e.g fastly) and manage
|
||||
master-sub account
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
@ -51,6 +51,7 @@ class CDNProvider(base.Driver):
|
||||
def provider_name(self):
|
||||
return 'CloudFront'
|
||||
|
||||
@property
|
||||
def client(self):
|
||||
return self.cloudfront_client
|
||||
|
||||
|
@ -15,8 +15,12 @@
|
||||
|
||||
from boto import cloudfront
|
||||
|
||||
from poppy.common import decorators
|
||||
from poppy.openstack.common import log
|
||||
from poppy.provider import base
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class ServiceController(base.ServiceBase):
|
||||
|
||||
@ -28,36 +32,46 @@ class ServiceController(base.ServiceBase):
|
||||
super(ServiceController, self).__init__(driver)
|
||||
|
||||
self.driver = driver
|
||||
self.current_customer = self.client.get_current_customer()
|
||||
|
||||
# TODO(obulpathi): get service
|
||||
def get(self, service_name):
|
||||
return {'domains': [], 'origins': [], 'caching': []}
|
||||
|
||||
# TODo(obulpathi): update service
|
||||
def update(self, service_name, service_json):
|
||||
def update(self, service_name, service_obj):
|
||||
return self.responder.updated(service_name)
|
||||
|
||||
def create(self, service_name, service_json):
|
||||
def create(self, service_obj):
|
||||
# TODO(obulpathi): create a single distribution for multiple origins
|
||||
origin = service_json['origins'][0]
|
||||
origin = service_obj.origins[0]
|
||||
LOG.info('Start creating cloudfront config for %s' % service_obj.name)
|
||||
try:
|
||||
# Create the origin for this domain:
|
||||
aws_origin = cloudfront.origin.CustomOrigin(
|
||||
dns_name=origin['origin'],
|
||||
http_port=origin['port'],
|
||||
https_port=origin['ssl'],
|
||||
dns_name=origin.origin,
|
||||
http_port=origin.port,
|
||||
# cannot specify ssl like this yet, CF takes a port #
|
||||
# https_port=origin.ssl,
|
||||
origin_protocol_policy='match-viewer')
|
||||
distribution = self.client.create_distribution(
|
||||
origin=aws_origin,
|
||||
enabled=True)
|
||||
if distribution.status == 'InProgress':
|
||||
status = 'in_progress'
|
||||
elif distribution.status == 'Deployed':
|
||||
status = 'deployed'
|
||||
else:
|
||||
status = 'unknown'
|
||||
except cloudfront.exception.CloudFrontServerError as e:
|
||||
return self.responder.failed(str(e))
|
||||
except Exception as e:
|
||||
return self.responder.failed(str(e))
|
||||
|
||||
links = [{'href': distribution.domain_name, 'rel': 'access_url'}]
|
||||
return self.responder.created(distribution.id, links)
|
||||
# extra information should be passed in here.
|
||||
LOG.info('Creating cloudfront config for %s'
|
||||
'successfull...' % service_obj.name)
|
||||
return self.responder.created(distribution.id, links, status=status)
|
||||
|
||||
def delete(self, service_name):
|
||||
# NOTE(obulpathi): distribution_id is the equivalent of service_name
|
||||
@ -68,3 +82,8 @@ class ServiceController(base.ServiceBase):
|
||||
return self.responder.deleted(distribution_id)
|
||||
except Exception as e:
|
||||
return self.responder.failed(str(e))
|
||||
|
||||
@decorators.lazy_property(write=False)
|
||||
def current_customer(self):
|
||||
# TODO(tonytan4ever/obulpathi): Implement cloudfront's current_customer
|
||||
pass
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
import fastly
|
||||
|
||||
from poppy.common import decorators
|
||||
from poppy.provider import base
|
||||
|
||||
|
||||
@ -28,47 +29,57 @@ class ServiceController(base.ServiceBase):
|
||||
super(ServiceController, self).__init__(driver)
|
||||
|
||||
self.driver = driver
|
||||
self.current_customer = self.client.get_current_customer()
|
||||
|
||||
def update(self, provider_service_id, service_json):
|
||||
def update(self, provider_service_id, service_obj):
|
||||
return self.responder.updated(provider_service_id)
|
||||
|
||||
def create(self, service_name, service_json):
|
||||
def create(self, service_obj):
|
||||
|
||||
try:
|
||||
# Create a new service
|
||||
service = self.client.create_service(self.current_customer.id,
|
||||
service_name)
|
||||
service_obj.name)
|
||||
|
||||
# Create a new version of the service.
|
||||
service_version = self.client.create_version(service.id)
|
||||
|
||||
# Create the domain for this service
|
||||
for domain in service_json["domains"]:
|
||||
for domain in service_obj.domains:
|
||||
domain = self.client.create_domain(service.id,
|
||||
service_version.number,
|
||||
domain["domain"])
|
||||
domain.domain)
|
||||
|
||||
# TODO(tonytan4ever): what if check_domains fail ?
|
||||
# For right now we fail the who create process.
|
||||
# But do we want to fail the whole service create ? probably not.
|
||||
# we need to carefully divise our try_catch here.
|
||||
links = [{"href": '.'.join([domain_dict['name'], suffix]),
|
||||
domain_checks = self.client.check_domains(service.id,
|
||||
service_version.number)
|
||||
links = [{"href": '.'.join([domain_check.domain.name,
|
||||
"global.prod.fastly.net"]),
|
||||
"rel": 'access_url'}
|
||||
for domain_dict, suffix, enabled in
|
||||
self.client.check_domains(service.id,
|
||||
service_version.number)
|
||||
if enabled]
|
||||
for domain_check in domain_checks]
|
||||
|
||||
for origin in service_json["origins"]:
|
||||
for origin in service_obj.origins:
|
||||
# Create the origins for this domain
|
||||
self.client.create_backend(service.id,
|
||||
service_version.number,
|
||||
origin["origin"],
|
||||
origin["origin"],
|
||||
origin["ssl"],
|
||||
origin["port"]
|
||||
origin.origin.replace(":", "-"),
|
||||
origin.origin,
|
||||
origin.ssl,
|
||||
origin.port
|
||||
)
|
||||
|
||||
# TODO(tonytan4ever): To incorporate caching, restriction change
|
||||
# once standarnd/limitation on these service details have been
|
||||
# figured out
|
||||
|
||||
# activate latest version of this fastly service
|
||||
service_versions = self.client.list_versions(service.id)
|
||||
latest_version_number = max([version.number
|
||||
for version in service_versions])
|
||||
self.client.activate_version(service.id, latest_version_number)
|
||||
|
||||
return self.responder.created(service.id, links)
|
||||
|
||||
except fastly.FastlyError:
|
||||
@ -122,3 +133,7 @@ class ServiceController(base.ServiceBase):
|
||||
return self.responder.failed("failed to GET service")
|
||||
except Exception:
|
||||
return self.responder.failed("failed to GET service")
|
||||
|
||||
@decorators.lazy_property(write=False)
|
||||
def current_customer(self):
|
||||
return self.client.get_current_customer()
|
||||
|
@ -13,8 +13,14 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import hashlib
|
||||
import re
|
||||
|
||||
from poppy.common import decorators
|
||||
from poppy.provider import base
|
||||
|
||||
MAXCDN_NAMING_REGEX = re.compile('^[a-zA-Z0-9-]{3,32}$')
|
||||
|
||||
|
||||
class ServiceController(base.ServiceBase):
|
||||
|
||||
@ -32,21 +38,17 @@ class ServiceController(base.ServiceBase):
|
||||
|
||||
self.driver = driver
|
||||
|
||||
# This returns the current customer account info
|
||||
account_info_return = self.client.get('/account.json')
|
||||
if account_info_return['code'] != 200:
|
||||
raise RuntimeError(account_info_return['error'])
|
||||
self.current_customer = account_info_return['data']['account']
|
||||
|
||||
def update(self, pullzone_id, service_json):
|
||||
def update(self, pullzone_id, service_obj):
|
||||
'''MaxCDN update.
|
||||
|
||||
manager needs to pass in pullzone id to delete.
|
||||
'''
|
||||
try:
|
||||
# TODO(tonytan4ever): correctly convert and update service_obj
|
||||
# to a dictionary passed into maxcdn call.
|
||||
update_response = self.client.put('/zones/pull.json/%s'
|
||||
% pullzone_id,
|
||||
params=service_json)
|
||||
params=service_obj.to_dict())
|
||||
if update_response['code'] != 200:
|
||||
return self.responder.failed('failed to update service')
|
||||
return self.responder.updated(
|
||||
@ -55,19 +57,21 @@ class ServiceController(base.ServiceBase):
|
||||
# this exception branch will most likely for a network failure
|
||||
return self.responder.failed('failed to update service')
|
||||
|
||||
def create(self, service_name, service_json):
|
||||
def create(self, service_obj):
|
||||
'''MaxCDN create.
|
||||
|
||||
manager needs to pass in a service name to create.
|
||||
'''
|
||||
try:
|
||||
# Create a new pull zone: maxcdn only supports 1 origin
|
||||
origin = service_json['origins'][0]
|
||||
origin = service_obj.origins[0]
|
||||
origin_prefix = 'https://' if origin.ssl else 'http://'
|
||||
create_response = self.client.post('/zones/pull.json', data={
|
||||
'name': service_name,
|
||||
'url': origin['origin'],
|
||||
'port': origin.get('port', 80),
|
||||
'sslshared': 1 if origin['ssl'] else 0,
|
||||
'name': self._map_service_name(service_obj.name),
|
||||
# TODO(tonytan4ever): maxcdn takes origin with
|
||||
# 'http://' or 'https://' prefix.
|
||||
'url': ''.join([origin_prefix, origin.origin]),
|
||||
'port': getattr(origin, 'port', 80),
|
||||
})
|
||||
|
||||
if create_response['code'] != 201:
|
||||
@ -77,12 +81,12 @@ class ServiceController(base.ServiceBase):
|
||||
|
||||
# Add custom domains to this service
|
||||
links = []
|
||||
for domain in service_json['domains']:
|
||||
custom_domain_response = self.client.post(
|
||||
for domain in service_obj.domains:
|
||||
self.client.post(
|
||||
'/zones/pull/%s/customdomains.json'
|
||||
% created_zone_info['id'],
|
||||
{'custom_domain': domain['domain']})
|
||||
links.append(custom_domain_response)
|
||||
{'custom_domain': domain.domain})
|
||||
links.append({'href': domain.domain, "rel": "access_url"})
|
||||
# TODO(tonytan4ever): What if it fails during add domains ?
|
||||
return self.responder.created(created_zone_info['id'], links)
|
||||
except Exception:
|
||||
@ -108,3 +112,23 @@ class ServiceController(base.ServiceBase):
|
||||
def get(self, service_name):
|
||||
'''Get details of the service, as stored by the provider.'''
|
||||
return {'domains': [], 'origins': [], 'caching': []}
|
||||
|
||||
def _map_service_name(self, service_name):
|
||||
"""Map poppy service name to provider's specific service name.
|
||||
|
||||
Map a poppy service name to a provider's service name so it
|
||||
can comply provider's naming convention.
|
||||
"""
|
||||
if MAXCDN_NAMING_REGEX.match(service_name):
|
||||
return service_name
|
||||
else:
|
||||
return hashlib.sha1(service_name.encode("utf-8")).hexdigest()[:30]
|
||||
|
||||
@decorators.lazy_property(write=False)
|
||||
def current_customer(self):
|
||||
# This returns the current customer account info
|
||||
account_info_return = self.client.get('/account.json')
|
||||
if account_info_return['code'] == 200:
|
||||
return account_info_return['data']['account']
|
||||
else:
|
||||
raise RuntimeError("Get maxcdn current customer failed...")
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
import uuid
|
||||
|
||||
from poppy.common import decorators
|
||||
from poppy.openstack.common import log
|
||||
from poppy.provider import base
|
||||
|
||||
@ -26,17 +27,23 @@ class ServiceController(base.ServiceBase):
|
||||
def __init__(self, driver):
|
||||
super(ServiceController, self).__init__(driver)
|
||||
|
||||
def update(self, provider_service_id, service_json):
|
||||
def update(self, provider_service_id, service_obj):
|
||||
return self.responder.updated(provider_service_id)
|
||||
|
||||
def create(self, service_name, service_json):
|
||||
LOG.debug("Mock creating service: %s" % service_name)
|
||||
def create(self, service_obj):
|
||||
# We generate a fake id here
|
||||
service_id = uuid.uuid1()
|
||||
return self.responder.created(str(service_id), {})
|
||||
return self.responder.created(str(service_id), [{
|
||||
"href": "www.mysite.com",
|
||||
'rel': "access_url"}])
|
||||
|
||||
def delete(self, provider_service_id):
|
||||
return self.responder.deleted(provider_service_id)
|
||||
|
||||
def get(self, service_name):
|
||||
return self.responder.get([], [], [])
|
||||
|
||||
@decorators.lazy_property(write=False)
|
||||
def current_customer(self):
|
||||
'''return current_customer for Mock. We can return a None.'''
|
||||
return None
|
||||
|
@ -49,3 +49,7 @@ class ServicesControllerBase(controller.StorageControllerBase):
|
||||
@abc.abstractmethod
|
||||
def get_provider_details(self, project_id, service_name):
|
||||
raise NotImplementedError
|
||||
|
||||
@abc.abstractmethod
|
||||
def update_provider_details(self, provider_details):
|
||||
raise NotImplementedError
|
||||
|
@ -83,11 +83,21 @@ class FlavorsController(base.FlavorsController):
|
||||
if (len(flavors) == 1):
|
||||
return flavors[0]
|
||||
else:
|
||||
raise LookupError("More than one flavor was retrieved.")
|
||||
raise LookupError("More than one flavor/no record was retrieved.")
|
||||
|
||||
def add(self, flavor):
|
||||
"""Add a new flavor."""
|
||||
|
||||
# check if the flavor already exist.
|
||||
# Note: If it does, no LookupError will be raised
|
||||
try:
|
||||
self.get(flavor.flavor_id)
|
||||
except LookupError:
|
||||
pass
|
||||
else:
|
||||
raise ValueError("Flavor %s already exists..."
|
||||
% flavor.flavor_id)
|
||||
|
||||
providers = dict((p.provider_id, p.provider_url)
|
||||
for p in flavor.providers)
|
||||
|
||||
|
@ -52,16 +52,21 @@ CQL_DELETE_SERVICE = '''
|
||||
CQL_CREATE_SERVICE = '''
|
||||
INSERT INTO services (project_id,
|
||||
service_name,
|
||||
flavor_id,
|
||||
domains,
|
||||
origins,
|
||||
caching_rules,
|
||||
restrictions)
|
||||
restrictions,
|
||||
provider_details
|
||||
)
|
||||
VALUES (%(project_id)s,
|
||||
%(service_name)s,
|
||||
%(flavor_id)s,
|
||||
%(domains)s,
|
||||
%(origins)s,
|
||||
%(caching_rules)s,
|
||||
%(restrictions)s)
|
||||
%(restrictions)s,
|
||||
%(provider_details)s)
|
||||
'''
|
||||
|
||||
CQL_UPDATE_DOMAINS = '''
|
||||
@ -94,6 +99,12 @@ CQL_GET_PROVIDER_DETAILS = '''
|
||||
WHERE project_id = %(project_id)s AND service_name = %(service_name)s
|
||||
'''
|
||||
|
||||
CQL_UPDATE_PROVIDER_DETAILS = '''
|
||||
UPDATE services
|
||||
set provider_details = %(provider_details)s
|
||||
WHERE project_id = %(project_id)s AND service_name = %(service_name)s
|
||||
'''
|
||||
|
||||
|
||||
class ServicesController(base.ServicesController):
|
||||
|
||||
@ -115,7 +126,7 @@ class ServicesController(base.ServicesController):
|
||||
# TODO(amitgandhinz): return services instead once its formatted.
|
||||
services = []
|
||||
for r in results:
|
||||
name = r.get("name", "unnamed")
|
||||
name = r.get("service_name")
|
||||
origins = r.get("origins", [])
|
||||
domains = r.get("domains", [])
|
||||
origins = [origin.Origin(json.loads(o)['origin'],
|
||||
@ -123,7 +134,8 @@ class ServicesController(base.ServicesController):
|
||||
json.loads(o).get("ssl", False))
|
||||
for o in origins]
|
||||
domains = [domain.Domain(json.loads(d)['domain']) for d in domains]
|
||||
services.append(service.Service(name, domains, origins))
|
||||
flavorRef = r.get("flavor_id")
|
||||
services.append(service.Service(name, domains, origins, flavorRef))
|
||||
return services
|
||||
|
||||
def get(self, project_id, service_name):
|
||||
@ -135,12 +147,12 @@ class ServicesController(base.ServicesController):
|
||||
results = self.session.execute(CQL_GET_SERVICE, args)
|
||||
|
||||
if len(results) != 1:
|
||||
raise ValueError("No service or multiple service found: %s"
|
||||
% service_name)
|
||||
raise LookupError("No service or multiple service found: %s"
|
||||
% service_name)
|
||||
|
||||
services = []
|
||||
for r in results:
|
||||
name = r.get("name", "unnamed")
|
||||
name = r.get("service_name")
|
||||
origins = r.get("origins", [])
|
||||
domains = r.get("domains", [])
|
||||
origins = [origin.Origin(json.loads(o)['origin'],
|
||||
@ -148,12 +160,23 @@ class ServicesController(base.ServicesController):
|
||||
json.loads(o).get("ssl", False))
|
||||
for o in origins]
|
||||
domains = [domain.Domain(json.loads(d)['domain']) for d in domains]
|
||||
services.append(service.Service(name, domains, origins))
|
||||
flavorRef = r.get("flavor_id")
|
||||
services.append(service.Service(name, domains, origins, flavorRef))
|
||||
return services[0]
|
||||
|
||||
def create(self, project_id, service_name, service_obj):
|
||||
|
||||
def create(self, project_id, service_obj):
|
||||
# create the service in storage
|
||||
service_name = service_obj.name
|
||||
|
||||
# check if the serivce already exist.
|
||||
# Note: If it does, no LookupError will be raised
|
||||
try:
|
||||
self.get(project_id, service_name)
|
||||
except LookupError:
|
||||
pass
|
||||
else:
|
||||
raise ValueError("Service %s already exists..." % service_name)
|
||||
|
||||
domains = [json.dumps(domain.to_dict())
|
||||
for domain in service_obj.domains]
|
||||
origins = [json.dumps(origin.to_dict())
|
||||
@ -167,10 +190,13 @@ class ServicesController(base.ServicesController):
|
||||
args = {
|
||||
'project_id': project_id,
|
||||
'service_name': service_name,
|
||||
'flavor_id': service_obj.flavorRef,
|
||||
'domains': domains,
|
||||
'origins': origins,
|
||||
'caching_rules': caching_rules,
|
||||
'restrictions': restrictions
|
||||
'restrictions': restrictions,
|
||||
# TODO(tonytan4ever): Incorporate flavor change.
|
||||
'provider_details': {}
|
||||
}
|
||||
|
||||
self.session.execute(CQL_CREATE_SERVICE, args)
|
||||
@ -206,12 +232,37 @@ class ServicesController(base.ServicesController):
|
||||
results = {}
|
||||
for provider_name in exec_results[0]:
|
||||
provider_detail_dict = json.loads(exec_results[0][provider_name])
|
||||
id = provider_detail_dict.get("id", None)
|
||||
access_url = provider_detail_dict.get("access_url", None)
|
||||
pr_id = provider_detail_dict.get("provider_service_id", None)
|
||||
access_urls = provider_detail_dict.get("access_urls", None)
|
||||
status = provider_detail_dict.get("status", u'unknown')
|
||||
error_info = provider_detail_dict.get("error_info", None)
|
||||
provider_detail_obj = provider_details.ProviderDetail(
|
||||
id=id,
|
||||
access_url=access_url,
|
||||
status=status)
|
||||
provider_service_id=pr_id,
|
||||
access_urls=access_urls,
|
||||
status=status,
|
||||
error_info=error_info)
|
||||
results[provider_name] = provider_detail_obj
|
||||
return results
|
||||
|
||||
def update_provider_details(self, project_id, service_name,
|
||||
provider_details):
|
||||
provider_detail_dict = {}
|
||||
for provider_name in provider_details:
|
||||
provider_detail_dict[provider_name] = json.dumps({
|
||||
"id": provider_details[provider_name].provider_service_id,
|
||||
"access_urls": provider_details[provider_name].access_urls,
|
||||
"status": provider_details[provider_name].status,
|
||||
"name": provider_details[provider_name].name,
|
||||
"error_info": provider_details[provider_name].error_info
|
||||
})
|
||||
args = {
|
||||
'project_id': project_id,
|
||||
'service_name': service_name,
|
||||
'provider_details': provider_detail_dict
|
||||
}
|
||||
# TODO(tonytan4ever): Not sure this returns a list or a single
|
||||
# dictionary.
|
||||
# Needs to verify after cassandra unittest framework has been added in
|
||||
# if a list, the return the first item of a list. if it is a dictionary
|
||||
# returns the dictionary
|
||||
self.session.execute(CQL_UPDATE_PROVIDER_DETAILS, args)
|
||||
|
@ -13,6 +13,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from poppy.model import flavor
|
||||
from poppy.storage import base
|
||||
|
||||
|
||||
@ -23,10 +24,24 @@ class FlavorsController(base.FlavorsController):
|
||||
return self._driver.database
|
||||
|
||||
def list(self):
|
||||
return []
|
||||
f = flavor.Flavor(
|
||||
"standard",
|
||||
[flavor.Provider("cloudfront", "www.cloudfront.com"),
|
||||
flavor.Provider("fastly", "www.fastly.com"),
|
||||
flavor.Provider("mock", "www.mock_provider.com")]
|
||||
)
|
||||
return [f]
|
||||
|
||||
def get(self, flavor_id):
|
||||
return None
|
||||
f = flavor.Flavor(
|
||||
"standard",
|
||||
[flavor.Provider("cloudfront", "www.cloudfront.com"),
|
||||
flavor.Provider("fastly", "www.fastly.com"),
|
||||
flavor.Provider("mock", "www.mock_provider.com")]
|
||||
)
|
||||
if flavor_id == "non_exist":
|
||||
raise LookupError("More than one flavor/no record was retrieved.")
|
||||
return f
|
||||
|
||||
def add(self, flavor):
|
||||
pass
|
||||
|
@ -42,6 +42,7 @@ class ServicesController(base.ServicesController):
|
||||
"ssl": False
|
||||
}
|
||||
],
|
||||
"flavorRef": "standard",
|
||||
"caching": [
|
||||
{"name": "default", "ttl": 3600},
|
||||
{
|
||||
@ -76,12 +77,14 @@ class ServicesController(base.ServicesController):
|
||||
|
||||
services_result = []
|
||||
for r in services:
|
||||
name = r.get("name", "unnamed")
|
||||
name = r.get("name")
|
||||
origins = r.get("origins", [])
|
||||
domains = r.get("domains", [])
|
||||
origins = [origin.Origin(d) for d in origins]
|
||||
domains = [domain.Domain(d) for d in domains]
|
||||
services_result.append(service.Service(name, domains, origins))
|
||||
flavorRef = r.get("name").split("/")[-1]
|
||||
services_result.append(service.Service(name, domains, origins,
|
||||
flavorRef))
|
||||
|
||||
return services_result
|
||||
|
||||
@ -101,6 +104,7 @@ class ServicesController(base.ServicesController):
|
||||
"ssl": False
|
||||
}
|
||||
],
|
||||
"flavorRef": "standard",
|
||||
"caching": [
|
||||
{"name": "default", "ttl": 3600},
|
||||
{
|
||||
@ -132,16 +136,18 @@ class ServicesController(base.ServicesController):
|
||||
],
|
||||
}
|
||||
|
||||
name = service_dict.get("name", "unnamed")
|
||||
name = service_dict.get("name")
|
||||
origins = service_dict.get("origins", [])
|
||||
domains = service_dict.get("domains", [])
|
||||
origins = [origin.Origin(d) for d in origins]
|
||||
domains = [domain.Domain(d) for d in domains]
|
||||
services_result = service.Service(name, domains, origins)
|
||||
flavorRef = service_dict.get("name").split("/")[-1]
|
||||
services_result = service.Service(name, domains, origins, flavorRef)
|
||||
return services_result
|
||||
|
||||
def create(self, project_id, service_name, service_json):
|
||||
|
||||
def create(self, project_id, service_obj):
|
||||
if service_obj.name == "mockdb1_service_name":
|
||||
raise ValueError("Service %s already exists..." % service_obj.name)
|
||||
return ""
|
||||
|
||||
def update(self, project_id, service_name, service_json):
|
||||
@ -156,19 +162,23 @@ class ServicesController(base.ServicesController):
|
||||
def get_provider_details(self, project_id, service_name):
|
||||
return {
|
||||
"MaxCDN": provider_details.ProviderDetail(
|
||||
id=11942,
|
||||
provider_service_id=11942,
|
||||
name='my_service_name',
|
||||
access_url='my_service_name'
|
||||
'.mycompanyalias.netdna-cdn.com'),
|
||||
access_urls=['my_service_name'
|
||||
'.mycompanyalias.netdna-cdn.com']),
|
||||
"Fastly": provider_details.ProviderDetail(
|
||||
id=3488,
|
||||
provider_service_id=3488,
|
||||
name="my_service_name",
|
||||
access_url='my_service_name'
|
||||
'.global.prod.fastly.net'),
|
||||
access_urls=['my_service_name'
|
||||
'.global.prod.fastly.net']),
|
||||
"CloudFront": provider_details.ProviderDetail(
|
||||
id=5892,
|
||||
access_url='my_service_name'
|
||||
'.gibberish.amzcf.com'),
|
||||
provider_service_id=5892,
|
||||
access_urls=['my_service_name'
|
||||
'.gibberish.amzcf.com']),
|
||||
"Mock": provider_details.ProviderDetail(
|
||||
id="73242",
|
||||
access_url='my_service_name.mock.com')}
|
||||
provider_service_id="73242",
|
||||
access_urls=['my_service_name.mock.com'])}
|
||||
|
||||
def update_provider_details(self, project_id, service_name,
|
||||
provider_details):
|
||||
pass
|
||||
|
@ -43,14 +43,12 @@ class FlavorsController(base.Controller):
|
||||
@pecan.expose('json')
|
||||
def get_one(self, flavor_id):
|
||||
flavors_controller = self.driver.manager.flavors_controller
|
||||
result = flavors_controller.get(flavor_id)
|
||||
|
||||
if result is not None:
|
||||
print (result)
|
||||
print('done')
|
||||
return flavor_response.Model(result, pecan.request)
|
||||
try:
|
||||
result = flavors_controller.get(flavor_id)
|
||||
except LookupError as e:
|
||||
pecan.abort(404, detail=str(e))
|
||||
else:
|
||||
pecan.response.status = 404
|
||||
return flavor_response.Model(result, pecan.request)
|
||||
|
||||
@pecan.expose('json')
|
||||
@decorators.validate(
|
||||
|
@ -17,6 +17,7 @@ import json
|
||||
|
||||
import pecan
|
||||
|
||||
from poppy.common import uri
|
||||
from poppy.transport.pecan.controllers import base
|
||||
from poppy.transport.pecan.models.request import service as req_service_model
|
||||
from poppy.transport.pecan.models.response import link
|
||||
@ -58,20 +59,28 @@ class ServicesController(base.Controller):
|
||||
|
||||
@pecan.expose('json')
|
||||
@decorators.validate(
|
||||
service_name=rule.Rule(
|
||||
helpers.is_valid_service_name(),
|
||||
helpers.abort_with_message),
|
||||
request=rule.Rule(
|
||||
helpers.json_matches_schema(
|
||||
service.ServiceSchema.get_schema("service", "PUT")),
|
||||
service.ServiceSchema.get_schema("service", "POST")),
|
||||
helpers.abort_with_message,
|
||||
stoplight_helpers.pecan_getter))
|
||||
def put(self, service_name):
|
||||
def post(self):
|
||||
services_controller = self._driver.manager.services_controller
|
||||
service_json_dict = json.loads(pecan.request.body.decode('utf-8'))
|
||||
service_obj = req_service_model.load_from_json(service_json_dict)
|
||||
return services_controller.create(self.project_id, service_name,
|
||||
service_obj)
|
||||
service_name = service_json_dict.get("name", None)
|
||||
try:
|
||||
services_controller.create(self.project_id, service_obj)
|
||||
except LookupError as e: # error handler for no flavor
|
||||
pecan.abort(400, detail=str(e))
|
||||
except ValueError as e: # error handler for existing service name
|
||||
pecan.abort(400, detail=str(e))
|
||||
service_url = str(
|
||||
uri.encode(u'{0}/v1.0/services/{1}'.format(
|
||||
pecan.request.host_url,
|
||||
service_name)))
|
||||
pecan.response.status = 202
|
||||
pecan.response.headers["Location"] = service_url
|
||||
|
||||
@pecan.expose('json')
|
||||
def delete(self, service_name):
|
||||
|
@ -17,5 +17,5 @@ from poppy.model.helpers import domain
|
||||
|
||||
|
||||
def load_from_json(json_data):
|
||||
domain_name = json_data.get("domain", None)
|
||||
domain_name = json_data.get("domain")
|
||||
return domain.Domain(domain_name)
|
||||
|
@ -17,7 +17,7 @@ from poppy.model.helpers import origin
|
||||
|
||||
|
||||
def load_from_json(json_data):
|
||||
origin_name = json_data.get("origin", "unnamed")
|
||||
origin_name = json_data.get("origin")
|
||||
port = json_data.get("port", 80)
|
||||
ssl = json_data.get("ssl", False)
|
||||
return origin.Origin(origin_name, port, ssl)
|
||||
|
@ -19,9 +19,10 @@ from poppy.transport.pecan.models.request import origin
|
||||
|
||||
|
||||
def load_from_json(json_data):
|
||||
name = json_data.get("name", "unnamed")
|
||||
name = json_data.get("name")
|
||||
origins = json_data.get("origins", [])
|
||||
domains = json_data.get("domains", [])
|
||||
flavorRef = json_data.get("flavorRef")
|
||||
origins = [origin.load_from_json(d) for d in origins]
|
||||
domains = [domain.load_from_json(d) for d in domains]
|
||||
return service.Service(name, origins, domains)
|
||||
return service.Service(name, domains, origins, flavorRef)
|
||||
|
@ -15,8 +15,8 @@
|
||||
|
||||
try:
|
||||
import ordereddict as collections
|
||||
except ImportError:
|
||||
import collections
|
||||
except ImportError: # pragma: no cover
|
||||
import collections # pragma: no cover
|
||||
|
||||
|
||||
class Model(collections.OrderedDict):
|
||||
|
@ -15,8 +15,8 @@
|
||||
|
||||
try:
|
||||
import ordereddict as collections
|
||||
except ImportError:
|
||||
import collections
|
||||
except ImportError: # pragma: no cover
|
||||
import collections # pragma: no cover
|
||||
|
||||
from poppy.transport.pecan.models.response import link
|
||||
|
||||
|
@ -15,8 +15,8 @@
|
||||
|
||||
try:
|
||||
import ordereddict as collections
|
||||
except ImportError:
|
||||
import collections
|
||||
except ImportError: # pragma: no cover
|
||||
import collections # pragma: no cover
|
||||
|
||||
|
||||
class Model(collections.OrderedDict):
|
||||
|
@ -15,8 +15,8 @@
|
||||
|
||||
try:
|
||||
import ordereddict as collections
|
||||
except ImportError:
|
||||
import collections
|
||||
except ImportError: # pragma: no cover
|
||||
import collections # pragma: no cover
|
||||
|
||||
|
||||
class Model(collections.OrderedDict):
|
||||
|
@ -15,8 +15,8 @@
|
||||
|
||||
try:
|
||||
import ordereddict as collections
|
||||
except ImportError:
|
||||
import collections
|
||||
except ImportError: # pragma: no cover
|
||||
import collections # pragma: no cover
|
||||
|
||||
from poppy.transport.pecan.models.response import domain
|
||||
from poppy.transport.pecan.models.response import link
|
||||
|
@ -178,7 +178,10 @@ def json_matches_schema_inner(request, schema=None):
|
||||
|
||||
if len(errors_list) > 0:
|
||||
details = dict(errors=[{
|
||||
'message': str(getattr(error, "message", error))}
|
||||
'message': '-'.join([
|
||||
"[%s]" % "][".join(repr(p) for p in error.path),
|
||||
str(getattr(error, "message", error))
|
||||
])}
|
||||
for error in errors_list])
|
||||
raise exceptions.ValidationFailed(json.dumps(details))
|
||||
else:
|
||||
|
@ -1,3 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2014 Rackspace, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -13,71 +14,82 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import re
|
||||
|
||||
from poppy.transport.validators import schema_base
|
||||
|
||||
|
||||
class ServiceSchema(schema_base.SchemaBase):
|
||||
|
||||
"""JSON Schmema validation for /service."""
|
||||
'''JSON Schmema validation for /service.'''
|
||||
|
||||
schema = {
|
||||
'service': {
|
||||
'PUT': {
|
||||
'name': 'service',
|
||||
'POST': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
"domains": {
|
||||
'name': {
|
||||
'type': 'string',
|
||||
'required': True,
|
||||
},
|
||||
'domains': {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': "object",
|
||||
"properties": {
|
||||
"domain": {
|
||||
"type": "string",
|
||||
'pattern': "^(([a-zA-Z]{1})|"
|
||||
"([a-zA-Z]{1}[a-zA-Z]{1})|"
|
||||
"([a-zA-Z]{1}[0-9]{1})"
|
||||
"|([0-9]{1}[a-zA-Z]{1})|"
|
||||
"([a-zA-Z0-9][a-zA-Z0-9-_]{1,61}"
|
||||
"[a-zA-Z0-9]))\."
|
||||
"([a-zA-Z]{2,6}|"
|
||||
"[a-zA-Z0-9-]{2,30}\.[a-zA-Z]{2,3})$"
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'domain': {
|
||||
'type': 'string',
|
||||
'pattern': re.compile(
|
||||
'^(([^:/?#]+):)?'
|
||||
'(//([^/?#]*))?'
|
||||
'([^?#]*)(\?([^#]*))?'
|
||||
'(#(.*))?$',
|
||||
re.UNICODE
|
||||
)
|
||||
}}},
|
||||
'required': True,
|
||||
"minItems": 1},
|
||||
"origins": {
|
||||
'minItems': 1},
|
||||
'origins': {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': "object",
|
||||
"properties": {
|
||||
"origin": {
|
||||
"type": "string",
|
||||
"required": True},
|
||||
"port": {
|
||||
"type": "integer",
|
||||
"enum": [
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'origin': {
|
||||
'type': 'string',
|
||||
'pattern': re.compile(
|
||||
'^(([^:/?#]+):)?'
|
||||
'(//([^/?#]*))?'
|
||||
'([^?#]*)(\?([^#]*))?'
|
||||
'(#(.*))?$',
|
||||
re.UNICODE
|
||||
),
|
||||
'required': True},
|
||||
'port': {
|
||||
'type': 'integer',
|
||||
'enum': [
|
||||
80,
|
||||
443]},
|
||||
"ssl": {
|
||||
"type": "boolean"}},
|
||||
'ssl': {
|
||||
'type': 'boolean'}},
|
||||
},
|
||||
'required': True,
|
||||
"minItems": 1},
|
||||
"caching": {
|
||||
'minItems': 1},
|
||||
'caching': {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"required": True},
|
||||
"ttl": {
|
||||
"type": "integer",
|
||||
"required": True},
|
||||
"rules": {
|
||||
"type": "array",
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'name': {
|
||||
'type': 'string',
|
||||
'required': True},
|
||||
'ttl': {
|
||||
'type': 'integer',
|
||||
'required': True},
|
||||
'rules': {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': "object",
|
||||
"properties": {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'name': {
|
||||
'type': 'string'},
|
||||
'request_url': {
|
||||
@ -85,60 +97,101 @@ class ServiceSchema(schema_base.SchemaBase):
|
||||
}},
|
||||
},
|
||||
},
|
||||
'restrictions': {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'name': {
|
||||
'type': 'string',
|
||||
'required': True},
|
||||
'rules': {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'name': {
|
||||
'type': 'string'},
|
||||
'request_url': {
|
||||
'type': 'string'},
|
||||
'http_host': {
|
||||
'type': 'string'},
|
||||
'client_ip': {
|
||||
'type': 'string'},
|
||||
'http_method': {
|
||||
'type': 'string',
|
||||
'enum': [
|
||||
'GET',
|
||||
'PUT',
|
||||
'POST',
|
||||
'PATCH']}}},
|
||||
}},
|
||||
},
|
||||
},
|
||||
'flavorRef': {
|
||||
'type': 'string',
|
||||
'required': True,
|
||||
}
|
||||
}},
|
||||
'PATCH': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
"domains": {
|
||||
'domains': {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': "object",
|
||||
"properties": {
|
||||
"domain": {
|
||||
"type": "string",
|
||||
'pattern': "^(([a-zA-Z]{1})|"
|
||||
"([a-zA-Z]{1}[a-zA-Z]{1})|"
|
||||
"([a-zA-Z]{1}[0-9]{1})"
|
||||
"|([0-9]{1}[a-zA-Z]{1})|"
|
||||
"([a-zA-Z0-9][a-zA-Z0-9-_]{1,61}"
|
||||
"[a-zA-Z0-9]))\."
|
||||
"([a-zA-Z]{2,6}|"
|
||||
"[a-zA-Z0-9-]{2,30}\.[a-zA-Z]{2,3})$"
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'domain': {
|
||||
'type': 'string',
|
||||
'pattern': re.compile(
|
||||
'^(([^:/?#]+):)?'
|
||||
'(//([^/?#]*))?'
|
||||
'([^?#]*)(\?([^#]*))?'
|
||||
'(#(.*))?$',
|
||||
re.UNICODE
|
||||
)
|
||||
}}},
|
||||
},
|
||||
"origins": {
|
||||
'origins': {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': "object",
|
||||
"properties": {
|
||||
"origin": {
|
||||
"type": "string",
|
||||
"required": True},
|
||||
"port": {
|
||||
"type": "integer",
|
||||
"enum": [
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'origin': {
|
||||
'type': 'string',
|
||||
'pattern': re.compile(
|
||||
'^(([^:/?#]+):)?'
|
||||
'(//([^/?#]*))?'
|
||||
'([^?#]*)(\?([^#]*))?'
|
||||
'(#(.*))?$',
|
||||
re.UNICODE
|
||||
),
|
||||
'required': True},
|
||||
'port': {
|
||||
'type': 'integer',
|
||||
'enum': [
|
||||
80,
|
||||
443]},
|
||||
"ssl": {
|
||||
"type": "boolean"}},
|
||||
'ssl': {
|
||||
'type': 'boolean'}},
|
||||
},
|
||||
},
|
||||
"caching": {
|
||||
'caching': {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"required": True},
|
||||
"ttl": {
|
||||
"type": "integer",
|
||||
"required": True},
|
||||
"rules": {
|
||||
"type": "array",
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'name': {
|
||||
'type': 'string',
|
||||
'required': True},
|
||||
'ttl': {
|
||||
'type': 'integer',
|
||||
'required': True},
|
||||
'rules': {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': "object",
|
||||
"properties": {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'name': {
|
||||
'type': 'string'},
|
||||
'request_url': {
|
||||
@ -146,6 +199,40 @@ class ServiceSchema(schema_base.SchemaBase):
|
||||
}},
|
||||
},
|
||||
},
|
||||
'restrictions': {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'name': {
|
||||
'type': 'string',
|
||||
'required': True},
|
||||
'rules': {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'name': {
|
||||
'type': 'string'},
|
||||
'request_url': {
|
||||
'type': 'string'},
|
||||
'http_host': {
|
||||
'type': 'string'},
|
||||
'client_ip': {
|
||||
'type': 'string'},
|
||||
'http_method': {
|
||||
'type': 'string',
|
||||
'enum': [
|
||||
'GET',
|
||||
'PUT',
|
||||
'POST',
|
||||
'PATCH']}}},
|
||||
}},
|
||||
},
|
||||
},
|
||||
'flavorRef': {
|
||||
'type': 'string',
|
||||
}
|
||||
}},
|
||||
},
|
||||
}
|
||||
|
@ -49,6 +49,8 @@ poppy.storage =
|
||||
poppy.provider =
|
||||
fastly = poppy.provider.fastly:Driver
|
||||
mock = poppy.provider.mock:Driver
|
||||
cloudfront = poppy.provider.cloudfront:Driver
|
||||
maxcdn = poppy.provider.maxcdn:Driver
|
||||
|
||||
[wheel]
|
||||
universal = 1
|
||||
|
@ -1,10 +1,12 @@
|
||||
{
|
||||
"all_fields": {
|
||||
"name": "my_service_name",
|
||||
"domain_list": [{"domain": "mywebsite.com"},
|
||||
{"domain": "blog.mywebsite.com"}],
|
||||
"origin_list": [{"origins": "mywebsite.com",
|
||||
"port": 443,
|
||||
"ssl": false}],
|
||||
"flavorRef": "standard",
|
||||
"caching_list": [{"name": "default", "ttl": 3600},
|
||||
{"name": "home",
|
||||
"ttl": 1200,
|
||||
@ -12,11 +14,13 @@
|
||||
"request_url" : "/index.htm"}]}]
|
||||
},
|
||||
"caching_empty": {
|
||||
"name": "my_service_name_2",
|
||||
"domain_list": [{"domain": "mywebsite.com"},
|
||||
{"domain": "blog.mywebsite.com"}],
|
||||
"origin_list": [{"origins": "mywebsite.com",
|
||||
"port": 443,
|
||||
"ssl": false}],
|
||||
"flavorRef": "standard",
|
||||
"caching_list": []
|
||||
}
|
||||
}
|
@ -78,8 +78,9 @@ class PoppyClient(client.AutoMarshallingHTTPClient):
|
||||
PUT
|
||||
services/{service_name}
|
||||
"""
|
||||
url = '{0}/v1.0/services/{1}'.format(self.url, service_name)
|
||||
request_object = requests.CreateService(domain_list=domain_list,
|
||||
url = '{0}/v1.0/services'.format(self.url)
|
||||
request_object = requests.CreateService(service_name=service_name,
|
||||
domain_list=domain_list,
|
||||
origin_list=origin_list,
|
||||
caching_list=caching_list)
|
||||
return self.request('PUT', url,
|
||||
|
@ -21,15 +21,20 @@ from cafe.engine.models import base
|
||||
class CreateService(base.AutoMarshallingModel):
|
||||
"""Marshalling for Create Service requests."""
|
||||
|
||||
def __init__(self, domain_list=None, origin_list=None, caching_list=None):
|
||||
def __init__(self, name=None, domain_list=None, origin_list=None,
|
||||
flavorRef=None, caching_list=None):
|
||||
super(CreateService, self).__init__()
|
||||
|
||||
self.service_name = name
|
||||
self.domain_list = domain_list or []
|
||||
self.origin_list = origin_list or []
|
||||
self.flavorRef = flavorRef
|
||||
self.caching_list = caching_list or []
|
||||
|
||||
def _obj_to_json(self):
|
||||
create_service_request = {"domains": self.domain_list,
|
||||
create_service_request = {"name": self.service_name,
|
||||
"domains": self.domain_list,
|
||||
"origins": self.origin_list,
|
||||
"flavorRef": self.flavorRef,
|
||||
"caching": self.caching_list}
|
||||
return json.dumps(create_service_request)
|
||||
|
@ -11,3 +11,7 @@ apikey = "MYAPIKEY"
|
||||
alias = "MYALIAS"
|
||||
consumer_secret = "MYCONSUMER_SECRET"
|
||||
consumer_key = "MYCONSUMERKEY"
|
||||
|
||||
[drivers:provider:cloudfront]
|
||||
aws_access_key_id = "MY_AWS_ACCESS_KEY_ID"
|
||||
aws_secret_access_key = "MY_AWS_SECRET_ACCESS_KEY"
|
||||
|
@ -52,4 +52,13 @@ keyspace = poppy
|
||||
database = poppy
|
||||
|
||||
[drivers:provider:fastly]
|
||||
apikey = "MYAPIKEY"
|
||||
apikey = "MYAPIKEY"
|
||||
|
||||
[drivers:provider:maxcdn]
|
||||
alias = "MYALIAS"
|
||||
consumer_secret = "MYCONSUMER_SECRET"
|
||||
consumer_key = "MYCONSUMERKEY"
|
||||
|
||||
[drivers:provider:cloudfront]
|
||||
aws_access_key_id = "MY_AWS_ACCESS_KEY_ID"
|
||||
aws_secret_access_key = "MY_AWS_SECRET_ACCESS_KEY"
|
||||
|
@ -0,0 +1,37 @@
|
||||
{
|
||||
"using_all_fields": {
|
||||
"name": "mocksite.com",
|
||||
"domains": [
|
||||
{"domain": "test.mocksite.com" },
|
||||
{"domain": "blog.mocksite.com"}
|
||||
],
|
||||
"origins": [
|
||||
{
|
||||
"origin": "mocksite.com",
|
||||
"port": 80,
|
||||
"ssl": false
|
||||
}
|
||||
],
|
||||
"flavorRef": "standard",
|
||||
"caching": [
|
||||
{
|
||||
"name": "default",
|
||||
"ttl": 3600
|
||||
}
|
||||
],
|
||||
"restrictions": [
|
||||
{
|
||||
"name": "website only",
|
||||
"rules": [
|
||||
{
|
||||
"name": "mocksite.com",
|
||||
"http_host": "www.mocksite.com"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -0,0 +1,70 @@
|
||||
{
|
||||
"missing_origin": {
|
||||
"name": "mocksite.com",
|
||||
"domain": "my_domain.com"
|
||||
},
|
||||
"non_existing_flavor_input": {
|
||||
"name": "mocksite.com",
|
||||
"domains": [
|
||||
{"domain": "test.mocksite.com" },
|
||||
{"domain": "blog.mocksite.com"}
|
||||
],
|
||||
"origins": [
|
||||
{
|
||||
"origin": "mocksite.com",
|
||||
"port": 80,
|
||||
"ssl": false
|
||||
}
|
||||
],
|
||||
"flavorRef": "non_exist",
|
||||
"caching": [
|
||||
{
|
||||
"name": "default",
|
||||
"ttl": 3600
|
||||
}
|
||||
],
|
||||
"restrictions": [
|
||||
{
|
||||
"name": "website only",
|
||||
"rules": [
|
||||
{
|
||||
"name": "mocksite.com",
|
||||
"http_host": "www.mocksite.com"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"existing_name_service_json": {
|
||||
"name": "mockdb1_service_name",
|
||||
"domains": [
|
||||
{"domain": "test.mocksite.com" },
|
||||
{"domain": "blog.mocksite.com"}
|
||||
],
|
||||
"origins": [
|
||||
{
|
||||
"origin": "mocksite.com",
|
||||
"port": 80,
|
||||
"ssl": false
|
||||
}
|
||||
],
|
||||
"flavorRef": "standard",
|
||||
"caching": [
|
||||
{
|
||||
"name": "default",
|
||||
"ttl": 3600
|
||||
}
|
||||
],
|
||||
"restrictions": [
|
||||
{
|
||||
"name": "website only",
|
||||
"rules": [
|
||||
{
|
||||
"name": "mocksite.com",
|
||||
"http_host": "www.mocksite.com"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -48,7 +48,7 @@ class FlavorControllerTest(base.FunctionalTest):
|
||||
self.assertEqual(200, response.status_code)
|
||||
|
||||
def test_get_not_found(self):
|
||||
response = self.app.get('/v1.0/flavors/{0}'.format(uuid.uuid1()),
|
||||
response = self.app.get('/v1.0/flavors/{0}'.format("non_exist"),
|
||||
status=404,
|
||||
expect_errors=True)
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
import json
|
||||
|
||||
import ddt
|
||||
import pecan
|
||||
from webtest import app
|
||||
|
||||
@ -22,6 +23,7 @@ from poppy.transport.pecan.controllers import base as c_base
|
||||
from tests.functional.transport.pecan import base
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class ServiceControllerTest(base.FunctionalTest):
|
||||
|
||||
def test_get_all(self):
|
||||
@ -45,52 +47,30 @@ class ServiceControllerTest(base.FunctionalTest):
|
||||
self.assertTrue("domains" in response_dict)
|
||||
self.assertTrue("origins" in response_dict)
|
||||
|
||||
def test_create(self):
|
||||
@ddt.file_data("data_create_service.json")
|
||||
def test_create(self, service_json):
|
||||
# create with errorenous data: invalid json data
|
||||
self.assertRaises(app.AppError, self.app.put,
|
||||
'/v1.0/0001/services/fake_service_name_2',
|
||||
self.assertRaises(app.AppError, self.app.post,
|
||||
'/v1.0/0001/services',
|
||||
params="{", headers={
|
||||
"Content-Type": "application/json"
|
||||
})
|
||||
|
||||
# create with good data
|
||||
response = self.app.post('/v1.0/0001/services',
|
||||
params=json.dumps(service_json),
|
||||
headers={"Content-Type": "application/json"})
|
||||
self.assertEqual(202, response.status_code)
|
||||
|
||||
@ddt.file_data("data_create_service_bad_input_json.json")
|
||||
def test_create_with_bad_input_json(self, service_json):
|
||||
# create with errorenous data
|
||||
self.assertRaises(app.AppError, self.app.put,
|
||||
'/v1.0/0001/services/fake_service_name_2',
|
||||
params=json.dumps({
|
||||
"domain": "www.mytest.com"
|
||||
}), headers={
|
||||
self.assertRaises(app.AppError, self.app.post,
|
||||
'/v1.0/0001/services',
|
||||
params=json.dumps(service_json), headers={
|
||||
"Content-Type": "application/json"
|
||||
})
|
||||
|
||||
# create with good data
|
||||
response = self.app.put('/v1.0/0001/services/fake_service_name_2',
|
||||
params=json.dumps({
|
||||
"domains": [
|
||||
{"domain": "www.mywebsite.com"},
|
||||
{"domain": "blog.mywebsite.com"},
|
||||
],
|
||||
"origins": [{"origin": "mywebsite.com",
|
||||
"port": 80,
|
||||
"ssl": False},
|
||||
{"origin": "mywebsite.com",
|
||||
}],
|
||||
"caching": [{"name": "default",
|
||||
"ttl": 3600},
|
||||
{"name": "home",
|
||||
"ttl": 17200,
|
||||
"rules": [
|
||||
{"name": "index",
|
||||
"request_url":
|
||||
"/index.htm"}]},
|
||||
{"name": "images",
|
||||
"ttl": 12800,
|
||||
"rules": [
|
||||
{"name": "images",
|
||||
"request_url": "*.png"}]}
|
||||
]}),
|
||||
headers={"Content-Type": "application/json"})
|
||||
self.assertEqual(200, response.status_code)
|
||||
|
||||
def test_update(self):
|
||||
# update with erroneous data
|
||||
self.assertRaises(app.AppError, self.app.patch,
|
||||
|
@ -27,7 +27,7 @@ from poppy.transport.validators.stoplight import rule
|
||||
|
||||
class MockPecanEndpoint(object):
|
||||
|
||||
testing_schema = service.ServiceSchema.get_schema("service", "PUT")
|
||||
testing_schema = service.ServiceSchema.get_schema("service", "POST")
|
||||
|
||||
@decorators.validation_function
|
||||
def is_valid_json(r):
|
||||
|
@ -48,8 +48,9 @@ class DummyRequest(object):
|
||||
|
||||
def __init__(self):
|
||||
self.headers = dict(header1='headervalue1')
|
||||
self.method = "PUT"
|
||||
self.method = "POST"
|
||||
self.body = json.dumps({
|
||||
"name": "fake_service_name",
|
||||
"domains": [
|
||||
{"domain": "www.mywebsite.com"},
|
||||
{"domain": "blog.mywebsite.com"},
|
||||
@ -75,7 +76,8 @@ class DummyRequest(object):
|
||||
{"name": "images",
|
||||
"ttl": 12800,
|
||||
}
|
||||
]
|
||||
],
|
||||
"flavorRef": "https://www.poppycdn.io/v1.0/flavors/standard"
|
||||
})
|
||||
|
||||
|
||||
@ -91,6 +93,7 @@ class DummyRequestWithInvalidHeader(DummyRequest):
|
||||
fake_request_good = DummyRequest()
|
||||
fake_request_bad_missing_domain = DummyRequest()
|
||||
fake_request_bad_missing_domain.body = json.dumps({
|
||||
"name": "fake_service_name",
|
||||
"origins": [
|
||||
{
|
||||
"origin": "mywebsite.com",
|
||||
@ -112,7 +115,8 @@ fake_request_bad_missing_domain.body = json.dumps({
|
||||
{"name": "images", "request_url": "*.png"}
|
||||
]
|
||||
}
|
||||
]
|
||||
],
|
||||
"flavorRef": "https://www.poppycdn.io/v1.0/flavors/standard"
|
||||
})
|
||||
fake_request_bad_invalid_json_body = DummyRequest()
|
||||
fake_request_bad_invalid_json_body.body = "{"
|
||||
|
@ -24,7 +24,7 @@ from poppy.transport.validators.stoplight import rule
|
||||
from tests.functional.transport.validator import base
|
||||
|
||||
|
||||
testing_schema = service.ServiceSchema.get_schema("service", "PUT")
|
||||
testing_schema = service.ServiceSchema.get_schema("service", "POST")
|
||||
request_fit_schema = functools.partial(
|
||||
helpers.with_schema_falcon,
|
||||
schema=testing_schema)
|
||||
|
@ -31,8 +31,8 @@ class TestProviderWrapper(base.TestCase):
|
||||
# fake a provider details to work with unittest
|
||||
self.fake_provider_details = {
|
||||
"Fastly": provider_details.ProviderDetail(
|
||||
id=uuid.uuid1(),
|
||||
access_url='mydummywebsite.prod.fastly.com')}
|
||||
provider_service_id=uuid.uuid1(),
|
||||
access_urls='mydummywebsite.prod.fastly.com')}
|
||||
|
||||
def test_update_with_keyerror(self):
|
||||
mock_ext = mock.Mock(provider_name="no_existent_provider")
|
||||
@ -47,7 +47,7 @@ class TestProviderWrapper(base.TestCase):
|
||||
self.provider_wrapper_obj.update(mock_ext,
|
||||
self.fake_provider_details, {})
|
||||
mock_ext.obj.service_controller.update.assert_called_once_with(
|
||||
fastly_provider_detail.id,
|
||||
fastly_provider_detail.provider_service_id,
|
||||
{})
|
||||
|
||||
def test_delete_with_keyerror(self):
|
||||
@ -62,4 +62,4 @@ class TestProviderWrapper(base.TestCase):
|
||||
fastly_provider_detail = self.fake_provider_details["Fastly"]
|
||||
self.provider_wrapper_obj.delete(mock_ext, self.fake_provider_details)
|
||||
mock_ext.obj.service_controller.delete.assert_called_once_with(
|
||||
fastly_provider_detail.id)
|
||||
fastly_provider_detail.provider_service_id)
|
||||
|
@ -21,7 +21,9 @@ from oslo.config import cfg
|
||||
|
||||
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.transport.pecan.models.request import service
|
||||
from tests.unit import base
|
||||
|
||||
|
||||
@ -44,21 +46,98 @@ class DefaultManagerServiceTests(base.TestCase):
|
||||
|
||||
self.project_id = 'mock_id'
|
||||
self.service_name = 'mock_service'
|
||||
self.service_json = ''
|
||||
self.service_json = {
|
||||
"name": "fake_service_name",
|
||||
"domains": [
|
||||
{"domain": "www.mywebsite.com"},
|
||||
{"domain": "blog.mywebsite.com"},
|
||||
],
|
||||
"origins": [
|
||||
{
|
||||
"origin": "mywebsite.com",
|
||||
"port": 80,
|
||||
"ssl": False
|
||||
},
|
||||
{
|
||||
"origin": "mywebsite.com",
|
||||
}
|
||||
],
|
||||
"caching": [
|
||||
{"name": "default", "ttl": 3600},
|
||||
{"name": "home",
|
||||
"ttl": 17200,
|
||||
"rules": [
|
||||
{"name": "index", "request_url": "/index.htm"}
|
||||
]
|
||||
},
|
||||
{"name": "images",
|
||||
"ttl": 12800,
|
||||
}
|
||||
],
|
||||
"flavorRef": "https://www.poppycdn.io/v1.0/flavors/standard"
|
||||
}
|
||||
|
||||
def test_create(self):
|
||||
service_obj = service.load_from_json(self.service_json)
|
||||
# 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")]
|
||||
)
|
||||
|
||||
self.sc.create(self.project_id, self.service_name, self.service_json)
|
||||
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(
|
||||
obj=mock.Mock(
|
||||
service_controller=mock.Mock(
|
||||
create=mock.Mock(
|
||||
return_value={
|
||||
'Cloudfront': {
|
||||
'id':
|
||||
'08d2e326-377e-11e4-b531-3c15c2b8d2d6',
|
||||
'links': [
|
||||
{
|
||||
'href': 'www.mysite.com',
|
||||
'rel': 'access_url'}],
|
||||
'status': "in_progress"}}),
|
||||
)))
|
||||
elif name == "fastly":
|
||||
return mock.Mock(obj=mock.Mock(service_controller=mock.Mock(
|
||||
create=mock.Mock(return_value={
|
||||
'Fastly': {'error': "fail to create servcice",
|
||||
'error_detail': 'Fastly Create failed'
|
||||
' because of XYZ'}})
|
||||
)
|
||||
))
|
||||
else:
|
||||
return mock.Mock(
|
||||
obj=mock.Mock(
|
||||
service_controller=mock.Mock(
|
||||
create=mock.Mock(
|
||||
return_value={
|
||||
name.title(): {
|
||||
'id':
|
||||
'08d2e326-377e-11e4-b531-3c15c2b8d2d6',
|
||||
'links': [
|
||||
{
|
||||
'href': 'www.mysite.com',
|
||||
'rel': 'access_url'}]}}),
|
||||
)))
|
||||
|
||||
providers.__getitem__.side_effect = get_provider_extension_by_name
|
||||
|
||||
self.sc.create(self.project_id, service_obj)
|
||||
|
||||
# ensure the manager calls the storage driver with the appropriate data
|
||||
self.sc.storage.create.assert_called_once_with(self.project_id,
|
||||
self.service_name,
|
||||
self.service_json)
|
||||
# and that the providers are notified.
|
||||
providers = self.sc._driver.providers
|
||||
providers.map.assert_called_once_with(self.sc.provider_wrapper.create,
|
||||
self.service_name,
|
||||
self.service_json)
|
||||
self.sc.storage_controller.create.assert_called_once_with(
|
||||
self.project_id,
|
||||
service_obj)
|
||||
|
||||
@ddt.file_data('data_provider_details.json')
|
||||
def test_update(self, provider_details_json):
|
||||
@ -67,27 +146,29 @@ class DefaultManagerServiceTests(base.TestCase):
|
||||
provider_detail_dict = json.loads(
|
||||
provider_details_json[provider_name]
|
||||
)
|
||||
id = provider_detail_dict.get("id", None)
|
||||
access_url = provider_detail_dict.get("access_url", None)
|
||||
provider_service_id = provider_detail_dict.get(
|
||||
"provider_service_id", None)
|
||||
access_urls = provider_detail_dict.get("access_url", None)
|
||||
status = provider_detail_dict.get("status", u'unknown')
|
||||
provider_detail_obj = provider_details.ProviderDetail(
|
||||
id=id,
|
||||
access_url=access_url,
|
||||
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
|
||||
|
||||
self.sc.storage.get_provider_details.return_value = (
|
||||
self.sc.storage_controller.get_provider_details.return_value = (
|
||||
self.provider_details
|
||||
)
|
||||
|
||||
self.sc.update(self.project_id, self.service_name, self.service_json)
|
||||
|
||||
# ensure the manager calls the storage driver with the appropriate data
|
||||
self.sc.storage.update.assert_called_once_with(self.project_id,
|
||||
self.service_name,
|
||||
self.service_json)
|
||||
self.sc.storage_controller.update.assert_called_once_with(
|
||||
self.project_id,
|
||||
self.service_name,
|
||||
self.service_json)
|
||||
# and that the providers are notified.
|
||||
providers.map.assert_called_once_with(self.sc.provider_wrapper.update,
|
||||
self.provider_details,
|
||||
@ -100,24 +181,27 @@ class DefaultManagerServiceTests(base.TestCase):
|
||||
provider_detail_dict = json.loads(
|
||||
provider_details_json[provider_name]
|
||||
)
|
||||
id = provider_detail_dict.get("id", None)
|
||||
access_url = provider_detail_dict.get("access_url", None)
|
||||
provider_service_id = provider_detail_dict.get(
|
||||
"provider_service_id", None)
|
||||
access_urls = provider_detail_dict.get("access_urls", None)
|
||||
status = provider_detail_dict.get("status", u'unknown')
|
||||
provider_detail_obj = provider_details.ProviderDetail(
|
||||
id=id,
|
||||
access_url=access_url,
|
||||
provider_service_id=provider_service_id,
|
||||
access_urls=access_urls,
|
||||
status=status)
|
||||
self.provider_details[provider_name] = provider_detail_obj
|
||||
|
||||
self.sc.storage.get_provider_details.return_value = (
|
||||
self.sc.storage_controller.get_provider_details.return_value = (
|
||||
self.provider_details
|
||||
)
|
||||
|
||||
self.sc.delete(self.project_id, self.service_name)
|
||||
|
||||
# ensure the manager calls the storage driver with the appropriate data
|
||||
self.sc.storage.delete.assert_called_once_with(self.project_id,
|
||||
self.service_name)
|
||||
self.sc.storage_controller.delete.assert_called_once_with(
|
||||
self.project_id,
|
||||
self.service_name
|
||||
)
|
||||
# and that the providers are notified.
|
||||
providers = self.sc._driver.providers
|
||||
providers.map.assert_called_once_with(self.sc.provider_wrapper.delete,
|
||||
|
@ -53,7 +53,7 @@ class TestServiceModel(base.TestCase):
|
||||
|
||||
def test_create(self):
|
||||
myservice = service.Service(
|
||||
self.service_name, self.mydomains, self.myorigins,
|
||||
self.service_name, self.mydomains, self.myorigins, self.flavorRef,
|
||||
self.mycaching, self.myrestrictions)
|
||||
|
||||
# test all properties
|
||||
@ -76,6 +76,11 @@ class TestServiceModel(base.TestCase):
|
||||
myservice.origins = []
|
||||
self.assertEqual(myservice.origins, [])
|
||||
|
||||
# flavorRef
|
||||
self.assertEqual(myservice.flavorRef, self.flavorRef)
|
||||
myservice.flavorRef = "standard"
|
||||
self.assertEqual(myservice.flavorRef, "standard")
|
||||
|
||||
self.assertEqual(myservice.restrictions, self.myrestrictions)
|
||||
myservice.restrictions = []
|
||||
self.assertEqual(myservice.restrictions, [])
|
||||
@ -88,12 +93,24 @@ class TestServiceModel(base.TestCase):
|
||||
# status
|
||||
self.assertEqual(myservice.status, u'unknown')
|
||||
|
||||
def test_init_from_dict_method(self):
|
||||
# this should generate a service copy from my service
|
||||
myservice = service.Service(
|
||||
self.service_name, self.mydomains, self.myorigins, self.flavorRef,
|
||||
self.mycaching, self.myrestrictions)
|
||||
cloned_service = service.Service.init_from_dict(myservice.to_dict())
|
||||
|
||||
self.assertEqual(cloned_service.domains, myservice.domains)
|
||||
self.assertEqual(cloned_service.origins, myservice.origins)
|
||||
self.assertEqual(cloned_service.flavorRef, myservice.flavorRef)
|
||||
|
||||
@ddt.data(u'', u'apple')
|
||||
def test_set_invalid_status(self, status):
|
||||
myservice = service.Service(
|
||||
self.service_name,
|
||||
self.mydomains,
|
||||
self.myorigins)
|
||||
self.myorigins,
|
||||
self.flavorRef)
|
||||
|
||||
self.assertRaises(ValueError, setattr, myservice, 'status', status)
|
||||
|
||||
@ -102,7 +119,8 @@ class TestServiceModel(base.TestCase):
|
||||
myservice = service.Service(
|
||||
self.service_name,
|
||||
self.mydomains,
|
||||
self.myorigins)
|
||||
self.myorigins,
|
||||
self.flavorRef)
|
||||
|
||||
myservice.status = status
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"service_json": {
|
||||
"name" : "mysite.com",
|
||||
"domains": [
|
||||
{"domain": "parsely.sage.com"},
|
||||
{"domain": "rosemary.thyme.net"}
|
||||
@ -7,6 +8,7 @@
|
||||
"origins": [
|
||||
{"origin": "mydomain.com", "ssl": false, "port": 80},
|
||||
{"origin": "youdomain.com", "ssl": true, "port": 443}
|
||||
]
|
||||
],
|
||||
"flavorRef" : "standard"
|
||||
}
|
||||
}
|
||||
|
@ -19,8 +19,8 @@ from boto import cloudfront
|
||||
import ddt
|
||||
import mock
|
||||
|
||||
|
||||
from poppy.provider.cloudfront import services
|
||||
from poppy.transport.pecan.models.request import service
|
||||
from tests.unit import base
|
||||
|
||||
|
||||
@ -46,30 +46,58 @@ class TestServices(base.TestCase):
|
||||
@ddt.file_data('data_service.json')
|
||||
def test_create_server_error(self, service_json):
|
||||
# create_distribution: CloudFrontServerError
|
||||
service_obj = service.load_from_json(service_json)
|
||||
side_effect = cloudfront.exception.CloudFrontServerError(
|
||||
503, 'Service Unavailable')
|
||||
self.controller.client.create_distribution.side_effect = side_effect
|
||||
|
||||
resp = self.controller.create(self.service_name, service_json)
|
||||
resp = self.controller.create(service_obj)
|
||||
self.assertIn('error', resp[self.driver.provider_name])
|
||||
|
||||
@ddt.file_data('data_service.json')
|
||||
def test_create_exception(self, service_json):
|
||||
# generic exception: Exception
|
||||
service_obj = service.load_from_json(service_json)
|
||||
self.controller.client.create_distribution.side_effect = Exception(
|
||||
'Creating service failed.')
|
||||
resp = self.controller.create(self.service_name, service_json)
|
||||
resp = self.controller.create(service_obj)
|
||||
self.assertIn('error', resp[self.driver.provider_name])
|
||||
|
||||
@ddt.file_data('data_service.json')
|
||||
def test_create(self, service_json):
|
||||
def test_create_service_in_progress(self, service_json):
|
||||
# clear run
|
||||
resp = self.controller.create(self.service_name, service_json)
|
||||
service_obj = service.load_from_json(service_json)
|
||||
self.controller.client.create_distribution.return_value = (
|
||||
mock.Mock(domain_name="jibberish.cloudfront.com",
|
||||
id=uuid.uuid1(),
|
||||
status="InProgress")
|
||||
)
|
||||
resp = self.controller.create(service_obj)
|
||||
self.assertIn('links', resp[self.driver.provider_name])
|
||||
|
||||
@ddt.file_data('data_service.json')
|
||||
def test_create_service_deployed(self, service_json):
|
||||
# clear run
|
||||
service_obj = service.load_from_json(service_json)
|
||||
self.controller.client.create_distribution.return_value = (
|
||||
mock.Mock(domain_name="jibberish.cloudfront.com",
|
||||
id=uuid.uuid1(),
|
||||
status="Deployed")
|
||||
)
|
||||
resp = self.controller.create(service_obj)
|
||||
self.assertIn('links', resp[self.driver.provider_name])
|
||||
|
||||
@ddt.file_data('data_service.json')
|
||||
def test_create_service_unknown(self, service_json):
|
||||
# clear run
|
||||
service_obj = service.load_from_json(service_json)
|
||||
resp = self.controller.create(service_obj)
|
||||
self.assertIn('links', resp[self.driver.provider_name])
|
||||
|
||||
@ddt.file_data('data_service.json')
|
||||
def test_update(self, service_json):
|
||||
resp = self.controller.update(self.service_name, service_json)
|
||||
service_obj = service.load_from_json(service_json)
|
||||
resp = self.controller.update(self.service_name, service_obj)
|
||||
self.assertIn('id', resp[self.driver.provider_name])
|
||||
|
||||
def test_delete_exceptions(self):
|
||||
@ -85,3 +113,8 @@ class TestServices(base.TestCase):
|
||||
|
||||
def test_client(self):
|
||||
self.assertNotEqual(self.controller.client(), None)
|
||||
|
||||
def test_current_customer(self):
|
||||
# TODO(tonytan4ever/obulpathi): fill in once correct
|
||||
# current_customer logic is done
|
||||
self.assertTrue(self.controller.current_customer is None)
|
||||
|
@ -1,11 +1,13 @@
|
||||
{
|
||||
"service_json": {
|
||||
"name" : "mysite.com",
|
||||
"domains": [
|
||||
{"domain": "parsely.sage.com"},
|
||||
{"domain": "rosemary.thyme.net"}
|
||||
],
|
||||
"origins": [
|
||||
{"origin": "mockdomain.com", "ssl": false, "port": 80}
|
||||
]
|
||||
],
|
||||
"flavorRef" : "standard"
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ import fastly
|
||||
import mock
|
||||
|
||||
from poppy.provider.fastly import services
|
||||
from poppy.transport.pecan.models.request import service
|
||||
from tests.unit import base
|
||||
|
||||
|
||||
@ -59,91 +60,118 @@ class TestServices(base.TestCase):
|
||||
def test_create_with_create_service_exception(self, service_json):
|
||||
# ASSERTIONS
|
||||
# create_service
|
||||
service_obj = service.load_from_json(service_json)
|
||||
|
||||
self.controller.client.create_service.side_effect = fastly.FastlyError(
|
||||
Exception('Creating service failed.'))
|
||||
resp = self.controller.create(self.service_name, service_json)
|
||||
resp = self.controller.create(service_obj)
|
||||
self.assertIn('error', resp[self.driver.provider_name])
|
||||
|
||||
@ddt.file_data('data_service.json')
|
||||
def test_create_with_create_version_exception(self, service_json):
|
||||
self.controller.client.reset_mock()
|
||||
self.controller.client.create_service.side_effect = None
|
||||
service_obj = service.load_from_json(service_json)
|
||||
|
||||
# create_version
|
||||
self.controller.client.create_version.side_effect = fastly.FastlyError(
|
||||
Exception('Creating version failed.'))
|
||||
resp = self.controller.create(self.service_name, service_json)
|
||||
resp = self.controller.create(service_obj)
|
||||
self.assertIn('error', resp[self.driver.provider_name])
|
||||
|
||||
@ddt.file_data('data_service.json')
|
||||
def test_create_with_create_domain_exception(self, service_json):
|
||||
self.controller.client.reset_mock()
|
||||
self.controller.client.create_version.side_effect = None
|
||||
service_obj = service.load_from_json(service_json)
|
||||
|
||||
# create domains
|
||||
self.controller.client.create_domain.side_effect = fastly.FastlyError(
|
||||
Exception('Creating domains failed.'))
|
||||
resp = self.controller.create(self.service_name, service_json)
|
||||
resp = self.controller.create(service_obj)
|
||||
self.assertIn('error', resp[self.driver.provider_name])
|
||||
|
||||
@ddt.file_data('data_service.json')
|
||||
def test_create_with_create_backend_exception(self, service_json):
|
||||
self.controller.client.reset_mock()
|
||||
self.controller.client.create_domain.side_effect = None
|
||||
service_obj = service.load_from_json(service_json)
|
||||
|
||||
# create backends
|
||||
self.controller.client.create_backend.side_effect = fastly.FastlyError(
|
||||
Exception('Creating backend failed.'))
|
||||
resp = self.controller.create(self.service_name, service_json)
|
||||
resp = self.controller.create(service_obj)
|
||||
self.assertIn('error', resp[self.driver.provider_name])
|
||||
|
||||
@ddt.file_data('data_service.json')
|
||||
def test_create_with_check_domains_exception(self, service_json):
|
||||
self.controller.client.reset_mock()
|
||||
self.controller.client.create_backend.side_effect = None
|
||||
service_obj = service.load_from_json(service_json)
|
||||
|
||||
self.controller.client.check_domains.side_effect = fastly.FastlyError(
|
||||
Exception('Check_domains failed.'))
|
||||
resp = self.controller.create(self.service_name, service_json)
|
||||
resp = self.controller.create(service_obj)
|
||||
self.assertIn('error', resp[self.driver.provider_name])
|
||||
|
||||
@ddt.file_data('data_service.json')
|
||||
def test_create_with_list_versions_exception(self, service_json):
|
||||
self.controller.client.reset_mock()
|
||||
self.controller.client.create_backend.side_effect = None
|
||||
service_obj = service.load_from_json(service_json)
|
||||
|
||||
self.controller.client.list_versions.side_effect = fastly.FastlyError(
|
||||
Exception('List_versions failed.'))
|
||||
resp = self.controller.create(service_obj)
|
||||
self.assertIn('error', resp[self.driver.provider_name])
|
||||
|
||||
@ddt.file_data('data_service.json')
|
||||
def test_create_with_activate_version_exception(self, service_json):
|
||||
self.controller.client.reset_mock()
|
||||
self.controller.client.create_backend.side_effect = None
|
||||
service_obj = service.load_from_json(service_json)
|
||||
|
||||
self.controller.client.active_version.side_effect = fastly.FastlyError(
|
||||
Exception('Active_version failed.'))
|
||||
resp = self.controller.create(service_obj)
|
||||
self.assertIn('error', resp[self.driver.provider_name])
|
||||
|
||||
@ddt.file_data('data_service.json')
|
||||
def test_create_with_general_exception(self, service_json):
|
||||
self.controller.client.reset_mock()
|
||||
self.controller.client.check_domains.side_effect = None
|
||||
service_obj = service.load_from_json(service_json)
|
||||
|
||||
# test a general exception
|
||||
self.controller.client.create_service.side_effect = Exception(
|
||||
'Wild exception occurred.')
|
||||
resp = self.controller.create(self.service_name, service_json)
|
||||
resp = self.controller.create(service_obj)
|
||||
self.assertIn('error', resp[self.driver.provider_name])
|
||||
|
||||
@ddt.file_data('data_service.json')
|
||||
def test_create(self, service_json):
|
||||
# instantiate
|
||||
# this case needs to set all return value for each call
|
||||
service_obj = service.load_from_json(service_json)
|
||||
|
||||
controller = services.ServiceController(self.driver)
|
||||
|
||||
controller.client.create_service.return_value = self.service_instance
|
||||
controller.client.create_version.return_value = self.version
|
||||
controller.client.list_versions.return_value = [self.version]
|
||||
controller.client.active_version.return_value = self.version
|
||||
|
||||
fastly_fake_domain_check = type(
|
||||
'FastlyDomain', (object,), {
|
||||
'name': 'fake_domain.global.prod.fastly.net'})
|
||||
controller.client.check_domains.return_value = [
|
||||
[{
|
||||
"name": "www.example.com",
|
||||
"comment": "",
|
||||
"service_id": "<fake_id>",
|
||||
"version": "1",
|
||||
"locked": True
|
||||
},
|
||||
"global.prod.fastly.net.",
|
||||
True
|
||||
]
|
||||
mock.Mock(domain=fastly_fake_domain_check)
|
||||
]
|
||||
|
||||
resp = controller.create(self.service_name, service_json)
|
||||
resp = controller.create(service_obj)
|
||||
|
||||
controller.client.create_service.assert_called_once_with(
|
||||
controller.current_customer.id, self.service_name)
|
||||
controller.current_customer.id, service_obj.name)
|
||||
|
||||
controller.client.create_version.assert_called_once_with(
|
||||
self.service_instance.id)
|
||||
@ -151,22 +179,22 @@ class TestServices(base.TestCase):
|
||||
controller.client.create_domain.assert_any_call(
|
||||
self.service_instance.id,
|
||||
self.version.number,
|
||||
service_json['domains'][0]['domain'])
|
||||
service_obj.domains[0].domain)
|
||||
|
||||
controller.client.create_domain.assert_any_call(
|
||||
self.service_instance.id,
|
||||
self.version.number,
|
||||
service_json['domains'][1]['domain'])
|
||||
service_obj.domains[1].domain)
|
||||
|
||||
controller.client.check_domains.assert_called_once_with(
|
||||
self.service_instance.id, self.version.number)
|
||||
|
||||
controller.client.create_backend.assert_has_any_call(
|
||||
self.service_instance.id, 1,
|
||||
service_json['origins'][0]['origin'],
|
||||
service_json['origins'][0]['origin'],
|
||||
service_json['origins'][0]['ssl'],
|
||||
service_json['origins'][0]['port'])
|
||||
service_obj.origins[0].origin.replace(":", "-"),
|
||||
service_obj.origins[0].origin,
|
||||
service_obj.origins[0].ssl,
|
||||
service_obj.origins[0].port)
|
||||
|
||||
self.assertIn('links', resp[self.driver.provider_name])
|
||||
|
||||
@ -231,10 +259,13 @@ class TestProviderValidation(base.TestCase):
|
||||
self.controller = services.ServiceController(self.driver)
|
||||
self.client = mock_client
|
||||
self.service_name = uuid.uuid1()
|
||||
service_json = {"domains": [{"domain": "parsely.sage.com"}],
|
||||
service_json = {"name": "mocksite.com",
|
||||
"domains": [{"domain": "parsely.sage.com"}],
|
||||
"origins": [{"origin": "mockdomain.com",
|
||||
"ssl": False, "port": 80}]}
|
||||
self.controller.create(self.service_name, service_json)
|
||||
"ssl": False, "port": 80}],
|
||||
"flavorRef": "standard"}
|
||||
service_obj = service.load_from_json(service_json)
|
||||
self.controller.create(service_obj)
|
||||
|
||||
@mock.patch('fastly.FastlyService')
|
||||
def test_get_service_details(self, mock_service):
|
||||
|
@ -1,11 +1,13 @@
|
||||
{
|
||||
"service_json": {
|
||||
"name" : "mysite.com",
|
||||
"domains": [
|
||||
{"domain": "parsely.sage.com"},
|
||||
{"domain": "rosemary.thyme.net"}
|
||||
],
|
||||
"origins": [
|
||||
{"origin": "mockdomain.com", "ssl": false, "port": 80}
|
||||
]
|
||||
],
|
||||
"flavorRef" : "standard"
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2014 Rackspace, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -19,6 +20,7 @@ from oslo.config import cfg
|
||||
|
||||
from poppy.provider.maxcdn import driver
|
||||
from poppy.provider.maxcdn import services
|
||||
from poppy.transport.pecan.models.request import service
|
||||
from tests.unit import base
|
||||
|
||||
|
||||
@ -45,7 +47,7 @@ fake_maxcdn_client_get_return_value = {u'code': 200,
|
||||
|
||||
fake_maxcdn_client_400_return_value = {
|
||||
u'code': 400,
|
||||
u'message': "operation PUT/GET/POST failed due to technical difficulties.."
|
||||
u'message': 'operation PUT/GET/POST failed due to technical difficulties..'
|
||||
}
|
||||
|
||||
|
||||
@ -63,8 +65,8 @@ class fake_maxcdn_api_client:
|
||||
def post(self, url=None, data=None):
|
||||
return {u'code': 201,
|
||||
u'data': {
|
||||
u"pullzone": {
|
||||
u"cdn_url": u"newpullzone1.alias.netdna-cdn.com",
|
||||
u'pullzone': {
|
||||
u'cdn_url': u'newpullzone1.alias.netdna-cdn.com',
|
||||
u'name': u'newpullzone1',
|
||||
u'id': u'97312'
|
||||
}}}
|
||||
@ -72,8 +74,8 @@ class fake_maxcdn_api_client:
|
||||
def put(self, url=None, params=None):
|
||||
return {u'code': 200,
|
||||
u'data': {
|
||||
u"pullzone": {
|
||||
u"cdn_url": u"newpullzone1.alias.netdna-cdn.com",
|
||||
u'pullzone': {
|
||||
u'cdn_url': u'newpullzone1.alias.netdna-cdn.com',
|
||||
u'name': u'newpullzone1',
|
||||
u'id': u'97312'
|
||||
}}}
|
||||
@ -91,19 +93,13 @@ class TestServices(base.TestCase):
|
||||
|
||||
self.conf = cfg.ConfigOpts()
|
||||
|
||||
@mock.patch.object(driver, 'MAXCDN_OPTIONS', new=MAXCDN_OPTIONS)
|
||||
def test_init(self):
|
||||
provider = driver.CDNProvider(self.conf)
|
||||
# instantiate will get
|
||||
self.assertRaises(RuntimeError, services.ServiceController, provider)
|
||||
|
||||
@mock.patch.object(driver.CDNProvider, 'client',
|
||||
new=fake_maxcdn_api_client())
|
||||
def test_get(self):
|
||||
new_driver = driver.CDNProvider(self.conf)
|
||||
# instantiate
|
||||
controller = services.ServiceController(new_driver)
|
||||
service_name = "test_service_name"
|
||||
service_name = 'test_service_name'
|
||||
self.assertTrue(controller.get(service_name) is not None)
|
||||
|
||||
@ddt.file_data('data_service.json')
|
||||
@ -114,8 +110,8 @@ class TestServices(base.TestCase):
|
||||
# instantiate
|
||||
controller = services.ServiceController(new_driver)
|
||||
# test create, everything goes through successfully
|
||||
service_name = "test_service_name"
|
||||
resp = controller.create(service_name, service_json)
|
||||
service_obj = service.load_from_json(service_json)
|
||||
resp = controller.create(service_obj)
|
||||
self.assertIn('id', resp[new_driver.provider_name])
|
||||
self.assertIn('links', resp[new_driver.provider_name])
|
||||
|
||||
@ -130,26 +126,23 @@ class TestServices(base.TestCase):
|
||||
driver.client.configure_mock(**{'get.return_value':
|
||||
fake_maxcdn_client_get_return_value
|
||||
})
|
||||
|
||||
service_name = "test_service_name"
|
||||
service_obj = service.load_from_json(service_json)
|
||||
|
||||
controller_with_create_exception = services.ServiceController(driver)
|
||||
controller_with_create_exception.client.configure_mock(**{
|
||||
"post.side_effect":
|
||||
'post.side_effect':
|
||||
RuntimeError('Creating service mysteriously failed.')})
|
||||
resp = controller_with_create_exception.create(
|
||||
service_name,
|
||||
service_json)
|
||||
service_obj)
|
||||
self.assertIn('error', resp[driver.provider_name])
|
||||
|
||||
controller_with_create_exception.client.reset_mock()
|
||||
controller_with_create_exception.client.configure_mock(**{
|
||||
'post.side_effect': None,
|
||||
"post.return_value": fake_maxcdn_client_400_return_value
|
||||
'post.return_value': fake_maxcdn_client_400_return_value
|
||||
})
|
||||
resp = controller_with_create_exception.create(
|
||||
service_name,
|
||||
service_json)
|
||||
service_obj)
|
||||
self.assertIn('error', resp[driver.provider_name])
|
||||
|
||||
@ddt.file_data('data_service.json')
|
||||
@ -160,8 +153,9 @@ class TestServices(base.TestCase):
|
||||
# instantiate
|
||||
controller = services.ServiceController(new_driver)
|
||||
# test create, everything goes through successfully
|
||||
service_name = "test_service_name"
|
||||
resp = controller.update(service_name, service_json)
|
||||
service_obj = service.load_from_json(service_json)
|
||||
service_name = 'test_service_name'
|
||||
resp = controller.update(service_name, service_obj)
|
||||
self.assertIn('id', resp[new_driver.provider_name])
|
||||
|
||||
@ddt.file_data('data_service.json')
|
||||
@ -176,11 +170,11 @@ class TestServices(base.TestCase):
|
||||
fake_maxcdn_client_get_return_value
|
||||
})
|
||||
|
||||
service_name = "test_service_name"
|
||||
service_name = 'test_service_name'
|
||||
|
||||
controller_with_update_exception = services.ServiceController(driver)
|
||||
controller_with_update_exception.client.configure_mock(**{
|
||||
"put.side_effect":
|
||||
'put.side_effect':
|
||||
RuntimeError('Updating service mysteriously failed.')})
|
||||
resp = controller_with_update_exception.update(
|
||||
service_name,
|
||||
@ -189,12 +183,13 @@ class TestServices(base.TestCase):
|
||||
|
||||
controller_with_update_exception.client.reset_mock()
|
||||
controller_with_update_exception.client.configure_mock(**{
|
||||
"put.side_effect": None,
|
||||
"put.return_value": fake_maxcdn_client_400_return_value
|
||||
'put.side_effect': None,
|
||||
'put.return_value': fake_maxcdn_client_400_return_value
|
||||
})
|
||||
service_obj = service.load_from_json(service_json)
|
||||
resp = controller_with_update_exception.update(
|
||||
service_name,
|
||||
service_json)
|
||||
service_obj)
|
||||
self.assertIn('error', resp[driver.provider_name])
|
||||
|
||||
@mock.patch.object(driver.CDNProvider, 'client',
|
||||
@ -204,7 +199,7 @@ class TestServices(base.TestCase):
|
||||
# instantiate
|
||||
controller = services.ServiceController(new_driver)
|
||||
# test create, everything goes through successfully
|
||||
service_name = "test_service_name"
|
||||
service_name = 'test_service_name'
|
||||
resp = controller.delete(service_name)
|
||||
self.assertIn('id', resp[new_driver.provider_name])
|
||||
|
||||
@ -219,11 +214,11 @@ class TestServices(base.TestCase):
|
||||
fake_maxcdn_client_get_return_value
|
||||
})
|
||||
|
||||
service_name = "test_service_name"
|
||||
service_name = 'test_service_name'
|
||||
|
||||
controller_with_delete_exception = services.ServiceController(driver)
|
||||
controller_with_delete_exception.client.configure_mock(**{
|
||||
"delete.side_effect":
|
||||
'delete.side_effect':
|
||||
RuntimeError('Deleting service mysteriously failed.')})
|
||||
resp = controller_with_delete_exception.delete(service_name)
|
||||
self.assertEqual(resp[driver.provider_name]['error'],
|
||||
@ -231,9 +226,57 @@ class TestServices(base.TestCase):
|
||||
|
||||
controller_with_delete_exception.client.reset_mock()
|
||||
controller_with_delete_exception.client.configure_mock(**{
|
||||
"delete.side_effect": None,
|
||||
"delete.return_value": fake_maxcdn_client_400_return_value
|
||||
'delete.side_effect': None,
|
||||
'delete.return_value': fake_maxcdn_client_400_return_value
|
||||
})
|
||||
resp = controller_with_delete_exception.delete(service_name)
|
||||
self.assertEqual(resp[driver.provider_name]['error'],
|
||||
'failed to delete service')
|
||||
|
||||
@ddt.data('good-service-name', 'yahooservice')
|
||||
@mock.patch.object(driver.CDNProvider, 'client',
|
||||
new=fake_maxcdn_api_client())
|
||||
def test_map_service_name_no_hash(self, service_name):
|
||||
maxcdn_driver = driver.CDNProvider(self.conf)
|
||||
controller = services.ServiceController(maxcdn_driver)
|
||||
self.assertEqual(controller._map_service_name(service_name),
|
||||
service_name)
|
||||
|
||||
@ddt.data(u'www.düsseldorf-Lörick.com', 'yahoo%_notvalid')
|
||||
@mock.patch.object(driver.CDNProvider, 'client',
|
||||
new=fake_maxcdn_api_client())
|
||||
def test_map_service_name_with_hash(self, service_name):
|
||||
maxcdn_driver = driver.CDNProvider(self.conf)
|
||||
controller = services.ServiceController(maxcdn_driver)
|
||||
# test hashed
|
||||
self.assertNotEqual(controller._map_service_name(service_name),
|
||||
service_name)
|
||||
# test deterministic-ity
|
||||
self.assertEqual(controller._map_service_name(service_name),
|
||||
controller._map_service_name(service_name))
|
||||
|
||||
@mock.patch.object(driver.CDNProvider, 'client',
|
||||
new=fake_maxcdn_api_client())
|
||||
def test_current_customer(self):
|
||||
new_driver = driver.CDNProvider(self.conf)
|
||||
# instantiate
|
||||
controller = services.ServiceController(new_driver)
|
||||
self.assertTrue(controller.current_customer['name'] ==
|
||||
u'<My_fake_company_alias>')
|
||||
|
||||
@mock.patch('poppy.provider.maxcdn.driver.CDNProvider.client')
|
||||
@mock.patch('poppy.provider.maxcdn.driver.CDNProvider')
|
||||
def test_current_customer_error(self, mock_controllerclient, mock_driver):
|
||||
# test create with exceptions
|
||||
driver = mock_driver()
|
||||
driver.attach_mock(mock_controllerclient, 'client')
|
||||
driver.client.configure_mock(**{'get.return_value':
|
||||
fake_maxcdn_client_400_return_value
|
||||
})
|
||||
|
||||
controller = services.ServiceController(mock_driver)
|
||||
# for some reason self.assertRaises doesn't work
|
||||
try:
|
||||
controller.current_customer
|
||||
except RuntimeError as e:
|
||||
self.assertTrue(str(e) == "Get maxcdn current customer failed...")
|
||||
|
@ -1,11 +1,13 @@
|
||||
{
|
||||
"service_json": {
|
||||
"name" : "mysite.com",
|
||||
"domains": [
|
||||
{"domain": "parsely.sage.com"},
|
||||
{"domain": "rosemary.thyme.net"}
|
||||
],
|
||||
"origins": [
|
||||
{"origin": "mockdomain.com", "ssl": false, "port": 80}
|
||||
]
|
||||
],
|
||||
"flavorRef" : "standard"
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2014 Rackspace, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -19,6 +20,7 @@ import ddt
|
||||
import mock
|
||||
|
||||
from poppy.provider.mock import services
|
||||
from poppy.transport.pecan.models.request import service
|
||||
from tests.unit import base
|
||||
|
||||
|
||||
@ -34,7 +36,8 @@ class MockProviderServicesTest(base.TestCase):
|
||||
|
||||
@ddt.file_data('data_service.json')
|
||||
def test_update(self, service_json):
|
||||
response = self.sc.update(self.test_provider_service_id, service_json)
|
||||
service_obj = service.load_from_json(service_json)
|
||||
response = self.sc.update(self.test_provider_service_id, service_obj)
|
||||
self.assertTrue(response is not None)
|
||||
|
||||
def test_delete(self):
|
||||
@ -47,5 +50,14 @@ class MockProviderServicesTest(base.TestCase):
|
||||
|
||||
@ddt.file_data('data_service.json')
|
||||
def test_create(self, service_json):
|
||||
response = self.sc.create("mock_name", service_json)
|
||||
service_obj = service.load_from_json(service_json)
|
||||
response = self.sc.create(service_obj)
|
||||
self.assertTrue(response is not None)
|
||||
|
||||
@ddt.data("my_mock_service.com", u'www.düsseldorf-Lörick.com')
|
||||
def test__map_service_name(self, service_name):
|
||||
self.assertTrue(self.sc._map_service_name(service_name),
|
||||
service_name)
|
||||
|
||||
def test_current_customer(self):
|
||||
self.assertTrue(self.sc.current_customer is None)
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"using_all_fields": [
|
||||
{
|
||||
"name" : "mocksite",
|
||||
"service_name" : "mocksite",
|
||||
"domains": [
|
||||
"{\"domain\": \"www.mocksite.com\"}",
|
||||
"{\"domain\": \"blog.mocksite.com\"}"
|
||||
@ -12,6 +12,7 @@
|
||||
"caching": [
|
||||
"{\"name\": \"default\", \"ttl\": 3600}"
|
||||
],
|
||||
"flavor_id" : "standard",
|
||||
"restrictions": [
|
||||
"{\"rules\": [{\"http_host\": \"www.mocksite.com\", \"name\": \"mocksite.com\"}], \"name\": \"website only\"}"
|
||||
]
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"using_all_fields": [
|
||||
{
|
||||
"name": "mocksite",
|
||||
"service_name": "mocksite",
|
||||
"domain": [
|
||||
"{\"domain\": \"www.mocksite.com\"}",
|
||||
"{\"domain\": \"blog.mocksite.com\"}"
|
||||
@ -9,6 +9,7 @@
|
||||
"origin": [
|
||||
"{\"origin\": \"mocksite.com\", \"ssl\": false, \"port\": 80}"
|
||||
],
|
||||
"flavor_id" : "standard",
|
||||
"caching": [
|
||||
"{\"name\": \"default\", \"ttl\": 3600}"
|
||||
],
|
||||
@ -17,7 +18,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "another_mocksite",
|
||||
"service_name": "another_mocksite",
|
||||
"domain": [
|
||||
"{\"domain\": \"www.mocksite.co.uk\"}",
|
||||
"{\"domain\": \"blog.mocksite.co.uk\"}"
|
||||
@ -25,6 +26,7 @@
|
||||
"origin": [
|
||||
"{\"origin\": \"mocksite.co.uk\", \"ssl\": false, \"port\": 80}"
|
||||
],
|
||||
"flavor_id" : "premium",
|
||||
"caching":[
|
||||
"{\"name\": \"default\", \"ttl\": 3600}"
|
||||
],
|
||||
|
@ -1,9 +1,9 @@
|
||||
{
|
||||
"provider_details":
|
||||
{
|
||||
"MaxCDN": "{\"id\": 11942, \"access_url\": \"mypullzone.netdata.com\"}",
|
||||
"Mock": "{\"id\": 73242, \"access_url\": \"mycdn.mock.com\"}",
|
||||
"CloudFront": "{\"id\": \"5ABC892\", \"access_url\": \"cf123.cloudcf.com\"}",
|
||||
"Fastly": "{\"id\": 3488, \"access_url\": \"mockcf123.fastly.prod.com\"}"
|
||||
"MaxCDN": "{\"provider_service_id\": 11942, \"access_urls\": [\"mypullzone.netdata.com\"]}",
|
||||
"Mock": "{\"provider_service_id\": 73242, \"access_urls\": [\"mycdn.mock.com\"]}",
|
||||
"CloudFront": "{\"provider_service_id\": \"5ABC892\", \"access_urls\": [\"cf123.cloudcf.com\"]}",
|
||||
"Fastly": "{\"provider_service_id\": 3488, \"access_urls\": [\"mockcf123.fastly.prod.com\"]}"
|
||||
}
|
||||
}
|
@ -20,6 +20,7 @@ import ddt
|
||||
import mock
|
||||
from oslo.config import cfg
|
||||
|
||||
from poppy.model import flavor as model_flavor
|
||||
from poppy.storage.cassandra import driver
|
||||
from poppy.storage.cassandra import flavors
|
||||
from poppy.transport.pecan.models.request import flavor
|
||||
@ -70,6 +71,10 @@ class CassandraStorageFlavorsTests(base.TestCase):
|
||||
@mock.patch.object(flavors.FlavorsController, 'session')
|
||||
@mock.patch.object(cassandra.cluster.Session, 'execute')
|
||||
def test_add_flavor(self, value, mock_session, mock_execute):
|
||||
self.fc.get = mock.Mock(side_effect=LookupError(
|
||||
"More than one flavor/no record was retrieved."
|
||||
))
|
||||
|
||||
# mock the response from cassandra
|
||||
mock_execute.execute.return_value = value
|
||||
|
||||
@ -79,6 +84,20 @@ class CassandraStorageFlavorsTests(base.TestCase):
|
||||
|
||||
self.assertEqual(actual_response, None)
|
||||
|
||||
@ddt.file_data('../data/data_create_flavor.json')
|
||||
@mock.patch.object(flavors.FlavorsController, 'session')
|
||||
@mock.patch.object(cassandra.cluster.Session, 'execute')
|
||||
def test_add_flavor_exist(self, value, mock_session, mock_execute):
|
||||
self.fc.get = mock.Mock(return_value=model_flavor.Flavor(
|
||||
flavor_id=value['id']
|
||||
))
|
||||
|
||||
# mock the response from cassandra
|
||||
mock_execute.execute.return_value = value
|
||||
new_flavor = flavor.load_from_json(value)
|
||||
|
||||
self.assertRaises(ValueError, self.fc.add, new_flavor)
|
||||
|
||||
@ddt.file_data('data_list_flavors.json')
|
||||
@mock.patch.object(flavors.FlavorsController, 'session')
|
||||
@mock.patch.object(cassandra.cluster.Session, 'execute')
|
||||
|
@ -13,12 +13,14 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import json
|
||||
|
||||
import cassandra
|
||||
import ddt
|
||||
import mock
|
||||
from oslo.config import cfg
|
||||
|
||||
from poppy.model import service
|
||||
from poppy.model.helpers import provider_details
|
||||
from poppy.storage.cassandra import driver
|
||||
from poppy.storage.cassandra import services
|
||||
from poppy.transport.pecan.models.request import service as req_service
|
||||
@ -63,7 +65,7 @@ class CassandraStorageServiceTests(base.TestCase):
|
||||
# mock the response from cassandra
|
||||
mock_execute.execute.return_value = []
|
||||
|
||||
self.assertRaises(ValueError, self.sc.get,
|
||||
self.assertRaises(LookupError, self.sc.get,
|
||||
self.project_id, self.service_name)
|
||||
|
||||
@ddt.file_data('../data/data_create_service.json')
|
||||
@ -71,10 +73,8 @@ class CassandraStorageServiceTests(base.TestCase):
|
||||
@mock.patch.object(cassandra.cluster.Session, 'execute')
|
||||
def test_create_service(self, value, mock_session, mock_execute):
|
||||
value.update({'name': self.service_name})
|
||||
request_service = req_service.load_from_json(value)
|
||||
service_obj = service.Service.init_from_dict(request_service.to_dict())
|
||||
responses = self.sc.create(self.project_id, self.service_name,
|
||||
service_obj)
|
||||
service_obj = req_service.load_from_json(value)
|
||||
responses = self.sc.create(self.project_id, service_obj)
|
||||
|
||||
# Expect the response to be None as there are no providers passed
|
||||
# into the driver to respond to this call
|
||||
@ -82,6 +82,18 @@ class CassandraStorageServiceTests(base.TestCase):
|
||||
|
||||
# TODO(amitgandhinz): need to validate the create to cassandra worked.
|
||||
|
||||
@ddt.file_data('../data/data_create_service.json')
|
||||
@mock.patch.object(services.ServicesController, 'session')
|
||||
@mock.patch.object(cassandra.cluster.Session, 'execute')
|
||||
def test_create_service_exist(self, value, mock_session, mock_execute):
|
||||
value.update({'name': self.service_name})
|
||||
service_obj = req_service.load_from_json(value)
|
||||
self.sc.get = mock.Mock(return_value=service_obj)
|
||||
|
||||
self.assertRaises(ValueError,
|
||||
self.sc.create,
|
||||
self.project_id, service_obj)
|
||||
|
||||
@ddt.file_data('data_list_services.json')
|
||||
@mock.patch.object(services.ServicesController, 'session')
|
||||
@mock.patch.object(cassandra.cluster.Session, 'execute')
|
||||
@ -133,6 +145,47 @@ class CassandraStorageServiceTests(base.TestCase):
|
||||
self.assertTrue("CloudFront" in actual_response)
|
||||
self.assertTrue("Fastly" in actual_response)
|
||||
|
||||
@ddt.file_data('data_provider_details.json')
|
||||
@mock.patch.object(services.ServicesController, 'session')
|
||||
@mock.patch.object(cassandra.cluster.Session, 'execute')
|
||||
def test_update_provider_details(self, provider_details_json,
|
||||
mock_session, mock_execute):
|
||||
provider_details_dict = {}
|
||||
for k, v in provider_details_json.items():
|
||||
provider_detail_dict = json.loads(v)
|
||||
provider_details_dict[k] = provider_details.ProviderDetail(
|
||||
provider_service_id=(
|
||||
provider_detail_dict["provider_service_id"]),
|
||||
access_urls=provider_detail_dict["access_urls"])
|
||||
|
||||
# mock the response from cassandra
|
||||
mock_execute.execute.return_value = None
|
||||
|
||||
self.sc.update_provider_details(
|
||||
self.project_id,
|
||||
self.service_name,
|
||||
provider_details_dict)
|
||||
|
||||
# this is for update_provider_details unittest code coverage
|
||||
arg_provider_details_dict = {}
|
||||
for provider_name in provider_details_dict:
|
||||
arg_provider_details_dict[provider_name] = json.dumps({
|
||||
"id": provider_details_dict[provider_name].provider_service_id,
|
||||
"access_urls": (
|
||||
provider_details_dict[provider_name].access_urls),
|
||||
"status": provider_details_dict[provider_name].status,
|
||||
"name": provider_details_dict[provider_name].name,
|
||||
"error_info": None
|
||||
})
|
||||
args = {
|
||||
'project_id': self.project_id,
|
||||
'service_name': self.service_name,
|
||||
'provider_details': arg_provider_details_dict
|
||||
}
|
||||
|
||||
mock_execute.execute.assert_called_once_with(
|
||||
services.CQL_UPDATE_PROVIDER_DETAILS, args)
|
||||
|
||||
@mock.patch.object(cassandra.cluster.Cluster, 'connect')
|
||||
def test_session(self, mock_service_database):
|
||||
session = self.sc.session
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"using_all_fields": {
|
||||
"name": "mocksite.com",
|
||||
"domains": [
|
||||
{"domain": "test.mocksite.com" },
|
||||
{"domain": "blog.mocksite.com"}
|
||||
@ -11,6 +12,7 @@
|
||||
"ssl": false
|
||||
}
|
||||
],
|
||||
"flavorRef": "standard",
|
||||
"caching": [
|
||||
{
|
||||
"name": "default",
|
||||
|
@ -45,7 +45,7 @@ class MockDBStorageFlavorsTests(base.TestCase):
|
||||
|
||||
actual_response = self.fc.get(self.flavor_id)
|
||||
|
||||
self.assertEqual(actual_response, None)
|
||||
self.assertEqual(actual_response.flavor_id, "standard")
|
||||
|
||||
@mock.patch.object(flavors.FlavorsController, 'session')
|
||||
@ddt.file_data('../data/data_create_flavor.json')
|
||||
@ -61,7 +61,8 @@ class MockDBStorageFlavorsTests(base.TestCase):
|
||||
actual_response = self.fc.list()
|
||||
|
||||
# confirm the correct number of results are returned
|
||||
self.assertEqual(actual_response, [])
|
||||
self.assertEqual(len(actual_response), 1)
|
||||
self.assertEqual(actual_response[0].flavor_id, "standard")
|
||||
|
||||
@mock.patch.object(flavors.FlavorsController, 'session')
|
||||
def test_delete_flavor(self, mock_session):
|
||||
|
Loading…
x
Reference in New Issue
Block a user