def test_publish_raise_invalid_s3_uri_when_create_application(self):
        self.serverlessrepo_mock.create_application.side_effect = self.invalid_s3_uri_exception
        with self.assertRaises(InvalidS3UriError) as context:
            publish_application(self.template)

        message = str(context.exception)
        self.assertIn("Invalid S3 URI", message)
    def test_publish_raise_client_error_when_create_application_version(self):
        self.serverlessrepo_mock.create_application.side_effect = self.application_exists_error
        self.serverlessrepo_mock.create_application_version.side_effect = self.not_conflict_exception

        # should raise exception if it's not ConflictException
        with self.assertRaises(ClientError):
            publish_application(self.template)
 def test_publish_template_dict_should_copy_template(self, copy_mock):
     self.serverlessrepo_mock.create_application.return_value = {
         'ApplicationId': self.application_id
     }
     copy_mock.return_value = self.template_dict
     publish_application(self.template_dict)
     copy_mock.assert_called_with(self.template_dict)
    def test_publish_raise_value_error_for_empty_template(self):
        with self.assertRaises(ValueError) as context:
            publish_application('')

        message = str(context.exception)
        expected = 'Require SAM template to publish the application'
        self.assertEqual(expected, message)
        self.serverlessrepo_mock.create_application.assert_not_called()
    def test_publish_raise_value_error_for_not_dict_or_string_template(self):
        with self.assertRaises(ValueError) as context:
            publish_application(123)

        message = str(context.exception)
        expected = 'Input template should be a string or dictionary'
        self.assertEqual(expected, message)
        self.serverlessrepo_mock.create_application.assert_not_called()
 def test_publish_template_string_should_parse_template(
         self, parse_template_mock):
     self.serverlessrepo_mock.create_application.return_value = {
         'ApplicationId': self.application_id
     }
     parse_template_mock.return_value = self.template_dict
     publish_application(self.template)
     parse_template_mock.assert_called_with(self.template)
 def test_publish_wrap_client_error_when_create_application_version(
         self, wrap_client_error_mock):
     self.serverlessrepo_mock.create_application.side_effect = self.application_exists_error
     self.serverlessrepo_mock.create_application_version.side_effect = self.not_conflict_exception
     wrap_client_error_mock.return_value = ServerlessRepoClientError(
         message="client error")
     with self.assertRaises(ServerlessRepoClientError):
         publish_application(self.template)
    def test_publish_raise_s3_permission_error_when_create_application(self):
        self.serverlessrepo_mock.create_application.side_effect = self.s3_denied_exception
        with self.assertRaises(S3PermissionsRequired) as context:
            publish_application(self.template)

        message = str(context.exception)
        self.assertIn(
            "The AWS Serverless Application Repository does not have read access to bucket "
            "'test-bucket', key 'test-file'.", message)
    def test_publish_exception_when_serverlessrepo_create_application(self):
        self.serverlessrepo_mock.create_application.side_effect = self.not_conflict_exception

        # should raise exception if it's not ConflictException
        with self.assertRaises(ClientError):
            publish_application(self.template)

        # shouldn't call the following APIs if the exception isn't application already exists
        self.serverlessrepo_mock.update_application.assert_not_called()
        self.serverlessrepo_mock.create_application_version.assert_not_called()
    def test_publish_raise_unsupported_resource_types_when_create_application(
            self):
        self.serverlessrepo_mock.create_application.side_effect = self.unsupported_resource_types_exception
        with self.assertRaises(UnsupportedResourceTypesError) as context:
            publish_application(self.template)

        message = str(context.exception)
        self.assertIn(
            "Template contains unsupported resource types: 'AWS::XXX::XXX'.",
            message)
    def test_publish_exception_when_validate_create_application_request(self):
        template_without_app_name = self.template.replace(
            "'Name': 'test-app',", '')
        with self.assertRaises(InvalidApplicationMetadataError) as context:
            publish_application(template_without_app_name)

        message = str(context.exception)
        self.assertEqual(
            "Required application metadata properties not provided: 'name'",
            message)
        # create_application shouldn't be called if application metadata is invalid
        self.serverlessrepo_mock.create_application.assert_not_called()
    def test_publish_raise_metadata_error_for_invalid_create_application_request(
            self):
        template_without_app_name = self.template.replace(
            '"Name": "test-app",', '')
        with self.assertRaises(InvalidApplicationMetadataError) as context:
            publish_application(template_without_app_name)

        message = str(context.exception)
        self.assertEqual(
            "Invalid application metadata: 'name properties not provided'",
            message)
        # create_application shouldn't be called if application metadata is invalid
        self.serverlessrepo_mock.create_application.assert_not_called()
    def test_publish_raise_s3_error_when_create_application(self):
        self.serverlessrepo_mock.create_application.side_effect = self.s3_denied_exception
        with self.assertRaises(S3PermissionsRequired) as context:
            publish_application(self.template)

        message = str(context.exception)
        self.assertIn(
            "The AWS Serverless Application Repository does not have read access to bucket "
            "'test-bucket', key 'test-file'.", message)

        # shouldn't call the following APIs if the exception isn't application already exists
        self.serverlessrepo_mock.update_application.assert_not_called()
        self.serverlessrepo_mock.create_application_version.assert_not_called()
    def test_update_application_with_passed_in_sar_client(self):
        sar_client = Mock()
        sar_client.create_application.side_effect = self.application_exists_error
        publish_application(self.template, sar_client)

        sar_client.create_application.assert_called_once()
        sar_client.update_application.assert_called_once()
        sar_client.create_application_version.assert_called_once()

        # the self initiated boto3 client shouldn't be used
        self.serverlessrepo_mock.create_application.assert_not_called()
        self.serverlessrepo_mock.update_application.assert_not_called()
        self.serverlessrepo_mock.create_application_version.assert_not_called()
    def test_create_application_with_passed_in_sar_client(self):
        sar_client = Mock()
        sar_client.create_application.return_value = {
            'ApplicationId': self.application_id
        }
        publish_application(self.template, sar_client)

        sar_client.create_application.assert_called_once()
        sar_client.update_application.assert_not_called()
        sar_client.create_application_version.assert_not_called()

        # the self initiated boto3 client shouldn't be used
        self.serverlessrepo_mock.create_application.assert_not_called()
        self.serverlessrepo_mock.update_application.assert_not_called()
        self.serverlessrepo_mock.create_application_version.assert_not_called()
    def test_publish_new_version_should_create_application_version(self):
        self.serverlessrepo_mock.create_application.side_effect = self.application_exists_error

        actual_result = publish_application(self.template)
        expected_result = {
            'application_id': self.application_id,
            'actions': [UPDATE_APPLICATION, CREATE_APPLICATION_VERSION],
            'details': {
                # Name and LicenseUrl shouldn't show up since they can't be updated
                'Description': 'hello world',
                'Author': 'abc',
                'ReadmeUrl': 's3://test-bucket/README.md',
                'Labels': ['test1', 'test2'],
                'HomePageUrl': 'https://github.com/abc/def',
                'SourceCodeUrl': 'https://github.com/abc/def',
                'SemanticVersion': '1.0.0'
            }
        }
        self.assertEqual(expected_result, actual_result)

        self.serverlessrepo_mock.create_application.assert_called_once()
        self.serverlessrepo_mock.update_application.assert_called_once()
        # should continue to create application version
        expected_request = {
            'ApplicationId': self.application_id,
            'SourceCodeUrl': 'https://github.com/abc/def',
            'SemanticVersion': '1.0.0',
            'TemplateBody': self.yaml_template_without_metadata
        }
        self.serverlessrepo_mock.create_application_version.assert_called_once_with(
            **expected_request)
    def test_publish_existing_application_should_update_application_if_version_exists(
            self):
        self.serverlessrepo_mock.create_application.side_effect = self.application_exists_error
        self.serverlessrepo_mock.create_application_version.side_effect = ClientError(
            {'Error': {
                'Code': 'ConflictException',
                'Message': 'Random'
            }}, 'create_application_version')

        actual_result = publish_application(self.template)
        expected_result = {
            'application_id': self.application_id,
            'actions': [UPDATE_APPLICATION],
            'details': {
                # Name, LicenseUrl and SourceCodeUrl shouldn't show up
                'Description': 'hello world',
                'Author': 'abc',
                'Labels': ['test1', 'test2'],
                'HomePageUrl': 'https://github.com/abc/def',
                'ReadmeUrl': 's3://test-bucket/README.md'
            }
        }
        self.assertEqual(expected_result, actual_result)

        self.serverlessrepo_mock.create_application.assert_called_once()
        self.serverlessrepo_mock.update_application.assert_called_once()
        self.serverlessrepo_mock.create_application_version.assert_called_once(
        )
    def test_publish_existing_application_should_update_application_if_version_not_specified(
            self):
        self.serverlessrepo_mock.create_application.side_effect = self.application_exists_error
        template_without_version = self.template.replace(
            '"SemanticVersion": "1.0.0"', '')

        actual_result = publish_application(template_without_version)
        expected_result = {
            'application_id': self.application_id,
            'actions': [UPDATE_APPLICATION],
            'details': {
                # Name, LicenseUrl and SourceCodeUrl shouldn't show up
                'Description': 'hello world',
                'Author': 'abc',
                'ReadmeUrl': 's3://test-bucket/README.md',
                'Labels': ['test1', 'test2'],
                'HomePageUrl': 'https://github.com/abc/def'
            }
        }
        self.assertEqual(expected_result, actual_result)

        self.serverlessrepo_mock.create_application.assert_called_once()
        # should continue to update application if the exception is application already exists
        expected_request = dict({'ApplicationId': self.application_id},
                                **expected_result['details'])
        self.serverlessrepo_mock.update_application.assert_called_once_with(
            **expected_request)
        # create_application_version shouldn't be called if version is not provided
        self.serverlessrepo_mock.create_application_version.assert_not_called()
def do_cli(ctx, template, semantic_version):
    """Publish the application based on command line inputs."""
    try:
        template_data = get_template_data(template)
    except ValueError as ex:
        click.secho("Publish Failed", fg="red")
        raise UserException(str(ex))

    # Override SemanticVersion in template metadata when provided in command input
    if semantic_version and SERVERLESS_REPO_APPLICATION in template_data.get(
            METADATA, {}):
        template_data.get(METADATA).get(
            SERVERLESS_REPO_APPLICATION)[SEMANTIC_VERSION] = semantic_version

    try:
        publish_output = publish_application(template_data)
        click.secho("Publish Succeeded", fg="green")
        click.secho(_gen_success_message(publish_output))
    except InvalidS3UriError:
        click.secho("Publish Failed", fg="red")
        raise UserException(
            "Your SAM template contains invalid S3 URIs. Please make sure that you have uploaded application "
            "artifacts to S3 by packaging the template. See more details in {}"
            .format(SAM_PACKAGE_DOC))
    except ServerlessRepoError as ex:
        click.secho("Publish Failed", fg="red")
        LOG.debug("Failed to publish application to serverlessrepo",
                  exc_info=True)
        error_msg = "{}\nPlease follow the instructions in {}".format(
            str(ex), SAM_PUBLISH_DOC)
        raise UserException(error_msg)

    application_id = publish_output.get("application_id")
    _print_console_link(ctx.region, application_id)
def publish(event, context):
    """Publish to AWS Serverless Application Repository.

    CodePipeline invokes the lambda to publish an application
    to AWS Serverless Application Repository. If the application
    already exists, it will update the application metadata. Besides,
    it will create an application version if SemanticVersion is specified
    in the Metadata section of the packaged template.

    Arguments:
        event {dict} -- The JSON event sent to AWS Lambda by AWS CodePipeline
        (https://docs.aws.amazon.com/codepipeline/latest/userguide/actions-invoke-lambda-function.html#actions-invoke-lambda-function-json-event-example)
        context {LambdaContext} -- The context passed by AWS Lambda
    """
    job_id = event['CodePipeline.job']['id']

    redacted_event = _remove_sensitive_items_from_event(event)
    LOG.info('CodePipeline publish to SAR request={}'.format(redacted_event))

    try:
        packaged_template_str = s3helper.get_input_artifact(event)
        LOG.info(
            'Making API calls to AWS Serverless Application Repository...')
        sar_response = serverlessrepo.publish_application(
            packaged_template_str)
        codepipelinehelper.put_job_success(job_id, sar_response)
    except Exception as e:
        LOG.error(str(e))
        codepipelinehelper.put_job_failure(job_id, e)
 def test_update_application_with_readmebody(self):
     self.serverlessrepo_mock.create_application.side_effect = self.application_exists_error
     template_with_readmebody = self.template \
         .replace('"SemanticVersion": "1.0.0"', '') \
         .replace('"ReadmeUrl": "s3://test-bucket/README.md"', '"ReadmeBody": "test test"')
     actual_result = publish_application(template_with_readmebody)
     expected_result = {
         'application_id': self.application_id,
         'actions': [UPDATE_APPLICATION],
         'details': {
             'Description': 'hello world',
             'Author': 'abc',
             'ReadmeBody': 'test test',
             'Labels': ['test1', 'test2'],
             'HomePageUrl': 'https://github.com/abc/def'
         }
     }
     self.assertEqual(expected_result, actual_result)
示例#22
0
def do_cli(ctx, template):
    """Publish the application based on command line inputs."""
    try:
        template_data = get_template_data(template)
    except ValueError as ex:
        click.secho("Publish Failed", fg='red')
        raise UserException(str(ex))

    try:
        publish_output = publish_application(template_data)
        click.secho("Publish Succeeded", fg="green")
        click.secho(_gen_success_message(publish_output), fg="yellow")
    except ServerlessRepoError as ex:
        click.secho("Publish Failed", fg='red')
        raise UserException(str(ex))
    except ClientError as ex:
        click.secho("Publish Failed", fg='red')
        raise _wrap_s3_uri_exception(ex)

    application_id = publish_output.get('application_id')
    _print_console_link(ctx.region, application_id)
    def test_publish_new_application_should_create_application(self):
        self.serverlessrepo_mock.create_application.return_value = {
            'ApplicationId': self.application_id
        }

        actual_result = publish_application(self.template)
        app_metadata_template = get_app_metadata(parse_template(
            self.template)).template_dict
        expected_result = {
            'application_id': self.application_id,
            'actions': [CREATE_APPLICATION],
            'details': app_metadata_template
        }
        self.assertEqual(expected_result, actual_result)

        expected_request = dict({'TemplateBody': self.template},
                                **app_metadata_template)
        self.serverlessrepo_mock.create_application.assert_called_once_with(
            **expected_request)
        # publish a new application will only call create_application
        self.serverlessrepo_mock.update_application.assert_not_called()
        self.serverlessrepo_mock.create_application_version.assert_not_called()
 def test_create_application_with_licensebody(self):
     self.serverlessrepo_mock.create_application.return_value = {
         'ApplicationId': self.application_id
     }
     template_with_licensebody = self.template \
         .replace('"LicenseUrl": "s3://test-bucket/LICENSE"', '"LicenseBody": "test test"')
     actual_result = publish_application(template_with_licensebody)
     expected_result = {
         'application_id': self.application_id,
         'actions': [CREATE_APPLICATION],
         'details': {
             'Author': 'abc',
             'Description': 'hello world',
             'HomePageUrl': 'https://github.com/abc/def',
             'Labels': ['test1', 'test2'],
             'LicenseBody': 'test test',
             'Name': 'test-app',
             'ReadmeUrl': 's3://test-bucket/README.md',
             'SemanticVersion': '1.0.0',
             'SourceCodeUrl': 'https://github.com/abc/def'
         }
     }
     self.assertEqual(expected_result, actual_result)