209 lines
9.4 KiB
TypeScript

/**
* Copyright (c) 2020 Gitpod GmbH. All rights reserved.
* Licensed under the GNU Affero General Public License (AGPL).
* See License-AGPL.txt in the project root for license information.
*/
import { injectable } from 'inversify';
import { GitpodHostUrl } from '@gitpod/gitpod-protocol/lib/util/gitpod-host-url';
import { AbstractComponentEnv, getEnvVar } from '@gitpod/gitpod-protocol/lib/env';
import { AuthProviderParams, parseAuthProviderParamsFromEnv } from './auth/auth-provider';
import * as fs from "fs";
import { Branding, NamedWorkspaceFeatureFlag, WorkspaceFeatureFlags } from '@gitpod/gitpod-protocol';
import { BrandingParser } from './branding-parser';
@injectable()
export class Env extends AbstractComponentEnv {
readonly serverVersion = process.env.SERVER_VERSION || 'dev';
readonly hostUrl = new GitpodHostUrl(process.env.HOST_URL || 'https://gitpod.io');
readonly localhostUrl?: GitpodHostUrl = process.env.LOCALHOST_URL ? new GitpodHostUrl(process.env.LOCALHOST_URL) : undefined;
readonly theiaPort = Number.parseInt(process.env.THEIA_PORT || '23000', 10) || 23000;
get theiaHeartbeatInterval() {
const envValue = process.env.THEIA_HEARTBEAT_INTERVAL;
return envValue ? parseInt(envValue, 10) : (1 * 60 * 1000);
}
get workspaceUserTimeout() {
const envValue = process.env.WORKSPACE_USER_TIMEOUT;
return envValue ? parseInt(envValue, 10) : 5 * this.theiaHeartbeatInterval;
}
readonly theiaVersion = process.env.THEIA_VERSION || this.serverVersion;
readonly theiaImageRepo = process.env.THEIA_IMAGE_REPO || 'unknown';
readonly theiaMounted = process.env.THEIA_MOUNTED === "true";
readonly ideDefaultImage = `${this.theiaImageRepo}:${this.theiaVersion}`;
readonly workspaceDefaultImage = process.env.WORKSPACE_DEFAULT_IMAGE || "gitpod/workspace-full:latest";
readonly ideImageAliases: { [index: string]: string } = (() => {
const envValue = process.env.IDE_IMAGE_ALIASES;
let res = !!envValue ? JSON.parse(envValue) : {};
res["theia"] = this.ideDefaultImage;
return res;
})()
readonly previewFeatureFlags: NamedWorkspaceFeatureFlag[] = (() => {
return this.parseStringArray('EXPERIMENTAL_FEATURE_FLAGS') as NamedWorkspaceFeatureFlag[];
})();
protected parseStringArray(name: string): string[] {
const json = process.env[name];
if (!json) {
return [];
}
let value;
try {
value = JSON.parse(json);
} catch (error) {
throw new Error(`Could not parse ${name}: ${error}`);
}
if (!Array.isArray(value) || value.some(e => typeof e !== 'string')) {
throw `${name} should be an array of string: ${json}`;
}
return value;
}
readonly gitpodRegion: string = process.env.GITPOD_REGION || 'unknown';
readonly sessionMaxAgeMs: number = Number.parseInt(process.env.SESSION_MAX_AGE_MS || '259200000' /* 3 days */, 10);
readonly githubAppEnabled: boolean = process.env.GITPOD_GITHUB_APP_ENABLED == "true";
readonly githubAppAppID: number = process.env.GITPOD_GITHUB_APP_ID ? parseInt(process.env.GITPOD_GITHUB_APP_ID, 10) : 0;
readonly githubAppWebhookSecret: string = process.env.GITPOD_GITHUB_APP_WEBHOOK_SECRET || "unknown";
readonly githubAppAuthProviderId: string = process.env.GITPOD_GITHUB_APP_AUTH_PROVIDER_ID || "Public-GitHub";
readonly githubAppCertPath: string = process.env.GITPOD_GITHUB_APP_CERT_PATH || "unknown";
readonly githubAppMarketplaceName: string = process.env.GITPOD_GITHUB_APP_MKT_NAME || "unknown";
readonly githubAppLogLevel?: string = process.env.LOG_LEVEL;
readonly definitelyGpDisabled: boolean = process.env.GITPOD_DEFINITELY_GP_DISABLED == "true";
readonly daysBeforeGarbageCollection: number = parseInt(process.env.GITPOD_DAYS_BEFORE_GARBAGE_COLLECTION || '14', 10);
readonly daysBeforeGarbageCollectingPrebuilds: number = parseInt(process.env.GITPOD_DAYS_BEFORE_GARBAGE_COLLECTING_PREBUILDS || '7', 10);
readonly garbageCollectionStartDate: number = process.env.GITPOD_GARBAGE_COLLECTION_START_DATE ?
new Date(process.env.GITPOD_GARBAGE_COLLECTION_START_DATE).getTime():
Date.now();
readonly garbageCollectionLimit: number = parseInt(process.env.GITPOD_GARBAGE_COLLECTION_LIMIT || '1000', 10);
readonly garbageCollectionDisabled: boolean = process.env.GITPOD_GARBAGE_COLLECTION_DISABLED === 'true';
readonly workspaceDeletionRetentionPeriodDays: number = parseInt(process.env.GITPOD_WORKSPACE_DELETION_RETENTION_PERIOD_DAYS || '21', 10);
readonly workspaceDeletionLimit: number = parseInt(process.env.GITPOD_WORKSPACE_DELETION_LIMIT || '1000', 10);
readonly devBranch = process.env.DEV_BRANCH || '';
readonly enableLocalApp = process.env.ENABLE_LOCAL_APP === "true";
readonly enableOAuthServer = process.env.ENABLE_OAUTH_SERVER === "true";
readonly authProviderConfigs = this.parseAuthProviderParams();
protected parseAuthProviderParams(): AuthProviderParams[] {
const envVar = getEnvVar('AUTH_PROVIDERS_CONFIG');
return parseAuthProviderParamsFromEnv(JSON.parse(envVar));
}
readonly builtinAuthProvidersConfigured = this.authProviderConfigs.length > 0;
readonly brandingConfig = this.parseBrandingConfig();
protected parseBrandingConfig(): Branding {
const envVar = getEnvVar('BRANDING_CONFIG');
return BrandingParser.parse(envVar);
}
readonly gitpodLicense: string | undefined = process.env.GITPOD_LICENSE;
readonly trialLicensePrivateKey: string | undefined = process.env.GITPOD_TRIAL_LICENSE_PVK;
// this limit should be so high that no regular user ever reaches it
readonly maxUserEnvvarCount = Number.parseInt(process.env.MAX_USER_ENVVAR_COUNT || '4048', 10) || 4048;
// maxConcurrentPrebuildsPerRef is the maximum number of prebuilds we allow per ref type at any given time
readonly maxConcurrentPrebuildsPerRef = Number.parseInt(process.env.MAX_CONCUR_PREBUILDS_PER_REF || '10', 10) || 10;
readonly incrementalPrebuildsRepositoryPassList: string[] = (() => {
try {
return this.parseStringArray('INCREMENTAL_PREBUILDS_REPO_PASSLIST');
} catch (error) {
console.error(error);
return [];
}
})()
readonly incrementalPrebuildsCommitHistory: number = Number.parseInt(process.env.INCREMENTAL_PREBUILDS_COMMIT_HISTORY || '100', 10) || 100;
protected gitpodLayernameFromFilesystem: string | null | undefined;
protected readGitpodLayernameFromFilesystem(): string | undefined {
if (this.gitpodLayernameFromFilesystem === null) {
// we've tried reading in the past, but were not able to do so
return undefined;
}
if (this.gitpodLayernameFromFilesystem !== undefined) {
// we have read this name previously and it worked
return this.gitpodLayernameFromFilesystem;
}
try {
this.gitpodLayernameFromFilesystem = fs.readFileSync("/gplayername.txt").toString().trim().split("\n")[0];
} catch (err) {
console.debug('unable to read /gplayername.txt - this might be ok', err)
this.gitpodLayernameFromFilesystem = null;
return undefined;
}
return this.gitpodLayernameFromFilesystem;
}
protected parseBool(name: string) {
return getEnvVar(name, 'false') === 'true';
}
readonly blockNewUsers: boolean = this.parseBool("BLOCK_NEW_USERS");
readonly blockNewUsersPassList: string[] = (() => {
try {
return this.parseStringArray('BLOCK_NEW_USERS_PASSLIST');
} catch (error) {
console.error(error);
return [];
}
})();
readonly makeNewUsersAdmin: boolean = this.parseBool("MAKE_NEW_USERS_ADMIN");
readonly disableDynamicAuthProviderLogin: boolean = this.parseBool("DISABLE_DYNAMIC_AUTH_PROVIDER_LOGIN");
/** This value - if present - overrides the default naming scheme for the GCloud bucket that Theia Plugins are stored in */
readonly theiaPluginsBucketNameOverride: string | undefined = process.env['THEIA_PLUGINS_BUCKET_NAME_OVERRIDE'];
/** defaultBaseImageRegistryWhitelist is the list of registryies users get acces to by default */
readonly defaultBaseImageRegistryWhitelist: string[] = (() => {
return this.parseStringArray('GITPOD_BASEIMG_REGISTRY_WHITELIST');
})()
readonly defaultFeatureFlags: NamedWorkspaceFeatureFlag[] = (() => {
try {
const r = (this.parseStringArray('GITPOD_DEFAULT_FEATURE_FLAGS') as NamedWorkspaceFeatureFlag[]);
return r.filter(e => e in WorkspaceFeatureFlags);
} catch (error) {
console.error(error);
return [];
}
})();
/** defaults to: false */
readonly portAccessForUsersOnly: boolean = this.parsePortAccessForUsersOnly();
protected parsePortAccessForUsersOnly() {
return getEnvVar('PORT_ACCESS_FOR_USERS_ONLY', 'false') === 'true';
}
readonly insecureNoDomain: boolean = getEnvVar('SERVE_INSECURE_NO_DOMAIN', 'false') === 'true';
readonly sessionSecret = this.parseSessionSecret();
protected parseSessionSecret(): string {
const envVar = getEnvVar('SESSION_SECRET');
return envVar;
}
readonly runDbDeleter: boolean = getEnvVar('RUN_DB_DELETER', 'false') === 'true';
readonly oauthServerJWTSecret = getEnvVar("OAUTH_SERVER_JWT_SECRET")
}