Skip to content

Commit

Permalink
[server] Remove deprecated image build log mechanism
Browse files Browse the repository at this point in the history
  • Loading branch information
csweichel committed May 20, 2022
1 parent 476fab2 commit c1508ba
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 87 deletions.
53 changes: 6 additions & 47 deletions components/server/src/workspace/gitpod-server-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ import {
RemotePageMessage,
RemoteTrackMessage,
} from "@gitpod/gitpod-protocol/lib/analytics";
import { ImageBuilderClientProvider, LogsRequest } from "@gitpod/image-builder/lib";
import { ImageBuilderClientProvider } from "@gitpod/image-builder/lib";
import { WorkspaceManagerClientProvider } from "@gitpod/ws-manager/lib/client-provider";
import {
ControlPortRequest,
Expand Down Expand Up @@ -1559,11 +1559,11 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable {
// during roll-out this is our fall-back case.
// Afterwards we might want to do some spinning-lock and re-check for a certain period (30s?) to give db-sync
// a change to move the imageBuildLogInfo across the globe.

log.warn(logCtx, "imageBuild logs: fallback!");
ctx.span?.setTag("workspace.imageBuild.logs.fallback", true);
await this.deprecatedDoWatchWorkspaceImageBuildLogs(ctx, logCtx, user, workspace);
return;
log.error(logCtx, "cannot watch imagebuild logs for workspaceId: no image build info available");
throw new ResponseError(
ErrorCodes.HEADLESS_LOG_NOT_YET_AVAILABLE,
"cannot watch imagebuild logs for workspaceId",
);
}

const aborted = new Deferred<boolean>();
Expand Down Expand Up @@ -1608,47 +1608,6 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable {
}
}

protected async deprecatedDoWatchWorkspaceImageBuildLogs(
ctx: TraceContext,
logCtx: LogContext,
user: User,
workspace: Workspace,
) {
if (!workspace.imageNameResolved) {
log.debug(logCtx, `No imageNameResolved set for workspaceId, cannot watch logs.`);
return;
}

try {
const imgbuilder = await this.imageBuilderClientProvider.getDefault(
user,
workspace,
{} as WorkspaceInstance,
);
const req = new LogsRequest();
req.setCensored(true);
req.setBuildRef(workspace.imageNameResolved);

let lineCount = 0;
await imgbuilder.logs(ctx, req, (data) => {
if (!this.client) {
return "stop";
}
data = data.replace("\n", WorkspaceImageBuild.LogLine.DELIMITER);
lineCount += data.split(WorkspaceImageBuild.LogLine.DELIMITER_REGEX).length;

this.client.onWorkspaceImageBuildLogs(undefined as any, {
text: data,
isDiff: true,
upToLine: lineCount,
});
return "continue";
});
} catch (err) {
log.error(logCtx, `cannot watch logs for workspaceId`, err);
}
}

async getHeadlessLog(ctx: TraceContext, instanceId: string): Promise<HeadlessLogUrls> {
traceAPIParams(ctx, { instanceId });

Expand Down
106 changes: 66 additions & 40 deletions components/ws-manager-api/typescript/src/client-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,33 @@
* See License-AGPL.txt in the project root for license information.
*/

import { createClientCallMetricsInterceptor, IClientCallMetrics } from "@gitpod/content-service/lib/client-call-metrics";
import {
createClientCallMetricsInterceptor,
IClientCallMetrics,
} from "@gitpod/content-service/lib/client-call-metrics";
import { Disposable, Workspace, WorkspaceInstance } from "@gitpod/gitpod-protocol";
import { defaultGRPCOptions } from '@gitpod/gitpod-protocol/lib/util/grpc';
import { log } from '@gitpod/gitpod-protocol/lib/util/logging';
import { WorkspaceClusterWoTLS, WorkspaceManagerConnectionInfo } from '@gitpod/gitpod-protocol/lib/workspace-cluster';
import { defaultGRPCOptions } from "@gitpod/gitpod-protocol/lib/util/grpc";
import { log } from "@gitpod/gitpod-protocol/lib/util/logging";
import { WorkspaceClusterWoTLS, WorkspaceManagerConnectionInfo } from "@gitpod/gitpod-protocol/lib/workspace-cluster";
import * as grpc from "@grpc/grpc-js";
import { inject, injectable, optional } from 'inversify';
import { WorkspaceManagerClientProviderCompositeSource, WorkspaceManagerClientProviderSource } from "./client-provider-source";
import { inject, injectable, optional } from "inversify";
import {
WorkspaceManagerClientProviderCompositeSource,
WorkspaceManagerClientProviderSource,
} from "./client-provider-source";
import { ExtendedUser, workspaceClusterSetsAuthorized } from "./constraints";
import { WorkspaceManagerClient } from './core_grpc_pb';
import { WorkspaceManagerClient } from "./core_grpc_pb";
import { linearBackoffStrategy, PromisifiedWorkspaceManagerClient } from "./promisified-client";

export const IWorkspaceManagerClientCallMetrics = Symbol('IWorkspaceManagerClientCallMetrics')
export const IWorkspaceManagerClientCallMetrics = Symbol("IWorkspaceManagerClientCallMetrics");

@injectable()
export class WorkspaceManagerClientProvider implements Disposable {
@inject(WorkspaceManagerClientProviderCompositeSource)
protected readonly source: WorkspaceManagerClientProviderSource;

@inject(IWorkspaceManagerClientCallMetrics) @optional()
@inject(IWorkspaceManagerClientCallMetrics)
@optional()
protected readonly clientCallMetrics: IClientCallMetrics;

// gRPC connections maintain their connectivity themselves, i.e. they reconnect when neccesary.
Expand All @@ -41,25 +48,31 @@ export class WorkspaceManagerClientProvider implements Disposable {
* @param instance the instance we want to start
* @returns a set of workspace clusters we can start the workspace in
*/
public async getStartClusterSets(user: ExtendedUser, workspace: Workspace, instance: WorkspaceInstance): Promise<IWorkspaceClusterStartSet> {
public async getStartClusterSets(
user: ExtendedUser,
workspace: Workspace,
instance: WorkspaceInstance,
): Promise<IWorkspaceClusterStartSet> {
const allClusters = await this.source.getAllWorkspaceClusters();
const availableClusters = allClusters.filter(c => c.score > 0 && c.state === "available");
const availableClusters = allClusters.filter((c) => c.score > 0 && c.state === "available");

const sets = workspaceClusterSetsAuthorized.map(constraints => {
const r = constraints.constraint(availableClusters, user, workspace, instance);
if (!r) {
return;
}
return new ClusterSet(this, r);
}).filter(s => s !== undefined) as ClusterSet[];
const sets = workspaceClusterSetsAuthorized
.map((constraints) => {
const r = constraints.constraint(availableClusters, user, workspace, instance);
if (!r) {
return;
}
return new ClusterSet(this, r);
})
.filter((s) => s !== undefined) as ClusterSet[];

return {
[Symbol.asyncIterator]: (): AsyncIterator<ClusterClientEntry> => {
return {
next: async (): Promise<IteratorResult<ClusterClientEntry>> => {
while (true) {
if (sets.length === 0) {
return {done: true, value: undefined};
return { done: true, value: undefined };
}

let res = await sets[0].next();
Expand All @@ -70,10 +83,10 @@ export class WorkspaceManagerClientProvider implements Disposable {

return res;
}
}
}
}
}
},
};
},
};
}

/**
Expand All @@ -92,7 +105,7 @@ export class WorkspaceManagerClientProvider implements Disposable {
let client = this.connectionCache.get(name);
if (!client) {
const info = await getConnectionInfo();
client = client = this.createConnection(WorkspaceManagerClient, info, grpcOptions);
client = this.createConnection(WorkspaceManagerClient, info, grpcOptions);
this.connectionCache.set(name, client);
} else if (client.getChannel().getConnectivityState(true) != grpc.connectivityState.READY) {
client.close();
Expand All @@ -105,11 +118,16 @@ export class WorkspaceManagerClientProvider implements Disposable {

let interceptor: grpc.Interceptor[] = [];
if (this.clientCallMetrics) {
interceptor = [ createClientCallMetricsInterceptor(this.clientCallMetrics) ];
interceptor = [createClientCallMetricsInterceptor(this.clientCallMetrics)];
}

const stopSignal = { stop: false };
return new PromisifiedWorkspaceManagerClient(client, linearBackoffStrategy(30, 1000, stopSignal), interceptor, stopSignal);
return new PromisifiedWorkspaceManagerClient(
client,
linearBackoffStrategy(30, 1000, stopSignal),
interceptor,
stopSignal,
);
}

/**
Expand All @@ -119,10 +137,14 @@ export class WorkspaceManagerClientProvider implements Disposable {
return this.source.getAllWorkspaceClusters();
}

public createConnection<T extends grpc.Client>(creator: { new(address: string, credentials: grpc.ChannelCredentials, options?: grpc.ClientOptions): T }, info: WorkspaceManagerConnectionInfo, grpcOptions?: object): T {
public createConnection<T extends grpc.Client>(
creator: { new (address: string, credentials: grpc.ChannelCredentials, options?: grpc.ClientOptions): T },
info: WorkspaceManagerConnectionInfo,
grpcOptions?: object,
): T {
const options: Partial<grpc.ClientOptions> = {
...grpcOptions,
'grpc.ssl_target_name_override': "ws-manager", // this makes sure we can call ws-manager with a URL different to "ws-manager"
"grpc.ssl_target_name_override": "ws-manager", // this makes sure we can call ws-manager with a URL different to "ws-manager"
};

let credentials: grpc.ChannelCredentials;
Expand All @@ -140,27 +162,33 @@ export class WorkspaceManagerClientProvider implements Disposable {
}

public dispose() {
Array.from(this.connectionCache.values()).map(c => c.close());
Array.from(this.connectionCache.values()).map((c) => c.close());
}
}

export interface IWorkspaceClusterStartSet extends AsyncIterable<ClusterClientEntry> {}

export interface ClusterClientEntry { manager: PromisifiedWorkspaceManagerClient, installation: string }
export interface ClusterClientEntry {
manager: PromisifiedWorkspaceManagerClient;
installation: string;
}

/**
* ClusterSet is an iterator
*/
class ClusterSet implements AsyncIterator<ClusterClientEntry> {
protected usedCluster: string[] = [];
constructor(protected readonly provider: WorkspaceManagerClientProvider, protected readonly cluster: WorkspaceClusterWoTLS[]) {}
constructor(
protected readonly provider: WorkspaceManagerClientProvider,
protected readonly cluster: WorkspaceClusterWoTLS[],
) {}

public async next(): Promise<IteratorResult<ClusterClientEntry>> {
const available = this.cluster.filter(c => !this.usedCluster.includes(c.name));
const available = this.cluster.filter((c) => !this.usedCluster.includes(c.name));
const chosenCluster = chooseCluster(available);
if (!chosenCluster) {
// empty set
return {done: true, value: undefined };
return { done: true, value: undefined };
}
this.usedCluster.push(chosenCluster.name);

Expand All @@ -173,7 +201,7 @@ class ClusterSet implements AsyncIterator<ClusterClientEntry> {
value: {
manager: client,
installation: chosenCluster.name,
}
},
};
}
}
Expand All @@ -185,7 +213,7 @@ class ClusterSet implements AsyncIterator<ClusterClientEntry> {
*/
function chooseCluster(availableCluster: WorkspaceClusterWoTLS[]): WorkspaceClusterWoTLS {
const scoreFunc = (c: WorkspaceClusterWoTLS): number => {
let score = c.score; // here is the point where we may want to implement non-static approaches
let score = c.score; // here is the point where we may want to implement non-static approaches

// clamp to maxScore
if (score > c.maxScore) {
Expand All @@ -194,14 +222,12 @@ function chooseCluster(availableCluster: WorkspaceClusterWoTLS[]): WorkspaceClus
return score;
};

const scoreSum = availableCluster
.map(scoreFunc)
.reduce((sum, cScore) => cScore + sum, 0);
const pNormalized = availableCluster.map(c => scoreFunc(c) / scoreSum);
const scoreSum = availableCluster.map(scoreFunc).reduce((sum, cScore) => cScore + sum, 0);
const pNormalized = availableCluster.map((c) => scoreFunc(c) / scoreSum);
const p = Math.random();
let pSummed = 0;
for (let i = 0; i < availableCluster.length; i++) {
pSummed += pNormalized[i]
pSummed += pNormalized[i];
if (p <= pSummed) {
return availableCluster[i];
}
Expand Down

0 comments on commit c1508ba

Please sign in to comment.