diff --git a/samcli/commands/pipeline/bootstrap/cli.py b/samcli/commands/pipeline/bootstrap/cli.py index 1b2c709372..1b9ee588bb 100644 --- a/samcli/commands/pipeline/bootstrap/cli.py +++ b/samcli/commands/pipeline/bootstrap/cli.py @@ -10,7 +10,7 @@ from samcli.cli.context import get_cmd_names from samcli.cli.main import pass_context, common_options, aws_creds_options, print_cmdline_args from samcli.lib.config.samconfig import SamConfig -from samcli.lib.pipeline.bootstrap.stage import Stage +from samcli.lib.pipeline.bootstrap.environment import Environment from samcli.lib.telemetry.metric import track_command from samcli.lib.utils.version_checker import check_newer_version from .guided_context import GuidedContext @@ -38,8 +38,8 @@ help="Disable interactive prompting for bootstrap parameters, and fail if any required arguments are missing.", ) @click.option( - "--stage-name", - help="The name of the corresponding pipeline stage. It is used as a suffix for the created resources.", + "--environment", + help="The name of the corresponding environment. It is used as a suffix for the created resources.", required=False, ) @click.option( @@ -51,7 +51,7 @@ ) @click.option( "--pipeline-execution-role", - help="The ARN of an IAM role to be assumed by the pipeline user to operate on this stage. " + help="The ARN of an IAM role to be assumed by the pipeline user to operate on this environment. " "Provide it only if you want to user your own role, otherwise, the command will create one", required=False, ) @@ -101,7 +101,7 @@ def cli( ctx: Any, interactive: bool, - stage_name: Optional[str], + environment: Optional[str], pipeline_user: Optional[str], pipeline_execution_role: Optional[str], cloudformation_execution_role: Optional[str], @@ -120,7 +120,7 @@ def cli( region=ctx.region, profile=ctx.profile, interactive=interactive, - stage_name=stage_name, + environment_name=environment, pipeline_user_arn=pipeline_user, pipeline_execution_role_arn=pipeline_execution_role, cloudformation_execution_role_arn=cloudformation_execution_role, @@ -138,7 +138,7 @@ def do_cli( region: Optional[str], profile: Optional[str], interactive: bool, - stage_name: Optional[str], + environment_name: Optional[str], pipeline_user_arn: Optional[str], pipeline_execution_role_arn: Optional[str], cloudformation_execution_role_arn: Optional[str], @@ -158,7 +158,7 @@ def do_cli( if interactive: guided_context = GuidedContext( - stage_name=stage_name, + environment_name=environment_name, pipeline_user_arn=pipeline_user_arn, pipeline_execution_role_arn=pipeline_execution_role_arn, cloudformation_execution_role_arn=cloudformation_execution_role_arn, @@ -168,7 +168,7 @@ def do_cli( pipeline_ip_range=pipeline_ip_range, ) guided_context.run() - stage_name = guided_context.stage_name + environment_name = guided_context.environment_name pipeline_user_arn = guided_context.pipeline_user_arn pipeline_execution_role_arn = guided_context.pipeline_execution_role_arn pipeline_ip_range = guided_context.pipeline_ip_range @@ -177,11 +177,11 @@ def do_cli( create_ecr_repo = guided_context.create_ecr_repo ecr_repo_arn = guided_context.ecr_repo_arn - if not stage_name: - raise click.UsageError("Missing required parameter '--stage-name'") + if not environment_name: + raise click.UsageError("Missing required parameter '--environment'") - stage: Stage = Stage( - name=stage_name, + environment: Environment = Environment( + name=environment_name, aws_profile=profile, aws_region=region, pipeline_user_arn=pipeline_user_arn, @@ -193,12 +193,12 @@ def do_cli( ecr_repo_arn=ecr_repo_arn, ) - bootstrapped: bool = stage.bootstrap(confirm_changeset=confirm_changeset) + bootstrapped: bool = environment.bootstrap(confirm_changeset=confirm_changeset) if bootstrapped: - stage.print_resources_summary() + environment.print_resources_summary() - stage.save_config_safe( + environment.save_config_safe( config_dir=PIPELINE_CONFIG_DIR, filename=PIPELINE_CONFIG_FILENAME, cmd_names=_get_command_names() ) diff --git a/samcli/commands/pipeline/bootstrap/guided_context.py b/samcli/commands/pipeline/bootstrap/guided_context.py index 13f2cb5cb0..332280fc77 100644 --- a/samcli/commands/pipeline/bootstrap/guided_context.py +++ b/samcli/commands/pipeline/bootstrap/guided_context.py @@ -1,5 +1,5 @@ """ -An interactive flow that prompt the user for required information to bootstrap the AWS account of a pipeline stage +An interactive flow that prompt the user for required information to bootstrap the AWS account of an environment with the required infrastructure """ from typing import Optional @@ -10,7 +10,7 @@ class GuidedContext: def __init__( self, - stage_name: Optional[str] = None, + environment_name: Optional[str] = None, pipeline_user_arn: Optional[str] = None, pipeline_execution_role_arn: Optional[str] = None, cloudformation_execution_role_arn: Optional[str] = None, @@ -19,7 +19,7 @@ def __init__( ecr_repo_arn: Optional[str] = None, pipeline_ip_range: Optional[str] = None, ) -> None: - self.stage_name = stage_name + self.environment_name = environment_name self.pipeline_user_arn = pipeline_user_arn self.pipeline_execution_role_arn = pipeline_execution_role_arn self.cloudformation_execution_role_arn = cloudformation_execution_role_arn @@ -34,13 +34,13 @@ def run(self) -> None: for the pipeline to work. Users can provide all, none or some resources' ARNs and leave the remaining empty and it will be created by the bootstrap command """ - if not self.stage_name: - self.stage_name = click.prompt("Stage Name", type=click.STRING) + if not self.environment_name: + self.environment_name = click.prompt("Environment Name", type=click.STRING) if not self.pipeline_user_arn: click.echo( - "\nThere must be exactly one pipeline user across all of the pipeline stages. " - "If you have ran this command before to bootstrap a previous pipeline stage, please " + "\nThere must be exactly one pipeline user across all of the environments. " + "If you have ran this command before to bootstrap a previous environment, please " "provide the ARN of the created pipeline user, otherwise, we will create a new user for you. " "Please make sure to store the credentials safely with the CI/CD provider." ) @@ -50,7 +50,7 @@ def run(self) -> None: if not self.pipeline_execution_role_arn: self.pipeline_execution_role_arn = click.prompt( - "\nPipeline execution role (an IAM role assumed by the pipeline user to operate on this stage) " + "\nPipeline execution role (an IAM role assumed by the pipeline user to operate on this environment) " "[leave blank to create one]", default="", type=click.STRING, diff --git a/samcli/commands/pipeline/init/pipeline_templates_manifest.py b/samcli/commands/pipeline/init/pipeline_templates_manifest.py index b24d2f2a7d..68d4fd2838 100644 --- a/samcli/commands/pipeline/init/pipeline_templates_manifest.py +++ b/samcli/commands/pipeline/init/pipeline_templates_manifest.py @@ -9,15 +9,15 @@ - displayName:Github Actions id: github-actions templates: - - displayName: jenkins-two-stages-pipeline + - displayName: jenkins-two-environments-pipeline provider: Jenkins - location: templates/cookiecutter-jenkins-two-stages-pipeline - - displayName: gitlab-two-stages-pipeline + location: templates/cookiecutter-jenkins-two-environments-pipeline + - displayName: gitlab-two-environments-pipeline provider: Gitlab - location: templates/cookiecutter-gitlab-two-stages-pipeline - - displayName: Github-Actions-two-stages-pipeline + location: templates/cookiecutter-gitlab-two-environments-pipeline + - displayName: Github-Actions-two-environments-pipeline provider: Github Actions - location: templates/cookiecutter-github-actions-two-stages-pipeline + location: templates/cookiecutter-github-actions-two-environments-pipeline """ from pathlib import Path from typing import Dict, List diff --git a/samcli/lib/pipeline/bootstrap/stage.py b/samcli/lib/pipeline/bootstrap/environment.py similarity index 83% rename from samcli/lib/pipeline/bootstrap/stage.py rename to samcli/lib/pipeline/bootstrap/environment.py index 3ea342a8e0..196b019d91 100644 --- a/samcli/lib/pipeline/bootstrap/stage.py +++ b/samcli/lib/pipeline/bootstrap/environment.py @@ -1,4 +1,4 @@ -""" Pipeline stage""" +""" Application Environment """ import os import pathlib import re @@ -12,8 +12,8 @@ CFN_TEMPLATE_PATH = str(pathlib.Path(os.path.dirname(__file__))) STACK_NAME_PREFIX = "aws-sam-cli-managed" -STAGE_RESOURCES_STACK_NAME_SUFFIX = "pipeline-resources" -STAGE_RESOURCES_CFN_TEMPLATE = "stage_resources.yaml" +ENVIRONMENT_RESOURCES_STACK_NAME_SUFFIX = "pipeline-resources" +ENVIRONMENT_RESOURCES_CFN_TEMPLATE = "environment_resources.yaml" PIPELINE_USER = "pipeline_user" PIPELINE_EXECUTION_ROLE = "pipeline_execution_role" CLOUDFORMATION_EXECUTION_ROLE = "cloudformation_execution_role" @@ -22,18 +22,18 @@ REGION = "region" -class Stage: +class Environment: """ - Represents a pipeline stage + Represents an application environment: Beta, Gamma, Prod ...etc Attributes ---------- name: str - The name of the stage + The name of the environment aws_profile: Optional[str] - The named AWS profile(in user's machine) of the AWS account to deploy this stage to. + The named AWS profile (in user's machine) of the AWS account to deploy this environment to. aws_region: Optional[str] - The AWS region to deploy this stage to. + The AWS region to deploy this environment to. pipeline_user: IAMUser The IAM User having its AccessKeyId and SecretAccessKey credentials shared with the CI/CD provider pipeline_execution_role: Resource @@ -55,14 +55,14 @@ class Stage: Methods: -------- did_user_provide_all_required_resources(self) -> bool: - checks if all of the stage required resources(pipeline_user, pipeline_execution_role, + checks if all of the environment's required resources (pipeline_user, pipeline_execution_role, cloudformation_execution_role, artifacts_bucket and ecr_repo) are provided by the user. bootstrap(self, confirm_changeset: bool = True) -> None: - deploys the CFN template ./stage_resources.yaml to the AWS account identified by aws_profile and aws_region - member fields. if aws_profile is not provided, it will fallback to default boto3 credentials' resolving. - Note that ./stage_resources.yaml template accepts the ARNs of already existing resources(if any) as parameters - and it will skip the creation of those resources but will use the ARNs to set the proper permissions of other - missing resources(resources created by the template) + deploys the CFN template ./environment_resources.yaml to the AWS account identified by aws_profile and + aws_region member fields. if aws_profile is not provided, it will fallback to default boto3 credentials' + resolving. Note that ./environment_resources.yaml template accepts the ARNs of already existing resources(if + any) as parameters and it will skip the creation of those resources but will use the ARNs to set the proper + permissions of other missing resources(resources created by the template) save_config(self, config_dir: str, filename: str, cmd_names: List[str]): save the Artifacts bucket name, ECR repo URI and ARNs of pipeline_user, pipeline_execution_role and cloudformation_execution_role to the "pipelineconfig.toml" file so that it can be auto-filled during @@ -96,7 +96,7 @@ def __init__( self.ecr_repo: ECRRepo = ECRRepo(arn=ecr_repo_arn) def did_user_provide_all_required_resources(self) -> bool: - """Check if the user provided all of the stage resources or not""" + """Check if the user provided all of the environment resources or not""" return all(resource.is_user_provided for resource in self._get_resources()) def _get_non_user_provided_resources_msg(self) -> str: @@ -115,13 +115,13 @@ def _get_non_user_provided_resources_msg(self) -> str: def bootstrap(self, confirm_changeset: bool = True) -> bool: """ - Deploys the CFN template(./stage_resources.yaml) which deploys: + Deploys the CFN template(./environment_resources.yaml) which deploys: * Pipeline IAM User * Pipeline execution IAM role * CloudFormation execution IAM role * Artifacts' S3 Bucket * ECR Repo - to the AWS account associated with the given stage. It will not redeploy the stack if already exists. + to the AWS account associated with the given environment. It will not redeploy the stack if already exists. This CFN template accepts the ARNs of the resources as parameters and will not create a resource if already provided, this way we can conditionally create a resource only if the user didn't provide it @@ -130,33 +130,36 @@ def bootstrap(self, confirm_changeset: bool = True) -> bool: Parameters ---------- confirm_changeset: bool - if set to false, the stage_resources.yaml CFN template will directly be deployed, otherwise, the user will - be prompted for confirmation + if set to false, the environment_resources.yaml CFN template will directly be deployed, otherwise, + the user will be prompted for confirmation Returns True if bootstrapped, otherwise False """ if self.did_user_provide_all_required_resources(): - click.secho(f"\nAll required resources for the {self.name} stage exist, skipping creation.", fg="yellow") + click.secho( + f"\nAll required resources for the {self.name} environment exist, skipping creation.", fg="yellow" + ) return True missing_resources_msg: str = self._get_non_user_provided_resources_msg() click.echo( - f"This will create the following required resources for the {self.name} stage: {missing_resources_msg}" + f"This will create the following required resources for the '{self.name}' environment: " + f"{missing_resources_msg}" ) if confirm_changeset: confirmed: bool = click.confirm("Should we proceed with the creation?") if not confirmed: return False - sanitized_stage_name: str = re.sub("[^0-9a-zA-Z]+", "-", self.name) - stack_name: str = f"{STACK_NAME_PREFIX}-{sanitized_stage_name}-{STAGE_RESOURCES_STACK_NAME_SUFFIX}" - stage_resources_template_body = Stage._read_template(STAGE_RESOURCES_CFN_TEMPLATE) + sanitized_environment_name: str = re.sub("[^0-9a-zA-Z]+", "-", self.name) + stack_name: str = f"{STACK_NAME_PREFIX}-{sanitized_environment_name}-{ENVIRONMENT_RESOURCES_STACK_NAME_SUFFIX}" + environment_resources_template_body = Environment._read_template(ENVIRONMENT_RESOURCES_CFN_TEMPLATE) output: StackOutput = manage_stack( stack_name=stack_name, region=self.aws_region, profile=self.aws_profile, - template_body=stage_resources_template_body, + template_body=environment_resources_template_body, parameter_overrides={ "PipelineUserArn": self.pipeline_user.arn or "", "PipelineExecutionRoleArn": self.pipeline_execution_role.arn or "", @@ -219,7 +222,7 @@ def save_config(self, config_dir: str, filename: str, cmd_names: List[str]) -> N except ValueError: ecr_repo_uri = "" - stage_specific_configs: Dict[str, Optional[str]] = { + environment_specific_configs: Dict[str, Optional[str]] = { PIPELINE_EXECUTION_ROLE: self.pipeline_execution_role.arn, CLOUDFORMATION_EXECUTION_ROLE: self.cloudformation_execution_role.arn, ARTIFACTS_BUCKET: artifacts_bucket_name, @@ -227,7 +230,7 @@ def save_config(self, config_dir: str, filename: str, cmd_names: List[str]) -> N REGION: self.aws_region, } - for key, value in stage_specific_configs.items(): + for key, value in environment_specific_configs.items(): if value: samconfig.put( cmd_names=cmd_names, @@ -278,7 +281,8 @@ def print_resources_summary(self) -> None: if provided_resources: click.secho( "\nYou provided the following resources. Please make sure it has the required permissions as shown at " - "https://github.com/aws/aws-sam-cli/blob/develop/samcli/lib/pipeline/bootstrap/stage_resources.yaml", + "https://github.com/aws/aws-sam-cli/blob/develop/" + "samcli/lib/pipeline/bootstrap/environment_resources.yaml", fg="green", ) for resource in provided_resources: diff --git a/samcli/lib/pipeline/bootstrap/stage_resources.yaml b/samcli/lib/pipeline/bootstrap/environment_resources.yaml similarity index 100% rename from samcli/lib/pipeline/bootstrap/stage_resources.yaml rename to samcli/lib/pipeline/bootstrap/environment_resources.yaml diff --git a/tests/unit/commands/pipeline/bootstrap/test_cli.py b/tests/unit/commands/pipeline/bootstrap/test_cli.py index 093e440e02..e6730dc6a9 100644 --- a/tests/unit/commands/pipeline/bootstrap/test_cli.py +++ b/tests/unit/commands/pipeline/bootstrap/test_cli.py @@ -15,7 +15,7 @@ ANY_REGION = "ANY_REGION" ANY_PROFILE = "ANY_PROFILE" -ANY_STAGE_NAME = "ANY_STAGE_NAME" +ANY_ENVIRONMENT_NAME = "ANY_ENVIRONMENT_NAME" ANY_PIPELINE_USER_ARN = "ANY_PIPELINE_USER_ARN" ANY_PIPELINE_EXECUTION_ROLE_ARN = "ANY_PIPELINE_EXECUTION_ROLE_ARN" ANY_CLOUDFORMATION_EXECUTION_ROLE_ARN = "ANY_CLOUDFORMATION_EXECUTION_ROLE_ARN" @@ -34,7 +34,7 @@ def setUp(self) -> None: "region": ANY_REGION, "profile": ANY_PROFILE, "interactive": True, - "stage_name": ANY_STAGE_NAME, + "environment_name": ANY_ENVIRONMENT_NAME, "pipeline_user_arn": ANY_PIPELINE_USER_ARN, "pipeline_execution_role_arn": ANY_PIPELINE_EXECUTION_ROLE_ARN, "cloudformation_execution_role_arn": ANY_CLOUDFORMATION_EXECUTION_ROLE_ARN, @@ -55,12 +55,12 @@ def test_bootstrap_command_default_argument_values(self, do_cli_mock): # interactive -> True # create_ecr_repo -> False # confirm_changeset -> True - # region, profile, stage_name and all ARNs are None + # region, profile, environment_name and all ARNs are None do_cli_mock.assert_called_once_with( region=None, profile=None, interactive=True, - stage_name=None, + environment_name=None, pipeline_user_arn=None, pipeline_execution_role_arn=None, cloudformation_execution_role_arn=None, @@ -92,25 +92,26 @@ def test_bootstrap_command_flag_arguments(self, do_cli_mock): def test_bootstrap_command_with_different_arguments_combination(self, do_cli_mock): runner: CliRunner = CliRunner() runner.invoke( - bootstrap_cmd, args=["--no-interactive", "--stage-name", "stage1", "--artifacts-bucket", "bucketARN"] + bootstrap_cmd, + args=["--no-interactive", "--environment", "environment1", "--artifacts-bucket", "bucketARN"], ) args, kwargs = do_cli_mock.call_args self.assertFalse(kwargs["interactive"]) - self.assertEqual(kwargs["stage_name"], "stage1") + self.assertEqual(kwargs["environment_name"], "environment1") self.assertEqual(kwargs["artifacts_bucket_arn"], "bucketARN") @patch("samcli.commands.pipeline.bootstrap.cli._get_command_names") @patch("samcli.commands.pipeline.bootstrap.cli._load_saved_pipeline_user_arn") - @patch("samcli.commands.pipeline.bootstrap.cli.Stage") + @patch("samcli.commands.pipeline.bootstrap.cli.Environment") @patch("samcli.commands.pipeline.bootstrap.cli.GuidedContext") def test_bootstrapping_normal_interactive_flow( - self, guided_context_mock, stage_mock, load_saved_pipeline_user_arn_mock, get_command_names_mock + self, guided_context_mock, environment_mock, load_saved_pipeline_user_arn_mock, get_command_names_mock ): # setup gc_instance = Mock() guided_context_mock.return_value = gc_instance - stage_instance = Mock() - stage_mock.return_value = stage_instance + environment_instance = Mock() + environment_mock.return_value = environment_instance load_saved_pipeline_user_arn_mock.return_value = ANY_PIPELINE_USER_ARN self.cli_context["interactive"] = True self.cli_context["pipeline_user_arn"] = None @@ -122,9 +123,9 @@ def test_bootstrapping_normal_interactive_flow( # verify load_saved_pipeline_user_arn_mock.assert_called_once() gc_instance.run.assert_called_once() - stage_instance.bootstrap.assert_called_once_with(confirm_changeset=True) - stage_instance.print_resources_summary.assert_called_once() - stage_instance.save_config_safe.assert_called_once_with( + environment_instance.bootstrap.assert_called_once_with(confirm_changeset=True) + environment_instance.print_resources_summary.assert_called_once() + environment_instance.save_config_safe.assert_called_once_with( config_dir=PIPELINE_CONFIG_DIR, filename=PIPELINE_CONFIG_FILENAME, cmd_names=PIPELINE_BOOTSTRAP_COMMAND_NAMES, @@ -132,20 +133,20 @@ def test_bootstrapping_normal_interactive_flow( @patch("samcli.commands.pipeline.bootstrap.cli._get_command_names") @patch("samcli.commands.pipeline.bootstrap.cli._load_saved_pipeline_user_arn") - @patch("samcli.commands.pipeline.bootstrap.cli.Stage") + @patch("samcli.commands.pipeline.bootstrap.cli.Environment") @patch("samcli.commands.pipeline.bootstrap.cli.GuidedContext") def test_bootstrap_will_not_try_loading_pipeline_user_if_already_provided( - self, guided_context_mock, stage_mock, load_saved_pipeline_user_arn_mock, get_command_names_mock + self, guided_context_mock, environment_mock, load_saved_pipeline_user_arn_mock, get_command_names_mock ): bootstrap_cli(**self.cli_context) load_saved_pipeline_user_arn_mock.assert_not_called() @patch("samcli.commands.pipeline.bootstrap.cli._get_command_names") @patch("samcli.commands.pipeline.bootstrap.cli._load_saved_pipeline_user_arn") - @patch("samcli.commands.pipeline.bootstrap.cli.Stage") + @patch("samcli.commands.pipeline.bootstrap.cli.Environment") @patch("samcli.commands.pipeline.bootstrap.cli.GuidedContext") def test_bootstrap_will_try_loading_pipeline_user_if_not_provided( - self, guided_context_mock, stage_mock, load_saved_pipeline_user_arn_mock, get_command_names_mock + self, guided_context_mock, environment_mock, load_saved_pipeline_user_arn_mock, get_command_names_mock ): self.cli_context["pipeline_user_arn"] = None bootstrap_cli(**self.cli_context) @@ -153,33 +154,33 @@ def test_bootstrap_will_try_loading_pipeline_user_if_not_provided( @patch("samcli.commands.pipeline.bootstrap.cli._get_command_names") @patch("samcli.commands.pipeline.bootstrap.cli._load_saved_pipeline_user_arn") - @patch("samcli.commands.pipeline.bootstrap.cli.Stage") + @patch("samcli.commands.pipeline.bootstrap.cli.Environment") @patch("samcli.commands.pipeline.bootstrap.cli.GuidedContext") - def test_stage_name_is_required_to_be_provided_in_case_of_non_interactive_mode( - self, guided_context_mock, stage_mock, load_saved_pipeline_user_arn_mock, get_command_names_mock + def test_environment_name_is_required_to_be_provided_in_case_of_non_interactive_mode( + self, guided_context_mock, environment_mock, load_saved_pipeline_user_arn_mock, get_command_names_mock ): self.cli_context["interactive"] = False - self.cli_context["stage_name"] = None + self.cli_context["environment_name"] = None with self.assertRaises(click.UsageError): bootstrap_cli(**self.cli_context) @patch("samcli.commands.pipeline.bootstrap.cli._get_command_names") @patch("samcli.commands.pipeline.bootstrap.cli._load_saved_pipeline_user_arn") - @patch("samcli.commands.pipeline.bootstrap.cli.Stage") + @patch("samcli.commands.pipeline.bootstrap.cli.Environment") @patch("samcli.commands.pipeline.bootstrap.cli.GuidedContext") - def test_stage_name_is_not_required_to_be_provided_in_case_of_interactive_mode( - self, guided_context_mock, stage_mock, load_saved_pipeline_user_arn_mock, get_command_names_mock + def test_environment_name_is_not_required_to_be_provided_in_case_of_interactive_mode( + self, guided_context_mock, environment_mock, load_saved_pipeline_user_arn_mock, get_command_names_mock ): self.cli_context["interactive"] = True - self.cli_context["stage_name"] = None + self.cli_context["environment_name"] = None bootstrap_cli(**self.cli_context) # No exception is thrown @patch("samcli.commands.pipeline.bootstrap.cli._get_command_names") @patch("samcli.commands.pipeline.bootstrap.cli._load_saved_pipeline_user_arn") - @patch("samcli.commands.pipeline.bootstrap.cli.Stage") + @patch("samcli.commands.pipeline.bootstrap.cli.Environment") @patch("samcli.commands.pipeline.bootstrap.cli.GuidedContext") def test_guided_context_will_be_enabled_or_disabled_based_on_the_interactive_mode( - self, guided_context_mock, stage_mock, load_saved_pipeline_user_arn_mock, get_command_names_mock + self, guided_context_mock, environment_mock, load_saved_pipeline_user_arn_mock, get_command_names_mock ): gc_instance = Mock() guided_context_mock.return_value = gc_instance @@ -192,20 +193,20 @@ def test_guided_context_will_be_enabled_or_disabled_based_on_the_interactive_mod @patch("samcli.commands.pipeline.bootstrap.cli._get_command_names") @patch("samcli.commands.pipeline.bootstrap.cli._load_saved_pipeline_user_arn") - @patch("samcli.commands.pipeline.bootstrap.cli.Stage") + @patch("samcli.commands.pipeline.bootstrap.cli.Environment") @patch("samcli.commands.pipeline.bootstrap.cli.GuidedContext") def test_bootstrapping_will_confirm_before_creating_the_resources_unless_the_user_choose_not_to( - self, guided_context_mock, stage_mock, load_saved_pipeline_user_arn_mock, get_command_names_mock + self, guided_context_mock, environment_mock, load_saved_pipeline_user_arn_mock, get_command_names_mock ): - stage_instance = Mock() - stage_mock.return_value = stage_instance + environment_instance = Mock() + environment_mock.return_value = environment_instance self.cli_context["confirm_changeset"] = False bootstrap_cli(**self.cli_context) - stage_instance.bootstrap.assert_called_once_with(confirm_changeset=False) - stage_instance.bootstrap.reset_mock() + environment_instance.bootstrap.assert_called_once_with(confirm_changeset=False) + environment_instance.bootstrap.reset_mock() self.cli_context["confirm_changeset"] = True bootstrap_cli(**self.cli_context) - stage_instance.bootstrap.assert_called_once_with(confirm_changeset=True) + environment_instance.bootstrap.assert_called_once_with(confirm_changeset=True) @patch("samcli.commands.pipeline.bootstrap.cli.SamConfig") @patch("samcli.commands.pipeline.bootstrap.cli._get_command_names") diff --git a/tests/unit/commands/pipeline/bootstrap/test_guided_context.py b/tests/unit/commands/pipeline/bootstrap/test_guided_context.py index dfdff1976b..47849de404 100644 --- a/tests/unit/commands/pipeline/bootstrap/test_guided_context.py +++ b/tests/unit/commands/pipeline/bootstrap/test_guided_context.py @@ -3,7 +3,7 @@ from samcli.commands.pipeline.bootstrap.guided_context import GuidedContext -ANY_STAGE_NAME = "ANY_STAGE_NAME" +ANY_ENVIRONMENT_NAME = "ANY_ENVIRONMENT_NAME" ANY_PIPELINE_USER_ARN = "ANY_PIPELINE_USER_ARN" ANY_PIPELINE_EXECUTION_ROLE_ARN = "ANY_PIPELINE_EXECUTION_ROLE_ARN" ANY_CLOUDFORMATION_EXECUTION_ROLE_ARN = "ANY_CLOUDFORMATION_EXECUTION_ROLE_ARN" @@ -17,7 +17,7 @@ class TestGuidedContext(TestCase): @patch("samcli.commands.pipeline.bootstrap.guided_context.click") def test_guided_context_will_not_prompt_for_fields_that_are_already_provided(self, click_mock): gc: GuidedContext = GuidedContext( - stage_name=ANY_STAGE_NAME, + environment_name=ANY_ENVIRONMENT_NAME, pipeline_user_arn=ANY_PIPELINE_USER_ARN, pipeline_execution_role_arn=ANY_PIPELINE_EXECUTION_ROLE_ARN, cloudformation_execution_role_arn=ANY_CLOUDFORMATION_EXECUTION_ROLE_ARN, @@ -35,7 +35,7 @@ def test_guided_context_will_prompt_for_fields_that_are_not_provided(self, click ecr_repo_arn=ANY_ECR_REPO_ARN # Exclude ECR repo, it has its own detailed test below ) gc.run() - self.assertTrue(self.did_prompt_text_like("Stage Name", click_mock.prompt)) + self.assertTrue(self.did_prompt_text_like("Environment Name", click_mock.prompt)) self.assertTrue(self.did_prompt_text_like("Pipeline user", click_mock.prompt)) self.assertTrue(self.did_prompt_text_like("Pipeline execution role", click_mock.prompt)) self.assertTrue(self.did_prompt_text_like("CloudFormation execution role", click_mock.prompt)) @@ -50,7 +50,7 @@ def test_guided_context_will_not_prompt_for_not_provided_ecr_repo_if_no_ecr_repo # 2 - Yes, I need a help creating one # 3 - I already have an ECR repo gc_without_ecr_info: GuidedContext = GuidedContext( - stage_name=ANY_STAGE_NAME, + environment_name=ANY_ENVIRONMENT_NAME, pipeline_user_arn=ANY_PIPELINE_USER_ARN, pipeline_execution_role_arn=ANY_PIPELINE_EXECUTION_ROLE_ARN, cloudformation_execution_role_arn=ANY_CLOUDFORMATION_EXECUTION_ROLE_ARN, diff --git a/tests/unit/commands/pipeline/init/test_pipeline_templates_manifest.py b/tests/unit/commands/pipeline/init/test_pipeline_templates_manifest.py index a1b765bb5c..d35541c3f6 100644 --- a/tests/unit/commands/pipeline/init/test_pipeline_templates_manifest.py +++ b/tests/unit/commands/pipeline/init/test_pipeline_templates_manifest.py @@ -18,9 +18,9 @@ NotProviders: - Jenkins Templates: - - NotName: jenkins-two-stages-pipeline + - NotName: jenkins-two-environments-pipeline provider: Jenkins - location: templates/cookiecutter-jenkins-two-stages-pipeline + location: templates/cookiecutter-jenkins-two-environments-pipeline """ VALID_MANIFEST = """ @@ -32,15 +32,15 @@ - displayName: Github Actions id: github-actions templates: - - displayName: jenkins-two-stages-pipeline + - displayName: jenkins-two-environments-pipeline provider: jenkins - location: templates/cookiecutter-jenkins-two-stages-pipeline - - displayName: gitlab-two-stages-pipeline + location: templates/cookiecutter-jenkins-two-environments-pipeline + - displayName: gitlab-two-environments-pipeline provider: gitlab - location: templates/cookiecutter-gitlab-two-stages-pipeline - - displayName: Github-Actions-two-stages-pipeline + location: templates/cookiecutter-gitlab-two-environments-pipeline + - displayName: Github-Actions-two-environments-pipeline provider: github-actions - location: templates/cookiecutter-github-actions-two-stages-pipeline + location: templates/cookiecutter-github-actions-two-environments-pipeline """ @@ -77,6 +77,6 @@ def test_manifest_happy_case(self): self.assertEquals(gitlab_provider.display_name, "Gitlab CI/CD") self.assertEquals(len(manifest.templates), 3) gitlab_template: PipelineTemplateMetadata = next(t for t in manifest.templates if t.provider == "gitlab") - self.assertEquals(gitlab_template.display_name, "gitlab-two-stages-pipeline") + self.assertEquals(gitlab_template.display_name, "gitlab-two-environments-pipeline") self.assertEquals(gitlab_template.provider, "gitlab") - self.assertEquals(gitlab_template.location, "templates/cookiecutter-gitlab-two-stages-pipeline") + self.assertEquals(gitlab_template.location, "templates/cookiecutter-gitlab-two-environments-pipeline") diff --git a/tests/unit/lib/pipeline/bootstrap/test_stage.py b/tests/unit/lib/pipeline/bootstrap/test_environment.py similarity index 55% rename from tests/unit/lib/pipeline/bootstrap/test_stage.py rename to tests/unit/lib/pipeline/bootstrap/test_environment.py index 348b5acd88..5f8a77f5e8 100644 --- a/tests/unit/lib/pipeline/bootstrap/test_stage.py +++ b/tests/unit/lib/pipeline/bootstrap/test_environment.py @@ -1,9 +1,9 @@ from unittest import TestCase from unittest.mock import Mock, patch, call -from samcli.lib.pipeline.bootstrap.stage import Stage +from samcli.lib.pipeline.bootstrap.environment import Environment -ANY_STAGE_NAME = "ANY_STAGE_NAME" +ANY_ENVIRONMENT_NAME = "ANY_ENVIRONMENT_NAME" ANY_PIPELINE_USER_ARN = "ANY_PIPELINE_USER_ARN" ANY_PIPELINE_EXECUTION_ROLE_ARN = "ANY_PIPELINE_EXECUTION_ROLE_ARN" ANY_CLOUDFORMATION_EXECUTION_ROLE_ARN = "ANY_CLOUDFORMATION_EXECUTION_ROLE_ARN" @@ -12,72 +12,72 @@ ANY_ARN = "ANY_ARN" -class TestStage(TestCase): - def test_stage_name_is_the_only_required_field_to_initialize_a_stage(self): - stage: Stage = Stage(name=ANY_STAGE_NAME) - self.assertEqual(stage.name, ANY_STAGE_NAME) - self.assertIsNone(stage.aws_profile) - self.assertIsNone(stage.aws_region) - self.assertIsNotNone(stage.pipeline_user) - self.assertIsNotNone(stage.pipeline_execution_role) - self.assertIsNotNone(stage.cloudformation_execution_role) - self.assertIsNotNone(stage.artifacts_bucket) - self.assertIsNotNone(stage.ecr_repo) +class TestEnvironment(TestCase): + def test_environment_name_is_the_only_required_field_to_initialize_an_environment(self): + environment: Environment = Environment(name=ANY_ENVIRONMENT_NAME) + self.assertEqual(environment.name, ANY_ENVIRONMENT_NAME) + self.assertIsNone(environment.aws_profile) + self.assertIsNone(environment.aws_region) + self.assertIsNotNone(environment.pipeline_user) + self.assertIsNotNone(environment.pipeline_execution_role) + self.assertIsNotNone(environment.cloudformation_execution_role) + self.assertIsNotNone(environment.artifacts_bucket) + self.assertIsNotNone(environment.ecr_repo) with self.assertRaises(TypeError): - stage = Stage() + environment = Environment() def test_did_user_provide_all_required_resources_when_not_all_resources_are_provided(self): - stage: Stage = Stage(name=ANY_STAGE_NAME) - self.assertFalse(stage.did_user_provide_all_required_resources()) - stage: Stage = Stage(name=ANY_STAGE_NAME, pipeline_user_arn=ANY_PIPELINE_USER_ARN) - self.assertFalse(stage.did_user_provide_all_required_resources()) - stage: Stage = Stage( - name=ANY_STAGE_NAME, + environment: Environment = Environment(name=ANY_ENVIRONMENT_NAME) + self.assertFalse(environment.did_user_provide_all_required_resources()) + environment: Environment = Environment(name=ANY_ENVIRONMENT_NAME, pipeline_user_arn=ANY_PIPELINE_USER_ARN) + self.assertFalse(environment.did_user_provide_all_required_resources()) + environment: Environment = Environment( + name=ANY_ENVIRONMENT_NAME, pipeline_user_arn=ANY_PIPELINE_USER_ARN, pipeline_execution_role_arn=ANY_PIPELINE_EXECUTION_ROLE_ARN, ) - self.assertFalse(stage.did_user_provide_all_required_resources()) - stage: Stage = Stage( - name=ANY_STAGE_NAME, + self.assertFalse(environment.did_user_provide_all_required_resources()) + environment: Environment = Environment( + name=ANY_ENVIRONMENT_NAME, pipeline_user_arn=ANY_PIPELINE_USER_ARN, pipeline_execution_role_arn=ANY_PIPELINE_EXECUTION_ROLE_ARN, cloudformation_execution_role_arn=ANY_CLOUDFORMATION_EXECUTION_ROLE_ARN, ) - self.assertFalse(stage.did_user_provide_all_required_resources()) - stage: Stage = Stage( - name=ANY_STAGE_NAME, + self.assertFalse(environment.did_user_provide_all_required_resources()) + environment: Environment = Environment( + name=ANY_ENVIRONMENT_NAME, pipeline_user_arn=ANY_PIPELINE_USER_ARN, pipeline_execution_role_arn=ANY_PIPELINE_EXECUTION_ROLE_ARN, cloudformation_execution_role_arn=ANY_CLOUDFORMATION_EXECUTION_ROLE_ARN, artifacts_bucket_arn=ANY_ARTIFACTS_BUCKET_ARN, create_ecr_repo=True, ) - self.assertFalse(stage.did_user_provide_all_required_resources()) + self.assertFalse(environment.did_user_provide_all_required_resources()) def test_did_user_provide_all_required_resources_ignore_ecr_repo_if_it_is_not_required(self): - stage: Stage = Stage( - name=ANY_STAGE_NAME, + environment: Environment = Environment( + name=ANY_ENVIRONMENT_NAME, pipeline_user_arn=ANY_PIPELINE_USER_ARN, pipeline_execution_role_arn=ANY_PIPELINE_EXECUTION_ROLE_ARN, cloudformation_execution_role_arn=ANY_CLOUDFORMATION_EXECUTION_ROLE_ARN, artifacts_bucket_arn=ANY_ARTIFACTS_BUCKET_ARN, create_ecr_repo=False, ) - self.assertTrue(stage.did_user_provide_all_required_resources()) + self.assertTrue(environment.did_user_provide_all_required_resources()) def test_did_user_provide_all_required_resources_when_ecr_repo_is_required(self): - stage: Stage = Stage( - name=ANY_STAGE_NAME, + environment: Environment = Environment( + name=ANY_ENVIRONMENT_NAME, pipeline_user_arn=ANY_PIPELINE_USER_ARN, pipeline_execution_role_arn=ANY_PIPELINE_EXECUTION_ROLE_ARN, cloudformation_execution_role_arn=ANY_CLOUDFORMATION_EXECUTION_ROLE_ARN, artifacts_bucket_arn=ANY_ARTIFACTS_BUCKET_ARN, create_ecr_repo=True, ) - self.assertFalse(stage.did_user_provide_all_required_resources()) - stage: Stage = Stage( - name=ANY_STAGE_NAME, + self.assertFalse(environment.did_user_provide_all_required_resources()) + environment: Environment = Environment( + name=ANY_ENVIRONMENT_NAME, pipeline_user_arn=ANY_PIPELINE_USER_ARN, pipeline_execution_role_arn=ANY_PIPELINE_EXECUTION_ROLE_ARN, cloudformation_execution_role_arn=ANY_CLOUDFORMATION_EXECUTION_ROLE_ARN, @@ -85,91 +85,91 @@ def test_did_user_provide_all_required_resources_when_ecr_repo_is_required(self) create_ecr_repo=True, ecr_repo_arn=ANY_ECR_REPO_ARN, ) - self.assertTrue(stage.did_user_provide_all_required_resources()) + self.assertTrue(environment.did_user_provide_all_required_resources()) - @patch("samcli.lib.pipeline.bootstrap.stage.click") - @patch("samcli.lib.pipeline.bootstrap.stage.manage_stack") - def test_did_user_provide_all_required_resources_returns_false_if_the_stage_was_initialized_without_any_of_the_resources_even_if_fulfilled_after_bootstrap( + @patch("samcli.lib.pipeline.bootstrap.environment.click") + @patch("samcli.lib.pipeline.bootstrap.environment.manage_stack") + def test_did_user_provide_all_required_resources_returns_false_if_the_environment_was_initialized_without_any_of_the_resources_even_if_fulfilled_after_bootstrap( self, manage_stack_mock, click_mock ): # setup stack_output = Mock() stack_output.get.return_value = ANY_ARN manage_stack_mock.return_value = stack_output - stage: Stage = Stage(name=ANY_STAGE_NAME) + environment: Environment = Environment(name=ANY_ENVIRONMENT_NAME) - self.assertFalse(stage.did_user_provide_all_required_resources()) + self.assertFalse(environment.did_user_provide_all_required_resources()) - stage.bootstrap(confirm_changeset=False) + environment.bootstrap(confirm_changeset=False) # After bootstrapping, all the resources should be fulfilled - self.assertEqual(ANY_ARN, stage.pipeline_user.arn) - self.assertEqual(ANY_ARN, stage.pipeline_execution_role.arn) - self.assertEqual(ANY_ARN, stage.cloudformation_execution_role.arn) - self.assertEqual(ANY_ARN, stage.artifacts_bucket.arn) - self.assertEqual(ANY_ARN, stage.ecr_repo.arn) + self.assertEqual(ANY_ARN, environment.pipeline_user.arn) + self.assertEqual(ANY_ARN, environment.pipeline_execution_role.arn) + self.assertEqual(ANY_ARN, environment.cloudformation_execution_role.arn) + self.assertEqual(ANY_ARN, environment.artifacts_bucket.arn) + self.assertEqual(ANY_ARN, environment.ecr_repo.arn) # although all of the resources got fulfilled, `did_user_provide_all_required_resources` should return false # as these resources are not provided by the user - self.assertFalse(stage.did_user_provide_all_required_resources()) + self.assertFalse(environment.did_user_provide_all_required_resources()) - @patch("samcli.lib.pipeline.bootstrap.stage.click") - @patch("samcli.lib.pipeline.bootstrap.stage.manage_stack") - @patch.object(Stage, "did_user_provide_all_required_resources") + @patch("samcli.lib.pipeline.bootstrap.environment.click") + @patch("samcli.lib.pipeline.bootstrap.environment.manage_stack") + @patch.object(Environment, "did_user_provide_all_required_resources") def test_bootstrap_will_not_deploy_the_cfn_template_if_all_resources_are_already_provided( self, did_user_provide_all_required_resources_mock, manage_stack_mock, click_mock ): did_user_provide_all_required_resources_mock.return_value = True - stage: Stage = Stage(name=ANY_STAGE_NAME) - stage.bootstrap(confirm_changeset=False) + environment: Environment = Environment(name=ANY_ENVIRONMENT_NAME) + environment.bootstrap(confirm_changeset=False) manage_stack_mock.assert_not_called() - @patch("samcli.lib.pipeline.bootstrap.stage.click") - @patch("samcli.lib.pipeline.bootstrap.stage.manage_stack") + @patch("samcli.lib.pipeline.bootstrap.environment.click") + @patch("samcli.lib.pipeline.bootstrap.environment.manage_stack") def test_bootstrap_will_confirm_before_deploying_unless_confirm_changeset_is_disabled( self, manage_stack_mock, click_mock ): click_mock.confirm.return_value = False - stage: Stage = Stage(name=ANY_STAGE_NAME) - stage.bootstrap(confirm_changeset=False) + environment: Environment = Environment(name=ANY_ENVIRONMENT_NAME) + environment.bootstrap(confirm_changeset=False) click_mock.confirm.assert_not_called() manage_stack_mock.assert_called_once() manage_stack_mock.reset_mock() - stage.bootstrap(confirm_changeset=True) + environment.bootstrap(confirm_changeset=True) click_mock.confirm.assert_called_once() manage_stack_mock.assert_not_called() # As the user choose to not confirm - @patch("samcli.lib.pipeline.bootstrap.stage.click") - @patch("samcli.lib.pipeline.bootstrap.stage.manage_stack") + @patch("samcli.lib.pipeline.bootstrap.environment.click") + @patch("samcli.lib.pipeline.bootstrap.environment.manage_stack") def test_bootstrap_will_not_deploy_the_cfn_template_if_the_user_did_not_confirm( self, manage_stack_mock, click_mock ): click_mock.confirm.return_value = False - stage: Stage = Stage(name=ANY_STAGE_NAME) - stage.bootstrap(confirm_changeset=True) + environment: Environment = Environment(name=ANY_ENVIRONMENT_NAME) + environment.bootstrap(confirm_changeset=True) manage_stack_mock.assert_not_called() - @patch("samcli.lib.pipeline.bootstrap.stage.click") - @patch("samcli.lib.pipeline.bootstrap.stage.manage_stack") + @patch("samcli.lib.pipeline.bootstrap.environment.click") + @patch("samcli.lib.pipeline.bootstrap.environment.manage_stack") def test_bootstrap_will_deploy_the_cfn_template_if_the_user_did_confirm(self, manage_stack_mock, click_mock): click_mock.confirm.return_value = True - stage: Stage = Stage(name=ANY_STAGE_NAME) - stage.bootstrap(confirm_changeset=True) + environment: Environment = Environment(name=ANY_ENVIRONMENT_NAME) + environment.bootstrap(confirm_changeset=True) manage_stack_mock.assert_called_once() - @patch("samcli.lib.pipeline.bootstrap.stage.click") - @patch("samcli.lib.pipeline.bootstrap.stage.manage_stack") + @patch("samcli.lib.pipeline.bootstrap.environment.click") + @patch("samcli.lib.pipeline.bootstrap.environment.manage_stack") def test_bootstrap_will_pass_arns_of_all_user_provided_resources_any_empty_strings_for_other_resources_to_the_cfn_stack( self, manage_stack_mock, click_mock ): click_mock.confirm.return_value = True - stage: Stage = Stage( - name=ANY_STAGE_NAME, + environment: Environment = Environment( + name=ANY_ENVIRONMENT_NAME, pipeline_user_arn=ANY_PIPELINE_USER_ARN, artifacts_bucket_arn=ANY_ARTIFACTS_BUCKET_ARN, create_ecr_repo=True, ecr_repo_arn=ANY_ECR_REPO_ARN, ) - stage.bootstrap() + environment.bootstrap() manage_stack_mock.assert_called_once() args, kwargs = manage_stack_mock.call_args_list[0] expected_parameter_overrides = { @@ -183,138 +183,142 @@ def test_bootstrap_will_pass_arns_of_all_user_provided_resources_any_empty_strin } self.assertEqual(expected_parameter_overrides, kwargs["parameter_overrides"]) - @patch("samcli.lib.pipeline.bootstrap.stage.click") - @patch("samcli.lib.pipeline.bootstrap.stage.manage_stack") + @patch("samcli.lib.pipeline.bootstrap.environment.click") + @patch("samcli.lib.pipeline.bootstrap.environment.manage_stack") def test_bootstrap_will_fullfill_all_resource_arns(self, manage_stack_mock, click_mock): # setup stack_output = Mock() stack_output.get.return_value = ANY_ARN manage_stack_mock.return_value = stack_output - stage: Stage = Stage(name=ANY_STAGE_NAME) + environment: Environment = Environment(name=ANY_ENVIRONMENT_NAME) click_mock.confirm.return_value = True # verify resources' ARNS are empty - self.assertIsNone(stage.pipeline_user.arn) - self.assertIsNone(stage.pipeline_execution_role.arn) - self.assertIsNone(stage.cloudformation_execution_role.arn) - self.assertIsNone(stage.artifacts_bucket.arn) + self.assertIsNone(environment.pipeline_user.arn) + self.assertIsNone(environment.pipeline_execution_role.arn) + self.assertIsNone(environment.cloudformation_execution_role.arn) + self.assertIsNone(environment.artifacts_bucket.arn) # trigger - stage.bootstrap() + environment.bootstrap() # verify manage_stack_mock.assert_called_once() - self.assertEqual(ANY_ARN, stage.pipeline_user.arn) - self.assertEqual(ANY_ARN, stage.pipeline_execution_role.arn) - self.assertEqual(ANY_ARN, stage.cloudformation_execution_role.arn) - self.assertEqual(ANY_ARN, stage.artifacts_bucket.arn) + self.assertEqual(ANY_ARN, environment.pipeline_user.arn) + self.assertEqual(ANY_ARN, environment.pipeline_execution_role.arn) + self.assertEqual(ANY_ARN, environment.cloudformation_execution_role.arn) + self.assertEqual(ANY_ARN, environment.artifacts_bucket.arn) - @patch("samcli.lib.pipeline.bootstrap.stage.SamConfig") + @patch("samcli.lib.pipeline.bootstrap.environment.SamConfig") def test_save_config_escapes_none_resources(self, samconfig_mock): cmd_names = ["any", "commands"] samconfig_instance_mock = Mock() samconfig_mock.return_value = samconfig_instance_mock - stage: Stage = Stage(name=ANY_STAGE_NAME) + environment: Environment = Environment(name=ANY_ENVIRONMENT_NAME) expected_calls = [] - self.trigger_and_assert_save_config_calls(stage, cmd_names, expected_calls, samconfig_instance_mock.put) + self.trigger_and_assert_save_config_calls(environment, cmd_names, expected_calls, samconfig_instance_mock.put) - stage.pipeline_user.arn = ANY_PIPELINE_USER_ARN + environment.pipeline_user.arn = ANY_PIPELINE_USER_ARN expected_calls.append( call(cmd_names=cmd_names, section="parameters", key="pipeline_user", value=ANY_PIPELINE_USER_ARN) ) - self.trigger_and_assert_save_config_calls(stage, cmd_names, expected_calls, samconfig_instance_mock.put) + self.trigger_and_assert_save_config_calls(environment, cmd_names, expected_calls, samconfig_instance_mock.put) - stage.pipeline_execution_role.arn = ANY_PIPELINE_EXECUTION_ROLE_ARN + environment.pipeline_execution_role.arn = ANY_PIPELINE_EXECUTION_ROLE_ARN expected_calls.append( call( cmd_names=cmd_names, section="parameters", - env="ANY_STAGE_NAME", + env=ANY_ENVIRONMENT_NAME, key="pipeline_execution_role", value=ANY_PIPELINE_EXECUTION_ROLE_ARN, ) ) - self.trigger_and_assert_save_config_calls(stage, cmd_names, expected_calls, samconfig_instance_mock.put) + self.trigger_and_assert_save_config_calls(environment, cmd_names, expected_calls, samconfig_instance_mock.put) - stage.cloudformation_execution_role.arn = ANY_CLOUDFORMATION_EXECUTION_ROLE_ARN + environment.cloudformation_execution_role.arn = ANY_CLOUDFORMATION_EXECUTION_ROLE_ARN expected_calls.append( call( cmd_names=cmd_names, section="parameters", - env="ANY_STAGE_NAME", + env=ANY_ENVIRONMENT_NAME, key="cloudformation_execution_role", value=ANY_CLOUDFORMATION_EXECUTION_ROLE_ARN, ) ) - self.trigger_and_assert_save_config_calls(stage, cmd_names, expected_calls, samconfig_instance_mock.put) + self.trigger_and_assert_save_config_calls(environment, cmd_names, expected_calls, samconfig_instance_mock.put) - stage.artifacts_bucket.arn = "arn:aws:s3:::artifact_bucket_name" + environment.artifacts_bucket.arn = "arn:aws:s3:::artifact_bucket_name" expected_calls.append( call( cmd_names=cmd_names, section="parameters", - env="ANY_STAGE_NAME", + env=ANY_ENVIRONMENT_NAME, key="artifacts_bucket", value="artifact_bucket_name", ) ) - self.trigger_and_assert_save_config_calls(stage, cmd_names, expected_calls, samconfig_instance_mock.put) + self.trigger_and_assert_save_config_calls(environment, cmd_names, expected_calls, samconfig_instance_mock.put) - stage.ecr_repo.arn = "arn:aws:ecr:us-east-2:111111111111:repository/ecr_repo_name" + environment.ecr_repo.arn = "arn:aws:ecr:us-east-2:111111111111:repository/ecr_repo_name" expected_calls.append( call( cmd_names=cmd_names, section="parameters", - env="ANY_STAGE_NAME", + env=ANY_ENVIRONMENT_NAME, key="ecr_repo", value="111111111111.dkr.ecr.us-east-2.amazonaws.com/ecr_repo_name", ) ) - self.trigger_and_assert_save_config_calls(stage, cmd_names, expected_calls, samconfig_instance_mock.put) + self.trigger_and_assert_save_config_calls(environment, cmd_names, expected_calls, samconfig_instance_mock.put) - def trigger_and_assert_save_config_calls(self, stage, cmd_names, expected_calls, samconfig_put_mock): - stage.save_config(config_dir="any_config_dir", filename="any_pipeline.toml", cmd_names=cmd_names) + def trigger_and_assert_save_config_calls(self, environment, cmd_names, expected_calls, samconfig_put_mock): + environment.save_config(config_dir="any_config_dir", filename="any_pipeline.toml", cmd_names=cmd_names) self.assertEqual(len(expected_calls), samconfig_put_mock.call_count) samconfig_put_mock.assert_has_calls(expected_calls) samconfig_put_mock.reset_mock() - @patch("samcli.lib.pipeline.bootstrap.stage.SamConfig") + @patch("samcli.lib.pipeline.bootstrap.environment.SamConfig") def test_save_config_ignores_exceptions_thrown_while_calculating_artifacts_bucket_name(self, samconfig_mock): samconfig_instance_mock = Mock() samconfig_mock.return_value = samconfig_instance_mock - stage: Stage = Stage(name=ANY_STAGE_NAME, artifacts_bucket_arn="invalid ARN") + environment: Environment = Environment(name=ANY_ENVIRONMENT_NAME, artifacts_bucket_arn="invalid ARN") # calling artifacts_bucket.name() during save_config() will raise a ValueError exception, we need to make sure # this exception is swallowed so that other configs can be safely saved to the pipelineconfig.toml file - stage.save_config(config_dir="any_config_dir", filename="any_pipeline.toml", cmd_names=["any", "commands"]) + environment.save_config( + config_dir="any_config_dir", filename="any_pipeline.toml", cmd_names=["any", "commands"] + ) - @patch("samcli.lib.pipeline.bootstrap.stage.SamConfig") + @patch("samcli.lib.pipeline.bootstrap.environment.SamConfig") def test_save_config_ignores_exceptions_thrown_while_calculating_ecr_repo_uri(self, samconfig_mock): samconfig_instance_mock = Mock() samconfig_mock.return_value = samconfig_instance_mock - stage: Stage = Stage(name=ANY_STAGE_NAME, ecr_repo_arn="invalid ARN") + environment: Environment = Environment(name=ANY_ENVIRONMENT_NAME, ecr_repo_arn="invalid ARN") # calling ecr_repo.get_uri() during save_config() will raise a ValueError exception, we need to make sure # this exception is swallowed so that other configs can be safely saved to the pipelineconfig.toml file - stage.save_config(config_dir="any_config_dir", filename="any_pipeline.toml", cmd_names=["any", "commands"]) + environment.save_config( + config_dir="any_config_dir", filename="any_pipeline.toml", cmd_names=["any", "commands"] + ) - @patch.object(Stage, "save_config") + @patch.object(Environment, "save_config") def test_save_config_safe(self, save_config_mock): save_config_mock.side_effect = Exception - stage: Stage = Stage(name=ANY_STAGE_NAME) - stage.save_config_safe(config_dir="any_config_dir", filename="any_pipeline.toml", cmd_names=["commands"]) + environment: Environment = Environment(name=ANY_ENVIRONMENT_NAME) + environment.save_config_safe(config_dir="any_config_dir", filename="any_pipeline.toml", cmd_names=["commands"]) save_config_mock.assert_called_once_with("any_config_dir", "any_pipeline.toml", ["commands"]) - @patch("samcli.lib.pipeline.bootstrap.stage.click") + @patch("samcli.lib.pipeline.bootstrap.environment.click") def test_print_resources_summary_when_no_resources_provided_by_the_user(self, click_mock): - stage: Stage = Stage(name=ANY_STAGE_NAME) - stage.print_resources_summary() + environment: Environment = Environment(name=ANY_ENVIRONMENT_NAME) + environment.print_resources_summary() self.assert_summary_has_a_message_like("We have created the following resources", click_mock.secho) self.assert_summary_does_not_have_a_message_like("You provided the following resources", click_mock.secho) - @patch("samcli.lib.pipeline.bootstrap.stage.click") + @patch("samcli.lib.pipeline.bootstrap.environment.click") def test_print_resources_summary_when_all_resources_are_provided_by_the_user(self, click_mock): - stage: Stage = Stage( - name=ANY_STAGE_NAME, + environment: Environment = Environment( + name=ANY_ENVIRONMENT_NAME, pipeline_user_arn=ANY_PIPELINE_USER_ARN, pipeline_execution_role_arn=ANY_PIPELINE_EXECUTION_ROLE_ARN, cloudformation_execution_role_arn=ANY_CLOUDFORMATION_EXECUTION_ROLE_ARN, @@ -322,48 +326,50 @@ def test_print_resources_summary_when_all_resources_are_provided_by_the_user(sel create_ecr_repo=True, ecr_repo_arn=ANY_ECR_REPO_ARN, ) - stage.print_resources_summary() + environment.print_resources_summary() self.assert_summary_does_not_have_a_message_like("We have created the following resources", click_mock.secho) self.assert_summary_has_a_message_like("You provided the following resources", click_mock.secho) - @patch("samcli.lib.pipeline.bootstrap.stage.click") + @patch("samcli.lib.pipeline.bootstrap.environment.click") def test_print_resources_summary_when_some_resources_are_provided_by_the_user(self, click_mock): - stage: Stage = Stage( - name=ANY_STAGE_NAME, + environment: Environment = Environment( + name=ANY_ENVIRONMENT_NAME, pipeline_user_arn=ANY_PIPELINE_USER_ARN, artifacts_bucket_arn=ANY_ARTIFACTS_BUCKET_ARN, create_ecr_repo=True, ecr_repo_arn=ANY_ECR_REPO_ARN, ) - stage.print_resources_summary() + environment.print_resources_summary() self.assert_summary_has_a_message_like("We have created the following resources", click_mock.secho) self.assert_summary_has_a_message_like("You provided the following resources", click_mock.secho) - @patch("samcli.lib.pipeline.bootstrap.stage.click") + @patch("samcli.lib.pipeline.bootstrap.environment.click") def test_print_resources_summary_prints_the_credentials_of_the_pipeline_user_iff_not_provided_by_the_user( self, click_mock ): - stage_with_provided_pipeline_user: Stage = Stage(name=ANY_STAGE_NAME, pipeline_user_arn=ANY_PIPELINE_USER_ARN) - stage_with_provided_pipeline_user.print_resources_summary() + environment_with_provided_pipeline_user: Environment = Environment( + name=ANY_ENVIRONMENT_NAME, pipeline_user_arn=ANY_PIPELINE_USER_ARN + ) + environment_with_provided_pipeline_user.print_resources_summary() self.assert_summary_does_not_have_a_message_like("ACCESS_KEY_ID", click_mock.secho) self.assert_summary_does_not_have_a_message_like("SECRET_ACCESS_KEY", click_mock.secho) click_mock.secho.reset_mock() - stage_without_provided_pipeline_user: Stage = Stage(name=ANY_STAGE_NAME) - stage_without_provided_pipeline_user.print_resources_summary() + environment_without_provided_pipeline_user: Environment = Environment(name=ANY_ENVIRONMENT_NAME) + environment_without_provided_pipeline_user.print_resources_summary() self.assert_summary_has_a_message_like("ACCESS_KEY_ID", click_mock.secho) self.assert_summary_has_a_message_like("SECRET_ACCESS_KEY", click_mock.secho) def assert_summary_has_a_message_like(self, msg, click_secho_mock): self.assertTrue( self.does_summary_have_a_message_like(msg, click_secho_mock), - msg=f'stage resources summary does not include "{msg}" which is unexpected', + msg=f'environment resources summary does not include "{msg}" which is unexpected', ) def assert_summary_does_not_have_a_message_like(self, msg, click_secho_mock): self.assertFalse( self.does_summary_have_a_message_like(msg, click_secho_mock), - msg=f'stage resources summary includes "{msg}" which is unexpected', + msg=f'environment resources summary includes "{msg}" which is unexpected', ) @staticmethod