Skip to content

Commit fec28cc

Browse files
committed
[server] Broadcast headless updates to subscribers
1 parent e76af59 commit fec28cc

File tree

4 files changed

+81
-9
lines changed

4 files changed

+81
-9
lines changed

components/gitpod-protocol/src/redis.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44
* See License.AGPL.txt in the project root for license information.
55
*/
66

7+
import { HeadlessWorkspaceEventType } from "./headless-workspace-log";
78
import { PrebuiltWorkspaceState } from "./protocol";
89

910
export const WorkspaceInstanceUpdatesChannel = "chan:workspace-instances";
1011
export const PrebuildUpdatesChannel = "chan:prebuilds";
12+
export const HeadlessUpdatesChannel = "chan:headless";
1113

1214
export type RedisWorkspaceInstanceUpdate = {
1315
ownerID: string;
@@ -21,3 +23,8 @@ export type RedisPrebuildUpdate = {
2123
workspaceID: string;
2224
projectID: string;
2325
};
26+
27+
export type RedisHeadlessUpdate = {
28+
workspaceID: string;
29+
type: HeadlessWorkspaceEventType;
30+
};

components/server/src/messaging/redis-subscriber.ts

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,19 @@
77
import {
88
HeadlessWorkspaceEventListener,
99
LocalMessageBroker,
10+
LocalRabbitMQBackedMessageBroker,
1011
PrebuildUpdateListener,
1112
WorkspaceInstanceUpdateListener,
1213
} from "./local-message-broker";
1314
import { inject, injectable } from "inversify";
1415
import {
1516
Disposable,
1617
DisposableCollection,
18+
HeadlessUpdatesChannel,
19+
HeadlessWorkspaceEvent,
1720
PrebuildUpdatesChannel,
1821
PrebuildWithStatus,
22+
RedisHeadlessUpdate,
1923
RedisPrebuildUpdate,
2024
RedisWorkspaceInstanceUpdate,
2125
WorkspaceInstanceUpdatesChannel,
@@ -39,11 +43,16 @@ export class RedisSubscriber implements LocalMessageBroker {
3943

4044
protected workspaceInstanceUpdateListeners: Map<string, WorkspaceInstanceUpdateListener[]> = new Map();
4145
protected prebuildUpdateListeners: Map<string, PrebuildUpdateListener[]> = new Map();
46+
protected headlessWorkspaceEventListeners: Map<string, HeadlessWorkspaceEventListener[]> = new Map();
4247

4348
protected readonly disposables = new DisposableCollection();
4449

4550
async start(): Promise<void> {
51+
<<<<<<< HEAD
4652
const channels = [WorkspaceInstanceUpdatesChannel, PrebuildUpdatesChannel];
53+
=======
54+
const channels = [WorkspaceInstanceUpdatesChannel, HeadlessUpdatesChannel];
55+
>>>>>>> 65ff502c2 ([server] Broadcast headless updates to subscribers)
4756

4857
for (const chan of channels) {
4958
await this.redis.subscribe(chan);
@@ -81,16 +90,27 @@ export class RedisSubscriber implements LocalMessageBroker {
8190
return this.onInstanceUpdate(JSON.parse(message) as RedisWorkspaceInstanceUpdate);
8291

8392
case PrebuildUpdatesChannel:
84-
const headlessUpdateEnabled = await this.isRedisPubSubByTypeEnabled("prebuild-updatable");
85-
if (!headlessUpdateEnabled) {
86-
log.debug("[redis] Redis headless update is disabled through feature flag", {
93+
const prebuildTypeEnabled = await this.isRedisPubSubByTypeEnabled("prebuild");
94+
if (!prebuildTypeEnabled) {
95+
log.debug("[redis] Redis prebuild update is disabled through feature flag", {
8796
channel,
8897
message,
8998
});
9099
return;
91100
}
92101
return this.onPrebuildUpdate(JSON.parse(message) as RedisPrebuildUpdate);
93102

103+
case HeadlessUpdatesChannel:
104+
const headlessTypeEnabled = await this.isRedisPubSubByTypeEnabled("prebuild-updatable");
105+
if (!headlessTypeEnabled) {
106+
log.debug("[redis] Redis headless update is disabled through feature flag", {
107+
channel,
108+
message,
109+
});
110+
return;
111+
}
112+
return this.onHeadlessUpdate(JSON.parse(message) as RedisHeadlessUpdate);
113+
94114
default:
95115
throw new Error(`Redis Pub/Sub received message on unknown channel: ${channel}`);
96116
}
@@ -163,6 +183,29 @@ export class RedisSubscriber implements LocalMessageBroker {
163183
}
164184
}
165185

186+
private async onHeadlessUpdate(update: RedisHeadlessUpdate): Promise<void> {
187+
log.debug("[redis] Received prebuild update", { update });
188+
189+
if (!update.type || !update.workspaceID) {
190+
return;
191+
}
192+
193+
const listeners =
194+
this.headlessWorkspaceEventListeners.get(LocalRabbitMQBackedMessageBroker.UNDEFINED_KEY) || [];
195+
if (listeners.length === 0) {
196+
return;
197+
}
198+
199+
const ctx = {};
200+
for (const l of listeners) {
201+
try {
202+
l(ctx, update);
203+
} catch (err) {
204+
log.error("Failed to broadcast headless update.", update, err);
205+
}
206+
}
207+
}
208+
166209
async stop() {
167210
this.disposables.dispose();
168211
}
@@ -172,8 +215,12 @@ export class RedisSubscriber implements LocalMessageBroker {
172215
}
173216

174217
listenForPrebuildUpdatableEvents(listener: HeadlessWorkspaceEventListener): Disposable {
175-
// TODO: not implemented
176-
return Disposable.create(() => {});
218+
// we're being cheap here in re-using a map where it just needs to be a plain array.
219+
return this.doRegister(
220+
LocalRabbitMQBackedMessageBroker.UNDEFINED_KEY,
221+
listener,
222+
this.headlessWorkspaceEventListeners,
223+
);
177224
}
178225

179226
listenForWorkspaceInstanceUpdates(userId: string, listener: WorkspaceInstanceUpdateListener): Disposable {

components/ws-manager-bridge/src/prebuild-updater.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import { inject, injectable } from "inversify";
88
import { TraceContext } from "@gitpod/gitpod-protocol/lib/util/tracing";
99
import { WorkspaceStatus, WorkspaceType } from "@gitpod/ws-manager/lib";
10-
import { WorkspaceInstance } from "@gitpod/gitpod-protocol";
10+
import { HeadlessWorkspaceEventType, WorkspaceInstance } from "@gitpod/gitpod-protocol";
1111
import { log, LogContext } from "@gitpod/gitpod-protocol/lib/util/logging";
1212
import { PrebuildStateMapper } from "./prebuild-state-mapper";
1313
import { DBWithTracing, TracedWorkspaceDB } from "@gitpod/gitpod-db/lib/traced-db";
@@ -89,7 +89,12 @@ export class PrebuildUpdater {
8989
type: update.type,
9090
workspaceID: workspaceId,
9191
});
92-
await this.publisher.publishHeadlessUpdate();
92+
if (!HeadlessWorkspaceEventType.isRunning(update.type)) {
93+
await this.publisher.publishHeadlessUpdate({
94+
type: update.type,
95+
workspaceID: workspaceId,
96+
});
97+
}
9398

9499
// prebuild info
95100
const info = (await this.workspaceDB.trace({ span }).findPrebuildInfos([updatedPrebuild.id]))[0];

components/ws-manager-bridge/src/redis/publisher.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ import { log } from "@gitpod/gitpod-protocol/lib/util/logging";
99
import { Metrics } from "../metrics";
1010
import { RedisClient } from "./client";
1111
import {
12+
HeadlessUpdatesChannel,
1213
PrebuildUpdatesChannel,
14+
RedisHeadlessUpdate,
1315
RedisPrebuildUpdate,
1416
RedisWorkspaceInstanceUpdate,
1517
WorkspaceInstanceUpdatesChannel,
@@ -52,8 +54,19 @@ export class RedisPublisher {
5254
}
5355
}
5456

55-
async publishHeadlessUpdate(): Promise<void> {
57+
async publishHeadlessUpdate(update: RedisHeadlessUpdate): Promise<void> {
5658
log.debug("[redis] Publish headless udpate invoked.");
57-
this.metrics.reportUpdatePublished("headless");
59+
60+
let err: Error | undefined;
61+
try {
62+
const serialized = JSON.stringify(update);
63+
await this.client.get().publish(HeadlessUpdatesChannel, serialized);
64+
log.debug("[redis] Succesfully published headless update.", update);
65+
} catch (e) {
66+
err = e;
67+
log.error("[redis] Failed to publish headless update.", e, update);
68+
} finally {
69+
this.metrics.reportUpdatePublished("headless", err);
70+
}
5871
}
5972
}

0 commit comments

Comments
 (0)