Switch service urls to use UUID instead of service name

This change makes services retrievable via serviceid
instead of by servicename.  This results in a more secure approach
to getting services, reducing the attack surface.

It also brings resources in line with other openstack services
where resources are referenced with a uuid.

Closes-Bug: 1404998

Change-Id: If9c7201e6d01a61aa7b8a3a1446e222d1f3f6c44
This commit is contained in:
amitgandhinz 2014-12-23 12:28:17 -05:00
parent 68561541c8
commit 6f90e793da
37 changed files with 705 additions and 345 deletions

View File

@ -24,5 +24,4 @@ sed -i -e "s/AKAM_SECURE_URL_LINK/$AKAM_SECURE_URL_LINK/" $CON
sed -i -e "s/AKAM_HTTP_POLICY/$AKAM_HTTP_POLICY/" $CONFIG
sed -i -e "s/AKAM_HTTPS_POLICY/$AKAM_HTTPS_POLICY/" $CONFIG
/usr/local/bin/uwsgi --ini /root/uwsgi.ini

View File

@ -13,3 +13,4 @@ no-orphans = true
vacuum = true
module = poppy.transport.app:app
py-auto-reload = 1
virtualenv = /usr/

View File

@ -3,7 +3,7 @@
##
##
FROM ubuntu
FROM ubuntu:14.04
MAINTAINER Tony Tan, tonytan198211@gmail.com
# Add PPA for the necessary JDK

View File

@ -1,6 +1,6 @@
# Dockerfile for Repose (www.openrepose.org)
FROM ubuntu
FROM ubuntu:14.04
MAINTAINER Felix Sargent (felix.sargent@rackspace.com)

View File

@ -42,11 +42,11 @@ class ServicesControllerBase(controller.ManagerControllerBase):
raise NotImplementedError
@abc.abstractmethod
def get(self, project_id, service_name):
def get(self, project_id, service_id):
"""GET
:param project_id
:param service_name
:param service_id
:raises: NotImplementedError
"""
raise NotImplementedError
@ -62,27 +62,27 @@ class ServicesControllerBase(controller.ManagerControllerBase):
raise NotImplementedError
@abc.abstractmethod
def update(self, project_id, service_name, service_obj):
def update(self, project_id, service_id, service_obj):
"""POST
:param project_id
:param service_name
:param service_id
:param service_obj
:raises: NotImplementedError
"""
raise NotImplementedError
@abc.abstractmethod
def delete(self, project_id, service_name):
def delete(self, project_id, service_id):
"""DELETE
:param project_id
:param service_name
:param service_id
:raises: NotImplementedError
"""
raise NotImplementedError
@abc.abstractmethod
def purge(self, project_id, service_name, purge_url=None):
def purge(self, project_id, service_id, purge_url=None):
'''If purge_url is none, all content of this service will be purge.'''
raise NotImplementedError

View File

@ -32,7 +32,7 @@ conf(project='poppy', prog='poppy', args=[])
def service_create_worker(providers_list_json,
project_id, service_name, service_obj_json):
project_id, service_id, service_obj_json):
LOG.logger.setLevel(logging.INFO)
bootstrap_obj = bootstrap.Bootstrap(conf)
service_controller = bootstrap_obj.manager.services_controller
@ -90,7 +90,7 @@ def service_create_worker(providers_list_json,
service_controller.storage_controller.update_provider_details(
project_id,
service_name,
service_id,
provider_details_dict)
service_controller.storage_controller._driver.close_connection()
@ -106,14 +106,14 @@ if __name__ == '__main__':
parser.add_argument('providers_list_json', action="store")
parser.add_argument('project_id', action="store")
parser.add_argument('service_name', action="store")
parser.add_argument('service_id', action="store")
parser.add_argument('service_obj_json', action="store")
result = parser.parse_args()
providers_list_json = result.providers_list_json
project_id = result.project_id
service_name = result.service_name
service_id = result.service_id
service_obj_json = result.service_obj_json
LOG.logger.setLevel(logging.INFO)
service_create_worker(providers_list_json, project_id,
service_name, service_obj_json)
service_id, service_obj_json)

View File

@ -32,7 +32,7 @@ conf(project='poppy', prog='poppy', args=[])
def service_delete_worker(provider_details,
project_id, service_name):
project_id, service_id):
LOG.logger.setLevel(logging.INFO)
bootstrap_obj = bootstrap.Bootstrap(conf)
service_controller = bootstrap_obj.manager.services_controller
@ -60,14 +60,14 @@ def service_delete_worker(provider_details,
if 'error' in responder[provider_name]:
LOG.info('Delete service from %s failed' % provider_name)
LOG.info('Updating provider detail status of %s for %s' %
(provider_name, service_name))
(provider_name, service_id))
# stores the error info for debugging purposes.
provider_details[provider_name].error_info = (
responder[provider_name].get('error_info'))
elif 'error' in dns_responder[provider_name]:
LOG.info('Delete service from DNS failed')
LOG.info('Updating provider detail status of %s for %s'.format(
(provider_name, service_name)))
(provider_name, service_id)))
# stores the error info for debugging purposes.
provider_details[provider_name].error_info = (
dns_responder[provider_name].get('error_info'))
@ -82,19 +82,19 @@ def service_delete_worker(provider_details,
# action, maybe for debug and/or support.
LOG.info('Delete failed for one or more providers'
'Updating poppy service provider details for %s' %
service_name)
service_id)
service_controller.storage_controller.update_provider_details(
project_id,
service_name,
service_id,
provider_details)
# always delete from Poppy. Provider Details will contain
# any provider issues that may have occurred.
LOG.info('Deleting poppy service %s from all providers successful'
% service_name)
service_controller.storage_controller.delete(project_id, service_name)
% service_id)
service_controller.storage_controller.delete(project_id, service_id)
service_controller.storage_controller._driver.close_connection()
LOG.info('Deleting poppy service %s succeeded' % service_name)
LOG.info('Deleting poppy service %s succeeded' % service_id)
LOG.info('Delete service worker process %s complete...' %
str(os.getpid()))
@ -107,10 +107,10 @@ if __name__ == '__main__':
parser.add_argument('provider_details', action="store")
parser.add_argument('project_id', action="store")
parser.add_argument('service_name', action="store")
parser.add_argument('service_id', action="store")
result = parser.parse_args()
provider_details = result.provider_details
project_id = result.project_id
service_name = result.service_name
service_delete_worker(provider_details, project_id, service_name)
service_id = result.service_id
service_delete_worker(provider_details, project_id, service_id)

View File

@ -32,7 +32,7 @@ conf(project='poppy', prog='poppy', args=[])
def service_purge_worker(provider_details,
project_id, service_name, purge_url):
project_id, service_id, purge_url):
LOG.logger.setLevel(logging.INFO)
bootstrap_obj = bootstrap.Bootstrap(conf)
service_controller = bootstrap_obj.manager.services_controller
@ -72,7 +72,7 @@ def service_purge_worker(provider_details,
if 'error' in responder[provider_name]:
LOG.info('Purging content from %s failed' % provider_name)
LOG.info('Updating provider detail status of %s for %s' %
(provider_name, service_name))
(provider_name, service_id))
# stores the error info for debugging purposes.
changed_provider_details_dict[provider_name] = (
provider_details[provider_name]
@ -88,7 +88,7 @@ def service_purge_worker(provider_details,
provider_details.update(changed_provider_details_dict)
service_controller.storage_controller.update_provider_details(
project_id,
service_name,
service_id,
provider_details)
service_controller.storage_controller._driver.close_connection()
@ -104,12 +104,12 @@ if __name__ == '__main__':
parser.add_argument('provider_details', action="store")
parser.add_argument('project_id', action="store")
parser.add_argument('service_name', action="store")
parser.add_argument('service_id', action="store")
parser.add_argument('purge_url', action="store")
result = parser.parse_args()
provider_details = result.provider_details
project_id = result.project_id
service_name = result.service_name
service_id = result.service_id
purge_url = result.purge_url
service_purge_worker(provider_details, project_id, service_name, purge_url)
service_purge_worker(provider_details, project_id, service_id, purge_url)

View File

@ -30,7 +30,7 @@ conf = cfg.CONF
conf(project='poppy', prog='poppy', args=[])
def update_worker(project_id, service_name,
def update_worker(project_id, service_id,
service_old, service_updates, service_obj):
LOG.logger.setLevel(logging.INFO)
bootstrap_obj = bootstrap.Bootstrap(conf)
@ -88,12 +88,12 @@ def update_worker(project_id, service_name,
'deployed')
# update the service object
service_controller.storage_controller.update(project_id, service_name,
service_controller.storage_controller.update(project_id, service_id,
service_obj)
# update the provider details
service_controller.storage_controller.update_provider_details(
project_id,
service_name,
service_id,
provider_details_dict)
service_controller.storage_controller._driver.close_connection()
@ -108,16 +108,16 @@ if __name__ == '__main__':
' script arg parser')
parser.add_argument('project_id', action="store")
parser.add_argument('service_name', action="store")
parser.add_argument('service_id', action="store")
parser.add_argument('service_old', action="store")
parser.add_argument('service_updates', action="store")
parser.add_argument('service_obj', action="store")
result = parser.parse_args()
project_id = result.project_id
service_name = result.service_name
service_id = result.service_id
service_old = result.service_old
service_updates = result.service_updates
service_obj = result.service_obj
update_worker(project_id, service_name, service_old, service_updates,
update_worker(project_id, service_id, service_old, service_updates,
service_obj)

View File

@ -32,6 +32,7 @@ LOG = log.getLogger(__name__)
class DefaultServicesController(base.ServicesController):
"""Default Services Controller."""
def __init__(self, manager):
@ -41,14 +42,14 @@ class DefaultServicesController(base.ServicesController):
self.flavor_controller = self._driver.storage.flavors_controller
self.dns_controller = self._driver.dns.services_controller
def _get_provider_details(self, project_id, service_name):
def _get_provider_details(self, project_id, service_id):
try:
provider_details = self.storage_controller.get_provider_details(
project_id,
service_name)
service_id)
except Exception:
raise LookupError(u'Service {0} does not exist'.format(
service_name))
service_id))
return provider_details
def list(self, project_id, marker=None, limit=None):
@ -61,14 +62,14 @@ class DefaultServicesController(base.ServicesController):
"""
return self.storage_controller.list(project_id, marker, limit)
def get(self, project_id, service_name):
def get(self, project_id, service_id):
"""get.
:param project_id
:param service_name
:param service_id
:return controller
"""
return self.storage_controller.get(project_id, service_name)
return self.storage_controller.get(project_id, service_id)
def create(self, project_id, service_obj):
"""create.
@ -83,7 +84,7 @@ class DefaultServicesController(base.ServicesController):
except LookupError as e:
raise e
providers = [p.provider_id for p in flavor.providers]
service_name = service_obj.name
service_id = service_obj.service_id
try:
self.storage_controller.create(
@ -107,7 +108,7 @@ class DefaultServicesController(base.ServicesController):
proxy_path,
script_path,
json.dumps(providers),
project_id, service_name,
project_id, service_id,
json.dumps(service_obj.to_dict())]
LOG.info('Starting create service subprocess: %s' % cmd_list)
p = subprocess.Popen(cmd_list, env=os.environ.copy())
@ -115,18 +116,18 @@ class DefaultServicesController(base.ServicesController):
return
def update(self, project_id, service_name, service_updates):
def update(self, project_id, service_id, service_updates):
"""update.
:param project_id
:param service_name
:param service_id
:param service_updates
"""
# get the current service object
service_old = self.storage_controller.get(project_id, service_name)
service_old = self.storage_controller.get(project_id, service_id)
if service_old.status != u'deployed':
raise errors.ServiceStatusNotDeployed(
u'Service {0} not deployed'.format(service_name))
u'Service {0} not deployed'.format(service_id))
service_obj = copy.deepcopy(service_old)
@ -145,14 +146,14 @@ class DefaultServicesController(base.ServicesController):
raise Exception(u'Currently this operation is not supported')
# get provider details for this service
provider_details = self._get_provider_details(project_id, service_name)
provider_details = self._get_provider_details(project_id, service_id)
# set status in provider details to u'update_in_progress'
for provider in provider_details:
provider_details[provider].status = u'update_in_progress'
self.storage_controller.update_provider_details(
project_id,
service_name,
service_id,
provider_details)
proxy_path = os.path.join(os.path.dirname(os.path.abspath(__file__)),
@ -168,7 +169,7 @@ class DefaultServicesController(base.ServicesController):
cmd_list = [executable,
proxy_path,
script_path,
project_id, service_name,
project_id, service_id,
json.dumps(service_old.to_dict()),
json.dumps(service_updates.to_dict()),
json.dumps(service_obj.to_dict())]
@ -178,14 +179,14 @@ class DefaultServicesController(base.ServicesController):
return
def delete(self, project_id, service_name):
def delete(self, project_id, service_id):
"""delete.
:param project_id
:param service_name
:param service_id
:raises LookupError
"""
provider_details = self._get_provider_details(project_id, service_name)
provider_details = self._get_provider_details(project_id, service_id)
# change each provider detail's status to delete_in_progress
# TODO(tonytan4ever): what if this provider is in 'failed' status?
@ -195,7 +196,7 @@ class DefaultServicesController(base.ServicesController):
self.storage_controller.update_provider_details(
project_id,
service_name,
service_id,
provider_details)
proxy_path = os.path.join(os.path.dirname(os.path.abspath(__file__)),
@ -213,16 +214,16 @@ class DefaultServicesController(base.ServicesController):
script_path,
json.dumps(dict([(k, v.to_dict())
for k, v in provider_details.items()])),
project_id, service_name]
project_id, service_id]
LOG.info('Starting delete service subprocess: %s' % cmd_list)
p = subprocess.Popen(cmd_list, env=os.environ.copy())
p.communicate()
return
def purge(self, project_id, service_name, purge_url=None):
def purge(self, project_id, service_id, purge_url=None):
'''If purge_url is none, all content of this service will be purge.'''
provider_details = self._get_provider_details(project_id, service_name)
provider_details = self._get_provider_details(project_id, service_id)
# possible validation of purge url here...
proxy_path = os.path.join(os.path.dirname(os.path.abspath(__file__)),
@ -240,7 +241,7 @@ class DefaultServicesController(base.ServicesController):
script_path,
json.dumps(dict([(k, v.to_dict())
for k, v in provider_details.items()])),
project_id, service_name,
project_id, service_id,
str(purge_url)]
LOG.info('Starting purge service subprocess: %s' % cmd_list)

View File

@ -60,6 +60,22 @@ class CachingRule(common.DictSerializableModel):
"""
self._rules = value
@classmethod
def init_from_dict(cls, dict_obj):
"""Construct a model instance from a dictionary.
This serves as a 2nd constructor
:param dict_obj: dictionary object
:returns o
"""
o = cls(
dict_obj.get("name", "unnamed"),
dict_obj.get("ttl", 0),
dict_obj.get("rules", [])
)
return o
def to_dict(self):
result = common.DictSerializableModel.to_dict(self)
# need to deserialize the nested rules object

View File

@ -17,6 +17,7 @@ from poppy.model import common
class Restriction(common.DictSerializableModel):
"""Restriction."""
def __init__(self, name, rules=[]):
@ -43,6 +44,21 @@ class Restriction(common.DictSerializableModel):
def rules(self, value):
self._rules = value
@classmethod
def init_from_dict(cls, dict_obj):
"""Construct a model instance from a dictionary.
This serves as a 2nd constructor
:param dict_obj: dictionary object
:returns o
"""
o = cls(
dict_obj.get("name", "unnamed"),
dict_obj.get("rules", [])
)
return o
def to_dict(self):
result = common.DictSerializableModel.to_dict(self)
# need to deserialize the nested rules object

View File

@ -12,10 +12,13 @@
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import uuid
from poppy.model import common
from poppy.model.helpers import cachingrule
from poppy.model.helpers import domain
from poppy.model.helpers import origin
from poppy.model.helpers import restriction
VALID_STATUSES = [u'create_in_progress', u'deployed', u'update_in_progress',
@ -27,12 +30,14 @@ class Service(common.DictSerializableModel):
"""Service Class."""
def __init__(self,
service_id,
name,
domains,
origins,
flavor_id,
caching=[],
restrictions=[]):
self._service_id = str(service_id)
self._name = name
self._domains = domains
self._origins = origins
@ -42,6 +47,15 @@ class Service(common.DictSerializableModel):
self._status = 'create_in_progress'
self._provider_details = {}
@property
def service_id(self):
"""Get service id."""
return self._service_id
@service_id.setter
def service_id(self, value):
self._service_id = value
@property
def name(self):
"""Get or set name."""
@ -159,13 +173,23 @@ class Service(common.DictSerializableModel):
When converting a model into a request model,
use to_dict.
"""
o = cls('unnamed', [], [], 'unnamed')
o = cls(service_id=uuid.uuid4(), name='unnamed',
domains=[], origins=[], flavor_id='unnamed')
domains = input_dict.get('domains', [])
input_dict['domains'] = [domain.Domain.init_from_dict(d)
for d in domains]
origins = input_dict.get('origins', [])
input_dict['origins'] = [origin.Origin.init_from_dict(og)
for og in origins]
caching_rules = input_dict.get('caching', [])
input_dict['caching'] = [cachingrule.CachingRule.init_from_dict(cr)
for cr in caching_rules]
restrictions = input_dict.get('restrictions', [])
input_dict['restrictions'] = [restriction.Restriction.init_from_dict(r)
for r in restrictions]
o.from_dict(input_dict)
return o

View File

@ -39,33 +39,33 @@ class ServicesControllerBase(controller.StorageControllerBase):
raise NotImplementedError
@abc.abstractmethod
def create(self, project_id, service_name, service_json):
def create(self, project_id, service_id, service_json):
"""create
:param project_id
:param service_name
:param service_id
:param service_json
:raise NotImplementedError
"""
raise NotImplementedError
@abc.abstractmethod
def update(self, project_id, service_name, service_json):
def update(self, project_id, service_id, service_json):
"""update
:param project_id
:param service_name
:param service_id
:param service_json
:raise NotImplementedError
"""
raise NotImplementedError
@abc.abstractmethod
def delete(self, project_id, service_name):
def delete(self, project_id, service_id):
"""delete
:param project_id
:param service_name
:param service_id
:raise NotImplementedError
"""
raise NotImplementedError
@ -79,11 +79,11 @@ class ServicesControllerBase(controller.StorageControllerBase):
raise NotImplementedError
@abc.abstractmethod
def get_provider_details(self, project_id, service_name):
def get_provider_details(self, project_id, service_id):
"""get_provider_details
:param project_id
:param service_name
:param service_id
:raise NotImplementedError
"""
raise NotImplementedError

View File

@ -3,6 +3,7 @@ USE poppy;
CREATE TABLE services (
project_id VARCHAR,
service_id UUID,
service_name VARCHAR,
flavor_id VARCHAR,
domains LIST<TEXT>,
@ -10,11 +11,19 @@ CREATE TABLE services (
caching_rules LIST<TEXT>,
restrictions LIST<TEXT>,
provider_details MAP<TEXT, TEXT>,
PRIMARY KEY (project_id, service_name)
PRIMARY KEY (project_id, service_id)
);
CREATE TABLE domain_names (
project_id VARCHAR,
service_id UUID,
domain_name VARCHAR,
PRIMARY KEY (domain_name)
);
CREATE TABLE archives (
project_id VARCHAR,
service_id UUID,
service_name VARCHAR,
flavor_id VARCHAR,
domains LIST<TEXT>,
@ -23,7 +32,7 @@ CREATE TABLE archives (
restrictions LIST<TEXT>,
provider_details MAP<TEXT, TEXT>,
archived_time timestamp,
PRIMARY KEY (project_id, service_name, archived_time)
PRIMARY KEY (project_id, service_id, archived_time)
);
CREATE TABLE flavors (

View File

@ -17,6 +17,7 @@
schema_statements = [
'''CREATE TABLE services (
project_id VARCHAR,
service_id UUID,
service_name VARCHAR,
flavor_id VARCHAR,
domains LIST<TEXT>,
@ -24,7 +25,7 @@ schema_statements = [
caching_rules LIST<TEXT>,
restrictions LIST<TEXT>,
provider_details MAP<TEXT, TEXT>,
PRIMARY KEY (project_id, service_name)
PRIMARY KEY (project_id, service_id)
);
''',
'''CREATE TABLE flavors (
@ -33,8 +34,16 @@ schema_statements = [
PRIMARY KEY (flavor_id)
);
''',
'''CREATE TABLE domain_names (
project_id VARCHAR,
service_id UUID,
domain_name VARCHAR,
PRIMARY KEY (domain_name)
);
''',
'''CREATE TABLE archives (
project_id VARCHAR,
service_id UUID,
service_name VARCHAR,
flavor_id VARCHAR,
domains LIST<TEXT>,
@ -43,7 +52,7 @@ schema_statements = [
restrictions LIST<TEXT>,
provider_details MAP<TEXT, TEXT>,
archived_time timestamp,
PRIMARY KEY (project_id, service_name, archived_time)
PRIMARY KEY (project_id, service_id, archived_time)
);
'''
]

View File

@ -15,11 +15,14 @@
import datetime
import json
import uuid
try:
import ordereddict as collections
except ImportError: # pragma: no cover
import collections # pragma: no cover
from cassandra import query
from poppy.model.helpers import cachingrule
from poppy.model.helpers import domain
from poppy.model.helpers import origin
@ -27,11 +30,15 @@ from poppy.model.helpers import provider_details
from poppy.model.helpers import restriction
from poppy.model.helpers import rule
from poppy.model import service
from poppy.openstack.common import log as logging
from poppy.storage import base
LOG = logging.getLogger(__name__)
CQL_GET_ALL_SERVICES = '''
SELECT project_id,
service_id,
service_name,
domains,
flavor_id,
@ -44,6 +51,7 @@ CQL_GET_ALL_SERVICES = '''
CQL_LIST_SERVICES = '''
SELECT project_id,
service_id,
service_name,
domains,
flavor_id,
@ -53,13 +61,14 @@ CQL_LIST_SERVICES = '''
provider_details
FROM services
WHERE project_id = %(project_id)s
AND service_name > %(service_name)s
ORDER BY service_name
AND service_id > %(marker)s
ORDER BY service_id
LIMIT %(limit)s
'''
CQL_GET_SERVICE = '''
SELECT project_id,
service_id,
service_name,
flavor_id,
domains,
@ -68,12 +77,30 @@ CQL_GET_SERVICE = '''
restrictions,
provider_details
FROM services
WHERE project_id = %(project_id)s AND service_name = %(service_name)s
WHERE project_id = %(project_id)s AND service_id = %(service_id)s
'''
CQL_VERIFY_DOMAIN = '''
SELECT project_id,
service_id,
domain_name
FROM domain_names
WHERE domain_name = %(domain_name)s
'''
CQL_CLAIM_DOMAIN = '''
INSERT INTO domain_names (domain_name,
project_id,
service_id)
VALUES (%(domain_name)s,
%(project_id)s,
%(service_id)s)
'''
CQL_ARCHIVE_SERVICE = '''
BEGIN BATCH
INSERT INTO archives (project_id,
service_id,
service_name,
flavor_id,
domains,
@ -84,6 +111,7 @@ CQL_ARCHIVE_SERVICE = '''
archived_time
)
VALUES (%(project_id)s,
%(service_id)s,
%(service_name)s,
%(flavor_id)s,
%(domains)s,
@ -94,17 +122,26 @@ CQL_ARCHIVE_SERVICE = '''
%(archived_time)s)
DELETE FROM services
WHERE project_id = %(project_id)s AND service_name = %(service_name)s;
WHERE project_id = %(project_id)s AND service_id = %(service_id)s;
DELETE FROM domain_names
WHERE domain_name IN %(domain_list)s
APPLY BATCH;
'''
CQL_DELETE_SERVICE = '''
DELETE FROM services
WHERE project_id = %(project_id)s AND service_name = %(service_name)s
BEGIN BATCH
DELETE FROM services
WHERE project_id = %(project_id)s AND service_id = %(service_id)s
DELETE FROM domain_names
WHERE domain_name IN %(domain_list)s
APPLY BATCH
'''
CQL_CREATE_SERVICE = '''
INSERT INTO services (project_id,
service_id,
service_name,
flavor_id,
domains,
@ -114,6 +151,7 @@ CQL_CREATE_SERVICE = '''
provider_details
)
VALUES (%(project_id)s,
%(service_id)s,
%(service_name)s,
%(flavor_id)s,
%(domains)s,
@ -128,37 +166,37 @@ CQL_UPDATE_SERVICE = CQL_CREATE_SERVICE
CQL_UPDATE_DOMAINS = '''
UPDATE services
SET domains = %(domains)s
WHERE project_id = %(project_id)s AND service_name = %(service_name)s
WHERE project_id = %(project_id)s AND service_id = %(service_id)s
'''
CQL_UPDATE_ORIGINS = '''
UPDATE services
SET origins = %(origins)s
WHERE project_id = %(project_id)s AND service_name = %(service_name)s
WHERE project_id = %(project_id)s AND service_id = %(service_id)s
'''
CQL_UPDATE_CACHING_RULES = '''
UPDATE services
SET caching_rules = %(caching_rules)s
WHERE project_id = %(project_id)s AND service_name = %(service_name)s
WHERE project_id = %(project_id)s AND service_id = %(service_id)s
'''
CQL_UPDATE_RESTRICTIONS = '''
UPDATE services
SET restrictions = %(restrictions)s
WHERE project_id = %(project_id)s AND service_name = %(service_name)s
WHERE project_id = %(project_id)s AND service_id = %(service_id)s
'''
CQL_GET_PROVIDER_DETAILS = '''
SELECT provider_details
FROM services
WHERE project_id = %(project_id)s AND service_name = %(service_name)s
WHERE project_id = %(project_id)s AND service_id = %(service_id)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
WHERE project_id = %(project_id)s AND service_id = %(service_id)s
'''
@ -184,9 +222,12 @@ class ServicesController(base.ServicesController):
:returns services
"""
# list services
if marker is None:
marker = '00000000-00000000-00000000-00000000'
args = {
'project_id': project_id,
'service_name': marker,
'marker': uuid.UUID(str(marker)),
'limit': limit
}
@ -195,7 +236,7 @@ class ServicesController(base.ServicesController):
return services
def get(self, project_id, service_name):
def get(self, project_id, service_id):
"""get.
:param project_id
@ -207,13 +248,13 @@ class ServicesController(base.ServicesController):
# get the requested service from storage
args = {
'project_id': project_id,
'service_name': service_name
'service_id': uuid.UUID(str(service_id))
}
results = self.session.execute(CQL_GET_SERVICE, args)
if len(results) != 1:
raise ValueError('No service or multiple service found: %s'
% service_name)
raise ValueError('No service found: %s'
% service_id)
# at this point, it is certain that there's exactly 1 result in
# results.
@ -221,6 +262,39 @@ class ServicesController(base.ServicesController):
return self.format_result(result)
def _exists_elsewhere(self, domain_name, service_id):
"""_exists_elsewhere
Check if a service with this domain name has already been created.
:param domain_name
:param service_id
:raises ValueError
:returns Boolean if the service exists with another user.
"""
try:
LOG.info("Check if service '{0}' exists".format(domain_name))
args = {
'domain_name': domain_name.lower()
}
results = self.session.execute(CQL_VERIFY_DOMAIN, args)
if results:
for r in results:
if r.get('service_id') != str(service_id):
LOG.info(
"Domain '{0}' has already been taken."
.format(domain_name))
return True
return False
else:
return False
except ValueError:
return False
else:
return False
def create(self, project_id, service_obj):
"""create.
@ -229,18 +303,18 @@ class ServicesController(base.ServicesController):
:raises ValueError
"""
# check if the service domain names already exist
for d in service_obj.domains:
if self._exists_elsewhere(
d.domain,
service_obj.service_id) is True:
raise ValueError(
"Domain %s has already been taken" % d.domain)
# create the service in storage
service_id = service_obj.service_id
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 ValueError: # this value error means this service doesn't exist
pass
else:
raise ValueError("Service %s already exists..." % service_name)
domains = [json.dumps(d.to_dict())
for d in service_obj.domains]
origins = [json.dumps(o.to_dict())
@ -250,9 +324,10 @@ class ServicesController(base.ServicesController):
restrictions = [json.dumps(r.to_dict())
for r in service_obj.restrictions]
# creates a new service
args = {
# create a new service
service_args = {
'project_id': project_id,
'service_id': uuid.UUID(service_id),
'service_name': service_name,
'flavor_id': service_obj.flavor_id,
'domains': domains,
@ -262,10 +337,32 @@ class ServicesController(base.ServicesController):
'provider_details': {}
}
self.session.execute(CQL_CREATE_SERVICE, args)
LOG.debug("Creating New Service - {0} ({1})".format(service_id,
service_name))
batch = query.BatchStatement()
batch.add(query.SimpleStatement(CQL_CREATE_SERVICE), service_args)
def update(self, project_id, service_name, service_obj):
for d in service_obj.domains:
domain_args = {
'domain_name': d.domain,
'project_id': project_id,
'service_id': uuid.UUID(service_id),
}
batch.add(query.SimpleStatement(CQL_CLAIM_DOMAIN), domain_args)
self.session.execute(batch)
def update(self, project_id, service_id, service_obj):
# check if the service domain names already exist
for d in service_obj.domains:
if self._exists_elsewhere(
d.domain,
service_id) is True:
raise ValueError(
"Domain %s has already been taken" % d)
service_name = service_obj.name
domains = [json.dumps(d.to_dict())
for d in service_obj.domains]
origins = [json.dumps(o.to_dict())
@ -275,9 +372,10 @@ class ServicesController(base.ServicesController):
restrictions = [json.dumps(r.to_dict())
for r in service_obj.restrictions]
# updates an existing new service
# updates an existing service
args = {
'project_id': project_id,
'service_id': uuid.UUID(str(service_id)),
'service_name': service_name,
'flavor_id': service_obj.flavor_id,
'domains': domains,
@ -289,7 +387,7 @@ class ServicesController(base.ServicesController):
self.session.execute(CQL_UPDATE_SERVICE, args)
def delete(self, project_id, service_name):
def delete(self, project_id, service_id):
"""delete.
Archive local configuration storage
@ -297,17 +395,21 @@ class ServicesController(base.ServicesController):
# delete local configuration from storage
args = {
'project_id': project_id,
'service_name': service_name
'service_id': uuid.UUID(str(service_id)),
}
if self._driver.archive_on_delete:
# get the existing service
results = self.session.execute(CQL_GET_SERVICE, args)
result = results[0]
# get the existing service
results = self.session.execute(CQL_GET_SERVICE, args)
result = results[0]
if (result):
if (result):
domains_list = [d.get('domain')
for d in result.get('domains')]
if self._driver.archive_on_delete:
archive_args = {
'project_id': result.get('project_id'),
'service_id': result.get('service_id'),
'service_name': result.get('service_name'),
'flavor_id': result.get('flavor_id'),
'domains': result.get('domains'),
@ -315,25 +417,31 @@ class ServicesController(base.ServicesController):
'caching_rules': result.get('caching_rules'),
'restrictions': result.get('restrictions'),
'provider_details': result.get('provider_details'),
'archived_time': datetime.datetime.utcnow()
'archived_time': datetime.datetime.utcnow(),
'domains_list': domains_list
}
# archive and delete the service
self.session.execute(CQL_ARCHIVE_SERVICE, archive_args)
else:
self.session.execute(CQL_DELETE_SERVICE, args)
else:
delete_args = {
'project_id': result.get('project_id'),
'service_id': result.get('service_id'),
'domains_list': domains_list
}
self.session.execute(CQL_DELETE_SERVICE, delete_args)
def get_provider_details(self, project_id, service_name):
def get_provider_details(self, project_id, service_id):
"""get_provider_details.
:param project_id
:param service_name
:param service_id
:returns results Provider details
"""
args = {
'project_id': project_id,
'service_name': service_name
'service_id': uuid.UUID(str(service_id))
}
# TODO(tonytan4ever): Not sure this returns a list or a single
# dictionary.
@ -361,12 +469,12 @@ class ServicesController(base.ServicesController):
results[provider_name] = provider_detail_obj
return results
def update_provider_details(self, project_id, service_name,
def update_provider_details(self, project_id, service_id,
provider_details):
"""update_provider_details.
:param project_id
:param service_name
:param service_id
:param provider_details
"""
provider_detail_dict = {}
@ -388,7 +496,7 @@ class ServicesController(base.ServicesController):
the_provider_detail_dict)
args = {
'project_id': project_id,
'service_name': service_name,
'service_id': uuid.UUID(str(service_id)),
'provider_details': provider_detail_dict
}
# TODO(tonytan4ever): Not sure this returns a list or a single
@ -405,6 +513,7 @@ class ServicesController(base.ServicesController):
:param result
:returns formatted result
"""
service_id = result.get('service_id')
name = result.get('service_name')
flavor_id = result.get('flavor_id')
@ -446,7 +555,7 @@ class ServicesController(base.ServicesController):
for caching_rule in caching_rules]
# create the service object
s = service.Service(name, domains, origins, flavor_id,
s = service.Service(service_id, name, domains, origins, flavor_id,
caching=caching_rules,
restrictions=restrictions)

View File

@ -14,6 +14,7 @@
# limitations under the License.
import json
import uuid
from poppy.model.helpers import domain
from poppy.model.helpers import origin
@ -27,7 +28,7 @@ class ServicesController(base.ServicesController):
def __init__(self, driver):
super(ServicesController, self).__init__(driver)
self.created_service_names = []
self.created_service_ids = []
@property
def session(self):
@ -50,8 +51,9 @@ class ServicesController(base.ServicesController):
[{'operator_url': 'mockcf123.fastly.prod.com'}]})}
services = []
for i in self.created_service_names:
services = [{'name': i,
for i in self.created_service_ids:
services = [{'service_id': i,
'name': uuid.uuid4(),
'domains': [json.dumps(
{'domain': 'www.mywebsite.com'})
],
@ -84,10 +86,10 @@ class ServicesController(base.ServicesController):
return services_result
def get(self, project_id, service_name):
def get(self, project_id, service_id):
# get the requested service from storage
if service_name not in self.created_service_names:
raise ValueError("service: % does not exist")
if service_id not in self.created_service_ids:
raise ValueError("service {0} does not exist".format(service_id))
else:
origin_json = json.dumps({'origin': 'mywebsite.com',
'port': 80,
@ -111,7 +113,8 @@ class ServicesController(base.ServicesController):
'access_urls':
[{'operator_url': 'mockcf123.fastly.prod.com'}]})}
service_dict = {'name': service_name,
service_dict = {'service_id': service_id,
'name': uuid.uuid4(),
'domains': [domain_json],
'origins': [origin_json],
'flavor_id': 'standard',
@ -136,42 +139,44 @@ class ServicesController(base.ServicesController):
return service_result
def create(self, project_id, service_obj):
if service_obj.name in self.created_service_names:
raise ValueError("Service %s already exists." % service_obj.name)
else:
# TODO(amitgandhinz): append the entire service
# instead of just the name
self.created_service_names.append(service_obj.name)
if service_obj.service_id in self.created_service_ids:
raise ValueError("Service %s already exists." %
service_obj.service_id)
def update(self, project_id, service_name, service_json):
# TODO(amitgandhinz): append the entire service
# instead of just the name
self.created_service_ids.append(service_obj.service_id)
def update(self, project_id, service_id, service_json):
# update configuration in storage
return ''
def delete(self, project_id, service_name):
if (service_name in self.created_service_names):
self.created_service_names.remove(service_name)
def delete(self, project_id, service_id):
if (service_id in self.created_service_ids):
self.created_service_ids.remove(service_id)
def get_provider_details(self, project_id, service_name):
if service_name == 'non_exist_service_name':
raise LookupError('Service non_exist_service_name does not exist')
return {
'MaxCDN': provider_details.ProviderDetail(
provider_service_id=11942,
name='my_service_name',
access_urls=['my_service_name'
'.mycompanyalias.netdna-cdn.com']),
'Fastly': provider_details.ProviderDetail(
provider_service_id=3488,
name="my_service_name",
access_urls=['my_service_name'
'.global.prod.fastly.net']),
'CloudFront': provider_details.ProviderDetail(
provider_service_id=5892,
access_urls=['my_service_name'
'.gibberish.amzcf.com']),
'Mock': provider_details.ProviderDetail(
provider_service_id="73242",
access_urls=['my_service_name.mock.com'])}
def get_provider_details(self, project_id, service_id):
if service_id not in self.created_service_ids:
raise ValueError("service: % does not exist")
else:
return {
'MaxCDN': provider_details.ProviderDetail(
provider_service_id=11942,
name='my_service_name',
access_urls=['my_service_name'
'.mycompanyalias.netdna-cdn.com']),
'Fastly': provider_details.ProviderDetail(
provider_service_id=3488,
name="my_service_name",
access_urls=['my_service_name'
'.global.prod.fastly.net']),
'CloudFront': provider_details.ProviderDetail(
provider_service_id=5892,
access_urls=['my_service_name'
'.gibberish.amzcf.com']),
'Mock': provider_details.ProviderDetail(
provider_service_id="73242",
access_urls=['my_service_name.mock.com'])}
def update_provider_details(self, project_id, service_name,
provider_details):
@ -179,6 +184,7 @@ class ServicesController(base.ServicesController):
@staticmethod
def format_result(result):
service_id = result.get('service_id')
name = result.get('service_name')
origins = [json.loads(o) for o in result.get('origins', [])]
domains = [json.loads(d) for d in result.get('domains', [])]
@ -188,7 +194,7 @@ class ServicesController(base.ServicesController):
for o in origins]
domains = [domain.Domain(d['domain']) for d in domains]
flavor_id = result.get('flavor_id')
s = service.Service(name, domains, origins, flavor_id)
s = service.Service(service_id, name, domains, origins, flavor_id)
provider_detail_results = result.get('provider_details') or {}
provider_details_dict = {}
for provider_name in provider_detail_results:

View File

@ -14,6 +14,7 @@
# limitations under the License.
import json
import uuid
from oslo.config import cfg
import pecan
@ -45,7 +46,12 @@ class ServiceAssetsController(base.Controller, hooks.HookController):
__hooks__ = [poppy_hooks.Context(), poppy_hooks.Error()]
@pecan.expose()
def delete(self, service_name):
@decorators.validate(
service_id=rule.Rule(
helpers.is_valid_service_id(),
helpers.abort_with_message)
)
def delete(self, service_id):
purge_url = pecan.request.GET.get('url', None)
purge_all = pecan.request.GET.get('all', False)
purge_all = (
@ -58,7 +64,7 @@ class ServiceAssetsController(base.Controller, hooks.HookController):
'and a url at the same time')
services_controller = self._driver.manager.services_controller
try:
services_controller.purge(self.project_id, service_name,
services_controller.purge(self.project_id, service_id,
purge_url)
except LookupError as e:
pecan.abort(404, detail=str(e))
@ -85,7 +91,7 @@ class ServicesController(base.Controller, hooks.HookController):
@pecan.expose('json')
def get_all(self):
marker = pecan.request.GET.get('marker', '')
marker = pecan.request.GET.get('marker', None)
limit = pecan.request.GET.get('limit', 10)
try:
limit = int(limit)
@ -100,6 +106,12 @@ class ServicesController(base.Controller, hooks.HookController):
u' or equal to {0}'.format(self.max_services_per_page))
pecan.abort(400, detail=error)
try:
if marker:
marker = str(uuid.UUID(marker))
except ValueError:
pecan.abort(400, detail="Marker must be a valid UUID")
services_controller = self._driver.manager.services_controller
service_resultset = services_controller.list(
self.project_id, marker, limit)
@ -112,7 +124,9 @@ class ServicesController(base.Controller, hooks.HookController):
if len(results) > 0:
links.append(
link.Model(u'{0}/services?marker={1}&limit={2}'.format(
self.base_url, results[-1]['name'], limit),
self.base_url,
results[-1]['id'],
limit),
'next'))
return {
@ -121,14 +135,19 @@ class ServicesController(base.Controller, hooks.HookController):
}
@pecan.expose('json')
def get_one(self, service_name):
@decorators.validate(
service_id=rule.Rule(
helpers.is_valid_service_id(),
helpers.abort_with_message)
)
def get_one(self, service_id):
services_controller = self._driver.manager.services_controller
try:
service_obj = services_controller.get(
self.project_id, service_name)
self.project_id, service_id)
except ValueError:
pecan.abort(404, detail='service %s is not found' %
service_name)
pecan.abort(404, detail='service %s could not be found' %
service_id)
# convert a service model into a response service model
return resp_service_model.Model(service_obj, self)
@ -143,7 +162,7 @@ class ServicesController(base.Controller, hooks.HookController):
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)
service_name = service_json_dict.get("name", None)
service_id = service_obj.service_id
try:
services_controller.create(self.project_id, service_obj)
except LookupError as e: # error handler for no flavor
@ -153,15 +172,20 @@ class ServicesController(base.Controller, hooks.HookController):
service_url = str(
uri.encode(u'{0}/v1.0/services/{1}'.format(
pecan.request.host_url,
service_name)))
service_id)))
return pecan.Response(None, 202, headers={"Location": service_url})
@pecan.expose('json')
def delete(self, service_name):
@decorators.validate(
service_id=rule.Rule(
helpers.is_valid_service_id(),
helpers.abort_with_message)
)
def delete(self, service_id):
services_controller = self._driver.manager.services_controller
try:
services_controller.delete(self.project_id, service_name)
services_controller.delete(self.project_id, service_id)
except LookupError as e:
pecan.abort(404, detail=str(e))
@ -169,15 +193,15 @@ class ServicesController(base.Controller, hooks.HookController):
@pecan.expose('json')
@decorators.validate(
service_name=rule.Rule(
helpers.is_valid_service_name(),
service_id=rule.Rule(
helpers.is_valid_service_id(),
helpers.abort_with_message),
request=rule.Rule(
helpers.json_matches_schema(
service.ServiceSchema.get_schema("service", "PATCH")),
helpers.abort_with_message,
stoplight_helpers.pecan_getter))
def patch_one(self, service_name):
def patch_one(self, service_id):
service_json_dict = json.loads(pecan.request.body.decode('utf-8'))
# TODO(obulpathi): remove these restrictions, once cachingrule and
@ -196,7 +220,9 @@ class ServicesController(base.Controller, hooks.HookController):
try:
services_controller.update(
self.project_id, service_name, service_updates)
self.project_id, service_id, service_updates)
except ValueError as e:
pecan.abort(404, detail='service could not be found')
except errors.ServiceStatusNotDeployed as e:
pecan.abort(400, detail=str(e))
except Exception as e:
@ -205,6 +231,6 @@ class ServicesController(base.Controller, hooks.HookController):
service_url = str(
uri.encode(u'{0}/v1.0/services/{1}'.format(
pecan.request.host_url,
service_name)))
service_id)))
return pecan.Response(None, 202, headers={"Location": service_url})

View File

@ -12,6 +12,7 @@
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import uuid
from poppy.model import service
from poppy.transport.pecan.models.request import cachingrule
@ -22,21 +23,32 @@ from poppy.transport.pecan.models.request import restriction
def load_from_json(json_data):
service_id = uuid.uuid4()
name = json_data.get("name")
origins = json_data.get("origins", [])
domains = json_data.get("domains", [])
flavor_id = json_data.get("flavor_id")
restrictions = json_data.get("restrictions", [])
pd = json_data.get("provider_details", {})
# load caching rules json string from input
caching = json_data.get("caching", [])
origins = [origin.load_from_json(o) for o in origins]
domains = [domain.load_from_json(d) for d in domains]
restrictions = [restriction.load_from_json(r) for r in restrictions]
# convert caching rule jsong string list into object list
# convert caching rule json string list into object list
caching = [cachingrule.load_from_json(c) for c in caching]
r = service.Service(name, domains, origins, flavor_id, caching,
r = service.Service(service_id,
name,
domains,
origins,
flavor_id,
caching,
restrictions)
r.provider_details = dict([(k, provider_details.load_from_json(v))
for k, v in pd.items()])
return r

View File

@ -33,6 +33,7 @@ class Model(collections.OrderedDict):
def __init__(self, service_obj, controller):
super(Model, self).__init__()
self["name"] = service_obj.name
self["id"] = str(service_obj.service_id)
self["domains"] = [domain.Model(d) for d in service_obj.domains]
self["origins"] = [origin.Model(o) for o in service_obj.origins]
self["restrictions"] = [restriction.Model(r) for r in
@ -44,14 +45,12 @@ class Model(collections.OrderedDict):
self["errors"] = []
# TODO(tonytan4ever) : add access_url links.
# This has things to do with provider_detail change. (CDN-172)
self["links"] = [
link.Model(
str(
uri.encode(u'{0}/services/{1}'.format(
controller.base_url,
self['name']))),
service_obj.service_id))),
'self'),
link.Model(
str(

View File

@ -16,6 +16,7 @@
import collections
import functools
import json
import uuid
try:
import falcon
@ -195,8 +196,11 @@ def json_matches_schema(input_schema):
@decorators.validation_function
def is_valid_service_name(service_name):
pass
def is_valid_service_id(service_id):
try:
uuid.UUID(service_id)
except ValueError:
raise exceptions.ValidationFailed('Invalid service id')
@decorators.validation_function

View File

@ -1 +1 @@
pecan==0.8.1
pecan>=0.8.1

View File

@ -88,11 +88,21 @@ class TestFlavorActions(base.TestBase):
super(TestFlavorActions, self).setUp()
if self.test_config.generate_flavors:
self.flavor_id = str(uuid.uuid1())
self.provider_list = [
{
"provider": "fastly",
"links": [
{
"href": "www.fastly.com",
"rel": "provider_url"
}
]
}
]
self.client.create_flavor(
flavor_id=self.flavor_id,
provider_list=[{"provider": "fastly",
"links": [{"href": "www.fastly.com",
"rel": "provider_url"}]}])
provider_list=self.provider_list)
else:
self.flavor_id = self.test_config.default_flavor

View File

@ -1,8 +1,8 @@
{
"all_fields": {
"name": "my_service_name",
"domain_list": [{"domain": "mywebsite.com"},
{"domain": "blog.mywebsite.com"}],
"domain_list": [{"domain": "mywebsite.com", "protocol": "http"},
{"domain": "blog.mywebsite.com", "protocol": "http"}],
"origin_list": [{"origin": "mywebsite1.com",
"port": 443,
"ssl": false}],
@ -14,8 +14,8 @@
},
"caching_empty": {
"name": "caching_empty",
"domain_list": [{"domain": "mywebsite.com"},
{"domain": "blog.mywebsite.com"}],
"domain_list": [{"domain": "mywebsite.com", "protocol": "http"},
{"domain": "blog.mywebsite.com", "protocol": "http"}],
"origin_list": [{"origin": "mywebsite1.com",
"port": 443,
"ssl": false}],
@ -23,8 +23,8 @@
},
"non_ASCII": {
"name": "איבערזעצן",
"domain_list": [{"domain": "сайт.com"},
{"domain": "ਦੀ ਵੈੱਬਸਾਈਟ.com"}],
"domain_list": [{"domain": "сайт.com", "protocol": "http"},
{"domain": "ਦੀ ਵੈੱਬਸਾਈਟ.com", "protocol": "http"}],
"origin_list": [{"origin": "www.இணையதளத்தில்.com",
"port": 443,
"ssl": false}],

View File

@ -16,6 +16,10 @@
# limitations under the License.
import time
try:
import urllib.parse as urlparse
except ImportError:
import urlparse
import uuid
import ddt
@ -33,6 +37,7 @@ class TestCreateService(providers.TestProviderBase):
def setUp(self):
super(TestCreateService, self).setUp()
self.service_url = ''
self.service_name = str(uuid.uuid1())
self.flavor_id = self.test_config.default_flavor
@ -61,8 +66,9 @@ class TestCreateService(providers.TestProviderBase):
flavor_id=flavor_id)
self.assertEqual(resp.status_code, 202)
self.assertEqual(resp.text, '')
self.service_url = resp.headers['location']
resp = self.client.get_service(service_name=self.service_name)
resp = self.client.get_service(location=self.service_url)
self.assertEqual(resp.status_code, 200)
body = resp.json()
@ -124,10 +130,14 @@ class TestCreateService(providers.TestProviderBase):
origin_list=origin_list,
caching_list=caching_list,
flavor_id=flavor_id)
if 'location' in resp.headers:
self.service_url = resp.headers['location']
self.assertEqual(resp.status_code, 400)
def tearDown(self):
self.client.delete_service(service_name=self.service_name)
if self.service_url != '':
self.client.delete_service(location=self.service_url)
if self.test_config.generate_flavors:
self.client.delete_flavor(flavor_id=self.flavor_id)
@ -153,12 +163,14 @@ class TestListServices(base.TestBase):
"rules": [{"name": "index",
"request_url": "/index.htm"}]}]
self.client.create_service(service_name=service_name,
domain_list=self.domain_list,
origin_list=self.origin_list,
caching_list=self.caching_list,
flavor_id=self.flavor_id)
return service_name
resp = self.client.create_service(service_name=service_name,
domain_list=self.domain_list,
origin_list=self.origin_list,
caching_list=self.caching_list,
flavor_id=self.flavor_id)
self.service_url = resp.headers["location"]
return self.service_url
def setUp(self):
super(TestListServices, self).setUp()
@ -222,9 +234,15 @@ class TestListServices(base.TestBase):
@attrib.attr('smoke')
@ddt.data(-1, -10000000000, 0, 10000000, 'invalid', '学校', '')
def test_list_services_various_markers(self, marker):
def test_list_services_various_invalid_markers(self, marker):
url_param = {'marker': marker}
resp = self.client.list_services(param=url_param)
self.assertEqual(resp.status_code, 400)
@attrib.attr('smoke')
def test_list_services_markers(self):
url_param = {'marker': str(uuid.uuid4())}
resp = self.client.list_services(param=url_param)
self.assertEqual(resp.status_code, 200)
@attrib.attr('smoke')
@ -235,7 +253,7 @@ class TestListServices(base.TestBase):
def tearDown(self):
for service in self.service_list:
self.client.delete_service(service_name=service)
self.client.delete_service(location=service)
if self.test_config.generate_flavors:
self.client.delete_flavor(flavor_id=self.flavor_id)
@ -262,7 +280,7 @@ class TestServiceActions(base.TestBase):
domain = str(uuid.uuid1()) + u'.com'
self.domain_list = [
{"domain": domain}
{"domain": domain, "protocol": "http"}
]
origin = str(uuid.uuid1()) + u'.com'
@ -302,25 +320,28 @@ class TestServiceActions(base.TestBase):
}
]
self.client.create_service(service_name=self.service_name,
domain_list=self.domain_list,
origin_list=self.origin_list,
caching_list=self.caching_list,
restrictions_list=self.restrictions_list,
flavor_id=self.flavor_id)
resp = self.client.create_service(
service_name=self.service_name,
domain_list=self.domain_list,
origin_list=self.origin_list,
caching_list=self.caching_list,
restrictions_list=self.restrictions_list,
flavor_id=self.flavor_id)
self.service_url = resp.headers["location"]
def test_delete_service(self):
resp = self.client.delete_service(service_name=self.service_name)
resp = self.client.delete_service(location=self.service_url)
self.assertEqual(resp.status_code, 202)
# As is, the servvice is still available in the DB till deleted from
# As is, the service is still available in the DB till deleted from
# the provider. The test should be able to handle this with
# exponential sleep or whatever(!).
status_code = 0
count = 0
while (count < 5):
service_deleted = self.client.get_service(
service_name=self.service_name)
location=self.service_url)
status_code = service_deleted.status_code
if status_code == 200:
time.sleep(1)
@ -332,7 +353,12 @@ class TestServiceActions(base.TestBase):
self.assertEqual(404, status_code)
def test_delete_non_existing_service(self):
resp = self.client.delete_service(service_name='this_cant_be_true')
parsed_url = urlparse.urlparse(self.service_url)
url = "{0}://{1}{2}{3}".format(parsed_url.scheme,
parsed_url.netloc,
'/v1.0/services/',
uuid.uuid4())
resp = self.client.delete_service(location=url)
self.assertEqual(resp.status_code, 404)
def test_delete_failed_service(self):
@ -343,17 +369,20 @@ class TestServiceActions(base.TestBase):
@ddt.file_data('data_get_service_by_name.json')
def test_get_service_by_name(self, value):
self.client.create_service(service_name=value,
domain_list=self.domain_list,
origin_list=self.origin_list,
caching_list=self.caching_list,
flavor_id=self.flavor_id)
resp = self.client.create_service(service_name=value,
domain_list=self.domain_list,
origin_list=self.origin_list,
caching_list=self.caching_list,
flavor_id=self.flavor_id)
resp = self.client.get_service(service_name=value)
self.assertEqual(resp.status_code, 202)
url = resp.headers["location"]
resp = self.client.get_service(location=url)
self.assertEqual(resp.status_code, 200)
def test_get_service(self):
resp = self.client.get_service(service_name=self.service_name)
resp = self.client.get_service(location=self.service_url)
self.assertEqual(resp.status_code, 200)
body = resp.json()
@ -370,7 +399,12 @@ class TestServiceActions(base.TestBase):
self.assertEqual(body['flavor_id'], self.flavor_id)
def test_get_non_existing_service(self):
resp = self.client.get_service(service_name='this_cant_be_true')
parsed_url = urlparse.urlparse(self.service_url)
url = "{0}://{1}{2}{3}".format(parsed_url.scheme,
parsed_url.netloc,
'/v1.0/services/',
uuid.uuid4())
resp = self.client.get_service(location=url)
self.assertEqual(resp.status_code, 404)
def test_get_failed_service(self):
@ -380,7 +414,7 @@ class TestServiceActions(base.TestBase):
pass
def tearDown(self):
self.client.delete_service(service_name=self.service_name)
self.client.delete_service(location=self.service_url)
if self.test_config.generate_flavors:
self.client.delete_flavor(flavor_id=self.flavor_id)
super(TestServiceActions, self).tearDown()
@ -416,13 +450,16 @@ class TestServicePatch(base.TestBase):
"rules": [{"name": "index",
"request_url": "/index.htm"}]}]
self.client.create_service(service_name=self.service_name,
domain_list=self.domain_list,
origin_list=self.origin_list,
caching_list=self.caching_list,
flavor_id=self.flavor_id)
resp = self.client.create_service(service_name=self.service_name,
domain_list=self.domain_list,
origin_list=self.origin_list,
caching_list=self.caching_list,
flavor_id=self.flavor_id)
self.service_url = resp.headers["location"]
self.client.wait_for_service_status(
service_name=self.service_name,
location=self.service_url,
status='deployed',
retry_interval=self.test_config.status_check_retry_interval,
retry_timeout=self.test_config.status_check_retry_timeout)
@ -431,22 +468,20 @@ class TestServicePatch(base.TestBase):
def test_patch_service(self, test_data):
'''Implemented - PATCH Origins & Domains.'''
resp = self.client.patch_service(service_name=self.service_name,
resp = self.client.patch_service(location=self.service_url,
request_body=test_data)
self.assertEqual(resp.status_code, 202)
location = resp.headers['location']
resp = self.client.get_service(location=location)
resp = self.client.get_service(location=self.service_url)
self.assertEqual(resp.status_code, 200)
self.client.wait_for_service_status(
service_name=self.service_name,
location=self.service_url,
status='deployed',
retry_interval=self.test_config.status_check_retry_interval,
retry_timeout=self.test_config.status_check_retry_timeout)
resp = self.client.get_service(service_name=self.service_name)
resp = self.client.get_service(location=self.service_url)
body = resp.json()
if 'domain_list' in test_data:
@ -467,11 +502,11 @@ class TestServicePatch(base.TestBase):
@ddt.file_data('data_patch_service_negative.json')
def test_patch_service_HTTP_400(self, test_data):
resp = self.client.patch_service(service_name=self.service_name,
resp = self.client.patch_service(location=self.service_url,
request_body=test_data)
self.assertEqual(resp.status_code, 400)
resp = self.client.get_service(service_name=self.service_name)
resp = self.client.get_service(location=self.service_url)
self.assertEqual(resp.status_code, 200)
body = resp.json()
@ -489,7 +524,7 @@ class TestServicePatch(base.TestBase):
# self.assertEqual(sorted(self.caching_list), sorted(body['caching']))
def tearDown(self):
self.client.delete_service(service_name=self.service_name)
self.client.delete_service(location=self.service_url)
if self.test_config.generate_flavors:
self.client.delete_flavor(flavor_id=self.flavor_id)
super(TestServicePatch, self).tearDown()

View File

@ -97,7 +97,7 @@ class PoppyClient(client.AutoMarshallingHTTPClient):
return self.request('POST', url, request_entity=request_object,
requestslib_kwargs=requestslib_kwargs)
def patch_service(self, service_name=None, request_body=None,
def patch_service(self, location, request_body=None,
requestslib_kwargs=None):
"""Updates Service
@ -105,25 +105,19 @@ class PoppyClient(client.AutoMarshallingHTTPClient):
PATCH
services/{service_name}
"""
url = '{0}/services/{1}'.format(self.url, service_name)
request_object = requests.PatchService(request_body=request_body)
return self.request('PATCH', url, request_entity=request_object,
return self.request('PATCH', location, request_entity=request_object,
requestslib_kwargs=requestslib_kwargs)
def get_service(self, location=None, service_name=None):
def get_service(self, location=None):
"""Get Service
:return: Response Object containing response code 200 and body with
details of service
GET
services/{service_name}
services/{service_id}
"""
if location:
url = location
else:
url = '{0}/services/{1}'.format(self.url, service_name)
return self.request('GET', url)
return self.request('GET', location)
def list_services(self, param=None):
"""Get a list of Services
@ -137,16 +131,15 @@ class PoppyClient(client.AutoMarshallingHTTPClient):
url = '{0}/services'.format(self.url)
return self.request('GET', url, params=param)
def delete_service(self, service_name):
def delete_service(self, location):
"""Delete Service
:return: Response Object containing response code 204
DELETE
services/{service_name}
services/{service_id}
"""
url = '{0}/services/{1}'.format(self.url, service_name)
return self.request('DELETE', url)
return self.request('DELETE', location)
def check_health(self):
"""Check Health of the application
@ -217,7 +210,7 @@ class PoppyClient(client.AutoMarshallingHTTPClient):
return self.request('DELETE', url)
def wait_for_service_status(self, service_name, status, retry_interval=2,
def wait_for_service_status(self, location, status, retry_interval=2,
retry_timeout=30):
"""Waits for a service to reach a given status."""
current_status = ''
@ -225,7 +218,7 @@ class PoppyClient(client.AutoMarshallingHTTPClient):
stop_time = start_time + retry_timeout
while current_status != status:
time.sleep(retry_interval)
service = self.get_service(service_name=service_name)
service = self.get_service(location=location)
body = service.json()
current_status = body['status']
if (current_status == status):

View File

@ -50,14 +50,23 @@ links = {'type': 'object',
'flavor']}}
}
error_message = {'type': 'object',
'properties': {
'message': {'type': 'string'}},
'required': ['message'],
'additionalProperties': False}
restrictions = {'type': 'array'}
flavor_id = {'type': 'string', 'pattern': '([a-zA-Z0-9_\-]{1,256})'}
service_name = {'type': 'string', 'pattern': '([a-zA-Z0-9_\-\.]{1,256})'}
uuid4 = '^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$' # noqa
service_id = {'type': 'string', 'pattern': uuid4}
# Response Schema Definition for Get Service API
get_service = {
'type': 'object',
'properties': {
'id': service_id,
'name': service_name,
'domains': {'type': 'array',
'items': domain,
@ -80,7 +89,9 @@ get_service = {
'enum': ['create_in_progress', 'creating',
'delete_in_progress', 'deployed', 'failed']},
'restrictions': restrictions,
'flavor_id': flavor_id
'flavor_id': flavor_id,
'errors': {'type': 'array',
'items': error_message}
},
'required': ['domains', 'origins', 'links', 'flavor_id', 'status',
'errors'],
@ -94,7 +105,7 @@ list_services_link = {
'pattern':
'(https?)(:/{1,3})([a-z0-9\.\-:]{1,400})'
'(/v1\.0/([a-z0-9]{1,400})/services'
'\?marker=)([a-zA-Z0-9_\-\.]{1,256})'
'\?marker=)(' + uuid4 + ')'
'(&limit=)([1-9]|1[0-9])'}},
'required': ['rel', 'href'],
'additionalProperties': False}

View File

@ -14,6 +14,10 @@
# limitations under the License.
import json
try:
import urllib.parse as urlparse
except ImportError:
import urlparse
import uuid
import ddt
@ -65,8 +69,8 @@ class ServiceControllerTest(base.FunctionalTest):
self.assertEqual(201, response.status_code)
# create an initial service to be used by the tests
service_json = {
"name": "mysite.com",
self.service_json = {
"name": self.service_name,
"domains": [
{"domain": "test.mocksite.com"},
{"domain": "blog.mocksite.com"}
@ -98,13 +102,15 @@ class ServiceControllerTest(base.FunctionalTest):
]
}
service_json['name'] = self.service_name
response = self.app.post('/v1.0/services',
params=json.dumps(service_json),
params=json.dumps(self.service_json),
headers={
'Content-Type': 'application/json',
'X-Project-ID': self.project_id})
self.assertEqual(202, response.status_code)
self.assertTrue('Location' in response.headers)
self.service_url = urlparse.urlparse(response.headers["Location"]).path
def tearDown(self):
super(ServiceControllerTest, self).tearDown()
@ -118,10 +124,8 @@ class ServiceControllerTest(base.FunctionalTest):
# self.assertEqual(200, response.status_code)
def test_get_all(self):
response = self.app.get('/v1.0/services', params={
"marker": 2,
"limit": 3
}, headers={'X-Project-ID': self.project_id})
response = self.app.get('/v1.0/services',
headers={'X-Project-ID': self.project_id})
self.assertEqual(200, response.status_code)
@ -134,8 +138,8 @@ class ServiceControllerTest(base.FunctionalTest):
self.limits_conf = self.conf[LIMITS_GROUP]
self.max_services_per_page = self.limits_conf.max_services_per_page
response = self.app.get('/v1.0/0001/services', params={
"marker": 'service_name',
response = self.app.get('/v1.0/services', params={
"marker": uuid.uuid4(),
"limit": self.max_services_per_page + 1
}, expect_errors=True)
@ -143,7 +147,7 @@ class ServiceControllerTest(base.FunctionalTest):
def test_get_one(self):
response = self.app.get(
'/v1.0/services/' + self.service_name,
self.service_url,
headers={'X-Project-ID': self.project_id})
self.assertEqual(200, response.status_code)
@ -153,7 +157,7 @@ class ServiceControllerTest(base.FunctionalTest):
self.assertTrue("origins" in response_dict)
def test_get_one_not_exist(self):
response = self.app.get('/v1.0/services/non_exist_service_name',
response = self.app.get('/v1.0/services/' + str(uuid.uuid4()),
headers={
'Content-Type': 'application/json',
'X-Project-ID': self.project_id},
@ -212,11 +216,11 @@ class ServiceControllerTest(base.FunctionalTest):
'X-Project-ID': self.project_id
},
expect_errors=True)
self.assertEqual(400, response.status_code)
self.assertEqual(202, response.status_code)
def test_update_with_bad_input(self):
# update with erroneous data
response = self.app.patch('/v1.0/services/' + self.service_name,
response = self.app.patch(self.service_url,
params=json.dumps({
"origins": [
{
@ -236,12 +240,12 @@ class ServiceControllerTest(base.FunctionalTest):
def test_update_with_good_input(self):
response = self.app.get(
'/v1.0/services/' + self.service_name,
self.service_url,
headers={'X-Project-ID': self.project_id})
self.assertEqual(200, response.status_code)
# update with good data
response = self.app.patch('/v1.0/services/' + self.service_name,
response = self.app.patch(self.service_url,
params=json.dumps({
"origins": [
{
@ -259,20 +263,22 @@ class ServiceControllerTest(base.FunctionalTest):
def test_patch_non_exist(self):
# This is for coverage 100%
response = self.app.patch("/v1.0",
headers={
'Content-Type': 'application/json',
'X-Project-ID': self.project_id
},
expect_errors=True)
self.assertEqual(404, response.status_code)
response = self.app.patch("/v1.0/",
headers={
'Content-Type': 'application/json',
'X-Project-ID': self.project_id
},
expect_errors=True)
response = self.app.patch(
"/v1.0/services/{0}".format(str(uuid.uuid4())),
headers={
'Content-Type': 'application/json',
'X-Project-ID': self.project_id
},
params=json.dumps({
"origins": [
{
"origin": "44.33.22.11",
"port": 80,
"ssl": False
}
]
}),
expect_errors=True)
self.assertEqual(404, response.status_code)
class FakeController(c_base.Controller):
@ -287,7 +293,7 @@ class ServiceControllerTest(base.FunctionalTest):
def test_delete(self):
response = self.app.delete(
'/v1.0/services/%s' % self.service_name,
self.service_url,
headers={
'Content-Type': 'application/json',
'X-Project-ID': self.project_id
@ -298,7 +304,7 @@ class ServiceControllerTest(base.FunctionalTest):
# TODO(amitgandhinz): commented out as thread model
# is not allowing thread to process with test
# # check if it actually gets deleted
# check if it actually gets deleted
# status_code = 0
# count = 0
# while (count < 5):
@ -322,18 +328,19 @@ class ServiceControllerTest(base.FunctionalTest):
# self.assertEqual(404, status_code)
def test_delete_non_exist(self):
response = self.app.delete('/v1.0/services/non_exist_service_name',
headers={
'Content-Type': 'application/json',
'X-Project-ID': self.project_id
},
expect_errors=True)
response = self.app.delete(
"/v1.0/services/{0}".format(str(uuid.uuid4())),
headers={
'Content-Type': 'application/json',
'X-Project-ID': self.project_id
},
expect_errors=True)
self.assertEqual(404, response.status_code)
def test_purge_non_exist(self):
# This is for coverage 100%
response = self.app.delete(
"/v1.0/services/non_exist_service_name/assets?all=true",
"/v1.0/services/{0}/assets?all=true".format(str(uuid.uuid4())),
headers={
"Content-Type": "application/json",
'X-Project-ID': self.project_id
@ -343,7 +350,7 @@ class ServiceControllerTest(base.FunctionalTest):
def test_purge_error_parms(self):
response = self.app.delete(
'/v1.0/services/fake_service_name_4/assets',
self.service_url + '/assets',
headers={
"Content-Type": "application/json",
'X-Project-ID': self.project_id
@ -353,7 +360,7 @@ class ServiceControllerTest(base.FunctionalTest):
self.assertEqual(400, response.status_code)
response = self.app.delete(
'/v1.0/services/fake_service_name_4/assets?all=true&url=/abc',
self.service_url + '/assets?all=true&url=/abc',
headers={
"Content-Type": "application/json",
'X-Project-ID': self.project_id
@ -364,7 +371,7 @@ class ServiceControllerTest(base.FunctionalTest):
def test_purge_all(self):
response = self.app.delete(
'/v1.0/services/fake_service_name_4/assets?all=true',
self.service_url + '/assets?all=true',
headers={
"Content-Type": "application/json",
'X-Project-ID': self.project_id
@ -375,7 +382,7 @@ class ServiceControllerTest(base.FunctionalTest):
def test_purge_single_url(self):
response = self.app.delete(
'/v1.0/services/fake_service_name_4/assets?url=/abc',
self.service_url + '/assets?url=/abc',
headers={
"Content-Type": "application/json",
'X-Project-ID': self.project_id

View File

@ -30,8 +30,9 @@ class ErrorHookTest(base.FunctionalTest):
self.headers['X-Project-Id'] = '000001'
# use try except to actually catch the response body in
# exception part
service_id = str(uuid.uuid4())
response = self.app.get(
'/v1.0/services/non_exist_service_name',
'/v1.0/services/' + service_id,
headers=self.headers,
status=404)
@ -39,5 +40,5 @@ class ErrorHookTest(base.FunctionalTest):
self.assertEqual('application/json', response.content_type)
response_json = json.loads(response.body.decode('utf-8'))
self.assertTrue('message' in response_json)
self.assertEqual('service non_exist_service_name is not found',
self.assertEqual('service {0} could not be found'.format(service_id),
response_json['message'])

View File

@ -13,6 +13,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import uuid
import ddt
import mock
from oslo.config import cfg
@ -58,8 +60,11 @@ class TestServicesCreate(base.TestCase):
if six.PY2:
responders = [{
'Fastly':
{'error_detail': 'Error in create',
'error': 'failed to create service'}}]
{
'error_detail': 'Error in create',
'error': 'failed to create service'
}
}]
self.controller.create(responders)
def test_create(self):
@ -78,7 +83,7 @@ class TestServicesCreate(base.TestCase):
'rel': 'access_url'
}
]}
}]
}]
self.controller.create(responders)
@ -115,7 +120,8 @@ class TestServicesUpdate(base.TestCase):
'Fastly': fastly_provider_details_old
}
self.service_old = service.Service(name='myservice',
self.service_old = service.Service(service_id=uuid.uuid4(),
name='myservice',
domains=self.domains_old,
origins=self.origins_old,
flavor_id='standard')
@ -133,7 +139,8 @@ class TestServicesUpdate(base.TestCase):
domains_new = [domain.Domain('www.domain1.com'),
domain.Domain('www.domain2.com'),
domain.Domain('www.domain3.com')]
service_updates = service.Service(name='myservice',
service_updates = service.Service(service_id=uuid.uuid4(),
name='myservice',
domains=domains_new,
origins=[],
flavor_id='standard')
@ -142,8 +149,8 @@ class TestServicesUpdate(base.TestCase):
'Fastly': {
'id': u'4PRhL3lHlZhrXr1mJUt24M',
'error': 'Create service failed'
}
}]
}
}]
self.controller.update(self.service_old,
service_updates,

View File

@ -14,6 +14,7 @@
# limitations under the License.
import json
import uuid
import ddt
import mock
@ -41,7 +42,7 @@ class DefaultManagerServiceTests(base.TestCase):
# create mocked config and driver
conf = cfg.ConfigOpts()
# mock a steveodore provider extension
# mock a stevedore provider extension
def get_provider_by_name(name):
name_p_name_mapping = {
'maxcdn': 'MaxCDN',
@ -61,10 +62,11 @@ class DefaultManagerServiceTests(base.TestCase):
# stubbed driver
self.sc = services.DefaultServicesController(manager_driver)
self.project_id = 'mock_id'
self.service_name = 'mock_service'
self.project_id = str(uuid.uuid4())
self.service_name = str(uuid.uuid4())
self.service_id = str(uuid.uuid4())
self.service_json = {
"name": "mock_service",
"name": self.service_name,
"domains": [
{"domain": "www.mywebsite.com"},
{"domain": "blog.mywebsite.com"},
@ -240,7 +242,7 @@ class DefaultManagerServiceTests(base.TestCase):
res = create_service_worker.service_create_worker(
json.dumps(providers_list),
self.project_id,
self.service_name,
self.service_id,
json.dumps(service_obj.to_dict()))
self.assertTrue(res is None)
@ -273,7 +275,7 @@ class DefaultManagerServiceTests(base.TestCase):
service_obj.status = u'deployed'
self.sc.storage_controller.get.return_value = service_obj
service_update_obj = service.load_from_json(update_json)
self.sc.update(self.project_id, self.service_name, service_update_obj)
self.sc.update(self.project_id, self.service_id, service_update_obj)
# ensure the manager calls the storage driver with the appropriate data
self.sc.storage_controller.update.assert_called_once()
@ -301,14 +303,14 @@ class DefaultManagerServiceTests(base.TestCase):
self.provider_details
)
self.sc.delete(self.project_id, self.service_name)
self.sc.delete(self.project_id, self.service_id)
# ensure the manager calls the storage driver with the appropriate data
# break into 2 lines.
sc = self.sc.storage_controller
sc.get_provider_details.assert_called_once_with(
self.project_id,
self.service_name)
self.service_id)
@ddt.file_data('data_provider_details.json')
def test_detele_service_worker_success(self, provider_details_json):
@ -376,10 +378,10 @@ class DefaultManagerServiceTests(base.TestCase):
for k, v in
self.provider_details.items()])),
self.project_id,
self.service_name)
self.service_id)
@ddt.file_data('data_provider_details.json')
def test_detele_service_worker_with_error(self, provider_details_json):
def test_delete_service_worker_with_error(self, provider_details_json):
self.provider_details = {}
for provider_name in provider_details_json:
provider_detail_dict = json.loads(
@ -441,7 +443,7 @@ class DefaultManagerServiceTests(base.TestCase):
for k, v in
self.provider_details.items()])),
self.project_id,
self.service_name)
self.service_id)
@ddt.file_data('data_provider_details.json')
def test_purge(self, provider_details_json):
@ -463,13 +465,13 @@ class DefaultManagerServiceTests(base.TestCase):
self.provider_details
)
self.sc.purge(self.project_id, self.service_name, None)
self.sc.purge(self.project_id, self.service_id, None)
# ensure the manager calls the storage driver with the appropriate data
sc = self.sc.storage_controller
sc.get_provider_details.assert_called_once_with(
self.project_id,
self.service_name,
self.service_id,
)
@ddt.file_data('data_provider_details.json')
@ -508,9 +510,11 @@ class DefaultManagerServiceTests(base.TestCase):
provider_name='MaxCDN',
service_controller=mock.Mock(
purge=mock.Mock(return_value={
'MaxCDN': {'id':
'08d2e326-377e-11e4-b531-3c15c2b8d2d6'
}})
'MaxCDN': {
'id':
'08d2e326-377e-11e4-b531-3c15c2b8d2d6'
}
})
)
))
else:
@ -533,7 +537,7 @@ class DefaultManagerServiceTests(base.TestCase):
for k, v in
self.provider_details.items()])),
self.project_id,
self.service_name,
self.service_id,
str(None))
@ddt.file_data('data_provider_details.json')
@ -587,5 +591,5 @@ class DefaultManagerServiceTests(base.TestCase):
for k, v in
self.provider_details.items()])),
self.project_id,
self.service_name,
self.service_id,
str(None))

View File

@ -32,6 +32,7 @@ class TestServiceModel(base.TestCase):
def setUp(self):
super(TestServiceModel, self).setUp()
self.service_id = str(uuid.uuid4())
self.service_name = uuid.uuid1()
self.flavor_id = "strawberry"
@ -59,10 +60,14 @@ class TestServiceModel(base.TestCase):
def test_create(self):
myservice = service.Service(
self.service_id,
self.service_name, self.mydomains, self.myorigins, self.flavor_id,
self.mycaching, self.myrestrictions)
# test all properties
# id
self.assertEqual(myservice.service_id, self.service_id)
# name
self.assertEqual(myservice.name, self.service_name)
changed_service_name = 'ChangedServiceName'
@ -104,23 +109,40 @@ class TestServiceModel(base.TestCase):
def test_init_from_dict_method(self):
# this should generate a service copy from my service
myservice = service.Service(
self.service_id,
self.service_name, self.mydomains, self.myorigins, self.flavor_id,
self.mycaching, self.myrestrictions)
cloned_service = service.Service.init_from_dict(myservice.to_dict())
self.assertEqual(cloned_service.service_id, myservice.service_id)
self.assertEqual(cloned_service.name, myservice.name)
self.assertEqual(cloned_service.flavor_id, myservice.flavor_id)
self.assertEqual([domain.to_dict() for domain
in cloned_service.domains],
[domain.to_dict() for domain
in myservice.domains])
self.assertEqual([origin.to_dict() for origin
in cloned_service.origins],
[origin.to_dict() for origin
in myservice.origins])
self.assertEqual(cloned_service.flavor_id, myservice.flavor_id)
# (amitgandhinz) need to add caching and restrictions
# self.assertEqual([caching.to_dict() for caching
# in cloned_service.caching],
# [caching.to_dict() for caching
# in myservice.caching])
# self.assertEqual([restrictions.to_dict() for restrictions
# in cloned_service.restrictions],
# [restrictions.to_dict() for restrictions
# in myservice.restrictions])
@ddt.data(u'', u'apple')
def test_set_invalid_status(self, status):
myservice = service.Service(
self.service_id,
self.service_name,
self.mydomains,
self.myorigins,
@ -131,6 +153,7 @@ class TestServiceModel(base.TestCase):
@ddt.data(u'create_in_progress', u'deployed', u'delete_in_progress')
def test_set_valid_status(self, status):
myservice = service.Service(
self.service_id,
self.service_name,
self.mydomains,
self.myorigins,

View File

@ -1,7 +1,8 @@
{
"using_all_fields": [
{
"service_name" : "mocksite",
"service_id": "00000000-0000-0000-0000-000000000000",
"service_name": "mocksite",
"domains": [
"{\"domain\": \"www.mocksite.com\"}",
"{\"domain\": \"blog.mocksite.com\"}"

View File

@ -181,12 +181,14 @@ class CassandraStorageDriverTests(base.TestCase):
def test_is_alive_no_connection(self):
"""No connection test for checking the health of Cassandra."""
self.skipTest('Too slow, need to mock exception')
self.cassandra_driver.session = None
self.assertFalse(self.cassandra_driver.is_alive())
def test_is_alive_with_exception(self):
"""Broken connection test for checking the health of Cassandra."""
self.skipTest('Too slow, need to mock exception')
self.cassandra_driver.session = None
self.cassandra_driver.connect = mock.Mock()
@ -198,6 +200,7 @@ class CassandraStorageDriverTests(base.TestCase):
def test_is_alive(self):
"""Happy path test for checking the health of Cassandra."""
self.skipTest('Too slow, need to mock exception')
self.cassandra_driver.session = None
self.cassandra_driver.connect = mock.Mock()

View File

@ -14,6 +14,7 @@
# limitations under the License.
import json
import uuid
try:
import ordereddict as collections
except ImportError: # pragma: no cover
@ -39,6 +40,7 @@ class CassandraStorageServiceTests(base.TestCase):
# mock arguments to use
self.project_id = '123456'
self.service_id = uuid.uuid4()
self.service_name = 'mocksite'
# create mocked config and driver
@ -61,13 +63,14 @@ class CassandraStorageServiceTests(base.TestCase):
def test_get_service(self, value, mock_session, mock_execute):
# mock the response from cassandra
value[0]['service_id'] = self.service_id
mock_execute.execute.return_value = value
actual_response = self.sc.get(self.project_id, self.service_name)
actual_response = self.sc.get(self.project_id, self.service_id)
# TODO(amitgandhinz): assert the response
# matches the expectation (using jsonschema)
self.assertEqual(actual_response.name, self.service_name)
self.assertEqual(str(actual_response.service_id), str(self.service_id))
@mock.patch.object(services.ServicesController, 'session')
@mock.patch.object(cassandra.cluster.Session, 'execute')
@ -77,12 +80,16 @@ class CassandraStorageServiceTests(base.TestCase):
mock_execute.execute.return_value = []
self.assertRaises(ValueError, self.sc.get,
self.project_id, self.service_name)
self.project_id, self.service_id)
@ddt.file_data('../data/data_create_service.json')
@mock.patch.object(services.ServicesController,
'_exists_elsewhere',
return_value=False)
@mock.patch.object(services.ServicesController, 'session')
@mock.patch.object(cassandra.cluster.Session, 'execute')
def test_create_service(self, value, mock_session, mock_execute):
def test_create_service(self, value,
mock_check, mock_session, mock_execute):
service_obj = req_service.load_from_json(value)
responses = self.sc.create(self.project_id, service_obj)
@ -93,10 +100,13 @@ 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,
'_exists_elsewhere',
return_value=True)
@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})
def test_create_service_exist(self, value,
mock_check, mock_session, mock_execute):
service_obj = req_service.load_from_json(value)
self.sc.get = mock.Mock(return_value=service_obj)
@ -122,26 +132,49 @@ class CassandraStorageServiceTests(base.TestCase):
@mock.patch.object(cassandra.cluster.Session, 'execute')
def test_delete_service(self, mock_session, mock_execute):
# mock the response from cassandra
actual_response = self.sc.delete(self.project_id, self.service_name)
actual_response = self.sc.delete(self.project_id, self.service_id)
# Expect the response to be None as there are no providers passed
# into the driver to respond to this call
self.assertEqual(actual_response, None)
@ddt.file_data('../data/data_update_service.json')
@mock.patch.object(services.ServicesController,
'_exists_elsewhere',
return_value=False)
@mock.patch.object(services.ServicesController, 'session')
@mock.patch.object(cassandra.cluster.Session, 'execute')
def test_update_service(self, service_json, mock_session, mock_execute):
# mock the response from cassandra
def test_update_service(self, service_json,
mock_check, mock_session, mock_execute):
mock_check.return_value = False
service_obj = req_service.load_from_json(service_json)
actual_response = self.sc.update(self.project_id,
self.service_name,
self.service_id,
service_obj)
# Expect the response to be None as there are no providers passed
# into the driver to respond to this call
self.assertEqual(actual_response, None)
@ddt.file_data('../data/data_update_service.json')
@mock.patch.object(services.ServicesController,
'_exists_elsewhere',
return_value=True)
@mock.patch.object(services.ServicesController, 'session')
@mock.patch.object(cassandra.cluster.Session, 'execute')
def test_update_service_duplicate_domain(self, service_json,
mock_check, mock_session,
mock_execute):
# mock the response from cassandra
service_obj = req_service.load_from_json(service_json)
# Expect the response to be a ValueError Exception
self.assertRaises(ValueError,
self.sc.update,
self.project_id,
self.service_id,
service_obj)
@ddt.file_data('data_provider_details.json')
@mock.patch.object(services.ServicesController, 'session')
@mock.patch.object(cassandra.cluster.Session, 'execute')
@ -151,7 +184,7 @@ class CassandraStorageServiceTests(base.TestCase):
mock_execute.execute.return_value = [{'provider_details':
provider_details_json}]
actual_response = self.sc.get_provider_details(self.project_id,
self.service_name)
self.service_id)
self.assertTrue("MaxCDN" in actual_response)
self.assertTrue("Mock" in actual_response)
self.assertTrue("CloudFront" in actual_response)
@ -175,7 +208,7 @@ class CassandraStorageServiceTests(base.TestCase):
self.sc.update_provider_details(
self.project_id,
self.service_name,
self.service_id,
provider_details_dict)
# this is for update_provider_details unittest code coverage
@ -198,7 +231,7 @@ class CassandraStorageServiceTests(base.TestCase):
the_provider_detail_dict)
args = {
'project_id': self.project_id,
'service_name': self.service_name,
'service_id': self.service_id,
'provider_details': arg_provider_details_dict
}

View File

@ -1,5 +1,6 @@
{
"using_all_fields": {
"service_id": "00000000-0000-0000-0000-000000000000",
"name": "mocksite.com",
"domains": [
{"domain": "test.mocksite.com" },