211 lines
6.1 KiB
TypeScript
Raw Normal View History

2024-05-05 21:45:43 -07:00
import express, { Express, Request, Response } from "express"
import dotenv from "dotenv"
2024-05-05 21:45:43 -07:00
import fs from "fs"
import yaml from "yaml"
import path from "path"
import cors from "cors"
import {
KubeConfig,
AppsV1Api,
CoreV1Api,
NetworkingV1Api,
} from "@kubernetes/client-node"
import { z } from "zod"
const app = express()
const port = process.env.PORT || 4001
app.use(express.json())
dotenv.config()
2024-05-05 21:45:43 -07:00
2024-05-11 21:13:43 -07:00
const corsOptions = {
origin: ['http://localhost:3000', 'https://s.ishaand.com', 'http://localhost:4000', /\.ws\.ishaand\.com$/],
2024-05-11 21:13:43 -07:00
}
// app.use(cors(corsOptions))
app.use(cors())
2024-05-05 21:45:43 -07:00
const kubeconfig = new KubeConfig()
2024-05-11 21:13:43 -07:00
if (process.env.NODE_ENV === "production") {
kubeconfig.loadFromOptions({
clusters: [
{
2024-05-13 03:00:15 -07:00
name: 'gke_sylvan-epoch-422219-f9_us-central1_sandbox-cluster',
2024-05-11 21:13:43 -07:00
server: process.env.GKE_CLUSTER_SERVER!,
caData: process.env.GKE_CLUSTER_CA_DATA,
}
],
users: [
{
2024-05-13 03:00:15 -07:00
name: 'gke_sylvan-epoch-422219-f9_us-central1_sandbox-cluster',
2024-05-11 21:13:43 -07:00
exec: {
apiVersion: 'client.authentication.k8s.io/v1beta1',
command: 'gke-gcloud-auth-plugin',
args: [],
env: null,
installHint: 'Install gke-gcloud-auth-plugin for use with kubectl by following https://cloud.google.com/kubernetes-engine/docs/how-to/cluster-access-for-kubectl#install_plugin',
interactiveMode: 'IfAvailable',
provideClusterInfo: true
}
}
],
contexts: [
{
name: 'docker-desktop',
cluster: 'docker-desktop',
user: 'docker-desktop'
},
{
name: 'gke_sylvan-epoch-422219-f9_us-central1_sandbox',
cluster: 'gke_sylvan-epoch-422219-f9_us-central1_sandbox',
user: 'gke_sylvan-epoch-422219-f9_us-central1_sandbox'
}
],
currentContext: "gke_sylvan-epoch-422219-f9_us-central1_sandbox",
});
}
2024-05-05 21:45:43 -07:00
kubeconfig.loadFromDefault()
2024-05-11 21:13:43 -07:00
2024-05-05 21:45:43 -07:00
const appsV1Api = kubeconfig.makeApiClient(AppsV1Api)
2024-05-13 03:00:15 -07:00
const coreV1Api = kubeconfig.makeApiClient(CoreV1Api)
2024-05-05 21:45:43 -07:00
const networkingV1Api = kubeconfig.makeApiClient(NetworkingV1Api)
const readAndParseKubeYaml = (
filePath: string,
sandboxId: string
): Array<any> => {
2024-05-05 21:45:43 -07:00
const fileContent = fs.readFileSync(filePath, "utf8")
const docs = yaml.parseAllDocuments(fileContent).map((doc) => {
let docString = doc.toString()
const regex = new RegExp(`<SANDBOX>`, "g")
docString = docString.replace(regex, sandboxId)
if (!process.env.CF_API_TOKEN) {
throw new Error("CF_API_TOKEN is not defined")
}
const regexEnv1 = new RegExp(`<CF_API_TOKEN>`, "g")
docString = docString.replace(regexEnv1, process.env.CF_API_TOKEN)
if (!process.env.CF_USER_ID) {
throw new Error("CF_USER_ID is not defined")
}
const regexEnv2 = new RegExp(`<CF_USER_ID>`, "g")
docString = docString.replace(regexEnv2, process.env.CF_USER_ID)
2024-05-05 21:45:43 -07:00
return yaml.parse(docString)
})
return docs
}
const dataSchema = z.object({
userId: z.string(),
sandboxId: z.string(),
})
const namespace = "ingress-nginx"
2024-05-11 21:13:43 -07:00
app.post("/test", async (req, res) => {
2024-05-11 21:13:43 -07:00
res.status(200).send({ message: "Orchestrator is up and running." })
})
app.post("/start", async (req, res) => {
2024-05-11 21:13:43 -07:00
const { sandboxId } = dataSchema.parse(req.body)
2024-05-05 21:45:43 -07:00
try {
console.log("Creating resources for sandbox", sandboxId)
2024-05-05 21:45:43 -07:00
const kubeManifests = readAndParseKubeYaml(
path.join(__dirname, "../service.yaml"),
sandboxId
)
2024-05-11 21:13:43 -07:00
async function resourceExists(api: any, getMethod: string, name: string) {
try {
2024-05-13 03:00:15 -07:00
await api[getMethod](name, namespace)
2024-05-11 21:13:43 -07:00
return true
} catch (e: any) {
2024-05-13 03:00:15 -07:00
if (e.response && e.response.statusCode === 404) {
console.log("Resource does not exist.", e.response.body.message, e.response.body.details)
return false
}
2024-05-11 21:13:43 -07:00
throw e
}
}
const promises = kubeManifests.map(async (manifest) => {
2024-05-11 21:13:43 -07:00
const { kind, metadata: { name } } = manifest
if (kind === "Deployment")
if (!(await resourceExists(appsV1Api, 'readNamespacedDeployment', name))) {
await appsV1Api.createNamespacedDeployment(namespace, manifest)
console.log("Made deploymnet")
2024-05-11 21:13:43 -07:00
}
else if (kind === "Service")
if (!(await resourceExists(coreV1Api, 'readNamespacedService', name))) {
await coreV1Api.createNamespacedService(namespace, manifest)
console.log("Made service")
2024-05-11 21:13:43 -07:00
}
else if (kind === "Ingress")
if (!(await resourceExists(networkingV1Api, 'readNamespacedIngress', name))) {
await networkingV1Api.createNamespacedIngress(namespace, manifest)
console.log("Made ingress")
2024-05-11 21:13:43 -07:00
}
2024-05-05 21:45:43 -07:00
})
await Promise.all(promises)
console.log("All done!")
2024-05-05 21:45:43 -07:00
res.status(200).send({ message: "Resources created." })
} catch (error: any) {
const body = error.response.body
console.log("Failed to create resources", body)
if (body.code === 409) {
return res.status(200).send({ message: "Resource already exists." })
}
2024-05-05 21:45:43 -07:00
res.status(500).send({ message: "Failed to create resources." })
}
})
app.post("/stop", async (req, res) => {
2024-05-11 21:13:43 -07:00
const { sandboxId } = dataSchema.parse(req.body)
console.log("Deleting resources for sandbox", sandboxId)
try {
const kubeManifests = readAndParseKubeYaml(
path.join(__dirname, "../service.yaml"),
sandboxId
)
const promises = kubeManifests.map(async (manifest) => {
if (manifest.kind === "Deployment")
await appsV1Api.deleteNamespacedDeployment(
manifest.metadata?.name || "",
namespace
)
else if (manifest.kind === "Service")
await coreV1Api.deleteNamespacedService(
manifest.metadata?.name || "",
namespace
)
else if (manifest.kind === "Ingress")
await networkingV1Api.deleteNamespacedIngress(
manifest.metadata?.name || "",
namespace
)
})
await Promise.all(promises)
res.status(200).send({ message: "Resources deleted." })
} catch (error) {
console.log("Failed to delete resources", error)
res.status(500).send({ message: "Failed to delete resources." })
}
})
2024-05-05 21:45:43 -07:00
app.listen(port, () => {
console.log(`Listening on port: ${port}`)
})