diff --git a/components/server/src/container-module.ts b/components/server/src/container-module.ts index 0c49448d2f90a2..58d09ef485b4c5 100644 --- a/components/server/src/container-module.ts +++ b/components/server/src/container-module.ts @@ -412,9 +412,9 @@ export const productionContainerModule = new ContainerModule( bind(DefaultWorkspaceImageValidator) .toDynamicValue((ctx) => // lazy load to avoid circular dependency - async (userId: string, imageRef: string) => { + async (userId: string, imageRef: string, organizationId?: string) => { const user = await ctx.container.get(UserService).findUserById(userId, userId); - await ctx.container.get(WorkspaceService).validateImageRef({}, user, imageRef); + await ctx.container.get(WorkspaceService).validateImageRef({}, user, imageRef, organizationId); }, ) .inSingletonScope(); diff --git a/components/server/src/orgs/default-workspace-image-validator.ts b/components/server/src/orgs/default-workspace-image-validator.ts index fda29e97c9ce03..721d71605e9f31 100644 --- a/components/server/src/orgs/default-workspace-image-validator.ts +++ b/components/server/src/orgs/default-workspace-image-validator.ts @@ -4,5 +4,9 @@ * See License.AGPL.txt in the project root for license information. */ -export type DefaultWorkspaceImageValidator = (userId: string, imageRef: string) => Promise; +export type DefaultWorkspaceImageValidator = ( + userId: string, + imageRef: string, + organizationId?: string, +) => Promise; export const DefaultWorkspaceImageValidator = Symbol("DefaultWorkspaceImageValidator"); diff --git a/components/server/src/orgs/organization-service.ts b/components/server/src/orgs/organization-service.ts index e83402ad0a986c..e3a7375a9ba4d1 100644 --- a/components/server/src/orgs/organization-service.ts +++ b/components/server/src/orgs/organization-service.ts @@ -493,7 +493,7 @@ export class OrganizationService { if (typeof settings.defaultWorkspaceImage === "string") { const defaultWorkspaceImage = settings.defaultWorkspaceImage.trim(); if (defaultWorkspaceImage) { - await this.validateDefaultWorkspaceImage(userId, defaultWorkspaceImage); + await this.validateDefaultWorkspaceImage(userId, defaultWorkspaceImage, orgId); settings = { ...settings, defaultWorkspaceImage }; } else { settings = { ...settings, defaultWorkspaceImage: null }; diff --git a/components/server/src/user/env-var-service.ts b/components/server/src/user/env-var-service.ts index a0195db81a6497..1e6c6c6a1fbdd5 100644 --- a/components/server/src/user/env-var-service.ts +++ b/components/server/src/user/env-var-service.ts @@ -234,6 +234,13 @@ export class EnvVarService { return this.orgDB.getOrgEnvironmentVariables(orgId); } + async listOrgEnvVarsWithValues(requestorId: string, orgId: string): Promise { + const envVars = (await ApplicationError.notFoundToUndefined(this.listOrgEnvVars(requestorId, orgId))) ?? []; + const envVarValues = await this.orgDB.getOrgEnvironmentVariableValues(envVars); + + return envVarValues; + } + async getOrgEnvVarById(requestorId: string, id: string): Promise { const result = await this.orgDB.getOrgEnvironmentVariableById(id); if (!result) { diff --git a/components/server/src/workspace/workspace-service.ts b/components/server/src/workspace/workspace-service.ts index 2ff2dd44820e0c..eeb972fa34c366 100644 --- a/components/server/src/workspace/workspace-service.ts +++ b/components/server/src/workspace/workspace-service.ts @@ -1407,9 +1407,17 @@ export class WorkspaceService { }); } - public async validateImageRef(ctx: TraceContext, user: User, imageRef: string) { + public async validateImageRef(ctx: TraceContext, user: User, imageRef: string, organizationId?: string) { try { - return await this.workspaceStarter.resolveBaseImage(ctx, user, imageRef); + return await this.workspaceStarter.resolveBaseImage( + ctx, + user, + imageRef, + undefined, + undefined, + undefined, + organizationId, + ); } catch (e) { // see https://github.com/gitpod-io/gitpod/blob/f3e41f8d86234e4101edff2199c54f50f8cbb656/components/image-builder-mk3/pkg/orchestrator/orchestrator.go#L561 // TODO(ak) ideally we won't check a message (subject to change) @@ -1423,8 +1431,8 @@ export class WorkspaceService { ) { let message = details; // strip confusing prefix - if (details.startsWith("cannt resolve base image ref: ")) { - message = details.substring("cannt resolve base image ref: ".length); + if (details.startsWith("can't resolve base image ref: ")) { + message = details.substring("can't resolve base image ref: ".length); } throw new ApplicationError(ErrorCodes.BAD_REQUEST, message); } diff --git a/components/server/src/workspace/workspace-starter.ts b/components/server/src/workspace/workspace-starter.ts index d9e32754d6f680..1cf0934526c1b4 100644 --- a/components/server/src/workspace/workspace-starter.ts +++ b/components/server/src/workspace/workspace-starter.ts @@ -2033,6 +2033,7 @@ export class WorkspaceStarter { workspace?: Workspace, instance?: WorkspaceInstance, region?: WorkspaceRegion, + organizationId?: string, ) { const req = new ResolveBaseImageRequest(); req.setRef(imageRef); @@ -2041,6 +2042,15 @@ export class WorkspaceStarter { const auth = new BuildRegistryAuth(); auth.setTotal(allowAll); req.setAuth(auth); + + // if the image resolution is for an organization, we also include the organization's set up env vars + if (organizationId) { + const envVars = await this.envVarService.listOrgEnvVarsWithValues(user.id, organizationId); + + const additionalAuth = await this.getAdditionalImageAuth({ workspace: envVars }); + additionalAuth.forEach((val, key) => auth.getAdditionalMap().set(key, val)); + } + const client = await this.getImageBuilderClient(user, workspace, instance, region); return client.resolveBaseImage({ span: ctx.span }, req); }