def delete_stack(stack_id: str, delete_options: dict) -> dict: """ DELETE /stacks/{id} Delete a stack """ sentry_client.capture_breadcrumb(data={ 'stack_id': stack_id, 'delete_options': delete_options, }) dry_run = delete_options.get('dry_run', False) force = delete_options.get('force', False) region = delete_options.get('region', config.region) # type: Optional[str] senza = Senza(region) logger.info("Removing stack %s...", stack_id) running_time = MeasureRunningTime('delete_stack.success') output = senza.remove(stack_id, dry_run=dry_run, force=force) running_time.finish() logger.info("Stack %s removed.", stack_id) return '', 204, _make_headers(output=output)
def get_stack_traffic(stack_id: str, region: str = None) -> Tuple[dict, int, dict]: """ GET /stacks/{id}/traffic Update traffic and Taupage image """ sentry_client.capture_breadcrumb(data={ 'stack_id': stack_id, 'region': region, }) stack_name, stack_version = stack_id.rsplit('-', 1) senza = Senza(region or config.region) running_time = MeasureRunningTime('get_stack_traffic.success') traffic_info = senza.traffic(stack_name=stack_name) if traffic_info: for stack_traffic_info in traffic_info: if stack_traffic_info['identifier'] == stack_id: return { 'weight': float(stack_traffic_info['weight%']) }, 200, _make_headers() running_time.finish() return connexion.problem(404, 'Not Found', 'Stack not found: {}'.format(stack_id), headers=_make_headers())
def patch_stack(stack_id: str, stack_patch: dict) -> dict: """ PATCH /stacks/{id} Update scale, traffic and Taupage image """ sentry_client.capture_breadcrumb(data={ 'stack_id': stack_id, 'stack_patch': stack_patch, }) stack_patch = filter_empty_values(stack_patch) stack_name, stack_version = stack_id.rsplit('-', 1) use_region = stack_patch.get('region', config.region) senza = Senza(use_region) log_info = {'stack_id': stack_id, 'stack_name': stack_name} running_time = MeasureRunningTime('patch_stack.success') if 'new_scale' in stack_patch: new_scale = stack_patch['new_scale'] senza.scale(stack_name, stack_version, new_scale) if 'new_ami_image' in stack_patch: # Change the AMI image of the Auto Scaling Group (ASG) and respawn the # instances to use new image. new_ami_image = stack_patch['new_ami_image'] senza.patch(stack_name, stack_version, new_ami_image) senza.respawn_instances(stack_name, stack_version) if 'new_traffic' in stack_patch: new_traffic = stack_patch['new_traffic'] domains = senza.domains(stack_name) if domains: logger.info("Switching app traffic to stack.", extra=log_info) senza.traffic(stack_name=stack_name, stack_version=stack_version, percentage=new_traffic) else: logger.info("App does not have a domain so traffic will not be switched.", extra=log_info) raise TrafficNotUpdated("App does not have a domain.") # refresh the dict stack_dict = Stack.get(stack_name, stack_version, region=use_region) running_time.finish() return stack_dict, 202, _make_headers()
def patch_stack(stack_id: str, stack_patch: dict) -> dict: """ PATCH /stacks/{id} Update scale, traffic and Taupage image """ sentry_client.capture_breadcrumb(data={ 'stack_id': stack_id, 'stack_patch': stack_patch, }) stack_patch = filter_empty_values(stack_patch) stack_name, stack_version = stack_id.rsplit('-', 1) use_region = stack_patch.get('region', config.region) senza = Senza(use_region) log_info = {'stack_id': stack_id, 'stack_name': stack_name} running_time = MeasureRunningTime('patch_stack.success') if 'new_scale' in stack_patch: new_scale = stack_patch['new_scale'] senza.scale(stack_name, stack_version, new_scale) if 'new_ami_image' in stack_patch: # Change the AMI image of the Auto Scaling Group (ASG) and respawn the # instances to use new image. new_ami_image = stack_patch['new_ami_image'] senza.patch(stack_name, stack_version, new_ami_image) senza.respawn_instances(stack_name, stack_version) if 'new_traffic' in stack_patch: new_traffic = stack_patch['new_traffic'] domains = senza.domains(stack_name) if domains: logger.info("Switching app traffic to stack.", extra=log_info) senza.traffic(stack_name=stack_name, stack_version=stack_version, percentage=new_traffic) else: logger.info( "App does not have a domain so traffic will not be switched.", extra=log_info) raise TrafficNotUpdated("App does not have a domain.") # refresh the dict stack_dict = Stack.get(stack_name, stack_version, region=use_region) running_time.finish() return stack_dict, 202, _make_headers()
def get_stack_traffic(stack_id: str, region: str=None) -> Tuple[dict, int, dict]: """ GET /stacks/{id}/traffic Update traffic and Taupage image """ sentry_client.capture_breadcrumb(data={ 'stack_id': stack_id, 'region': region, }) stack_name, stack_version = stack_id.rsplit('-', 1) senza = Senza(region or config.region) running_time = MeasureRunningTime('get_stack_traffic.success') traffic_info = senza.traffic(stack_name=stack_name) if traffic_info: for stack_traffic_info in traffic_info: if stack_traffic_info['identifier'] == stack_id: return {'weight': float(stack_traffic_info['weight%'])}, 200, _make_headers() running_time.finish() return connexion.problem(404, 'Not Found', 'Stack not found: {}'.format(stack_id), headers=_make_headers())
def create_stack(new_stack: dict) -> dict: """ POST /stacks/ :param new_stack: New stack """ keep_stacks = new_stack['keep_stacks'] # type: int new_traffic = new_stack['new_traffic'] # type: int stack_version = new_stack['stack_version'] # type: str senza_yaml = new_stack['senza_yaml'] # type: str parameters = new_stack.get('parameters', []) disable_rollback = new_stack.get('disable_rollback', False) region = new_stack.get('region', config.region) # type: Optional[str] dry_run = new_stack.get('dry_run', False) tags = new_stack.get('tags', []) running_time = MeasureRunningTime('create_stack.success') sentry_client.capture_breadcrumb( data={ 'keep_stacks': keep_stacks, 'new_traffic': new_traffic, 'stack_version': stack_version, 'disable_rollback': disable_rollback, }) try: senza_definition = yaml.load(senza_yaml) except yaml.YAMLError as exception: metrics.count('create_stack.invalid_senza_file') return connexion.problem(400, 'Invalid senza yaml', exception.message, headers=_make_headers()) try: stack_name = senza_definition['SenzaInfo']['StackName'] except KeyError as exception: logger.error("Couldn't get stack name from definition.", extra={'senza_yaml': repr(senza_yaml)}) missing_property = str(exception) problem = connexion.problem( 400, 'Invalid senza yaml', "Missing property in senza yaml: {}".format(missing_property), headers=_make_headers()) metrics.count('create_stack.missing_stack_name_prop') return problem # Create the Stack logger.info("Creating stack %s...", stack_name) senza = Senza(region) tags = [ 'LizzyKeepStacks={}'.format(keep_stacks), 'LizzyTargetTraffic={}'.format(new_traffic), *tags ] output = senza.create(senza_yaml, stack_version, parameters, disable_rollback, dry_run, tags) logger.info("Stack created.", extra={ 'stack_name': stack_name, 'stack_version': stack_version, 'parameters': parameters }) stack_dict = (Stack.get(stack_name, stack_version, region=region) if not dry_run else { 'stack_name': stack_name, 'creation_time': '', 'description': '', 'status': 'DRY-RUN', 'version': stack_version }) running_time.finish() metrics.count('create_stack.region.{}'.format(region)) return stack_dict, 201, _make_headers(output=output)
def create_stack(new_stack: dict) -> dict: """ POST /stacks/ :param new_stack: New stack """ keep_stacks = new_stack['keep_stacks'] # type: int new_traffic = new_stack['new_traffic'] # type: int stack_version = new_stack['stack_version'] # type: str senza_yaml = new_stack['senza_yaml'] # type: str parameters = new_stack.get('parameters', []) disable_rollback = new_stack.get('disable_rollback', False) region = new_stack.get('region', config.region) # type: Optional[str] dry_run = new_stack.get('dry_run', False) tags = new_stack.get('tags', []) running_time = MeasureRunningTime('create_stack.success') sentry_client.capture_breadcrumb(data={ 'keep_stacks': keep_stacks, 'new_traffic': new_traffic, 'stack_version': stack_version, 'disable_rollback': disable_rollback, }) try: senza_definition = yaml.load(senza_yaml) except yaml.YAMLError as exception: metrics.count('create_stack.invalid_senza_file') return connexion.problem(400, 'Invalid senza yaml', exception.message, headers=_make_headers()) try: stack_name = senza_definition['SenzaInfo']['StackName'] except KeyError as exception: logger.error("Couldn't get stack name from definition.", extra={'senza_yaml': repr(senza_yaml)}) missing_property = str(exception) problem = connexion.problem(400, 'Invalid senza yaml', "Missing property in senza yaml: {}".format( missing_property), headers=_make_headers()) metrics.count('create_stack.missing_stack_name_prop') return problem # Create the Stack logger.info("Creating stack %s...", stack_name) senza = Senza(region) tags = ['LizzyKeepStacks={}'.format(keep_stacks), 'LizzyTargetTraffic={}'.format(new_traffic), *tags] output = senza.create(senza_yaml, stack_version, parameters, disable_rollback, dry_run, tags) logger.info("Stack created.", extra={'stack_name': stack_name, 'stack_version': stack_version, 'parameters': parameters}) stack_dict = (Stack.get(stack_name, stack_version, region=region) if not dry_run else {'stack_name': stack_name, 'creation_time': '', 'description': '', 'status': 'DRY-RUN', 'version': stack_version}) running_time.finish() metrics.count('create_stack.region.{}'.format(region)) return stack_dict, 201, _make_headers(output=output)