Skip to content

Commit

Permalink
[server] remove definitely-gp (#18278)
Browse files Browse the repository at this point in the history
  • Loading branch information
svenefftinge authored Jul 13, 2023
1 parent 3e8e55f commit 0ea1b3a
Show file tree
Hide file tree
Showing 4 changed files with 22 additions and 241 deletions.
1 change: 0 additions & 1 deletion components/gitpod-protocol/go/gitpod-service.go
Original file line number Diff line number Diff line change
Expand Up @@ -1702,7 +1702,6 @@ type WorkspaceConfig struct {
// Where the config object originates from.
//
// repo - from the repository
// definitely-gp - from github.com/gitpod-io/definitely-gp
// derived - computed based on analyzing the repository
// default - our static catch-all default config
Origin string `json:"_origin,omitempty"`
Expand Down
11 changes: 1 addition & 10 deletions components/gitpod-protocol/src/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -970,12 +970,11 @@ export interface WorkspaceConfig {
* Where the config object originates from.
*
* repo - from the repository
* definitly-gp - from github.com/gitpod-io/definitely-gp
* derived - computed based on analyzing the repository
* additional-content - config comes from additional content, usually provided through the project's configuration
* default - our static catch-all default config
*/
_origin?: "repo" | "definitely-gp" | "derived" | "additional-content" | "default";
_origin?: "repo" | "derived" | "additional-content" | "default";

/**
* Set of automatically infered feature flags. That's not something the user can set, but
Expand Down Expand Up @@ -1170,14 +1169,6 @@ export namespace ImageConfigFile {
return typeof config === "object" && "file" in config;
}
}
export interface ExternalImageConfigFile extends ImageConfigFile {
externalSource: Commit;
}
export namespace ExternalImageConfigFile {
export function is(config: any | undefined): config is ExternalImageConfigFile {
return typeof config === "object" && "file" in config && "externalSource" in config;
}
}

export interface WorkspaceContext {
title: string;
Expand Down
226 changes: 18 additions & 208 deletions components/server/src/workspace/config-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,55 +4,38 @@
* See License.AGPL.txt in the project root for license information.
*/

import * as crypto from "crypto";
import { inject, injectable } from "inversify";
import fetch from "node-fetch";
import * as path from "path";
import * as crypto from "crypto";

import { log, LogContext } from "@gitpod/gitpod-protocol/lib/util/logging";
import {
User,
WorkspaceConfig,
AdditionalContentContext,
CommitContext,
Repository,
ImageConfigString,
ExternalImageConfigFile,
ImageConfigFile,
Commit,
NamedWorkspaceFeatureFlag,
AdditionalContentContext,
WithDefaultConfig,
ProjectConfig,
User,
WithDefaultConfig,
WorkspaceConfig,
} from "@gitpod/gitpod-protocol";
import { GitpodFileParser } from "@gitpod/gitpod-protocol/lib/gitpod-file-parser";
import { log, LogContext } from "@gitpod/gitpod-protocol/lib/util/logging";

import { MaybeContent } from "../repohost/file-provider";
import { ConfigurationService } from "../config/configuration-service";
import { HostContextProvider } from "../auth/host-context-provider";
import { AuthorizationService } from "../user/authorization-service";
import { TraceContext } from "@gitpod/gitpod-protocol/lib/util/tracing";
import { HostContextProvider } from "../auth/host-context-provider";
import { Config } from "../config";
import { EntitlementService } from "../billing/entitlement-service";
import { TeamDB } from "@gitpod/gitpod-db/lib";
import { ConfigurationService } from "../config/configuration-service";

const POD_PATH_WORKSPACE_BASE = "/workspace";

@injectable()
export class ConfigProvider {
static readonly DEFINITELY_GP_REPO: Repository = {
host: "github.com",
owner: "gitpod-io",
name: "definitely-gp",
cloneUrl: "https://github.com/gitpod-io/definitely-gp",
};

@inject(GitpodFileParser) protected readonly gitpodParser: GitpodFileParser;
@inject(HostContextProvider) protected readonly hostContextProvider: HostContextProvider;
@inject(AuthorizationService) protected readonly authService: AuthorizationService;
@inject(Config) protected readonly config: Config;
@inject(ConfigurationService) protected readonly configurationService: ConfigurationService;
@inject(EntitlementService) protected readonly entitlementService: EntitlementService;
@inject(TeamDB) protected readonly teamDB: TeamDB;
constructor(
@inject(GitpodFileParser) private readonly gitpodParser: GitpodFileParser,
@inject(HostContextProvider) private readonly hostContextProvider: HostContextProvider,
@inject(Config) private readonly config: Config,
@inject(ConfigurationService) private readonly configurationService: ConfigurationService,
) {}

public async fetchConfig(
ctx: TraceContext,
Expand All @@ -64,7 +47,6 @@ export class ConfigProvider {
commit,
});
const logContext: LogContext = { userId: user.id };
let configBasePath = "";
try {
let customConfig: WorkspaceConfig | undefined;
let literalConfig: ProjectConfig | undefined;
Expand All @@ -73,7 +55,6 @@ export class ConfigProvider {
const cc = await this.fetchCustomConfig(ctx, user, commit);
if (!!cc) {
customConfig = cc.customConfig;
configBasePath = cc.configBasePath;
literalConfig = cc.literalConfig;
}
}
Expand All @@ -94,29 +75,6 @@ export class ConfigProvider {
const config = customConfig;
if (!config.image) {
config.image = this.config.workspaceDefaults.workspaceImage;
} else if (ImageConfigFile.is(config.image)) {
const dockerfilePath = [configBasePath, config.image.file].filter((s) => !!s).join("/");
let repo = commit.repository;
let rev = commit.revision;
const image = config.image!;

if (config._origin === "definitely-gp") {
repo = ConfigProvider.DEFINITELY_GP_REPO;
rev = "master";
image.file = dockerfilePath;
}
if (!(AdditionalContentContext.is(commit) && commit.additionalFiles[dockerfilePath])) {
config.image = <ExternalImageConfigFile>{
...image,
externalSource: await this.fetchWorkspaceImageSourceDocker(
{ span },
repo,
rev,
user,
dockerfilePath,
),
};
}
}

config.vscode = {
Expand All @@ -143,7 +101,7 @@ export class ConfigProvider {
}
}

protected async fetchCustomConfig(
private async fetchCustomConfig(
ctx: TraceContext,
user: User,
commit: CommitContext,
Expand All @@ -154,7 +112,7 @@ export class ConfigProvider {

try {
let customConfig: WorkspaceConfig | undefined;
let configBasePath = "";
const configBasePath = "";
if (AdditionalContentContext.is(commit) && commit.additionalFiles[".gitpod.yml"]) {
customConfigString = commit.additionalFiles[".gitpod.yml"];
const parseResult = this.gitpodParser.parse(customConfigString);
Expand Down Expand Up @@ -183,21 +141,6 @@ export class ConfigProvider {
customConfigString = await contextRepoConfig;
let origin: WorkspaceConfig["_origin"] = "repo";

if (!customConfigString) {
/* We haven't found a Gitpod configuration file in the context repo - check definitely-gp.
*
* In case we had found a config file here, we'd still be checking the definitely GP repo, just to save some time.
* While all those checks will be in vain, they should not leak memory either as they'll simply
* be resolved and garbage collected.
*/
const definitelyGpConfig = this.fetchExternalGitpodFileContent({ span }, commit.repository);
const { content, basePath } = await definitelyGpConfig;
customConfigString = content;
// We do not only care about the config itself but also where we got it from
configBasePath = basePath;
origin = "definitely-gp";
}

if (!customConfigString) {
const inferredConfig = this.configurationService.guessRepositoryConfiguration(
{ span },
Expand Down Expand Up @@ -248,140 +191,7 @@ export class ConfigProvider {
};
}

protected async fetchWorkspaceImageSourceDocker(
ctx: TraceContext,
repository: Repository,
revisionOrTagOrBranch: string,
user: User,
dockerFilePath: string,
): Promise<Commit> {
const span = TraceContext.startSpan("fetchWorkspaceImageSourceDocker", ctx);
span.addTags({
repository,
revisionOrTagOrBranch,
dockerFilePath,
});

try {
const host = repository.host;
const hostContext = this.hostContextProvider.get(host);
if (!hostContext || !hostContext.services) {
throw new Error(`Cannot fetch workspace image source for host: ${host}`);
}
const repoHost = hostContext.services;
const lastDockerFileSha = await repoHost.fileProvider.getLastChangeRevision(
repository,
revisionOrTagOrBranch,
user,
dockerFilePath,
);
return {
repository,
revision: lastDockerFileSha,
};
} catch (e) {
TraceContext.setError({ span }, e);
throw e;
} finally {
span.finish();
}
}

protected async fillInDefaultLocations(
cfg: WorkspaceConfig | undefined,
inferredConfig: Promise<WorkspaceConfig | undefined>,
): Promise<void> {
if (!cfg) {
// there is no config - return
return;
}

if (!cfg.checkoutLocation) {
const inferredCfg = await inferredConfig;
if (inferredCfg) {
cfg.checkoutLocation = inferredCfg.checkoutLocation;
}
}
if (!cfg.workspaceLocation) {
const inferredCfg = await inferredConfig;
if (inferredCfg) {
cfg.workspaceLocation = inferredCfg.workspaceLocation;
}
}
}

protected async fetchExternalGitpodFileContent(
ctx: TraceContext,
repository: Repository,
): Promise<{ content: MaybeContent; basePath: string }> {
const span = TraceContext.startSpan("fetchExternalGitpodFileContent", ctx);
span.setTag("repo", `${repository.owner}/${repository.name}`);

if (this.config.definitelyGpDisabled) {
span.finish();
return {
content: undefined,
basePath: `${repository.name}`,
};
}

try {
const ownerConfigBasePath = `${repository.name}/${repository.owner}`;
const baseConfigBasePath = `${repository.name}`;

const possibleConfigs = [
[this.fetchDefinitelyGpContent({ span }, `${ownerConfigBasePath}/.gitpod.yml`), ownerConfigBasePath],
[this.fetchDefinitelyGpContent({ span }, `${ownerConfigBasePath}/.gitpod`), ownerConfigBasePath],
[this.fetchDefinitelyGpContent({ span }, `${baseConfigBasePath}/.gitpod.yml`), baseConfigBasePath],
[this.fetchDefinitelyGpContent({ span }, `${baseConfigBasePath}/.gitpod`), baseConfigBasePath],
];
for (const [configPromise, basePath] of possibleConfigs) {
const ownerConfig = await configPromise;
if (ownerConfig !== undefined) {
return {
content: ownerConfig,
basePath: basePath as string,
};
}
}
return {
content: undefined,
basePath: baseConfigBasePath,
};
} catch (e) {
TraceContext.setError({ span }, e);
throw e;
} finally {
span.finish();
}
}

protected async fetchDefinitelyGpContent(ctx: TraceContext, filePath: string) {
const span = TraceContext.startSpan("fetchDefinitelyGpContent", ctx);
span.setTag("filePath", filePath);

try {
const url = `https://raw.githubusercontent.com/gitpod-io/definitely-gp/master/${filePath}`;
const response = await fetch(url, {
timeout: 10000,
method: "GET",
});
let content;
if (response.ok) {
try {
content = await response.text();
} catch {}
}
return content;
} catch (e) {
TraceContext.setError({ span }, e);
throw e;
} finally {
span.finish();
}
}

protected async validateConfig(config: WorkspaceConfig, user: User): Promise<void> {
private async validateConfig(config: WorkspaceConfig, user: User): Promise<void> {
// Make sure the projectRoot does not leave POD_PATH_WORKSPACE_BASE as that's a common
// assumption throughout the code (e.g. ws-daemon)
const checkoutLocation = config.checkoutLocation;
Expand All @@ -407,7 +217,7 @@ export class ConfigProvider {
}
}

protected leavesWorkspaceBase(normalizedPath: string) {
private leavesWorkspaceBase(normalizedPath: string) {
const pathSegments = normalizedPath.split(path.sep);
return normalizedPath.includes("..") || pathSegments.slice(0, 2).join("/") != POD_PATH_WORKSPACE_BASE;
}
Expand Down
25 changes: 3 additions & 22 deletions components/server/src/workspace/image-source-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,14 @@ import {
WorkspaceImageSourceReference,
WorkspaceImageSourceDocker,
ImageConfigFile,
ExternalImageConfigFile,
User,
AdditionalContentContext,
} from "@gitpod/gitpod-protocol";
import { createHash } from "crypto";

@injectable()
export class ImageSourceProvider {
@inject(HostContextProvider) protected readonly hostContextProvider: HostContextProvider;
constructor(@inject(HostContextProvider) private readonly hostContextProvider: HostContextProvider) {}

public async getImageSource(
ctx: TraceContext,
Expand All @@ -36,25 +35,7 @@ export class ImageSourceProvider {
let result: WorkspaceImageSource;

const imgcfg = config.image;
if (ExternalImageConfigFile.is(imgcfg)) {
// 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).
const repository = imgcfg.externalSource.repository;
const hostContext = this.hostContextProvider.get(repository.host);
if (!hostContext || !hostContext.services) {
throw new Error(`Cannot fetch workspace image source for host: ${repository.host}`);
}
const lastDockerFileSha = await hostContext.services.fileProvider.getLastChangeRevision(
repository,
imgcfg.externalSource.revision,
user,
imgcfg.file,
);
result = <WorkspaceImageSourceDocker>{
dockerFilePath: imgcfg.file,
dockerFileSource: imgcfg.externalSource,
dockerFileHash: lastDockerFileSha,
};
} else if (ImageConfigFile.is(imgcfg)) {
if (ImageConfigFile.is(imgcfg)) {
// if a dockerfile sits in the additional content we use its contents sha
if (
AdditionalContentContext.is(context) &&
Expand Down Expand Up @@ -100,7 +81,7 @@ export class ImageSourceProvider {
}
}

protected getContentSHA(contents: string): string {
private getContentSHA(contents: string): string {
return createHash("sha256").update(contents).digest("hex");
}
}

0 comments on commit 0ea1b3a

Please sign in to comment.