运行 AWS SAM 从 Python 脚本中构建

Running AWS SAM build from within Python script

我正在将整个 CloudFormation 堆栈迁移到 Troposphere,包括 Lambda 和依赖 Lambda 的 CFN 自定义资源。

我的目标之一是完全避免模板文件的创建,使 Python 代码成为唯一的 "source of truth"(即没有创建模板文件,因此可以编辑,导致配置漂移)。

这需要能够:

  1. 将类似文件的对象传递给 SAM 生成器(而不是文件名)

  2. 从 Python 而不是 CLI

  3. 调用 AWS SAM 构建器

我第一个幼稚的想法是,我可以从 aws-sam-cli 导入一些模块,并在其周围放置 io.StringIO 的包装器(将模板保存为类似文件的对象),然后 presto !然后我查看了 sam build 的源代码,所有的希望都离开​​了我:

这里是Python源码的核心

with BuildContext(template,
                  base_dir,
                  build_dir,
                  clean=clean,
                  manifest_path=manifest_path,
                  use_container=use_container,
                  parameter_overrides=parameter_overrides,
                  docker_network=docker_network,
                  skip_pull_image=skip_pull_image,
                  mode=mode) as ctx:

    builder = ApplicationBuilder(ctx.function_provider,
                                 ctx.build_dir,
                                 ctx.base_dir,
                                 manifest_path_override=ctx.manifest_path_override,
                                 container_manager=ctx.container_manager,
                                 mode=ctx.mode
                                 )
    try:
        artifacts = builder.build()
        modified_template = builder.update_template(ctx.template_dict,
                                                    ctx.original_template_path,
                                                    artifacts)

        move_template(ctx.original_template_path,
                      ctx.output_template_path,
                      modified_template)

        click.secho("\nBuild Succeeded", fg="green")

        msg = gen_success_msg(os.path.relpath(ctx.build_dir),
                              os.path.relpath(ctx.output_template_path),
                              os.path.abspath(ctx.build_dir) == os.path.abspath(DEFAULT_BUILD_DIR))

        click.secho(msg, fg="yellow")

这依赖于来自 aws-sam-cli 内部库的大量导入,其中构建重点是

from samcli.commands.build.build_context import BuildContext
from samcli.lib.build.app_builder import ApplicationBuilder, BuildError, UnsupportedBuilderLibraryVersionError, ContainerBuildNotSupported
from samcli.lib.build.workflow_config import UnsupportedRuntimeException

很明显,这意味着它不像创建 boto3 客户端之类的东西那么简单,我走了!看起来更像是我必须分叉整个东西并扔掉几乎所有东西,只剩下构建命令、上下文和环境。

有趣的是,根据文档,sam packagesam deploy 仅仅是 aws cloudformation packageaws cloudformation deploy 的别名,这意味着 可以 用于boto3!

有人可能已经解决了这个问题吗?我在这里用谷歌搜索,但没有找到任何东西。

我使用 PyCharm and the AWS Toolkit 如果它非常适合开发和调试,我可以从那里 运行 SAM 构建,但它是 "hidden" PyCharm 插件 - 用 Kotlin 编写!

我目前的解决方法是将 CFN 模板创建为临时文件,并将它们传递给从 Python 调用的 CLI 命令 - 我一直不喜欢的方法。

我可能会向 aws-sam-cli 团队提出功能请求,看看他们怎么说,除非他们中有人读到了这篇文章。

我正在使用以下 python 脚本来执行 运行 sam cli 命令。这应该也适合你。

import json
import sys
import os

try:
    LAMBDA_S3_BUCKET="s3-bucket-name-in-same-region"
    AWS_REGION="us-east-1"      
    API_NAME = "YourAPIName"
    BASE_PATH="/path/to/your/project/code/dir"
    STACK_NAME="YourCloudFormationStackName"
    BUILD_DIR="%s/%s" % (BASE_PATH, "build_artifact")

    if not os.path.exists(BUILD_DIR):
        os.mkdir(BUILD_DIR)

    os.system("cd %s && sam build --template template.yaml --build-dir %s" % (BASE_PATH, BUILD_DIR))
    os.system("cd %s && sam package --template-file %s/template.yaml --output-template-file packaged.yaml --s3-bucket %s" %(BASE_PATH, BUILD_DIR,  LAMBDA_S3_BUCKET))
    os.system("cd %s && sam deploy  --template-file packaged.yaml --stack-name %s --capabilities CAPABILITY_IAM --region %s" %(BASE_PATH, STACK_NAME, AWS_REGION))  

except Exception as e:
    print(e.message)
    exit(1)

我已成功从 python3 脚本启动 sam local start-api

首先,pip3 install aws-sam-cli

然后可以导入单个命令并运行。

import sys
from samcli.commands.local.start_api.cli import cli
sys.exit(cli())

...前提是当前目录下有template.yaml

我(尚未)设法影响 cli() 将接收的命令行参数,以便我可以告诉它使用哪个 -t template

编辑

查看 aws-sam-cli 集成测试的工作方式,似乎它们实际上启动了 运行 CLI 的进程。所以他们实际上根本没有将参数传递给 cli() 调用:-(

例如:

class TestSamPython36HelloWorldIntegration(InvokeIntegBase):
    template = Path("template.yml")

    def test_invoke_returncode_is_zero(self):
        command_list = self.get_command_list(
            "HelloWorldServerlessFunction", template_path=self.template_path, event_path=self.event_path
        )

        process = Popen(command_list, stdout=PIPE)
        return_code = process.wait()

        self.assertEquals(return_code, 0)

   .... etc

来自 https://github.com/awslabs/aws-sam-cli/blob/a83aa9e620ff679ca740496a3f1ff4872b88894a/tests/integration/local/invoke/test_integrations_cli.py

另请参阅同一存储库中的 start_api_integ_base.py

我认为总的来说这是可以预料的,因为整个事情都是根据 click 命令行应用程序框架实现的。很遗憾。

例如 http://click.palletsprojects.com/en/7.x/testing/ 上面写着 "The CliRunner.invoke() method runs the command line script in isolation ..." -- 我的重点。