@action-llama/action-llama 0.8.2 → 0.9.1
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.
- package/dist/cli/commands/cloud-deploy.d.ts +15 -0
- package/dist/cli/commands/cloud-deploy.d.ts.map +1 -0
- package/dist/cli/commands/cloud-deploy.js +136 -0
- package/dist/cli/commands/cloud-deploy.js.map +1 -0
- package/dist/cli/commands/cloud-setup.d.ts.map +1 -1
- package/dist/cli/commands/cloud-setup.js +298 -3
- package/dist/cli/commands/cloud-setup.js.map +1 -1
- package/dist/cli/commands/cloud-teardown.js +12 -2
- package/dist/cli/commands/cloud-teardown.js.map +1 -1
- package/dist/cli/commands/logs.d.ts.map +1 -1
- package/dist/cli/commands/logs.js +31 -0
- package/dist/cli/commands/logs.js.map +1 -1
- package/dist/cli/commands/status.d.ts.map +1 -1
- package/dist/cli/commands/status.js +25 -0
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/main.js +8 -0
- package/dist/cli/main.js.map +1 -1
- package/dist/cloud/deploy-apprunner.d.ts +37 -0
- package/dist/cloud/deploy-apprunner.d.ts.map +1 -0
- package/dist/cloud/deploy-apprunner.js +201 -0
- package/dist/cloud/deploy-apprunner.js.map +1 -0
- package/dist/cloud/deploy-cloudrun.d.ts +36 -0
- package/dist/cloud/deploy-cloudrun.d.ts.map +1 -0
- package/dist/cloud/deploy-cloudrun.js +154 -0
- package/dist/cloud/deploy-cloudrun.js.map +1 -0
- package/dist/cloud/image-builder.d.ts +35 -0
- package/dist/cloud/image-builder.d.ts.map +1 -0
- package/dist/cloud/image-builder.js +111 -0
- package/dist/cloud/image-builder.js.map +1 -0
- package/dist/cloud/scheduler-image.d.ts +27 -0
- package/dist/cloud/scheduler-image.d.ts.map +1 -0
- package/dist/cloud/scheduler-image.js +127 -0
- package/dist/cloud/scheduler-image.js.map +1 -0
- package/dist/docker/aws-shared.d.ts.map +1 -1
- package/dist/docker/aws-shared.js +13 -8
- package/dist/docker/aws-shared.js.map +1 -1
- package/dist/docker/ecs-runtime.d.ts +1 -1
- package/dist/docker/ecs-runtime.d.ts.map +1 -1
- package/dist/docker/ecs-runtime.js +2 -2
- package/dist/docker/ecs-runtime.js.map +1 -1
- package/dist/docker/local-runtime.d.ts.map +1 -1
- package/dist/docker/local-runtime.js +4 -2
- package/dist/docker/local-runtime.js.map +1 -1
- package/dist/scheduler/index.d.ts.map +1 -1
- package/dist/scheduler/index.js +22 -107
- package/dist/scheduler/index.js.map +1 -1
- package/dist/shared/aws-constants.d.ts +12 -0
- package/dist/shared/aws-constants.d.ts.map +1 -1
- package/dist/shared/aws-constants.js +12 -0
- package/dist/shared/aws-constants.js.map +1 -1
- package/dist/shared/config.d.ts +4 -0
- package/dist/shared/config.d.ts.map +1 -1
- package/dist/shared/config.js.map +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `al cloud deploy` — Build and deploy the scheduler + agents to the cloud.
|
|
3
|
+
*
|
|
4
|
+
* Steps:
|
|
5
|
+
* 1. Validate config (must have [cloud] section)
|
|
6
|
+
* 2. Run doctor -c to ensure creds are pushed + IAM is set up
|
|
7
|
+
* 3. Create cloud runtime + build all agent images
|
|
8
|
+
* 4. Build scheduler image
|
|
9
|
+
* 5. Deploy scheduler service (App Runner or Cloud Run)
|
|
10
|
+
* 6. Print deployed URL
|
|
11
|
+
*/
|
|
12
|
+
export declare function execute(opts: {
|
|
13
|
+
project: string;
|
|
14
|
+
}): Promise<void>;
|
|
15
|
+
//# sourceMappingURL=cloud-deploy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloud-deploy.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/cloud-deploy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAWH,wBAAsB,OAAO,CAAC,IAAI,EAAE;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA8FtE"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `al cloud deploy` — Build and deploy the scheduler + agents to the cloud.
|
|
3
|
+
*
|
|
4
|
+
* Steps:
|
|
5
|
+
* 1. Validate config (must have [cloud] section)
|
|
6
|
+
* 2. Run doctor -c to ensure creds are pushed + IAM is set up
|
|
7
|
+
* 3. Create cloud runtime + build all agent images
|
|
8
|
+
* 4. Build scheduler image
|
|
9
|
+
* 5. Deploy scheduler service (App Runner or Cloud Run)
|
|
10
|
+
* 6. Print deployed URL
|
|
11
|
+
*/
|
|
12
|
+
import { resolve } from "path";
|
|
13
|
+
import { loadGlobalConfig, discoverAgents, loadAgentConfig, validateAgentConfig } from "../../shared/config.js";
|
|
14
|
+
import { execute as runDoctor } from "./doctor.js";
|
|
15
|
+
import { buildAllImages } from "../../cloud/image-builder.js";
|
|
16
|
+
import { buildSchedulerImage } from "../../cloud/scheduler-image.js";
|
|
17
|
+
import { createLogger } from "../../shared/logger.js";
|
|
18
|
+
export async function execute(opts) {
|
|
19
|
+
const projectPath = resolve(opts.project);
|
|
20
|
+
const globalConfig = loadGlobalConfig(projectPath);
|
|
21
|
+
const cloud = globalConfig.cloud;
|
|
22
|
+
if (!cloud) {
|
|
23
|
+
throw new Error("No [cloud] section found in config.toml. Run 'al cloud setup' first.");
|
|
24
|
+
}
|
|
25
|
+
const logger = createLogger(projectPath, "deploy");
|
|
26
|
+
console.log(`\n=== Cloud Deploy (${cloud.provider}) ===\n`);
|
|
27
|
+
// 1. Run doctor -c to push credentials and reconcile IAM
|
|
28
|
+
console.log("Step 1: Validating credentials and IAM...");
|
|
29
|
+
await runDoctor({ project: opts.project, cloud: true, checkOnly: true });
|
|
30
|
+
console.log("");
|
|
31
|
+
// 2. Set up cloud credential backend
|
|
32
|
+
const { setDefaultBackend } = await import("../../shared/credentials.js");
|
|
33
|
+
const { createBackendFromCloudConfig } = await import("../../shared/remote.js");
|
|
34
|
+
const backend = await createBackendFromCloudConfig(cloud);
|
|
35
|
+
setDefaultBackend(backend);
|
|
36
|
+
// 3. Discover agents and create runtime
|
|
37
|
+
const agentNames = discoverAgents(projectPath);
|
|
38
|
+
if (agentNames.length === 0) {
|
|
39
|
+
throw new Error("No agents found. Create agents first.");
|
|
40
|
+
}
|
|
41
|
+
const agentConfigs = agentNames.map((name) => loadAgentConfig(projectPath, name));
|
|
42
|
+
for (const config of agentConfigs) {
|
|
43
|
+
validateAgentConfig(config);
|
|
44
|
+
}
|
|
45
|
+
const activeAgentConfigs = agentConfigs.filter((a) => (a.scale ?? 1) > 0);
|
|
46
|
+
const runtime = await createCloudRuntime(cloud);
|
|
47
|
+
// 4. Build agent images
|
|
48
|
+
console.log("Step 2: Building agent images...");
|
|
49
|
+
const lastProgress = new Map();
|
|
50
|
+
await buildAllImages({
|
|
51
|
+
projectPath,
|
|
52
|
+
globalConfig,
|
|
53
|
+
activeAgentConfigs,
|
|
54
|
+
runtime,
|
|
55
|
+
runtimeType: cloud.provider,
|
|
56
|
+
logger,
|
|
57
|
+
skills: { locking: true },
|
|
58
|
+
onProgress: (label, msg) => {
|
|
59
|
+
// Avoid repeating the same message for the same label
|
|
60
|
+
if (lastProgress.get(label) !== msg) {
|
|
61
|
+
lastProgress.set(label, msg);
|
|
62
|
+
console.log(` [${label}] ${msg}`);
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
console.log("Agent images built and pushed.\n");
|
|
67
|
+
// 5. Build scheduler image
|
|
68
|
+
console.log("Step 3: Building scheduler image...");
|
|
69
|
+
const schedulerImageUri = await buildSchedulerImage({
|
|
70
|
+
projectPath,
|
|
71
|
+
globalConfig,
|
|
72
|
+
runtime,
|
|
73
|
+
logger,
|
|
74
|
+
onProgress: (msg) => console.log(` ${msg}`),
|
|
75
|
+
});
|
|
76
|
+
console.log(`Scheduler image: ${schedulerImageUri}\n`);
|
|
77
|
+
// 6. Deploy scheduler service
|
|
78
|
+
console.log("Step 4: Deploying scheduler service...");
|
|
79
|
+
const serviceInfo = await deploySchedulerService(cloud, schedulerImageUri);
|
|
80
|
+
console.log(`\nScheduler deployed successfully!`);
|
|
81
|
+
console.log(` URL: ${serviceInfo.serviceUrl}`);
|
|
82
|
+
console.log(` Status: ${serviceInfo.status}`);
|
|
83
|
+
// Print webhook URLs
|
|
84
|
+
const webhookSources = globalConfig.webhooks ?? {};
|
|
85
|
+
const providerTypes = new Set(agentConfigs.flatMap((a) => a.webhooks?.map((t) => webhookSources[t.source]?.type).filter(Boolean) || []));
|
|
86
|
+
if (providerTypes.size > 0) {
|
|
87
|
+
console.log("\nWebhook endpoints:");
|
|
88
|
+
for (const pt of providerTypes) {
|
|
89
|
+
console.log(` ${pt}: ${serviceInfo.serviceUrl}/webhooks/${pt}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
console.log("");
|
|
93
|
+
}
|
|
94
|
+
async function createCloudRuntime(cloud) {
|
|
95
|
+
if (cloud.provider === "cloud-run") {
|
|
96
|
+
const { CloudRunJobRuntime } = await import("../../docker/cloud-run-runtime.js");
|
|
97
|
+
const { gcpProject, region, artifactRegistry, serviceAccount, secretPrefix } = cloud;
|
|
98
|
+
if (!gcpProject || !region || !artifactRegistry || !serviceAccount) {
|
|
99
|
+
throw new Error("Cloud Run deployment requires cloud.gcpProject, cloud.region, " +
|
|
100
|
+
"cloud.artifactRegistry, and cloud.serviceAccount in config.toml");
|
|
101
|
+
}
|
|
102
|
+
return new CloudRunJobRuntime({ gcpProject, region, artifactRegistry, serviceAccount, secretPrefix });
|
|
103
|
+
}
|
|
104
|
+
if (cloud.provider === "ecs") {
|
|
105
|
+
const { ECSFargateRuntime } = await import("../../docker/ecs-runtime.js");
|
|
106
|
+
const cc = cloud;
|
|
107
|
+
if (!cc.awsRegion || !cc.ecsCluster || !cc.ecrRepository || !cc.executionRoleArn || !cc.taskRoleArn || !cc.subnets?.length) {
|
|
108
|
+
throw new Error("ECS deployment requires cloud.awsRegion, cloud.ecsCluster, cloud.ecrRepository, " +
|
|
109
|
+
"cloud.executionRoleArn, cloud.taskRoleArn, and cloud.subnets in config.toml");
|
|
110
|
+
}
|
|
111
|
+
return new ECSFargateRuntime({
|
|
112
|
+
awsRegion: cc.awsRegion,
|
|
113
|
+
ecsCluster: cc.ecsCluster,
|
|
114
|
+
ecrRepository: cc.ecrRepository,
|
|
115
|
+
executionRoleArn: cc.executionRoleArn,
|
|
116
|
+
taskRoleArn: cc.taskRoleArn,
|
|
117
|
+
subnets: cc.subnets,
|
|
118
|
+
securityGroups: cc.securityGroups,
|
|
119
|
+
secretPrefix: cc.awsSecretPrefix,
|
|
120
|
+
buildBucket: cc.buildBucket,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
throw new Error(`Unknown cloud provider: "${cloud.provider}"`);
|
|
124
|
+
}
|
|
125
|
+
async function deploySchedulerService(cloud, imageUri) {
|
|
126
|
+
if (cloud.provider === "ecs") {
|
|
127
|
+
const { deployAppRunner } = await import("../../cloud/deploy-apprunner.js");
|
|
128
|
+
return await deployAppRunner({ imageUri, cloudConfig: cloud });
|
|
129
|
+
}
|
|
130
|
+
if (cloud.provider === "cloud-run") {
|
|
131
|
+
const { deployCloudRun } = await import("../../cloud/deploy-cloudrun.js");
|
|
132
|
+
return await deployCloudRun({ imageUri, cloudConfig: cloud });
|
|
133
|
+
}
|
|
134
|
+
throw new Error(`Unknown cloud provider: "${cloud.provider}"`);
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=cloud-deploy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloud-deploy.js","sourceRoot":"","sources":["../../../src/cli/commands/cloud-deploy.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloud-setup.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/cloud-setup.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cloud-setup.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/cloud-setup.ts"],"names":[],"mappings":"AAgDA,wBAAsB,OAAO,CAAC,IAAI,EAAE;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA0GtE"}
|
|
@@ -8,7 +8,7 @@ import { teardownCloud } from "./cloud-teardown.js";
|
|
|
8
8
|
import { STSClient, GetCallerIdentityCommand } from "@aws-sdk/client-sts";
|
|
9
9
|
import { ECRClient, DescribeRepositoriesCommand, CreateRepositoryCommand, SetRepositoryPolicyCommand, } from "@aws-sdk/client-ecr";
|
|
10
10
|
import { ECSClient, ListClustersCommand, DescribeClustersCommand, CreateClusterCommand, } from "@aws-sdk/client-ecs";
|
|
11
|
-
import { IAMClient, ListRolesCommand, CreateRoleCommand, GetRoleCommand, AttachRolePolicyCommand, PutRolePolicyCommand, PutUserPolicyCommand, } from "@aws-sdk/client-iam";
|
|
11
|
+
import { IAMClient, ListRolesCommand, CreateRoleCommand, GetRoleCommand, AttachRolePolicyCommand, PutRolePolicyCommand, PutUserPolicyCommand, CreateServiceLinkedRoleCommand, } from "@aws-sdk/client-iam";
|
|
12
12
|
import { EC2Client, DescribeVpcsCommand, DescribeSubnetsCommand, DescribeSecurityGroupsCommand, } from "@aws-sdk/client-ec2";
|
|
13
13
|
import { CloudWatchLogsClient, CreateLogGroupCommand, } from "@aws-sdk/client-cloudwatch-logs";
|
|
14
14
|
import { AWS_CONSTANTS } from "../../shared/aws-constants.js";
|
|
@@ -146,6 +146,8 @@ async function setupEcsCloud(cloud) {
|
|
|
146
146
|
let accountId;
|
|
147
147
|
const identity = await stsClient.send(new GetCallerIdentityCommand({}));
|
|
148
148
|
accountId = identity.Account;
|
|
149
|
+
// Ensure service-linked roles exist (one-time per AWS account)
|
|
150
|
+
await ensureServiceLinkedRoles(iamClient);
|
|
149
151
|
cloud.ecrRepository = await pickOrCreateEcrRepo(ecrClient, region, accountId);
|
|
150
152
|
await ensureLambdaEcrPolicy(ecrClient, cloud.ecrRepository);
|
|
151
153
|
cloud.ecsCluster = await pickOrCreateEcsCluster(ecsClient);
|
|
@@ -197,6 +199,9 @@ async function setupEcsCloud(cloud) {
|
|
|
197
199
|
}
|
|
198
200
|
// Create CodeBuild service role for remote image builds
|
|
199
201
|
await ensureCodeBuildRole(iamClient, accountId, region, cloud.ecrRepository);
|
|
202
|
+
// Create App Runner roles for cloud deploy
|
|
203
|
+
cloud.appRunnerAccessRoleArn = await ensureAppRunnerAccessRole(iamClient);
|
|
204
|
+
cloud.appRunnerInstanceRoleArn = await ensureAppRunnerInstanceRole(iamClient, accountId, region, cloud.ecrRepository);
|
|
200
205
|
const result = await pickVpcAndSubnets(ec2Client);
|
|
201
206
|
cloud.subnets = result.subnets;
|
|
202
207
|
const sgs = await pickSecurityGroups(ec2Client, result.vpcId);
|
|
@@ -230,13 +235,37 @@ async function setupEcsCloud(cloud) {
|
|
|
230
235
|
Resource: [
|
|
231
236
|
`arn:aws:logs:${region}:${accountId}:log-group:${AWS_CONSTANTS.LOG_GROUP}*`,
|
|
232
237
|
`arn:aws:logs:${region}:${accountId}:log-group:${AWS_CONSTANTS.LAMBDA_LOG_GROUP}/al-*`,
|
|
238
|
+
`arn:aws:logs:${region}:${accountId}:log-group:${AWS_CONSTANTS.APPRUNNER_LOG_GROUP}*`,
|
|
233
239
|
],
|
|
234
240
|
},
|
|
241
|
+
{
|
|
242
|
+
Effect: "Allow",
|
|
243
|
+
Action: [
|
|
244
|
+
"apprunner:CreateService",
|
|
245
|
+
"apprunner:UpdateService",
|
|
246
|
+
"apprunner:DescribeService",
|
|
247
|
+
"apprunner:DeleteService",
|
|
248
|
+
],
|
|
249
|
+
Resource: `arn:aws:apprunner:${region}:${accountId}:service/al-scheduler/*`,
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
Effect: "Allow",
|
|
253
|
+
Action: "apprunner:ListServices",
|
|
254
|
+
Resource: "*",
|
|
255
|
+
},
|
|
235
256
|
{
|
|
236
257
|
Effect: "Allow",
|
|
237
258
|
Action: "iam:PutUserPolicy",
|
|
238
259
|
Resource: `arn:aws:iam::${accountId}:user/${userName}`,
|
|
239
260
|
},
|
|
261
|
+
{
|
|
262
|
+
Effect: "Allow",
|
|
263
|
+
Action: "iam:CreateServiceLinkedRole",
|
|
264
|
+
Resource: [
|
|
265
|
+
`arn:aws:iam::${accountId}:role/aws-service-role/ecs.amazonaws.com/*`,
|
|
266
|
+
`arn:aws:iam::${accountId}:role/aws-service-role/apprunner.amazonaws.com/*`,
|
|
267
|
+
],
|
|
268
|
+
},
|
|
240
269
|
],
|
|
241
270
|
});
|
|
242
271
|
try {
|
|
@@ -255,6 +284,30 @@ async function setupEcsCloud(cloud) {
|
|
|
255
284
|
}
|
|
256
285
|
return true;
|
|
257
286
|
}
|
|
287
|
+
// --- Service-linked roles ---
|
|
288
|
+
async function ensureServiceLinkedRoles(iamClient) {
|
|
289
|
+
const services = [
|
|
290
|
+
{ name: "ECS", serviceName: "ecs.amazonaws.com" },
|
|
291
|
+
{ name: "App Runner", serviceName: "apprunner.amazonaws.com" },
|
|
292
|
+
];
|
|
293
|
+
for (const svc of services) {
|
|
294
|
+
try {
|
|
295
|
+
await iamClient.send(new CreateServiceLinkedRoleCommand({
|
|
296
|
+
AWSServiceName: svc.serviceName,
|
|
297
|
+
}));
|
|
298
|
+
console.log(` Created service-linked role for ${svc.name}`);
|
|
299
|
+
}
|
|
300
|
+
catch (err) {
|
|
301
|
+
if (err.name === "InvalidInputException" && err.message?.includes("already exists")) {
|
|
302
|
+
console.log(` Service-linked role for ${svc.name} already exists`);
|
|
303
|
+
}
|
|
304
|
+
else {
|
|
305
|
+
console.log(` Warning: could not create service-linked role for ${svc.name}: ${err.message}`);
|
|
306
|
+
console.log(` You may need to run: aws iam create-service-linked-role --aws-service-name ${svc.serviceName}`);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
258
311
|
// --- Resource pickers ---
|
|
259
312
|
async function pickOrCreateEcrRepo(ecrClient, region, accountId) {
|
|
260
313
|
console.log("Looking for ECR repositories...");
|
|
@@ -399,12 +452,19 @@ async function pickOrCreateEcsRole(iamClient, label, defaultName, managedPolicie
|
|
|
399
452
|
}
|
|
400
453
|
});
|
|
401
454
|
if (ecsRoles.length > 0) {
|
|
455
|
+
// Sort so the expected default role appears first
|
|
456
|
+
const sorted = [...ecsRoles].sort((a, b) => {
|
|
457
|
+
const aMatch = a.RoleName === defaultName ? 0 : 1;
|
|
458
|
+
const bMatch = b.RoleName === defaultName ? 0 : 1;
|
|
459
|
+
return aMatch - bMatch || a.RoleName.localeCompare(b.RoleName);
|
|
460
|
+
});
|
|
461
|
+
const defaultArn = sorted.find((r) => r.RoleName === defaultName)?.Arn;
|
|
402
462
|
const choices = [
|
|
403
|
-
...
|
|
463
|
+
...sorted.map((r) => ({ name: r.RoleName, value: r.Arn })),
|
|
404
464
|
{ name: `Create new: ${defaultName}`, value: CREATE_NEW },
|
|
405
465
|
{ name: "Enter ARN manually", value: MANUAL_INPUT },
|
|
406
466
|
];
|
|
407
|
-
const choice = await select({ message: `${label}:`, choices });
|
|
467
|
+
const choice = await select({ message: `${label}:`, choices, default: defaultArn });
|
|
408
468
|
if (choice === MANUAL_INPUT)
|
|
409
469
|
return input({ message: `${label} ARN:` });
|
|
410
470
|
if (choice !== CREATE_NEW)
|
|
@@ -609,6 +669,241 @@ async function pickVpcAndSubnets(ec2Client) {
|
|
|
609
669
|
const raw = await input({ message: "Subnet IDs (comma-separated):" });
|
|
610
670
|
return { subnets: raw.split(",").map(s => s.trim()).filter(Boolean), vpcId };
|
|
611
671
|
}
|
|
672
|
+
async function ensureAppRunnerAccessRole(iamClient) {
|
|
673
|
+
const roleName = AWS_CONSTANTS.APPRUNNER_ACCESS_ROLE;
|
|
674
|
+
console.log(`\nEnsuring App Runner access role (${roleName})...`);
|
|
675
|
+
const trustPolicy = JSON.stringify({
|
|
676
|
+
Version: "2012-10-17",
|
|
677
|
+
Statement: [{
|
|
678
|
+
Effect: "Allow",
|
|
679
|
+
Principal: { Service: "build.apprunner.amazonaws.com" },
|
|
680
|
+
Action: "sts:AssumeRole",
|
|
681
|
+
}],
|
|
682
|
+
});
|
|
683
|
+
try {
|
|
684
|
+
const data = await iamClient.send(new CreateRoleCommand({
|
|
685
|
+
RoleName: roleName,
|
|
686
|
+
AssumeRolePolicyDocument: trustPolicy,
|
|
687
|
+
}));
|
|
688
|
+
console.log(` Created role: ${roleName}`);
|
|
689
|
+
}
|
|
690
|
+
catch (err) {
|
|
691
|
+
if (err.name === "EntityAlreadyExistsException") {
|
|
692
|
+
console.log(` Role already exists`);
|
|
693
|
+
}
|
|
694
|
+
else {
|
|
695
|
+
console.log(` Warning: could not create ${roleName}: ${err.message}`);
|
|
696
|
+
return input({ message: "App Runner access role ARN:" });
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
try {
|
|
700
|
+
await iamClient.send(new AttachRolePolicyCommand({
|
|
701
|
+
RoleName: roleName,
|
|
702
|
+
PolicyArn: "arn:aws:iam::aws:policy/service-role/AWSAppRunnerServicePolicyForECRAccess",
|
|
703
|
+
}));
|
|
704
|
+
console.log(` Attached AWSAppRunnerServicePolicyForECRAccess`);
|
|
705
|
+
}
|
|
706
|
+
catch (err) {
|
|
707
|
+
console.log(` Warning: could not attach ECR access policy: ${err.message}`);
|
|
708
|
+
}
|
|
709
|
+
const data = await iamClient.send(new GetRoleCommand({ RoleName: roleName }));
|
|
710
|
+
return data.Role.Arn;
|
|
711
|
+
}
|
|
712
|
+
async function ensureAppRunnerInstanceRole(iamClient, accountId, region, ecrRepository) {
|
|
713
|
+
const roleName = AWS_CONSTANTS.APPRUNNER_INSTANCE_ROLE;
|
|
714
|
+
console.log(`\nEnsuring App Runner instance role (${roleName})...`);
|
|
715
|
+
const trustPolicy = JSON.stringify({
|
|
716
|
+
Version: "2012-10-17",
|
|
717
|
+
Statement: [{
|
|
718
|
+
Effect: "Allow",
|
|
719
|
+
Principal: { Service: "tasks.apprunner.amazonaws.com" },
|
|
720
|
+
Action: "sts:AssumeRole",
|
|
721
|
+
}],
|
|
722
|
+
});
|
|
723
|
+
try {
|
|
724
|
+
await iamClient.send(new CreateRoleCommand({
|
|
725
|
+
RoleName: roleName,
|
|
726
|
+
AssumeRolePolicyDocument: trustPolicy,
|
|
727
|
+
}));
|
|
728
|
+
console.log(` Created role: ${roleName}`);
|
|
729
|
+
}
|
|
730
|
+
catch (err) {
|
|
731
|
+
if (err.name === "EntityAlreadyExistsException") {
|
|
732
|
+
console.log(` Role already exists`);
|
|
733
|
+
}
|
|
734
|
+
else {
|
|
735
|
+
console.log(` Warning: could not create ${roleName}: ${err.message}`);
|
|
736
|
+
return input({ message: "App Runner instance role ARN:" });
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
// The instance role needs the same permissions as the operator:
|
|
740
|
+
// ECS, ECR, CodeBuild, S3, SecretsManager, CloudWatch Logs, IAM, Lambda, App Runner
|
|
741
|
+
const repoName = ecrRepository.split("/").pop();
|
|
742
|
+
const bucketName = AWS_CONSTANTS.buildBucket(accountId, region);
|
|
743
|
+
try {
|
|
744
|
+
await iamClient.send(new PutRolePolicyCommand({
|
|
745
|
+
RoleName: roleName,
|
|
746
|
+
PolicyName: "ActionLlamaScheduler",
|
|
747
|
+
PolicyDocument: JSON.stringify({
|
|
748
|
+
Version: "2012-10-17",
|
|
749
|
+
Statement: [
|
|
750
|
+
{
|
|
751
|
+
Sid: "Identity",
|
|
752
|
+
Effect: "Allow",
|
|
753
|
+
Action: "sts:GetCallerIdentity",
|
|
754
|
+
Resource: "*",
|
|
755
|
+
},
|
|
756
|
+
{
|
|
757
|
+
Sid: "ECS",
|
|
758
|
+
Effect: "Allow",
|
|
759
|
+
Action: [
|
|
760
|
+
"ecs:RegisterTaskDefinition",
|
|
761
|
+
"ecs:RunTask",
|
|
762
|
+
"ecs:DescribeTasks",
|
|
763
|
+
"ecs:ListTasks",
|
|
764
|
+
"ecs:StopTask",
|
|
765
|
+
],
|
|
766
|
+
Resource: "*",
|
|
767
|
+
},
|
|
768
|
+
{
|
|
769
|
+
Sid: "Logs",
|
|
770
|
+
Effect: "Allow",
|
|
771
|
+
Action: [
|
|
772
|
+
"logs:CreateLogGroup",
|
|
773
|
+
"logs:CreateLogStream",
|
|
774
|
+
"logs:PutLogEvents",
|
|
775
|
+
"logs:GetLogEvents",
|
|
776
|
+
"logs:FilterLogEvents",
|
|
777
|
+
],
|
|
778
|
+
Resource: [
|
|
779
|
+
`arn:aws:logs:${region}:${accountId}:log-group:/ecs/action-llama*`,
|
|
780
|
+
`arn:aws:logs:${region}:${accountId}:log-group:/aws/lambda/al-*`,
|
|
781
|
+
`arn:aws:logs:${region}:${accountId}:log-group:/apprunner/al-scheduler*`,
|
|
782
|
+
],
|
|
783
|
+
},
|
|
784
|
+
{
|
|
785
|
+
Sid: "SecretsManager",
|
|
786
|
+
Effect: "Allow",
|
|
787
|
+
Action: [
|
|
788
|
+
"secretsmanager:ListSecrets",
|
|
789
|
+
"secretsmanager:CreateSecret",
|
|
790
|
+
"secretsmanager:PutSecretValue",
|
|
791
|
+
"secretsmanager:GetSecretValue",
|
|
792
|
+
],
|
|
793
|
+
Resource: "*",
|
|
794
|
+
},
|
|
795
|
+
{
|
|
796
|
+
Sid: "PassRole",
|
|
797
|
+
Effect: "Allow",
|
|
798
|
+
Action: "iam:PassRole",
|
|
799
|
+
Resource: `arn:aws:iam::${accountId}:role/al-*`,
|
|
800
|
+
Condition: {
|
|
801
|
+
StringEquals: {
|
|
802
|
+
"iam:PassedToService": [
|
|
803
|
+
"ecs-tasks.amazonaws.com",
|
|
804
|
+
"codebuild.amazonaws.com",
|
|
805
|
+
"lambda.amazonaws.com",
|
|
806
|
+
"apprunner.amazonaws.com",
|
|
807
|
+
],
|
|
808
|
+
},
|
|
809
|
+
},
|
|
810
|
+
},
|
|
811
|
+
{
|
|
812
|
+
Sid: "IAMAgentRoles",
|
|
813
|
+
Effect: "Allow",
|
|
814
|
+
Action: [
|
|
815
|
+
"iam:CreateRole",
|
|
816
|
+
"iam:GetRole",
|
|
817
|
+
"iam:GetRolePolicy",
|
|
818
|
+
"iam:PutRolePolicy",
|
|
819
|
+
"iam:DeleteRole",
|
|
820
|
+
"iam:DeleteRolePolicy",
|
|
821
|
+
"iam:AttachRolePolicy",
|
|
822
|
+
],
|
|
823
|
+
Resource: `arn:aws:iam::${accountId}:role/al-*`,
|
|
824
|
+
},
|
|
825
|
+
{
|
|
826
|
+
Sid: "IAMListRoles",
|
|
827
|
+
Effect: "Allow",
|
|
828
|
+
Action: "iam:ListRoles",
|
|
829
|
+
Resource: "*",
|
|
830
|
+
},
|
|
831
|
+
{
|
|
832
|
+
Sid: "ECR",
|
|
833
|
+
Effect: "Allow",
|
|
834
|
+
Action: [
|
|
835
|
+
"ecr:BatchGetImage",
|
|
836
|
+
"ecr:GetDownloadUrlForLayer",
|
|
837
|
+
"ecr:BatchCheckLayerAvailability",
|
|
838
|
+
"ecr:GetAuthorizationToken",
|
|
839
|
+
"ecr:SetRepositoryPolicy",
|
|
840
|
+
],
|
|
841
|
+
Resource: "*",
|
|
842
|
+
},
|
|
843
|
+
{
|
|
844
|
+
Sid: "CodeBuild",
|
|
845
|
+
Effect: "Allow",
|
|
846
|
+
Action: [
|
|
847
|
+
"codebuild:StartBuild",
|
|
848
|
+
"codebuild:BatchGetBuilds",
|
|
849
|
+
"codebuild:CreateProject",
|
|
850
|
+
],
|
|
851
|
+
Resource: `arn:aws:codebuild:${region}:${accountId}:project/al-image-builder`,
|
|
852
|
+
},
|
|
853
|
+
{
|
|
854
|
+
Sid: "Lambda",
|
|
855
|
+
Effect: "Allow",
|
|
856
|
+
Action: [
|
|
857
|
+
"lambda:GetFunction",
|
|
858
|
+
"lambda:CreateFunction",
|
|
859
|
+
"lambda:UpdateFunctionCode",
|
|
860
|
+
"lambda:UpdateFunctionConfiguration",
|
|
861
|
+
"lambda:InvokeFunction",
|
|
862
|
+
],
|
|
863
|
+
Resource: `arn:aws:lambda:${region}:${accountId}:function:al-*`,
|
|
864
|
+
},
|
|
865
|
+
{
|
|
866
|
+
Sid: "S3",
|
|
867
|
+
Effect: "Allow",
|
|
868
|
+
Action: [
|
|
869
|
+
"s3:CreateBucket",
|
|
870
|
+
"s3:PutObject",
|
|
871
|
+
"s3:GetObject",
|
|
872
|
+
"s3:ListBucket",
|
|
873
|
+
],
|
|
874
|
+
Resource: [
|
|
875
|
+
`arn:aws:s3:::${bucketName}`,
|
|
876
|
+
`arn:aws:s3:::${bucketName}/*`,
|
|
877
|
+
],
|
|
878
|
+
},
|
|
879
|
+
{
|
|
880
|
+
Sid: "AppRunner",
|
|
881
|
+
Effect: "Allow",
|
|
882
|
+
Action: [
|
|
883
|
+
"apprunner:CreateService",
|
|
884
|
+
"apprunner:UpdateService",
|
|
885
|
+
"apprunner:DescribeService",
|
|
886
|
+
"apprunner:DeleteService",
|
|
887
|
+
],
|
|
888
|
+
Resource: `arn:aws:apprunner:${region}:${accountId}:service/al-scheduler/*`,
|
|
889
|
+
},
|
|
890
|
+
{
|
|
891
|
+
Sid: "AppRunnerList",
|
|
892
|
+
Effect: "Allow",
|
|
893
|
+
Action: "apprunner:ListServices",
|
|
894
|
+
Resource: "*",
|
|
895
|
+
},
|
|
896
|
+
],
|
|
897
|
+
}),
|
|
898
|
+
}));
|
|
899
|
+
console.log(` Attached ActionLlamaScheduler policy`);
|
|
900
|
+
}
|
|
901
|
+
catch (err) {
|
|
902
|
+
console.log(` Warning: could not attach policy to ${roleName}: ${err.message}`);
|
|
903
|
+
}
|
|
904
|
+
const data = await iamClient.send(new GetRoleCommand({ RoleName: roleName }));
|
|
905
|
+
return data.Role.Arn;
|
|
906
|
+
}
|
|
612
907
|
async function pickSecurityGroups(ec2Client, vpcId) {
|
|
613
908
|
if (!vpcId) {
|
|
614
909
|
const raw = await input({ message: "Security group IDs (comma-separated, optional):" });
|