def _get_instance(srv_inst): # get the latest info mani_id = srv_inst.context['manifest_id'] mani = STORE.get_manifest(manifest_id=mani_id) if len(mani) < 1: return 'no manifest found.', 404 # Get the latest info of the instance # could also use STORE.get_service_instance(srv_inst) but will not have all details inst_info = RM.info(instance_id=srv_inst.context['id'], manifest_type=mani[0].manifest_type) if inst_info['srv_inst.state.state'] == 'failed': # try epm.delete(instance_id=instance_id)? return 'There has been a failure in creating the service instance.', 500 srv_inst.state.state = inst_info['srv_inst.state.state'] srv_inst.state.description = inst_info['srv_inst.state.description'] # don't need you any more, buh-bye! del inst_info['srv_inst.state.state'] del inst_info['srv_inst.state.description'] # merge the two context dicts srv_inst.context = {**srv_inst.context, **inst_info} # update the service instance record - there should be an asynch method doing the update - event based STORE.add_service_instance(srv_inst) return srv_inst
def store_manifest(manifest_id, manifest): """ takes deployment description of a software service and associates with a service and plan takes deployment description of a software service and associates with a service and plan that is already registered in the service catalog. :param manifest_id: The manifest_id of a manifest to be associated with a plan of a servicetype. :type manifest_id: str :param manifest: the manifest to store :type manifest: dict | bytes :rtype: ServiceResponse """ ok, message, code = _version_ok() if not ok: return message, code else: if connexion.request.is_json: manifest = Manifest.from_dict(connexion.request.get_json()) else: return "Supplied body content is not or is mal-formed JSON", 400 if manifest.manifest_content.find( '</br>') > 0: # TODO(dizz) remove this check in R4 return "Manifest content contains '</br>'. Please remove these and replace with '\n'", 400 manifest.id = manifest_id result, code = STORE.add_manifest(manifest) if code == 200: return Empty(), code else: return result, code
def test_create_service_instance(self): """ Test case for create_service_instance Provisions a service instance """ self._assert200(self.response) self.assertEquals(len(STORE.get_service_instance()), 1)
def list_manifests(): """ returns a list of manifests registered at with the ESM hi! :rtype: List[Manifest] """ manifests = STORE.get_manifest() return manifests, 200
def delete_service_type(service_id): # noqa: E501 """Deletes a registered service type Deletes a service that is already registered with the service manager. It does not delete the manifest or plan associated with the service type. # noqa: E501 :param service_id: service ID to be deleted :type service_id: str :rtype: Empty """ srv = STORE.get_service(service_id) # expectation: if not found, srv is None if srv: STORE.delete_service(service_id) return Empty() else: return "Service type not found", 404
def health_check(): db_engines_ok = STORE.is_ok() # calls specific DB check resource_engine_ok = RM.is_ok() # calls specific RM check this_ok = (1 + 1 == 2) # basic local check # maintenance = False if this_ok and db_engines_ok and resource_engine_ok: return {'status', 'up'} # elif not maintenance: # return {'status', 'out_of_service'} else: return {'status', 'down'}
def register_service(service): """ Registers the service with the catalog. \"Service providers need a means to register their service with a service broker. This provides this functionality. Also using PUT a service provider can update their registration. Note that this requires the complete content and will REPLACE the existing service information registered with the broker.\" :param service: the service description to register :type service: dict | bytes :rtype: Empty """ ok, message, code = _version_ok() if not ok: return message, code else: if connexion.request.is_json: service = ServiceType.from_dict(connexion.request.get_json()) else: return "Supplied body content is not or is mal-formed JSON", 400 STORE.add_service(service=service) return Empty()
def deprovision_service_instance(instance_id, service_id, plan_id, accept_incomplete=None): """ Deprovisions a service instance. 'When a broker receives a deprovision request from a client, it should delete any resources it created during the provision. Usually this means that all resources are immediately reclaimed for future provisions.' :param instance_id: 'The instance_id of a service instance is provided by the client. This ID will be used for future requests (bind and deprovision), so the broker must use it to correlate the resource it creates.' :type instance_id: str :param service_id: service ID to be deprovisioned :type service_id: str :param plan_id: plan ID of the service to be deprovisioned :type plan_id: str :param accept_incomplete: Indicates that the client is supporting asynchronous operations :type accept_incomplete: bool :rtype: UpdateOperationResponse """ ok, message, code = _version_ok() if not ok: return message, code else: # XXX if there's bindings remove first? # XXX what about undo? # check that the instance exists first instance = STORE.get_service_instance(instance_id=instance_id) if len(instance) == 1: mani_id = instance[0].context['manifest_id'] mani = STORE.get_manifest(manifest_id=mani_id) if len(mani) < 1: return 'no service manifest found.', 404 RM.delete(instance_id=instance_id, manifest_type=mani[0].manifest_type) STORE.delete_service_instance(instance_id) # we don't delete the last_operation explicitly as its embedded in the service_instance document # STORE.delete_last_operation(instance_id) return Empty(), 200 else: return Empty(), 404
def get_manifest(manifest_id): """ returns a specific of manifest registered at with the ESM hi! :param manifest_id: The manifest_id of a manifest to be associated with a plan of a servicetype. :type manifest_id: str :rtype: Manifest """ manifest = STORE.get_manifest(manifest_id) if len(manifest) > 0: return manifest, 200 else: return 'Manifest {id} could not be found'.format(id=manifest_id), 404
def all_instance_info(): """ Returns information about the service instance. Returns all service instances that are accessible to the end-user on this service manager. :rtype: List[ServiceInstance] """ ok, message, code = _version_ok() if not ok: return message, code else: instances = STORE.get_service_instance() insts = list() for inst in instances: insts.append(_get_instance(inst)) return insts, 200
def catalog(): """ Gets services registered within the broker \"The first endpoint that a broker must implement is the service catalog. The client will initially fetch this endpoint from all brokers and make adjustments to the user-facing service catalog stored in the a client database. \\n\" :rtype: Catalog """ # get all services from the service collection ok, message, code = _version_ok() if not ok: return message, code else: services = STORE.get_service() return Catalog(services=services), 200
def instance_info(instance_id): """ Returns information about the service instance. Returns information about the service instance. This is a simple read operation against the broker database and is provided as a developer/consumer convienence. :param instance_id: 'The instance_id of a service instance is provided by the client. This ID will be used for future requests (bind and deprovision), so the broker must use it to correlate the resource it creates.' :type instance_id: str :rtype: ServiceInstance """ ok, message, code = _version_ok() if not ok: return message, code else: # service instance should already be recorded srv_inst = STORE.get_service_instance(instance_id) if len(srv_inst) < 1: return 'no service instance found.', 404 srv_inst = srv_inst[0] srv_inst = _get_instance(srv_inst) return srv_inst, 200
def setUp(self): super().setUp() sql_host = os.environ.get('ESM_SQL_HOST', os.environ.get('ET_EDM_MYSQL_HOST', '')) if sql_host: STORE.set_up() self.store = STORE if len(self.store.get_service_instance()) > 0: raise Exception( 'This shouldnt happen - the store should be empty on each run!' ) self.instance_id = 'this_is_a_test_instance' self.binding_id = 'this_is_a_test_instance_binding' self.test_plan = Plan(id='testplan', name='testing plan', description='plan for testing', metadata=None, free=True, bindable=False) self.test_service = ServiceType(id='test-svc', name='test_svc', description='this is a test service', bindable=True, tags=['test', 'tester'], metadata=None, requires=[], plan_updateable=False, plans=[self.test_plan], dashboard_client=None) self.store.add_service(self.test_service) print('Service registration content of:\n {content}'.format( content=json.dumps(self.test_service))) path = os.path.dirname( os.path.abspath(inspect.getfile(inspect.currentframe()))) with open(path + MANIFEST, "r") as mani_file: mani = mani_file.read() with open(path + '/manifests/test_endpoints.json', 'r') as ep_file: ep = ep_file.read() if os.getenv('DOCKER_TESTS', 'NO') == 'YES': m_type = 'docker-compose' else: m_type = 'dummy' self.test_manifest = Manifest(id='test-mani', plan_id=self.test_plan.id, service_id=self.test_service.id, manifest_type=m_type, manifest_content=mani, endpoints=json.loads(ep)) self.store.add_manifest(self.test_manifest) print('Manifest registration content of:\n {content}'.format( content=json.dumps(self.test_manifest))) self.response = self._send_service_request() self._assert200(self.response)
def create_service_instance(instance_id, service, accept_incomplete=None): """ Provisions a service instance When the broker receives a provision request from a client, it should synchronously take whatever action is necessary to create a new service resource for the developer. The result of provisioning varies by service type, although there are a few common actions that work for many services. Supports asynchronous operations.' :param instance_id: 'The instance_id of a service instance is provided by the client. This ID will be used for future requests (bind and deprovision), so the broker must use it to correlate the resource it creates.' :type instance_id: str :param service: Service information. :type service: dict | bytes :param accept_incomplete: Indicates that the client is supporting asynchronous operations :type accept_incomplete: bool :rtype: ServiceResponse """ ok, message, code = _version_ok() if not ok: return message, code else: if len(STORE.get_service_instance(instance_id=instance_id)) == 1: return 'Service instance with id {id} already exists'.format(id=instance_id), 409 if connexion.request.is_json: service = ServiceRequest.from_dict(connexion.request.get_json()) else: return "Supplied body content is not or is mal-formed JSON", 400 # look up manifest based on plan id # based on the manifest type, select the driver # send the manifest for creation to the target system # store the ID along with refs to service, plan and manifest # get the manifest for the service/plan svc_type = STORE.get_service(service.service_id)[0] if svc_type is None: return 'Unrecognised service requested to be instantiated', 404 plans = svc_type.plans plan = [p for p in plans if p.id == service.plan_id] if len(plan) <= 0: return 'Plan {p_id} found.'.format(p_id=service.plan_id), 404 mani = STORE.get_manifest(plan_id=plan[0].id) if len(mani) <= 0: return 'no manifest for service {plan} found.'.format(plan=service.plan_id), 404 mani = mani[0] if accept_incomplete: # given docker-compose runs in detached mode this is not needed - only timing can verify # XXX put this in a thread to allow for asynch processing? RM.create(instance_id=instance_id, content=mani.manifest_content, c_type=mani.manifest_type, parameters=service.parameters) else: RM.create(instance_id=instance_id, content=mani.manifest_content, c_type=mani.manifest_type, parameters=service.parameters) last_op = LastOperation( # stored within the service instance doc state='creating', description='service instance is being created' ) # store the instance Id with manifest id srv_inst = ServiceInstance( service_type=svc_type, state=last_op, context={ 'id': instance_id, 'manifest_id': mani.id, } ) STORE.add_service_instance(srv_inst) if accept_incomplete: STORE.add_last_operation(instance_id=instance_id, last_operation=last_op) return 'created', 200