Skip to content

Commit

Permalink
Add support for versioned buckets
Browse files Browse the repository at this point in the history
  • Loading branch information
FilipPyrek committed Mar 17, 2020
1 parent 8b1d38a commit bf52ade
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 67 deletions.
169 changes: 104 additions & 65 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,32 @@
'use strict';
"use strict";

const chalk = require('chalk');
const prompt = require('prompt');
const messagePrefix = 'S3 Remover: ';
const chalk = require("chalk");
const prompt = require("prompt");
const messagePrefix = "S3 Remover: ";

class Remover {
constructor(serverless, options) {
this.serverless = serverless;
this.options = options;
this.provider = this.serverless.getProvider('aws');
this.provider = this.serverless.getProvider("aws");

this.commands = {
s3remove: {
usage: 'Remove all files in S3 buckets',
lifecycleEvents: [
'remove'
],
usage: "Remove all files in S3 buckets",
lifecycleEvents: ["remove"],
options: {
verbose: {
usage: 'Increase verbosity',
shortcut: 'v'
usage: "Increase verbosity",
shortcut: "v"
}
}
}
};

this.hooks = {
'before:remove:remove': () => Promise.resolve().then(this.remove.bind(this)),
's3remove:remove': () => Promise.resolve().then(this.remove.bind(this))
"before:remove:remove": () =>
Promise.resolve().then(this.remove.bind(this)),
"s3remove:remove": () => Promise.resolve().then(this.remove.bind(this))
};
}

Expand All @@ -40,118 +39,158 @@ class Remover {
remove() {
const self = this;

const getAllKeys = (bucket) => {
const getAllKeys = bucket => {
const get = (src = {}) => {
const data = src.data;
const keys = src.keys || [];
let keys = src.keys || [];
const param = {
Bucket: bucket
};
if (data) {
param.ContinuationToken = data.NextContinuationToken;
param.VersionIdMarker = data.NextVersionIdMarker;
param.KeyMarker = data.NextKeyMarker;
}
return self.provider.request('S3', 'listObjectsV2', param).then((result) => {
return new Promise((resolve) => {
resolve({
data: result, keys: keys.concat(result.Contents.map((item) => {
return item.Key;
}))
return self.provider
.request("S3", "listObjectVersions", param)
.then(result => {
return new Promise(resolve => {
if (result.Versions && result.Versions.length) {
keys = keys.concat(
result.Versions.map(item => {
return { VersionId: item.VersionId, Key: item.Key };
})
);
}
if (result.DeleteMarkers && result.DeleteMarkers.length) {
keys = keys.concat(
result.DeleteMarkers.map(item => {
return { VersionId: item.VersionId, Key: item.Key };
})
);
}
resolve({
data: result,
keys
});
});
});
});
};
const list = (src = {}) => {
return get(src).then((result) => {
return get(src).then(result => {
if (result.data.IsTruncated) {
return list(result);
} else {
const keys = result.keys;
const batched = [];
for (let i = 0; i < keys.length; i += 1000) {
const objects = keys.slice(i, i + 1000).map((item) => {
return {Key: item};
});
const objects = keys.slice(i, i + 1000);
batched.push({
Bucket: bucket,
Delete: {
Objects: objects
}
});
}
return new Promise((resolve) => {
return new Promise(resolve => {
resolve(batched);
});
}
});
};
return list();
};
const executeRemove = (params) => {
return Promise.all(params.map(param => {
return self.provider.request('S3', 'deleteObjects', param);
}));
const executeRemove = params => {
return Promise.all(
params.map(param => {
return self.provider.request("S3", "deleteObjects", param);
})
);
};

const populateConfig = () => {
return this.serverless.variables.populateObject(this.serverless.service.custom.remover)
return this.serverless.variables
.populateObject(this.serverless.service.custom.remover)
.then(fileConfig => {
const defaultConfig = {
prompt: false,
buckets: [],
buckets: []
};
return Object.assign({}, defaultConfig, fileConfig);
});
};

return new Promise((resolve) => {
return new Promise(resolve => {
return populateConfig().then(config => {
if (!config.prompt) {
let promisses = [];
const promisses = [];
for (const b of config.buckets) {
promisses.push(getAllKeys(b).then(executeRemove).then(() => {
const message = `Success: ${b} is empty.`;
self.log(message);
self.serverless.cli.consoleLog(`${messagePrefix}${chalk.yellow(message)}`);
}).catch((err) => {
const message = `Faild: ${b} may not be empty.`;
self.log(message);
self.log(err);
self.serverless.cli.consoleLog(`${messagePrefix}${chalk.yellow(message)}`);
}));
promisses.push(
getAllKeys(b)
.then(executeRemove)
.then(() => {
const message = `Success: ${b} is empty.`;
self.log(message);
self.serverless.cli.consoleLog(
`${messagePrefix}${chalk.yellow(message)}`
);
})
.catch(err => {
console.log(err);
const message = `Faild: ${b} may not be empty.`;
self.log(message);
self.log(err);
self.serverless.cli.consoleLog(
`${messagePrefix}${chalk.yellow(message)}`
);
})
);
}
return Promise.all(promisses).then(resolve);
}
prompt.message = messagePrefix;
prompt.delimiter = '';
prompt.delimiter = "";
prompt.start();
const schema = {properties: {}};
config.buckets.forEach((b) => {
const schema = { properties: {} };
config.buckets.forEach(b => {
schema.properties[b] = {
message: `Make ${b} empty. Are you sure? [yes/no]:`,
validator: /(yes|no)/,
required: true,
warning: 'Must respond yes or no'
warning: "Must respond yes or no"
};
});
prompt.get(schema, (err, result) => {
let promisses = [];
const promisses = [];
for (const b of config.buckets) {
if (result[b].match(/^y/)) {
promisses.push(getAllKeys(b).then(executeRemove).then(() => {
const message = `Success: ${b} is empty.`;
self.log(message);
self.serverless.cli.consoleLog(`${messagePrefix}${chalk.yellow(message)}`);
}).catch(() => {
const message = `Faild: ${b} may not be empty.`;
self.log(message);
self.serverless.cli.consoleLog(`${messagePrefix}${chalk.yellow(message)}`);
}));
promisses.push(
getAllKeys(b)
.then(executeRemove)
.then(() => {
const message = `Success: ${b} is empty.`;
self.log(message);
self.serverless.cli.consoleLog(
`${messagePrefix}${chalk.yellow(message)}`
);
})
.catch(() => {
const message = `Faild: ${b} may not be empty.`;
self.log(message);
self.serverless.cli.consoleLog(
`${messagePrefix}${chalk.yellow(message)}`
);
})
);
} else {
promisses.push(Promise.resolve().then(() => {
const message = `Remove cancelled: ${b}`;
self.log(message);
self.serverless.cli.consoleLog(`${messagePrefix}${chalk.yellow(message)}`);
}));
promisses.push(
Promise.resolve().then(() => {
const message = `Remove cancelled: ${b}`;
self.log(message);
self.serverless.cli.consoleLog(
`${messagePrefix}${chalk.yellow(message)}`
);
})
);
}
}
Promise.all(promisses).then(resolve);
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit bf52ade

Please sign in to comment.