Skip to content

Commit f8086b9

Browse files
svenefftingeroboquat
authored andcommitted
[server] use owner and repo name for workspace id
This change introduces optional arguments in generateWorkspaceId for the first two segments. And makes use of it in workspace factory using the repos org/group and name. fixes #4129
1 parent 9e69e6e commit f8086b9

File tree

8 files changed

+59
-14
lines changed

8 files changed

+59
-14
lines changed

components/gitpod-protocol/src/util/generate-workspace-id.spec.ts

+16
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,21 @@ const expect = chai.expect
2727
expect(longestName.length <= 36, `"${longestName}" is longer than 36 chars (${longestName.length})`).to.be.true;
2828
}
2929

30+
@test public async testCustomName() {
31+
const data = [
32+
['foo','bar','foo-bar-'],
33+
['f','bar','.{2,16}-bar-'],
34+
['gitpod-io','gitpod','gitpodio-gitpod-'],
35+
['this is rather long and has some "§$"% special chars','also here pretty long and needs abbreviation','thisisratherlon-alsohere-'],
36+
['breatheco-de', 'python-flask-api-tutorial', 'breathecode-pythonflaska-'],
37+
]
38+
for (const d of data) {
39+
const id = await generateWorkspaceID(d[0], d[1]);
40+
expect(id).match(new RegExp("^"+d[2]));
41+
expect(new GitpodHostUrl().withWorkspacePrefix(id, "eu").workspaceId).to.equal(id);
42+
expect(id.length <= 36, `"${id}" is longer than 36 chars (${id.length})`).to.be.true;
43+
}
44+
}
45+
3046
}
3147
module.exports = new TestGenerateWorkspaceId()

components/gitpod-protocol/src/util/generate-workspace-id.ts

+19-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,25 @@
55
*/
66
import randomNumber = require("random-number-csprng");
77

8-
export async function generateWorkspaceID(): Promise<string> {
9-
return (await random(colors))+'-'+(await random(animals))+'-'+(await random(characters, 8));
8+
export async function generateWorkspaceID(firstSegment?: string, secondSegment?: string): Promise<string> {
9+
const firstSeg = clean(firstSegment) || await random(colors);
10+
const secSeg = clean(secondSegment, Math.min(15, 23 - firstSeg.length)) || await random(animals);
11+
return firstSeg+'-'+secSeg+'-'+(await random(characters, 11));
12+
}
13+
14+
function clean(segment: string | undefined, maxChars: number = 15) {
15+
if (!segment) {
16+
return undefined;
17+
}
18+
let result = '';
19+
for (let i =0; i < segment.length; i++) {
20+
if (characters.indexOf(segment[i]) !== -1) {
21+
result += segment[i];
22+
}
23+
}
24+
if (result.length >= 2) {
25+
return result.substring(0, maxChars);
26+
}
1027
}
1128

1229
async function random(array: string[], length: number = 1): Promise<string> {

components/gitpod-protocol/src/util/gitpod-host-url.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@ export interface UrlChange {
1212
}
1313
export type UrlUpdate = UrlChange | Partial<URL>;
1414

15-
const basewoWkspaceIDRegex = "(([a-f][0-9a-f]{7}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})|([0-9a-z]{2,16}-[0-9a-z]{2,16}-[0-9a-z]{8}))";
15+
const baseWorkspaceIDRegex = "(([a-f][0-9a-f]{7}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})|([0-9a-z]{2,16}-[0-9a-z]{2,16}-[0-9a-z]{8,11}))";
1616

1717
// this pattern matches v4 UUIDs as well as the new generated workspace ids (e.g. pink-panda-ns35kd21)
18-
const workspaceIDRegex = RegExp(`^${basewoWkspaceIDRegex}$`);
18+
const workspaceIDRegex = RegExp(`^${baseWorkspaceIDRegex}$`);
1919

2020
// this pattern matches URL prefixes of workspaces
21-
const workspaceUrlPrefixRegex = RegExp(`^([0-9]{4,6}-)?${basewoWkspaceIDRegex}\\.`);
21+
const workspaceUrlPrefixRegex = RegExp(`^([0-9]{4,6}-)?${baseWorkspaceIDRegex}\\.`);
2222

2323
export class GitpodHostUrl {
2424
readonly url: URL;

components/gitpod-protocol/src/util/parse-workspace-id.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* See License-AGPL.txt in the project root for license information.
55
*/
66

7-
const REGEX_WORKSPACE_ID = /[0-9a-z]{2,16}-[0-9a-z]{2,16}-[0-9a-z]{8}/;
7+
const REGEX_WORKSPACE_ID = /[0-9a-z]{2,16}-[0-9a-z]{2,16}-[0-9a-z]{8,11}/;
88
const REGEX_WORKSPACE_ID_EXACT = new RegExp(`^${REGEX_WORKSPACE_ID.source}$`);
99
// We need to parse the workspace id precisely here to get the case '<some-str>-<port>-<wsid>.ws.' right
1010
const REGEX_WORKSPACE_ID_FROM_HOSTNAME = new RegExp(`(${REGEX_WORKSPACE_ID.source})\.ws`);

components/local-app/main.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ func run(origin, sshConfig string, apiPort int, allowCORSFromPort bool, autoTunn
162162
return err
163163
}
164164
wsHostRegex := "(\\.[^.]+)\\." + strings.ReplaceAll(originURL.Host, ".", "\\.")
165-
wsHostRegex = "([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}|[0-9a-z]{2,16}-[0-9a-z]{2,16}-[0-9a-z]{8})" + wsHostRegex
165+
wsHostRegex = "([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}|[0-9a-z]{2,16}-[0-9a-z]{2,16}-[0-9a-z]{8,11})" + wsHostRegex
166166
if allowCORSFromPort {
167167
wsHostRegex = "([0-9]+)-" + wsHostRegex
168168
}

components/server/ee/src/workspace/workspace-factory.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import { LicenseEvaluator } from '@gitpod/licensor/lib';
1414
import { Feature } from '@gitpod/licensor/lib/api';
1515
import { ResponseError } from 'vscode-jsonrpc';
1616
import { ErrorCodes } from '@gitpod/gitpod-protocol/lib/messaging/error';
17-
import { generateWorkspaceID } from '@gitpod/gitpod-protocol/lib/util/generate-workspace-id';
1817
import { HostContextProvider } from '../../../src/auth/host-context-provider';
1918
import { RepoURL } from '../../../src/repohost';
2019

@@ -220,7 +219,7 @@ export class WorkspaceFactoryEE extends WorkspaceFactory {
220219
}
221220
}
222221

223-
const id = await generateWorkspaceID();
222+
const id = await this.generateWorkspaceID(context);
224223
const newWs: Workspace = {
225224
id,
226225
type: "regular",

components/server/src/workspace/workspace-factory.ts

+17-4
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@
55
*/
66

77
import { DBWithTracing, TracedWorkspaceDB, WorkspaceDB, ProjectDB, TeamDB } from '@gitpod/gitpod-db/lib';
8-
import { AdditionalContentContext, CommitContext, IssueContext, PullRequestContext, Repository, SnapshotContext, User, Workspace, WorkspaceConfig, WorkspaceContext, WorkspaceProbeContext } from '@gitpod/gitpod-protocol';
8+
import { AdditionalContentContext, CommitContext, IssueContext, PrebuiltWorkspaceContext, PullRequestContext, Repository, SnapshotContext, User, Workspace, WorkspaceConfig, WorkspaceContext, WorkspaceProbeContext } from '@gitpod/gitpod-protocol';
99
import { ErrorCodes } from '@gitpod/gitpod-protocol/lib/messaging/error';
1010
import { generateWorkspaceID } from '@gitpod/gitpod-protocol/lib/util/generate-workspace-id';
1111
import { log } from '@gitpod/gitpod-protocol/lib/util/logging';
1212
import { TraceContext } from '@gitpod/gitpod-protocol/lib/util/tracing';
1313
import { inject, injectable } from 'inversify';
1414
import { ResponseError } from 'vscode-jsonrpc';
15+
import { RepoURL } from '../repohost';
1516
import { ConfigProvider } from './config-provider';
1617
import { ImageSourceProvider } from './image-source-provider';
1718

@@ -55,7 +56,7 @@ export class WorkspaceFactory {
5556
// Basically we're using the raw alpine image bait-and-switch style without adding the GP layer.
5657
const imageSource = await this.imageSourceProvider.getImageSource(ctx, user, null as any, config);
5758

58-
const id = await generateWorkspaceID();
59+
const id = await this.generateWorkspaceID(context);
5960
const date = new Date().toISOString();
6061
const newWs: Workspace = {
6162
id,
@@ -94,7 +95,7 @@ export class WorkspaceFactory {
9495
throw new Error(`The original workspace has been deleted - cannot open this snapshot.`);
9596
}
9697

97-
const id = await generateWorkspaceID();
98+
const id = await this.generateWorkspaceID(context);
9899
const date = new Date().toISOString();
99100
const newWs = <Workspace>{
100101
id,
@@ -169,7 +170,7 @@ export class WorkspaceFactory {
169170
}
170171
}
171172

172-
const id = await generateWorkspaceID();
173+
const id = await this.generateWorkspaceID(context);
173174
const newWs: Workspace = {
174175
id,
175176
type: "regular",
@@ -210,4 +211,16 @@ export class WorkspaceFactory {
210211
return context.title;
211212
}
212213

214+
protected async generateWorkspaceID(context: WorkspaceContext): Promise<string> {
215+
let ctx = context;
216+
if (PrebuiltWorkspaceContext.is(context)) {
217+
ctx = context.originalContext;
218+
}
219+
if (CommitContext.is(ctx)) {
220+
const parsed = RepoURL.parseRepoUrl(ctx.repository.cloneUrl);
221+
return await generateWorkspaceID(parsed?.owner, parsed?.repo);
222+
}
223+
return await generateWorkspaceID();
224+
}
225+
213226
}

components/ws-proxy/pkg/proxy/workspacerouter.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ const (
3131
forwardedHostnameHeader = "x-wsproxy-host"
3232

3333
// This pattern matches v4 UUIDs as well as the new generated workspace ids (e.g. pink-panda-ns35kd21).
34-
workspaceIDRegex = "(?P<" + workspaceIDIdentifier + ">[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}|[0-9a-z]{2,16}-[0-9a-z]{2,16}-[0-9a-z]{8})"
34+
workspaceIDRegex = "(?P<" + workspaceIDIdentifier + ">[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}|[0-9a-z]{2,16}-[0-9a-z]{2,16}-[0-9a-z]{8,11})"
3535
workspacePortRegex = "(?P<" + workspacePortIdentifier + ">[0-9]+)-"
3636
)
3737

0 commit comments

Comments
 (0)