diff --git a/MODULE.bazel b/MODULE.bazel index a359293400..7e9ca9566e 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -121,6 +121,7 @@ use_repo( go_deps, "co_honnef_go_tools", "com_github_a_h_generate", + "com_github_aws_aws_lambda_go", "com_github_bazelbuild_bazel_watcher", "com_github_bazelbuild_buildtools", "com_github_go_delve_delve", diff --git a/go.mod b/go.mod index fe28d2c41d..9b4ebb049f 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ toolchain go1.23.4 require ( github.com/a-h/generate v0.0.0-20220105161013-96c14dfdfb60 + github.com/aws/aws-lambda-go v1.47.0 github.com/bazelbuild/bazel-gazelle v0.40.0 github.com/bazelbuild/bazel-watcher v0.25.3 github.com/bazelbuild/buildtools v0.0.0-20240918101019-be1c24cc9a44 diff --git a/go.sum b/go.sum index dc44436cc4..ebb794551f 100644 --- a/go.sum +++ b/go.sum @@ -6,6 +6,8 @@ github.com/a-h/generate v0.0.0-20220105161013-96c14dfdfb60 h1:/rNdG6EuzjwcR1KRFp github.com/a-h/generate v0.0.0-20220105161013-96c14dfdfb60/go.mod h1:traiLYQ0YD7qUMCdjo6/jSaJRPHXniX4HVs+PhEhYpc= github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= +github.com/aws/aws-lambda-go v1.47.0 h1:0H8s0vumYx/YKs4sE7YM0ktwL2eWse+kfopsRI1sXVI= +github.com/aws/aws-lambda-go v1.47.0/go.mod h1:dpMpZgvWx5vuQJfBt0zqBha60q7Dd7RfgJv23DymV8A= github.com/bazelbuild/bazel-gazelle v0.40.0 h1:SAYys3KRG5i3KTgQAvO423bLT1rQMSgqEKReMkM/CW0= github.com/bazelbuild/bazel-gazelle v0.40.0/go.mod h1:xI42W5YdKQg0g3rj1jZfdW8j1UihNmvb5KwWDdHg2ec= github.com/bazelbuild/bazel-watcher v0.25.3 h1:qX33Z4DDPXpe9Ry0KGTvPkuuTekrB1b59E5fQk5BjiY= @@ -247,10 +249,6 @@ github.com/tdewolff/parse/v2 v2.7.19 h1:7Ljh26yj+gdLFEq/7q9LT4SYyKtwQX4ocNrj45UC github.com/tdewolff/parse/v2 v2.7.19/go.mod h1:3FbJWZp3XT9OWVN3Hmfp0p/a08v4h8J9W1aghka0soA= github.com/tdewolff/test v1.0.11-0.20231101010635-f1265d231d52 h1:gAQliwn+zJrkjAHVcBEYW/RFvd2St4yYimisvozAYlA= github.com/tdewolff/test v1.0.11-0.20231101010635-f1265d231d52/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE= -github.com/twilio/twilio-go v1.23.6 h1:9gjIZ8w3MN+8ifPZgK74vF3CLfnJ6ytMNqOI2r2ipLs= -github.com/twilio/twilio-go v1.23.6/go.mod h1:zRkMjudW7v7MqQ3cWNZmSoZJ7EBjPZ4OpNh2zm7Q6ko= -github.com/twilio/twilio-go v1.23.7 h1:2h22Typ0Y8knSx8mhEgwnWmVdyWObgDvp/Sa3Dxq2vs= -github.com/twilio/twilio-go v1.23.7/go.mod h1:zRkMjudW7v7MqQ3cWNZmSoZJ7EBjPZ4OpNh2zm7Q6ko= github.com/twilio/twilio-go v1.23.8 h1:kuuYWsNHFVK9JEAnOqBfnsgtLy+fYdapqCV5SBr3nXU= github.com/twilio/twilio-go v1.23.8/go.mod h1:zRkMjudW7v7MqQ3cWNZmSoZJ7EBjPZ4OpNh2zm7Q6ko= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= @@ -295,16 +293,12 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= -golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= -golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -333,8 +327,6 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220906165534-d0df966e6959/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= -golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -343,16 +335,12 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= -golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o= -golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q= golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= golang.org/x/tools/go/vcs v0.1.0-deprecated h1:cOIJqWBl99H1dH5LWizPa+0ImeeJq3t3cJjaeOWUAL4= diff --git a/ts/pulumi/BUILD.bazel b/ts/pulumi/BUILD.bazel index 56b8e484ac..cbe9276400 100644 --- a/ts/pulumi/BUILD.bazel +++ b/ts/pulumi/BUILD.bazel @@ -31,6 +31,7 @@ ts_project( "//ts/pulumi/lulu.computer:pulumi_ts", "//ts/pulumi/pleaseintroducemetoyour.dog:ts", "//ts/pulumi/shadwell.im", + "//ts/pulumi/zemn.me/hello_world:ts", ], ) diff --git a/ts/pulumi/lib/oci/oci_image.tmpl.ts b/ts/pulumi/lib/oci/oci_image.tmpl.ts index 7c06416983..d4eaa478ab 100644 --- a/ts/pulumi/lib/oci/oci_image.tmpl.ts +++ b/ts/pulumi/lib/oci/oci_image.tmpl.ts @@ -1,13 +1,50 @@ +import { createHash } from 'node:crypto'; +import { writeFile } from 'node:fs/promises'; +import { tmpdir } from 'node:os'; +import path, { resolve } from 'node:path'; + import { local } from '@pulumi/command'; -import { ComponentResource, ComponentResourceOptions, Input, output } from "@pulumi/pulumi"; +import { all, ComponentResource, ComponentResourceOptions, Input, Output, output } from "@pulumi/pulumi"; export interface Args { repository: Input + token?: Input +} + +export async function createPodmanAuthFile( + token: string, + registry: string +): Promise { + // Encode credentials + + // Build the auth.json structure + const authData = { + auths: { + [`https://${registry}`]: { + auth: token + }, + }, + }; + + const fileContent = JSON.stringify(authData) + + const hash = createHash('sha256').update(fileContent).digest('hex'); + + // Generate a temporary file path + const tempDir = tmpdir(); + const randomName = `${hash}.json`; + const filePath = path.join(tempDir, randomName); + + // Write the JSON data to the file + await writeFile(filePath, JSON.stringify(authData)); + + // Return the path to the newly created temporary file + return resolve(filePath) } export class __ClassName extends ComponentResource { - url: string = "TODO" // will fill once i've deployed for the first time + url: Output constructor( name: string, args: Args, @@ -15,15 +52,29 @@ export class __ClassName extends ComponentResource { ) { super('__TYPE', name, args, opts); + const arg = all([args.repository]).apply(([repo]) => [ + "--repository", `${repo}` + ]) + + const authFile = all([args.token, args.repository]).apply(([token, registry]) => { + if (!token || !registry) return; + return createPodmanAuthFile(token, registry) + }); + const upload = new local.Command(`${name}_push`, { - create: - output(args.repository).apply(repository => [ + environment: output(authFile).apply(f => ({ + REGISTRY_AUTH_FILE: f + })as {[v: string]: string}), + interpreter: + arg.apply(arg => [ "__PUSH_BIN", - "--repository", - repository, - ].join(" ")) + "-v", + ...arg + ]) }, { parent: this }) + this.url = upload.stdout + super.registerOutputs({ upload }) } diff --git a/ts/pulumi/lib/oci/rules.bzl b/ts/pulumi/lib/oci/rules.bzl index 5f39a8508f..4f96df0024 100644 --- a/ts/pulumi/lib/oci/rules.bzl +++ b/ts/pulumi/lib/oci/rules.bzl @@ -33,7 +33,7 @@ def pulumi_image( oci_push( name = name + "_push_bin", image = src, - repository = "why is this mandatory", + repository = "None", ) expand_template_rule( diff --git a/ts/pulumi/lib/tags.ts b/ts/pulumi/lib/tags.ts index c0d6bdd3ff..fd6a04cd3a 100644 --- a/ts/pulumi/lib/tags.ts +++ b/ts/pulumi/lib/tags.ts @@ -1,6 +1,6 @@ import { all, Input } from '@pulumi/pulumi'; -type TagSet = Input>>; +export type TagSet = Input>>; export const mergeTags = (a?: TagSet, b?: TagSet): TagSet => all([a, b]).apply(([a, b]) => ({ ...a, ...b })); diff --git a/ts/pulumi/zemn.me/hello_world/BUILD.bazel b/ts/pulumi/zemn.me/hello_world/BUILD.bazel new file mode 100644 index 0000000000..4539d6a4bb --- /dev/null +++ b/ts/pulumi/zemn.me/hello_world/BUILD.bazel @@ -0,0 +1,60 @@ +load("@rules_oci//oci:defs.bzl", "oci_image") +load("@rules_pkg//pkg:tar.bzl", "pkg_tar") +load("//bzl:rules.bzl", "bazel_lint") +load("//go:rules.bzl", "go_binary", "go_library") +load("//ts:rules.bzl", "ts_project") +load("//ts/pulumi/lib/oci:rules.bzl", "pulumi_image") + +go_library( + name = "hello_world_lib", + srcs = ["hello.go"], + importpath = "github.com/zemn-me/monorepo/ts/pulumi/zemn.me/hello_world", + visibility = ["//visibility:private"], + deps = ["@com_github_aws_aws_lambda_go//lambda"], +) + +go_binary( + name = "hello_world", + embed = [":hello_world_lib"], + visibility = ["//visibility:public"], +) + +bazel_lint( + name = "bazel_lint", + srcs = ["BUILD.bazel"], +) + +pkg_tar( + name = "tar", + srcs = [":hello_world"], +) + +oci_image( + name = "image", + base = "@distroless_base", + entrypoint = ["/hello_world"], + tars = [":tar"], +) + +pulumi_image( + name = "oci_image", + src = ":image", + component_name = "HelloWorldImage", +) + +ts_project( + name = "ts", + srcs = [ + "hello_world.ts", + ], + visibility = [ + "//ts/pulumi:__subpackages__", + ], + deps = [ + ":oci_image", + "//:node_modules/@pulumi/aws", + "//:node_modules/@pulumi/awsx", + "//:node_modules/@pulumi/pulumi", + "//ts/pulumi/lib", + ], +) diff --git a/ts/pulumi/zemn.me/hello_world/hello.go b/ts/pulumi/zemn.me/hello_world/hello.go new file mode 100644 index 0000000000..deb7f0d62c --- /dev/null +++ b/ts/pulumi/zemn.me/hello_world/hello.go @@ -0,0 +1,17 @@ +package main + +import ( + "context" + + "github.com/aws/aws-lambda-go/lambda" +) + +// Handler is the Lambda function entry point +func Handler(ctx context.Context) (string, error) { + return "Hello, World!", nil +} + +func main() { + // Start the Lambda function + lambda.Start(Handler) +} diff --git a/ts/pulumi/zemn.me/hello_world/hello_world.ts b/ts/pulumi/zemn.me/hello_world/hello_world.ts new file mode 100644 index 0000000000..c0eca329da --- /dev/null +++ b/ts/pulumi/zemn.me/hello_world/hello_world.ts @@ -0,0 +1,61 @@ +import * as aws from "@pulumi/aws"; +import { ComponentResource, ComponentResourceOptions } from "@pulumi/pulumi"; + +import { mergeTags, TagSet, tagTrue } from "#root/ts/pulumi/lib/tags.js"; +import { HelloWorldImage } from "#root/ts/pulumi/zemn.me/hello_world/HelloWorldImage.js"; + +export interface Args { + tags?: TagSet; +} + +export class LambdaHelloWorld extends ComponentResource { + constructor( + name: string, + args: Args, + opts?: ComponentResourceOptions + ) { + super("ts:pulumi:Component", name, args, opts); + + const tag = name; + const tags = mergeTags(args.tags, tagTrue(tag)); + + // Create the ECR repository to store our container image + const repo = new aws.ecr.Repository("repo", { + forceDelete: true, + tags: tags, + }); + + const auth = aws.ecr.getAuthorizationToken(); + + // Build and push the container image + const img = new HelloWorldImage(`${name}_img`, { + repository: repo.repositoryUrl, + token: auth.then(auth => auth.authorizationToken) + }); + + // Create the Lambda function using the container image + const lambda = new aws.lambda.Function(`${name}_lambda`, { + packageType: "Image", + imageUri: img.url, + role: new aws.iam.Role("lambdaRole", { + assumeRolePolicy: aws.iam.assumeRolePolicyForPrincipal({ + Service: "lambda.amazonaws.com", + }), + }).arn, + timeout: 30, + memorySize: 512, + tags: tags, + }); + + // Add permissions for Lambda to log to CloudWatch + new aws.iam.RolePolicyAttachment(`${name}_lambda_logging`, { + role: lambda.role!, + policyArn: aws.iam.ManagedPolicy.AWSLambdaBasicExecutionRole, + }); + + // Export the Lambda function name + this.registerOutputs({ + lambdaFunctionName: lambda.name, + }); + } +} diff --git a/ts/pulumi/zemn.me/index.ts b/ts/pulumi/zemn.me/index.ts index b825a895f5..36c840ef86 100644 --- a/ts/pulumi/zemn.me/index.ts +++ b/ts/pulumi/zemn.me/index.ts @@ -5,6 +5,7 @@ import { bskyDid } from '#root/project/zemn.me/bio/bio.js'; import { BlueskyDisplayNameClaim } from '#root/ts/pulumi/lib/bluesky_username_claim.js'; import { mergeTags, tagTrue } from '#root/ts/pulumi/lib/tags.js'; import Website from '#root/ts/pulumi/lib/website.js'; +import { LambdaHelloWorld } from '#root/ts/pulumi/zemn.me/hello_world/hello_world.js'; export interface Args { zoneId: Pulumi.Input; @@ -33,6 +34,10 @@ export class Component extends Pulumi.ComponentResource { { parent: this } ); + new LambdaHelloWorld(`${name}_fargate`, { + tags: args.tags + }); + this.site = new Website( `${name}_zemn_me`, {