-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlambda.ts
More file actions
111 lines (92 loc) · 3.37 KB
/
lambda.ts
File metadata and controls
111 lines (92 loc) · 3.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import * as lib from 'synapse:lib'
import * as aws from 'terraform-provider:aws'
import * as Lambda from '@aws-sdk/client-lambda'
interface LambdaOptions {
arch?: 'arm64' | 'x64'
env?: Record<string, string>
bucket?: aws.S3Bucket
}
let assetBucket: aws.S3Bucket
function getAssetBucket() {
return assetBucket ??= new aws.S3Bucket()
}
export class LambdaFunction<T extends any[] = any[], U = unknown> {
private readonly client = new Lambda.Lambda({})
public readonly resource: aws.LambdaFunction
public readonly principal: aws.IamRole
public constructor(target: (...args: T) => Promise<U> | U, opt?: LambdaOptions) {
const arch = opt?.arch ?? 'arm64'
const handler = `handler.default`
const environment = opt?.env ?? {}
const entrypoint = new lib.Bundle(wrap(target), {
os: 'linux',
arch,
includeAssets: true, // Currently required when using Zig modules
})
const role = new aws.IamRole({
assumeRolePolicy: JSON.stringify(spPolicy('lambda.amazonaws.com')),
managedPolicyArns: ['arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole'],
})
this.principal = role
const bucket = opt?.bucket ?? getAssetBucket()
const code = new lib.Archive(entrypoint)
const obj = new aws.S3Object({
bucket: bucket.bucket,
key: entrypoint.destination,
source: code.filePath,
})
const architectures = arch === 'arm64' ? [arch]
: arch === 'x64' ? ['x86_64'] : undefined
const fn = new aws.LambdaFunction({
functionName: lib.generateIdentifier(aws.LambdaFunction, 'functionName', 64),
s3Bucket: obj.bucket,
s3Key: obj.key,
handler,
architectures,
timeout: 30,
memorySize: 1024,
packageType: 'Zip',
runtime: 'nodejs20.x',
role: role.arn,
environment: {
variables: environment,
},
})
this.resource = fn
}
public async invoke(...args: T): Promise<U> {
const payload = args.length > 0 ? Buffer.from(JSON.stringify({ args })) : undefined
const resp = await this.client.invoke({
FunctionName: this.resource.functionName,
Payload: payload,
})
const resultString = Buffer.from(resp.Payload).toString('utf-8')
if (resultString === 'null') {
return void 0 as U
}
if (resp.FunctionError) {
const errResp = JSON.parse(resultString) as { errorType: string; errorMessage: string; trace: string }
const err = new Error(errResp.errorMessage)
err.name = errResp.errorType
throw err
}
return JSON.parse(resultString)
}
}
type LambdaHandler = (event: any) => Promise<any>
function wrap<T, U extends any[]>(fn: (...args: U) => T): LambdaHandler {
return async (event) => {
if (typeof event !== 'object' || !Array.isArray(event?.args)) {
throw new Error('Got unexpected event payload')
}
return fn(...event.args)
}
}
const spPolicy = (principal: string | string[]) => ({
Version: "2012-10-17",
Statement: [{
Effect: "Allow",
Action: "sts:AssumeRole",
Principal: { Service: principal },
}]
})