Skip to content

Commit f24ea1d

Browse files
committed
implement entire compute server api via conat RPC with typescript
1 parent 3d1e9f4 commit f24ea1d

File tree

8 files changed

+309
-33
lines changed

8 files changed

+309
-33
lines changed
Lines changed: 167 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,53 @@
1-
import { authFirstRequireAccount } from "./util";
1+
import { authFirstRequireAccount, authFirst } from "./util";
22
import type {
3-
//Action,
3+
Action,
44
Cloud,
5-
//ComputeServerTemplate,
6-
//ComputeServerUserInfo,
5+
ComputeServerTemplate,
6+
ComputeServerUserInfo,
77
Configuration,
8-
//Images,
9-
//GoogleCloudImages,
8+
Images,
9+
GoogleCloudImages,
1010
} from "@cocalc/util/db-schema/compute-servers";
11+
import type { GoogleCloudData } from "@cocalc/util/compute/cloud/google-cloud/compute-cost";
12+
import type { HyperstackPriceData } from "@cocalc/util/compute/cloud/hyperstack/pricing";
13+
import type {
14+
ConfigurationTemplate,
15+
ConfigurationTemplates,
16+
} from "@cocalc/util/compute/templates";
1117

1218
export const compute = {
1319
createServer: authFirstRequireAccount,
20+
computeServerAction: authFirstRequireAccount,
21+
getServersById: authFirstRequireAccount,
22+
getServers: authFirstRequireAccount,
23+
getServerState: authFirstRequireAccount,
24+
getSerialPortOutput: authFirstRequireAccount,
25+
deleteServer: authFirstRequireAccount,
26+
undeleteServer: authFirstRequireAccount,
27+
isDnsAvailable: authFirstRequireAccount,
28+
setServerColor: authFirstRequireAccount,
29+
setServerTitle: authFirstRequireAccount,
30+
setServerConfiguration: authFirstRequireAccount,
31+
setTemplate: authFirstRequireAccount,
32+
getTemplate: true,
33+
getTemplates: authFirstRequireAccount,
34+
setServerCloud: authFirstRequireAccount,
35+
setServerOwner: authFirstRequireAccount,
36+
getGoogleCloudPriceData: authFirstRequireAccount,
37+
getHyperstackPriceData: authFirstRequireAccount,
38+
getNetworkUsage: authFirstRequireAccount,
39+
getApiKey: authFirstRequireAccount,
40+
deleteApiKey: authFirstRequireAccount,
41+
getLog: authFirstRequireAccount,
42+
getTitle: authFirstRequireAccount,
43+
setDetailedState: authFirstRequireAccount,
44+
getImages: authFirst,
45+
getGoogleCloudImages: authFirst,
46+
setImageTested: authFirstRequireAccount,
1447
};
1548

1649
export interface Compute {
50+
// server lifecycle
1751
createServer: (opts: {
1852
account_id?: string;
1953
project_id: string;
@@ -26,4 +60,131 @@ export interface Compute {
2660
course_project_id?: string;
2761
course_server_id?: number;
2862
}) => Promise<number>;
63+
64+
computeServerAction: (opts: {
65+
account_id?: string;
66+
id: number;
67+
action: Action;
68+
}) => Promise<void>;
69+
70+
getServersById: (opts: {
71+
account_id?: string;
72+
ids: number[];
73+
fields?: Array<keyof ComputeServerUserInfo>;
74+
}) => Promise<Partial<ComputeServerUserInfo>[]>;
75+
76+
getServers: (opts: {
77+
account_id?: string;
78+
id?: number;
79+
project_id: string;
80+
}) => Promise<ComputeServerUserInfo[]>;
81+
82+
getServerState: (opts: {
83+
account_id?: string;
84+
id: number;
85+
}) => Promise<ComputeServerUserInfo["state"]>;
86+
getSerialPortOutput: (opts: {
87+
account_id?: string;
88+
id: number;
89+
}) => Promise<string>;
90+
91+
deleteServer: (opts: { account_id?: string; id: number }) => Promise<void>;
92+
undeleteServer: (opts: { account_id?: string; id: number }) => Promise<void>;
93+
94+
isDnsAvailable: (opts: {
95+
account_id?: string;
96+
dns: string;
97+
}) => Promise<boolean>;
98+
99+
// ownership & metadata
100+
setServerColor: (opts: {
101+
account_id?: string;
102+
id: number;
103+
color: string;
104+
}) => Promise<void>;
105+
setServerTitle: (opts: {
106+
account_id?: string;
107+
id: number;
108+
title: string;
109+
}) => Promise<void>;
110+
setServerConfiguration: (opts: {
111+
account_id?: string;
112+
id: number;
113+
configuration: Partial<Configuration>;
114+
}) => Promise<void>;
115+
116+
setTemplate: (opts: {
117+
account_id?: string;
118+
id: number;
119+
template: ComputeServerTemplate;
120+
}) => Promise<void>;
121+
122+
getTemplate: (opts: {
123+
account_id?: string;
124+
id: number;
125+
}) => Promise<ConfigurationTemplate>;
126+
getTemplates: () => Promise<ConfigurationTemplates>;
127+
128+
setServerCloud: (opts: {
129+
account_id?: string;
130+
id: number;
131+
cloud: Cloud | string;
132+
}) => Promise<void>;
133+
setServerOwner: (opts: {
134+
account_id?: string;
135+
id: number;
136+
new_account_id: string;
137+
}) => Promise<void>;
138+
139+
// pricing caches
140+
getGoogleCloudPriceData: () => Promise<GoogleCloudData>;
141+
getHyperstackPriceData: () => Promise<HyperstackPriceData>;
142+
143+
// usage & logs
144+
getNetworkUsage: (opts: {
145+
account_id?: string;
146+
id: number;
147+
start: Date;
148+
end: Date;
149+
}) => Promise<{ amount: number; cost: number }>;
150+
151+
getApiKey: (opts: { account_id?: string; id: number }) => Promise<string>;
152+
deleteApiKey: (opts: { account_id?: string; id: number }) => Promise<void>;
153+
154+
getLog: (opts: {
155+
account_id?: string;
156+
id: number;
157+
type: "activity" | "files";
158+
}) => Promise<any>;
159+
160+
getTitle: (opts: { account_id?: string; id: number }) => Promise<{
161+
title: string;
162+
color: string;
163+
project_specific_id: number;
164+
}>;
165+
166+
setDetailedState: (opts: {
167+
account_id?: string;
168+
project_id: string;
169+
id: number;
170+
name: string;
171+
state?: string;
172+
extra?: string;
173+
timeout?: number;
174+
progress?: number;
175+
}) => Promise<void>;
176+
177+
getImages: (opts?: {
178+
noCache?: boolean;
179+
account_id?: string;
180+
}) => Promise<Images>;
181+
getGoogleCloudImages: (opts?: {
182+
noCache?: boolean;
183+
account_id?: string;
184+
}) => Promise<GoogleCloudImages>;
185+
setImageTested: (opts: {
186+
account_id?: string;
187+
id: number;
188+
tested: boolean;
189+
}) => Promise<void>;
29190
}

src/packages/frontend/compute/api.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,8 +212,8 @@ export async function getNetworkUsage(opts: {
212212
export async function getApiKey(opts: { id }): Promise<string> {
213213
return await api("compute/get-api-key", opts);
214214
}
215-
export async function deleteApiKey(opts: { id }): Promise<string> {
216-
return await api("compute/delete-api-key", opts);
215+
export async function deleteApiKey(opts: { id }): Promise<void> {
216+
await api("compute/delete-api-key", opts);
217217
}
218218

219219
// Get the project log entries directly for just one compute server

src/packages/next/pages/api/v2/compute/get-images-google.ts

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ Get all google cloud images.
55
import getAccountId from "lib/account/get-account";
66
import { getAllImages } from "@cocalc/server/compute/cloud/google-cloud/images";
77
import getParams from "lib/api/get-params";
8-
import userIsInGroup from "@cocalc/server/accounts/is-in-group";
98

109
import { apiRoute, apiRouteOperation } from "lib/api";
1110
import {
@@ -28,13 +27,7 @@ async function get(req) {
2827
throw Error("must be signed in");
2928
}
3029
let { noCache } = getParams(req);
31-
if (noCache) {
32-
// NOTE: only admins can specify noCache
33-
if (!(await userIsInGroup(account_id, "admin"))) {
34-
throw Error("only admin are allowed to specify noCache");
35-
}
36-
}
37-
return await getAllImages({ noCache: !!noCache });
30+
return await getAllImages({ noCache: !!noCache, account_id });
3831
}
3932

4033
export default apiRoute({

src/packages/next/pages/api/v2/compute/get-images.ts

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ Get IMAGES.
55
import getAccountId from "lib/account/get-account";
66
import { getImages } from "@cocalc/server/compute/images";
77
import getParams from "lib/api/get-params";
8-
import userIsInGroup from "@cocalc/server/accounts/is-in-group";
98

109
import { apiRoute, apiRouteOperation } from "lib/api";
1110
import {
@@ -28,13 +27,7 @@ async function get(req) {
2827
throw Error("must be signed in");
2928
}
3029
let { noCache } = getParams(req);
31-
if (noCache) {
32-
// NOTE: only admins can specify noCache
33-
if (!(await userIsInGroup(account_id, "admin"))) {
34-
throw Error("only admin are allowed to specify noCache");
35-
}
36-
}
37-
return await getImages({ noCache: !!noCache });
30+
return await getImages({ noCache: !!noCache, account_id });
3831
}
3932

4033
export default apiRoute({

src/packages/server/compute/compute-server-action.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { start, stop, suspend, resume, reboot, deprovision } from "./control";
33

44
interface Options {
55
id: number;
6-
account_id: string;
6+
account_id?: string;
77
action: Action;
88
}
99

@@ -12,6 +12,9 @@ export default async function computeServerAction({
1212
account_id,
1313
action,
1414
}: Options): Promise<void> {
15+
if (!account_id) {
16+
throw Error("must be signed in");
17+
}
1518
switch (action) {
1619
case "start":
1720
return await start({ id, account_id });

src/packages/server/compute/database-cache.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ another level of namespacing.
2020
import { getPool } from "@cocalc/database";
2121
import json from "json-stable-stringify";
2222
import getLogger from "@cocalc/backend/logger";
23+
import isAdmin from "@cocalc/server/accounts/is-admin";
2324

2425
const logger = getLogger("server:compute:database-cache");
2526

@@ -119,7 +120,8 @@ export function createDatabaseCachedResource<T>({
119120
// Used by everything else in cocalc to get access to the cached data.
120121
const getData: GetFunction<T> = async ({
121122
noCache,
122-
}: { noCache?: boolean } = {}): Promise<T> => {
123+
account_id,
124+
}: { noCache?: boolean; account_id?: string } = {}): Promise<T> => {
123125
logger.debug(cloud, key, "getData");
124126
const { rows } = await db.query(
125127
"SELECT value, expire FROM compute_servers_cache WHERE cloud=$1 AND key=$2",
@@ -129,7 +131,7 @@ export function createDatabaseCachedResource<T>({
129131
logger.debug(cloud, key, "data not in database at all, so we have fetch");
130132
return await fetchDataAndUpdateDatabase(true);
131133
}
132-
if (noCache) {
134+
if (noCache && (await isAdmin(account_id))) {
133135
return await fetchDataAndUpdateDatabase();
134136
}
135137
const { value, expire } = rows[0];

src/packages/server/compute/get-servers.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import isCollaborator, {
88
import { getProjectSpecificId } from "./project-specific-id";
99

1010
interface Options {
11-
account_id: string; // user making the request
11+
account_id?: string; // user making the request
1212
id?: number; // id of the compute server
1313
project_id?: string;
1414
}
@@ -23,7 +23,7 @@ export default async function getServers({
2323
id,
2424
project_id,
2525
}: Options): Promise<ComputeServerUserInfo[]> {
26-
if (!(await isValidUUID(account_id))) {
26+
if (!account_id || !(await isValidUUID(account_id))) {
2727
throw Error("account_id is not a valid uuid");
2828
}
2929
let query = `SELECT ${FIELDS.join(",")} FROM compute_servers`;
@@ -128,11 +128,11 @@ export async function getServersById({
128128
ids,
129129
fields,
130130
}: {
131-
account_id: string;
131+
account_id?: string;
132132
ids: number[];
133133
fields?: string[];
134134
}): Promise<Partial<ComputeServerUserInfo>[]> {
135-
if (!(await isValidUUID(account_id))) {
135+
if (!account_id || !(await isValidUUID(account_id))) {
136136
throw Error("account_id is not a valid uuid");
137137
}
138138
if (fields == null) {
@@ -161,4 +161,3 @@ export async function getServersById({
161161
}
162162
return stripNullFields(rows);
163163
}
164-

0 commit comments

Comments
 (0)