import * as AWS from 'aws-sdk' ; import { Context , } from 'aws-lambda' ; import { CreateTaskInput , pprint , assert , makeid , TaskProgress } from '../common' ; interface QueryTaskCfnResponse { stackId : string ; stackStatus : string ; stackStatusReason ? : string ; } interface CfnTaskInput extends CreateTaskInput { templateUrl : string ; id : string ; } interface StopTaskInput { id : string ; stackId : string ; } interface QueryCfnTaskInput extends CfnTaskInput { stackId : string ; } param input param context exports . createTaskCfn = async function ( input , context ) { pprint ( 'INPUT' , input ) ; pprint ( 'CONTEXT' , context ) ; assert ( process . env . TASK_TABLE !== undefined , 'No TASK_TABLE env' ) ; assert ( process . env . AWS_REGION !== undefined , 'No AWS_REGION env' ) ; const id = makeid ( 5 ) ; const cfn = new AWS . CloudFormation ( ) ; const stack = await cfn . createStack ( { TemplateURL : input . templateUrl , StackName : `DTH-${ input . type . toString ( ) }-${ id }` , Parameters : input . parameters , Capabilities : [ 'CAPABILITY_NAMED_IAM' ] , Tags : [ { Key : "TaskId" , Value : id } ] } ) . promise ( ) ; assert ( stack . StackId !== undefined , 'No Stack ID' ) ; const ddb = new AWS . DynamoDB . DocumentClient ( { region : process . env . AWS_REGION } ) ; const updatedItemRes = await ddb . update ( { TableName : process . env . TASK_TABLE , Key : { id : input . id } , UpdateExpression : 'set progress = :progress, stackId = :stackId, stackStatus = :stackStatus' , ExpressionAttributeValues : { ':progress' : 'STARTING' , ':stackId' : stack . StackId , ':stackStatus' : 'CREATE_IN_PROGRESS' } , ReturnValues : "ALL_NEW" } ) . promise ( ) ; pprint ( 'updatedItemRes.Attributes' , updatedItemRes . Attributes ) ; return updatedItemRes . Attributes ; } ; param input exports . stopTaskCfn = async function ( input ) { assert ( process . env . TASK_TABLE !== undefined , 'NO TASK_TABLE env' ) ; pprint ( 'INPUT' , input ) ; const cfn = new AWS . CloudFormation ( ) ; const ddb = new AWS . DynamoDB . DocumentClient ( ) ; const deleteStackRes = await cfn . deleteStack ( { StackName : input . stackId } ) . promise ( ) ; pprint ( 'deleteStackRes' , deleteStackRes ) ; const task = await ddb . update ( { TableName : process . env . TASK_TABLE , Key : { id : input . id } , UpdateExpression : 'set progress = :progress' , ExpressionAttributeValues : { ':progress' : 'STOPPING' } , ReturnValues : "ALL_NEW" } ) . promise ( ) ; pprint ( 'task' , task . Attributes ) ; return task . Attributes ; } ; param input exports . queryTaskCfn = async function ( input ) { assert ( process . env . TASK_TABLE !== undefined , 'NO TASK_TABLE env' ) ; pprint ( 'INPUT' , input ) ; const cfn = new AWS . CloudFormation ( ) ; const ddb = new AWS . DynamoDB . DocumentClient ( ) ; const describeStackResult = await cfn . describeStacks ( { StackName : input . stackId } ) . promise ( ) ; if ( describeStackResult . Stacks && describeStackResult . Stacks . length > 0 ) { const queryResult = { stackId : input . stackId , stackStatus : describeStackResult . Stacks [ 0 ] . StackStatus , stackStatusReason : describeStackResult . Stacks [ 0 ] . StackStatusReason } ; const getProgress = ( ) => { const stackStatus = queryResult . stackStatus ; switch ( stackStatus ) { case 'CREATE_IN_PROGRESS' : return TaskProgress . STARTING ; case 'CREATE_COMPLETE' : return TaskProgress . IN_PROGRESS ; case 'DELETE_IN_PROGRESS' : return TaskProgress . STOPPING ; case 'DELETE_COMPLETE' : return TaskProgress . STOPPED ; default : return 'ERROR' ; } } ; const updatedTask = await ddb . update ( { TableName : process . env . TASK_TABLE , Key : { id : input . id } , UpdateExpression : 'set stackStatus = :stackStatus, progress = :progress' , ConditionExpression : `stackStatus <> ${ queryResult . stackStatus }` , ExpressionAttributeValues : { ':stackStatus' : queryResult . stackStatus , ':progress' : getProgress ( ) } , ReturnValues : "ALL_NEW" } ) . promise ( ) ; pprint ( 'updatedTask.Attributes' , updatedTask . Attributes ) ; return updatedTask . Attributes ; } else { return new Error ( `Query failed, stackId: ${ input . stackId }` ) ; } } ;