diff --git a/lib/core-resources.ts b/lib/core-resources.ts index 75ca1f0..eb43cc3 100644 --- a/lib/core-resources.ts +++ b/lib/core-resources.ts @@ -16,14 +16,24 @@ import { StartingPosition, } from "aws-cdk-lib/aws-lambda"; import path from "path"; +import { Revantios } from "./revantios"; +import { Role, ServicePrincipal } from "aws-cdk-lib/aws-iam"; const ENV_VARIABLE_REVANT_COST_TABLE_NAME = "REVANT_COST_TABLE_NAME"; +const ENV_VARIABLE_REVANT_COST_LIMIT_PREFIX = "REVANT_COST_LIMIT"; +const ENV_VARIABLE_REVANT_SCHEDULE_ROLE_ARN = "REVANT_SCHEDULE_ROLE_ARN"; +const ENV_VARIABLE_REVANT_SCHEDULE_FUNCTION_ARN = + "REVANT_SCHEDULE_FUNCTION_ARN"; const DYNAMODB_INCURRED_EXPENSES_RATE_ATTRIBUTE_NAME = "incurredExpensesRate"; export class CoreRessources extends Construct { public dynamoDBTable: Table; + private _lambdaCommonResources?: LambdaCommonResources; private _ec2CommonResources?: EC2CommonResources; + + private updateAccruedExpensesWithCurrentIncurredExpensesRate: NodejsFunction; + static instance: CoreRessources; private constructor(scope: Construct) { @@ -37,7 +47,7 @@ export class CoreRessources extends Construct { billingMode: BillingMode.PAY_PER_REQUEST, stream: StreamViewType.NEW_AND_OLD_IMAGES, }); - const updateAccruedExpensesWithCurrentIncurredExpensesRate = + this.updateAccruedExpensesWithCurrentIncurredExpensesRate = new NodejsFunction( this, "UpdateAccruedExpensesWithCurrentIncurredExpensesRateFunction", @@ -49,14 +59,14 @@ export class CoreRessources extends Construct { } ); this.dynamoDBTable.grant( - updateAccruedExpensesWithCurrentIncurredExpensesRate, + this.updateAccruedExpensesWithCurrentIncurredExpensesRate, "dynamodb:UpdateItem" ); - updateAccruedExpensesWithCurrentIncurredExpensesRate.addEnvironment( + this.updateAccruedExpensesWithCurrentIncurredExpensesRate.addEnvironment( ENV_VARIABLE_REVANT_COST_TABLE_NAME, this.dynamoDBTable.tableName ); - updateAccruedExpensesWithCurrentIncurredExpensesRate.addEventSource( + this.updateAccruedExpensesWithCurrentIncurredExpensesRate.addEventSource( new DynamoEventSource(this.dynamoDBTable, { startingPosition: StartingPosition.LATEST, reportBatchItemFailures: true, @@ -74,8 +84,29 @@ export class CoreRessources extends Construct { ], }) ); + + const scheduleRole = new Role(this, "ScheduleRole", {}); + scheduleRole.grantAssumeRole( + new ServicePrincipal("scheduler.amazonaws.com") + ); + + this.updateAccruedExpensesWithCurrentIncurredExpensesRate.addEnvironment( + ENV_VARIABLE_REVANT_SCHEDULE_ROLE_ARN, + scheduleRole.roleArn + ); + this.updateAccruedExpensesWithCurrentIncurredExpensesRate.addEnvironment( + ENV_VARIABLE_REVANT_SCHEDULE_FUNCTION_ARN, + "test-arn" + ); } + public registerBudget = (address: string, budget: number) => { + this.updateAccruedExpensesWithCurrentIncurredExpensesRate.addEnvironment( + [ENV_VARIABLE_REVANT_COST_LIMIT_PREFIX, address].join("_"), + Revantios.fromCents(budget).toString() + ); + }; + public get lambdaCommonResources() { if (this._lambdaCommonResources === undefined) { this._lambdaCommonResources = new LambdaCommonResources(this); diff --git a/lib/cost-limit.ts b/lib/cost-limit.ts index 339ecff..18bdc63 100644 --- a/lib/cost-limit.ts +++ b/lib/cost-limit.ts @@ -2,6 +2,7 @@ import { Aspects, IAspect } from "aws-cdk-lib"; import { IConstruct } from "constructs"; import { Function } from "./services/lambda"; import { Instance } from "./services/ec2"; +import { CoreRessources } from "./core-resources"; export type CostLimitProps = { /** @@ -28,6 +29,7 @@ export class CostLimit implements IAspect { ) as this | undefined; if (nodeWithCostLimitAspect !== undefined) { this.address = node.node.addr; + CoreRessources.getInstance(node).registerBudget(this.address, this.budget); } CostLimitedConstructs.map((CostLimitedConstruct) => { diff --git a/lib/functions/updateAccruedExpensesWithCurrentIncurredExpensesRate.ts b/lib/functions/updateAccruedExpensesWithCurrentIncurredExpensesRate.ts index d6d21e4..68c60da 100644 --- a/lib/functions/updateAccruedExpensesWithCurrentIncurredExpensesRate.ts +++ b/lib/functions/updateAccruedExpensesWithCurrentIncurredExpensesRate.ts @@ -2,8 +2,15 @@ import { DynamoDBStreamHandler } from "aws-lambda"; import { unmarshall } from "@aws-sdk/util-dynamodb"; import { AttributeValue, DynamoDBClient } from "@aws-sdk/client-dynamodb"; import { DynamoDBDocumentClient, UpdateCommand } from "@aws-sdk/lib-dynamodb"; +import { + SchedulerClient, + CreateScheduleCommand, +} from "@aws-sdk/client-scheduler"; const ENV_VARIABLE_REVANT_COST_TABLE_NAME = "REVANT_COST_TABLE_NAME"; +const ENV_VARIABLE_REVANT_COST_LIMIT_PREFIX = "REVANT_COST_LIMIT"; +const ENV_VARIABLE_REVANT_SCHEDULE_ROLE_ARN = "REVANT_SCHEDULE_ROLE_ARN"; +const ENV_VARIABLE_REVANT_SCHEDULE_FUNCTION_ARN = "REVANT_SCHEDULE_FUNCTION_ARN"; const DYNAMODB_ACCRUED_EXPENSES_ATTRIBUTE_NAME = "accruedExpenses"; const DYNAMODB_INCURRED_EXPENSES_RATE_ATTRIBUTE_NAME = "incurredExpensesRate"; @@ -23,6 +30,7 @@ type BudgetUpdateOperation = { const dynamoDBClient = new DynamoDBClient({}); const dynamoDBDocumentClient = DynamoDBDocumentClient.from(dynamoDBClient); +const schedulerClient = new SchedulerClient({}); const isBudgetUpdateOperation = ({ oldBudget, @@ -41,6 +49,25 @@ const calculateNewAccruedExpenses = ({ 1000 ) * oldBudget[DYNAMODB_INCURRED_EXPENSES_RATE_ATTRIBUTE_NAME]; +const calculateBudgetReachedEstimatedDate = ({ + accruedExpenses, + incurredExpensesRate, + updatedAt, + budget, +}: { + accruedExpenses: number; + incurredExpensesRate: number; + updatedAt: Date; + budget: number; +}): Date => { + const budgetReachedDate = new Date(updatedAt); + budgetReachedDate.setSeconds( + budgetReachedDate.getSeconds() + + (budget - accruedExpenses) / incurredExpensesRate + ); + return budgetReachedDate; +}; + export const handler: DynamoDBStreamHandler = async ({ Records }) => { console.log(`${Records.length} records received`); const budgetUpdatesOperations = Records.map((record) => ({ @@ -56,12 +83,21 @@ export const handler: DynamoDBStreamHandler = async ({ Records }) => { `${budgetUpdatesOperations.length} budget update operations received` ); + const budgets = Object.fromEntries( + Object.entries(process.env) + .filter(([key]) => key.startsWith(ENV_VARIABLE_REVANT_COST_LIMIT_PREFIX)) + .map(([key, value]) => [ + key.slice(ENV_VARIABLE_REVANT_COST_LIMIT_PREFIX.length + 1), + Number(value), + ]) + ); + const failedUpdateIds: { itemIdentifier: string }[] = []; await Promise.all( budgetUpdatesOperations.map( async ({ itemIdentifier, oldBudget, newBudget }) => { try { - await dynamoDBDocumentClient.send( + const { Attributes } = await dynamoDBDocumentClient.send( new UpdateCommand({ TableName: process.env[ENV_VARIABLE_REVANT_COST_TABLE_NAME], Key: { PK: oldBudget.PK }, @@ -75,8 +111,39 @@ export const handler: DynamoDBStreamHandler = async ({ Records }) => { newBudget, }), }, + ReturnValues: "ALL_NEW", }) ); + if (Attributes === undefined) { + console.error("Did not get any updated budget from DynamDB"); + return; + } + + const address = oldBudget.PK.split("#")[1]; + const budget = budgets[address]; + const budgetReachedDate = calculateBudgetReachedEstimatedDate({ + accruedExpenses: Attributes[ + DYNAMODB_ACCRUED_EXPENSES_ATTRIBUTE_NAME + ] as number, + incurredExpensesRate: Attributes[ + DYNAMODB_INCURRED_EXPENSES_RATE_ATTRIBUTE_NAME + ] as number, + updatedAt: new Date( + Attributes[DYNAMODB_LAST_UPDATE_ATTRIBUTE_NAME] + ), + budget, + }); + await schedulerClient.send(new CreateScheduleCommand({ + Name: address, + ScheduleExpression: `at(${budgetReachedDate.toISOString().split('.')[0]})`, + Target: { + RoleArn: process.env[ENV_VARIABLE_REVANT_SCHEDULE_ROLE_ARN], + Arn: process.env[ENV_VARIABLE_REVANT_SCHEDULE_FUNCTION_ARN], + }, + FlexibleTimeWindow: { + Mode: "OFF" + } + })); } catch (error) { failedUpdateIds.push({ itemIdentifier }); } diff --git a/package-lock.json b/package-lock.json index 6a708ca..f2fb227 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@aws-sdk/client-ec2": "^3.405.0", "@aws-sdk/client-lambda": "^3.395.0", "@aws-sdk/client-pricing": "^3.405.0", + "@aws-sdk/client-scheduler": "^3.409.0", "@aws-sdk/lib-dynamodb": "^3.395.0", "@aws-sdk/util-dynamodb": "^3.405.0", "@types/aws-lambda": "^8.10.119", @@ -1196,6 +1197,453 @@ } } }, + "node_modules/@aws-sdk/client-scheduler": { + "version": "3.409.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-scheduler/-/client-scheduler-3.409.0.tgz", + "integrity": "sha512-2azD6OPoLYXPeBt6515ANwXmikejEY0rsAj+77UTVTY/BXcAHCodEaKy99+3Oaho+cq7pwKKW9RKo8NR8eahtQ==", + "dev": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sts": "3.409.0", + "@aws-sdk/credential-provider-node": "3.409.0", + "@aws-sdk/middleware-host-header": "3.408.0", + "@aws-sdk/middleware-logger": "3.408.0", + "@aws-sdk/middleware-recursion-detection": "3.408.0", + "@aws-sdk/middleware-signing": "3.408.0", + "@aws-sdk/middleware-user-agent": "3.408.0", + "@aws-sdk/types": "3.408.0", + "@aws-sdk/util-endpoints": "3.408.0", + "@aws-sdk/util-user-agent-browser": "3.408.0", + "@aws-sdk/util-user-agent-node": "3.408.0", + "@smithy/config-resolver": "^2.0.5", + "@smithy/fetch-http-handler": "^2.0.5", + "@smithy/hash-node": "^2.0.5", + "@smithy/invalid-dependency": "^2.0.5", + "@smithy/middleware-content-length": "^2.0.5", + "@smithy/middleware-endpoint": "^2.0.5", + "@smithy/middleware-retry": "^2.0.5", + "@smithy/middleware-serde": "^2.0.5", + "@smithy/middleware-stack": "^2.0.0", + "@smithy/node-config-provider": "^2.0.6", + "@smithy/node-http-handler": "^2.0.5", + "@smithy/protocol-http": "^2.0.5", + "@smithy/smithy-client": "^2.0.5", + "@smithy/types": "^2.2.2", + "@smithy/url-parser": "^2.0.5", + "@smithy/util-base64": "^2.0.0", + "@smithy/util-body-length-browser": "^2.0.0", + "@smithy/util-body-length-node": "^2.1.0", + "@smithy/util-defaults-mode-browser": "^2.0.6", + "@smithy/util-defaults-mode-node": "^2.0.6", + "@smithy/util-retry": "^2.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.5.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-scheduler/node_modules/@aws-sdk/client-sso": { + "version": "3.409.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.409.0.tgz", + "integrity": "sha512-vlXcIzcmUhObuEJ6q3lsp1ZHeDeD9bUrG3dmdSTeII4U6A9imgvaXONWI9GFEUsgzCrrCxtCqBX2RqMfZDhylw==", + "dev": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/middleware-host-header": "3.408.0", + "@aws-sdk/middleware-logger": "3.408.0", + "@aws-sdk/middleware-recursion-detection": "3.408.0", + "@aws-sdk/middleware-user-agent": "3.408.0", + "@aws-sdk/types": "3.408.0", + "@aws-sdk/util-endpoints": "3.408.0", + "@aws-sdk/util-user-agent-browser": "3.408.0", + "@aws-sdk/util-user-agent-node": "3.408.0", + "@smithy/config-resolver": "^2.0.5", + "@smithy/fetch-http-handler": "^2.0.5", + "@smithy/hash-node": "^2.0.5", + "@smithy/invalid-dependency": "^2.0.5", + "@smithy/middleware-content-length": "^2.0.5", + "@smithy/middleware-endpoint": "^2.0.5", + "@smithy/middleware-retry": "^2.0.5", + "@smithy/middleware-serde": "^2.0.5", + "@smithy/middleware-stack": "^2.0.0", + "@smithy/node-config-provider": "^2.0.6", + "@smithy/node-http-handler": "^2.0.5", + "@smithy/protocol-http": "^2.0.5", + "@smithy/smithy-client": "^2.0.5", + "@smithy/types": "^2.2.2", + "@smithy/url-parser": "^2.0.5", + "@smithy/util-base64": "^2.0.0", + "@smithy/util-body-length-browser": "^2.0.0", + "@smithy/util-body-length-node": "^2.1.0", + "@smithy/util-defaults-mode-browser": "^2.0.6", + "@smithy/util-defaults-mode-node": "^2.0.6", + "@smithy/util-retry": "^2.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-scheduler/node_modules/@aws-sdk/client-sts": { + "version": "3.409.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.409.0.tgz", + "integrity": "sha512-yNL9zYWDVIOWZhIlsy2tiHetSYvio5ZVJ3nvR4xWPTwqOQveZx/K0PTK+nh6T6w5R3w5IOSKvd+vPCpY4bGx8Q==", + "dev": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/credential-provider-node": "3.409.0", + "@aws-sdk/middleware-host-header": "3.408.0", + "@aws-sdk/middleware-logger": "3.408.0", + "@aws-sdk/middleware-recursion-detection": "3.408.0", + "@aws-sdk/middleware-sdk-sts": "3.408.0", + "@aws-sdk/middleware-signing": "3.408.0", + "@aws-sdk/middleware-user-agent": "3.408.0", + "@aws-sdk/types": "3.408.0", + "@aws-sdk/util-endpoints": "3.408.0", + "@aws-sdk/util-user-agent-browser": "3.408.0", + "@aws-sdk/util-user-agent-node": "3.408.0", + "@smithy/config-resolver": "^2.0.5", + "@smithy/fetch-http-handler": "^2.0.5", + "@smithy/hash-node": "^2.0.5", + "@smithy/invalid-dependency": "^2.0.5", + "@smithy/middleware-content-length": "^2.0.5", + "@smithy/middleware-endpoint": "^2.0.5", + "@smithy/middleware-retry": "^2.0.5", + "@smithy/middleware-serde": "^2.0.5", + "@smithy/middleware-stack": "^2.0.0", + "@smithy/node-config-provider": "^2.0.6", + "@smithy/node-http-handler": "^2.0.5", + "@smithy/protocol-http": "^2.0.5", + "@smithy/smithy-client": "^2.0.5", + "@smithy/types": "^2.2.2", + "@smithy/url-parser": "^2.0.5", + "@smithy/util-base64": "^2.0.0", + "@smithy/util-body-length-browser": "^2.0.0", + "@smithy/util-body-length-node": "^2.1.0", + "@smithy/util-defaults-mode-browser": "^2.0.6", + "@smithy/util-defaults-mode-node": "^2.0.6", + "@smithy/util-retry": "^2.0.0", + "@smithy/util-utf8": "^2.0.0", + "fast-xml-parser": "4.2.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-scheduler/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.408.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.408.0.tgz", + "integrity": "sha512-GCpgHEHxRTzKaMkwDC2gLb3xlD+ZxhKPUJ1DVcO7I9E3eCGJsYVedIi0/2XE+NP+HVoy8LyW2qH8QQWh64JKow==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.408.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/types": "^2.2.2", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-scheduler/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.409.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.409.0.tgz", + "integrity": "sha512-Z7hb0Kj0FuqD5HimDrtt0LRjKBHA5pvLcTYYdVorJovaBxEvfDpISSDVRIUmvhMGAlv7XezbvqESOU5cn0Gpzw==", + "dev": true, + "dependencies": { + "@aws-sdk/credential-provider-env": "3.408.0", + "@aws-sdk/credential-provider-process": "3.408.0", + "@aws-sdk/credential-provider-sso": "3.409.0", + "@aws-sdk/credential-provider-web-identity": "3.408.0", + "@aws-sdk/types": "3.408.0", + "@smithy/credential-provider-imds": "^2.0.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/shared-ini-file-loader": "^2.0.6", + "@smithy/types": "^2.2.2", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-scheduler/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.409.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.409.0.tgz", + "integrity": "sha512-kXmfBVYnHoEAACo6zskEryDSgMSo1QYiv6P8n6Go/RsJHe4Ec+YtrOMLg3hTOptiIGHOTWZ1ANaU/IfIxmqumA==", + "dev": true, + "dependencies": { + "@aws-sdk/credential-provider-env": "3.408.0", + "@aws-sdk/credential-provider-ini": "3.409.0", + "@aws-sdk/credential-provider-process": "3.408.0", + "@aws-sdk/credential-provider-sso": "3.409.0", + "@aws-sdk/credential-provider-web-identity": "3.408.0", + "@aws-sdk/types": "3.408.0", + "@smithy/credential-provider-imds": "^2.0.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/shared-ini-file-loader": "^2.0.6", + "@smithy/types": "^2.2.2", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-scheduler/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.408.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.408.0.tgz", + "integrity": "sha512-qCTf9tr6+I2s3+v5zP4YRQQrGlYw/jyZ7u/k6bGshhlvgwGPfjNuHrM8uK/W1kv4ng1myxaL1/tAY6RVVdXz4Q==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.408.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/shared-ini-file-loader": "^2.0.6", + "@smithy/types": "^2.2.2", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-scheduler/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.409.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.409.0.tgz", + "integrity": "sha512-Bh0ykbDpnUK4W8sQMEpRA/TlZxwpPLl4aU8eBLlbEcTL2M8or2nr0dQzOOvabZo8hbaPM6yfOl+vLTvWGs75zg==", + "dev": true, + "dependencies": { + "@aws-sdk/client-sso": "3.409.0", + "@aws-sdk/token-providers": "3.408.0", + "@aws-sdk/types": "3.408.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/shared-ini-file-loader": "^2.0.6", + "@smithy/types": "^2.2.2", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-scheduler/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.408.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.408.0.tgz", + "integrity": "sha512-5FbDPF/zY/1t6k1zRI/HnrxcH2v7SwsEYu2SThI2qbzaP/K7MTnTanV5vNFcdQOpuQ7x3PrzTlH3AWZueCr3Vw==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.408.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/types": "^2.2.2", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-scheduler/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.408.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.408.0.tgz", + "integrity": "sha512-eofCXuSZ+ntbLzeCRdHzraXzgWqAplXU7W2qFFVC4O9lZBhADwNPI8n8x98TH0mftnmvZxh5Bo5U8WvEolIDkw==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.408.0", + "@smithy/protocol-http": "^2.0.5", + "@smithy/types": "^2.2.2", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-scheduler/node_modules/@aws-sdk/middleware-logger": { + "version": "3.408.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.408.0.tgz", + "integrity": "sha512-otwXPCubsGRFv8Hb6nKw6Vvnu4dC8CcPk05buStj42nF8QdjWrKGb2rDCvLph5lr576LF5HN+Y2moyOi7z/I7g==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.408.0", + "@smithy/types": "^2.2.2", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-scheduler/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.408.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.408.0.tgz", + "integrity": "sha512-QfZwmX5z0IRC2c8pBi9VozSqbJw19V5oxyykSTqdjGe3CG3yNujXObV6xQesK67CWSnPb9wDgVGKUoYuIXwOxw==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.408.0", + "@smithy/protocol-http": "^2.0.5", + "@smithy/types": "^2.2.2", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-scheduler/node_modules/@aws-sdk/middleware-sdk-sts": { + "version": "3.408.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.408.0.tgz", + "integrity": "sha512-dIO9BTX049P2PwaeAK2lxJeA2rZi9/bWzMP1GIE60VrMDHmN5Ljvh1lLActECLAqNQIqN5Ub0bKV2tC/jMn+CA==", + "dev": true, + "dependencies": { + "@aws-sdk/middleware-signing": "3.408.0", + "@aws-sdk/types": "3.408.0", + "@smithy/types": "^2.2.2", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-scheduler/node_modules/@aws-sdk/middleware-signing": { + "version": "3.408.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.408.0.tgz", + "integrity": "sha512-flLiLKATJ4NLcLb7lPojyQ6NvLSyQ3axqIClqwMRnhSRxvREB7OgBKwmPecSl0I5JxsNEqo+mjARdMjUHadgWQ==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.408.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/protocol-http": "^2.0.5", + "@smithy/signature-v4": "^2.0.0", + "@smithy/types": "^2.2.2", + "@smithy/util-middleware": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-scheduler/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.408.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.408.0.tgz", + "integrity": "sha512-UvlKri8/Mgf5W+tFU6ZJ65fC6HljcysIqfRFts/8Wurl322IS1I4j+pyjV2P6eK1054bzynfi3Trv+tRYHtVcA==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.408.0", + "@aws-sdk/util-endpoints": "3.408.0", + "@smithy/protocol-http": "^2.0.5", + "@smithy/types": "^2.2.2", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-scheduler/node_modules/@aws-sdk/token-providers": { + "version": "3.408.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.408.0.tgz", + "integrity": "sha512-D//BjUrVtDzDdCz1mRdZZSAc822fh75Ssq46smeS6S6NKq3vJeHhfrQJMyVU1GclXu1tn9AwykaQW5Jwb5im+g==", + "dev": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/middleware-host-header": "3.408.0", + "@aws-sdk/middleware-logger": "3.408.0", + "@aws-sdk/middleware-recursion-detection": "3.408.0", + "@aws-sdk/middleware-user-agent": "3.408.0", + "@aws-sdk/types": "3.408.0", + "@aws-sdk/util-endpoints": "3.408.0", + "@aws-sdk/util-user-agent-browser": "3.408.0", + "@aws-sdk/util-user-agent-node": "3.408.0", + "@smithy/config-resolver": "^2.0.5", + "@smithy/fetch-http-handler": "^2.0.5", + "@smithy/hash-node": "^2.0.5", + "@smithy/invalid-dependency": "^2.0.5", + "@smithy/middleware-content-length": "^2.0.5", + "@smithy/middleware-endpoint": "^2.0.5", + "@smithy/middleware-retry": "^2.0.5", + "@smithy/middleware-serde": "^2.0.5", + "@smithy/middleware-stack": "^2.0.0", + "@smithy/node-config-provider": "^2.0.6", + "@smithy/node-http-handler": "^2.0.5", + "@smithy/property-provider": "^2.0.0", + "@smithy/protocol-http": "^2.0.5", + "@smithy/shared-ini-file-loader": "^2.0.6", + "@smithy/smithy-client": "^2.0.5", + "@smithy/types": "^2.2.2", + "@smithy/url-parser": "^2.0.5", + "@smithy/util-base64": "^2.0.0", + "@smithy/util-body-length-browser": "^2.0.0", + "@smithy/util-body-length-node": "^2.1.0", + "@smithy/util-defaults-mode-browser": "^2.0.6", + "@smithy/util-defaults-mode-node": "^2.0.6", + "@smithy/util-retry": "^2.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-scheduler/node_modules/@aws-sdk/types": { + "version": "3.408.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.408.0.tgz", + "integrity": "sha512-sIsR5224xWQTW7O6h4V0S7DMWs4bK4DCunwOo7Avpq7ZVmH2YyLTs0n4NGL186j8xTosycF1ACQgpM48SLIvaA==", + "dev": true, + "dependencies": { + "@smithy/types": "^2.2.2", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-scheduler/node_modules/@aws-sdk/util-endpoints": { + "version": "3.408.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.408.0.tgz", + "integrity": "sha512-N1D5cKEkCqf5Q7IF/pI9kfcNrT+/5ctZ6cQo4Ex6xaOcnUzdOZcXdPqaMRZVZRn8enjK2SpoLlRpXGISOugPaw==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.408.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-scheduler/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.408.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.408.0.tgz", + "integrity": "sha512-wOVjDprG5h6kM8aJZk/tRX/RgxNxr73d6kIsUePlAgil13q62M9lcFMcIXduqtDsa1B6FfVB2wx/pyUuOZri5g==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.408.0", + "@smithy/types": "^2.2.2", + "bowser": "^2.11.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/client-scheduler/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.408.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.408.0.tgz", + "integrity": "sha512-BzMFV+cIXrtfcfJk3GpXnkANFkzZisvAtD306TMgIscn5FF26K1jD5DU+h5Q5WMq7gx+oXh9kJ3Lu3hi7hahKQ==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.408.0", + "@smithy/node-config-provider": "^2.0.6", + "@smithy/types": "^2.2.2", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, "node_modules/@aws-sdk/client-sso": { "version": "3.395.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.395.0.tgz", @@ -4956,6 +5404,342 @@ "@esbuild/win32-x64": "0.19.2" } }, + "node_modules/esbuild/node_modules/@esbuild/android-arm": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.2.tgz", + "integrity": "sha512-tM8yLeYVe7pRyAu9VMi/Q7aunpLwD139EY1S99xbQkT4/q2qa6eA4ige/WJQYdJ8GBL1K33pPFhPfPdJ/WzT8Q==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild/node_modules/@esbuild/android-arm64": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.2.tgz", + "integrity": "sha512-lsB65vAbe90I/Qe10OjkmrdxSX4UJDjosDgb8sZUKcg3oefEuW2OT2Vozz8ef7wrJbMcmhvCC+hciF8jY/uAkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild/node_modules/@esbuild/android-x64": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.2.tgz", + "integrity": "sha512-qK/TpmHt2M/Hg82WXHRc/W/2SGo/l1thtDHZWqFq7oi24AjZ4O/CpPSu6ZuYKFkEgmZlFoa7CooAyYmuvnaG8w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild/node_modules/@esbuild/darwin-x64": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.2.tgz", + "integrity": "sha512-tP+B5UuIbbFMj2hQaUr6EALlHOIOmlLM2FK7jeFBobPy2ERdohI4Ka6ZFjZ1ZYsrHE/hZimGuU90jusRE0pwDw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild/node_modules/@esbuild/freebsd-arm64": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.2.tgz", + "integrity": "sha512-YbPY2kc0acfzL1VPVK6EnAlig4f+l8xmq36OZkU0jzBVHcOTyQDhnKQaLzZudNJQyymd9OqQezeaBgkTGdTGeQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild/node_modules/@esbuild/freebsd-x64": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.2.tgz", + "integrity": "sha512-nSO5uZT2clM6hosjWHAsS15hLrwCvIWx+b2e3lZ3MwbYSaXwvfO528OF+dLjas1g3bZonciivI8qKR/Hm7IWGw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-arm": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.2.tgz", + "integrity": "sha512-Odalh8hICg7SOD7XCj0YLpYCEc+6mkoq63UnExDCiRA2wXEmGlK5JVrW50vZR9Qz4qkvqnHcpH+OFEggO3PgTg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-arm64": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.2.tgz", + "integrity": "sha512-ig2P7GeG//zWlU0AggA3pV1h5gdix0MA3wgB+NsnBXViwiGgY77fuN9Wr5uoCrs2YzaYfogXgsWZbm+HGr09xg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-ia32": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.2.tgz", + "integrity": "sha512-mLfp0ziRPOLSTek0Gd9T5B8AtzKAkoZE70fneiiyPlSnUKKI4lp+mGEnQXcQEHLJAcIYDPSyBvsUbKUG2ri/XQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-loong64": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.2.tgz", + "integrity": "sha512-hn28+JNDTxxCpnYjdDYVMNTR3SKavyLlCHHkufHV91fkewpIyQchS1d8wSbmXhs1fiYDpNww8KTFlJ1dHsxeSw==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-mips64el": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.2.tgz", + "integrity": "sha512-KbXaC0Sejt7vD2fEgPoIKb6nxkfYW9OmFUK9XQE4//PvGIxNIfPk1NmlHmMg6f25x57rpmEFrn1OotASYIAaTg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-ppc64": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.2.tgz", + "integrity": "sha512-dJ0kE8KTqbiHtA3Fc/zn7lCd7pqVr4JcT0JqOnbj4LLzYnp+7h8Qi4yjfq42ZlHfhOCM42rBh0EwHYLL6LEzcw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-riscv64": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.2.tgz", + "integrity": "sha512-7Z/jKNFufZ/bbu4INqqCN6DDlrmOTmdw6D0gH+6Y7auok2r02Ur661qPuXidPOJ+FSgbEeQnnAGgsVynfLuOEw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-s390x": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.2.tgz", + "integrity": "sha512-U+RinR6aXXABFCcAY4gSlv4CL1oOVvSSCdseQmGO66H+XyuQGZIUdhG56SZaDJQcLmrSfRmx5XZOWyCJPRqS7g==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-x64": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.2.tgz", + "integrity": "sha512-oxzHTEv6VPm3XXNaHPyUTTte+3wGv7qVQtqaZCrgstI16gCuhNOtBXLEBkBREP57YTd68P0VgDgG73jSD8bwXQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild/node_modules/@esbuild/netbsd-x64": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.2.tgz", + "integrity": "sha512-WNa5zZk1XpTTwMDompZmvQLHszDDDN7lYjEHCUmAGB83Bgs20EMs7ICD+oKeT6xt4phV4NDdSi/8OfjPbSbZfQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild/node_modules/@esbuild/openbsd-x64": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.2.tgz", + "integrity": "sha512-S6kI1aT3S++Dedb7vxIuUOb3oAxqxk2Rh5rOXOTYnzN8JzW1VzBd+IqPiSpgitu45042SYD3HCoEyhLKQcDFDw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild/node_modules/@esbuild/sunos-x64": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.2.tgz", + "integrity": "sha512-VXSSMsmb+Z8LbsQGcBMiM+fYObDNRm8p7tkUDMPG/g4fhFX5DEFmjxIEa3N8Zr96SjsJ1woAhF0DUnS3MF3ARw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild/node_modules/@esbuild/win32-arm64": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.2.tgz", + "integrity": "sha512-5NayUlSAyb5PQYFAU9x3bHdsqB88RC3aM9lKDAz4X1mo/EchMIT1Q+pSeBXNgkfNmRecLXA0O8xP+x8V+g/LKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild/node_modules/@esbuild/win32-ia32": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.2.tgz", + "integrity": "sha512-47gL/ek1v36iN0wL9L4Q2MFdujR0poLZMJwhO2/N3gA89jgHp4MR8DKCmwYtGNksbfJb9JoTtbkoe6sDhg2QTA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild/node_modules/@esbuild/win32-x64": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.2.tgz", + "integrity": "sha512-tcuhV7ncXBqbt/Ybf0IyrMcwVOAPDckMK9rXNHtF17UTK18OKLpg08glminN06pt2WCoALhXdLfSPbVvK/6fxw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", diff --git a/package.json b/package.json index b4c8079..d67b9b3 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "@aws-sdk/client-ec2": "^3.405.0", "@aws-sdk/client-lambda": "^3.395.0", "@aws-sdk/client-pricing": "^3.405.0", + "@aws-sdk/client-scheduler": "^3.409.0", "@aws-sdk/lib-dynamodb": "^3.395.0", "@aws-sdk/util-dynamodb": "^3.405.0", "@types/aws-lambda": "^8.10.119",