Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add compute server functionality to course management #8090

Merged
merged 70 commits into from
Jan 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
033c093
course compute server: second approach oriented around assignments an…
williamstein Dec 26, 2024
992311b
course: way to configure a compute server
williamstein Dec 26, 2024
18a64bf
course compute: slight refactor
williamstein Dec 26, 2024
9c18a5b
Merge branch 'master' into course-compute-2
williamstein Dec 26, 2024
533dcf5
course compute servers: work in progress on UI
williamstein Dec 26, 2024
83e5141
Merge branch 'master' into course-compute-2
williamstein Dec 28, 2024
dac00ab
course: create compute server (just barely doing something)
williamstein Dec 29, 2024
0296bdf
course compute servers -- code refactoring
williamstein Dec 29, 2024
405e25a
create compute server
williamstein Dec 29, 2024
86cb7d7
course compute: better button state
williamstein Dec 29, 2024
5196578
add api call to get any number of servers info by id's
williamstein Dec 29, 2024
7216e97
course compute servers: surface server state
williamstein Dec 30, 2024
32ab4ae
course compute: implementing commands
williamstein Dec 30, 2024
84749e7
Merge branch 'master' into course-compute-2
williamstein Dec 30, 2024
5ba85b6
style changes
williamstein Dec 30, 2024
a029099
Merge branch 'master' into course-compute-2
williamstein Dec 30, 2024
0d3fa1f
course compute -- adding checkboxes
williamstein Dec 31, 2024
1a10910
course compute servers -- ability to apply command to all selected se…
williamstein Dec 31, 2024
f6bba1d
course compute server: allow collaborator control
williamstein Dec 31, 2024
4a40b78
course compute -- little things
williamstein Dec 31, 2024
90e788b
course/compute: think about transfer-ownership
williamstein Dec 31, 2024
4f47820
course compute: working on terminal
williamstein Dec 31, 2024
854df3b
course/compute -- better communication of command
williamstein Dec 31, 2024
dacdd2f
course compute -- do not allow self hosted compute servers
williamstein Dec 31, 2024
2d2062e
course compute -- started working on terminal
williamstein Dec 31, 2024
735a403
implemented running terminal command on each running compute server i…
williamstein Jan 1, 2025
624ea2b
course compute: make running terminal command on each server maybe ac…
williamstein Jan 1, 2025
764c603
course compute -- use asyncMap
williamstein Jan 1, 2025
fce9d01
course compute: add course_project_id link from compute server for st…
williamstein Jan 2, 2025
e9b1020
course compute -- proper synctable tracking of compute servers of stu…
williamstein Jan 2, 2025
a3baeb2
course compute: added reboot; also confirmation for stop
williamstein Jan 2, 2025
0268f65
course compute: only create one compute server even if selected for m…
williamstein Jan 2, 2025
98e6710
course: sort students in handout same as for assignment (same as sele…
williamstein Jan 3, 2025
c659efb
course compute: fix student sorting more systematically
williamstein Jan 3, 2025
f79ecc2
course compute: fix setting compute server config for each student
williamstein Jan 3, 2025
c0adb32
course compute: address some throttling issues
williamstein Jan 3, 2025
e42c625
compute: better checking that dns name is available when creating com…
williamstein Jan 3, 2025
97fd541
compute dns: minor UI improvements
williamstein Jan 3, 2025
6d6cde6
compute: ensure dns is valid; expose api errors during dev
williamstein Jan 3, 2025
30cba6b
Merge branch 'master' into course-compute-2
williamstein Jan 3, 2025
757b225
compute server: frontend ui for idle timeout and automatic shutdown
williamstein Jan 4, 2025
74ee654
compute server automatic shutdown -- support disabled and improve ui
williamstein Jan 4, 2025
5d53dc0
project idle timeout -- make it so if user actively uses file or term…
williamstein Jan 4, 2025
0671401
implement backend side of idle timeout (not tested)
williamstein Jan 4, 2025
d6e0d36
idle timeout -- fix two little issues
williamstein Jan 4, 2025
51dbbb1
compute server idle timeout maintenance -- fix some obvious issues
williamstein Jan 4, 2025
a09ed43
compute: set initial idle timeout in future on start
williamstein Jan 4, 2025
87971d2
compute server: add displayed info about idle timeout
williamstein Jan 4, 2025
757a87e
course: selecting a compute server for a notebook or terminal in the …
williamstein Jan 4, 2025
717a496
slight tweaks to opening files on compute servers
williamstein Jan 5, 2025
f3bc85a
course compute: show current running cost
williamstein Jan 5, 2025
4cea62c
compute server: add a little bit more running progress info
williamstein Jan 5, 2025
b98eaf6
course compute -- some trivial style
williamstein Jan 5, 2025
bf100ef
compute servers: add option to get purchases api call to get purchase…
williamstein Jan 5, 2025
f72b033
compute server log: add tab for purchases
williamstein Jan 5, 2025
d8f5622
project pages: improve some css a little
williamstein Jan 5, 2025
f1fea01
course compute server -- show total cost spent on student's server
williamstein Jan 6, 2025
ede5a51
fix some style issues with compute servers / courses
williamstein Jan 6, 2025
3105682
add spend limit to compute sever base configuration
williamstein Jan 6, 2025
d468d04
Merge branch 'master' into course-compute-2
williamstein Jan 7, 2025
03ca3fd
compute spending limit: rough draft of UI
williamstein Jan 7, 2025
66d3fc3
compute: spending limit -- it's just a form of automatic shutdown
williamstein Jan 7, 2025
78d1c69
compute: refactor and clean up automatic shutdown strategies frontend ui
williamstein Jan 7, 2025
d306216
compute shutdown strategies -- add icons
williamstein Jan 7, 2025
d046f76
compute servers: implement backend part of spend limit
williamstein Jan 7, 2025
df9bb2d
compute server: rewrite automatic shutdown related code to store all …
williamstein Jan 8, 2025
7d4bdac
compute server: deal better with long logs
williamstein Jan 8, 2025
7ce6d34
fix typescript error
williamstein Jan 8, 2025
ce1f3fa
fix some tests
williamstein Jan 8, 2025
7de60bf
surface spend limits in course UI
williamstein Jan 8, 2025
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
8 changes: 4 additions & 4 deletions src/compute/compute/dev/env.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ export API_SERVER=`cat conf/api_server`
export PROJECT_ID=`cat conf/project_id`
export COMPUTE_SERVER_ID=`cat conf/compute_server_id`
export HOSTNAME=`cat conf/hostname`
export UNIONFS_UPPER=/tmp/upper
export UNIONFS_LOWER=/tmp/lower
export PROJECT_HOME=/tmp/home
export READ_TRACKING_FILE=/tmp/reads
export UNIONFS_UPPER=/tmp/upper5002
export UNIONFS_LOWER=/tmp/lower5002
export PROJECT_HOME=/tmp/home5002
export READ_TRACKING_FILE=/tmp/reads5002
export METADATA_FILE=$UNIONFS_LOWER/.compute-servers/$COMPUTE_SERVER_ID/meta/meta.lz4
export EXCLUDE_FROM_SYNC=`cat conf/exclude_from_sync`

Expand Down
4 changes: 2 additions & 2 deletions src/compute/compute/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
"scripts": {
"preinstall": "npx only-allow pnpm",
"clean": "rm -rf dist node_modules",
"make": "pnpm install && pnpm run build",
"build": "../../packages/node_modules/.bin/tsc --build",
"make": "pnpm run build",
"build": "../../packages/node_modules/.bin/tsc",
"tsc": "../../packages/node_modules/.bin/tsc --watch --pretty --preserveWatchOutput"
},
"bin": {
Expand Down
9 changes: 9 additions & 0 deletions src/packages/frontend/client/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ async function callApi(
args?: object,
numRetriesOnFail?: number,
) {
// console.log("callApi", { endpoint, args });
const url = join(appBasePath, "api", endpoint);
const resp = await fetch(url, {
method: "POST",
Expand Down Expand Up @@ -73,5 +74,13 @@ async function callApi(
if (typeof json == "object" && json.error) {
throw Error(json.error);
}
if (typeof json == "object" && json.errors) {
// This is what happens when the api request fails due to schema validation issues.
// I.e., this is soemthing we only see in dev mode since the schema stuff is disabled in production.
throw Error(
`API Schema Error: ${json.message} ${JSON.stringify(json.errors)}`,
);
}
// console.log("got ", json);
return json;
}
11 changes: 5 additions & 6 deletions src/packages/frontend/client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ export interface WebappClient extends EventEmitter {
synctable_project: Function;
project_websocket: Function;
prettier: Function;
exec: Function; // TODO: rewrite project_actions.ts to not use this at all.
touch_project: (project_id: string) => void;
exec: Function;
touch_project: (project_id: string, compute_server_id?: number) => void;
ipywidgetsGetBuffer: (
project_id: string,
path: string,
Expand All @@ -99,7 +99,6 @@ export interface WebappClient extends EventEmitter {
is_deleted: (filename: string, project_id: string) => boolean;
set_deleted: Function;
mark_file: (opts: any) => Promise<void>;

set_connected?: Function;
version: Function;
}
Expand Down Expand Up @@ -157,8 +156,8 @@ class Client extends EventEmitter implements WebappClient {
synctable_project: Function;
project_websocket: Function;
prettier: Function;
exec: Function; // TODO: rewrite project_actions.ts to not use this at all.
touch_project: (project_id: string) => void;
exec: Function;
touch_project: (project_id: string, compute_server_id?: number) => void;
ipywidgetsGetBuffer: (
project_id: string,
path: string,
Expand Down Expand Up @@ -242,7 +241,7 @@ class Client extends EventEmitter implements WebappClient {
this.idle_reset = this.idle_client.idle_reset.bind(this.idle_client);

this.exec = this.project_client.exec.bind(this.project_client);
this.touch_project = this.project_client.touch.bind(this.project_client);
this.touch_project = this.project_client.touch_project.bind(this.project_client);
this.ipywidgetsGetBuffer = this.project_client.ipywidgetsGetBuffer.bind(
this.project_client,
);
Expand Down
51 changes: 49 additions & 2 deletions src/packages/frontend/client/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ import { reuseInFlight } from "@cocalc/util/reuse-in-flight";
import { DirectoryListingEntry } from "@cocalc/util/types";
import httpApi from "./api";
import { WebappClient } from "./client";
import { throttle } from "lodash";

export class ProjectClient {
private client: WebappClient;
Expand Down Expand Up @@ -414,7 +415,24 @@ export class ProjectClient {
await this.call(message.remove_all_upgrades({ projects }));
}

public async touch(project_id: string): Promise<void> {
touch_project = async (
// project_id where activity occured
project_id: string,
// optional global id of a compute server (in the given project), in which case we also mark
// that compute server as active, which keeps it running in case it has idle timeout configured.
compute_server_id?: number,
): Promise<void> => {
if (compute_server_id) {
// this is throttled, etc. and is independent of everything below.
touchComputeServer({
project_id,
compute_server_id,
client: this.client,
});
// that said, we do still touch the project, since if a user is actively
// using a compute server, the project should also be considered active.
}

const state = redux.getStore("projects")?.get_state(project_id);
if (!(state == null && redux.getStore("account")?.get("is_admin"))) {
// not trying to view project as admin so do some checks
Expand Down Expand Up @@ -448,7 +466,7 @@ export class ProjectClient {
// the project (updating the db), but it still *does*
// ensure there is a TCP connection to the project.
}
}
};

// Print file to pdf
// The printed version of the file will be created in the same directory
Expand Down Expand Up @@ -622,3 +640,32 @@ export class ProjectClient {
return await computeServers(project_id)?.getServerIdForPath(path);
};
}

// (NOTE: this won't throw an exception)
const touchComputeServer = throttle(
async ({ project_id, compute_server_id, client }) => {
if (!compute_server_id) {
// nothing to do
return;
}
try {
await client.async_query({
query: {
compute_servers: {
project_id,
id: compute_server_id,
last_edited_user: client.server_time(),
},
},
});
} catch (err) {
// just a warning -- if we can't connect then touching isn't something we should be doing anyways.
console.log(
"WARNING: failed to touch compute server -- ",
{ compute_server_id },
err,
);
}
},
30000,
);
2 changes: 1 addition & 1 deletion src/packages/frontend/compute/action.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,7 @@ function OnPremGuide({ setShow, configuration, id, title, action }) {
</div>
)}
{!apiKey && !error && <Spin />}
{error && <ShowError error={error} setError={setError} />}
<ShowError error={error} setError={setError} />
</div>
{action == "stop" && (
<div>
Expand Down
38 changes: 31 additions & 7 deletions src/packages/frontend/compute/api.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import api from "@cocalc/frontend/client/api";
("");
import type {
Action,
Cloud,
ComputeServerTemplate,
ComputeServerUserInfo,
Configuration,
Cloud,
Images,
GoogleCloudImages,
} from "@cocalc/util/db-schema/compute-servers";
Expand All @@ -21,11 +21,12 @@ export async function createServer(opts: {
project_id: string;
title?: string;
color?: string;
idle_timeout?: number;
autorestart?: boolean;
cloud?: Cloud;
configuration?: Configuration;
notes?: string;
course_project_id?: string;
course_server_id?: number;
}): Promise<number> {
return await api("compute/create-server", opts);
}
Expand All @@ -37,7 +38,25 @@ export async function computeServerAction(opts: {
await api("compute/compute-server-action", opts);
}

export async function getServers(opts: { id?: number; project_id: string }) {
// Get servers across potentially different projects by their global unique id.
// Use the fields parameter to restrict to a much smaller subset of information
// about each server (e.g., just the state field). Caller must be a collaborator
// on each project containing the servers.
// If you give an id of a server that doesn't exist, it'll just be excluded in the result.
// Similarly, if you give a field that doesn't exist, it is excluded.
// The order of the returned servers and count probably will NOT match that in
// ids, so you should include 'id' in fields.
export async function getServersById(opts: {
ids: number[];
fields?: string[];
}): Promise<Partial<ComputeServerUserInfo>[]> {
return await api("compute/get-servers-by-id", opts);
}

export async function getServers(opts: {
id?: number;
project_id: string;
}): Promise<ComputeServerUserInfo[]> {
return await api("compute/get-servers", opts);
}

Expand All @@ -50,7 +69,12 @@ export async function getSerialPortOutput(id: number) {
}

export async function deleteServer(id: number) {
await api("compute/delete-server", { id });
return await api("compute/delete-server", { id });
}

export async function isDnsAvailable(dns: string) {
const { isAvailable } = await api("compute/is-dns-available", { dns });
return isAvailable;
}

export async function undeleteServer(id: number) {
Expand All @@ -69,7 +93,7 @@ export async function setServerTitle(opts: { id: number; title: string }) {

export async function setServerConfiguration(opts: {
id: number;
configuration;
configuration: Partial<Configuration>;
}) {
return await api("compute/set-server-configuration", opts);
}
Expand Down Expand Up @@ -186,7 +210,7 @@ export async function deleteApiKey(opts: { id }): Promise<string> {
}

// Get the project log entries directly for just one compute server
export async function getLog(opts: { id }) {
export async function getLog(opts: { id; type: "activity" | "files" }) {
return await api("compute/get-log", opts);
}

Expand Down
Loading
Loading