Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@architect/destroy

Package Overview
Dependencies
Maintainers
6
Versions
59
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@architect/destroy - npm Package Compare versions

Comparing version 3.0.13 to 4.0.0-RC.0

21

package.json
{
"name": "@architect/destroy",
"version": "3.0.13",
"version": "4.0.0-RC.0",
"description": "Destroy projects created with Architect",

@@ -26,5 +26,9 @@ "main": "src/index.js",

"dependencies": {
"@architect/inventory": "~3.6.0",
"@architect/inventory": "~3.6.6-RC.0",
"@architect/utils": "~3.1.9",
"aws-sdk": "^2.1363.0",
"@aws-lite/client": "~0.13.4",
"@aws-lite/cloudformation": "~0.0.1",
"@aws-lite/cloudwatch-logs": "~0.0.2",
"@aws-lite/s3": "~0.1.8",
"@aws-lite/ssm": "~0.2.2",
"minimist": "~1.2.8",

@@ -35,10 +39,9 @@ "run-parallel": "~1.2.0",

"devDependencies": {
"@architect/deploy": "~4.5.1",
"@architect/eslint-config": "^2.1.1",
"aws-sdk-mock": "~5.8.0",
"@architect/deploy": "~4.6.3",
"@architect/eslint-config": "^2.1.2",
"cross-env": "~7.0.3",
"eslint": "^8.47.0",
"eslint": "~8.56.0",
"nyc": "~15.1.0",
"tap-arc": "~0.3.5",
"tape": "~5.6.6"
"tap-arc": "~1.2.2",
"tape": "~5.7.2"
},

@@ -45,0 +48,0 @@ "eslintConfig": {

@@ -1,54 +0,29 @@

let aws = require('aws-sdk')
let waterfall = require('run-waterfall')
module.exports = function deleteBucketContents ({ bucket }, callback) {
let region = process.env.AWS_REGION
let s3 = new aws.S3({ region })
let objects = []
module.exports = function deleteBucketContents ({ aws, bucket: Bucket }, callback) {
let bucketExists = false
function ensureBucket (callback) {
s3.headBucket({ Bucket: bucket }, function done (err) {
if (err) bucketExists = false
else bucketExists = true
callback(null)
})
}
function collectObjects (ContinuationToken, callback) {
s3.listObjectsV2({
Bucket: bucket,
ContinuationToken
}, function done (err, result) {
if (err) {
callback(err)
}
else {
objects = objects.concat(result.Contents)
if (result.IsTruncated) {
collectObjects(result.NextContinuationToken, callback)
}
else {
callback(null, objects.map(item => ({ Key: item.Key })))
}
}
})
function collectObjects (callback) {
aws.s3.ListObjectsV2({ Bucket, paginate: true })
.then(result => {
let { Contents } = result
let objectsToDelete = Contents.map(({ Key }) => ({ Key })).filter(Boolean)
callback(null, objectsToDelete)
})
.catch(err => callback(err))
}
function deleteObjects (objs, callback) {
let batch = objs.splice(0, 1000) // S3.deleteObjects supports up to 1k keys
s3.deleteObjects({
Bucket: bucket,
Delete: {
Objects: batch
}
},
function done (err) {
if (err) callback(err)
else if (objs.length) {
deleteObjects(objs, callback)
}
else callback()
function deleteObjects (objectsToDelete, callback) {
let Objects = objectsToDelete.splice(0, 1000) // S3.deleteObjects supports up to 1k keys
aws.s3.DeleteObjects({
Bucket,
Delete: { Objects },
})
.then(() => {
if (objectsToDelete.length) {
deleteObjects(objectsToDelete, callback)
}
else callback()
})
.catch(err => callback(err))
}

@@ -58,17 +33,22 @@

function checkBucketExists (callback) {
ensureBucket(callback)
aws.s3.HeadBucket({ Bucket })
.then(() => {
bucketExists = true
callback()
})
.catch(() => callback())
},
function maybeCollectObjectsInBucket (callback) {
if (bucketExists) collectObjects(null, callback)
else callback(null, [])
if (bucketExists) {
collectObjects(callback)
}
else callback(null, false)
},
function maybeDeleteBucketObjects (stuffToDelete, callback) {
if (bucketExists && Array.isArray(stuffToDelete) && stuffToDelete.length > 0) {
deleteObjects(stuffToDelete, callback)
function maybeDeleteBucketObjects (objectsToDelete, callback) {
if (bucketExists && objectsToDelete.length) {
deleteObjects(objectsToDelete, callback)
}
else {
callback()
}
else callback()
},

@@ -78,6 +58,5 @@

if (bucketExists) {
s3.deleteBucket({ Bucket: bucket }, function (err) {
if (err) callback(err)
else callback()
})
aws.s3.DeleteBucket({ Bucket })
.then(() => callback())
.catch(err => callback(err))
}

@@ -84,0 +63,0 @@ else callback()

@@ -1,41 +0,33 @@

let aws = require('aws-sdk')
module.exports = function deleteLogs ({ aws, StackName, update }, callback) {
aws.cloudwatchlogs.DescribeLogGroups({
logGroupNamePrefix: `/aws/lambda/${StackName}-`,
paginate: true,
})
.then(data => {
if (data?.logGroups?.length) {
let logGroups = data.logGroups.map(({ logGroupName }) => logGroupName)
deleter(aws, logGroups, update, callback)
}
else callback()
})
.catch(err => callback(err))
}
module.exports = function deleteLogs ({ StackName, update }, callback) {
let cloudwatch = new aws.CloudWatchLogs()
let logGroups = []
function getLogs (nextToken, cb) {
let params = {
logGroupNamePrefix: `/aws/lambda/${StackName}-`
}
if (nextToken) params.nextToken = nextToken
cloudwatch.describeLogGroups(params, function (err, data) {
if (err) cb(err)
else {
data.logGroups.forEach(l => {
logGroups.push(l.logGroupName)
function deleter (aws, logGroups, update, callback) {
let timer = 0
let numComplete = 0
logGroups.forEach(log => {
timer += 400 // max of about 2-3 transactions per second :/
setTimeout(function delayedDelete () {
aws.cloudwatchlogs.DeleteLogGroup({ logGroupName: log })
.then(() => {
numComplete++
if (logGroups.length === numComplete) callback()
})
if (data.nextToken) getLogs(data.nextToken, cb)
else cb()
}
})
}
getLogs(null, function (err) {
if (err) callback(err)
else if (logGroups.length) {
let timer = 0
let numComplete = 0
logGroups.forEach(log => {
timer += 400 // max of about 2-3 transactions per second :/
let params = { logGroupName: log }
setTimeout(function delayedDelete () {
cloudwatch.deleteLogGroup(params, function (err /* , data */) {
if (err) update.warn(err)
numComplete++
if (logGroups.length === numComplete) callback()
})
}, timer)
})
}
else callback()
.catch(err => {
numComplete++
update.warn(err)
})
}, timer)
})
}

@@ -1,2 +0,1 @@

let aws = require('aws-sdk')
let parallel = require('run-parallel')

@@ -8,40 +7,34 @@

module.exports = {
getDeployBucket: function getDeployBucket (appname, callback) {
let region = process.env.AWS_REGION
let ssm = new aws.SSM({ region })
ssm.getParameter({
getDeployBucket: function getDeployBucket (aws, appname, callback) {
aws.ssm.GetParameter({
Name: `/${appname}/deploy/bucket`,
WithDecryption: true
}, function (err, data) {
if (err && err.code !== 'ParameterNotFound') callback(err)
else callback(null, (data && data.Parameter && data.Parameter.Value ? data.Parameter.Value : null))
})
.then(data => {
let value = data?.Parameter?.Value ? data.Parameter.Value : null
callback(null, value)
})
.catch(err => {
if (err && err.code !== 'ParameterNotFound') callback(err)
else callback()
})
},
deleteAll: function deleteAll (appname, env, callback) {
let region = process.env.AWS_REGION
let ssm = new aws.SSM({ region })
// set up for recursive retrieval of all parameters associated to the app
// since SSM only support max 10 param retrieval at a time
let results = {}
function collectByPath (rootPath, NextToken, cb) {
if (!results[rootPath]) results[rootPath] = []
let query = {
deleteAll: function deleteAll (aws, appname, env, callback) {
let Names = []
function collectByPath (rootPath, cb) {
aws.ssm.GetParametersByPath({
Path: rootPath,
Recursive: true,
MaxResults: 10
}
if (NextToken) query.NextToken = NextToken
ssm.getParametersByPath(query, function (err, data) {
// if the parameters are gone, that's fine too
if (err && err.code !== 'ParameterNotFound') cb(err)
else {
if (data && data.Parameters && data.Parameters.length) {
results[rootPath] = results[rootPath].concat(data.Parameters.map(param => param.Name))
if (data.NextToken) collectByPath(rootPath, data.NextToken, cb)
else cb(null, results[rootPath])
paginate: true
})
.then(data => {
if (data?.Parameters?.length) {
Names.push(...data.Parameters.map(({ Name }) => Name))
}
else cb(null, results[rootPath])
}
})
cb()
})
.catch(err => {
if (err && err.code !== 'ParameterNotFound') cb(err)
else cb()
})
}

@@ -52,8 +45,8 @@

// /<app-name>/<env>/* - environment variables via `arc env`
let paths = [ `/${appname}/${env}`, `/${appname}/deploy` ]
parallel(paths.map(path => collectByPath.bind(null, path, null)), function paramsCollected (err, res) {
let ops = [ `/${appname}/${env}`, `/${appname}/deploy` ]
.map(path => collectByPath.bind(null, path))
parallel(ops, (err) => {
if (err) callback(err)
else {
// combine all the various parameters by different path names into a single array
let Names = res.reduce((aggregate, current) => aggregate.concat(current), [])
function deleteThings () {

@@ -63,7 +56,8 @@ if (Names.length) {

let chunk = Names.splice(0, 10)
ssm.deleteParameters({ Names: chunk }, function deleteParameters (err) {
if (err) callback(err)
else if (!Names.length) callback()
else deleteThings()
})
aws.ssm.DeleteParameters({ Names: chunk })
.then(() => {
if (!Names.length) callback()
else deleteThings()
})
.catch(err => callback(err))
}

@@ -70,0 +64,0 @@ else callback()

@@ -1,5 +0,3 @@

// eslint-disable-next-line
try { require('aws-sdk/lib/maintenance_mode_message').suppress = true }
catch { /* Noop */ }
let aws = require('aws-sdk')
let _inventory = require('@architect/inventory')
let awsLite = require('@aws-lite/client')
let waterfall = require('run-waterfall')

@@ -12,3 +10,4 @@ let deleteBucket = require('./_delete-bucket')

function stackNotFound (StackName, err) {
if (err && err.code == 'ValidationError' && err.message == `Stack with id ${StackName} does not exist`) {
if (err && err.code == 'ValidationError' &&
err.message.includes(`Stack with id ${StackName} does not exist`)) {
return true

@@ -18,2 +17,3 @@ }

}
/**

@@ -27,3 +27,3 @@ * @param {object} params - named parameters

module.exports = function destroy (params, callback) {
let { appname, env, stackname, force = false, now, retries, update } = params
let { appname, env, force = false, inventory, now, retries, stackname, update } = params
if (!update) update = updater('Destroy')

@@ -45,3 +45,2 @@

// hack around no native promise in aws-sdk
let promise

@@ -57,6 +56,3 @@ if (!callback) {

let stackExists
// actual code
let region = process.env.AWS_REGION
let cloudformation = new aws.CloudFormation({ region })
let aws, stack

@@ -78,42 +74,60 @@ waterfall([

// Set up inventory to get region
function (callback) {
if (!inventory) {
_inventory({}, (err, result) => {
if (err) callback(err)
else {
inventory = result
callback()
}
})
}
else callback()
},
// Instantiate client
function (callback) {
awsLite({ region: inventory.inv.aws.region, debug: true })
.then(_aws => {
aws = _aws
callback()
})
.catch(err => callback(err))
},
// check for the stack
function (callback) {
update.status(`Destroying ${StackName}`)
cloudformation.describeStacks({
StackName
},
function (err, data) {
if (stackNotFound(StackName, err)) {
stackExists = false
callback(null, false)
}
else if (err) callback(err)
else callback(null, data.Stacks[0])
})
aws.CloudFormation.DescribeStacks({ StackName })
.then(data => {
stack = data.Stacks[0]
callback()
})
.catch(err => {
if (stackNotFound(StackName, err)) {
callback()
}
else callback(err)
})
},
// check for dynamodb tables and if force flag not provided, error out
function (stack, callback) {
function (callback) {
if (stack) {
stackExists = true
cloudformation.describeStackResources({
StackName
},
function (err, data) {
if (err) callback(err)
else {
aws.CloudFormation.DescribeStackResources({ StackName })
.then(data => {
let type = t => t.ResourceType
let table = i => i === 'AWS::DynamoDB::Table'
let hasTables = data.StackResources.map(type).some(table)
if (hasTables && !force) callback(Error('table_exists'))
else callback(null, stack)
}
})
else callback()
})
.catch(err => callback(err))
}
else callback(null, stack)
else callback()
},
// check if static bucket exists in stack
function (stack, callback) {
function (callback) {
if (stack) {

@@ -132,5 +146,3 @@ let bucket = o => o.OutputKey === 'BucketURL'

update.status('Deleting static S3 bucket...')
deleteBucket({
bucket
}, callback)
deleteBucket({ aws, bucket }, callback)
}

@@ -149,3 +161,3 @@ else if (bucketExists && !force) {

update.status('Retrieving deployment bucket...')
ssm.getDeployBucket(appname, callback)
ssm.getDeployBucket(aws, appname, callback)
},

@@ -157,3 +169,3 @@

update.status('Deleting deployment S3 bucket...')
deleteBucket({ bucket: deploymentBucket }, callback)
deleteBucket({ aws, bucket: deploymentBucket }, callback)
}

@@ -171,3 +183,3 @@ else callback()

update.status('Deleting SSM parameters...')
ssm.deleteAll(appname, env, callback)
ssm.deleteAll(aws, appname, env, callback)
}

@@ -179,3 +191,3 @@ },

update.status('Deleting CloudWatch log groups...')
deleteLogs({ StackName, update }, callback)
deleteLogs({ aws, StackName, update }, callback)
},

@@ -185,11 +197,7 @@

function (callback) {
if (stackExists) {
if (stack) {
update.start(`Destroying CloudFormation Stack ${StackName}...`)
cloudformation.deleteStack({
StackName,
},
function (err) {
if (err) callback(err)
else callback(null, true)
})
aws.cloudformation.DeleteStack({ StackName })
.then(() => callback(null, true))
.catch(err => callback(err))
}

@@ -204,29 +212,30 @@ else callback(null, false)

let max = retries // typical values are 15 or 999; see cli.js
function checkit () {
cloudformation.describeStacks({
StackName
},
function done (err, result) {
if (stackNotFound(StackName, err)) {
update.done(`Successfully destroyed ${StackName}`)
return callback()
}
if (!err && result.Stacks) {
let stack = result.Stacks.find(s => s.StackName === StackName)
if (stack && stack.StackStatus === 'DELETE_FAILED') {
return callback(Error(`CloudFormation Stack "${StackName}" destroy failed: ${stack.StackStatusReason}`))
function check () {
aws.cloudformation.DescribeStacks({ StackName })
.then(result => {
if (result.Stacks) {
let stack = result.Stacks.find(s => s.StackName === StackName)
if (stack && stack.StackStatus === 'DELETE_FAILED') {
return callback(Error(`CloudFormation Stack "${StackName}" destroy failed: ${stack.StackStatusReason}`))
}
}
}
setTimeout(function delay () {
if (tries === max) {
callback(Error(`CloudFormation Stack destroy still ongoing; aborting as we hit max number of retries (${max})`))
setTimeout(function delay () {
if (tries === max) {
callback(Error(`CloudFormation Stack destroy still ongoing; aborting as we hit max number of retries (${max})`))
}
else {
tries += 1
check()
}
}, 10000)
})
.catch(err => {
if (stackNotFound(StackName, err)) {
update.done(`Successfully destroyed ${StackName}`)
callback()
}
else {
tries += 1
checkit()
}
}, 10000)
})
else callback(err)
})
}
checkit()
check()
}

@@ -233,0 +242,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc