Skip to content

Commit e83c1f4

Browse files
committed
WIP: [image-builder, et. al] Switch image build logs to work server-to-imagebuild workspace instead of server-to-imagebuilder
The goal here is to make image build logs accessible from all meta clusters. This helps to move forward with removing the cross-cluster messagebus dependency while image-builder(s) still runs in the meta cluster(s).
1 parent 1f259aa commit e83c1f4

File tree

16 files changed

+232
-75
lines changed

16 files changed

+232
-75
lines changed

components/gitpod-db/src/typeorm/entity/db-workspace.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import { PrimaryColumn, Column, Entity, Index } from "typeorm";
88

9-
import { Workspace, WorkspaceConfig, WorkspaceContext, WorkspaceImageSource, WorkspaceType, WorkspaceSoftDeletion } from "@gitpod/gitpod-protocol";
9+
import { Workspace, WorkspaceConfig, WorkspaceContext, WorkspaceImageSource, WorkspaceType, WorkspaceSoftDeletion, ImageBuildLogInfo } from "@gitpod/gitpod-protocol";
1010
import { TypeORM } from "../typeorm";
1111
import { Transformer } from "../transformer";
1212

@@ -105,4 +105,7 @@ export class DBWorkspace implements Workspace {
105105
})
106106
@Index('ind_basedOnSnapshotId')
107107
basedOnSnapshotId?: string;
108+
109+
@Column("simple-json", { nullable: true })
110+
imageBuildLogInfo?: ImageBuildLogInfo;
108111
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import {MigrationInterface, QueryRunner} from "typeorm";
2+
import { columnExists } from "./helper/helper";
3+
4+
export class WorkspaceImageBuildLogInfo1642088620203 implements MigrationInterface {
5+
6+
public async up(queryRunner: QueryRunner): Promise<void> {
7+
if (!(await columnExists(queryRunner, "d_b_workspace", "imageBuildLogInfo"))) {
8+
await queryRunner.query("ALTER TABLE d_b_workspace ADD COLUMN `imageBuildLogInfo` text NULL");
9+
}
10+
}
11+
12+
public async down(queryRunner: QueryRunner): Promise<void> {
13+
}
14+
15+
}

components/gitpod-db/src/typeorm/workspace-db-impl.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import { DBPrebuiltWorkspace } from "./entity/db-prebuilt-workspace";
2020
import { DBPrebuiltWorkspaceUpdatable } from "./entity/db-prebuilt-workspace-updatable";
2121
import { BUILTIN_WORKSPACE_PROBE_USER_ID } from "../user-db";
2222
import { DBPrebuildInfo } from "./entity/db-prebuild-info-entry";
23-
import { FindInstanceOptions } from "..";
2423

2524
type RawTo<T> = (instance: WorkspaceInstance, ws: Workspace) => T;
2625
interface OrderBy {

components/gitpod-db/src/workspace-db.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,6 @@ export interface PrebuildWithWorkspace {
4646
workspace: Workspace;
4747
}
4848

49-
export type FindInstanceOptions = Partial<Pick<
50-
WorkspaceInstance, "region"> & {
51-
phasePersisted: WorkspaceInstancePhase;
52-
}>;
53-
5449
export type WorkspaceAndOwner = Pick<Workspace, "id" | "ownerId">;
5550
export type WorkspaceOwnerAndSoftDeleted = Pick<Workspace, "id" | "ownerId" | "softDeleted">;
5651

components/gitpod-protocol/src/protocol.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,12 +448,19 @@ export interface Workspace {
448448
basedOnPrebuildId?: string;
449449

450450
basedOnSnapshotId?: string;
451+
452+
imageBuildLogInfo?: ImageBuildLogInfo;
451453
}
452454

453455
export type WorkspaceSoftDeletion = "user" | "gc";
454456

455457
export type WorkspaceType = "regular" | "prebuild" | "probe";
456458

459+
export interface ImageBuildLogInfo {
460+
url: string,
461+
headers: { [key: string]: string },
462+
}
463+
457464
export namespace Workspace {
458465

459466
export function getFullRepositoryName(ws: Workspace): string | undefined {

components/image-builder-api/go/imgbuilder.pb.go

Lines changed: 91 additions & 61 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

components/image-builder-api/go/imgbuilder_grpc.pb.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

components/image-builder-api/go/mock/mock.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

components/image-builder-api/imgbuilder.proto

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,4 +128,6 @@ message BuildInfo {
128128
BuildStatus status = 2;
129129
int64 started_at = 3;
130130
string build_id = 5;
131+
string log_url = 6;
132+
map<string, string> log_url_extra_headers = 7;
131133
}

components/image-builder-api/typescript/src/imgbuilder_grpc_pb.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright (c) 2021 Gitpod GmbH. All rights reserved.
2+
* Copyright (c) 2022 Gitpod GmbH. All rights reserved.
33
* Licensed under the GNU Affero General Public License (AGPL).
44
* See License-AGPL.txt in the project root for license information.
55
*/

components/image-builder-api/typescript/src/imgbuilder_grpc_pb.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright (c) 2021 Gitpod GmbH. All rights reserved.
2+
* Copyright (c) 2022 Gitpod GmbH. All rights reserved.
33
* Licensed under the GNU Affero General Public License (AGPL).
44
* See License-AGPL.txt in the project root for license information.
55
*/

components/image-builder-api/typescript/src/imgbuilder_pb.d.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright (c) 2021 Gitpod GmbH. All rights reserved.
2+
* Copyright (c) 2022 Gitpod GmbH. All rights reserved.
33
* Licensed under the GNU Affero General Public License (AGPL).
44
* See License-AGPL.txt in the project root for license information.
55
*/
@@ -455,6 +455,11 @@ export class BuildInfo extends jspb.Message {
455455
setStartedAt(value: number): BuildInfo;
456456
getBuildId(): string;
457457
setBuildId(value: string): BuildInfo;
458+
getLogUrl(): string;
459+
setLogUrl(value: string): BuildInfo;
460+
461+
getLogUrlExtraHeadersMap(): jspb.Map<string, string>;
462+
clearLogUrlExtraHeadersMap(): void;
458463

459464
serializeBinary(): Uint8Array;
460465
toObject(includeInstance?: boolean): BuildInfo.AsObject;
@@ -473,6 +478,9 @@ export namespace BuildInfo {
473478
status: BuildStatus,
474479
startedAt: number,
475480
buildId: string,
481+
logUrl: string,
482+
483+
logUrlExtraHeadersMap: Array<[string, string]>,
476484
}
477485
}
478486

components/image-builder-api/typescript/src/imgbuilder_pb.js

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright (c) 2021 Gitpod GmbH. All rights reserved.
2+
* Copyright (c) 2022 Gitpod GmbH. All rights reserved.
33
* Licensed under the GNU Affero General Public License (AGPL).
44
* See License-AGPL.txt in the project root for license information.
55
*/
@@ -3426,7 +3426,9 @@ proto.builder.BuildInfo.toObject = function(includeInstance, msg) {
34263426
baseRef: jspb.Message.getFieldWithDefault(msg, 4, ""),
34273427
status: jspb.Message.getFieldWithDefault(msg, 2, 0),
34283428
startedAt: jspb.Message.getFieldWithDefault(msg, 3, 0),
3429-
buildId: jspb.Message.getFieldWithDefault(msg, 5, "")
3429+
buildId: jspb.Message.getFieldWithDefault(msg, 5, ""),
3430+
logUrl: jspb.Message.getFieldWithDefault(msg, 6, ""),
3431+
logUrlExtraHeadersMap: (f = msg.getLogUrlExtraHeadersMap()) ? f.toObject(includeInstance, undefined) : []
34303432
};
34313433

34323434
if (includeInstance) {
@@ -3483,6 +3485,16 @@ proto.builder.BuildInfo.deserializeBinaryFromReader = function(msg, reader) {
34833485
var value = /** @type {string} */ (reader.readString());
34843486
msg.setBuildId(value);
34853487
break;
3488+
case 6:
3489+
var value = /** @type {string} */ (reader.readString());
3490+
msg.setLogUrl(value);
3491+
break;
3492+
case 7:
3493+
var value = msg.getLogUrlExtraHeadersMap();
3494+
reader.readMessage(value, function(message, reader) {
3495+
jspb.Map.deserializeBinary(message, reader, jspb.BinaryReader.prototype.readString, jspb.BinaryReader.prototype.readString, null, "", "");
3496+
});
3497+
break;
34863498
default:
34873499
reader.skipField();
34883500
break;
@@ -3547,6 +3559,17 @@ proto.builder.BuildInfo.serializeBinaryToWriter = function(message, writer) {
35473559
f
35483560
);
35493561
}
3562+
f = message.getLogUrl();
3563+
if (f.length > 0) {
3564+
writer.writeString(
3565+
6,
3566+
f
3567+
);
3568+
}
3569+
f = message.getLogUrlExtraHeadersMap(true);
3570+
if (f && f.getLength() > 0) {
3571+
f.serializeBinary(7, writer, jspb.BinaryWriter.prototype.writeString, jspb.BinaryWriter.prototype.writeString);
3572+
}
35503573
};
35513574

35523575

@@ -3640,6 +3663,46 @@ proto.builder.BuildInfo.prototype.setBuildId = function(value) {
36403663
};
36413664

36423665

3666+
/**
3667+
* optional string log_url = 6;
3668+
* @return {string}
3669+
*/
3670+
proto.builder.BuildInfo.prototype.getLogUrl = function() {
3671+
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 6, ""));
3672+
};
3673+
3674+
3675+
/**
3676+
* @param {string} value
3677+
* @return {!proto.builder.BuildInfo} returns this
3678+
*/
3679+
proto.builder.BuildInfo.prototype.setLogUrl = function(value) {
3680+
return jspb.Message.setProto3StringField(this, 6, value);
3681+
};
3682+
3683+
3684+
/**
3685+
* map<string, string> log_url_extra_headers = 7;
3686+
* @param {boolean=} opt_noLazyCreate Do not create the map if
3687+
* empty, instead returning `undefined`
3688+
* @return {!jspb.Map<string,string>}
3689+
*/
3690+
proto.builder.BuildInfo.prototype.getLogUrlExtraHeadersMap = function(opt_noLazyCreate) {
3691+
return /** @type {!jspb.Map<string,string>} */ (
3692+
jspb.Message.getMapField(this, 7, opt_noLazyCreate,
3693+
null));
3694+
};
3695+
3696+
3697+
/**
3698+
* Clears values from the map. The map will be non-null.
3699+
* @return {!proto.builder.BuildInfo} returns this
3700+
*/
3701+
proto.builder.BuildInfo.prototype.clearLogUrlExtraHeadersMap = function() {
3702+
this.getLogUrlExtraHeadersMap().clear();
3703+
return this;};
3704+
3705+
36433706
/**
36443707
* @enum {number}
36453708
*/

components/image-builder-api/typescript/src/sugar.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { BuildRequest, BuildResponse, BuildStatus, LogsRequest, LogsResponse, Re
1515
import { injectable, inject, optional } from 'inversify';
1616
import * as grpc from "@grpc/grpc-js";
1717
import { TextDecoder } from "util";
18+
import { ImageBuildLogInfo } from "@gitpod/gitpod-protocol";
1819

1920
export const ImageBuilderClientProvider = Symbol("ImageBuilderClientProvider");
2021

@@ -82,6 +83,7 @@ export class CachingImageBuilderClientProvider implements ImageBuilderClientProv
8283
// StagedBuildResponse captures the multi-stage nature (starting, running, done) of image builds.
8384
export interface StagedBuildResponse {
8485
buildPromise: Promise<BuildResponse>;
86+
logPromise: Promise<ImageBuildLogInfo>;
8587

8688
actuallyNeedsBuild: boolean;
8789
ref: string;
@@ -133,9 +135,11 @@ export class PromisifiedImageBuilderClient {
133135
const span = TraceContext.startSpan(`/image-builder/build`, ctx);
134136

135137
const buildResult = new Deferred<BuildResponse>();
138+
const logInfo = new Deferred<ImageBuildLogInfo>();
136139

137140
const result = new Deferred<StagedBuildResponse>();
138141
const resultResp: StagedBuildResponse = {
142+
logPromise: logInfo.promise,
139143
buildPromise: buildResult.promise,
140144
actuallyNeedsBuild: true,
141145
ref: "unknown",
@@ -151,6 +155,7 @@ export class PromisifiedImageBuilderClient {
151155
result.reject(err);
152156
} else {
153157
buildResult.reject(err);
158+
logInfo.reject(err);
154159
}
155160

156161
TraceContext.setError({ span }, err);
@@ -166,6 +171,21 @@ export class PromisifiedImageBuilderClient {
166171
resultResp.baseRef = resp.getBaseRef();
167172
}
168173

174+
if (resp.hasInfo() && !logInfo.isResolved) {
175+
// assumes that log info stays stable for instance lifetime
176+
const info = resp.getInfo()
177+
if (info) {
178+
const headers: { [key: string]: string } = {};
179+
for (const [k, v] of info.getLogUrlExtraHeadersMap().entries()) {
180+
headers[k] = v;
181+
}
182+
logInfo.resolve({
183+
url: info.getLogUrl(),
184+
headers,
185+
});
186+
}
187+
}
188+
169189
if (resp.getStatus() == BuildStatus.RUNNING) {
170190
resultResp.actuallyNeedsBuild = true;
171191
result.resolve(resultResp);
@@ -177,6 +197,9 @@ export class PromisifiedImageBuilderClient {
177197
} else {
178198
buildResult.resolve(resp);
179199
}
200+
if (!logInfo.isResolved) {
201+
logInfo.reject(new Error("no log stream for this image build"));
202+
}
180203

181204
span.finish();
182205
}

components/image-builder-mk3/pkg/orchestrator/monitor.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"github.com/hashicorp/go-retryablehttp"
2121
"golang.org/x/xerrors"
2222

23+
"github.com/gitpod-io/gitpod/common-go/kubernetes"
2324
"github.com/gitpod-io/gitpod/common-go/log"
2425
"github.com/gitpod-io/gitpod/common-go/tracing"
2526
"github.com/gitpod-io/gitpod/image-builder/api"
@@ -202,6 +203,10 @@ func extractBuildStatus(status *wsmanapi.WorkspaceStatus) *api.BuildInfo {
202203
BaseRef: status.Metadata.Annotations[annotationBaseRef],
203204
Status: s,
204205
StartedAt: status.Metadata.StartedAt.Seconds,
206+
LogUrl: fmt.Sprintf("%s/_supervisor/v1", status.Metadata.Annotations[kubernetes.WorkspaceURLAnnotation]),
207+
LogUrlExtraHeaders: map[string]string{
208+
"x-gitpod-owner-token": status.Auth.OwnerToken,
209+
},
205210
}
206211
}
207212

components/server/src/workspace/workspace-starter.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,13 @@ export class WorkspaceStarter {
516516
span.log({"ref": workspace.imageNameResolved});
517517
await this.workspaceDb.trace({ span }).store(workspace);
518518

519+
// Make sure we persist logInfo once we retrieve it
520+
result.logPromise.then(async logInfo => {
521+
await this.workspaceDb.trace({span}).updatePartial(workspace.id, {
522+
imageBuildLogInfo: logInfo,
523+
}).catch(err => log.error("error writing image build log info to the DB", err));
524+
}).catch(err => log.warn("image build: never received log info"));
525+
519526
// Update workspace instance to tell the world we're building an image
520527
const workspaceImage = result.ref;
521528
const status: WorkspaceInstanceStatus = result.actuallyNeedsBuild ? { ...instance.status, phase: 'preparing' } : instance.status;

0 commit comments

Comments
 (0)