diff --git a/samcli/commands/_utils/options.py b/samcli/commands/_utils/options.py index cc849f351b..62a9021e5e 100644 --- a/samcli/commands/_utils/options.py +++ b/samcli/commands/_utils/options.py @@ -474,19 +474,20 @@ def stack_name_option(f, required=False, callback=None): return stack_name_click_option(required, callback)(f) -def s3_bucket_click_option(guided): - callback = None if guided else partial(artifact_callback, artifact=ZIP) +def s3_bucket_click_option(disable_callback): + callback = None if disable_callback else partial(artifact_callback, artifact=ZIP) + return click.option( "--s3-bucket", required=False, - callback=callback, help="The name of the S3 bucket where this command uploads the artifacts that are referenced in your template.", + callback=callback, ) @parameterized_option -def s3_bucket_option(f, guided=False): - return s3_bucket_click_option(guided)(f) +def s3_bucket_option(f, disable_callback=False): + return s3_bucket_click_option(disable_callback)(f) def build_dir_click_option(): diff --git a/samcli/commands/deploy/command.py b/samcli/commands/deploy/command.py index 1a31d0d6f8..17f2358eab 100644 --- a/samcli/commands/deploy/command.py +++ b/samcli/commands/deploy/command.py @@ -109,7 +109,7 @@ help="Preserves the state of previously provisioned resources when an operation fails.", ) @stack_name_option(callback=guided_deploy_stack_name) # pylint: disable=E1120 -@s3_bucket_option(guided=True) # pylint: disable=E1120 +@s3_bucket_option(disable_callback=True) # pylint: disable=E1120 @image_repository_option @image_repositories_option @force_upload_option diff --git a/samcli/commands/sync/command.py b/samcli/commands/sync/command.py index 7270817818..5aaa205af1 100644 --- a/samcli/commands/sync/command.py +++ b/samcli/commands/sync/command.py @@ -8,6 +8,7 @@ from samcli.cli.main import pass_context, common_options as cli_framework_options, aws_creds_options, print_cmdline_args from samcli.commands._utils.cdk_support_decorators import unsupported_command_cdk from samcli.commands._utils.options import ( + s3_bucket_option, template_option_without_build, parameter_override_option, capabilities_option, @@ -137,6 +138,7 @@ @base_dir_option @image_repository_option @image_repositories_option +@s3_bucket_option(disable_callback=True) # pylint: disable=E1120 @s3_prefix_option @kms_key_id_option @role_arn_option @@ -168,6 +170,7 @@ def cli( parameter_overrides: dict, image_repository: str, image_repositories: Optional[Tuple[str]], + s3_bucket: str, s3_prefix: str, kms_key_id: str, capabilities: Optional[List[str]], @@ -199,6 +202,7 @@ def cli( mode, image_repository, image_repositories, + s3_bucket, s3_prefix, kms_key_id, capabilities, @@ -226,6 +230,7 @@ def do_cli( mode: Optional[str], image_repository: str, image_repositories: Optional[Tuple[str]], + s3_bucket: str, s3_prefix: str, kms_key_id: str, capabilities: Optional[List[str]], @@ -255,7 +260,7 @@ def do_cli( set_experimental(ExperimentalFlag.Accelerate) update_experimental_context() - s3_bucket = manage_stack(profile=profile, region=region) + s3_bucket_name = s3_bucket or manage_stack(profile=profile, region=region) build_dir = DEFAULT_BUILD_DIR_WITH_AUTO_DEPENDENCY_LAYER if dependency_layer else DEFAULT_BUILD_DIR LOG.debug("Using build directory as %s", build_dir) @@ -282,7 +287,7 @@ def do_cli( with osutils.tempfile_platform_independent() as output_template_file: with PackageContext( template_file=built_template, - s3_bucket=s3_bucket, + s3_bucket=s3_bucket_name, image_repository=image_repository, image_repositories=image_repositories, s3_prefix=s3_prefix, @@ -308,7 +313,7 @@ def do_cli( with DeployContext( template_file=output_template_file.name, stack_name=stack_name, - s3_bucket=s3_bucket, + s3_bucket=s3_bucket_name, image_repository=image_repository, image_repositories=image_repositories, no_progressbar=True, diff --git a/tests/integration/sync/sync_integ_base.py b/tests/integration/sync/sync_integ_base.py index b5bab04e8a..1aa6aa4b31 100644 --- a/tests/integration/sync/sync_integ_base.py +++ b/tests/integration/sync/sync_integ_base.py @@ -221,6 +221,7 @@ def get_sync_command_list( base_dir=None, image_repository=None, image_repositories=None, + s3_bucket=None, s3_prefix=None, kms_key_id=None, capabilities=None, @@ -262,6 +263,8 @@ def get_sync_command_list( command_list += ["--image-repository", str(image_repository)] if image_repositories: command_list += ["--image-repositories", str(image_repositories)] + if s3_bucket: + command_list += ["--s3-bucket", str(s3_bucket)] if s3_prefix: command_list += ["--s3-prefix", str(s3_prefix)] if kms_key_id: diff --git a/tests/integration/sync/test_sync_infra.py b/tests/integration/sync/test_sync_infra.py index 47c4260ce1..a06e52db6d 100644 --- a/tests/integration/sync/test_sync_infra.py +++ b/tests/integration/sync/test_sync_infra.py @@ -193,6 +193,49 @@ def test_sync_infra_no_capabilities(self, template_file): str(sync_process_execute.stderr), ) + @parameterized.expand(["infra/template-python-before.yaml"]) + def test_sync_infra_s3_bucket_option(self, template_file): + template_path = str(self.test_data_path.joinpath(template_file)) + stack_name = self._method_to_stack_name(self.id()) + + sync_command_list = self.get_sync_command_list( + template_file=template_path, + code=False, + watch=False, + dependency_layer=self.dependency_layer, + stack_name=stack_name, + parameter_overrides="Parameter=Clarity", + image_repository=self.ecr_repo_name, + s3_bucket=self.bucket_name, + s3_prefix=self.s3_prefix, + kms_key_id=self.kms_key, + capabilities_list=["CAPABILITY_IAM", "CAPABILITY_AUTO_EXPAND"], + tags="integ=true clarity=yes foo_bar=baz", + ) + + sync_process_execute = run_command_with_input(sync_command_list, "y\n".encode()) + self.assertEqual(sync_process_execute.process.returncode, 0) + self.assertIn("Stack creation succeeded. Sync infra completed.", str(sync_process_execute.stderr)) + + # Make sure all resources are created properly after specifying custom bucket + # CFN Api call here to collect all the stack resources + self.stack_resources = self._get_stacks(stack_name) + + # Lambda Api call here, which tests both the python function and the layer + lambda_functions = self.stack_resources.get(AWS_LAMBDA_FUNCTION) + for lambda_function in lambda_functions: + lambda_response = json.loads(self._get_lambda_response(lambda_function)) + self.assertIn("extra_message", lambda_response) + self.assertEqual(lambda_response.get("message"), "7") + + # ApiGateway Api call here, which tests both of the RestApi + rest_api = self.stack_resources.get(AWS_APIGATEWAY_RESTAPI)[0] + self.assertEqual(self._get_api_message(rest_api), '{"message": "hello 1"}') + + # SFN Api call here, which tests the StateMachine + state_machine = self.stack_resources.get(AWS_STEPFUNCTIONS_STATEMACHINE)[0] + self.assertEqual(self._get_sfn_response(state_machine), '"World 1"') + @skipIf(SKIP_SYNC_TESTS, "Skip sync tests in CI/CD only") class TestSyncInfraCDKTemplates(SyncIntegBase): diff --git a/tests/unit/commands/samconfig/test_samconfig.py b/tests/unit/commands/samconfig/test_samconfig.py index e47f4e2763..db9092a9f8 100644 --- a/tests/unit/commands/samconfig/test_samconfig.py +++ b/tests/unit/commands/samconfig/test_samconfig.py @@ -947,6 +947,7 @@ def test_sync( "stack_name": "mystack", "image_repository": "123456789012.dkr.ecr.us-east-1.amazonaws.com/test1", "base_dir": "path", + "s3_bucket": "mybucket", "s3_prefix": "myprefix", "kms_key_id": "mykms", "parameter_overrides": 'Key1=Value1 Key2="Multiple spaces in the value"', @@ -990,6 +991,7 @@ def test_sync( None, "123456789012.dkr.ecr.us-east-1.amazonaws.com/test1", None, + "mybucket", "myprefix", "mykms", ["cap1", "cap2"], diff --git a/tests/unit/commands/sync/test_command.py b/tests/unit/commands/sync/test_command.py index 5846b638d8..08d162198b 100644 --- a/tests/unit/commands/sync/test_command.py +++ b/tests/unit/commands/sync/test_command.py @@ -31,6 +31,7 @@ def setUp(self): self.image_repository = "123456789012.dkr.ecr.us-east-1.amazonaws.com/test1" self.image_repositories = None self.mode = "mode" + self.s3_bucket = "s3-bucket" self.s3_prefix = "s3-prefix" self.kms_key_id = "kms-key-id" self.notification_arns = [] @@ -104,6 +105,7 @@ def test_infra_must_succeed_sync( self.mode, self.image_repository, self.image_repositories, + self.s3_bucket, self.s3_prefix, self.kms_key_id, self.capabilities, @@ -236,6 +238,7 @@ def test_watch_must_succeed_sync( self.mode, self.image_repository, self.image_repositories, + self.s3_bucket, self.s3_prefix, self.kms_key_id, self.capabilities, @@ -366,6 +369,7 @@ def test_code_must_succeed_sync( self.mode, self.image_repository, self.image_repositories, + self.s3_bucket, self.s3_prefix, self.kms_key_id, self.capabilities,