From cd98f7394a2ef6e766810b4cae25a4c7836663a0 Mon Sep 17 00:00:00 2001 From: Christian Weichel Date: Wed, 2 Mar 2022 15:00:16 +0000 Subject: [PATCH] Support private registries --- components/gitpod-db/src/project-db.ts | 2 +- .../gitpod-db/src/typeorm/project-db-impl.ts | 6 +- .../image-builder-api/go/imgbuilder.pb.go | 286 ++++++++++-------- components/image-builder-api/imgbuilder.proto | 3 +- .../typescript/src/imgbuilder_pb.d.ts | 11 +- .../typescript/src/imgbuilder_pb.js | 47 ++- components/image-builder-bob/cmd/proxy.go | 9 +- .../image-builder-bob/pkg/proxy/auth.go | 35 ++- components/image-builder-mk3/debug.sh | 12 +- components/image-builder-mk3/pkg/auth/auth.go | 119 ++++---- .../pkg/orchestrator/orchestrator.go | 37 ++- .../ee/src/prebuilds/prebuild-manager.ts | 4 +- .../server/src/projects/projects-service.ts | 4 +- .../src/workspace/gitpod-server-impl.ts | 11 +- .../src/workspace/image-source-provider.ts | 23 +- .../server/src/workspace/workspace-starter.ts | 50 +-- 16 files changed, 391 insertions(+), 268 deletions(-) diff --git a/components/gitpod-db/src/project-db.ts b/components/gitpod-db/src/project-db.ts index 5fbeb551e28216..cdb43b800b0690 100644 --- a/components/gitpod-db/src/project-db.ts +++ b/components/gitpod-db/src/project-db.ts @@ -18,7 +18,7 @@ export interface ProjectDB { updateProject(partialProject: PartialProject): Promise; markDeleted(projectId: string): Promise; setProjectEnvironmentVariable(projectId: string, name: string, value: string, censored: boolean): Promise; - getProjectEnvironmentVariables(projectId: string): Promise; + getProjectEnvironmentVariables(projectId: string): Promise; getProjectEnvironmentVariableById(variableId: string): Promise; deleteProjectEnvironmentVariable(variableId: string): Promise; getProjectEnvironmentVariableValues(envVars: ProjectEnvVar[]): Promise; diff --git a/components/gitpod-db/src/typeorm/project-db-impl.ts b/components/gitpod-db/src/typeorm/project-db-impl.ts index 3557ae8b66b41e..20884fa1860c85 100644 --- a/components/gitpod-db/src/typeorm/project-db-impl.ts +++ b/components/gitpod-db/src/typeorm/project-db-impl.ts @@ -162,11 +162,9 @@ export class ProjectDBImpl implements ProjectDB { }); } - public async getProjectEnvironmentVariables(projectId: string): Promise { + public async getProjectEnvironmentVariables(projectId: string): Promise { const envVarRepo = await this.getProjectEnvVarRepo(); - const envVarsWithValue = await envVarRepo.find({ projectId, deleted: false }); - const envVars = envVarsWithValue.map(toProjectEnvVar); - return envVars; + return await envVarRepo.find({ projectId, deleted: false }); } public async getProjectEnvironmentVariableById(variableId: string): Promise { diff --git a/components/image-builder-api/go/imgbuilder.pb.go b/components/image-builder-api/go/imgbuilder.pb.go index 5f28b4f2fcae64..cd25d508ccd9d9 100644 --- a/components/image-builder-api/go/imgbuilder.pb.go +++ b/components/image-builder-api/go/imgbuilder.pb.go @@ -502,7 +502,7 @@ type BuildRequest struct { Source *BuildSource `protobuf:"bytes,1,opt,name=source,proto3" json:"source,omitempty"` Auth *BuildRegistryAuth `protobuf:"bytes,2,opt,name=auth,proto3" json:"auth,omitempty"` - ForceRebuild bool `protobuf:"varint,3,opt,name=forceRebuild,proto3" json:"forceRebuild,omitempty"` + ForceRebuild bool `protobuf:"varint,3,opt,name=force_rebuild,json=forceRebuild,proto3" json:"force_rebuild,omitempty"` } func (x *BuildRequest) Reset() { @@ -566,7 +566,8 @@ type BuildRegistryAuth struct { // Types that are assignable to Mode: // *BuildRegistryAuth_Total // *BuildRegistryAuth_Selective - Mode isBuildRegistryAuth_Mode `protobuf_oneof:"mode"` + Mode isBuildRegistryAuth_Mode `protobuf_oneof:"mode"` + Additional map[string]string `protobuf:"bytes,3,rep,name=additional,proto3" json:"additional,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } func (x *BuildRegistryAuth) Reset() { @@ -622,6 +623,13 @@ func (x *BuildRegistryAuth) GetSelective() *BuildRegistryAuthSelective { return nil } +func (x *BuildRegistryAuth) GetAdditional() map[string]string { + if x != nil { + return x.Additional + } + return nil +} + type isBuildRegistryAuth_Mode interface { isBuildRegistryAuth_Mode() } @@ -1223,119 +1231,127 @@ var file_imgbuilder_proto_rawDesc = []byte{ 0x61, 0x73, 0x65, 0x52, 0x65, 0x66, 0x12, 0x2c, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x22, 0x90, 0x01, 0x0a, 0x0c, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, + 0x61, 0x74, 0x75, 0x73, 0x22, 0x91, 0x01, 0x0a, 0x0c, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x2e, 0x0a, 0x04, 0x61, 0x75, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x41, 0x75, 0x74, 0x68, 0x52, 0x04, 0x61, - 0x75, 0x74, 0x68, 0x12, 0x22, 0x0a, 0x0c, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x52, 0x65, 0x62, 0x75, - 0x69, 0x6c, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x66, 0x6f, 0x72, 0x63, 0x65, - 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x22, 0x99, 0x01, 0x0a, 0x11, 0x42, 0x75, 0x69, 0x6c, - 0x64, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x41, 0x75, 0x74, 0x68, 0x12, 0x37, 0x0a, - 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x62, - 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x67, 0x69, - 0x73, 0x74, 0x72, 0x79, 0x41, 0x75, 0x74, 0x68, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x48, 0x00, 0x52, - 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x43, 0x0a, 0x09, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, - 0x69, 0x76, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x62, 0x75, 0x69, 0x6c, - 0x64, 0x65, 0x72, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, - 0x79, 0x41, 0x75, 0x74, 0x68, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x48, 0x00, - 0x52, 0x09, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x42, 0x06, 0x0a, 0x04, 0x6d, - 0x6f, 0x64, 0x65, 0x22, 0x35, 0x0a, 0x16, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x67, 0x69, - 0x73, 0x74, 0x72, 0x79, 0x41, 0x75, 0x74, 0x68, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x1b, 0x0a, - 0x09, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x6c, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x08, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x41, 0x6c, 0x6c, 0x22, 0x87, 0x01, 0x0a, 0x1a, 0x42, - 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x41, 0x75, 0x74, 0x68, - 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x6c, 0x6c, - 0x6f, 0x77, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x72, 0x65, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x0c, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x42, 0x61, 0x73, 0x65, 0x72, 0x65, 0x70, 0x12, 0x2d, - 0x0a, 0x12, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x72, 0x65, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x61, 0x6c, 0x6c, 0x6f, - 0x77, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x72, 0x65, 0x70, 0x12, 0x15, 0x0a, - 0x06, 0x61, 0x6e, 0x79, 0x5f, 0x6f, 0x66, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x61, - 0x6e, 0x79, 0x4f, 0x66, 0x22, 0xac, 0x01, 0x0a, 0x0d, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x65, 0x66, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x65, 0x66, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x61, 0x73, 0x65, - 0x5f, 0x72, 0x65, 0x66, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x62, 0x61, 0x73, 0x65, - 0x52, 0x65, 0x66, 0x12, 0x2c, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x42, 0x75, - 0x69, 0x6c, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x26, 0x0a, 0x04, 0x69, - 0x6e, 0x66, 0x6f, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x62, 0x75, 0x69, 0x6c, - 0x64, 0x65, 0x72, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x04, 0x69, - 0x6e, 0x66, 0x6f, 0x22, 0x61, 0x0a, 0x0b, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x72, 0x65, 0x66, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x66, 0x12, - 0x1a, 0x0a, 0x08, 0x63, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x08, 0x63, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x65, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x62, - 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x62, - 0x75, 0x69, 0x6c, 0x64, 0x49, 0x64, 0x22, 0x28, 0x0a, 0x0c, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, - 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, - 0x22, 0x13, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x40, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x75, 0x69, - 0x6c, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x62, - 0x75, 0x69, 0x6c, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x62, 0x75, - 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x52, - 0x06, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x22, 0xcd, 0x01, 0x0a, 0x09, 0x42, 0x75, 0x69, 0x6c, - 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x65, 0x66, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x72, 0x65, 0x66, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x61, 0x73, 0x65, 0x5f, - 0x72, 0x65, 0x66, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x62, 0x61, 0x73, 0x65, 0x52, - 0x65, 0x66, 0x12, 0x2c, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x42, 0x75, 0x69, - 0x6c, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, - 0x19, 0x0a, 0x08, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x07, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x64, 0x12, 0x2b, 0x0a, 0x08, 0x6c, 0x6f, - 0x67, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x62, - 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x4c, 0x6f, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, - 0x6c, 0x6f, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x90, 0x01, 0x0a, 0x07, 0x4c, 0x6f, 0x67, 0x49, - 0x6e, 0x66, 0x6f, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x37, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, - 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, - 0x2e, 0x4c, 0x6f, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x1a, 0x3a, - 0x0a, 0x0c, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, - 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, - 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x2a, 0x4b, 0x0a, 0x0b, 0x42, 0x75, - 0x69, 0x6c, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0b, 0x0a, 0x07, 0x75, 0x6e, 0x6b, - 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, - 0x67, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x64, 0x6f, 0x6e, 0x65, 0x5f, 0x73, 0x75, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x10, 0x02, 0x12, 0x10, 0x0a, 0x0c, 0x64, 0x6f, 0x6e, 0x65, 0x5f, 0x66, 0x61, - 0x69, 0x6c, 0x75, 0x72, 0x65, 0x10, 0x03, 0x32, 0x91, 0x03, 0x0a, 0x0c, 0x49, 0x6d, 0x61, 0x67, - 0x65, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x12, 0x59, 0x0a, 0x10, 0x52, 0x65, 0x73, 0x6f, - 0x6c, 0x76, 0x65, 0x42, 0x61, 0x73, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x20, 0x2e, 0x62, - 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x42, 0x61, - 0x73, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, - 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, - 0x42, 0x61, 0x73, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x12, 0x68, 0x0a, 0x15, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x57, 0x6f, - 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x25, 0x2e, 0x62, - 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x57, 0x6f, - 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x52, 0x65, - 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x6d, - 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3a, 0x0a, - 0x05, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x15, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, - 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, - 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x37, 0x0a, 0x04, 0x4c, 0x6f, 0x67, - 0x73, 0x12, 0x14, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x4c, 0x6f, 0x67, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, - 0x72, 0x2e, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x30, 0x01, 0x12, 0x47, 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x73, - 0x12, 0x1a, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x42, - 0x75, 0x69, 0x6c, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x62, + 0x75, 0x74, 0x68, 0x12, 0x23, 0x0a, 0x0d, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x62, + 0x75, 0x69, 0x6c, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x66, 0x6f, 0x72, 0x63, + 0x65, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x22, 0xa4, 0x02, 0x0a, 0x11, 0x42, 0x75, 0x69, + 0x6c, 0x64, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x41, 0x75, 0x74, 0x68, 0x12, 0x37, + 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, + 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x67, + 0x69, 0x73, 0x74, 0x72, 0x79, 0x41, 0x75, 0x74, 0x68, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x48, 0x00, + 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x43, 0x0a, 0x09, 0x73, 0x65, 0x6c, 0x65, 0x63, + 0x74, 0x69, 0x76, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x62, 0x75, 0x69, + 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, + 0x72, 0x79, 0x41, 0x75, 0x74, 0x68, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x48, + 0x00, 0x52, 0x09, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x12, 0x4a, 0x0a, 0x0a, + 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x2a, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, + 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x41, 0x75, 0x74, 0x68, 0x2e, 0x41, 0x64, 0x64, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x61, 0x64, + 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x1a, 0x3d, 0x0a, 0x0f, 0x41, 0x64, 0x64, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x06, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x22, + 0x35, 0x0a, 0x16, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, + 0x41, 0x75, 0x74, 0x68, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x6c, 0x6c, + 0x6f, 0x77, 0x5f, 0x61, 0x6c, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x61, 0x6c, + 0x6c, 0x6f, 0x77, 0x41, 0x6c, 0x6c, 0x22, 0x87, 0x01, 0x0a, 0x1a, 0x42, 0x75, 0x69, 0x6c, 0x64, + 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x41, 0x75, 0x74, 0x68, 0x53, 0x65, 0x6c, 0x65, + 0x63, 0x74, 0x69, 0x76, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x62, + 0x61, 0x73, 0x65, 0x72, 0x65, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x61, 0x6c, + 0x6c, 0x6f, 0x77, 0x42, 0x61, 0x73, 0x65, 0x72, 0x65, 0x70, 0x12, 0x2d, 0x0a, 0x12, 0x61, 0x6c, + 0x6c, 0x6f, 0x77, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x72, 0x65, 0x70, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x57, 0x6f, 0x72, + 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x72, 0x65, 0x70, 0x12, 0x15, 0x0a, 0x06, 0x61, 0x6e, 0x79, + 0x5f, 0x6f, 0x66, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x61, 0x6e, 0x79, 0x4f, 0x66, + 0x22, 0xac, 0x01, 0x0a, 0x0d, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x65, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x72, 0x65, 0x66, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x72, 0x65, 0x66, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x62, 0x61, 0x73, 0x65, 0x52, 0x65, 0x66, 0x12, + 0x2c, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x14, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x18, 0x0a, + 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x26, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, + 0x42, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x22, + 0x61, 0x0a, 0x0b, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, + 0x0a, 0x09, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x72, 0x65, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x66, 0x12, 0x1a, 0x0a, 0x08, 0x63, + 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x63, + 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x65, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x75, 0x69, 0x6c, 0x64, + 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x62, 0x75, 0x69, 0x6c, 0x64, + 0x49, 0x64, 0x22, 0x28, 0x0a, 0x0c, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x22, 0x13, 0x0a, 0x11, + 0x4c, 0x69, 0x73, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x22, 0x40, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x62, 0x75, 0x69, 0x6c, 0x64, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, + 0x72, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x62, 0x75, 0x69, + 0x6c, 0x64, 0x73, 0x22, 0xcd, 0x01, 0x0a, 0x09, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x66, + 0x6f, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x65, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x72, 0x65, 0x66, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x72, 0x65, 0x66, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x62, 0x61, 0x73, 0x65, 0x52, 0x65, 0x66, 0x12, 0x2c, + 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, + 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1d, 0x0a, 0x0a, + 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x62, + 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x62, + 0x75, 0x69, 0x6c, 0x64, 0x49, 0x64, 0x12, 0x2b, 0x0a, 0x08, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x6e, + 0x66, 0x6f, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, + 0x65, 0x72, 0x2e, 0x4c, 0x6f, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, 0x6c, 0x6f, 0x67, 0x49, + 0x6e, 0x66, 0x6f, 0x22, 0x90, 0x01, 0x0a, 0x07, 0x4c, 0x6f, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x12, + 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, + 0x6c, 0x12, 0x37, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x4c, 0x6f, 0x67, + 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x1a, 0x3a, 0x0a, 0x0c, 0x48, 0x65, + 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x2a, 0x4b, 0x0a, 0x0b, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0b, 0x0a, 0x07, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, + 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x10, 0x01, 0x12, + 0x10, 0x0a, 0x0c, 0x64, 0x6f, 0x6e, 0x65, 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x10, + 0x02, 0x12, 0x10, 0x0a, 0x0c, 0x64, 0x6f, 0x6e, 0x65, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, + 0x65, 0x10, 0x03, 0x32, 0x91, 0x03, 0x0a, 0x0c, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x42, 0x75, 0x69, + 0x6c, 0x64, 0x65, 0x72, 0x12, 0x59, 0x0a, 0x10, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x42, + 0x61, 0x73, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x20, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, + 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x42, 0x61, 0x73, 0x65, 0x49, 0x6d, + 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x62, 0x75, 0x69, + 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x42, 0x61, 0x73, 0x65, + 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, + 0x68, 0x0a, 0x15, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x25, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, + 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x26, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, + 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3a, 0x0a, 0x05, 0x42, 0x75, 0x69, + 0x6c, 0x64, 0x12, 0x15, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x42, 0x75, 0x69, + 0x6c, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x62, 0x75, 0x69, 0x6c, + 0x64, 0x65, 0x72, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x37, 0x0a, 0x04, 0x4c, 0x6f, 0x67, 0x73, 0x12, 0x14, 0x2e, + 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x4c, 0x6f, + 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x47, + 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x12, 0x1a, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x2f, 0x5a, 0x2d, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, - 0x2d, 0x69, 0x6f, 0x2f, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2f, 0x69, 0x6d, 0x61, 0x67, 0x65, - 0x2d, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, + 0x65, 0x72, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x2f, 0x5a, 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2d, 0x69, 0x6f, 0x2f, + 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2d, 0x62, 0x75, 0x69, + 0x6c, 0x64, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1351,7 +1367,7 @@ func file_imgbuilder_proto_rawDescGZIP() []byte { } var file_imgbuilder_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_imgbuilder_proto_msgTypes = make([]protoimpl.MessageInfo, 19) +var file_imgbuilder_proto_msgTypes = make([]protoimpl.MessageInfo, 20) var file_imgbuilder_proto_goTypes = []interface{}{ (BuildStatus)(0), // 0: builder.BuildStatus (*BuildSource)(nil), // 1: builder.BuildSource @@ -1372,13 +1388,14 @@ var file_imgbuilder_proto_goTypes = []interface{}{ (*ListBuildsResponse)(nil), // 16: builder.ListBuildsResponse (*BuildInfo)(nil), // 17: builder.BuildInfo (*LogInfo)(nil), // 18: builder.LogInfo - nil, // 19: builder.LogInfo.HeadersEntry - (*api.WorkspaceInitializer)(nil), // 20: contentservice.WorkspaceInitializer + nil, // 19: builder.BuildRegistryAuth.AdditionalEntry + nil, // 20: builder.LogInfo.HeadersEntry + (*api.WorkspaceInitializer)(nil), // 21: contentservice.WorkspaceInitializer } var file_imgbuilder_proto_depIdxs = []int32{ 2, // 0: builder.BuildSource.ref:type_name -> builder.BuildSourceReference 3, // 1: builder.BuildSource.file:type_name -> builder.BuildSourceDockerfile - 20, // 2: builder.BuildSourceDockerfile.source:type_name -> contentservice.WorkspaceInitializer + 21, // 2: builder.BuildSourceDockerfile.source:type_name -> contentservice.WorkspaceInitializer 9, // 3: builder.ResolveBaseImageRequest.auth:type_name -> builder.BuildRegistryAuth 1, // 4: builder.ResolveWorkspaceImageRequest.source:type_name -> builder.BuildSource 9, // 5: builder.ResolveWorkspaceImageRequest.auth:type_name -> builder.BuildRegistryAuth @@ -1387,27 +1404,28 @@ var file_imgbuilder_proto_depIdxs = []int32{ 9, // 8: builder.BuildRequest.auth:type_name -> builder.BuildRegistryAuth 10, // 9: builder.BuildRegistryAuth.total:type_name -> builder.BuildRegistryAuthTotal 11, // 10: builder.BuildRegistryAuth.selective:type_name -> builder.BuildRegistryAuthSelective - 0, // 11: builder.BuildResponse.status:type_name -> builder.BuildStatus - 17, // 12: builder.BuildResponse.info:type_name -> builder.BuildInfo - 17, // 13: builder.ListBuildsResponse.builds:type_name -> builder.BuildInfo - 0, // 14: builder.BuildInfo.status:type_name -> builder.BuildStatus - 18, // 15: builder.BuildInfo.log_info:type_name -> builder.LogInfo - 19, // 16: builder.LogInfo.headers:type_name -> builder.LogInfo.HeadersEntry - 4, // 17: builder.ImageBuilder.ResolveBaseImage:input_type -> builder.ResolveBaseImageRequest - 6, // 18: builder.ImageBuilder.ResolveWorkspaceImage:input_type -> builder.ResolveWorkspaceImageRequest - 8, // 19: builder.ImageBuilder.Build:input_type -> builder.BuildRequest - 13, // 20: builder.ImageBuilder.Logs:input_type -> builder.LogsRequest - 15, // 21: builder.ImageBuilder.ListBuilds:input_type -> builder.ListBuildsRequest - 5, // 22: builder.ImageBuilder.ResolveBaseImage:output_type -> builder.ResolveBaseImageResponse - 7, // 23: builder.ImageBuilder.ResolveWorkspaceImage:output_type -> builder.ResolveWorkspaceImageResponse - 12, // 24: builder.ImageBuilder.Build:output_type -> builder.BuildResponse - 14, // 25: builder.ImageBuilder.Logs:output_type -> builder.LogsResponse - 16, // 26: builder.ImageBuilder.ListBuilds:output_type -> builder.ListBuildsResponse - 22, // [22:27] is the sub-list for method output_type - 17, // [17:22] is the sub-list for method input_type - 17, // [17:17] is the sub-list for extension type_name - 17, // [17:17] is the sub-list for extension extendee - 0, // [0:17] is the sub-list for field type_name + 19, // 11: builder.BuildRegistryAuth.additional:type_name -> builder.BuildRegistryAuth.AdditionalEntry + 0, // 12: builder.BuildResponse.status:type_name -> builder.BuildStatus + 17, // 13: builder.BuildResponse.info:type_name -> builder.BuildInfo + 17, // 14: builder.ListBuildsResponse.builds:type_name -> builder.BuildInfo + 0, // 15: builder.BuildInfo.status:type_name -> builder.BuildStatus + 18, // 16: builder.BuildInfo.log_info:type_name -> builder.LogInfo + 20, // 17: builder.LogInfo.headers:type_name -> builder.LogInfo.HeadersEntry + 4, // 18: builder.ImageBuilder.ResolveBaseImage:input_type -> builder.ResolveBaseImageRequest + 6, // 19: builder.ImageBuilder.ResolveWorkspaceImage:input_type -> builder.ResolveWorkspaceImageRequest + 8, // 20: builder.ImageBuilder.Build:input_type -> builder.BuildRequest + 13, // 21: builder.ImageBuilder.Logs:input_type -> builder.LogsRequest + 15, // 22: builder.ImageBuilder.ListBuilds:input_type -> builder.ListBuildsRequest + 5, // 23: builder.ImageBuilder.ResolveBaseImage:output_type -> builder.ResolveBaseImageResponse + 7, // 24: builder.ImageBuilder.ResolveWorkspaceImage:output_type -> builder.ResolveWorkspaceImageResponse + 12, // 25: builder.ImageBuilder.Build:output_type -> builder.BuildResponse + 14, // 26: builder.ImageBuilder.Logs:output_type -> builder.LogsResponse + 16, // 27: builder.ImageBuilder.ListBuilds:output_type -> builder.ListBuildsResponse + 23, // [23:28] is the sub-list for method output_type + 18, // [18:23] is the sub-list for method input_type + 18, // [18:18] is the sub-list for extension type_name + 18, // [18:18] is the sub-list for extension extendee + 0, // [0:18] is the sub-list for field type_name } func init() { file_imgbuilder_proto_init() } @@ -1647,7 +1665,7 @@ func file_imgbuilder_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_imgbuilder_proto_rawDesc, NumEnums: 1, - NumMessages: 19, + NumMessages: 20, NumExtensions: 0, NumServices: 1, }, diff --git a/components/image-builder-api/imgbuilder.proto b/components/image-builder-api/imgbuilder.proto index a7ea091a51efd1..8592e2508fe53f 100644 --- a/components/image-builder-api/imgbuilder.proto +++ b/components/image-builder-api/imgbuilder.proto @@ -64,7 +64,7 @@ message ResolveWorkspaceImageResponse { message BuildRequest { BuildSource source = 1; BuildRegistryAuth auth = 2; - bool forceRebuild = 3; + bool force_rebuild = 3; } message BuildRegistryAuth { @@ -72,6 +72,7 @@ message BuildRegistryAuth { BuildRegistryAuthTotal total = 1; BuildRegistryAuthSelective selective = 2; } + map additional = 3; } message BuildRegistryAuthTotal { diff --git a/components/image-builder-api/typescript/src/imgbuilder_pb.d.ts b/components/image-builder-api/typescript/src/imgbuilder_pb.d.ts index c1a17cac6dc666..b974dc291878d7 100644 --- a/components/image-builder-api/typescript/src/imgbuilder_pb.d.ts +++ b/components/image-builder-api/typescript/src/imgbuilder_pb.d.ts @@ -215,8 +215,8 @@ export class BuildRequest extends jspb.Message { clearAuth(): void; getAuth(): BuildRegistryAuth | undefined; setAuth(value?: BuildRegistryAuth): BuildRequest; - getForcerebuild(): boolean; - setForcerebuild(value: boolean): BuildRequest; + getForceRebuild(): boolean; + setForceRebuild(value: boolean): BuildRequest; serializeBinary(): Uint8Array; toObject(includeInstance?: boolean): BuildRequest.AsObject; @@ -232,7 +232,7 @@ export namespace BuildRequest { export type AsObject = { source?: BuildSource.AsObject, auth?: BuildRegistryAuth.AsObject, - forcerebuild: boolean, + forceRebuild: boolean, } } @@ -248,6 +248,9 @@ export class BuildRegistryAuth extends jspb.Message { getSelective(): BuildRegistryAuthSelective | undefined; setSelective(value?: BuildRegistryAuthSelective): BuildRegistryAuth; + getAdditionalMap(): jspb.Map; + clearAdditionalMap(): void; + getModeCase(): BuildRegistryAuth.ModeCase; serializeBinary(): Uint8Array; @@ -264,6 +267,8 @@ export namespace BuildRegistryAuth { export type AsObject = { total?: BuildRegistryAuthTotal.AsObject, selective?: BuildRegistryAuthSelective.AsObject, + + additionalMap: Array<[string, string]>, } export enum ModeCase { diff --git a/components/image-builder-api/typescript/src/imgbuilder_pb.js b/components/image-builder-api/typescript/src/imgbuilder_pb.js index cdd94a9affa125..f299c6112235bd 100644 --- a/components/image-builder-api/typescript/src/imgbuilder_pb.js +++ b/components/image-builder-api/typescript/src/imgbuilder_pb.js @@ -1764,7 +1764,7 @@ proto.builder.BuildRequest.toObject = function(includeInstance, msg) { var f, obj = { source: (f = msg.getSource()) && proto.builder.BuildSource.toObject(includeInstance, f), auth: (f = msg.getAuth()) && proto.builder.BuildRegistryAuth.toObject(includeInstance, f), - forcerebuild: jspb.Message.getBooleanFieldWithDefault(msg, 3, false) + forceRebuild: jspb.Message.getBooleanFieldWithDefault(msg, 3, false) }; if (includeInstance) { @@ -1813,7 +1813,7 @@ proto.builder.BuildRequest.deserializeBinaryFromReader = function(msg, reader) { break; case 3: var value = /** @type {boolean} */ (reader.readBool()); - msg.setForcerebuild(value); + msg.setForceRebuild(value); break; default: reader.skipField(); @@ -1860,7 +1860,7 @@ proto.builder.BuildRequest.serializeBinaryToWriter = function(message, writer) { proto.builder.BuildRegistryAuth.serializeBinaryToWriter ); } - f = message.getForcerebuild(); + f = message.getForceRebuild(); if (f) { writer.writeBool( 3, @@ -1945,10 +1945,10 @@ proto.builder.BuildRequest.prototype.hasAuth = function() { /** - * optional bool forceRebuild = 3; + * optional bool force_rebuild = 3; * @return {boolean} */ -proto.builder.BuildRequest.prototype.getForcerebuild = function() { +proto.builder.BuildRequest.prototype.getForceRebuild = function() { return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 3, false)); }; @@ -1957,7 +1957,7 @@ proto.builder.BuildRequest.prototype.getForcerebuild = function() { * @param {boolean} value * @return {!proto.builder.BuildRequest} returns this */ -proto.builder.BuildRequest.prototype.setForcerebuild = function(value) { +proto.builder.BuildRequest.prototype.setForceRebuild = function(value) { return jspb.Message.setProto3BooleanField(this, 3, value); }; @@ -2021,7 +2021,8 @@ proto.builder.BuildRegistryAuth.prototype.toObject = function(opt_includeInstanc proto.builder.BuildRegistryAuth.toObject = function(includeInstance, msg) { var f, obj = { total: (f = msg.getTotal()) && proto.builder.BuildRegistryAuthTotal.toObject(includeInstance, f), - selective: (f = msg.getSelective()) && proto.builder.BuildRegistryAuthSelective.toObject(includeInstance, f) + selective: (f = msg.getSelective()) && proto.builder.BuildRegistryAuthSelective.toObject(includeInstance, f), + additionalMap: (f = msg.getAdditionalMap()) ? f.toObject(includeInstance, undefined) : [] }; if (includeInstance) { @@ -2068,6 +2069,12 @@ proto.builder.BuildRegistryAuth.deserializeBinaryFromReader = function(msg, read reader.readMessage(value,proto.builder.BuildRegistryAuthSelective.deserializeBinaryFromReader); msg.setSelective(value); break; + case 3: + var value = msg.getAdditionalMap(); + reader.readMessage(value, function(message, reader) { + jspb.Map.deserializeBinary(message, reader, jspb.BinaryReader.prototype.readString, jspb.BinaryReader.prototype.readString, null, "", ""); + }); + break; default: reader.skipField(); break; @@ -2113,6 +2120,10 @@ proto.builder.BuildRegistryAuth.serializeBinaryToWriter = function(message, writ proto.builder.BuildRegistryAuthSelective.serializeBinaryToWriter ); } + f = message.getAdditionalMap(true); + if (f && f.getLength() > 0) { + f.serializeBinary(3, writer, jspb.BinaryWriter.prototype.writeString, jspb.BinaryWriter.prototype.writeString); + } }; @@ -2190,6 +2201,28 @@ proto.builder.BuildRegistryAuth.prototype.hasSelective = function() { }; +/** + * map additional = 3; + * @param {boolean=} opt_noLazyCreate Do not create the map if + * empty, instead returning `undefined` + * @return {!jspb.Map} + */ +proto.builder.BuildRegistryAuth.prototype.getAdditionalMap = function(opt_noLazyCreate) { + return /** @type {!jspb.Map} */ ( + jspb.Message.getMapField(this, 3, opt_noLazyCreate, + null)); +}; + + +/** + * Clears values from the map. The map will be non-null. + * @return {!proto.builder.BuildRegistryAuth} returns this + */ +proto.builder.BuildRegistryAuth.prototype.clearAdditionalMap = function() { + this.getAdditionalMap().clear(); + return this;}; + + diff --git a/components/image-builder-bob/cmd/proxy.go b/components/image-builder-bob/cmd/proxy.go index f2917e8605613a..7954892d9fd123 100644 --- a/components/image-builder-bob/cmd/proxy.go +++ b/components/image-builder-bob/cmd/proxy.go @@ -20,6 +20,7 @@ import ( var proxyOpts struct { BaseRef, TargetRef string Auth string + AdditionalAuth string } // proxyCmd represents the build command @@ -30,10 +31,15 @@ var proxyCmd = &cobra.Command{ log.Init("bob", "", true, os.Getenv("SUPERVISOR_DEBUG_ENABLE") == "true") log := log.WithField("command", "proxy") - authP, err := proxy.NewAuthorizerFromEnvVar(proxyOpts.Auth) + authP, err := proxy.NewAuthorizerFromDockerEnvVar(proxyOpts.Auth) if err != nil { log.WithError(err).WithField("auth", proxyOpts.Auth).Fatal("cannot unmarshal auth") } + authA, err := proxy.NewAuthorizerFromEnvVar(proxyOpts.AdditionalAuth) + if err != nil { + log.WithError(err).WithField("auth", proxyOpts.Auth).Fatal("cannot unmarshal auth") + } + authP = authP.AddIfNotExists(authA) baseref, err := reference.ParseNormalizedNamed(proxyOpts.BaseRef) if err != nil { @@ -87,4 +93,5 @@ func init() { proxyCmd.Flags().StringVar(&proxyOpts.BaseRef, "base-ref", os.Getenv("WORKSPACEKIT_BOBPROXY_BASEREF"), "ref of the base image") proxyCmd.Flags().StringVar(&proxyOpts.TargetRef, "target-ref", os.Getenv("WORKSPACEKIT_BOBPROXY_TARGETREF"), "ref of the target image") proxyCmd.Flags().StringVar(&proxyOpts.Auth, "auth", os.Getenv("WORKSPACEKIT_BOBPROXY_AUTH"), "authentication to use") + proxyCmd.Flags().StringVar(&proxyOpts.AdditionalAuth, "additional-auth", os.Getenv("WORKSPACEKIT_BOBPROXY_ADDITIONALAUTH"), "additional authentication to use") } diff --git a/components/image-builder-bob/pkg/proxy/auth.go b/components/image-builder-bob/pkg/proxy/auth.go index 21f4613219792a..e2cfb09af06331 100644 --- a/components/image-builder-bob/pkg/proxy/auth.go +++ b/components/image-builder-bob/pkg/proxy/auth.go @@ -20,9 +20,9 @@ type authConfig struct { Auth string `json:"auth"` } -type authorizerImpl map[string]authConfig +type MapAuthorizer map[string]authConfig -func (a authorizerImpl) Authorize(host string) (user, pass string, err error) { +func (a MapAuthorizer) Authorize(host string) (user, pass string, err error) { defer func() { log.WithFields(logrus.Fields{ "host": host, @@ -54,11 +54,25 @@ func (a authorizerImpl) Authorize(host string) (user, pass string, err error) { return } +func (a MapAuthorizer) AddIfNotExists(other MapAuthorizer) MapAuthorizer { + res := make(map[string]authConfig) + for k, v := range a { + res[k] = v + } + for k, v := range other { + if _, ok := a[k]; ok { + continue + } + res[k] = v + } + return MapAuthorizer(res) +} + type Authorizer interface { Authorize(host string) (user, pass string, err error) } -func NewAuthorizerFromEnvVar(content string) (auth Authorizer, err error) { +func NewAuthorizerFromDockerEnvVar(content string) (auth MapAuthorizer, err error) { var res struct { Auths map[string]authConfig `json:"auths"` } @@ -66,5 +80,18 @@ func NewAuthorizerFromEnvVar(content string) (auth Authorizer, err error) { if err != nil { return } - return authorizerImpl(res.Auths), nil + return MapAuthorizer(res.Auths), nil +} + +func NewAuthorizerFromEnvVar(content string) (auth MapAuthorizer, err error) { + if content == "" { + return nil, nil + } + + var res map[string]authConfig + err = json.Unmarshal([]byte(content), &res) + if err != nil { + return + } + return MapAuthorizer(res), nil } diff --git a/components/image-builder-mk3/debug.sh b/components/image-builder-mk3/debug.sh index d1750574b66107..b1b63829bc203c 100755 --- a/components/image-builder-mk3/debug.sh +++ b/components/image-builder-mk3/debug.sh @@ -5,8 +5,12 @@ docker ps &> /dev/null || (echo "You need a working Docker daemon. Maybe set DOCKER_HOST?"; exit 1) gcloud auth list | grep typefox &>/dev/null || (echo "Login using 'gcloud auth login' for the docker push to work"; exit 1) -leeway build .:docker -Dversion=dev -devImage=eu.gcr.io/gitpod-dev/image-builder:dev +leeway build -v .:docker -Dversion=cw-dev -DimageRepoBase=eu.gcr.io/gitpod-core-dev/build +devImage=eu.gcr.io/gitpod-core-dev/build/image-builder-mk3:cw-dev -kubectl patch deployment image-builder --patch '{"spec": {"template": {"spec": {"containers": [{"name": "service","image": "'$devImage'"}]}}}}' -kubectl get pods --no-headers -o=custom-columns=:metadata.name | grep image-builder | xargs kubectl delete pod \ No newline at end of file +kubectl patch deployment image-builder-mk3 --patch '{"spec": {"template": {"spec": {"containers": [{"name": "image-builder-mk3","imagePullPolicy":"Always","image": "'$devImage'"}]}}}}' +kubectl rollout restart deployment/image-builder-mk3 +kubectl rollout status -w deployment/image-builder-mk3 +# give the old pod time to disappear +sleep 20 +gpctl debug logs image-builder-mk3 \ No newline at end of file diff --git a/components/image-builder-mk3/pkg/auth/auth.go b/components/image-builder-mk3/pkg/auth/auth.go index eb79fbed723e5f..fa674661b6c40d 100644 --- a/components/image-builder-mk3/pkg/auth/auth.go +++ b/components/image-builder-mk3/pkg/auth/auth.go @@ -5,7 +5,9 @@ package auth import ( + "encoding/base64" "os" + "strings" "github.com/docker/cli/cli/config/configfile" "github.com/docker/distribution/reference" @@ -67,16 +69,16 @@ type Authentication types.AuthConfig // AllowedAuthFor describes for which repositories authentication may be provided for type AllowedAuthFor struct { - All bool - Explicit []string + All bool + Explicit []string + Additional map[string]string } -var ( - // AllowedAuthForAll means auth for all repositories is allowed - AllowedAuthForAll AllowedAuthFor = AllowedAuthFor{true, nil} - // AllowedAuthForNone means auth for no repositories is allowed - AllowedAuthForNone AllowedAuthFor = AllowedAuthFor{false, nil} -) +// AllowedAuthForAll means auth for all repositories is allowed +func AllowedAuthForAll() AllowedAuthFor { return AllowedAuthFor{true, nil, nil} } + +// AllowedAuthForNone means auth for no repositories is allowed +func AllowedAuthForNone() AllowedAuthFor { return AllowedAuthFor{false, nil, nil} } // IsAllowNone returns true if we are to allow authentication for no repos func (a AllowedAuthFor) IsAllowNone() bool { @@ -96,7 +98,7 @@ func (a AllowedAuthFor) Elevate(ref string) AllowedAuthFor { return a } - return AllowedAuthFor{a.All, append(a.Explicit, reference.Domain(pref))} + return AllowedAuthFor{a.All, append(a.Explicit, reference.Domain(pref)), a.Additional} } // ExplicitlyAll produces an AllowedAuthFor that allows authentication for all @@ -117,7 +119,7 @@ type Resolver struct { // ResolveRequestAuth computes the allowed authentication for a build based on its request func (r Resolver) ResolveRequestAuth(auth *api.BuildRegistryAuth) (authFor AllowedAuthFor) { // by default we allow nothing - authFor = AllowedAuthForNone + authFor = AllowedAuthForNone() if auth == nil { return } @@ -125,9 +127,9 @@ func (r Resolver) ResolveRequestAuth(auth *api.BuildRegistryAuth) (authFor Allow switch ath := auth.Mode.(type) { case *api.BuildRegistryAuth_Total: if ath.Total.AllowAll { - authFor = AllowedAuthForAll + authFor = AllowedAuthForAll() } else { - authFor = AllowedAuthForNone + authFor = AllowedAuthForNone() } case *api.BuildRegistryAuth_Selective: var explicit []string @@ -140,10 +142,13 @@ func (r Resolver) ResolveRequestAuth(auth *api.BuildRegistryAuth) (authFor Allow explicit = append(explicit, reference.Domain(ref)) } explicit = append(explicit, ath.Selective.AnyOf...) - authFor = AllowedAuthFor{false, explicit} + authFor = AllowedAuthFor{false, explicit, nil} default: - authFor = AllowedAuthForNone + authFor = AllowedAuthForNone() } + + authFor.Additional = auth.Additional + return } @@ -157,8 +162,20 @@ func (a AllowedAuthFor) GetAuthFor(auth RegistryAuthenticator, refstr string) (r if err != nil { return nil, xerrors.Errorf("cannot parse image ref: %v", err) } - reg := reference.Domain(ref) + + // If we haven't found authentication using the built-in way, we'll resort to additional auth + // the user sent us. + defer func() { + if err == nil && res == nil { + res = a.additionalAuth(reg) + + if res != nil { + log.WithField("reg", reg).Debug("found additional auth") + } + } + }() + var regAllowed bool if a.IsAllowAll() { // free for all @@ -179,57 +196,45 @@ func (a AllowedAuthFor) GetAuthFor(auth RegistryAuthenticator, refstr string) (r return auth.Authenticate(reg) } +func (a AllowedAuthFor) additionalAuth(domain string) *Authentication { + ath, ok := a.Additional[domain] + if !ok { + return nil + } + + res := &Authentication{ + Auth: ath, + } + dec, err := base64.StdEncoding.DecodeString(ath) + if err == nil { + segs := strings.Split(string(dec), ":") + if len(segs) == 2 { + res.Username = segs[0] + res.Password = segs[1] + } + } + return res +} + // ImageBuildAuth is the format image builds needs type ImageBuildAuth map[string]types.AuthConfig // GetImageBuildAuthFor produces authentication in the format an image builds needs -func (a AllowedAuthFor) GetImageBuildAuthFor(auth RegistryAuthenticator, refstr []string) (res ImageBuildAuth, err error) { - if auth == nil { - return nil, nil - } - +func (a AllowedAuthFor) GetImageBuildAuthFor(blocklist []string) (res ImageBuildAuth) { res = make(ImageBuildAuth) - for _, r := range refstr { - ref, err := reference.ParseNormalizedNamed(r) - if err != nil { - return nil, xerrors.Errorf("cannot parse image ref: %v", err) - } - - reg := reference.Domain(ref) - var regAllowed bool - if a.IsAllowAll() { - // free for all - regAllowed = true - } else { - for _, a := range a.Explicit { - if a == reg { - regAllowed = true - break - } + for reg := range a.Additional { + var blocked bool + for _, blk := range blocklist { + if blk == reg { + blocked = true + break } } - if !regAllowed { + if blocked { continue } - - ra, err := auth.Authenticate(reg) - if err != nil { - return nil, xerrors.Errorf("cannot get registry authentication: %v", err) - } - - res[reg] = types.AuthConfig(*ra) - } - for _, reg := range a.Explicit { - if _, ok := res[reg]; ok { - continue - } - - ra, err := auth.Authenticate(reg) - if err != nil { - return nil, xerrors.Errorf("cannot get registry authentication: %v", err) - } - - res[reg] = types.AuthConfig(*ra) + ath := a.additionalAuth(reg) + res[reg] = types.AuthConfig(*ath) } return diff --git a/components/image-builder-mk3/pkg/orchestrator/orchestrator.go b/components/image-builder-mk3/pkg/orchestrator/orchestrator.go index e753d3aae05121..5acdb3bbaf1a0a 100644 --- a/components/image-builder-mk3/pkg/orchestrator/orchestrator.go +++ b/components/image-builder-mk3/pkg/orchestrator/orchestrator.go @@ -7,6 +7,7 @@ package orchestrator import ( "context" "crypto/sha256" + "encoding/json" "errors" "fmt" "io" @@ -26,6 +27,7 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/credentials" "google.golang.org/grpc/status" + "google.golang.org/protobuf/encoding/protojson" common_grpc "github.com/gitpod-io/gitpod/common-go/grpc" "github.com/gitpod-io/gitpod/common-go/log" @@ -144,9 +146,12 @@ func (o *Orchestrator) Start(ctx context.Context) error { func (o *Orchestrator) ResolveBaseImage(ctx context.Context, req *protocol.ResolveBaseImageRequest) (resp *protocol.ResolveBaseImageResponse, err error) { span, ctx := opentracing.StartSpanFromContext(ctx, "ResolveBaseImage") defer tracing.FinishSpan(span, &err) - tracing.LogRequestSafe(span, req) + reqs, _ := protojson.Marshal(req) + safeReqs, _ := log.RedactJSON(reqs) + log.WithField("req", safeReqs).Debug("ResolveBaseImage") + reqauth := o.AuthResolver.ResolveRequestAuth(req.Auth) refstr, err := o.getAbsoluteImageRef(ctx, req.Ref, reqauth) @@ -165,12 +170,16 @@ func (o *Orchestrator) ResolveWorkspaceImage(ctx context.Context, req *protocol. defer tracing.FinishSpan(span, &err) tracing.LogRequestSafe(span, req) + reqs, _ := protojson.Marshal(req) + safeReqs, _ := log.RedactJSON(reqs) + log.WithField("req", safeReqs).Debug("ResolveWorkspaceImage") + reqauth := o.AuthResolver.ResolveRequestAuth(req.Auth) baseref, err := o.getBaseImageRef(ctx, req.Source, reqauth) if err != nil { return nil, status.Errorf(codes.Internal, "cannot resolve base image: %s", err.Error()) } - refstr, err := o.getWorkspaceImageRef(ctx, baseref, reqauth) + refstr, err := o.getWorkspaceImageRef(ctx, baseref) if err != nil { return nil, status.Errorf(codes.InvalidArgument, "cannot produce image ref: %v", err) } @@ -178,7 +187,7 @@ func (o *Orchestrator) ResolveWorkspaceImage(ctx context.Context, req *protocol. // to check if the image exists we must have access to the image caching registry and the refstr we check here does not come // from the user. Thus we can safely use auth.AllowedAuthForAll here. - auth, err := auth.AllowedAuthForAll.GetAuthFor(o.Auth, refstr) + auth, err := auth.AllowedAuthForAll().GetAuthFor(o.Auth, refstr) if err != nil { return nil, status.Errorf(codes.Internal, "cannot get workspace image authentication: %v", err) } @@ -221,11 +230,11 @@ func (o *Orchestrator) Build(req *protocol.BuildRequest, resp protocol.ImageBuil if err != nil { return status.Errorf(codes.Internal, "cannot resolve base image: %q", err) } - wsrefstr, err := o.getWorkspaceImageRef(ctx, baseref, reqauth) + wsrefstr, err := o.getWorkspaceImageRef(ctx, baseref) if err != nil { return status.Errorf(codes.Internal, "cannot produce workspace image ref: %q", err) } - wsrefAuth, err := auth.AllowedAuthForAll.GetAuthFor(o.Auth, wsrefstr) + wsrefAuth, err := auth.AllowedAuthForAll().GetAuthFor(o.Auth, wsrefstr) if err != nil { return status.Errorf(codes.Internal, "cannot get workspace image authentication: %q", err) } @@ -238,7 +247,7 @@ func (o *Orchestrator) Build(req *protocol.BuildRequest, resp protocol.ImageBuil if exists && !req.GetForceRebuild() { // If the workspace image exists, so should the baseimage if we've built it. // If we didn't build it and the base image doesn't exist anymore, getWorkspaceImageRef will have failed to resolve the baseref. - baserefAbsolute, err := o.getAbsoluteImageRef(ctx, baseref, auth.AllowedAuthForAll) + baserefAbsolute, err := o.getAbsoluteImageRef(ctx, baseref, auth.AllowedAuthForAll()) if err != nil { return status.Errorf(codes.Internal, "cannot resolve base image ref: %q", err) } @@ -317,6 +326,16 @@ func (o *Orchestrator) Build(req *protocol.BuildRequest, resp protocol.ImageBuil } else { bobBaseref += ":latest" } + wsref, err := reference.ParseNamed(wsrefstr) + var additionalAuth []byte + if err == nil { + additionalAuth, err = json.Marshal(reqauth.GetImageBuildAuthFor([]string{ + reference.Domain(wsref), + })) + if err != nil { + return xerrors.Errorf("cannot marshal additional auth: %w", err) + } + } var swr *wsmanapi.StartWorkspaceResponse err = retry(ctx, func(ctx context.Context) (err error) { @@ -360,6 +379,10 @@ func (o *Orchestrator) Build(req *protocol.BuildRequest, resp protocol.ImageBuil Key: ".dockerconfigjson", }, }, + { + Name: "WORKSPACEKIT_BOBPROXY_ADDITIONALAUTH", + Value: string(additionalAuth), + }, {Name: "SUPERVISOR_DEBUG_ENABLE", Value: fmt.Sprintf("%v", log.Log.Logger.IsLevelEnabled(logrus.DebugLevel))}, }, }, @@ -592,7 +615,7 @@ func (o *Orchestrator) getBaseImageRef(ctx context.Context, bs *protocol.BuildSo } } -func (o *Orchestrator) getWorkspaceImageRef(ctx context.Context, baseref string, allowedAuth auth.AllowedAuthFor) (ref string, err error) { +func (o *Orchestrator) getWorkspaceImageRef(ctx context.Context, baseref string) (ref string, err error) { cnt := []byte(fmt.Sprintf("%s\n%d\n", baseref, workspaceBuildProcessVersion)) hash := sha256.New() n, err := hash.Write(cnt) diff --git a/components/server/ee/src/prebuilds/prebuild-manager.ts b/components/server/ee/src/prebuilds/prebuild-manager.ts index 2ef2b4a67c5831..55ce15d2870888 100644 --- a/components/server/ee/src/prebuilds/prebuild-manager.ts +++ b/components/server/ee/src/prebuilds/prebuild-manager.ts @@ -5,7 +5,7 @@ */ import { DBWithTracing, TracedWorkspaceDB, WorkspaceDB } from '@gitpod/gitpod-db/lib'; -import { CommitContext, Project, ProjectEnvVar, StartPrebuildContext, StartPrebuildResult, TaskConfig, User, WorkspaceConfig, WorkspaceInstance } from '@gitpod/gitpod-protocol'; +import { CommitContext, Project, ProjectEnvVarWithValue, StartPrebuildContext, StartPrebuildResult, TaskConfig, User, WorkspaceConfig, WorkspaceInstance } from '@gitpod/gitpod-protocol'; import { log } from '@gitpod/gitpod-protocol/lib/util/logging'; import { TraceContext } from '@gitpod/gitpod-protocol/lib/util/tracing'; import { inject, injectable } from 'inversify'; @@ -162,7 +162,7 @@ export class PrebuildManager { if (!prebuild) { throw new Error('No prebuild found for workspace ' + workspaceId); } - let projectEnvVars: ProjectEnvVar[] = []; + let projectEnvVars: ProjectEnvVarWithValue[] = []; if (workspace.projectId) { projectEnvVars = await this.projectService.getProjectEnvironmentVariables(workspace.projectId); } diff --git a/components/server/src/projects/projects-service.ts b/components/server/src/projects/projects-service.ts index b0d7bf93ea7dfe..a048943376468b 100644 --- a/components/server/src/projects/projects-service.ts +++ b/components/server/src/projects/projects-service.ts @@ -6,7 +6,7 @@ import { inject, injectable } from "inversify"; import { DBWithTracing, ProjectDB, TeamDB, TracedWorkspaceDB, UserDB, WorkspaceDB } from "@gitpod/gitpod-db/lib"; -import { Branch, PrebuildWithStatus, CreateProjectParams, FindPrebuildsParams, Project, ProjectEnvVar, User } from "@gitpod/gitpod-protocol"; +import { Branch, PrebuildWithStatus, CreateProjectParams, FindPrebuildsParams, Project, ProjectEnvVar, User, ProjectEnvVarWithValue } from "@gitpod/gitpod-protocol"; import { HostContextProvider } from "../auth/host-context-provider"; import { RepoURL } from "../repohost"; import { log } from '@gitpod/gitpod-protocol/lib/util/logging'; @@ -205,7 +205,7 @@ export class ProjectsService { return this.projectDB.setProjectEnvironmentVariable(projectId, name, value, censored); } - async getProjectEnvironmentVariables(projectId: string): Promise { + async getProjectEnvironmentVariables(projectId: string): Promise { return this.projectDB.getProjectEnvironmentVariables(projectId); } diff --git a/components/server/src/workspace/gitpod-server-impl.ts b/components/server/src/workspace/gitpod-server-impl.ts index 78e71624a001b6..399159dbf9af76 100644 --- a/components/server/src/workspace/gitpod-server-impl.ts +++ b/components/server/src/workspace/gitpod-server-impl.ts @@ -6,7 +6,7 @@ import { DownloadUrlRequest, DownloadUrlResponse, UploadUrlRequest, UploadUrlResponse } from '@gitpod/content-service/lib/blobs_pb'; import { AppInstallationDB, UserDB, UserMessageViewsDB, WorkspaceDB, DBWithTracing, TracedWorkspaceDB, DBGitpodToken, DBUser, UserStorageResourcesDB, TeamDB, InstallationAdminDB, ProjectDB } from '@gitpod/gitpod-db/lib'; -import { AuthProviderEntry, AuthProviderInfo, CommitContext, Configuration, CreateWorkspaceMode, DisposableCollection, GetWorkspaceTimeoutResult, GitpodClient as GitpodApiClient, GitpodServer, GitpodToken, GitpodTokenType, InstallPluginsParams, PermissionName, PortVisibility, PrebuiltWorkspace, PrebuiltWorkspaceContext, PreparePluginUploadParams, ResolvedPlugins, ResolvePluginsParams, SetWorkspaceTimeoutResult, StartPrebuildContext, StartWorkspaceResult, Terms, Token, UninstallPluginParams, User, UserEnvVar, UserEnvVarValue, UserInfo, WhitelistedRepository, Workspace, WorkspaceContext, WorkspaceCreationResult, WorkspaceImageBuild, WorkspaceInfo, WorkspaceInstance, WorkspaceInstancePort, WorkspaceInstanceUser, WorkspaceTimeoutDuration, GuessGitTokenScopesParams, GuessedGitTokenScopes, Team, TeamMemberInfo, TeamMembershipInvite, CreateProjectParams, Project, ProviderRepository, TeamMemberRole, WithDefaultConfig, FindPrebuildsParams, PrebuildWithStatus, StartPrebuildResult, ClientHeaderFields, Permission, SnapshotContext } from '@gitpod/gitpod-protocol'; +import { AuthProviderEntry, AuthProviderInfo, CommitContext, Configuration, CreateWorkspaceMode, DisposableCollection, GetWorkspaceTimeoutResult, GitpodClient as GitpodApiClient, GitpodServer, GitpodToken, GitpodTokenType, InstallPluginsParams, PermissionName, PortVisibility, PrebuiltWorkspace, PrebuiltWorkspaceContext, PreparePluginUploadParams, ResolvedPlugins, ResolvePluginsParams, SetWorkspaceTimeoutResult, StartPrebuildContext, StartWorkspaceResult, Terms, Token, UninstallPluginParams, User, UserEnvVar, UserEnvVarValue, UserInfo, WhitelistedRepository, Workspace, WorkspaceContext, WorkspaceCreationResult, WorkspaceImageBuild, WorkspaceInfo, WorkspaceInstance, WorkspaceInstancePort, WorkspaceInstanceUser, WorkspaceTimeoutDuration, GuessGitTokenScopesParams, GuessedGitTokenScopes, Team, TeamMemberInfo, TeamMembershipInvite, CreateProjectParams, Project, ProviderRepository, TeamMemberRole, WithDefaultConfig, FindPrebuildsParams, PrebuildWithStatus, StartPrebuildResult, ClientHeaderFields, Permission, SnapshotContext, ProjectEnvVarWithValue } from '@gitpod/gitpod-protocol'; import { AccountStatement } from "@gitpod/gitpod-protocol/lib/accounting-protocol"; import { AdminBlockUserRequest, AdminGetListRequest, AdminGetListResult, AdminGetWorkspacesRequest, AdminModifyPermanentWorkspaceFeatureFlagRequest, AdminModifyRoleOrPermissionRequest, WorkspaceAndInstance } from '@gitpod/gitpod-protocol/lib/admin-protocol'; import { GetLicenseInfoResult, LicenseFeature, LicenseValidationResult } from '@gitpod/gitpod-protocol/lib/license-protocol'; @@ -1582,7 +1582,12 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable { traceAPIParams(ctx, { projectId }); const user = this.checkAndBlockUser("getProjectEnvironmentVariables"); await this.guardProjectOperation(user, projectId, "get"); - return this.projectsService.getProjectEnvironmentVariables(projectId); + const res = (await this.projectsService.getProjectEnvironmentVariables(projectId)).map(ev => { + const envVar: ProjectEnvVar = { ...ev }; + delete (envVar as any)['value']; + return envVar; + }); + return res; } async deleteProjectEnvironmentVariable(ctx: TraceContext, variableId: string): Promise { @@ -1596,7 +1601,7 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable { return this.projectsService.deleteProjectEnvironmentVariable(envVar.id); } - protected async internalGetPublicProjectEnvVars(projectId?: string): Promise { + protected async internalGetPublicProjectEnvVars(projectId?: string): Promise { if (!projectId) { return []; } diff --git a/components/server/src/workspace/image-source-provider.ts b/components/server/src/workspace/image-source-provider.ts index 39cad1aa61e320..10fb4b55a02746 100644 --- a/components/server/src/workspace/image-source-provider.ts +++ b/components/server/src/workspace/image-source-provider.ts @@ -5,7 +5,7 @@ */ import { injectable, inject } from "inversify"; -import { ImageBuilderClientProvider, ResolveBaseImageRequest, BuildRegistryAuthTotal, BuildRegistryAuth } from "@gitpod/image-builder/lib"; +import { ImageBuilderClientProvider } from "@gitpod/image-builder/lib"; import { HostContextProvider } from "../auth/host-context-provider"; import { TraceContext } from "@gitpod/gitpod-protocol/lib/util/tracing"; import { CommitContext, WorkspaceImageSource, WorkspaceConfig, WorkspaceImageSourceReference, WorkspaceImageSourceDocker, ImageConfigFile, ExternalImageConfigFile, User, AdditionalContentContext } from "@gitpod/gitpod-protocol"; @@ -57,27 +57,8 @@ export class ImageSourceProvider { dockerFileHash: lastDockerFileSha, } } else if (typeof (imgcfg) === "string") { - // We resolve this request allowing all configured auth because at this poing we don't have access to the user or permission service. - // If anyone feels like changing this and properly use the REGISTRY_ACCESS permission, be my guest. - // - // This might leak a tiny bit of information in that the workspace start failes - // differently if the image is present as compared to when it's not. This way users can find out if an image exists even if they don't - // have access to the registry themselves. - // - // If feel this issue is negligeble. - const req = new ResolveBaseImageRequest(); - req.setRef(imgcfg); - const allowAll = new BuildRegistryAuthTotal(); - allowAll.setAllowAll(true); - const auth = new BuildRegistryAuth(); - auth.setTotal(allowAll); - req.setAuth(auth); - - const client = this.imagebuilderClientProvider.getDefault(); - const res = await client.resolveBaseImage({ span }, req); - result = { - baseImageResolved: res.getRef() + baseImageResolved: imgcfg } } else { throw new Error("unknown workspace image source config"); diff --git a/components/server/src/workspace/workspace-starter.ts b/components/server/src/workspace/workspace-starter.ts index 56bb9d9155a262..7eb87fedb8f435 100644 --- a/components/server/src/workspace/workspace-starter.ts +++ b/components/server/src/workspace/workspace-starter.ts @@ -6,8 +6,8 @@ import { CloneTargetMode, FileDownloadInitializer, GitAuthMethod, GitConfig, GitInitializer, PrebuildInitializer, SnapshotInitializer, WorkspaceInitializer } from "@gitpod/content-service/lib"; import { CompositeInitializer, FromBackupInitializer } from "@gitpod/content-service/lib/initializer_pb"; -import { DBUser, DBWithTracing, ProjectDB, TracedUserDB, TracedWorkspaceDB, UserDB, WorkspaceDB } from '@gitpod/gitpod-db/lib'; -import { CommitContext, Disposable, GitpodToken, GitpodTokenType, IssueContext, NamedWorkspaceFeatureFlag, PullRequestContext, RefType, SnapshotContext, StartWorkspaceResult, User, UserEnvVar, UserEnvVarValue, WithEnvvarsContext, WithPrebuild, Workspace, WorkspaceContext, WorkspaceImageSource, WorkspaceImageSourceDocker, WorkspaceImageSourceReference, WorkspaceInstance, WorkspaceInstanceConfiguration, WorkspaceInstanceStatus, WorkspaceProbeContext, Permission, HeadlessWorkspaceEvent, HeadlessWorkspaceEventType, DisposableCollection, AdditionalContentContext, ImageConfigFile, ProjectEnvVar, ImageBuildLogInfo } from "@gitpod/gitpod-protocol"; +import { DBUser, DBWithTracing, TracedUserDB, TracedWorkspaceDB, UserDB, WorkspaceDB } from '@gitpod/gitpod-db/lib'; +import { CommitContext, Disposable, GitpodToken, GitpodTokenType, IssueContext, NamedWorkspaceFeatureFlag, PullRequestContext, RefType, SnapshotContext, StartWorkspaceResult, User, UserEnvVar, UserEnvVarValue, WithEnvvarsContext, WithPrebuild, Workspace, WorkspaceContext, WorkspaceImageSource, WorkspaceImageSourceDocker, WorkspaceImageSourceReference, WorkspaceInstance, WorkspaceInstanceConfiguration, WorkspaceInstanceStatus, WorkspaceProbeContext, Permission, HeadlessWorkspaceEvent, HeadlessWorkspaceEventType, DisposableCollection, AdditionalContentContext, ImageConfigFile, ImageBuildLogInfo, ProjectEnvVarWithValue } from "@gitpod/gitpod-protocol"; import { IAnalyticsWriter } from '@gitpod/gitpod-protocol/lib/analytics'; import { log } from '@gitpod/gitpod-protocol/lib/util/logging'; import { TraceContext } from "@gitpod/gitpod-protocol/lib/util/tracing"; @@ -60,9 +60,8 @@ export class WorkspaceStarter { @inject(IAnalyticsWriter) protected readonly analytics: IAnalyticsWriter; @inject(TheiaPluginService) protected readonly theiaService: TheiaPluginService; @inject(OneTimeSecretServer) protected readonly otsServer: OneTimeSecretServer; - @inject(ProjectDB) protected readonly projectDB: ProjectDB; - public async startWorkspace(ctx: TraceContext, workspace: Workspace, user: User, userEnvVars: UserEnvVar[], projectEnvVars: ProjectEnvVar[], options?: StartWorkspaceOptions): Promise { + public async startWorkspace(ctx: TraceContext, workspace: Workspace, user: User, userEnvVars: UserEnvVar[], projectEnvVars: ProjectEnvVarWithValue[], options?: StartWorkspaceOptions): Promise { const span = TraceContext.startSpan("WorkspaceStarter.startWorkspace", ctx); span.setTag("workspaceId", workspace.id); @@ -114,7 +113,8 @@ export class WorkspaceStarter { try { // if we need to build the workspace image we musn't wait for actuallyStartWorkspace to return as that would block the // frontend until the image is built. - needsImageBuild = forceRebuild || await this.needsImageBuild({ span }, user, workspace, instance); + const additionalAuth = this.getAdditionalImageAuth(projectEnvVars); + needsImageBuild = forceRebuild || await this.needsImageBuild({ span }, user, workspace, instance, additionalAuth); if (needsImageBuild) { instance.status.conditions = { neededImageBuild: true, @@ -148,12 +148,13 @@ export class WorkspaceStarter { } // Note: this function does not expect to be awaited for by its caller. This means that it takes care of error handling itself. - protected async actuallyStartWorkspace(ctx: TraceContext, instance: WorkspaceInstance, workspace: Workspace, user: User, mustHaveBackup: boolean, ideConfig: IDEConfig, userEnvVars: UserEnvVar[], projectEnvVars: ProjectEnvVar[], rethrow?: boolean, forceRebuild?: boolean): Promise { + protected async actuallyStartWorkspace(ctx: TraceContext, instance: WorkspaceInstance, workspace: Workspace, user: User, mustHaveBackup: boolean, ideConfig: IDEConfig, userEnvVars: UserEnvVar[], projectEnvVars: ProjectEnvVarWithValue[], rethrow?: boolean, forceRebuild?: boolean): Promise { const span = TraceContext.startSpan("actuallyStartWorkspace", ctx); try { // build workspace image - instance = await this.buildWorkspaceImage({ span }, user, workspace, instance, forceRebuild, forceRebuild); + const additionalAuth = this.getAdditionalImageAuth(projectEnvVars); + instance = await this.buildWorkspaceImage({ span }, user, workspace, instance, additionalAuth, forceRebuild, forceRebuild); let type: WorkspaceType = WorkspaceType.REGULAR; if (workspace.type === 'prebuild') { @@ -259,6 +260,21 @@ export class WorkspaceStarter { } } + protected getAdditionalImageAuth(projectEnvVars: ProjectEnvVarWithValue[]): Map { + const res = new Map(); + const imageAuth = projectEnvVars.find(e => e.name === 'GITPOD_IMAGE_AUTH'); + if (!imageAuth) { + return res; + } + + (imageAuth.value || "") + .split(",") + .map(e => e.trim().split(":")) + .filter(e => e.length == 2) + .forEach(e => res.set(e[0], e[1])); + return res; + } + protected async notifyOnPrebuildQueued(ctx: TraceContext, workspaceId: string) { const span = TraceContext.startSpan("notifyOnPrebuildQueued", ctx); const prebuild = await this.workspaceDb.trace({ span }).findPrebuildByWorkspaceID(workspaceId); @@ -442,7 +458,7 @@ export class WorkspaceStarter { return undefined; } - protected async prepareBuildRequest(ctx: TraceContext, workspace: Workspace, imgsrc: WorkspaceImageSource, user: User, ignoreBaseImageresolvedAndRebuildBase: boolean = false): Promise<{ src: BuildSource, auth: BuildRegistryAuth, disposable?: Disposable }> { + protected async prepareBuildRequest(ctx: TraceContext, workspace: Workspace, imgsrc: WorkspaceImageSource, user: User, additionalAuth: Map, ignoreBaseImageresolvedAndRebuildBase: boolean = false): Promise<{ src: BuildSource, auth: BuildRegistryAuth, disposable?: Disposable }> { const span = TraceContext.startSpan("prepareBuildRequest", ctx); try { @@ -483,6 +499,7 @@ export class WorkspaceStarter { selectiveAuth.setAnyOfList(this.config.defaultBaseImageRegistryWhitelist); auth.setSelective(selectiveAuth); } + additionalAuth.forEach((val, key) => auth.getAdditionalMap().set(key, val)); if (WorkspaceImageSourceDocker.is(imgsrc)) { let source: WorkspaceInitializer; const disp = new DisposableCollection(); @@ -540,11 +557,11 @@ export class WorkspaceStarter { } } - protected async needsImageBuild(ctx: TraceContext, user: User, workspace: Workspace, instance: WorkspaceInstance): Promise { + protected async needsImageBuild(ctx: TraceContext, user: User, workspace: Workspace, instance: WorkspaceInstance, additionalAuth: Map): Promise { const span = TraceContext.startSpan("needsImageBuild", ctx); try { const client = this.imagebuilderClientProvider.getDefault(); - const { src, auth, disposable } = await this.prepareBuildRequest({ span }, workspace, workspace.imageSource!, user); + const { src, auth, disposable } = await this.prepareBuildRequest({ span }, workspace, workspace.imageSource!, user, additionalAuth); const req = new ResolveWorkspaceImageRequest(); req.setSource(src); @@ -564,18 +581,18 @@ export class WorkspaceStarter { } } - protected async buildWorkspaceImage(ctx: TraceContext, user: User, workspace: Workspace, instance: WorkspaceInstance, ignoreBaseImageresolvedAndRebuildBase: boolean = false, forceRebuild: boolean = false): Promise { + protected async buildWorkspaceImage(ctx: TraceContext, user: User, workspace: Workspace, instance: WorkspaceInstance, additionalAuth: Map, ignoreBaseImageresolvedAndRebuildBase: boolean = false, forceRebuild: boolean = false): Promise { const span = TraceContext.startSpan("buildWorkspaceImage", ctx); try { // Start build... const client = this.imagebuilderClientProvider.getDefault(); - const { src, auth, disposable } = await this.prepareBuildRequest({ span }, workspace, workspace.imageSource!, user, ignoreBaseImageresolvedAndRebuildBase || forceRebuild); + const { src, auth, disposable } = await this.prepareBuildRequest({ span }, workspace, workspace.imageSource!, user, additionalAuth, ignoreBaseImageresolvedAndRebuildBase || forceRebuild); const req = new BuildRequest(); req.setSource(src); req.setAuth(auth); - req.setForcerebuild(forceRebuild); + req.setForceRebuild(forceRebuild); // Make sure we persist logInfo as soon as we retrieve it const imageBuildLogInfo = new Deferred(); @@ -612,7 +629,7 @@ export class WorkspaceStarter { if (err && err.message && err.message.includes("base image does not exist") && !ignoreBaseImageresolvedAndRebuildBase) { // we've attempted to add the base layer for a workspace whoose base image has gone missing. // Ignore the previously built (now missing) base image and force a rebuild. - return this.buildWorkspaceImage(ctx, user, workspace, instance, true, forceRebuild); + return this.buildWorkspaceImage(ctx, user, workspace, instance, additionalAuth, true, forceRebuild); } else { throw err; } @@ -661,7 +678,7 @@ export class WorkspaceStarter { } } - protected async createSpec(traceCtx: TraceContext, user: User, workspace: Workspace, instance: WorkspaceInstance, mustHaveBackup: boolean, ideConfig: IDEConfig, userEnvVars: UserEnvVarValue[], projectEnvVars: ProjectEnvVar[]): Promise { + protected async createSpec(traceCtx: TraceContext, user: User, workspace: Workspace, instance: WorkspaceInstance, mustHaveBackup: boolean, ideConfig: IDEConfig, userEnvVars: UserEnvVarValue[], projectEnvVars: ProjectEnvVarWithValue[]): Promise { const context = workspace.context; let allEnvVars: EnvVarWithValue[] = []; @@ -674,8 +691,7 @@ export class WorkspaceStarter { } } if (projectEnvVars.length > 0) { - const projectEnvVarsWithValues = await this.projectDB.getProjectEnvironmentVariableValues(projectEnvVars); - allEnvVars = allEnvVars.concat(projectEnvVarsWithValues); + allEnvVars = allEnvVars.concat(projectEnvVars); } if (WithEnvvarsContext.is(context)) { allEnvVars = allEnvVars.concat(context.envvars);