1import * as AWS from "alchemy/AWS";
2import * as Output from "alchemy/Output";
3import * as Effect from "effect/Effect";
4
5/**
6 * Shared infrastructure for the HyperPod cluster.
7 *
8 * Resources are memoized by logical id, so this Effect can be yielded from
9 * the stack program and from any other module and always converges on the
10 * same single resource instances.
11 */
12export const HyperPodInfra = Effect.gen(function* () {
13 /**
14 * The bucket holding the cluster's lifecycle scripts. Every HyperPod
15 * instance group downloads and runs its `OnCreate` script from S3 when a
16 * node boots.
17 */
18 const bucket = yield* AWS.S3.Bucket("LifecycleScripts", {
19 forceDestroy: true,
20 });
21
22 /**
23 * The instance execution role. HyperPod nodes assume it to pull the
24 * lifecycle script (inline grant on the bucket) and to publish logs and
25 * metrics (the AWS-managed cluster-instance policy).
26 */
27 const role = yield* AWS.IAM.Role("HyperPodInstanceRole", {
28 assumeRolePolicyDocument: {
29 Version: "2012-10-17",
30 Statement: [
31 {
32 Effect: "Allow",
33 Principal: { Service: "sagemaker.amazonaws.com" },
34 Action: ["sts:AssumeRole"],
35 },
36 ],
37 },
38 managedPolicyArns: [
39 "arn:aws:iam::aws:policy/AmazonSageMakerClusterInstanceRolePolicy",
40 ],
41 inlinePolicies: {
42 "lifecycle-scripts": {
43 Version: "2012-10-17",
44 Statement: [
45 {
46 Effect: "Allow",
47 Action: ["s3:GetObject", "s3:ListBucket"],
48 Resource: [
49 bucket.bucketArn,
50 Output.interpolate`${bucket.bucketArn}/*`,
51 ],
52 },
53 ],
54 },
55 },
56 });
57
58 return { bucket, role };
59});