Skip to content

Instantly share code, notes, and snippets.

@arielshaqed
Created December 25, 2019 08:17
Show Gist options
  • Save arielshaqed/8e22d59bbb07feb30fe6cd11480477a1 to your computer and use it in GitHub Desktop.
Save arielshaqed/8e22d59bbb07feb30fe6cd11480477a1 to your computer and use it in GitHub Desktop.
Recursively clean up old projects (*including* their clusters) from MongoDB Atlas: anything starting `autoAtlas-`.
import * as baseRequest from 'request-promise-native';
import { StatusCodeError } from 'request-promise-native/errors';
import { concurrently, backoff } from '@binaris/nodeutils';
const orgId = process.env.ATLAS_ORG_ID
const publicKey = process.env.ATLAS_PUBLIC_KEY;
const privateKey = process.env.ATLAS_PRIVATE_KEY;
const concurrency = parseInt(process.env.ATLAS_CONCURRENCY || '30', 10);
const request = baseRequest.defaults({
auth: { user: publicKey, password: privateKey, sendImmediately: false },
json: true,
baseUrl: 'https://cloud.mongodb.com/api/atlas/v1.0/',
simple: true,
});
async function getProjectIds(): Promise<string[]> {
const res = await request.get(`orgs/${orgId}/groups`);
return res.results.map(({ id }: any) => id as string);
}
interface Project {
clusterCount: number;
created: string;
id: string;
name: string;
// Other fields exist notably links. See
// https://docs.atlas.mongodb.com/reference/api/project-get-one/
}
async function getProject(projectId: string): Promise<Project> {
const res = await request.get(`groups/${projectId}`);
return res;
}
async function getProjects() {
const projectIds = await getProjectIds();
return await concurrently(projectIds.map((projectId: string) => (() => getProject(projectId))), concurrency);
}
async function getClusterNamesInProject(projectId: string): Promise<string[]> {
const res = await request.get(`groups/${projectId}/clusters`);
return res.results.map(({ name }: any) => name);
}
async function clusterExists(projectId: string, clusterName: string): Promise<boolean> {
try {
await request.get(`groups/${projectId}/clusters/${clusterName}`);
return true;
} catch (e) {
if (!(e instanceof StatusCodeError)) throw e;
if (e.statusCode !== 404) throw e;
return false;
}
}
async function deleteCluster(projectId: string, clusterName: string) {
console.log('DELETE project', projectId, 'cluster', clusterName);
return request.delete(`groups/${projectId}/clusters/${clusterName}`);
}
async function deleteProject(projectId: string) {
return request.delete(`groups/${projectId}`);
}
const errors: any[] = [];
// When deleting *many* projects, pass concurrency === 1.
async function deleteProjectContents(projectId: string, concurrency = 1) {
async function wait(clusterName: string) {
console.log(`Waiting for ${clusterName}...`);
try {
await backoff.retry(
async () => {
if (await clusterExists(projectId, clusterName)) throw new Error('please continue');
return true;
},
{ initialMsec: 500, factor: 1.01, lowNoise: 0.7, highNoise: 0.3, maxMsec: 60000});
console.log(`${clusterName} deleted!`);
} catch (e) {
console.log(`giving up on ${clusterName}`);
}
}
const clusterNames = await getClusterNamesInProject(projectId);
await concurrently(
clusterNames.map((clusterName) => async () => {
try {
await deleteCluster(projectId, clusterName);
await wait(clusterName);
} catch (e) {
if (! (e instanceof StatusCodeError)) throw e;
if (e.error.errorCode !== 'CLUSTER_NOT_FOUND' &&
e.error.errorCode !== 'CLUSTER_ALREADY_REQUESTED_DELETION') {
throw e;
}
console.error(e.error.detail);
errors.push(e.error);
await wait(clusterName);
}
}),
concurrency);
deleteProject(projectId).catch((_) => undefined /* ignore */);
}
async function main() {
const projectIds = (await getProjects())
.filter(({ name }) => name.startsWith('autoAtlas-'))
.map(({ id }) => id);
console.log(`Deleting ${projectIds.length} projects...`);
await concurrently(
projectIds.map((projectId) => () => deleteProjectContents(projectId, 1)),
concurrency
);
}
main().then(() => {
if (errors.length) {
console.error(`${errors.length} failed`);
process.exit(1);
}
console.log('Done!');
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment