Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
"test": "jest --config jest.config.js"
},
"dependencies": {
"@apollo/client": "^3.7.1",
"@kubernetes/client-node": "^0.17.1",
"@prisma/client": "4.3.1",
"apollo-server": "^3.5.0",
"apollo-server-express": "3.10.2",
Expand All @@ -25,7 +27,6 @@
"zeromq": "^6.0.0-beta.6"
},
"devDependencies": {
"@apollo/client": "^3.6.9",
"@types/bcryptjs": "^2.4.2",
"@types/dockerode": "^3.3.11",
"@types/express": "^4.17.14",
Expand Down
22 changes: 19 additions & 3 deletions api/src/resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,17 @@ import {
repos,
updatePod,
} from "./resolver_repo";
import { killRuntime, listAllRuntimes, spawnRuntime } from "./resolver_runtime";
import { listAllRuntimes } from "./resolver_runtime";

// chooes between docker and k8s spawners
import {
spawnRuntime as spawnRuntime_docker,
killRuntime as killRuntime_docker,
} from "./spawner-docker";
import {
spawnRuntime as spawnRuntime_k8s,
killRuntime as killRuntime_k8s,
} from "./spawner-k8s";

export const resolvers = {
Query: {
Expand All @@ -35,7 +45,13 @@ export const resolvers = {
addPod,
updatePod,
deletePod,
spawnRuntime,
killRuntime,
spawnRuntime:
process.env.RUNTIME_SPAWNER === "k8s"
? spawnRuntime_k8s
: spawnRuntime_docker,
killRuntime:
process.env.RUNTIME_SPAWNER === "k8s"
? killRuntime_k8s
: killRuntime_docker,
},
};
6 changes: 0 additions & 6 deletions api/src/resolver_repo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,6 @@ export async function pod(_, { id }) {

export async function createRepo(_, { id, name }, { userId }) {
if (!userId) throw Error("Unauthenticated");
const user = await prisma.user.findFirst({
where: {
id: userId,
},
});
// create repo $name under userId
const repo = await prisma.repo.create({
data: {
id,
Expand Down
89 changes: 5 additions & 84 deletions api/src/resolver_runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,6 @@ const apollo_client = new ApolloClient({
uri: process.env.PROXY_API_URL,
});

const GET_URLS_QUERY = gql`
query getUrls {
getUrls
}
`;

export async function listAllRuntimes(_, {}, { userId }) {
// 1. get all containers, and filter by container name. This is the safest way to get all the running instances.
// If this is too expensive, I should maintain a DB, and periodically check for zombie containers.
Expand All @@ -33,7 +27,11 @@ export async function listAllRuntimes(_, {}, { userId }) {
// getUrls
// }
// `,
query: GET_URLS_QUERY,
query: gql`
query {
getUrls
}
`,
fetchPolicy: "network-only",
});
let res = urls.data.getUrls
Expand All @@ -47,80 +45,3 @@ export async function listAllRuntimes(_, {}, { userId }) {
.filter((x) => x);
return res;
}

const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

export async function spawnRuntime(_, { sessionId }) {
// launch the kernel
console.log("Spawning ");
let url = `/${sessionId}`;
console.log("spawning kernel");
let zmq_host = `cpkernel_${sessionId}`;
let need_wait = false;
let created = await loadOrCreateContainer(
process.env.ZMQ_KERNEL_IMAGE,
zmq_host,
// "cpkernel1_hello_world-foo",
"codepod"
);
console.log("spawning ws");
let ws_host = `cpruntime_${sessionId}`;
need_wait ||= created;
created = await loadOrCreateContainer(
process.env.WS_RUNTIME_IMAGE,
ws_host,
// "cpruntime1_hello_world-foo",
"codepod",
[`ZMQ_HOST=${zmq_host}`]
);
need_wait ||= created;
console.log("adding route", url, ws_host);
// add to routing table
await apollo_client.mutate({
mutation: gql`
mutation addRoute($url: String, $target: String) {
addRoute(url: $url, target: $target)
}
`,
variables: {
url,
// This 4020 is the WS listening port in WS_RUNTIME_IMAGE
target: `${ws_host}:4020`,
},
// refetchQueries: ["getUrls"],
refetchQueries: [{ query: GET_URLS_QUERY }],
});
if (need_wait) {
console.log("Waiting for 2 seconds for the container to startup.");
await delay(1000);
}
console.log("returning");
// console.log("res", res);
return true;
}

export async function killRuntime(_, { sessionId }) {
// TODO kill the runtime server.
// FIXME handle exception, and kill zombie containers
let url = `/${sessionId!}`;
let zmq_host = `cpkernel_${sessionId}`;
await removeContainer(zmq_host);
let ws_host = `cpruntime_${sessionId}`;
await removeContainer(ws_host);
// remote route
console.log("Removing route ..");
await apollo_client.mutate({
mutation: gql`
mutation deleteRoute($url: String) {
deleteRoute(url: $url)
}
`,
variables: {
url,
},
// FIMXE why name doesn't work? Actually the refetchQueries doesn't work
// refetchQueries: ["getUrls"],
// refetchQueries: [{ query: GET_URLS_QUERY }],
});
return true;
}
93 changes: 93 additions & 0 deletions api/src/spawner-docker.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
import Docker from "dockerode";

import { ApolloClient, InMemoryCache, gql } from "@apollo/client/core";

const apollo_client = new ApolloClient({
cache: new InMemoryCache({}),
uri: process.env.PROXY_API_URL,
});

const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

export async function removeContainer(name) {
return new Promise((resolve, reject) => {
var docker = new Docker();
Expand Down Expand Up @@ -167,3 +176,87 @@ async function createContainer(image, name, network, Env) {
);
});
}

export async function spawnRuntime(_, { sessionId }) {
// launch the kernel
console.log("Spawning ");
let url = `/${sessionId}`;
console.log("spawning kernel");
let zmq_host = `cpkernel_${sessionId}`;
let need_wait = false;
let created = await loadOrCreateContainer(
process.env.ZMQ_KERNEL_IMAGE,
zmq_host,
// "cpkernel1_hello_world-foo",
"codepod"
);
console.log("spawning ws");
let ws_host = `cpruntime_${sessionId}`;
need_wait ||= created;
created = await loadOrCreateContainer(
process.env.WS_RUNTIME_IMAGE,
ws_host,
// "cpruntime1_hello_world-foo",
"codepod",
[`ZMQ_HOST=${zmq_host}`]
);
need_wait ||= created;
console.log("adding route", url, ws_host);
// add to routing table
await apollo_client.mutate({
mutation: gql`
mutation addRoute($url: String, $target: String) {
addRoute(url: $url, target: $target)
}
`,
variables: {
url,
// This 4020 is the WS listening port in WS_RUNTIME_IMAGE
target: `${ws_host}:4020`,
},
// refetchQueries: ["getUrls"],
refetchQueries: [
{
query: gql`
query getUrls {
getUrls
}
`,
},
],
});

if (need_wait) {
console.log("Waiting for 2 seconds for the container to startup.");
await delay(1000);
}
console.log("returning");
// console.log("res", res);
return true;
}

export async function killRuntime(_, { sessionId }) {
// TODO kill the runtime server.
// FIXME handle exception, and kill zombie containers
let url = `/${sessionId!}`;
let zmq_host = `cpkernel_${sessionId}`;
await removeContainer(zmq_host);
let ws_host = `cpruntime_${sessionId}`;
await removeContainer(ws_host);
// remote route
console.log("Removing route ..");
await apollo_client.mutate({
mutation: gql`
mutation deleteRoute($url: String) {
deleteRoute(url: $url)
}
`,
variables: {
url,
},
// FIMXE why name doesn't work? Actually the refetchQueries doesn't work
// refetchQueries: ["getUrls"],
// refetchQueries: [{ query: GET_URLS_QUERY }],
});
return true;
}
Loading