def deprovision(instance_id): try: plan_id = request.args["plan_id"] service_id = request.args["service_id"] accepts_incomplete = 'true' == request.args.get("accepts_incomplete", 'false') deprovision_details = DeprovisionDetails(service_id=service_id, plan_id=plan_id) deprovision_details.originating_identity = request.originating_identity deprovision_details.authorization_username = extract_authorization_username(request) if not _check_plan_id(service_broker, deprovision_details.plan_id): raise TypeError('plan_id not found in this service.') except (TypeError, KeyError) as e: logger.exception(e) return to_json_response(ErrorResponse(description=str(e))), HTTPStatus.BAD_REQUEST try: result = service_broker.deprovision(instance_id, deprovision_details, accepts_incomplete) except errors.ErrInstanceDoesNotExist as e: logger.exception(e) return to_json_response(EmptyResponse()), HTTPStatus.GONE except errors.ErrAsyncRequired as e: logger.exception(e) return to_json_response(ErrorResponse( error="AsyncRequired", description="This service plan requires client support for asynchronous service operations." )), HTTPStatus.UNPROCESSABLE_ENTITY if result.is_async: return to_json_response(DeprovisionResponse(result.operation)), HTTPStatus.ACCEPTED else: return to_json_response(EmptyResponse()), HTTPStatus.OK
def update(instance_id): try: accepts_incomplete = 'true' == request.args.get("accepts_incomplete", 'false') update_details = UpdateDetails(**json.loads(request.data)) update_details.originating_identity = request.originating_identity update_details.authorization_username = extract_authorization_username(request) plan_id = update_details.plan_id if plan_id and not _check_plan_id(service_broker, plan_id): raise TypeError('plan_id not found in this service.') except (TypeError, KeyError, JSONDecodeError) as e: logger.exception(e) return to_json_response(ErrorResponse(description=str(e))), HTTPStatus.BAD_REQUEST try: result = service_broker.update(instance_id, update_details, accepts_incomplete) except errors.ErrInvalidParameters as e: return to_json_response(ErrorResponse('InvalidParameters', str(e))), HTTPStatus.BAD_REQUEST except errors.ErrAsyncRequired as e: logger.exception(e) return to_json_response(ErrorResponse( error="AsyncRequired", description="This service plan requires client support for asynchronous service operations." )), HTTPStatus.UNPROCESSABLE_ENTITY if result.is_async: return to_json_response(UpdateResponse(result.operation, result.dashboard_url)), HTTPStatus.ACCEPTED else: return to_json_response(UpdateResponse(None, result.dashboard_url)), HTTPStatus.OK
def update(instance_id): try: accepts_incomplete = 'true' == request.args.get( "accepts_incomplete", 'false') update_details = UpdateDetails(**json.loads(request.data)) update_details.originating_identity = request.originating_identity update_details.authorization_username = request.authorization.username broker = get_broker_by_id(update_details.service_id) if not broker.check_plan_id(update_details.plan_id): raise TypeError('plan_id not found in this service.') except (TypeError, KeyError, JSONDecodeError) as e: logger.exception(e) return to_json_response( ErrorResponse(description=str(e))), HTTPStatus.BAD_REQUEST try: result = broker.update(instance_id, update_details, accepts_incomplete) add_service_id_to_async_response(result, broker.service_id()) except errors.ErrAsyncRequired as e: logger.exception(e) return to_json_response( ErrorResponse( error="AsyncRequired", description= "This service plan requires client support for asynchronous service operations." )), HTTPStatus.UNPROCESSABLE_ENTITY if result.is_async: return to_json_response(UpdateResponse( result.operation)), HTTPStatus.ACCEPTED else: return to_json_response(EmptyResponse()), HTTPStatus.OK
def unbind(instance_id, binding_id): try: accepts_incomplete = 'true' == request.args.get("accepts_incomplete", 'false') plan_id = request.args["plan_id"] service_id = request.args["service_id"] unbind_details = UnbindDetails(service_id=service_id, plan_id=plan_id) unbind_details.originating_identity = request.originating_identity unbind_details.authorization_username = extract_authorization_username(request) if not _check_plan_id(service_broker, unbind_details.plan_id): raise TypeError('plan_id not found in this service.') except (TypeError, KeyError) as e: logger.exception(e) return to_json_response(ErrorResponse(description=str(e))), HTTPStatus.BAD_REQUEST try: result = service_broker.unbind(instance_id, binding_id, unbind_details, accepts_incomplete) except errors.ErrBindingDoesNotExist as e: logger.exception(e) return to_json_response(EmptyResponse()), HTTPStatus.GONE if result.is_async: return to_json_response(UnbindResponse(result.operation)), HTTPStatus.ACCEPTED else: return to_json_response(EmptyResponse()), HTTPStatus.OK
def last_operation(instance_id): # TODO: forward them # service_id = request.args.get("service_id", None) # plan_id = request.args.get("plan_id", None) operation_data = request.args.get("operation", None) try: result = service_broker.last_operation(instance_id, operation_data) return to_json_response(LastOperationResponse(result.state, result.description)), HTTPStatus.OK except errors.ErrInstanceDoesNotExist: return to_json_response(LastOperationResponse(OperationState.SUCCEEDED, '')), HTTPStatus.GONE
def check_version(): from flask import request version = request.headers.get("X-Broker-Api-Version", None) if not version: return to_json_response( ErrorResponse(description="No X-Broker-Api-Version found.") ), HTTPStatus.BAD_REQUEST if MIN_VERSION > version_tuple(version): return to_json_response( ErrorResponse( description="Service broker requires version %d.%d+." % MIN_VERSION)), HTTPStatus.PRECONDITION_FAILED
def get_binding(instance_id, binding_id): try: result = service_broker.get_binding(instance_id, binding_id) response = GetBindingResponse( credentials=result.credentials, syslog_drain_url=result.syslog_drain_url, route_service_url=result.route_service_url, volume_mounts=result.volume_mounts, parameters=result.parameters, ) return to_json_response(response), HTTPStatus.OK except errors.ErrBindingDoesNotExist: return to_json_response(EmptyResponse()), HTTPStatus.NOT_FOUND
def handle_base_exception(e): logger.exception(e) if config.TESTING: response = ErrorResponse(description=str(e)) else: response = ErrorResponse(description="Unhandled error during request") return (to_json_response(response), HTTPStatus.INTERNAL_SERVER_ERROR)
def last_binding_operation(instance_id, binding_id): # TODO: forward them # service_id = request.args.get("service_id", None) # plan_id = request.args.get("plan_id", None) operation_data = request.args.get("operation", None) result = service_broker.last_binding_operation(instance_id, binding_id, operation_data) return to_json_response(LastOperationResponse(result.state, result.description)), HTTPStatus.OK
def get_instance(instance_id): try: result = service_broker.get_instance(instance_id) response = GetInstanceResponse( service_id=result.service_id, plan_id=result.plan_id, dashboard_url=result.dashboard_url, parameters=result.parameters, ) return to_json_response(response), HTTPStatus.OK except errors.ErrInstanceDoesNotExist: return to_json_response(EmptyResponse()), HTTPStatus.NOT_FOUND except errors.ErrConcurrentInstanceAccess: error_response = ErrorResponse(error='ConcurrencyError', description='The Service Broker does not support concurrent requests that mutate the same resource.') return to_json_response(error_response), HTTPStatus.UNPROCESSABLE_ENTITY
def wrapped(*args, **kwargs): from flask import request if request.get_json(silent=True) is None: er = ErrorResponse( description= 'Improper Content-Type header. Expecting "application/json"') return to_json_response(er), HTTPStatus.BAD_REQUEST else: return f(*args, **kwargs)
def unbind(instance_id, binding_id): try: plan_id = request.args["plan_id"] service_id = request.args["service_id"] unbind_details = UnbindDetails(plan_id, service_id) unbind_details.originating_identity = request.originating_identity unbind_details.authorization_username = request.authorization.username broker = get_broker_by_id(unbind_details.service_id) if not broker.check_plan_id(unbind_details.plan_id): raise TypeError('plan_id not found in this service.') except (TypeError, KeyError) as e: logger.exception(e) return to_json_response( ErrorResponse(description=str(e))), HTTPStatus.BAD_REQUEST try: broker.unbind(instance_id, binding_id, unbind_details) except errors.ErrBindingDoesNotExist as e: logger.exception(e) return to_json_response(EmptyResponse()), HTTPStatus.GONE return to_json_response(EmptyResponse()), HTTPStatus.OK
def provision(instance_id): try: accepts_incomplete = 'true' == request.args.get( "accepts_incomplete", 'false') if not request.is_json: er = ErrorResponse( description= 'Improper Content-Type header. Expecting "application/json"' ) return to_json_response(er), HTTPStatus.BAD_REQUEST provision_details = ProvisionDetails(**json.loads(request.data)) provision_details.originating_identity = request.originating_identity provision_details.authorization_username = request.authorization.username broker = get_broker_by_id(provision_details.service_id) if not broker.check_plan_id(provision_details.plan_id): raise TypeError('plan_id not found in this service.') except (TypeError, KeyError) as e: logger.exception(e) return to_json_response( ErrorResponse(description=str(e))), HTTPStatus.BAD_REQUEST try: result = broker.provision(instance_id, provision_details, accepts_incomplete) add_service_id_to_async_response(result, broker.service_id()) except errors.ErrInstanceAlreadyExists as e: logger.exception(e) return to_json_response(EmptyResponse()), HTTPStatus.CONFLICT except errors.ErrAsyncRequired as e: logger.exception(e) return to_json_response( ErrorResponse( error="AsyncRequired", description= "This service plan requires client support for asynchronous service operations." )), HTTPStatus.UNPROCESSABLE_ENTITY if result.state == ProvisionState.IS_ASYNC: return to_json_response( ProvisioningResponse(result.dashboard_url, result.operation)), HTTPStatus.ACCEPTED elif result.state == ProvisionState.IDENTICAL_ALREADY_EXISTS: return to_json_response( ProvisioningResponse(result.dashboard_url, result.operation)), HTTPStatus.OK elif result.state == ProvisionState.SUCCESSFUL_CREATED: return to_json_response( ProvisioningResponse(result.dashboard_url, result.operation)), HTTPStatus.CREATED else: raise errors.ServiceException( 'IllegalState, ProvisioningState unknown.')
def bind(instance_id, binding_id): try: binding_details = BindDetails(**json.loads(request.data)) binding_details.originating_identity = request.originating_identity binding_details.authorization_username = extract_authorization_username( request) broker = get_broker_by_id(binding_details.service_id) if not broker.check_plan_id(binding_details.plan_id): raise TypeError('plan_id not found in this service.') except (TypeError, KeyError, JSONDecodeError) as e: logger.exception(e) return to_json_response( ErrorResponse(description=str(e))), HTTPStatus.BAD_REQUEST try: result = broker.bind(instance_id, binding_id, binding_details) except errors.ErrBindingAlreadyExists as e: logger.exception(e) return to_json_response(EmptyResponse()), HTTPStatus.CONFLICT except errors.ErrAppGuidNotProvided as e: logger.exception(e) return to_json_response( ErrorResponse( error="RequiresApp", description= "This service supports generation of credentials through binding an application only." )), HTTPStatus.UNPROCESSABLE_ENTITY response = BindResponse(credentials=result.credentials, syslog_drain_url=result.syslog_drain_url, route_service_url=result.route_service_url, volume_mounts=result.volume_mounts) if result.state == BindState.SUCCESSFUL_BOUND: return to_json_response(response), HTTPStatus.CREATED elif result.state == BindState.IDENTICAL_ALREADY_EXISTS: return to_json_response(response), HTTPStatus.OK else: raise errors.ServiceException('IllegalState, BindState unknown.')
def last_operation(instance_id): # Not required # service_id = request.args.get("service_id", None) # plan_id = request.args.get("plan_id", None) operation_data = request.args.get("operation", None) data = operation_data.split(' ', maxsplit=1) service_id = data[0] if len(data) == 2: operation_data = data[1] else: operation_data = None try: broker = get_broker_by_id(service_id) except KeyError as e: logger.exception(e) return to_json_response( ErrorResponse(description=str(e))), HTTPStatus.BAD_REQUEST result = broker.last_operation(instance_id, operation_data) return to_json_response( LastOperationResponse(result.state, result.description)), HTTPStatus.OK
def requires_auth(): """Check authentication over all provided usernames else sends a 401 response that enables basic auth""" from flask import request auth = request.authorization if auth: for credentials in broker_credentials: if auth.username == credentials.username and auth.password == credentials.password: return return to_json_response( ErrorResponse( description= 'Could not verify your access level for that URL.\nYou have to login with proper credentials' )), HTTPStatus.UNAUTHORIZED, { 'WWW-Authenticate': 'Basic realm="Login Required"' }
def provision(instance_id): try: accepts_incomplete = 'true' == request.args.get( "accepts_incomplete", 'false') provision_details = ProvisionDetails(**json.loads(request.data)) provision_details.originating_identity = request.originating_identity provision_details.authorization_username = extract_authorization_username( request) if not _check_plan_id(service_broker, provision_details.plan_id): raise TypeError('plan_id not found in this service.') except (TypeError, KeyError, JSONDecodeError) as e: logger.exception(e) return to_json_response( ErrorResponse(description=str(e))), HTTPStatus.BAD_REQUEST try: result = service_broker.provision(instance_id, provision_details, accepts_incomplete) except errors.ErrInstanceAlreadyExists as e: logger.exception(e) return to_json_response(EmptyResponse()), HTTPStatus.CONFLICT except errors.ErrInvalidParameters as e: return to_json_response(ErrorResponse( 'InvalidParameters', str(e))), HTTPStatus.BAD_REQUEST except errors.ErrAsyncRequired as e: logger.exception(e) return to_json_response( ErrorResponse( error="AsyncRequired", description= "This service plan requires client support for asynchronous service operations." )), HTTPStatus.UNPROCESSABLE_ENTITY if result.state == ProvisionState.IS_ASYNC: return to_json_response( ProvisioningResponse(result.dashboard_url, result.operation)), HTTPStatus.ACCEPTED elif result.state == ProvisionState.IDENTICAL_ALREADY_EXISTS: return to_json_response( ProvisioningResponse(result.dashboard_url, result.operation)), HTTPStatus.OK elif result.state == ProvisionState.SUCCESSFUL_CREATED: return to_json_response( ProvisioningResponse(result.dashboard_url, result.operation)), HTTPStatus.CREATED else: raise errors.ServiceException( 'IllegalState, ProvisioningState unknown.')
def last_operation(instance_id): # Not required # service_id = request.args.get("service_id", None) # plan_id = request.args.get("plan_id", None) operation_data = request.args.get("operation", None) data = operation_data.split(' ', maxsplit=1) service_id = data[0] if len(data) == 2: operation_data = data[1] else: operation_data = None broker = get_broker_by_id(service_id) result = broker.last_operation(instance_id, operation_data) return to_json_response( LastOperationResponse(result.state, result.description)), HTTPStatus.OK
def check_originating_identity(): """ Check and decode the "X-Broker-API-Originating-Identity" header https://github.com/openservicebrokerapi/servicebroker/blob/v2.13/spec.md#originating-identity """ from flask import request, json if "X-Broker-API-Originating-Identity" in request.headers: try: platform, value = request.headers[ "X-Broker-API-Originating-Identity"].split(None, 1) request.originating_identity = { 'platform': platform, 'value': json.loads(base64.standard_b64decode(value)) } except ValueError as e: return to_json_response( ErrorResponse( description= 'Improper "X-Broker-API-Originating-Identity" header. ' + str(e))), HTTPStatus.BAD_REQUEST else: request.originating_identity = None
def error_handler(e): logger.exception(e) return to_json_response(ErrorResponse( description=constants.DEFAULT_EXCEPTION_ERROR_MESSAGE )), HTTPStatus.INTERNAL_SERVER_ERROR
def handle_not_implemented(e): logger.exception(e) return ( to_json_response(ErrorResponse(description="Not Implemented")), HTTPStatus.NOT_IMPLEMENTED, )
def error_handler_bad_request(e): logger.exception(e) return to_json_response( ErrorResponse(description=str(e))), HTTPStatus.BAD_REQUEST
def error_handler_not_implemented(e): logger.exception(e) return to_json_response( ErrorResponse(description=str(e))), HTTPStatus.NOT_IMPLEMENTED
def error_handler(e): logger.exception(e) return to_json_response(ErrorResponse( description=str(e))), HTTPStatus.INTERNAL_SERVER_ERROR
def error_handler_bad_request(e): logger.exception(e) return to_json_response(ErrorResponse( description=constants.DEFAULT_BAD_REQUEST_ERROR_MESSAGE )), HTTPStatus.BAD_REQUEST
def catalog(): """ :return: Catalog of broker (List of services) """ catalog = ensure_list(service_broker.catalog()) return to_json_response(CatalogResponse(list(catalog)))
def catalog(): """ :return: Catalog of broker (List of services) """ return to_json_response( CatalogResponse(list(s.catalog() for s in service_brokers)))
def error_handler_not_implemented(e): logger.exception(e) return to_json_response(ErrorResponse( description=constants.DEFAULT_NOT_IMPLEMENTED_ERROR_MESSAGE )), HTTPStatus.NOT_IMPLEMENTED