def test_returns_202_if_update_is_in_progress(self): self.broker.update.return_value = UpdateServiceSpec(True, "operation") response = self.client.patch( "/v2/service_instances/abc?accepts_incomplete=true", data=json.dumps({ "service_id": "service-guid-here", "plan_id": "plan-guid-here", "previous_values": { "plan_id": "old-plan-guid-here", "service_id": "service-guid-here", "organization_id": "org-guid-here", "space_id": "space-guid-here" } }), headers={ 'X-Broker-Api-Version': '2.13', 'Content-Type': 'application/json', 'Authorization': self.auth_header }) self.assertEqual(response.status_code, http.HTTPStatus.ACCEPTED) self.assertEqual(response.json, dict( operation="operation" ))
def test_routes_update(self): operation_str = str(uuid4()) self.b1.update.return_value = UpdateServiceSpec(is_async=True, operation=operation_str) update = self.router.update(str(uuid4()), UpdateDetails('s1', 'p1'), True) self.assertEqual('s1 ' + operation_str, update.operation) self.assertTrue(self.b1.update.called)
def test_update_can_change_dashboard_url(self): new_dashboard_url = 'https://new.dashboard/' self.broker.update.return_value = UpdateServiceSpec( is_async=False, operation=None, dashboard_url=new_dashboard_url) response = self.client.patch( "/v2/service_instances/here-service-instance-id", data=json.dumps({ "service_id": "service-guid-here", }), headers={ 'X-Broker-Api-Version': '2.13', 'Content-Type': 'application/json', 'Authorization': self.auth_header }) self.assertEqual(dict(dashboard_url=new_dashboard_url), response.json)
def test_update_ignores_unknown_parameters(self): self.broker.update.return_value = UpdateServiceSpec(False, "operation") self.client.patch( "/v2/service_instances/here-service-instance-id?accepts_incomplete=true", data=json.dumps({ "service_id": "service-guid-here", "plan_id": "plan-guid-here", "unknown": "unknown", "parameters": { "parameter1": 1 }, "previous_values": { "unknown": "unknown", "plan_id": "old-plan-guid-here", "service_id": "service-guid-here", "organization_id": "org-guid-here", "space_id": "space-guid-here" } }), headers={ 'X-Broker-Api-Version': '2.13', 'Content-Type': 'application/json', 'Authorization': self.auth_header }) actual_service_id, actual_details, actual_async = self.broker.update.call_args[ 0] self.assertEqual(actual_service_id, "here-service-instance-id") self.assertEqual(actual_async, True) self.assertIsInstance(actual_details, UpdateDetails) self.assertIsInstance(actual_details.previous_values, PreviousValues) self.assertEqual(actual_details.service_id, "service-guid-here") self.assertEqual(actual_details.plan_id, "plan-guid-here") self.assertEqual(actual_details.parameters, dict(parameter1=1)) self.assertEqual(actual_details.previous_values.plan_id, "old-plan-guid-here") self.assertEqual(actual_details.previous_values.service_id, "service-guid-here") self.assertEqual(actual_details.previous_values.organization_id, "org-guid-here") self.assertEqual(actual_details.previous_values.space_id, "space-guid-here")
def test_returns_200_if_updated(self): self.broker.update.return_value = UpdateServiceSpec(False, "operation") response = self.client.patch( "/v2/service_instances/abc", data=json.dumps({ "service_id": "service-guid-here", "plan_id": "plan-guid-here", "previous_values": { "plan_id": "old-plan-guid-here", "service_id": "service-guid-here", "organization_id": "org-guid-here", "space_id": "space-guid-here" } }), headers={ 'X-Broker-Api-Version': '2.13', 'Authorization': self.auth_header }) self.assertEqual(response.status_code, http.HTTPStatus.OK) self.assertEqual(response.json, dict())
def test_update_called_called_just_with_required_fields(self): self.broker.update.return_value = UpdateServiceSpec(False, "operation") _ = self.client.patch( "/v2/service_instances/here-service-instance-id", data=json.dumps({ "service_id": "service-guid-here", }), headers={ 'X-Broker-Api-Version': '2.13', 'Authorization': self.auth_header }) actual_instance_id, actual_details, actual_async = self.broker.update.call_args[0] self.assertEqual(actual_instance_id, "here-service-instance-id") self.assertEqual(actual_async, False) self.assertIsInstance(actual_details, UpdateDetails) self.assertEqual(actual_details.service_id, "service-guid-here") self.assertIsNone(actual_details.plan_id) self.assertIsNone(actual_details.parameters) self.assertIsNone(actual_details.previous_values)
def update(self, instance_id: str, details: UpdateDetails, async_allowed: bool, **kwargs) -> UpdateServiceSpec: if not async_allowed: raise errors.ErrAsyncRequired() params = details.parameters or {} instance = ServiceInstance.query.get(instance_id) if not instance: raise errors.ErrBadRequest("Service instance does not exist") if instance.deactivated_at: raise errors.ErrBadRequest( "Cannot update instance because it was already canceled") if instance.has_active_operations(): raise errors.ErrBadRequest( "Instance has an active operation in progress") domain_names = parse_domain_options(params) noop = True if domain_names is not None: self.logger.info("validating CNAMEs") validators.CNAME(domain_names).validate() self.logger.info("validating unique domains") validators.UniqueDomains(domain_names).validate(instance) noop = noop and (sorted(domain_names) == sorted( instance.domain_names)) if instance.instance_type == "cdn_service_instance" and noop: instance.new_certificate = instance.current_certificate instance.domain_names = domain_names if instance.instance_type == "cdn_service_instance": # N.B. we're using "param" in params rather than # params.get("param") because the OSBAPI spec # requires we do not mess with params that were not # specified, so unset and set to None have different meanings noop = False if details.plan_id != CDN_PLAN_ID: raise ClientError("Updating service plan is not supported") if "origin" in params: if params["origin"]: origin_hostname = params["origin"] validators.Hostname(origin_hostname).validate() else: origin_hostname = config.DEFAULT_CLOUDFRONT_ORIGIN instance.cloudfront_origin_hostname = origin_hostname if "path" in params: if params["path"]: cloudfront_origin_path = params["path"] else: cloudfront_origin_path = "" instance.cloudfront_origin_path = cloudfront_origin_path if "forward_cookies" in params: forward_cookie_policy, forwarded_cookies = parse_cookie_options( params) instance.forward_cookie_policy = forward_cookie_policy instance.forwarded_cookies = forwarded_cookies if "forward_headers" in params: forwarded_headers = parse_header_options(params) else: # .copy() so sqlalchemy recognizes the field has changed forwarded_headers = instance.forwarded_headers.copy() if instance.cloudfront_origin_hostname == config.DEFAULT_CLOUDFRONT_ORIGIN: forwarded_headers.append("HOST") forwarded_headers = normalize_header_list(forwarded_headers) instance.forwarded_headers = forwarded_headers if "insecure_origin" in params: origin_protocol_policy = "https-only" if params["insecure_origin"]: if (instance.cloudfront_origin_hostname == config.DEFAULT_CLOUDFRONT_ORIGIN): raise errors.ErrBadRequest( "Cannot use insecure_origin with default origin") origin_protocol_policy = "http-only" instance.origin_protocol_policy = origin_protocol_policy if "error_responses" in params: instance.error_responses = params["error_responses"] validators.ErrorResponseConfig( instance.error_responses).validate() queue = queue_all_cdn_update_tasks_for_operation elif instance.instance_type == "alb_service_instance": if details.plan_id != ALB_PLAN_ID: raise ClientError("Updating service plan is not supported") queue = queue_all_alb_update_tasks_for_operation elif instance.instance_type == "migration_service_instance": if details.plan_id == CDN_PLAN_ID: noop = False validate_migration_to_cdn_params(params) instance = change_instance_type(instance, CDNServiceInstance, db.session) update_cdn_params_for_migration(instance, params) db.session.add(instance.current_certificate) queue = queue_all_cdn_broker_migration_tasks_for_operation elif details.plan_id == ALB_PLAN_ID: noop = False validate_migration_to_alb_params(params) instance = change_instance_type(instance, ALBServiceInstance, db.session) update_alb_params_for_migration(instance, params) db.session.add(instance.current_certificate) queue = queue_all_domain_broker_migration_tasks_for_operation else: raise ClientError( "Updating to this service plan is not supported") if noop: return UpdateServiceSpec(False) operation = Operation( state=Operation.States.IN_PROGRESS.value, service_instance=instance, action=Operation.Actions.UPDATE.value, step_description="Queuing tasks", ) db.session.add(operation) db.session.add(instance) db.session.commit() queue(operation.id, cf_logging.FRAMEWORK.context.get_correlation_id()) return UpdateServiceSpec(True, operation=str(operation.id))