![](/img/trans.png)
[英]"TypeError: hashConstructor is not a constructor", error when using "@aws-sdk/signature-v4" on AWS-Lambda
[英]Lambda function does not automatically set aws-sdk credentials when using a bundler
我有一個看起來像這樣的函數(構建后):
myFunction/
package.json
node_modules/
myUtils/
index.ts
index.js
index.ts
index.js
在myUtils/index.ts
,我具有:
import { SecretsManager } from 'aws-sdk'
...
const secretsManager = new SecretsManager()
如果我壓縮myFunction/
的內容並將其部署到AWS Lambda,它們將按預期工作。 但是,存檔不必要地膨脹(大約60 MB,而使用捆綁程序時則為3MB)。
所以我安裝了Parcel並運行了npx parcel build index.ts --no-source-maps
,它創建了一個名為dist/
的文件夾,其中包含一個文件( index.js
)。 如果我將dist/
壓縮並部署到AWS Lambda,則函數調用現在會失敗,並顯示錯誤消息,指示未向SecretsManager
構造函數提供區域或憑據。
使用捆綁程序時,是否需要其他步驟來使用執行角色中的憑據配置aws-sdk
?
若要自動配置aws-sdk
的憑據,必須從默認的Lambda運行時環境加載aws-sdk
。
包裹捆綁器有兩種模式: parcel ... --target node
和parcel ... --target node --bundle-node-modules
。
aws-sdk
,從而覆蓋了默認的Lambda運行時環境。 解決方案是使用動態導入來抑制aws-sdk
捆綁,同時捆綁所有其他模塊。
所以這
import { SecretsManager } from 'aws-sdk'
...
const secretsManager = new SecretsManager()
變成
const awsSdk = import('aws-sdk')
...
const { SecretsManager } = await awsSdk
const secretsManager = new SecretsManager()
CLI參數為:
parcel build index.ts --no-source-maps --target node --bundle-node-modules
上面的解決方案有效,但是我一直是下面描述的基於Lambda層的解決方案,我認為它是優越的。 為了處理monorepos(我正在使用Rush),構建腳本有點笨拙,可能需要修改才能在項目中使用它。 如果您不使用monorepo,則可以忽略將內部和外部依賴項拆分的部分。
這樣做可能是更好的方法,因此歡迎提出批評。
// @my-scope/my-layer/package.json
{
"name": "@my-scope/my-layer",
"scripts": {
"build": "build-layer"
},
"dependencies": {
"uuid": "^3.3.3",
"axios": "~0.19.0",
"date-fns": "~2.4.1"
},
"devDependencies": {
"@my-scope/build-scripts": "0.0.0"
}
}
// @my-scope/build-scripts/package.json
{
"name": "@meal-planner/tool-build-scripts",
"files": [
"out/**/*"
],
"main": "out/lib/index.js",
"bin": {
"build-layer": "out/bin/build-layer.js",
},
"scripts": {
"build": "tsc"
},
"dependencies": {
"jsonc-parser": "~2.1.1",
"fs-extra": "~8.1.0",
"del": "~5.1.0"
},
"devDependencies": {
"@types/node": "~12.11.1",
"@types/fs-extra": "~8.0.1",
"typescript": "~3.6.4",
}
}
// @my-scope/build-scripts/bin/build-layer.ts
#!/usr/bin/env node
import * as jsonc from 'jsonc-parser'
import * as fs from 'fs'
import * as fse from 'fs-extra'
import * as path from 'path'
import * as util from 'util'
import * as del from 'del'
import { spawnSyncCore } from '../lib'
const access = util.promisify(fs.access)
const readFile = util.promisify(fs.readFile)
const writeFile = util.promisify(fs.writeFile)
const mkdir = util.promisify(fs.mkdir)
const BUILD_DIR = `dist`
const TARGET_PATH = path.join(BUILD_DIR, `nodejs`)
const MANIFEST_NAME = `package.json`
const MANIFEST_LOCK_NAME = `package-lock.json`
const INTERNAL_SCOPE = `@my-scope/`
interface IMinimalPackageManifest {
description: string
repository: unknown
license: string
dependencies: {
[packageName: string]: string | undefined
}
}
async function buildLayer(_argv: readonly string[] = process.argv): Promise<void> {
const {
description,
repository,
license,
dependencies
} = await readManifest(MANIFEST_NAME)
const { internal, external } = splitDependencies(dependencies)
const targetManifestPath = path.join(TARGET_PATH, MANIFEST_NAME)
const targetManifestLockPath = path.join(TARGET_PATH, MANIFEST_LOCK_NAME)
await writeManifest(
targetManifestPath,
{
description,
repository,
license,
dependencies: external
}
)
installExternalDependencies(TARGET_PATH)
await installInternalDependencies(internal, TARGET_PATH)
del.sync(targetManifestPath)
del.sync(targetManifestLockPath)
}
async function readManifest(sourcePath: string): Promise<IMinimalPackageManifest> {
const raw = (await readFile(sourcePath)).toString()
return jsonc.parse(raw)
}
async function writeManifest(targetPath: string, manifest: IMinimalPackageManifest): Promise<void> {
const targetDir = path.dirname(targetPath)
try {
await access(targetDir)
} catch {
await mkdir(targetDir, {
recursive: true
})
}
const raw = JSON.stringify(manifest)
await writeFile(targetPath, raw)
}
interface IDependencyMap {
[key: string]: string | undefined
}
interface IDepedencyGroups {
internal: IDependencyMap
external: IDependencyMap
}
function splitDependencies(dependencies: IDependencyMap): IDepedencyGroups {
return Object.keys(dependencies).reduce<IDepedencyGroups>(
(groups, name) => {
if (name.startsWith(INTERNAL_SCOPE)) {
groups.internal[name] = dependencies[name]
} else {
groups.external[name] = dependencies[name]
}
return groups
},
{
internal: {},
external: {}
}
)
}
function installExternalDependencies(targetDir: string): void {
spawnSyncCore({
command: `npm`,
cwd: targetDir,
env: {
...process.env,
NODE_ENV: `production`
},
args: [
`install`
],
})
}
async function installInternalDependencies(dependencies: IDependencyMap, targetDir: string): Promise<void> {
const sourcePaths = Object.keys(dependencies)
.map(dependency => path.join(`node_modules`, dependency))
for (const sourcePath of sourcePaths) {
const targetPath = path.join(targetDir, sourcePath)
const sourceManifestPath = path.join(sourcePath, MANIFEST_NAME)
const sourceDistPath = path.join(sourcePath, BUILD_DIR)
await fse.copy(sourcePath, targetPath, {
dereference: true,
recursive: true,
filter: (src, _dest) => {
// Only copy package.json and dist/ folder.
return src === sourcePath || src === sourceManifestPath || src.startsWith(sourceDistPath)
}
})
}
}
/* eslint-disable @typescript-eslint/no-floating-promises */
buildLayer()
/* eslint-enable @typescript-eslint/no-floating-promises */
// @my-scope/build-scripts/lib/index.ts
import { spawnSync } from 'child_process'
const TAB_WIDTH = 4
export interface ISpawnSyncCoreOptions {
command: string
cwd?: string
env?: NodeJS.ProcessEnv
args?: readonly string[]
}
export function spawnSyncCore({
command,
cwd,
env,
args
}: ISpawnSyncCoreOptions): void {
const result = spawnSync(command, args, {
shell: true,
cwd,
env
})
if (result.error) {
throw result.error
}
if (result.status !== 0) {
throw new Error(`${command} returned a non-zero exit code: ${JSON.stringify({
stdout: result.stdout.toString(),
stderr: result.stderr.toString(),
status: result.status,
signal: result.signal
}, undefined, TAB_WIDTH)}`)
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.