Skip to content

Commit 6d703bd

Browse files
authored
Revert "[server] remove definitely-gp (#18278) (#18316)" (#18335)
This reverts commit aeb6884.
1 parent e3ee87b commit 6d703bd

File tree

4 files changed

+176
-28
lines changed

4 files changed

+176
-28
lines changed

components/gitpod-protocol/go/gitpod-service.go

+1
Original file line numberDiff line numberDiff line change
@@ -1702,6 +1702,7 @@ type WorkspaceConfig struct {
17021702
// Where the config object originates from.
17031703
//
17041704
// repo - from the repository
1705+
// definitely-gp - from github.com/gitpod-io/definitely-gp
17051706
// derived - computed based on analyzing the repository
17061707
// default - our static catch-all default config
17071708
Origin string `json:"_origin,omitempty"`

components/gitpod-protocol/src/protocol.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -970,11 +970,12 @@ export interface WorkspaceConfig {
970970
* Where the config object originates from.
971971
*
972972
* repo - from the repository
973+
* definitly-gp - from github.com/gitpod-io/definitely-gp
973974
* derived - computed based on analyzing the repository
974975
* additional-content - config comes from additional content, usually provided through the project's configuration
975976
* default - our static catch-all default config
976977
*/
977-
_origin?: "repo" | "derived" | "additional-content" | "default";
978+
_origin?: "repo" | "definitely-gp" | "derived" | "additional-content" | "default";
978979

979980
/**
980981
* Set of automatically infered feature flags. That's not something the user can set, but

components/server/src/workspace/config-provider.ts

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

7-
import * as crypto from "crypto";
87
import { inject, injectable } from "inversify";
8+
import fetch from "node-fetch";
99
import * as path from "path";
10+
import * as crypto from "crypto";
1011

12+
import { log, LogContext } from "@gitpod/gitpod-protocol/lib/util/logging";
1113
import {
12-
AdditionalContentContext,
13-
Commit,
14+
User,
15+
WorkspaceConfig,
1416
CommitContext,
17+
Repository,
18+
ImageConfigString,
1519
ExternalImageConfigFile,
1620
ImageConfigFile,
17-
ImageConfigString,
21+
Commit,
1822
NamedWorkspaceFeatureFlag,
19-
ProjectConfig,
20-
Repository,
21-
User,
23+
AdditionalContentContext,
2224
WithDefaultConfig,
23-
WorkspaceConfig,
25+
ProjectConfig,
2426
} from "@gitpod/gitpod-protocol";
2527
import { GitpodFileParser } from "@gitpod/gitpod-protocol/lib/gitpod-file-parser";
26-
import { log, LogContext } from "@gitpod/gitpod-protocol/lib/util/logging";
2728

28-
import { TraceContext } from "@gitpod/gitpod-protocol/lib/util/tracing";
29+
import { MaybeContent } from "../repohost/file-provider";
30+
import { ConfigurationService } from "../config/configuration-service";
2931
import { HostContextProvider } from "../auth/host-context-provider";
32+
import { AuthorizationService } from "../user/authorization-service";
33+
import { TraceContext } from "@gitpod/gitpod-protocol/lib/util/tracing";
3034
import { Config } from "../config";
31-
import { ConfigurationService } from "../config/configuration-service";
35+
import { EntitlementService } from "../billing/entitlement-service";
36+
import { TeamDB } from "@gitpod/gitpod-db/lib";
3237

3338
const POD_PATH_WORKSPACE_BASE = "/workspace";
3439

3540
@injectable()
3641
export class ConfigProvider {
37-
constructor(
38-
@inject(GitpodFileParser) private readonly gitpodParser: GitpodFileParser,
39-
@inject(HostContextProvider) private readonly hostContextProvider: HostContextProvider,
40-
@inject(Config) private readonly config: Config,
41-
@inject(ConfigurationService) private readonly configurationService: ConfigurationService,
42-
) {}
42+
static readonly DEFINITELY_GP_REPO: Repository = {
43+
host: "github.com",
44+
owner: "gitpod-io",
45+
name: "definitely-gp",
46+
cloneUrl: "https://github.com/gitpod-io/definitely-gp",
47+
};
48+
49+
@inject(GitpodFileParser) protected readonly gitpodParser: GitpodFileParser;
50+
@inject(HostContextProvider) protected readonly hostContextProvider: HostContextProvider;
51+
@inject(AuthorizationService) protected readonly authService: AuthorizationService;
52+
@inject(Config) protected readonly config: Config;
53+
@inject(ConfigurationService) protected readonly configurationService: ConfigurationService;
54+
@inject(EntitlementService) protected readonly entitlementService: EntitlementService;
55+
@inject(TeamDB) protected readonly teamDB: TeamDB;
4356

4457
public async fetchConfig(
4558
ctx: TraceContext,
@@ -83,10 +96,15 @@ export class ConfigProvider {
8396
config.image = this.config.workspaceDefaults.workspaceImage;
8497
} else if (ImageConfigFile.is(config.image)) {
8598
const dockerfilePath = [configBasePath, config.image.file].filter((s) => !!s).join("/");
86-
const repo = commit.repository;
87-
const rev = commit.revision;
99+
let repo = commit.repository;
100+
let rev = commit.revision;
88101
const image = config.image!;
89102

103+
if (config._origin === "definitely-gp") {
104+
repo = ConfigProvider.DEFINITELY_GP_REPO;
105+
rev = "master";
106+
image.file = dockerfilePath;
107+
}
90108
if (!(AdditionalContentContext.is(commit) && commit.additionalFiles[dockerfilePath])) {
91109
config.image = <ExternalImageConfigFile>{
92110
...image,
@@ -125,7 +143,7 @@ export class ConfigProvider {
125143
}
126144
}
127145

128-
private async fetchCustomConfig(
146+
protected async fetchCustomConfig(
129147
ctx: TraceContext,
130148
user: User,
131149
commit: CommitContext,
@@ -136,7 +154,7 @@ export class ConfigProvider {
136154

137155
try {
138156
let customConfig: WorkspaceConfig | undefined;
139-
const configBasePath = "";
157+
let configBasePath = "";
140158
if (AdditionalContentContext.is(commit) && commit.additionalFiles[".gitpod.yml"]) {
141159
customConfigString = commit.additionalFiles[".gitpod.yml"];
142160
const parseResult = this.gitpodParser.parse(customConfigString);
@@ -165,6 +183,21 @@ export class ConfigProvider {
165183
customConfigString = await contextRepoConfig;
166184
let origin: WorkspaceConfig["_origin"] = "repo";
167185

186+
if (!customConfigString) {
187+
/* We haven't found a Gitpod configuration file in the context repo - check definitely-gp.
188+
*
189+
* In case we had found a config file here, we'd still be checking the definitely GP repo, just to save some time.
190+
* While all those checks will be in vain, they should not leak memory either as they'll simply
191+
* be resolved and garbage collected.
192+
*/
193+
const definitelyGpConfig = this.fetchExternalGitpodFileContent({ span }, commit.repository);
194+
const { content, basePath } = await definitelyGpConfig;
195+
customConfigString = content;
196+
// We do not only care about the config itself but also where we got it from
197+
configBasePath = basePath;
198+
origin = "definitely-gp";
199+
}
200+
168201
if (!customConfigString) {
169202
const inferredConfig = this.configurationService.guessRepositoryConfiguration(
170203
{ span },
@@ -215,7 +248,7 @@ export class ConfigProvider {
215248
};
216249
}
217250

218-
private async fetchWorkspaceImageSourceDocker(
251+
protected async fetchWorkspaceImageSourceDocker(
219252
ctx: TraceContext,
220253
repository: Repository,
221254
revisionOrTagOrBranch: string,
@@ -254,7 +287,101 @@ export class ConfigProvider {
254287
}
255288
}
256289

257-
private async validateConfig(config: WorkspaceConfig, user: User): Promise<void> {
290+
protected async fillInDefaultLocations(
291+
cfg: WorkspaceConfig | undefined,
292+
inferredConfig: Promise<WorkspaceConfig | undefined>,
293+
): Promise<void> {
294+
if (!cfg) {
295+
// there is no config - return
296+
return;
297+
}
298+
299+
if (!cfg.checkoutLocation) {
300+
const inferredCfg = await inferredConfig;
301+
if (inferredCfg) {
302+
cfg.checkoutLocation = inferredCfg.checkoutLocation;
303+
}
304+
}
305+
if (!cfg.workspaceLocation) {
306+
const inferredCfg = await inferredConfig;
307+
if (inferredCfg) {
308+
cfg.workspaceLocation = inferredCfg.workspaceLocation;
309+
}
310+
}
311+
}
312+
313+
protected async fetchExternalGitpodFileContent(
314+
ctx: TraceContext,
315+
repository: Repository,
316+
): Promise<{ content: MaybeContent; basePath: string }> {
317+
const span = TraceContext.startSpan("fetchExternalGitpodFileContent", ctx);
318+
span.setTag("repo", `${repository.owner}/${repository.name}`);
319+
320+
if (this.config.definitelyGpDisabled) {
321+
span.finish();
322+
return {
323+
content: undefined,
324+
basePath: `${repository.name}`,
325+
};
326+
}
327+
328+
try {
329+
const ownerConfigBasePath = `${repository.name}/${repository.owner}`;
330+
const baseConfigBasePath = `${repository.name}`;
331+
332+
const possibleConfigs = [
333+
[this.fetchDefinitelyGpContent({ span }, `${ownerConfigBasePath}/.gitpod.yml`), ownerConfigBasePath],
334+
[this.fetchDefinitelyGpContent({ span }, `${ownerConfigBasePath}/.gitpod`), ownerConfigBasePath],
335+
[this.fetchDefinitelyGpContent({ span }, `${baseConfigBasePath}/.gitpod.yml`), baseConfigBasePath],
336+
[this.fetchDefinitelyGpContent({ span }, `${baseConfigBasePath}/.gitpod`), baseConfigBasePath],
337+
];
338+
for (const [configPromise, basePath] of possibleConfigs) {
339+
const ownerConfig = await configPromise;
340+
if (ownerConfig !== undefined) {
341+
return {
342+
content: ownerConfig,
343+
basePath: basePath as string,
344+
};
345+
}
346+
}
347+
return {
348+
content: undefined,
349+
basePath: baseConfigBasePath,
350+
};
351+
} catch (e) {
352+
TraceContext.setError({ span }, e);
353+
throw e;
354+
} finally {
355+
span.finish();
356+
}
357+
}
358+
359+
protected async fetchDefinitelyGpContent(ctx: TraceContext, filePath: string) {
360+
const span = TraceContext.startSpan("fetchDefinitelyGpContent", ctx);
361+
span.setTag("filePath", filePath);
362+
363+
try {
364+
const url = `https://raw.githubusercontent.com/gitpod-io/definitely-gp/master/${filePath}`;
365+
const response = await fetch(url, {
366+
timeout: 10000,
367+
method: "GET",
368+
});
369+
let content;
370+
if (response.ok) {
371+
try {
372+
content = await response.text();
373+
} catch {}
374+
}
375+
return content;
376+
} catch (e) {
377+
TraceContext.setError({ span }, e);
378+
throw e;
379+
} finally {
380+
span.finish();
381+
}
382+
}
383+
384+
protected async validateConfig(config: WorkspaceConfig, user: User): Promise<void> {
258385
// Make sure the projectRoot does not leave POD_PATH_WORKSPACE_BASE as that's a common
259386
// assumption throughout the code (e.g. ws-daemon)
260387
const checkoutLocation = config.checkoutLocation;
@@ -280,7 +407,7 @@ export class ConfigProvider {
280407
}
281408
}
282409

283-
private leavesWorkspaceBase(normalizedPath: string) {
410+
protected leavesWorkspaceBase(normalizedPath: string) {
284411
const pathSegments = normalizedPath.split(path.sep);
285412
return normalizedPath.includes("..") || pathSegments.slice(0, 2).join("/") != POD_PATH_WORKSPACE_BASE;
286413
}

components/server/src/workspace/image-source-provider.ts

+22-3
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,15 @@ import {
1414
WorkspaceImageSourceReference,
1515
WorkspaceImageSourceDocker,
1616
ImageConfigFile,
17+
ExternalImageConfigFile,
1718
User,
1819
AdditionalContentContext,
1920
} from "@gitpod/gitpod-protocol";
2021
import { createHash } from "crypto";
2122

2223
@injectable()
2324
export class ImageSourceProvider {
24-
constructor(@inject(HostContextProvider) private readonly hostContextProvider: HostContextProvider) {}
25+
@inject(HostContextProvider) protected readonly hostContextProvider: HostContextProvider;
2526

2627
public async getImageSource(
2728
ctx: TraceContext,
@@ -35,7 +36,25 @@ export class ImageSourceProvider {
3536
let result: WorkspaceImageSource;
3637

3738
const imgcfg = config.image;
38-
if (ImageConfigFile.is(imgcfg)) {
39+
if (ExternalImageConfigFile.is(imgcfg)) {
40+
// we're asked to pull the Dockerfile from a repo possibly different than the one we're opening a workspace for (e.g. definitely-gp).
41+
const repository = imgcfg.externalSource.repository;
42+
const hostContext = this.hostContextProvider.get(repository.host);
43+
if (!hostContext || !hostContext.services) {
44+
throw new Error(`Cannot fetch workspace image source for host: ${repository.host}`);
45+
}
46+
const lastDockerFileSha = await hostContext.services.fileProvider.getLastChangeRevision(
47+
repository,
48+
imgcfg.externalSource.revision,
49+
user,
50+
imgcfg.file,
51+
);
52+
result = <WorkspaceImageSourceDocker>{
53+
dockerFilePath: imgcfg.file,
54+
dockerFileSource: imgcfg.externalSource,
55+
dockerFileHash: lastDockerFileSha,
56+
};
57+
} else if (ImageConfigFile.is(imgcfg)) {
3958
// if a dockerfile sits in the additional content we use its contents sha
4059
if (
4160
AdditionalContentContext.is(context) &&
@@ -81,7 +100,7 @@ export class ImageSourceProvider {
81100
}
82101
}
83102

84-
private getContentSHA(contents: string): string {
103+
protected getContentSHA(contents: string): string {
85104
return createHash("sha256").update(contents).digest("hex");
86105
}
87106
}

0 commit comments

Comments
 (0)