def list_change_sets(ctx, path, url): """ List change sets for stack. \f :param path: Path to execute the command on. :type path: str :param url: Write out a console URL instead. :type url: bool """ context = SceptreContext( command_path=path, project_path=ctx.obj.get("project_path"), user_variables=ctx.obj.get("user_variables"), output_format=ctx.obj.get("output_format"), options=ctx.obj.get("options"), ignore_dependencies=ctx.obj.get("ignore_dependencies") ) plan = SceptrePlan(context) responses = [ response for response in plan.list_change_sets(url).values() if response ] for response in responses: write(response, context.output_format)
def create_command(ctx, path, change_set_name, yes): """ Creates a stack or a change set. Creates a stack for a given config PATH. Or if CHANGE_SET_NAME is specified creates a change set for stack in PATH. :param path: Path to a Stack or StackGroup :type path: str :param change_set_name: A name of the Change Set - optional :type change_set_name: str :param yes: A flag to assume yes to all questions. :type yes: bool """ context = SceptreContext( command_path=path, project_path=ctx.obj.get("project_path"), user_variables=ctx.obj.get("user_variables"), options=ctx.obj.get("options"), ignore_dependencies=ctx.obj.get("ignore_dependencies")) action = "create" plan = SceptrePlan(context) if change_set_name: confirmation(action, yes, change_set=change_set_name, command_path=path) plan.create_change_set(change_set_name) else: confirmation(action, yes, command_path=path) responses = plan.create() exit(stack_status_exit_code(responses.values()))
def delete_command(ctx, path, change_set_name, yes): """ Deletes a stack or a change set. Deletes a stack for a given config PATH. Or if CHANGE_SET_NAME is specified deletes a change set for stack in PATH. """ context = SceptreContext(command_path=path, project_path=ctx.obj.get("project_path"), user_variables=ctx.obj.get("user_variables"), options=ctx.obj.get("options")) action = "delete" stack, stack_group = get_stack_or_stack_group(context) if stack: if change_set_name: confirmation(action, yes, change_set=change_set_name, stack=path) command = 'delete_change_set' plan = SceptrePlan(context, command, stack) plan.execute(change_set_name) else: confirmation(action, yes, stack=path) plan = SceptrePlan(context, action, stack) response = plan.execute() if response != StackStatus.COMPLETE: exit(1) elif stack_group: confirmation(action, yes, stack_group=path) plan = SceptrePlan(context, action, stack_group) response = plan.execute() if not all(status == StackStatus.COMPLETE for status in response.values()): exit(1)
def estimate_cost_command(ctx, path): """ Estimates the cost of the template. Prints a URI to STOUT that provides an estimated cost based on the resources in the stack. This command will also attempt to open a web browser with the returned URI. """ action = 'estimate_cost' context = SceptreContext( command_path=path, project_path=ctx.obj.get("project_path"), user_variables=ctx.obj.get("user_variables"), options=ctx.obj.get("options"), output_format=ctx.obj.get("output_format") ) stack, _ = get_stack_or_stack_group(context) plan = SceptrePlan(context, action, stack.template) response = plan.execute() if response['ResponseMetadata']['HTTPStatusCode'] == 200: del response['ResponseMetadata'] click.echo("View the estimated cost at:") response = response["Url"] webbrowser.open(response, new=2) write(response + "\n", 'str')
def describe_change_set(ctx, path, change_set_name, verbose): """ Describes the change set. :param path: Path to execute the command on. :type path: str :param change_set_name: Name of the Change Set to use. :type change_set_name: str :param verbose: A flag to display verbose output. :type verbose: bool """ context = SceptreContext( command_path=path, project_path=ctx.obj.get("project_path"), user_variables=ctx.obj.get("user_variables"), options=ctx.obj.get("options"), output_format=ctx.obj.get("output_format"), no_colour=ctx.obj.get("no_colour"), ignore_dependencies=ctx.obj.get("ignore_dependencies") ) plan = SceptrePlan(context) responses = plan.describe_change_set(change_set_name) for response in responses.values(): description = response if not verbose: description = simplify_change_set_description(description) write(description, context.output_format, context.no_colour)
def step_impl(context, stack_name): sceptre_context = SceptreContext(command_path=stack_name + '.yaml', project_path=context.sceptre_dir, ignore_dependencies=True) sceptre_plan = SceptrePlan(sceptre_context) context.output = list(sceptre_plan.describe_resources().values())
def step_impl(context, stack_name): sceptre_context = SceptreContext(command_path=stack_name + '.yaml', project_path=context.sceptre_dir) config_path = sceptre_context.full_config_path() template_path = sceptre_context.full_templates_path() with open(os.path.join(config_path, stack_name + '.yaml')) as config_file: stack_config = yaml.safe_load(config_file) if "template" in stack_config and stack_config["template"]["type"].lower( ) == "s3": segments = stack_config["template"]["path"].split('/') bucket = segments[0] key = "/".join(segments[1:]) source_file = f'{template_path}/{segments[-1]}' boto3.client('s3').upload_file(source_file, bucket, key) else: config_path = sceptre_context.full_config_path() with open(os.path.join(config_path, stack_name + '.yaml')) as config_file: stack_config = yaml.safe_load(config_file) sceptre_plan = SceptrePlan(sceptre_context) try: context.output = sceptre_plan.generate() except Exception as e: context.error = e
def delete_command(ctx, path, change_set_name, yes): """ Deletes a stack or a change set. Deletes a stack for a given config PATH. Or if CHANGE_SET_NAME is specified deletes a change set for stack in PATH. """ action = "delete" stack, stack_group = get_stack_or_stack_group(ctx, path) if stack: if change_set_name: confirmation(action, yes, change_set=change_set_name, stack=path) command = 'delete_change_set' plan = SceptrePlan(path, command, stack) plan.execute(change_set_name) else: confirmation(action, yes, stack=path) plan = SceptrePlan(path, action, stack) response = plan.execute() if response != StackStatus.COMPLETE: exit(1) elif stack_group: confirmation(action, yes, stack_group=path) plan = SceptrePlan(path, action, stack_group) response = plan.execute() if not all(status == StackStatus.COMPLETE for status in response.values()): exit(1)
def drift_show(ctx, path): """ Show stack drift on deployed stacks. In the event that the stack does not exist, we return a StackResourceDriftStatus of STACK_DOES_NOT_EXIST. In the event that drift detection times out, we return a StackResourceDriftStatus of TIMED_OUT. The timeout is set at 5 minutes, a value that cannot be configured. """ context = SceptreContext( command_path=path, project_path=ctx.obj.get("project_path"), user_variables=ctx.obj.get("user_variables"), options=ctx.obj.get("options"), output_format=ctx.obj.get("output_format"), ignore_dependencies=ctx.obj.get("ignore_dependencies")) plan = SceptrePlan(context) responses = plan.drift_show() output_format = "json" if context.output_format == "json" else "yaml" exit_status = 0 for stack, (status, response) in responses.items(): if status in BAD_STATUSES: exit_status += 1 response.pop("ResponseMetadata", None) write({stack.external_name: deserialize_json_properties(response)}, output_format) exit(exit_status)
def execute_command(ctx, path, change_set_name, yes): """ Executes a Change Set. \f :param path: Path to execute the command on. :type path: str :param change_set_name: Change Set to use. :type change_set_name: str :param yes: A flag to answer 'yes' too all CLI questions. :type yes: bool """ context = SceptreContext( command_path=path, project_path=ctx.obj.get("project_path"), user_variables=ctx.obj.get("user_variables"), options=ctx.obj.get("options"), ignore_dependencies=ctx.obj.get("ignore_dependencies") ) plan = SceptrePlan(context) confirmation( plan.execute_change_set.__name__, yes, change_set=change_set_name, command_path=path ) plan.execute_change_set(change_set_name)
def step_impl(context, stack_group_name): sceptre_context = SceptreContext(command_path=stack_group_name, project_path=context.sceptre_dir, ignore_dependencies=True) sceptre_plan = SceptrePlan(sceptre_context) sceptre_plan.delete()
def generate_command(ctx, no_placeholders, path): """ Prints the template used for stack in PATH. \f :param path: Path to execute the command on. :type path: str """ context = SceptreContext( command_path=path, project_path=ctx.obj.get("project_path"), user_variables=ctx.obj.get("user_variables"), options=ctx.obj.get("options"), output_format=ctx.obj.get("output_format"), ignore_dependencies=ctx.obj.get("ignore_dependencies")) plan = SceptrePlan(context) execution_context = null_context( ) if no_placeholders else use_resolver_placeholders_on_error() with execution_context: responses = plan.generate() output = [template for template in responses.values()] write(output, context.output_format)
def list_change_sets(ctx, path): """ List change sets for stack. :param path: Path to execute the command on. :type path: str """ context = SceptreContext( command_path=path, project_path=ctx.obj.get("project_path"), user_variables=ctx.obj.get("user_variables"), output_format=ctx.obj.get("output_format"), options=ctx.obj.get("options"), ignore_dependencies=ctx.obj.get("ignore_dependencies") ) plan = SceptrePlan(context) responses = [ response for response in plan.list_change_sets().values() if response ] for response in responses: if response['ResponseMetadata']['HTTPStatusCode'] == 200: del response['ResponseMetadata'] write(response, context.output_format)
def list_outputs(ctx, path, export): """ List outputs for stack. :param path: Path to execute the command on. :type path: str :param export: Specify the export formatting. :type export: str """ context = SceptreContext(command_path=path, project_path=ctx.obj.get("project_path", None), user_variables=ctx.obj.get("user_variables", {}), options=ctx.obj.get("options", {}), output_format=ctx.obj.get("output_format", {})) plan = SceptrePlan(context) responses = [ response for response in plan.describe_outputs().values() if response ] if export == "envvar": write("\n".join("export SCEPTRE_{0}={1}".format( output["OutputKey"], output["OutputValue"]) for response in responses for output in response)) else: write(list(responses), context.output_format)
def validate_command(ctx, path): """ Validates the template used for stack in PATH. \f :param path: Path to execute the command on. :type path: str """ context = SceptreContext( command_path=path, project_path=ctx.obj.get("project_path"), user_variables=ctx.obj.get("user_variables"), options=ctx.obj.get("options"), output_format=ctx.obj.get("output_format"), ignore_dependencies=ctx.obj.get("ignore_dependencies")) plan = SceptrePlan(context) responses = plan.validate() for stack, response in responses.items(): if response['ResponseMetadata']['HTTPStatusCode'] == 200: del response['ResponseMetadata'] click.echo("Template {} is valid. Template details:\n".format( stack.name)) write(response, context.output_format)
def estimate_cost_command(ctx, path): """ Prints a URI to STOUT that provides an estimated cost based on the resources in the stack. This command will also attempt to open a web browser with the returned URI. \f :param path: Path to execute the command on. :type path: str """ context = SceptreContext( command_path=path, project_path=ctx.obj.get("project_path"), user_variables=ctx.obj.get("user_variables"), options=ctx.obj.get("options"), output_format=ctx.obj.get("output_format"), ignore_dependencies=ctx.obj.get("ignore_dependencies")) plan = SceptrePlan(context) responses = plan.estimate_cost() for stack, response in responses.items(): if response['ResponseMetadata']['HTTPStatusCode'] == 200: del response['ResponseMetadata'] click.echo("View the estimated cost for {} at:".format(stack.name)) response = response["Url"] webbrowser.open(response, new=2) write(response + "\n", 'text')
def describe_policy(ctx, path): """ Displays the stack policy used. :param path: Path to execute the command on. :type path: str """ context = SceptreContext( command_path=path, project_path=ctx.obj.get("project_path"), user_variables=ctx.obj.get("user_variables"), options=ctx.obj.get("options"), output_format=ctx.obj.get("output_format"), no_colour=ctx.obj.get("no_colour"), ignore_dependencies=ctx.obj.get("ignore_dependencies") ) plan = SceptrePlan(context) responses = plan.get_policy() for response in responses.values(): write( response.get('StackPolicyBody', {}), context.output_format, context.no_colour )
def fetch_remote_template_command(ctx, path): """ Prints the remote template used for stack in PATH. \f :param path: Path to execute the command on. :type path: str """ context = SceptreContext( command_path=path, project_path=ctx.obj.get("project_path"), user_variables=ctx.obj.get("user_variables"), options=ctx.obj.get("options"), output_format=ctx.obj.get("output_format"), ignore_dependencies=ctx.obj.get("ignore_dependencies")) plan = SceptrePlan(context) responses = plan.fetch_remote_template() output = [] for stack, template in responses.items(): if template is None: logger.warning(f"{stack.external_name} does not exist") else: output.append(template) write(output, context.output_format)
def step_impl(context, stack_group_name): sceptre_context = SceptreContext(command_path=stack_group_name, project_path=context.sceptre_dir, ignore_dependencies=True) sceptre_plan = SceptrePlan(sceptre_context) context.response = sceptre_plan.describe_resources().values()
def list_outputs(ctx, path, export): """ List outputs for stack. \f :param path: Path to execute the command on. :type path: str :param export: Specify the export formatting. :type export: str """ context = SceptreContext( command_path=path, project_path=ctx.obj.get("project_path", None), user_variables=ctx.obj.get("user_variables", {}), options=ctx.obj.get("options", {}), output_format=ctx.obj.get("output_format"), ignore_dependencies=ctx.obj.get("ignore_dependencies")) plan = SceptrePlan(context) responses = [ response for response in plan.describe_outputs().values() if response ] if export == "envvar": for response in responses: for stack in response.values(): for output in stack: write( "export SCEPTRE_{0}='{1}'".format( output.get("OutputKey"), output.get("OutputValue")), 'text') else: write(responses, context.output_format)
def step_impl(context, stack_name): sceptre_context = SceptreContext(command_path=stack_name + '.yaml', project_path=context.sceptre_dir) sceptre_plan = SceptrePlan(sceptre_context) try: context.output = sceptre_plan.generate() except Exception as e: context.error = e
def step_impl(context, stack_group_name): sceptre_context = SceptreContext( command_path=stack_group_name, project_path=context.sceptre_dir ) sceptre_plan = SceptrePlan(sceptre_context) sceptre_plan.launch()
def test_planner_executes_with_params(self): plan = SceptrePlan('test/path', 'test-command', self.stack_group) plan.execute = Mock(name='execute') plan.execute.return_value = sentinel.success result = plan.execute('test-attribute') plan.execute.assert_called_once_with('test-attribute') assert result == sentinel.success
def diff_command(ctx: Context, differ: str, show_no_echo: bool, path: str): """Indicates the difference between the currently DEPLOYED stacks in the command path and the stacks configured in Sceptre right now. This command will compare both the templates as well as the subset of stack configurations that can be compared. Some settings (such as sceptre_user_data) are not available in a CloudFormation stack description, so the diff will not be indicated. Currently compared stack configurations are: \b * parameters * notifications * role_arn * stack_tags Important: There are resolvers (notably !stack_output, among others) that rely on other stacks to be already deployed when they are resolved. When producing a diff on Stack Configs that have such resolvers that point to non-deployed stacks, this presents a challenge, since this means those resolvers cannot be resolved. This particularly applies to stack parameters and when a stack's template uses sceptre_user_data with resolvers in it. In order to continue to be useful when producing a diff in these conditions, this command will do the following: 1. If the resolver CAN be resolved, it will be resolved and the resolved value will be in the diff results. 2. If the resolver CANNOT be resolved, it will be replaced with a string that represents the resolver and its arguments. For example: !stack_output my_stack.yaml::MyOutput will resolve in the parameters to "{ !StackOutput(my_stack.yaml::MyOutput) }". Particularly in cases where the replaced value doesn't work in the template as the template logic requires and causes an error, there is nothing further Sceptre can do and diffing will fail. """ context = SceptreContext( command_path=path, project_path=ctx.obj.get("project_path"), user_variables=ctx.obj.get("user_variables"), options=ctx.obj.get("options"), ignore_dependencies=ctx.obj.get("ignore_dependencies"), output_format=ctx.obj.get('output_format'), no_colour=ctx.obj.get('no_colour') ) output_format = context.output_format plan = SceptrePlan(context) if differ == "deepdiff": stack_differ = DeepDiffStackDiffer(show_no_echo) writer_class = DeepDiffWriter elif differ == 'difflib': stack_differ = DifflibStackDiffer(show_no_echo) writer_class = DiffLibWriter else: raise ValueError(f"Unexpected differ type: {differ}") diffs: Dict[Stack, StackDiff] = plan.diff(stack_differ) num_stacks_with_diff = output_diffs(diffs.values(), writer_class, sys.stdout, output_format) if num_stacks_with_diff: logger.warning( f"{num_stacks_with_diff} stacks with differences detected." )
def step_impl(context, stack_name): sceptre_context = SceptreContext(command_path=stack_name + '.yaml', project_path=context.sceptre_dir) sceptre_plan = SceptrePlan(sceptre_context) try: sceptre_plan.lock() except ClientError as e: context.error = e
def step_impl(context, stack_name, desired_status): full_name = get_cloudformation_stack_name(context, stack_name) sceptre_context = SceptreContext(command_path=stack_name + '.yaml', project_path=context.sceptre_dir) sceptre_plan = SceptrePlan(sceptre_context) status = sceptre_plan.get_status() status = get_stack_status(context, full_name) assert (status == desired_status)
def execute_command(ctx, path, change_set_name, yes): """ Executes a change set. """ stack, _ = get_stack_or_stack_group(ctx, path) confirmation("execute", yes, change_set=change_set_name, stack=path) action = 'execute_change_set' plan = SceptrePlan(path, action, stack) plan.execute(change_set_name)
def generate_command(ctx, path): """ Prints the template. Prints the template used for stack in PATH. """ stack, _ = get_stack_or_stack_group(ctx, path) action = 'generate' plan = SceptrePlan(path, action, stack.template) write(plan.execute())
def describe_policy(ctx, path): """ Displays the stack policy used. """ stack, _ = get_stack_or_stack_group(ctx, path) action = 'get_policy' plan = SceptrePlan(path, action, stack) response = plan.execute() write(response.get('StackPolicyBody', {}))
def step_impl(context, stack_name): sceptre_context = SceptreContext(command_path=stack_name + '.yaml', project_path=context.sceptre_dir) sceptre_plan = SceptrePlan(sceptre_context) try: response = sceptre_plan.validate() context.response = response except ClientError as e: context.error = e