Last active
April 1, 2021 01:45
-
-
Save furlanf/9467d152b6bac0b7212f401292358334 to your computer and use it in GitHub Desktop.
Old script to clean unused lambda layers versions based on used lambda functions
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const AWS = require("aws-sdk"); | |
const retry = require("async-retry"); | |
const lambda = new AWS.Lambda(); | |
const bailIfErrorNotRetryable = (bail) => (error) => { | |
if (!error.retryable) { | |
bail(error); | |
} else { | |
throw error; | |
} | |
}; | |
const getRetryConfig = (onRetry) => ({ | |
retries: parseInt(process.env.RETRIES || "5"), | |
minTimeout: parseFloat(process.env.RETRY_MIN_TIMEOUT || "5000"), | |
maxTimeout: parseFloat(process.env.RETRY_MAX_TIMEOUT || "60000"), | |
factor: 2, | |
onRetry, | |
}); | |
/** | |
* https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Lambda.html#listFunctions-property | |
* @returns array of ARN lambda layers used by your active functions | |
*/ | |
const listFunctionsLayers = async () => { | |
console.info("listing all available functions..."); | |
const loop = async (acc = [], marker) => { | |
const params = { | |
Marker: marker, | |
MaxItems: 50, | |
}; | |
const res = await retry( | |
(bail) => | |
lambda | |
.listFunctions(params) | |
.promise() | |
.catch(bailIfErrorNotRetryable(bail)), | |
getRetryConfig((err) => { | |
console.warn("retrying listFunctions after error...", err); | |
}) | |
); | |
let layers = []; | |
res.Functions.map((f) => { | |
if (f.Layers) { | |
layers.push(f.Layers.map((l) => l.Arn)); | |
} | |
}); | |
const newAcc = acc.concat(...new Set(layers)); | |
if (res.NextMarker) { | |
return loop(newAcc, res.NextMarker); | |
} else { | |
console.info(`found ${newAcc.length} functions`, { | |
count: newAcc.length, | |
}); | |
return [...new Set(newAcc)]; | |
} | |
}; | |
return loop(); | |
}; | |
/** | |
* Lists AWS Lambda layers and shows information about the latest version of each. | |
* @returns array of registered layers | |
*/ | |
const listLayers = async () => { | |
console.debug("listing layers..."); | |
const loop = async (acc = [], marker) => { | |
const params = { | |
Marker: marker, | |
MaxItems: 20, | |
}; | |
const res = await retry( | |
(bail) => | |
lambda | |
.listLayers(params) | |
.promise() | |
.catch(bailIfErrorNotRetryable(bail)), | |
getRetryConfig((err) => { | |
console.warn( | |
"retrying listLayers after error...", | |
{ function: funcArn }, | |
err | |
); | |
}) | |
); | |
const layer = res.Layers.map((x) => x.LayerName); | |
const newAcc = acc.concat(layer); | |
if (res.NextMarker) { | |
return loop(newAcc, res.NextMarker); | |
} else { | |
return newAcc; | |
} | |
}; | |
return loop(); | |
}; | |
/** | |
* https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Lambda.html#listLayerVersions-property | |
* Lists the versions of an AWS Lambda layer filtered by the layer name | |
*/ | |
const listLayerVersion = async (layerName) => { | |
console.debug("listing versions...", { function: layerName }); | |
const loop = async (acc = [], marker) => { | |
const params = { | |
LayerName: layerName, | |
Marker: marker, | |
MaxItems: 50, | |
}; | |
const res = await retry( | |
(bail) => | |
lambda | |
.listLayerVersions(params) | |
.promise() | |
.catch(bailIfErrorNotRetryable(bail)), | |
getRetryConfig((err) => { | |
console.warn( | |
"retrying listLayerVersions after error...", | |
{ function: layerName }, | |
err | |
); | |
}) | |
); | |
const versions = res.LayerVersions.map((x) => x.Version); | |
const newAcc = acc.concat(...versions); | |
if (res.NextMarker) { | |
return loop(newAcc, res.NextMarker); | |
} else { | |
return newAcc; | |
} | |
}; | |
return loop(); | |
}; | |
/** | |
* https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Lambda.html#deleteLayerVersion-property | |
* Deletes a version of an AWS Lambda layer. | |
*/ | |
const deleteLayerVersion = async (LayerName, VersionNumber) => { | |
console.info("deleting...", { layer: LayerName, VersionNumber }); | |
var params = { | |
LayerName: LayerName, | |
VersionNumber: VersionNumber, | |
}; | |
await retry( | |
(bail) => | |
lambda | |
.deleteLayerVersion(params) | |
.promise() | |
.catch(bailIfErrorNotRetryable(bail)), | |
getRetryConfig((err) => { | |
console.warn( | |
"retrying deleteFunction after error...", | |
{ function: funcArn, version }, | |
err | |
); | |
}) | |
); | |
}; | |
const handler = async () => { | |
await clean(); | |
}; | |
const getUsedVersions = (layers, name) => { | |
const versionsToNotDelete = []; | |
for (const layerArn of layers) { | |
const splittedArn = layerArn.split(":"); | |
const layerName = splittedArn[6]; | |
const layerVersion = splittedArn[7]; | |
if (layerName === name) { | |
console.log("found >> ", { layerVersion }); | |
versionsToNotDelete.push(parseInt(layerVersion)); | |
} | |
} | |
console.log("version to not delete >> ", { versionsToNotDelete }); | |
return versionsToNotDelete; | |
}; | |
const clean = async () => { | |
const layersArn = await listFunctionsLayers(); // Get all used ARN (Amazon Resource Name) Layers | |
const layerNames = await listLayers(); // List all lambda layers | |
console.log(`${layersArn.length} used layers...`); | |
for (const name of layerNames) { | |
console.log("layerName >> ", { name }); | |
const layerVersions = await listLayerVersion(name); // Get all versions filtered by layers name | |
console.log(`layers to clean...`, { | |
versions: layerVersions, | |
count: layerVersions.length, | |
}); | |
const usedVersions = getUsedVersions(layersArn, name); // verify if version is being used | |
const versionsToDelete = layerVersions.filter( | |
(version) => !usedVersions.find((not) => not === version) | |
); | |
console.log("version to delete >> ", { | |
layerName: name, | |
versionsToDelete, | |
count: versionsToDelete.length, | |
}); | |
deleteLayer(name, versionsToDelete); | |
} | |
}; | |
const deleteLayer = async (layerName, versions) => { | |
for (const version of versions) { | |
await deleteLayerVersion(layerName, version); | |
} | |
}; | |
handler(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment