Skip to content

Instantly share code, notes, and snippets.

@homeyjd
Created March 26, 2019 20:08
Show Gist options
  • Save homeyjd/7d228c86ef3ddaf7c4585a516dc44252 to your computer and use it in GitHub Desktop.
Save homeyjd/7d228c86ef3ddaf7c4585a516dc44252 to your computer and use it in GitHub Desktop.
lifecycle-poller Lambda with less characters
let AWS = require('aws-sdk')
let ecs = new AWS.ECS()
let sqs = new AWS.SQS()
let asg = new AWS.AutoScaling()
let cluster = process.env.CLUSTER
let QueueUrl = process.env.QUEUE_URL
let drainingTimeout = process.env.DRAINING_TIMEOUT
async function getContainerInstanceArn(id) {
let o = { cluster, filter: `ec2InstanceId == '${id}'` }
console.log("getContainerInstanceArn(", o, ")")
return (await ecs.listContainerInstances(o).promise()).containerInstanceArns[0]
}
async function countTasks(containerInstance) {
let o = { cluster, containerInstance }
console.log("countTasks(", o, ")")
return (await ecs.listTasks(o).promise()).taskArns.length
}
function drainInstance(arn) {
let o = { cluster, containerInstances: [arn], status: 'DRAINING' }
console.log("drainInstance(", o, ")")
return ecs.updateContainerInstancesState(o).promise()
}
function wait(r) {
r = {
Service: 'DrainInstance',
Event: 'custom:DRAIN_WAIT',
ContainerInstanceArn: r.ContainerInstanceArn,
AutoScalingGroupName: r.AutoScalingGroupName,
LifecycleHookName: r.LifecycleHookName,
LifecycleActionToken: r.LifecycleActionToken,
TerminateTime: r.TerminateTime,
}
console.log("wait(", r, ")")
return sqs.sendMessage({
QueueUrl,
DelaySeconds: 60,
MessageBody: JSON.stringify(r)
}).promise()
}
function deleteMessage(ReceiptHandle) {
let req = { QueueUrl, ReceiptHandle }
console.log("deleteMessage(", req, ")")
return sqs.deleteMessage(req).promise()
}
function terminateInstance(r) {
r = {
AutoScalingGroupName: r.AutoScalingGroupName,
LifecycleHookName: r.LifecycleHookName,
LifecycleActionToken: r.LifecycleActionToken,
LifecycleActionResult: 'CONTINUE'
}
console.log("terminateInstance(", r, ")")
return asg.completeLifecycleAction(r).promise()
}
function heartbeat(r) {
r = {
AutoScalingGroupName: r.AutoScalingGroupName,
LifecycleHookName: r.LifecycleHookName,
LifecycleActionToken: r.LifecycleActionToken,
}
console.log("heartbeat(", r, ")")
return asg.recordLifecycleActionHeartbeat(r).promise()
}
exports.handler = async event => {
console.log(`Invoke: ${JSON.stringify(event)}`)
let rec = event.Records[0];
let body = JSON.parse(rec.body)// batch size is 1
if (body.Service === 'AWS Auto Scaling') {
if (body.Event === 'autoscaling:TEST_NOTIFICATION') {
console.log('Ignoring autoscaling:TEST_NOTIFICATION')
} else if (body.LifecycleTransition === 'autoscaling:EC2_INSTANCE_TERMINATING') {
body.ContainerInstanceArn = await getContainerInstanceArn(body.EC2InstanceId)
body.TerminateTime = body.Time
await Promise.all([drainInstance(body.ContainerInstanceArn), wait(body)])
}
} else if (body.Service === 'DrainInstance' && body.Event === 'custom:DRAIN_WAIT') {
if (0 === await countTasks(body.ContainerInstanceArn)) {
await terminateInstance(body)
} else {
let actionDuration = Math.abs(new Date(body.TerminateTime).getTime() - new Date().getTime()) / 1000
if (actionDuration < drainingTimeout) {
await Promise.all([heartbeat(body), wait(body)])
} else {
console.log('Timeout for instance termination reached.')
await terminateInstance(body)
}
}
} else {
console.log('Ignoring invalid event ...')
}
await deleteMessage(rec.receiptHandle)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment