Skip to content

Commit f6ab263

Browse files
committed
chore: add cleanup script on CI for atlas envs
1 parent 4cd078d commit f6ab263

File tree

8 files changed

+128
-0
lines changed

8 files changed

+128
-0
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
---
2+
name: "Cleanup stale Atlas test environments"
3+
on:
4+
workflow_dispatch:
5+
schedule:
6+
- cron: "0 0 * * *"
7+
8+
permissions: {}
9+
10+
jobs:
11+
cleanup-envs:
12+
runs-on: ubuntu-latest
13+
steps:
14+
- uses: GitHubSecurityLab/actions-permissions/monitor@v1
15+
if: matrix.os == 'ubuntu-latest'
16+
- uses: actions/checkout@v5
17+
- uses: actions/setup-node@v5
18+
with:
19+
node-version-file: package.json
20+
cache: "npm"
21+
- name: Install dependencies
22+
run: npm ci
23+
- name: Run cleanup script
24+
env:
25+
MDB_MCP_API_CLIENT_ID: ${{ secrets.TEST_ATLAS_CLIENT_ID }}
26+
MDB_MCP_API_CLIENT_SECRET: ${{ secrets.TEST_ATLAS_CLIENT_SECRET }}
27+
MDB_MCP_API_BASE_URL: ${{ vars.TEST_ATLAS_BASE_URL }}
28+
run: npm test -- scripts/cleanupAtlasTestLeftovers.test.ts
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import type { Group, AtlasOrganization } from "../src/common/atlas/openapi.js";
2+
import { ApiClient } from "../src/common/atlas/apiClient.js";
3+
import { ConsoleLogger } from "../src/common/logger.js";
4+
import { Keychain } from "../src/lib.js";
5+
import { describe, it } from "vitest";
6+
7+
function isOlderThanADay(date: string): boolean {
8+
const oneDayInMs = 24 * 60 * 60 * 1000;
9+
const projectDate = new Date(date);
10+
const currentDate = new Date();
11+
return currentDate.getTime() - projectDate.getTime() > oneDayInMs;
12+
}
13+
14+
async function findTestOrganization(client: ApiClient): Promise<AtlasOrganization> {
15+
const orgs = await client.listOrganizations();
16+
const testOrg = orgs?.results?.find((org) => org.name === "MongoDB MCP Test");
17+
18+
if (!testOrg) {
19+
throw new Error('Test organization "MongoDB MCP Test" not found.');
20+
}
21+
22+
return testOrg;
23+
}
24+
25+
async function findAllTestProjects(client: ApiClient, orgId: string): Promise<Group[]> {
26+
const projects = await client.listOrganizationProjects({
27+
params: {
28+
path: {
29+
orgId,
30+
},
31+
},
32+
});
33+
34+
const testProjects = projects?.results?.filter((proj) => proj.name.startsWith("testProj-")) || [];
35+
return testProjects.filter((proj) => isOlderThanADay(proj.created));
36+
}
37+
38+
async function deleteAllClustersOnStaleProject(client: ApiClient, projectId: string): Promise<void> {
39+
const allClusters = await client
40+
.listClusters({
41+
params: {
42+
path: {
43+
groupId: projectId || "",
44+
},
45+
},
46+
})
47+
.then((res) => res.results || []);
48+
49+
await Promise.allSettled(
50+
allClusters.map((cluster) =>
51+
client.deleteCluster({ params: { path: { groupId: projectId || "", clusterName: cluster.name || "" } } })
52+
)
53+
);
54+
}
55+
56+
async function main(): Promise<void> {
57+
const apiClient = new ApiClient(
58+
{
59+
baseUrl: process.env.TEST_ATLAS_BASE_URL || "https://cloud-dev.mongodb.com",
60+
credentials: {
61+
clientId: process.env.MDB_MCP_API_CLIENT_ID || "",
62+
clientSecret: process.env.MDB_MCP_API_CLIENT_SECRET || "",
63+
},
64+
},
65+
new ConsoleLogger(Keychain.root)
66+
);
67+
68+
const testOrg = await findTestOrganization(apiClient);
69+
const testProjects = await findAllTestProjects(apiClient, testOrg.id || "");
70+
71+
if (testProjects.length === 0) {
72+
console.log("No stale test projects found for cleanup.");
73+
}
74+
75+
for (const project of testProjects) {
76+
console.log(`Cleaning up project: ${project.name} (${project.id})`);
77+
if (!project.id) {
78+
console.warn(`Skipping project with missing ID: ${project.name}`);
79+
continue;
80+
}
81+
82+
await deleteAllClustersOnStaleProject(apiClient, project.id);
83+
await apiClient.deleteProject({
84+
params: {
85+
path: {
86+
groupId: project.id,
87+
},
88+
},
89+
});
90+
console.log(`Deleted project: ${project.name} (${project.id})`);
91+
}
92+
93+
return;
94+
}
95+
96+
describe("Cleanup Atlas Test Leftovers", () => {
97+
it("should clean up stale test projects", async () => {
98+
await main();
99+
});
100+
});

0 commit comments

Comments
 (0)