apx-aws-lambda 0.1.0__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/PKG-INFO +37 -2
- apx_aws_lambda-0.2.0/README.md +75 -0
- {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/apx_aws_lambda/lambda_.py +89 -8
- {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/apx_aws_lambda/plugin.py +1 -1
- {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/apx_aws_lambda/shell.py +7 -2
- {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/apx_aws_lambda.egg-info/PKG-INFO +37 -2
- {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/pyproject.toml +1 -1
- {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/tests/test_targets.py +48 -0
- apx_aws_lambda-0.1.0/README.md +0 -40
- {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/LICENSE +0 -0
- {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/apx_aws_lambda/__init__.py +0 -0
- {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/apx_aws_lambda/cli.py +0 -0
- {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/apx_aws_lambda/overlays/cloud/aws/lambda/cookiecutter.json +0 -0
- {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/apx_aws_lambda/overlays/cloud/aws/lambda/hooks/post_gen_project.py +0 -0
- {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/apx_aws_lambda/overlays/cloud/aws/lambda/{{cookiecutter.project_slug}}/.github/workflows/deploy.yml +0 -0
- {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/apx_aws_lambda/overlays/cloud/aws/lambda/{{cookiecutter.project_slug}}/DEPLOY.md +0 -0
- {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/apx_aws_lambda/overlays/cloud/aws/lambda/{{cookiecutter.project_slug}}/_lang/python/Makefile +0 -0
- {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/apx_aws_lambda/overlays/cloud/aws/lambda/{{cookiecutter.project_slug}}/_lang/python/lambda_handler.py +0 -0
- {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/apx_aws_lambda/overlays/cloud/aws/lambda/{{cookiecutter.project_slug}}/requirements/policy.json +0 -0
- {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/apx_aws_lambda/overlays/cloud/aws/lambda/{{cookiecutter.project_slug}}/requirements/trust.json +0 -0
- {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/apx_aws_lambda/overlays/cloud/aws/lambda/{{cookiecutter.project_slug}}/samconfig.toml +0 -0
- {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/apx_aws_lambda/overlays/cloud/aws/lambda/{{cookiecutter.project_slug}}/template.yaml +0 -0
- {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/apx_aws_lambda/overlays/index.json +0 -0
- {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/apx_aws_lambda/tools.py +0 -0
- {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/apx_aws_lambda.egg-info/SOURCES.txt +0 -0
- {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/apx_aws_lambda.egg-info/dependency_links.txt +0 -0
- {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/apx_aws_lambda.egg-info/entry_points.txt +0 -0
- {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/apx_aws_lambda.egg-info/requires.txt +0 -0
- {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/apx_aws_lambda.egg-info/top_level.txt +0 -0
- {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: apx-aws-lambda
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: AWS Lambda for Action Platform: the aws/lambda deploy target (SAM) with its overlay, and tools to read stacks and functions.
|
|
5
5
|
Author-email: Action Platform <cloud@actionplatform.io>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -42,9 +42,44 @@ target = "aws/lambda"
|
|
|
42
42
|
region = "us-east-1" # optional; samconfig.toml / AWS_REGION otherwise
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
+
## Credentials without keys
|
|
46
|
+
|
|
47
|
+
Set `role_arn` and nothing else: the target assumes that role with a short-lived OIDC token the platform signs (`sts assume-role-with-web-identity`). No access key on the platform, on your machine or in the repository; what the app may do is the role's policy.
|
|
48
|
+
|
|
49
|
+
```toml
|
|
50
|
+
[deploy]
|
|
51
|
+
target = "aws/lambda"
|
|
52
|
+
role_arn = "arn:aws:iam::123456789012:role/shop-deploy"
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Once per AWS account, register the platform as an identity provider — its discovery document is `https://<platform>/.well-known/openid-configuration`:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
aws iam create-open-id-connect-provider --url https://platform.example.com --client-id-list sts.amazonaws.com
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Then a role per app (or per project) whose trust policy names the platform and the app; `requirements/policy.json` from the overlay is its permission policy:
|
|
62
|
+
|
|
63
|
+
```json
|
|
64
|
+
{
|
|
65
|
+
"Version": "2012-10-17",
|
|
66
|
+
"Statement": [{
|
|
67
|
+
"Effect": "Allow",
|
|
68
|
+
"Principal": { "Federated": "arn:aws:iam::123456789012:oidc-provider/platform.example.com" },
|
|
69
|
+
"Action": "sts:AssumeRoleWithWebIdentity",
|
|
70
|
+
"Condition": {
|
|
71
|
+
"StringEquals": { "platform.example.com:aud": "sts.amazonaws.com" },
|
|
72
|
+
"StringLike": { "platform.example.com:sub": "org:acme:project:shop:app:orders" }
|
|
73
|
+
}
|
|
74
|
+
}]
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
`sub` is `org:<org>:project:<project>:app:<app>` for a deploy the platform runs; `org:<org>` plus an `actor` claim for `action-platform deploy` from a logged-in machine (`StringLike` with `org:acme:*` covers both). Without `role_arn` the AWS CLI's own chain applies: `aws sso login`, a profile, an instance role — still no key in a file when you use SSO.
|
|
79
|
+
|
|
45
80
|
Tools (`action-platform mcp`): `aws_lambda_stacks`, `aws_lambda_functions`. Commands: `action-platform aws-lambda stacks|functions`. Deploying itself goes through the core's `deploy` / `rollback` / `diagnose`, which drive the target.
|
|
46
81
|
|
|
47
|
-
Needs: AWS CLI v2 (`aws`), SAM CLI (`sam`),
|
|
82
|
+
Needs: AWS CLI v2 (`aws`), SAM CLI (`sam`), `AWS_REGION` (or `region` under `[deploy]`), and either `role_arn` (OIDC, no keys) or the AWS CLI's own credentials. Talks to `*.amazonaws.com` only.
|
|
48
83
|
|
|
49
84
|
## Development
|
|
50
85
|
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# apx-aws-lambda
|
|
2
|
+
|
|
3
|
+
AWS Lambda for [Action Platform](https://github.com/actionplatform/action-platform): the `aws/lambda` deploy target (SAM) with its overlay, read-only tools and commands. `apx-` is the prefix every Action Platform extension carries.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
action-platform plugin install aws-lambda
|
|
7
|
+
action-platform cloud set aws/lambda # overlay files come from this plugin
|
|
8
|
+
action-platform deploy --dry-run # preflight: sam validate, credentials
|
|
9
|
+
action-platform deploy # sam build + sam deploy --config-env <stage>
|
|
10
|
+
action-platform diagnose
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
| | |
|
|
14
|
+
|---|---|
|
|
15
|
+
| Overlay | `template.yaml`, `samconfig.toml`, `lambda_handler.py`, `Makefile`, `requirements/` (IAM), `.github/workflows/deploy.yml` |
|
|
16
|
+
| Deploy | `sam build` + `sam deploy --config-env <stage>` (`dev` → `default`, `prod` → `prod`) |
|
|
17
|
+
| Rollback | CloudFormation `rollback-stack` — previous stack state |
|
|
18
|
+
| Diagnose | stack status and the HTTP API url |
|
|
19
|
+
| Destroy | `sam delete` |
|
|
20
|
+
|
|
21
|
+
`[deploy]` in platform.toml:
|
|
22
|
+
|
|
23
|
+
```toml
|
|
24
|
+
[deploy]
|
|
25
|
+
target = "aws/lambda"
|
|
26
|
+
region = "us-east-1" # optional; samconfig.toml / AWS_REGION otherwise
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Credentials without keys
|
|
30
|
+
|
|
31
|
+
Set `role_arn` and nothing else: the target assumes that role with a short-lived OIDC token the platform signs (`sts assume-role-with-web-identity`). No access key on the platform, on your machine or in the repository; what the app may do is the role's policy.
|
|
32
|
+
|
|
33
|
+
```toml
|
|
34
|
+
[deploy]
|
|
35
|
+
target = "aws/lambda"
|
|
36
|
+
role_arn = "arn:aws:iam::123456789012:role/shop-deploy"
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Once per AWS account, register the platform as an identity provider — its discovery document is `https://<platform>/.well-known/openid-configuration`:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
aws iam create-open-id-connect-provider --url https://platform.example.com --client-id-list sts.amazonaws.com
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Then a role per app (or per project) whose trust policy names the platform and the app; `requirements/policy.json` from the overlay is its permission policy:
|
|
46
|
+
|
|
47
|
+
```json
|
|
48
|
+
{
|
|
49
|
+
"Version": "2012-10-17",
|
|
50
|
+
"Statement": [{
|
|
51
|
+
"Effect": "Allow",
|
|
52
|
+
"Principal": { "Federated": "arn:aws:iam::123456789012:oidc-provider/platform.example.com" },
|
|
53
|
+
"Action": "sts:AssumeRoleWithWebIdentity",
|
|
54
|
+
"Condition": {
|
|
55
|
+
"StringEquals": { "platform.example.com:aud": "sts.amazonaws.com" },
|
|
56
|
+
"StringLike": { "platform.example.com:sub": "org:acme:project:shop:app:orders" }
|
|
57
|
+
}
|
|
58
|
+
}]
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
`sub` is `org:<org>:project:<project>:app:<app>` for a deploy the platform runs; `org:<org>` plus an `actor` claim for `action-platform deploy` from a logged-in machine (`StringLike` with `org:acme:*` covers both). Without `role_arn` the AWS CLI's own chain applies: `aws sso login`, a profile, an instance role — still no key in a file when you use SSO.
|
|
63
|
+
|
|
64
|
+
Tools (`action-platform mcp`): `aws_lambda_stacks`, `aws_lambda_functions`. Commands: `action-platform aws-lambda stacks|functions`. Deploying itself goes through the core's `deploy` / `rollback` / `diagnose`, which drive the target.
|
|
65
|
+
|
|
66
|
+
Needs: AWS CLI v2 (`aws`), SAM CLI (`sam`), `AWS_REGION` (or `region` under `[deploy]`), and either `role_arn` (OIDC, no keys) or the AWS CLI's own credentials. Talks to `*.amazonaws.com` only.
|
|
67
|
+
|
|
68
|
+
## Development
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
pip install -e ".[dev]"
|
|
72
|
+
pytest
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
The tests fake `aws` and `sam`; nothing reaches AWS.
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
"""`aws/lambda`: a SAM stack per stage. Deploy = `sam build` + `sam deploy --config-env <stage>`; rollback = CloudFormation rollback to the previous stack state; diagnose = stack status and the HTTP API url.
|
|
1
|
+
"""`aws/lambda`: a SAM stack per stage. Deploy = `sam build` + `sam deploy --config-env <stage>`; rollback = CloudFormation rollback to the previous stack state; diagnose = stack status and the HTTP API url.
|
|
2
|
+
|
|
3
|
+
Credentials: with `role_arn` under `[deploy]`, the target assumes that role with a short-lived OIDC token the platform signs (`sts assume-role-with-web-identity`) — no access key anywhere. Without it, the AWS CLI's own chain applies (profile, SSO, instance role)."""
|
|
2
4
|
|
|
3
5
|
from __future__ import annotations
|
|
4
6
|
|
|
@@ -7,7 +9,8 @@ import tomllib
|
|
|
7
9
|
|
|
8
10
|
from action_platform.abc import DeployTarget
|
|
9
11
|
from action_platform.core.context import Context, DeployResult, Diagnosis
|
|
10
|
-
from action_platform.core.exception import DeployError
|
|
12
|
+
from action_platform.core.exception import ActionPlatformError, DeployError
|
|
13
|
+
from action_platform.remote.client import Remote
|
|
11
14
|
|
|
12
15
|
from apx_aws_lambda import shell
|
|
13
16
|
|
|
@@ -16,7 +19,12 @@ class LambdaTarget(DeployTarget):
|
|
|
16
19
|
name = "aws/lambda"
|
|
17
20
|
|
|
18
21
|
def __init__(
|
|
19
|
-
self,
|
|
22
|
+
self,
|
|
23
|
+
region: str | None = None,
|
|
24
|
+
config: str = "samconfig.toml",
|
|
25
|
+
role_arn: str | None = None,
|
|
26
|
+
session_name: str = "action-platform",
|
|
27
|
+
**_: object,
|
|
20
28
|
) -> None:
|
|
21
29
|
self.region = (
|
|
22
30
|
region
|
|
@@ -24,6 +32,22 @@ class LambdaTarget(DeployTarget):
|
|
|
24
32
|
or os.environ.get("AWS_DEFAULT_REGION")
|
|
25
33
|
)
|
|
26
34
|
self.config = config
|
|
35
|
+
self.role_arn = role_arn or os.environ.get("AWS_ROLE_ARN")
|
|
36
|
+
self.session_name = session_name
|
|
37
|
+
self._env: dict[str, str] | None = None
|
|
38
|
+
|
|
39
|
+
def env(self, ctx: Context) -> dict[str, str] | None:
|
|
40
|
+
"""The environment `aws` and `sam` run with: temporary credentials from `role_arn` when set, the caller's own otherwise."""
|
|
41
|
+
if not self.role_arn:
|
|
42
|
+
return None
|
|
43
|
+
|
|
44
|
+
if self._env is None:
|
|
45
|
+
self._env = {
|
|
46
|
+
**os.environ,
|
|
47
|
+
**assume_role(ctx, self.role_arn, self.session_name, self.region),
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return self._env
|
|
27
51
|
|
|
28
52
|
def _stage(self, ctx: Context) -> str:
|
|
29
53
|
return "prod" if ctx.stage == "prod" else "default"
|
|
@@ -72,19 +96,25 @@ class LambdaTarget(DeployTarget):
|
|
|
72
96
|
)
|
|
73
97
|
|
|
74
98
|
self._stack(ctx)
|
|
75
|
-
shell.aws(
|
|
76
|
-
|
|
99
|
+
shell.aws(
|
|
100
|
+
"sts", "get-caller-identity", region=self._region(ctx), env=self.env(ctx)
|
|
101
|
+
)
|
|
102
|
+
shell.run(
|
|
103
|
+
[shell.require("sam", ""), "validate", "--lint"],
|
|
104
|
+
cwd=ctx.repo_root,
|
|
105
|
+
env=self.env(ctx),
|
|
106
|
+
)
|
|
77
107
|
|
|
78
108
|
def deploy(self, ctx: Context) -> DeployResult:
|
|
79
109
|
sam = shell.require("sam", "pip install aws-sam-cli")
|
|
80
110
|
stage = self._stage(ctx)
|
|
81
|
-
shell.run([sam, "build"], cwd=ctx.repo_root)
|
|
111
|
+
shell.run([sam, "build"], cwd=ctx.repo_root, env=self.env(ctx))
|
|
82
112
|
args = [sam, "deploy", "--no-confirm-changeset", "--no-fail-on-empty-changeset"]
|
|
83
113
|
|
|
84
114
|
if stage != "default":
|
|
85
115
|
args += ["--config-env", stage]
|
|
86
116
|
|
|
87
|
-
shell.run(args, cwd=ctx.repo_root)
|
|
117
|
+
shell.run(args, cwd=ctx.repo_root, env=self.env(ctx))
|
|
88
118
|
url = self._url(ctx)
|
|
89
119
|
|
|
90
120
|
return DeployResult(
|
|
@@ -104,6 +134,7 @@ class LambdaTarget(DeployTarget):
|
|
|
104
134
|
"--stack-name",
|
|
105
135
|
self._stack(ctx),
|
|
106
136
|
region=self._region(ctx),
|
|
137
|
+
env=self.env(ctx),
|
|
107
138
|
)
|
|
108
139
|
|
|
109
140
|
def diagnose(self, ctx: Context) -> Diagnosis:
|
|
@@ -116,6 +147,7 @@ class LambdaTarget(DeployTarget):
|
|
|
116
147
|
"--stack-name",
|
|
117
148
|
stack,
|
|
118
149
|
region=self._region(ctx),
|
|
150
|
+
env=self.env(ctx),
|
|
119
151
|
)
|
|
120
152
|
except DeployError as e:
|
|
121
153
|
return Diagnosis(
|
|
@@ -151,10 +183,59 @@ class LambdaTarget(DeployTarget):
|
|
|
151
183
|
if region:
|
|
152
184
|
args += ["--region", region]
|
|
153
185
|
|
|
154
|
-
shell.run(args, cwd=ctx.repo_root)
|
|
186
|
+
shell.run(args, cwd=ctx.repo_root, env=self.env(ctx))
|
|
155
187
|
|
|
156
188
|
def _url(self, ctx: Context) -> str | None:
|
|
157
189
|
try:
|
|
158
190
|
return self.diagnose(ctx).url
|
|
159
191
|
except DeployError:
|
|
160
192
|
return None
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def assume_role(
|
|
196
|
+
ctx: Context, role_arn: str, session_name: str, region: str | None
|
|
197
|
+
) -> dict[str, str]:
|
|
198
|
+
"""Temporary credentials for `role_arn` from an OIDC token: the platform's for a deploy it runs, the platform the CLI is logged in to otherwise."""
|
|
199
|
+
token = ctx.identity_token("sts.amazonaws.com")
|
|
200
|
+
|
|
201
|
+
if token is None:
|
|
202
|
+
try:
|
|
203
|
+
token = Remote.from_credentials().identity_token("sts.amazonaws.com")[
|
|
204
|
+
"token"
|
|
205
|
+
]
|
|
206
|
+
except ActionPlatformError as e:
|
|
207
|
+
raise DeployError(
|
|
208
|
+
f"role_arn is set but nothing can sign an identity token here: {e}. "
|
|
209
|
+
"Deploy from the platform, or log in with `action-platform login`."
|
|
210
|
+
) from e
|
|
211
|
+
|
|
212
|
+
data = shell.aws(
|
|
213
|
+
"sts",
|
|
214
|
+
"assume-role-with-web-identity",
|
|
215
|
+
"--role-arn",
|
|
216
|
+
role_arn,
|
|
217
|
+
"--role-session-name",
|
|
218
|
+
session_name,
|
|
219
|
+
"--web-identity-token",
|
|
220
|
+
token,
|
|
221
|
+
"--duration-seconds",
|
|
222
|
+
"3600",
|
|
223
|
+
region=region,
|
|
224
|
+
env={
|
|
225
|
+
k: v
|
|
226
|
+
for k, v in os.environ.items()
|
|
227
|
+
if not k.startswith("AWS_ACCESS") and k != "AWS_SESSION_TOKEN"
|
|
228
|
+
},
|
|
229
|
+
)
|
|
230
|
+
creds = data.get("Credentials") or {}
|
|
231
|
+
|
|
232
|
+
if not creds.get("AccessKeyId"):
|
|
233
|
+
raise DeployError(
|
|
234
|
+
f"assume-role-with-web-identity gave no credentials for {role_arn}"
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
return {
|
|
238
|
+
"AWS_ACCESS_KEY_ID": creds["AccessKeyId"],
|
|
239
|
+
"AWS_SECRET_ACCESS_KEY": creds["SecretAccessKey"],
|
|
240
|
+
"AWS_SESSION_TOKEN": creds["SessionToken"],
|
|
241
|
+
}
|
|
@@ -18,7 +18,7 @@ class AwsLambdaPlugin(Plugin):
|
|
|
18
18
|
min_core = "0.16"
|
|
19
19
|
needs = [
|
|
20
20
|
"tool: aws (AWS CLI v2), sam (AWS SAM CLI)",
|
|
21
|
-
"env:
|
|
21
|
+
"env: AWS_REGION; credentials from role_arn (OIDC, no keys) or the AWS CLI chain (SSO, profile, instance role)",
|
|
22
22
|
"net: *.amazonaws.com",
|
|
23
23
|
]
|
|
24
24
|
|
|
@@ -35,12 +35,17 @@ def run(
|
|
|
35
35
|
return result.stdout
|
|
36
36
|
|
|
37
37
|
|
|
38
|
-
def aws(
|
|
38
|
+
def aws(
|
|
39
|
+
*args: str,
|
|
40
|
+
region: str | None = None,
|
|
41
|
+
cwd: Path | None = None,
|
|
42
|
+
env: dict[str, str] | None = None,
|
|
43
|
+
) -> Any:
|
|
39
44
|
command = [require("aws", "https://aws.amazon.com/cli/"), *args, "--output", "json"]
|
|
40
45
|
|
|
41
46
|
if region:
|
|
42
47
|
command += ["--region", region]
|
|
43
48
|
|
|
44
|
-
out = run(command, cwd=cwd)
|
|
49
|
+
out = run(command, cwd=cwd, env=env)
|
|
45
50
|
|
|
46
51
|
return json.loads(out) if out.strip() else {}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: apx-aws-lambda
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: AWS Lambda for Action Platform: the aws/lambda deploy target (SAM) with its overlay, and tools to read stacks and functions.
|
|
5
5
|
Author-email: Action Platform <cloud@actionplatform.io>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -42,9 +42,44 @@ target = "aws/lambda"
|
|
|
42
42
|
region = "us-east-1" # optional; samconfig.toml / AWS_REGION otherwise
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
+
## Credentials without keys
|
|
46
|
+
|
|
47
|
+
Set `role_arn` and nothing else: the target assumes that role with a short-lived OIDC token the platform signs (`sts assume-role-with-web-identity`). No access key on the platform, on your machine or in the repository; what the app may do is the role's policy.
|
|
48
|
+
|
|
49
|
+
```toml
|
|
50
|
+
[deploy]
|
|
51
|
+
target = "aws/lambda"
|
|
52
|
+
role_arn = "arn:aws:iam::123456789012:role/shop-deploy"
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Once per AWS account, register the platform as an identity provider — its discovery document is `https://<platform>/.well-known/openid-configuration`:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
aws iam create-open-id-connect-provider --url https://platform.example.com --client-id-list sts.amazonaws.com
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Then a role per app (or per project) whose trust policy names the platform and the app; `requirements/policy.json` from the overlay is its permission policy:
|
|
62
|
+
|
|
63
|
+
```json
|
|
64
|
+
{
|
|
65
|
+
"Version": "2012-10-17",
|
|
66
|
+
"Statement": [{
|
|
67
|
+
"Effect": "Allow",
|
|
68
|
+
"Principal": { "Federated": "arn:aws:iam::123456789012:oidc-provider/platform.example.com" },
|
|
69
|
+
"Action": "sts:AssumeRoleWithWebIdentity",
|
|
70
|
+
"Condition": {
|
|
71
|
+
"StringEquals": { "platform.example.com:aud": "sts.amazonaws.com" },
|
|
72
|
+
"StringLike": { "platform.example.com:sub": "org:acme:project:shop:app:orders" }
|
|
73
|
+
}
|
|
74
|
+
}]
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
`sub` is `org:<org>:project:<project>:app:<app>` for a deploy the platform runs; `org:<org>` plus an `actor` claim for `action-platform deploy` from a logged-in machine (`StringLike` with `org:acme:*` covers both). Without `role_arn` the AWS CLI's own chain applies: `aws sso login`, a profile, an instance role — still no key in a file when you use SSO.
|
|
79
|
+
|
|
45
80
|
Tools (`action-platform mcp`): `aws_lambda_stacks`, `aws_lambda_functions`. Commands: `action-platform aws-lambda stacks|functions`. Deploying itself goes through the core's `deploy` / `rollback` / `diagnose`, which drive the target.
|
|
46
81
|
|
|
47
|
-
Needs: AWS CLI v2 (`aws`), SAM CLI (`sam`),
|
|
82
|
+
Needs: AWS CLI v2 (`aws`), SAM CLI (`sam`), `AWS_REGION` (or `region` under `[deploy]`), and either `role_arn` (OIDC, no keys) or the AWS CLI's own credentials. Talks to `*.amazonaws.com` only.
|
|
48
83
|
|
|
49
84
|
## Development
|
|
50
85
|
|
|
@@ -141,3 +141,51 @@ class PluginTest(unittest.TestCase):
|
|
|
141
141
|
names = {t.name for t in asyncio.run(server.build().list_tools())}
|
|
142
142
|
|
|
143
143
|
self.assertLessEqual({"aws_lambda_stacks", "aws_lambda_functions"}, names)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
class AssumeRoleTest(unittest.TestCase):
|
|
147
|
+
def test_role_arn_turns_the_platform_token_into_temporary_credentials(self):
|
|
148
|
+
calls: list[list[str]] = []
|
|
149
|
+
|
|
150
|
+
def run(args, cwd=None, env=None):
|
|
151
|
+
calls.append([Path(args[0]).name, *args[1:]])
|
|
152
|
+
if args[1:3] == ["sts", "assume-role-with-web-identity"]:
|
|
153
|
+
return json.dumps(
|
|
154
|
+
{
|
|
155
|
+
"Credentials": {
|
|
156
|
+
"AccessKeyId": "AKIA",
|
|
157
|
+
"SecretAccessKey": "s",
|
|
158
|
+
"SessionToken": "t",
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
)
|
|
162
|
+
return json.dumps(
|
|
163
|
+
{"Stacks": [{"StackStatus": "UPDATE_COMPLETE", "Outputs": []}]}
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
ctx = Context(repo_root=Path("."), identity=lambda aud: f"jwt-for-{aud}")
|
|
167
|
+
|
|
168
|
+
with mock.patch.multiple(shell, run=run, require=lambda t, h: f"/usr/bin/{t}"):
|
|
169
|
+
env = LambdaTarget(
|
|
170
|
+
role_arn="arn:aws:iam::1:role/deploy", region="us-east-1"
|
|
171
|
+
).env(ctx)
|
|
172
|
+
|
|
173
|
+
self.assertEqual(env["AWS_ACCESS_KEY_ID"], "AKIA")
|
|
174
|
+
self.assertEqual(env["AWS_SESSION_TOKEN"], "t")
|
|
175
|
+
sts = next(
|
|
176
|
+
c for c in calls if c[1:3] == ["sts", "assume-role-with-web-identity"]
|
|
177
|
+
)
|
|
178
|
+
self.assertIn("jwt-for-sts.amazonaws.com", sts)
|
|
179
|
+
self.assertIn("arn:aws:iam::1:role/deploy", sts)
|
|
180
|
+
|
|
181
|
+
def test_without_an_issuer_or_a_login_it_says_so(self):
|
|
182
|
+
with (
|
|
183
|
+
mock.patch.object(shell, "require", lambda t, h: "/usr/bin/aws"),
|
|
184
|
+
mock.patch.dict("os.environ", {"AP_HOME": "/nonexistent"}, clear=False),
|
|
185
|
+
):
|
|
186
|
+
with self.assertRaises(DeployError) as caught:
|
|
187
|
+
LambdaTarget(role_arn="arn:aws:iam::1:role/deploy").env(
|
|
188
|
+
Context(repo_root=Path("."))
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
self.assertIn("log in", str(caught.exception))
|
apx_aws_lambda-0.1.0/README.md
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
# apx-aws-lambda
|
|
2
|
-
|
|
3
|
-
AWS Lambda for [Action Platform](https://github.com/actionplatform/action-platform): the `aws/lambda` deploy target (SAM) with its overlay, read-only tools and commands. `apx-` is the prefix every Action Platform extension carries.
|
|
4
|
-
|
|
5
|
-
```bash
|
|
6
|
-
action-platform plugin install aws-lambda
|
|
7
|
-
action-platform cloud set aws/lambda # overlay files come from this plugin
|
|
8
|
-
action-platform deploy --dry-run # preflight: sam validate, credentials
|
|
9
|
-
action-platform deploy # sam build + sam deploy --config-env <stage>
|
|
10
|
-
action-platform diagnose
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
| | |
|
|
14
|
-
|---|---|
|
|
15
|
-
| Overlay | `template.yaml`, `samconfig.toml`, `lambda_handler.py`, `Makefile`, `requirements/` (IAM), `.github/workflows/deploy.yml` |
|
|
16
|
-
| Deploy | `sam build` + `sam deploy --config-env <stage>` (`dev` → `default`, `prod` → `prod`) |
|
|
17
|
-
| Rollback | CloudFormation `rollback-stack` — previous stack state |
|
|
18
|
-
| Diagnose | stack status and the HTTP API url |
|
|
19
|
-
| Destroy | `sam delete` |
|
|
20
|
-
|
|
21
|
-
`[deploy]` in platform.toml:
|
|
22
|
-
|
|
23
|
-
```toml
|
|
24
|
-
[deploy]
|
|
25
|
-
target = "aws/lambda"
|
|
26
|
-
region = "us-east-1" # optional; samconfig.toml / AWS_REGION otherwise
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
Tools (`action-platform mcp`): `aws_lambda_stacks`, `aws_lambda_functions`. Commands: `action-platform aws-lambda stacks|functions`. Deploying itself goes through the core's `deploy` / `rollback` / `diagnose`, which drive the target.
|
|
30
|
-
|
|
31
|
-
Needs: AWS CLI v2 (`aws`), SAM CLI (`sam`), credentials in the environment (`AWS_PROFILE` or keys), `AWS_REGION`. Talks to `*.amazonaws.com` only.
|
|
32
|
-
|
|
33
|
-
## Development
|
|
34
|
-
|
|
35
|
-
```bash
|
|
36
|
-
pip install -e ".[dev]"
|
|
37
|
-
pytest
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
The tests fake `aws` and `sam`; nothing reaches AWS.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|