@gitpod/gitpod-protocol 0.1.5-pavel-10259-1.39 → 0.1.5-pavel-git-status-fix.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/data/gitpod-schema.json +16 -0
- package/lib/admin-protocol.d.ts +4 -0
- package/lib/admin-protocol.d.ts.map +1 -1
- package/lib/admin-protocol.js.map +1 -1
- package/lib/attribution.d.ts +20 -0
- package/lib/attribution.d.ts.map +1 -0
- package/lib/attribution.js +40 -0
- package/lib/attribution.js.map +1 -0
- package/lib/blocked-repositories-protocol.d.ts +13 -0
- package/lib/blocked-repositories-protocol.d.ts.map +1 -0
- package/lib/blocked-repositories-protocol.js +8 -0
- package/lib/blocked-repositories-protocol.js.map +1 -0
- package/lib/context-url.js +1 -1
- package/lib/context-url.js.map +1 -1
- package/lib/experiments/always-default.d.ts +8 -0
- package/lib/experiments/always-default.d.ts.map +1 -0
- package/lib/experiments/always-default.js +20 -0
- package/lib/experiments/always-default.js.map +1 -0
- package/lib/experiments/configcat-server.d.ts +8 -0
- package/lib/experiments/configcat-server.d.ts.map +1 -0
- package/lib/experiments/configcat-server.js +35 -0
- package/lib/experiments/configcat-server.js.map +1 -0
- package/lib/experiments/configcat.d.ts +22 -0
- package/lib/experiments/configcat.d.ts.map +1 -0
- package/lib/experiments/configcat.js +53 -0
- package/lib/experiments/configcat.js.map +1 -0
- package/lib/experiments/types.d.ts +23 -0
- package/lib/experiments/types.d.ts.map +1 -0
- package/lib/experiments/types.js +10 -0
- package/lib/experiments/types.js.map +1 -0
- package/lib/gitpod-service.d.ts +11 -2
- package/lib/gitpod-service.d.ts.map +1 -1
- package/lib/gitpod-service.js.map +1 -1
- package/lib/installation-admin-protocol.d.ts +3 -0
- package/lib/installation-admin-protocol.d.ts.map +1 -1
- package/lib/installation-admin-protocol.js +1 -0
- package/lib/installation-admin-protocol.js.map +1 -1
- package/lib/protocol.d.ts +49 -1
- package/lib/protocol.d.ts.map +1 -1
- package/lib/protocol.js +90 -2
- package/lib/protocol.js.map +1 -1
- package/lib/protocol.spec.d.ts +7 -0
- package/lib/protocol.spec.d.ts.map +1 -0
- package/lib/protocol.spec.js +127 -0
- package/lib/protocol.spec.js.map +1 -0
- package/lib/teams-projects-protocol.d.ts +1 -0
- package/lib/teams-projects-protocol.d.ts.map +1 -1
- package/lib/teams-projects-protocol.js.map +1 -1
- package/lib/usage.d.ts +22 -0
- package/lib/usage.d.ts.map +1 -0
- package/lib/usage.js +115 -0
- package/lib/usage.js.map +1 -0
- package/lib/util/debug-app.d.ts +25 -0
- package/lib/util/debug-app.d.ts.map +1 -0
- package/lib/util/debug-app.js +100 -0
- package/lib/util/debug-app.js.map +1 -0
- package/lib/util/logging.d.ts.map +1 -1
- package/lib/util/logging.js +8 -1
- package/lib/util/logging.js.map +1 -1
- package/lib/util/timeutil.d.ts +1 -0
- package/lib/util/timeutil.d.ts.map +1 -1
- package/lib/util/timeutil.js +7 -1
- package/lib/util/timeutil.js.map +1 -1
- package/lib/workspace-cluster.d.ts +6 -1
- package/lib/workspace-cluster.d.ts.map +1 -1
- package/lib/workspace-cluster.js.map +1 -1
- package/lib/workspace-instance.d.ts +6 -0
- package/lib/workspace-instance.d.ts.map +1 -1
- package/package.json +9 -6
- package/pkg-yarn.lock +7 -5
- package/provenance-bundle.jsonl +1 -1
- package/src/admin-protocol.ts +7 -0
- package/src/attribution.ts +48 -0
- package/src/blocked-repositories-protocol.ts +13 -0
- package/src/context-url.ts +1 -1
- package/src/experiments/always-default.ts +24 -0
- package/src/experiments/configcat-server.ts +38 -0
- package/src/experiments/configcat.ts +58 -0
- package/src/experiments/types.ts +35 -0
- package/src/gitpod-service.ts +16 -1
- package/src/installation-admin-protocol.ts +3 -0
- package/src/protocol.spec.ts +97 -0
- package/src/protocol.ts +134 -3
- package/src/teams-projects-protocol.ts +1 -0
- package/src/usage.ts +146 -0
- package/src/util/debug-app.ts +81 -0
- package/src/util/logging.ts +11 -2
- package/src/util/timeutil.ts +6 -0
- package/src/workspace-cluster.ts +3 -1
- package/src/workspace-instance.ts +11 -0
package/src/gitpod-service.ts
CHANGED
|
@@ -24,6 +24,9 @@ import {
|
|
|
24
24
|
GuessGitTokenScopesParams,
|
|
25
25
|
GuessedGitTokenScopes,
|
|
26
26
|
ProjectEnvVar,
|
|
27
|
+
PrebuiltWorkspace,
|
|
28
|
+
UserSSHPublicKeyValue,
|
|
29
|
+
SSHPublicKeyValue,
|
|
27
30
|
} from "./protocol";
|
|
28
31
|
import {
|
|
29
32
|
Team,
|
|
@@ -57,6 +60,7 @@ import { RemotePageMessage, RemoteTrackMessage, RemoteIdentifyMessage } from "./
|
|
|
57
60
|
import { IDEServer } from "./ide-protocol";
|
|
58
61
|
import { InstallationAdminSettings, TelemetryData } from "./installation-admin-protocol";
|
|
59
62
|
import { Currency } from "./plans";
|
|
63
|
+
import { BillableSession } from "./usage";
|
|
60
64
|
|
|
61
65
|
export interface GitpodClient {
|
|
62
66
|
onInstanceUpdate(instance: WorkspaceInstance): void;
|
|
@@ -148,6 +152,12 @@ export interface GitpodServer extends JsonRpcServer<GitpodClient>, AdminServer,
|
|
|
148
152
|
setEnvVar(variable: UserEnvVarValue): Promise<void>;
|
|
149
153
|
deleteEnvVar(variable: UserEnvVarValue): Promise<void>;
|
|
150
154
|
|
|
155
|
+
// User SSH Keys
|
|
156
|
+
hasSSHPublicKey(): Promise<boolean>;
|
|
157
|
+
getSSHPublicKeys(): Promise<UserSSHPublicKeyValue[]>;
|
|
158
|
+
addSSHPublicKey(value: SSHPublicKeyValue): Promise<UserSSHPublicKeyValue>;
|
|
159
|
+
deleteSSHPublicKey(id: string): Promise<void>;
|
|
160
|
+
|
|
151
161
|
// Teams
|
|
152
162
|
getTeams(): Promise<Team[]>;
|
|
153
163
|
getTeamMembers(teamId: string): Promise<TeamMemberInfo[]>;
|
|
@@ -172,6 +182,8 @@ export interface GitpodServer extends JsonRpcServer<GitpodClient>, AdminServer,
|
|
|
172
182
|
getUserProjects(): Promise<Project[]>;
|
|
173
183
|
getProjectOverview(projectId: string): Promise<Project.Overview | undefined>;
|
|
174
184
|
findPrebuilds(params: FindPrebuildsParams): Promise<PrebuildWithStatus[]>;
|
|
185
|
+
findPrebuildByWorkspaceID(workspaceId: string): Promise<PrebuiltWorkspace | undefined>;
|
|
186
|
+
getPrebuild(prebuildId: string): Promise<PrebuildWithStatus | undefined>;
|
|
175
187
|
triggerPrebuild(projectId: string, branchName: string | null): Promise<StartPrebuildResult>;
|
|
176
188
|
cancelPrebuild(projectId: string, prebuildId: string): Promise<void>;
|
|
177
189
|
fetchProjectRepositoryConfiguration(projectId: string): Promise<string | undefined>;
|
|
@@ -273,10 +285,12 @@ export interface GitpodServer extends JsonRpcServer<GitpodClient>, AdminServer,
|
|
|
273
285
|
|
|
274
286
|
getStripePublishableKey(): Promise<string>;
|
|
275
287
|
getStripeSetupIntentClientSecret(): Promise<string>;
|
|
276
|
-
|
|
288
|
+
findStripeSubscriptionIdForTeam(teamId: string): Promise<string | undefined>;
|
|
277
289
|
subscribeTeamToStripe(teamId: string, setupIntentId: string, currency: Currency): Promise<void>;
|
|
278
290
|
getStripePortalUrlForTeam(teamId: string): Promise<string>;
|
|
279
291
|
|
|
292
|
+
getBilledUsage(attributionId: string): Promise<BillableSession[]>;
|
|
293
|
+
|
|
280
294
|
/**
|
|
281
295
|
* Analytics
|
|
282
296
|
*/
|
|
@@ -380,6 +394,7 @@ export interface SetWorkspaceTimeoutResult {
|
|
|
380
394
|
|
|
381
395
|
export interface GetWorkspaceTimeoutResult {
|
|
382
396
|
duration: WorkspaceTimeoutDuration;
|
|
397
|
+
durationRaw: string;
|
|
383
398
|
canChange: boolean;
|
|
384
399
|
}
|
|
385
400
|
|
|
@@ -8,6 +8,7 @@ import { v4 as uuidv4 } from "uuid";
|
|
|
8
8
|
|
|
9
9
|
const InstallationAdminSettingsPrototype = {
|
|
10
10
|
sendTelemetry: true,
|
|
11
|
+
sendCustomerID: true,
|
|
11
12
|
};
|
|
12
13
|
|
|
13
14
|
export type InstallationAdminSettings = typeof InstallationAdminSettingsPrototype;
|
|
@@ -28,6 +29,8 @@ export interface TelemetryData {
|
|
|
28
29
|
totalUsers: number;
|
|
29
30
|
totalWorkspaces: number;
|
|
30
31
|
totalInstances: number;
|
|
32
|
+
licenseType: string;
|
|
33
|
+
customerID?: string;
|
|
31
34
|
}
|
|
32
35
|
|
|
33
36
|
export namespace InstallationAdmin {
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2020 Gitpod GmbH. All rights reserved.
|
|
3
|
+
* Licensed under the GNU Affero General Public License (AGPL).
|
|
4
|
+
* See License-AGPL.txt in the project root for license information.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { suite, test } from "mocha-typescript";
|
|
8
|
+
import * as chai from "chai";
|
|
9
|
+
import { SSHPublicKeyValue } from ".";
|
|
10
|
+
|
|
11
|
+
const expect = chai.expect;
|
|
12
|
+
|
|
13
|
+
@suite
|
|
14
|
+
class TestSSHPublicKeyValue {
|
|
15
|
+
private key =
|
|
16
|
+
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDCnrN9UdK1bNGPmZfenTWXLuYYDjlYvZE8S+WOfP08WpR1GETzX5ZvgYOEZGwEE8KUPHC9cge4Hvo/ydIS9aqbZ5MiVGJ8cAIq1Ic89SjlDWU6fl8TwIqOPCi2imAASlEDP4q8vMLK1N6UOW1EVbxyL3uybGd10ysC1t1FxFPveIGNsYE/MOQeuEWS16AplpXYXIfVRSlgAskeBft2w8Ud3B4gNe8ECLA/FXu96UpvZkdtOarA3JZ9Z27GveNJg9Mtmmw0+US0KXiO9x9NyH7G8+mqVDwDY+nNvaFA5gtQxkkl/uY2oz9k/B4Rjlj3jOiUXe5uQs3XUm5m8g9a9fh62DabLpA2fEvtfg+a/VqNe52dNa5YjupwvBd6Inb5uMW/TYjNl6bNHPlXFKw/nwLOVzukpkjxMZUKS6+4BGkpoasj6y2rTU/wkpbdD8J7yjI1p6J9aKkC6KksIWgN7xGmHkv2PCGDqMHTNbnQyowtNKMgA/667vAYJ0qW7HAHBFXJRs6uRi/DI3+c1QV2s4wPCpEHDIYApovQ0fbON4WDPoGMyHd7kPh9xB/bX7Dj0uMXImu1pdTd62fQ/1XXX64+vjAAXS/P9RSCD0RCRt/K3LPKl2m7GPI3y1niaE52XhxZw+ms9ays6NasNVMw/ZC+f02Ti+L5FBEVf8230RVVRQ== notfound@gitpod.io";
|
|
17
|
+
|
|
18
|
+
@test public testValidate() {
|
|
19
|
+
const key = this.key;
|
|
20
|
+
const [t, k, e] = key.split(" ");
|
|
21
|
+
expect(
|
|
22
|
+
SSHPublicKeyValue.getData({
|
|
23
|
+
key,
|
|
24
|
+
name: "NiceName",
|
|
25
|
+
}),
|
|
26
|
+
).to.deep.equal({ type: t, key: k, email: e });
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@test public testValidateWithDiffType() {
|
|
30
|
+
const key = this.key;
|
|
31
|
+
const [_, k, e] = key.split(" ");
|
|
32
|
+
expect(
|
|
33
|
+
SSHPublicKeyValue.getData({
|
|
34
|
+
key: key.replace("ssh-rsa", "sk-ecdsa-sha2-nistp256@openssh.com"),
|
|
35
|
+
name: "NiceName",
|
|
36
|
+
}),
|
|
37
|
+
).to.deep.equal({ type: "sk-ecdsa-sha2-nistp256@openssh.com", key: k, email: e });
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@test public testValidateWithoutEmail() {
|
|
41
|
+
const key = this.key;
|
|
42
|
+
const [t, k, _] = key.split(" ");
|
|
43
|
+
expect(
|
|
44
|
+
SSHPublicKeyValue.getData({
|
|
45
|
+
key: key.replace(" notfound@gitpod.io", ""),
|
|
46
|
+
name: "NiceName",
|
|
47
|
+
}),
|
|
48
|
+
).to.deep.equal({ type: t, key: k, email: undefined });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@test public testValidateWithoutEmailButEndsWithSpaces() {
|
|
52
|
+
const key = this.key;
|
|
53
|
+
const [t, k, _] = key.split(" ");
|
|
54
|
+
expect(
|
|
55
|
+
SSHPublicKeyValue.getData({
|
|
56
|
+
key: key.replace("notfound@gitpod.io", " "),
|
|
57
|
+
name: "NiceName",
|
|
58
|
+
}),
|
|
59
|
+
).to.deep.equal({ type: t, key: k, email: undefined });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
@test public testValidateWithError() {
|
|
63
|
+
expect(() =>
|
|
64
|
+
SSHPublicKeyValue.getData({
|
|
65
|
+
key: "Hello World",
|
|
66
|
+
name: "NiceName",
|
|
67
|
+
}),
|
|
68
|
+
).throw("Key is invalid");
|
|
69
|
+
|
|
70
|
+
expect(() =>
|
|
71
|
+
SSHPublicKeyValue.getData({
|
|
72
|
+
key: "",
|
|
73
|
+
name: "NiceName",
|
|
74
|
+
}),
|
|
75
|
+
).throw("Key is invalid");
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
@test public testGetFingerprint() {
|
|
79
|
+
const key = this.key;
|
|
80
|
+
expect(
|
|
81
|
+
SSHPublicKeyValue.getFingerprint({
|
|
82
|
+
key,
|
|
83
|
+
name: "NiceName",
|
|
84
|
+
}),
|
|
85
|
+
).to.equal("ykjP/b5aqoa3envmXzWpPMCGgEFMu3QvubfSTNrJCMA=");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
@test public testGetFingerprintWithIncorrectPublicKey() {
|
|
89
|
+
expect(() =>
|
|
90
|
+
SSHPublicKeyValue.getFingerprint({
|
|
91
|
+
key: "Hello World",
|
|
92
|
+
name: "NiceName",
|
|
93
|
+
}),
|
|
94
|
+
).to.throw("Key is invalid");
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
module.exports = new TestSSHPublicKeyValue(); // Only to circumvent no usage warning :-/
|
package/src/protocol.ts
CHANGED
|
@@ -70,11 +70,14 @@ export namespace User {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
/**
|
|
73
|
-
*
|
|
73
|
+
* Returns the stored email or if it doesn't exist returns the primaryEmail of the first identity this user signed up with.
|
|
74
74
|
* @param user
|
|
75
75
|
* @returns A primaryEmail, or undefined if there is none.
|
|
76
76
|
*/
|
|
77
77
|
export function getPrimaryEmail(user: User): string | undefined {
|
|
78
|
+
if (user.additionalData?.profile?.emailAddress) {
|
|
79
|
+
return user.additionalData?.profile?.emailAddress;
|
|
80
|
+
}
|
|
78
81
|
const identities = user.identities.filter((i) => !!i.primaryEmail);
|
|
79
82
|
if (identities.length <= 0) {
|
|
80
83
|
return undefined;
|
|
@@ -136,6 +139,50 @@ export namespace User {
|
|
|
136
139
|
}
|
|
137
140
|
user.additionalData.ideSettings = newIDESettings;
|
|
138
141
|
}
|
|
142
|
+
|
|
143
|
+
export function getProfile(user: User): Profile {
|
|
144
|
+
return {
|
|
145
|
+
name: User.getName(user!) || "",
|
|
146
|
+
email: User.getPrimaryEmail(user!) || "",
|
|
147
|
+
company: user?.additionalData?.profile?.companyName,
|
|
148
|
+
avatarURL: user?.avatarUrl,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function setProfile(user: User, profile: Profile): User {
|
|
153
|
+
user.fullName = profile.name;
|
|
154
|
+
user.avatarUrl = profile.avatarURL;
|
|
155
|
+
|
|
156
|
+
if (!user.additionalData) {
|
|
157
|
+
user.additionalData = {};
|
|
158
|
+
}
|
|
159
|
+
if (!user.additionalData.profile) {
|
|
160
|
+
user.additionalData.profile = {};
|
|
161
|
+
}
|
|
162
|
+
user.additionalData.profile.emailAddress = profile.email;
|
|
163
|
+
user.additionalData.profile.companyName = profile.company;
|
|
164
|
+
user.additionalData.profile.lastUpdatedDetailsNudge = new Date().toISOString();
|
|
165
|
+
|
|
166
|
+
return user;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// The actual Profile of a User
|
|
170
|
+
export interface Profile {
|
|
171
|
+
name: string;
|
|
172
|
+
email: string;
|
|
173
|
+
company?: string;
|
|
174
|
+
avatarURL?: string;
|
|
175
|
+
}
|
|
176
|
+
export namespace Profile {
|
|
177
|
+
export function hasChanges(before: Profile, after: Profile) {
|
|
178
|
+
return (
|
|
179
|
+
before.name !== after.name ||
|
|
180
|
+
before.email !== after.email ||
|
|
181
|
+
before.company !== after.company ||
|
|
182
|
+
before.avatarURL !== after.avatarURL
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
139
186
|
}
|
|
140
187
|
|
|
141
188
|
export interface AdditionalUserData {
|
|
@@ -150,9 +197,24 @@ export interface AdditionalUserData {
|
|
|
150
197
|
oauthClientsApproved?: { [key: string]: string };
|
|
151
198
|
// to remember GH Orgs the user installed/updated the GH App for
|
|
152
199
|
knownGitHubOrgs?: string[];
|
|
153
|
-
|
|
154
200
|
// Git clone URL pointing to the user's dotfile repo
|
|
155
201
|
dotfileRepo?: string;
|
|
202
|
+
// Identifies an explicit team or user ID to which all the user's workspace usage should be attributed to (e.g. for billing purposes)
|
|
203
|
+
usageAttributionId?: string;
|
|
204
|
+
// preferred workspace classes
|
|
205
|
+
workspaceClasses?: WorkspaceClasses;
|
|
206
|
+
// additional user profile data
|
|
207
|
+
profile?: ProfileDetails;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// The format in which we store User Profiles in
|
|
211
|
+
export interface ProfileDetails {
|
|
212
|
+
// when was the last time the user updated their profile information or has been nudged to do so.
|
|
213
|
+
lastUpdatedDetailsNudge?: string;
|
|
214
|
+
// the user's company name
|
|
215
|
+
companyName?: string;
|
|
216
|
+
// the user's email
|
|
217
|
+
emailAddress?: string;
|
|
156
218
|
}
|
|
157
219
|
|
|
158
220
|
export interface EmailNotificationSettings {
|
|
@@ -171,6 +233,11 @@ export type IDESettings = {
|
|
|
171
233
|
useLatestVersion?: boolean;
|
|
172
234
|
};
|
|
173
235
|
|
|
236
|
+
export interface WorkspaceClasses {
|
|
237
|
+
regular: string;
|
|
238
|
+
prebuild: string;
|
|
239
|
+
}
|
|
240
|
+
|
|
174
241
|
export interface UserPlatform {
|
|
175
242
|
uid: string;
|
|
176
243
|
userAgent: string;
|
|
@@ -205,6 +272,7 @@ export const WorkspaceFeatureFlags = {
|
|
|
205
272
|
full_workspace_backup: undefined,
|
|
206
273
|
fixed_resources: undefined,
|
|
207
274
|
persistent_volume_claim: undefined,
|
|
275
|
+
protected_secrets: undefined,
|
|
208
276
|
};
|
|
209
277
|
export type NamedWorkspaceFeatureFlag = keyof typeof WorkspaceFeatureFlags;
|
|
210
278
|
|
|
@@ -356,6 +424,68 @@ export namespace UserEnvVar {
|
|
|
356
424
|
}
|
|
357
425
|
}
|
|
358
426
|
|
|
427
|
+
export interface SSHPublicKeyValue {
|
|
428
|
+
name: string;
|
|
429
|
+
key: string;
|
|
430
|
+
}
|
|
431
|
+
export interface UserSSHPublicKey extends SSHPublicKeyValue {
|
|
432
|
+
id: string;
|
|
433
|
+
key: string;
|
|
434
|
+
userId: string;
|
|
435
|
+
fingerprint: string;
|
|
436
|
+
creationTime: string;
|
|
437
|
+
lastUsedTime?: string;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
export type UserSSHPublicKeyValue = Omit<UserSSHPublicKey, "key" | "userId">;
|
|
441
|
+
|
|
442
|
+
export namespace SSHPublicKeyValue {
|
|
443
|
+
export function validate(value: SSHPublicKeyValue): string | undefined {
|
|
444
|
+
if (value.name.length === 0) {
|
|
445
|
+
return "Title must not be empty.";
|
|
446
|
+
}
|
|
447
|
+
if (value.name.length > 255) {
|
|
448
|
+
return "Title too long. Maximum value length is 255 characters.";
|
|
449
|
+
}
|
|
450
|
+
if (value.key.length === 0) {
|
|
451
|
+
return "Key must not be empty.";
|
|
452
|
+
}
|
|
453
|
+
try {
|
|
454
|
+
getData(value);
|
|
455
|
+
} catch (e) {
|
|
456
|
+
return "Key is invalid. You must supply a key in OpenSSH public key format.";
|
|
457
|
+
}
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
export function getData(value: SSHPublicKeyValue) {
|
|
462
|
+
// Begins with 'ssh-rsa', 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-nistp521', 'ssh-ed25519', 'sk-ecdsa-sha2-nistp256@openssh.com', or 'sk-ssh-ed25519@openssh.com'.
|
|
463
|
+
const regex =
|
|
464
|
+
/^(?<type>ssh-rsa|ecdsa-sha2-nistp256|ecdsa-sha2-nistp384|ecdsa-sha2-nistp521|ssh-ed25519|sk-ecdsa-sha2-nistp256@openssh\.com|sk-ssh-ed25519@openssh\.com) (?<key>.*?)( (?<email>.*?))?$/;
|
|
465
|
+
const resultGroup = regex.exec(value.key.trim());
|
|
466
|
+
if (!resultGroup) {
|
|
467
|
+
throw new Error("Key is invalid.");
|
|
468
|
+
}
|
|
469
|
+
return {
|
|
470
|
+
type: resultGroup.groups?.["type"] as string,
|
|
471
|
+
key: resultGroup.groups?.["key"] as string,
|
|
472
|
+
email: resultGroup.groups?.["email"] || undefined,
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
export function getFingerprint(value: SSHPublicKeyValue) {
|
|
477
|
+
const data = getData(value);
|
|
478
|
+
let buf = Buffer.from(data.key, "base64");
|
|
479
|
+
// gitlab style
|
|
480
|
+
// const hash = createHash("md5").update(buf).digest("hex");
|
|
481
|
+
// github style
|
|
482
|
+
const hash = createHash("sha256").update(buf).digest("base64");
|
|
483
|
+
return hash;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
export const MAXIMUM_KEY_LENGTH = 5;
|
|
487
|
+
}
|
|
488
|
+
|
|
359
489
|
export interface GitpodToken {
|
|
360
490
|
/** Hash value (SHA256) of the token (primary key). */
|
|
361
491
|
tokenHash: string;
|
|
@@ -564,7 +694,7 @@ export interface Workspace {
|
|
|
564
694
|
|
|
565
695
|
export type WorkspaceSoftDeletion = "user" | "gc";
|
|
566
696
|
|
|
567
|
-
export type WorkspaceType = "regular" | "prebuild" | "probe";
|
|
697
|
+
export type WorkspaceType = "regular" | "prebuild" | "probe"; // TODO(gpl) Removed prove here
|
|
568
698
|
|
|
569
699
|
export namespace Workspace {
|
|
570
700
|
export function getFullRepositoryName(ws: Workspace): string | undefined {
|
|
@@ -640,6 +770,7 @@ export interface JetBrainsConfig {
|
|
|
640
770
|
}
|
|
641
771
|
export interface JetBrainsProductConfig {
|
|
642
772
|
prebuilds?: JetBrainsPrebuilds;
|
|
773
|
+
vmoptions?: string;
|
|
643
774
|
}
|
|
644
775
|
export interface JetBrainsPrebuilds {
|
|
645
776
|
version?: "stable" | "latest" | "both";
|
package/src/usage.ts
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2022 Gitpod GmbH. All rights reserved.
|
|
3
|
+
* Licensed under the GNU Affero General Public License (AGPL).
|
|
4
|
+
* See License-AGPL.txt in the project root for license information.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { WorkspaceType } from "./protocol";
|
|
8
|
+
|
|
9
|
+
export interface BillableSession {
|
|
10
|
+
// The id of the one paying the bill
|
|
11
|
+
attributionId: string;
|
|
12
|
+
|
|
13
|
+
// Relevant for workspace type. When prebuild, shows "prebuild"
|
|
14
|
+
userId?: string;
|
|
15
|
+
teamId?: string;
|
|
16
|
+
|
|
17
|
+
instanceId: string;
|
|
18
|
+
|
|
19
|
+
workspaceId: string;
|
|
20
|
+
|
|
21
|
+
workspaceType: BillableWorkspaceType;
|
|
22
|
+
|
|
23
|
+
// "standard" or "XL"
|
|
24
|
+
workspaceClass: string;
|
|
25
|
+
|
|
26
|
+
// When the workspace started
|
|
27
|
+
startTime: number;
|
|
28
|
+
|
|
29
|
+
// When the workspace ended
|
|
30
|
+
endTime: number;
|
|
31
|
+
|
|
32
|
+
// The credits used for this session
|
|
33
|
+
credits: number;
|
|
34
|
+
|
|
35
|
+
// TODO - maybe
|
|
36
|
+
projectId?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export type BillableWorkspaceType = Omit<WorkspaceType, "probe">;
|
|
40
|
+
|
|
41
|
+
export const billableSessionDummyData: BillableSession[] = [
|
|
42
|
+
{
|
|
43
|
+
attributionId: "some-attribution-id",
|
|
44
|
+
userId: "prebuild",
|
|
45
|
+
teamId: "prebuild",
|
|
46
|
+
instanceId: "some-instance-id",
|
|
47
|
+
workspaceId: "some-workspace-id",
|
|
48
|
+
workspaceType: "prebuild",
|
|
49
|
+
workspaceClass: "XL",
|
|
50
|
+
startTime: Date.now() + -3 * 24 * 3600 * 1000, // 3 days ago
|
|
51
|
+
endTime: Date.now(),
|
|
52
|
+
credits: 320,
|
|
53
|
+
projectId: "project-123",
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
attributionId: "some-attribution-id2",
|
|
57
|
+
userId: "some-user",
|
|
58
|
+
teamId: "some-team",
|
|
59
|
+
instanceId: "some-instance-id2",
|
|
60
|
+
workspaceId: "some-workspace-id2",
|
|
61
|
+
workspaceType: "regular",
|
|
62
|
+
workspaceClass: "standard",
|
|
63
|
+
startTime: Date.now() + -5 * 24 * 3600 * 1000,
|
|
64
|
+
endTime: Date.now(),
|
|
65
|
+
credits: 130,
|
|
66
|
+
projectId: "project-123",
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
attributionId: "some-attribution-id3",
|
|
70
|
+
userId: "some-other-user",
|
|
71
|
+
teamId: "some-other-team",
|
|
72
|
+
instanceId: "some-instance-id3",
|
|
73
|
+
workspaceId: "some-workspace-id3",
|
|
74
|
+
workspaceType: "regular",
|
|
75
|
+
workspaceClass: "XL",
|
|
76
|
+
startTime: Date.now() + -5 * 24 * 3600 * 1000,
|
|
77
|
+
endTime: Date.now() + -4 * 24 * 3600 * 1000,
|
|
78
|
+
credits: 150,
|
|
79
|
+
projectId: "project-134",
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
attributionId: "some-attribution-id4",
|
|
83
|
+
userId: "some-other-user2",
|
|
84
|
+
teamId: "some-other-team2",
|
|
85
|
+
instanceId: "some-instance-id4",
|
|
86
|
+
workspaceId: "some-workspace-id4",
|
|
87
|
+
workspaceType: "regular",
|
|
88
|
+
workspaceClass: "standard",
|
|
89
|
+
startTime: Date.now() + -10 * 24 * 3600 * 1000,
|
|
90
|
+
endTime: Date.now() + -9 * 24 * 3600 * 1000,
|
|
91
|
+
credits: 330,
|
|
92
|
+
projectId: "project-137",
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
attributionId: "some-attribution-id5",
|
|
96
|
+
userId: "some-other-user3",
|
|
97
|
+
teamId: "some-other-team3",
|
|
98
|
+
instanceId: "some-instance-id5",
|
|
99
|
+
workspaceId: "some-workspace-id5",
|
|
100
|
+
workspaceType: "regular",
|
|
101
|
+
workspaceClass: "XL",
|
|
102
|
+
startTime: Date.now() + -2 * 24 * 3600 * 1000,
|
|
103
|
+
endTime: Date.now(),
|
|
104
|
+
credits: 222,
|
|
105
|
+
projectId: "project-138",
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
attributionId: "some-attribution-id6",
|
|
109
|
+
userId: "some-other-user4",
|
|
110
|
+
teamId: "some-other-team4",
|
|
111
|
+
instanceId: "some-instance-id6",
|
|
112
|
+
workspaceId: "some-workspace-id3",
|
|
113
|
+
workspaceType: "regular",
|
|
114
|
+
workspaceClass: "XL",
|
|
115
|
+
startTime: Date.now() + -7 * 24 * 3600 * 1000,
|
|
116
|
+
endTime: Date.now() + -6 * 24 * 3600 * 1000,
|
|
117
|
+
credits: 300,
|
|
118
|
+
projectId: "project-134",
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
attributionId: "some-attribution-id8",
|
|
122
|
+
userId: "some-other-user5",
|
|
123
|
+
teamId: "some-other-team5",
|
|
124
|
+
instanceId: "some-instance-id8",
|
|
125
|
+
workspaceId: "some-workspace-id3",
|
|
126
|
+
workspaceType: "regular",
|
|
127
|
+
workspaceClass: "standard",
|
|
128
|
+
startTime: Date.now() + -1 * 24 * 3600 * 1000,
|
|
129
|
+
endTime: Date.now(),
|
|
130
|
+
credits: 100,
|
|
131
|
+
projectId: "project-567",
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
attributionId: "some-attribution-id7",
|
|
135
|
+
userId: "prebuild",
|
|
136
|
+
teamId: "some-other-team7",
|
|
137
|
+
instanceId: "some-instance-id7",
|
|
138
|
+
workspaceId: "some-workspace-id7",
|
|
139
|
+
workspaceType: "prebuild",
|
|
140
|
+
workspaceClass: "XL",
|
|
141
|
+
startTime: Date.now() + -1 * 24 * 3600 * 1000,
|
|
142
|
+
endTime: Date.now(),
|
|
143
|
+
credits: 200,
|
|
144
|
+
projectId: "project-345",
|
|
145
|
+
},
|
|
146
|
+
];
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2021 Gitpod GmbH. All rights reserved.
|
|
3
|
+
* Licensed under the GNU Affero General Public License (AGPL).
|
|
4
|
+
* See License-AGPL.txt in the project root for license information.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as http from "http";
|
|
8
|
+
import * as express from "express";
|
|
9
|
+
import { injectable, postConstruct } from "inversify";
|
|
10
|
+
import { log, LogrusLogLevel } from "./logging";
|
|
11
|
+
|
|
12
|
+
export interface SetLogLevelRequest {
|
|
13
|
+
level: LogrusLogLevel;
|
|
14
|
+
}
|
|
15
|
+
export namespace SetLogLevelRequest {
|
|
16
|
+
export function is(o: any): o is SetLogLevelRequest {
|
|
17
|
+
return typeof o === "object" && "level" in o;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@injectable()
|
|
22
|
+
export class DebugApp {
|
|
23
|
+
protected _app: express.Application;
|
|
24
|
+
protected httpServer: http.Server | undefined = undefined;
|
|
25
|
+
|
|
26
|
+
@postConstruct()
|
|
27
|
+
public ctor() {
|
|
28
|
+
this._app = this.create();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
create(): express.Application {
|
|
32
|
+
const app = express();
|
|
33
|
+
|
|
34
|
+
app.use(express.json());
|
|
35
|
+
app.use(express.urlencoded({ extended: true }));
|
|
36
|
+
|
|
37
|
+
app.post("/debug/logging", (req, res) => {
|
|
38
|
+
try {
|
|
39
|
+
const levelRequest = req.body;
|
|
40
|
+
if (!SetLogLevelRequest.is(levelRequest)) {
|
|
41
|
+
res.status(400).end("not a SetLogLevelRequest");
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const newLogLevel = levelRequest.level;
|
|
46
|
+
log.setLogLevel(newLogLevel);
|
|
47
|
+
log.info("set log level", { newLogLevel });
|
|
48
|
+
res.status(200).end(JSON.stringify(levelRequest));
|
|
49
|
+
} catch (err) {
|
|
50
|
+
res.status(500).end(err);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
return app;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
public start(port: number = 6060) {
|
|
57
|
+
this.httpServer = this._app.listen(port, "localhost", () => {
|
|
58
|
+
log.info(`debug server listening on port: ${port}`);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
public async stop() {
|
|
63
|
+
const server = this.httpServer;
|
|
64
|
+
if (!server) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
return new Promise<void>((resolve) =>
|
|
68
|
+
server.close((err: any) => {
|
|
69
|
+
if (err) {
|
|
70
|
+
log.warn(`error while closing http server`, { err });
|
|
71
|
+
}
|
|
72
|
+
this.httpServer = undefined;
|
|
73
|
+
resolve();
|
|
74
|
+
}),
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
public get app(): express.Application {
|
|
79
|
+
return this._app;
|
|
80
|
+
}
|
|
81
|
+
}
|
package/src/util/logging.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
const inspect: (object: any) => string = require("util").inspect; // undefined in frontend
|
|
8
8
|
|
|
9
|
+
let plainLogging: boolean = false; // set to true during development to get non JSON output
|
|
9
10
|
let jsonLogging: boolean = false;
|
|
10
11
|
let component: string | undefined;
|
|
11
12
|
let version: string | undefined;
|
|
@@ -309,7 +310,7 @@ function makeLogItem(
|
|
|
309
310
|
}
|
|
310
311
|
|
|
311
312
|
const payload: any = payloadArgs.length == 0 ? undefined : payloadArgs.length == 1 ? payloadArgs[0] : payloadArgs;
|
|
312
|
-
const logItem
|
|
313
|
+
const logItem = {
|
|
313
314
|
// undefined fields get eliminated in JSON.stringify()
|
|
314
315
|
...reportedErrorEvent,
|
|
315
316
|
component,
|
|
@@ -321,11 +322,19 @@ function makeLogItem(
|
|
|
321
322
|
payload,
|
|
322
323
|
loggedViaConsole: calledViaConsole ? true : undefined,
|
|
323
324
|
};
|
|
325
|
+
if (plainLogging) {
|
|
326
|
+
return `[${logItem.severity}] [${logItem.component}] ${logItem.message}
|
|
327
|
+
${JSON.stringify(payload || "", undefined, " ")}
|
|
328
|
+
${error || ""}
|
|
329
|
+
`.trim();
|
|
330
|
+
}
|
|
324
331
|
let result: string = stringifyLogItem(logItem);
|
|
325
332
|
|
|
326
333
|
if (result.length > maxAllowedLogItemLength && payload !== undefined) {
|
|
327
334
|
delete logItem.payload;
|
|
328
|
-
|
|
335
|
+
(<any>(
|
|
336
|
+
logItem
|
|
337
|
+
)).payloadStub = `Payload stripped as log item was longer than ${maxAllowedLogItemLength} characters`;
|
|
329
338
|
|
|
330
339
|
result = stringifyLogItem(logItem);
|
|
331
340
|
|
package/src/util/timeutil.ts
CHANGED
|
@@ -47,6 +47,12 @@ export const orderAsc = (d1: string, d2: string): number => liftDate(d1, d2, (d1
|
|
|
47
47
|
export const liftDate1 = <T>(d1: string, f: (d1: Date) => T): T => f(new Date(d1));
|
|
48
48
|
export const liftDate = <T>(d1: string, d2: string, f: (d1: Date, d2: Date) => T): T => f(new Date(d1), new Date(d2));
|
|
49
49
|
|
|
50
|
+
export function hoursBefore(date: string, hours: number): string {
|
|
51
|
+
const result = new Date(date);
|
|
52
|
+
result.setHours(result.getHours() - hours);
|
|
53
|
+
return result.toISOString();
|
|
54
|
+
}
|
|
55
|
+
|
|
50
56
|
export function hoursLater(date: string, hours: number): string {
|
|
51
57
|
const result = new Date(date);
|
|
52
58
|
result.setHours(result.getHours() + hours);
|
package/src/workspace-cluster.ts
CHANGED
|
@@ -57,11 +57,13 @@ export type AdmissionConstraint =
|
|
|
57
57
|
| AdmissionConstraintFeaturePreview
|
|
58
58
|
| AdmissionConstraintHasPermission
|
|
59
59
|
| AdmissionConstraintHasUserLevel
|
|
60
|
-
| AdmissionConstraintHasMoreResources
|
|
60
|
+
| AdmissionConstraintHasMoreResources
|
|
61
|
+
| AdmissionConstraintHasClass;
|
|
61
62
|
export type AdmissionConstraintFeaturePreview = { type: "has-feature-preview" };
|
|
62
63
|
export type AdmissionConstraintHasPermission = { type: "has-permission"; permission: PermissionName };
|
|
63
64
|
export type AdmissionConstraintHasUserLevel = { type: "has-user-level"; level: string };
|
|
64
65
|
export type AdmissionConstraintHasMoreResources = { type: "has-more-resources" };
|
|
66
|
+
export type AdmissionConstraintHasClass = { type: "has-class"; id: string; displayName: string };
|
|
65
67
|
|
|
66
68
|
export namespace AdmissionConstraint {
|
|
67
69
|
export function is(o: any): o is AdmissionConstraint {
|