agent-claw: automated task changes

This commit is contained in:
daniel
2026-05-06 18:55:16 -05:00
parent 38905bb1e9
commit 732b00fb66
8494 changed files with 2018127 additions and 4 deletions

View File

@@ -0,0 +1,2 @@
export * from './provider';
export * from './waiter-state-machine';

View File

@@ -0,0 +1 @@
"use strict";var __createBinding=exports&&exports.__createBinding||(Object.create?(function(o,m,k,k2){k2===void 0&&(k2=k);var desc=Object.getOwnPropertyDescriptor(m,k);(!desc||("get"in desc?!m.__esModule:desc.writable||desc.configurable))&&(desc={enumerable:!0,get:function(){return m[k]}}),Object.defineProperty(o,k2,desc)}):(function(o,m,k,k2){k2===void 0&&(k2=k),o[k2]=m[k]})),__exportStar=exports&&exports.__exportStar||function(m,exports2){for(var p in m)p!=="default"&&!Object.prototype.hasOwnProperty.call(exports2,p)&&__createBinding(exports2,m,p)};Object.defineProperty(exports,"__esModule",{value:!0});var _noFold;exports.Provider=void 0,Object.defineProperty(exports,_noFold="Provider",{enumerable:!0,configurable:!0,get:()=>{var value=require("./provider").Provider;return Object.defineProperty(exports,_noFold="Provider",{enumerable:!0,configurable:!0,value}),value}}),exports.WaiterStateMachine=void 0,Object.defineProperty(exports,_noFold="WaiterStateMachine",{enumerable:!0,configurable:!0,get:()=>{var value=require("./waiter-state-machine").WaiterStateMachine;return Object.defineProperty(exports,_noFold="WaiterStateMachine",{enumerable:!0,configurable:!0,value}),value}});

View File

@@ -0,0 +1,199 @@
import { Construct } from 'constructs';
import type { LogOptions } from './waiter-state-machine';
import '../../../aws-cloudformation';
import type * as ec2 from '../../../aws-ec2';
import * as iam from '../../../aws-iam';
import type * as kms from '../../../aws-kms';
import * as lambda from '../../../aws-lambda';
import type * as logs from '../../../aws-logs';
import { Duration } from '../../../core';
/**
* Initialization properties for the `Provider` construct.
*/
export interface ProviderProps {
/**
* The AWS Lambda function to invoke for all resource lifecycle operations
* (CREATE/UPDATE/DELETE).
*
* This function is responsible to begin the requested resource operation
* (CREATE/UPDATE/DELETE) and return any additional properties to add to the
* event, which will later be passed to `isComplete`. The `PhysicalResourceId`
* property must be included in the response.
*/
readonly onEventHandler: lambda.IFunction;
/**
* The AWS Lambda function to invoke in order to determine if the operation is
* complete.
*
* This function will be called immediately after `onEvent` and then
* periodically based on the configured query interval as long as it returns
* `false`. If the function still returns `false` and the alloted timeout has
* passed, the operation will fail.
*
* @default - provider is synchronous. This means that the `onEvent` handler
* is expected to finish all lifecycle operations within the initial invocation.
*/
readonly isCompleteHandler?: lambda.IFunction;
/**
* Time between calls to the `isComplete` handler which determines if the
* resource has been stabilized.
*
* The first `isComplete` will be called immediately after `handler` and then
* every `queryInterval` seconds, and until `timeout` has been reached or until
* `isComplete` returns `true`.
*
* @default Duration.seconds(5)
*/
readonly queryInterval?: Duration;
/**
* Total timeout for the entire operation.
*
* The maximum timeout is 1 hour (yes, it can exceed the AWS Lambda 15 minutes)
*
* @default Duration.minutes(30)
*/
readonly totalTimeout?: Duration;
/**
* The number of days framework log events are kept in CloudWatch Logs. When
* updating this property, unsetting it doesn't remove the log retention policy.
* To remove the retention policy, set the value to `INFINITE`.
*
* This is a legacy API and we strongly recommend you migrate to `logGroup` if you can.
* `logGroup` allows you to create a fully customizable log group and instruct the Lambda function to send logs to it.
*
* @default logs.RetentionDays.INFINITE
*/
readonly logRetention?: logs.RetentionDays;
/**
* The Log Group used for logging of events emitted by the custom resource's lambda function.
*
* Providing a user-controlled log group was rolled out to commercial regions on 2023-11-16.
* If you are deploying to another type of region, please check regional availability first.
*
* @default - a default log group created by AWS Lambda
*/
readonly logGroup?: logs.ILogGroupRef;
/**
* The vpc to provision the lambda functions in.
*
* @default - functions are not provisioned inside a vpc.
*/
readonly vpc?: ec2.IVpc;
/**
* Which subnets from the VPC to place the lambda functions in.
*
* Only used if 'vpc' is supplied. Note: internet access for Lambdas
* requires a NAT gateway, so picking Public subnets is not allowed.
*
* @default - the Vpc default strategy if not specified
*/
readonly vpcSubnets?: ec2.SubnetSelection;
/**
* Security groups to attach to the provider functions.
*
* Only used if 'vpc' is supplied
*
* @default - If `vpc` is not supplied, no security groups are attached. Otherwise, a dedicated security
* group is created for each function.
*/
readonly securityGroups?: ec2.ISecurityGroup[];
/**
* AWS Lambda execution role.
*
* The role is shared by provider framework's onEvent, isComplete lambda, and onTimeout Lambda functions.
* This role will be assumed by the AWS Lambda, so it must be assumable by the 'lambda.amazonaws.com'
* service principal.
*
* @default - A default role will be created.
* @deprecated - Use frameworkOnEventRole, frameworkCompleteAndTimeoutRole
*/
readonly role?: iam.IRole;
/**
* Lambda execution role for provider framework's onEvent Lambda function. Note that this role must be assumed
* by the 'lambda.amazonaws.com' service principal.
*
* This property cannot be used with 'role' property
*
* @default - A default role will be created.
*/
readonly frameworkOnEventRole?: iam.IRole;
/**
* Lambda execution role for provider framework's isComplete/onTimeout Lambda function. Note that this role
* must be assumed by the 'lambda.amazonaws.com' service principal. To prevent circular dependency problem
* in the provider framework, please ensure you specify a different IAM Role for 'frameworkCompleteAndTimeoutRole'
* from 'frameworkOnEventRole'.
*
* This property cannot be used with 'role' property
*
* @default - A default role will be created.
*/
readonly frameworkCompleteAndTimeoutRole?: iam.IRole;
/**
* Provider Lambda name.
*
* The provider lambda function name.
*
* @default - CloudFormation default name from unique physical ID
*/
readonly providerFunctionName?: string;
/**
* AWS KMS key used to encrypt provider lambda's environment variables.
*
* @default - AWS Lambda creates and uses an AWS managed customer master key (CMK)
*/
readonly providerFunctionEnvEncryption?: kms.IKeyRef;
/**
* Defines what execution history events of the waiter state machine are logged and where they are logged.
*
* @default - A default log group will be created if logging for the waiter state machine is enabled.
*/
readonly waiterStateMachineLogOptions?: LogOptions;
/**
* Whether logging for the waiter state machine is disabled.
*
* @default - true
*/
readonly disableWaiterStateMachineLogging?: boolean;
/**
* Log level of the provider framework lambda
*
* @default true - Logging is disabled by default
*/
readonly frameworkLambdaLoggingLevel?: lambda.ApplicationLogLevel;
}
/**
* Defines an AWS CloudFormation custom resource provider.
*/
export declare class Provider extends Construct {
/**
* Uniquely identifies this class.
*/
static readonly PROPERTY_INJECTION_ID: string;
/**
* The user-defined AWS Lambda function which is invoked for all resource
* lifecycle operations (CREATE/UPDATE/DELETE).
*/
readonly onEventHandler: lambda.IFunction;
/**
* The user-defined AWS Lambda function which is invoked asynchronously in
* order to determine if the operation is complete.
*/
readonly isCompleteHandler?: lambda.IFunction;
/**
* The service token to use in order to define custom resources that are
* backed by this provider.
*/
readonly serviceToken: string;
private readonly entrypoint;
private readonly logRetention?;
private readonly logGroup?;
private readonly vpc?;
private readonly vpcSubnets?;
private readonly securityGroups?;
private readonly role?;
private readonly providerFunctionEnvEncryption?;
private readonly frameworkLambdaLoggingLevel?;
constructor(scope: Construct, id: string, props: ProviderProps);
private addPermissions;
private createFunction;
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,21 @@
import type { OnEventResponse } from '../types';
export declare const CREATE_FAILED_PHYSICAL_ID_MARKER = "AWSCDK::CustomResourceProviderFramework::CREATE_FAILED";
export declare const MISSING_PHYSICAL_ID_MARKER = "AWSCDK::CustomResourceProviderFramework::MISSING_PHYSICAL_ID";
export interface CloudFormationResponseOptions {
readonly reason?: string;
readonly noEcho?: boolean;
}
export interface CloudFormationEventContext {
StackId: string;
RequestId: string;
PhysicalResourceId?: string;
LogicalResourceId: string;
ResponseURL: string;
Data?: any;
}
export declare function submitResponse(status: 'SUCCESS' | 'FAILED', event: CloudFormationEventContext, options?: CloudFormationResponseOptions): Promise<void>;
export declare let includeStackTraces: boolean;
export declare function safeHandler(block: (event: any) => Promise<void>): (event: any) => Promise<void>;
export declare function redactDataFromPayload(payload: OnEventResponse): OnEventResponse;
export declare class Retry extends Error {
}

View File

@@ -0,0 +1 @@
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.Retry=exports.includeStackTraces=exports.MISSING_PHYSICAL_ID_MARKER=exports.CREATE_FAILED_PHYSICAL_ID_MARKER=void 0,exports.submitResponse=submitResponse,exports.safeHandler=safeHandler,exports.redactDataFromPayload=redactDataFromPayload;const url=require("url"),outbound_1=require("./outbound"),util_1=require("./util");exports.CREATE_FAILED_PHYSICAL_ID_MARKER="AWSCDK::CustomResourceProviderFramework::CREATE_FAILED",exports.MISSING_PHYSICAL_ID_MARKER="AWSCDK::CustomResourceProviderFramework::MISSING_PHYSICAL_ID";async function submitResponse(status,event,options={}){const json={Status:status,Reason:options.reason||status,StackId:event.StackId,RequestId:event.RequestId,PhysicalResourceId:event.PhysicalResourceId||exports.MISSING_PHYSICAL_ID_MARKER,LogicalResourceId:event.LogicalResourceId,NoEcho:options.noEcho,Data:event.Data},responseBody=JSON.stringify(json),parsedUrl=url.parse(event.ResponseURL),loggingSafeUrl=`${parsedUrl.protocol}//${parsedUrl.hostname}/${parsedUrl.pathname}?***`;options?.noEcho?(0,util_1.log)("submit redacted response to cloudformation",loggingSafeUrl,redactDataFromPayload(json)):(0,util_1.log)("submit response to cloudformation",loggingSafeUrl,json);const retryOptions={attempts:5,sleep:1e3};await(0,util_1.withRetries)(retryOptions,outbound_1.httpRequest)({hostname:parsedUrl.hostname,path:parsedUrl.path,method:"PUT",headers:{"content-type":"","content-length":Buffer.byteLength(responseBody,"utf8")}},responseBody)}exports.includeStackTraces=!0;function safeHandler(block){return async event=>{if(event.RequestType==="Delete"&&event.PhysicalResourceId===exports.CREATE_FAILED_PHYSICAL_ID_MARKER){(0,util_1.log)("ignoring DELETE event caused by a failed CREATE event"),await submitResponse("SUCCESS",event);return}try{await block(event)}catch(e){if(e instanceof Retry)throw(0,util_1.log)("retry requested by handler"),e;event.PhysicalResourceId||(event.RequestType==="Create"?((0,util_1.log)("CREATE failed, responding with a marker physical resource id so that the subsequent DELETE will be ignored"),event.PhysicalResourceId=exports.CREATE_FAILED_PHYSICAL_ID_MARKER):(0,util_1.log)(`ERROR: Malformed event. "PhysicalResourceId" is required: ${JSON.stringify({...event,ResponseURL:"..."})}`)),await submitResponse("FAILED",event,{reason:exports.includeStackTraces?e.stack:e.message})}}}function redactDataFromPayload(payload){const redactedPayload=JSON.parse(JSON.stringify(payload));if(redactedPayload.Data){const keys=Object.keys(redactedPayload.Data);for(const key of keys)redactedPayload.Data[key]="*****"}return redactedPayload}class Retry extends Error{}exports.Retry=Retry;

View File

@@ -0,0 +1,6 @@
export declare const USER_ON_EVENT_FUNCTION_ARN_ENV = "USER_ON_EVENT_FUNCTION_ARN";
export declare const USER_IS_COMPLETE_FUNCTION_ARN_ENV = "USER_IS_COMPLETE_FUNCTION_ARN";
export declare const WAITER_STATE_MACHINE_ARN_ENV = "WAITER_STATE_MACHINE_ARN";
export declare const FRAMEWORK_ON_EVENT_HANDLER_NAME = "onEvent";
export declare const FRAMEWORK_IS_COMPLETE_HANDLER_NAME = "isComplete";
export declare const FRAMEWORK_ON_TIMEOUT_HANDLER_NAME = "onTimeout";

View File

@@ -0,0 +1 @@
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.FRAMEWORK_ON_TIMEOUT_HANDLER_NAME=exports.FRAMEWORK_IS_COMPLETE_HANDLER_NAME=exports.FRAMEWORK_ON_EVENT_HANDLER_NAME=exports.WAITER_STATE_MACHINE_ARN_ENV=exports.USER_IS_COMPLETE_FUNCTION_ARN_ENV=exports.USER_ON_EVENT_FUNCTION_ARN_ENV=void 0,exports.USER_ON_EVENT_FUNCTION_ARN_ENV="USER_ON_EVENT_FUNCTION_ARN",exports.USER_IS_COMPLETE_FUNCTION_ARN_ENV="USER_IS_COMPLETE_FUNCTION_ARN",exports.WAITER_STATE_MACHINE_ARN_ENV="WAITER_STATE_MACHINE_ARN",exports.FRAMEWORK_ON_EVENT_HANDLER_NAME="onEvent",exports.FRAMEWORK_IS_COMPLETE_HANDLER_NAME="isComplete",exports.FRAMEWORK_ON_TIMEOUT_HANDLER_NAME="onTimeout";

View File

@@ -0,0 +1,7 @@
declare const _default: {
onEvent: (event: any) => Promise<void>;
isComplete: (event: any) => Promise<void>;
onTimeout: typeof onTimeout;
};
export = _default;
declare function onTimeout(timeoutEvent: any): Promise<void>;

View File

@@ -0,0 +1,3 @@
"use strict";const cfnResponse=require("./cfn-response"),consts=require("./consts"),outbound_1=require("./outbound"),util_1=require("./util");async function onEvent(cfnRequest){const sanitizedRequest={...cfnRequest,ResponseURL:"..."};(0,util_1.log)("onEventHandler",sanitizedRequest),cfnRequest.ResourceProperties=cfnRequest.ResourceProperties||{};const onEventResult=await invokeUserFunction(consts.USER_ON_EVENT_FUNCTION_ARN_ENV,sanitizedRequest,cfnRequest.ResponseURL);onEventResult?.NoEcho?(0,util_1.log)("redacted onEvent returned:",cfnResponse.redactDataFromPayload(onEventResult)):(0,util_1.log)("onEvent returned:",onEventResult);const resourceEvent=createResponseEvent(cfnRequest,onEventResult),sanitizedEvent={...resourceEvent,ResponseURL:"..."};if(onEventResult?.NoEcho?(0,util_1.log)("readacted event:",cfnResponse.redactDataFromPayload(sanitizedEvent)):(0,util_1.log)("event:",sanitizedEvent),!process.env[consts.USER_IS_COMPLETE_FUNCTION_ARN_ENV])return cfnResponse.submitResponse("SUCCESS",resourceEvent,{noEcho:resourceEvent.NoEcho});const waiter={stateMachineArn:(0,util_1.getEnv)(consts.WAITER_STATE_MACHINE_ARN_ENV),input:JSON.stringify(resourceEvent)};(0,util_1.log)("starting waiter",{stateMachineArn:(0,util_1.getEnv)(consts.WAITER_STATE_MACHINE_ARN_ENV)}),await(0,outbound_1.startExecution)(waiter)}async function isComplete(event){const sanitizedRequest={...event,ResponseURL:"..."};event?.NoEcho?(0,util_1.log)("redacted isComplete request",cfnResponse.redactDataFromPayload(sanitizedRequest)):(0,util_1.log)("isComplete",sanitizedRequest);const isCompleteResult=await invokeUserFunction(consts.USER_IS_COMPLETE_FUNCTION_ARN_ENV,sanitizedRequest,event.ResponseURL);if(event?.NoEcho?(0,util_1.log)("redacted user isComplete returned:",cfnResponse.redactDataFromPayload(isCompleteResult)):(0,util_1.log)("user isComplete returned:",isCompleteResult),!isCompleteResult.IsComplete)throw isCompleteResult.Data&&Object.keys(isCompleteResult.Data).length>0?new Error('"Data" is not allowed if "IsComplete" is "False"'):new cfnResponse.Retry(JSON.stringify(event));const response={...event,...isCompleteResult,Data:{...event.Data,...isCompleteResult.Data}};await cfnResponse.submitResponse("SUCCESS",response,{noEcho:event.NoEcho})}async function onTimeout(timeoutEvent){(0,util_1.log)("timeoutHandler",timeoutEvent);const isCompleteRequest=JSON.parse(JSON.parse(timeoutEvent.Cause).errorMessage);await cfnResponse.submitResponse("FAILED",isCompleteRequest,{reason:"Operation timed out"})}async function invokeUserFunction(functionArnEnv,sanitizedPayload,responseUrl){const functionArn=(0,util_1.getEnv)(functionArnEnv);(0,util_1.log)(`executing user function ${functionArn} with payload`,sanitizedPayload);const resp=await(0,outbound_1.invokeFunction)({FunctionName:functionArn,Payload:JSON.stringify({...sanitizedPayload,ResponseURL:responseUrl})});(0,util_1.log)("user function response:",resp,typeof resp);const jsonPayload=(0,util_1.parseJsonPayload)(resp.Payload);if(resp.FunctionError){(0,util_1.log)("user function threw an error:",resp.FunctionError);const errorMessage=jsonPayload.errorMessage||"error",arn=functionArn.split(":"),functionName=arn[arn.length-1],message=[errorMessage,"",`Logs: /aws/lambda/${functionName}`,""].join(`
`),e=new Error(message);throw jsonPayload.trace&&(e.stack=[message,...jsonPayload.trace.slice(1)].join(`
`)),e}return jsonPayload}function createResponseEvent(cfnRequest,onEventResult){onEventResult=onEventResult||{};const physicalResourceId=onEventResult.PhysicalResourceId||defaultPhysicalResourceId(cfnRequest);if(cfnRequest.RequestType==="Delete"&&physicalResourceId!==cfnRequest.PhysicalResourceId)throw new Error(`DELETE: cannot change the physical resource ID from "${cfnRequest.PhysicalResourceId}" to "${onEventResult.PhysicalResourceId}" during deletion`);return cfnRequest.RequestType==="Update"&&physicalResourceId!==cfnRequest.PhysicalResourceId&&(0,util_1.log)(`UPDATE: changing physical resource ID from "${cfnRequest.PhysicalResourceId}" to "${onEventResult.PhysicalResourceId}"`),{...cfnRequest,...onEventResult,PhysicalResourceId:physicalResourceId}}function defaultPhysicalResourceId(req){switch(req.RequestType){case"Create":return req.RequestId;case"Update":case"Delete":return req.PhysicalResourceId;default:throw new Error(`Invalid "RequestType" in request "${JSON.stringify(req)}"`)}}module.exports={[consts.FRAMEWORK_ON_EVENT_HANDLER_NAME]:cfnResponse.safeHandler(onEvent),[consts.FRAMEWORK_IS_COMPLETE_HANDLER_NAME]:cfnResponse.safeHandler(isComplete),[consts.FRAMEWORK_ON_TIMEOUT_HANDLER_NAME]:onTimeout};

View File

@@ -0,0 +1,10 @@
import * as https from 'https';
import type { InvocationResponse, InvokeCommandInput } from '@aws-sdk/client-lambda';
import type { StartExecutionInput, StartExecutionOutput } from '@aws-sdk/client-sfn';
declare function defaultHttpRequest(options: https.RequestOptions, requestBody: string): Promise<void>;
declare function defaultStartExecution(req: StartExecutionInput): Promise<StartExecutionOutput>;
declare function defaultInvokeFunction(req: InvokeCommandInput): Promise<InvocationResponse>;
export declare let startExecution: typeof defaultStartExecution;
export declare let invokeFunction: typeof defaultInvokeFunction;
export declare let httpRequest: typeof defaultHttpRequest;
export {};

View File

@@ -0,0 +1 @@
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.httpRequest=exports.invokeFunction=exports.startExecution=void 0;const https=require("https"),client_lambda_1=require("@aws-sdk/client-lambda"),client_sfn_1=require("@aws-sdk/client-sfn"),FRAMEWORK_HANDLER_TIMEOUT=9e5,awsSdkConfig={httpOptions:{timeout:FRAMEWORK_HANDLER_TIMEOUT}};async function defaultHttpRequest(options,requestBody){return new Promise((resolve,reject)=>{try{const request=https.request(options,response=>{response.resume(),!response.statusCode||response.statusCode>=400?reject(new Error(`Unsuccessful HTTP response: ${response.statusCode}`)):resolve()});request.on("error",reject),request.write(requestBody),request.end()}catch(e){reject(e)}})}let sfn,lambda;async function defaultStartExecution(req){return sfn||(sfn=new client_sfn_1.SFN(awsSdkConfig)),sfn.startExecution(req)}async function defaultInvokeFunction(req){lambda||(lambda=new client_lambda_1.Lambda(awsSdkConfig));try{return await lambda.invoke(req)}catch{return await(0,client_lambda_1.waitUntilFunctionActiveV2)({client:lambda,maxWaitTime:300},{FunctionName:req.FunctionName}),lambda.invoke(req)}}exports.startExecution=defaultStartExecution,exports.invokeFunction=defaultInvokeFunction,exports.httpRequest=defaultHttpRequest;

View File

@@ -0,0 +1,10 @@
export declare function getEnv(name: string): string;
export declare function log(title: any, ...args: any[]): void;
export interface RetryOptions {
/** How many retries (will at least try once) */
readonly attempts: number;
/** Sleep base, in ms */
readonly sleep: number;
}
export declare function withRetries<A extends Array<any>, B>(options: RetryOptions, fn: (...xs: A) => Promise<B>): (...xs: A) => Promise<B>;
export declare function parseJsonPayload(payload: string | Buffer | Uint8Array | undefined | null): any;

View File

@@ -0,0 +1 @@
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.getEnv=getEnv,exports.log=log,exports.withRetries=withRetries,exports.parseJsonPayload=parseJsonPayload;function getEnv(name){const value=process.env[name];if(!value)throw new Error(`The environment variable "${name}" is not defined`);return value}function log(title,...args){console.log("[provider-framework]",title,...args.map(x=>typeof x=="object"?JSON.stringify(x,void 0,2):x))}function withRetries(options,fn){return async(...xs)=>{let attempts=options.attempts,ms=options.sleep;for(;;)try{return await fn(...xs)}catch(e){if(attempts--<=0)throw e;await sleep(Math.floor(Math.random()*ms)),ms*=2}}}async function sleep(ms){return new Promise(ok=>setTimeout(ok,ms))}function parseJsonPayload(payload){const text=new TextDecoder().decode(Buffer.from(payload??""));if(!text)return{};try{return JSON.parse(text)}catch{throw new Error(`return values from user-handlers must be JSON objects. got: "${text}"`)}}

View File

@@ -0,0 +1,117 @@
// this is a type definition file that exports ambiant types that can be used
// to implement async custom resource handler and enjoy the comfort of type safety.
/**
* these types can be accessed without needing to `import` the module.
* e.g. `AWSCDKAsyncCustomResource.OnEventRequest`
*/
export as namespace AWSCDKAsyncCustomResource;
/**
* Signature for the `onEvent` handler, which is called when a lifecycle event occurs.
*/
export type OnEventHandler = (event: OnEventRequest) => Promise<OnEventResponse | undefined>;
/**
* Signature for the `isComplete` handler, which is called to detemrine if the
* event handling is complete. As long as this method returns `IsComplete:
* false`, the handler will be called (based on the rety policy defined by the
* provider) until a timeout occurs, an error is thrown or until it returns
* `true`.
*/
export type IsCompleteHandler = (event: IsCompleteRequest) => Promise<IsCompleteResponse>;
/**
* The object passed to the user-defined `onEvent` handler.
*/
export interface OnEventRequest extends AWSLambda.CloudFormationCustomResourceEventCommon {
/**
* The request type is set by the AWS CloudFormation stack operation
* (create-stack, update-stack, or delete-stack) that was initiated by the
* template developer for the stack that contains the custom resource.
*/
readonly RequestType: 'Create' | 'Update' | 'Delete';
/**
* Used only for Update requests. Contains the resource properties that were
* declared previous to the update request.
*/
readonly OldResourceProperties?: { [key: string]: any };
/**
* A required custom resource provider-defined physical ID that is unique for
* that provider.
*
* Always sent with 'Update' and 'Delete' requests; never sent with 'Create'.
*/
readonly PhysicalResourceId?: string;
}
/**
* The object returned from the user-defined `onEvent` handler.
*/
interface OnEventResponse {
/**
* A required custom resource provider-defined physical ID that is unique for
* that provider.
*
* In order to reduce the chance for mistakes, all event types MUST return
* with `PhysicalResourceId`.
*
* - For `Create`, this will be the user-defined or generated physical
* resource ID.
* - For `Update`, if the returned PhysicalResourceId is different value from
* the current one, it means that the old physical resource needs to be
* deleted, and CloudFormation will immediately send a `Delete` event with
* the old physical ID.
* - For `Delete`, this must be the same value received in the event.
*
* @default - for "Create" requests, defaults to the event's RequestId, for
* "Update" and "Delete", defaults to the current `PhysicalResourceId`.
*/
readonly PhysicalResourceId?: string;
/**
* Resource attributes to return.
*/
readonly Data?: { [name: string]: any };
/**
* Custom fields returned from OnEvent will be passed to IsComplete.
*/
readonly [key: string]: any;
/**
* Whether to mask the output of the custom resource when retrieved
* by using the `Fn::GetAtt` function. If set to `true`, all returned
* values are masked with asterisks (*****).
*
* @default false
*/
readonly NoEcho?: boolean;
}
/**
* The input to the `isComplete` user defined handler.
*/
export type IsCompleteRequest = OnEventRequest & OnEventResponse;
/**
* The output of the `isComplete` user-defined handler.
*/
export interface IsCompleteResponse {
/**
* Indicates if the resource operation is complete or should we retry.
*/
readonly IsComplete: boolean;
/**
* If present, overrides the PhysicalResourceId of OnEventResponse with the PhysicalResourceId of IsCompleteResponse.
*/
readonly PhysicalResourceId?: string;
/**
* Additional/changes to resource attributes. This hash will be merged with the one returned from `OnEventResponse`.
*/
readonly Data?: { [name: string]: any };
}

View File

@@ -0,0 +1,10 @@
import type { IConstruct } from 'constructs';
import { Duration } from '../../../core';
export declare function calculateRetryPolicy(scope: IConstruct, props?: {
totalTimeout?: Duration;
queryInterval?: Duration;
}): {
maxAttempts: number;
interval: Duration;
backoffRate: number;
};

View File

@@ -0,0 +1 @@
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.calculateRetryPolicy=calculateRetryPolicy;var core_1=()=>{var tmp=require("../../../core");return core_1=()=>tmp,tmp},literal_string_1=()=>{var tmp=require("../../../core/lib/private/literal-string");return literal_string_1=()=>tmp,tmp};const DEFAULT_TIMEOUT=core_1().Duration.minutes(30),DEFAULT_INTERVAL=core_1().Duration.seconds(5);function calculateRetryPolicy(scope,props={}){const totalTimeout=props.totalTimeout||DEFAULT_TIMEOUT,interval=props.queryInterval||DEFAULT_INTERVAL,maxAttempts=totalTimeout.toSeconds()/interval.toSeconds();if(Math.round(maxAttempts)!==maxAttempts)throw new(core_1()).ValidationError((0,literal_string_1().lit)`CannotDetermineRetryCountSince`,`Cannot determine retry count since totalTimeout=${totalTimeout.toSeconds()}s is not integrally dividable by queryInterval=${interval.toSeconds()}s`,scope);return{maxAttempts,interval,backoffRate:1}}

View File

@@ -0,0 +1,94 @@
import { Construct } from 'constructs';
import type { IGrantable } from '../../../aws-iam';
import { Grant } from '../../../aws-iam';
import type { IFunction } from '../../../aws-lambda';
import type { ILogGroupRef } from '../../../aws-logs';
import { LogLevel } from '../../../aws-stepfunctions';
import type { Duration } from '../../../core';
/**
* Log Options for the state machine.
*/
export interface LogOptions {
/**
* The log group where the execution history events will be logged.
*
* @default - a new log group will be created
*/
readonly destination?: ILogGroupRef;
/**
* Determines whether execution data is included in your log.
*
* @default - false
*/
readonly includeExecutionData?: boolean;
/**
* Defines which category of execution history events are logged.
*
* @default - ERROR
*/
readonly level?: LogLevel;
}
/**
* Initialization properties for the `WaiterStateMachine` construct.
*/
export interface WaiterStateMachineProps {
/**
* The main handler that notifies if the waiter to decide 'complete' or 'incomplete'.
*/
readonly isCompleteHandler: IFunction;
/**
* The handler to call if the waiter times out and is incomplete.
*/
readonly timeoutHandler: IFunction;
/**
* The interval to wait between attempts.
*/
readonly interval: Duration;
/**
* Number of attempts.
*/
readonly maxAttempts: number;
/**
* Backoff between attempts.
*/
readonly backoffRate: number;
/**
* Defines what execution history events are logged and where they are logged.
*
* @default - A default log group will be created if logging is enabled.
*/
readonly logOptions?: LogOptions;
/**
* Whether logging for the state machine is disabled.
*
* @default - false
*/
readonly disableLogging?: boolean;
}
/**
* A very simple StateMachine construct highly customized to the provider framework.
* We previously used `CfnResource` instead of `CfnStateMachine` to avoid depending
* on `aws-stepfunctions` module, but now it is okay.
*
* The state machine continuously calls the isCompleteHandler, until it succeeds or times out.
* The handler is called `maxAttempts` times with an `interval` duration and a `backoffRate` rate.
*/
export declare class WaiterStateMachine extends Construct {
/**
* Uniquely identifies this class.
*/
static readonly PROPERTY_INJECTION_ID: string;
/**
* The ARN of the state machine.
*/
readonly stateMachineArn: string;
private readonly isCompleteHandler;
constructor(scope: Construct, id: string, props: WaiterStateMachineProps);
/**
* Grant the given identity permissions on StartExecution of the state machine.
*
* [disable-awslint:no-grants]
*/
grantStartExecution(identity: IGrantable): Grant;
private renderLoggingConfiguration;
}

File diff suppressed because one or more lines are too long