Skip to content

Commit ad26ca1

Browse files
authored
[rebuild] fix 16535: auto manage windows (#16561)
1 parent f0d1d1f commit ad26ca1

File tree

9 files changed

+447
-246
lines changed

9 files changed

+447
-246
lines changed

components/gitpod-cli/cmd/rebuild.go

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -306,9 +306,9 @@ Connect using SSH keys (https://gitpod.io/keys):
306306
%s
307307
308308
%s`, sep, workspaceUrl, ssh, sep)
309-
err := notify(ctx, supervisorClient, workspaceUrl.String(), "The workspace is UP.")
309+
err := openWindow(ctx, workspaceUrl.String())
310310
if err != nil && ctx.Err() == nil {
311-
log.WithError(err).Error("failed to notify")
311+
log.WithError(err).Error("failed to open window")
312312
}
313313
}()
314314

@@ -408,26 +408,15 @@ func setLoggerFormatter(logger *logrus.Logger) {
408408
})
409409
}
410410

411-
func notify(ctx context.Context, supervisorClient *supervisor.SupervisorClient, workspaceUrl, message string) error {
412-
response, err := supervisorClient.Notification.Notify(ctx, &api.NotifyRequest{
413-
Level: api.NotifyRequest_INFO,
414-
Message: message,
415-
Actions: []string{"Open"},
416-
})
411+
func openWindow(ctx context.Context, workspaceUrl string) error {
412+
gpPath, err := exec.LookPath("gp")
417413
if err != nil {
418414
return err
419415
}
420-
if response.Action == "Open" {
421-
gpPath, err := exec.LookPath("gp")
422-
if err != nil {
423-
return err
424-
}
425-
gpCmd := exec.CommandContext(ctx, gpPath, "preview", "--external", workspaceUrl)
426-
gpCmd.Stdout = os.Stdout
427-
gpCmd.Stderr = os.Stderr
428-
return gpCmd.Run()
429-
}
430-
return nil
416+
gpCmd := exec.CommandContext(ctx, gpPath, "preview", "--external", workspaceUrl)
417+
gpCmd.Stdout = os.Stdout
418+
gpCmd.Stderr = os.Stderr
419+
return gpCmd.Run()
431420
}
432421

433422
var rebuildOpts struct {

components/supervisor-api/go/status.pb.go

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

components/supervisor-api/go/status.pb.gw.go

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

components/supervisor-api/status.proto

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ service StatusService {
1919
rpc SupervisorStatus(SupervisorStatusRequest) returns (SupervisorStatusResponse) {
2020
option (google.api.http) = {
2121
get: "/v1/status/supervisor"
22+
additional_bindings {
23+
get: "/v1/status/supervisor/willShutdown/{willShutdown=true}",
24+
}
2225
};
2326
}
2427

@@ -81,7 +84,10 @@ service StatusService {
8184

8285
}
8386

84-
message SupervisorStatusRequest {}
87+
message SupervisorStatusRequest {
88+
// if true this request will return either when it times out or when the supervisor is about to shutdown.
89+
bool willShutdown = 1;
90+
}
8591
message SupervisorStatusResponse {
8692
bool ok = 1;
8793
}

components/supervisor/frontend/src/ide/supervisor-service-client.ts

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import {
1010
ContentStatusResponse,
1111
} from "@gitpod/supervisor-api-grpc/lib/status_pb";
1212
import { WorkspaceInfoResponse } from "@gitpod/supervisor-api-grpc/lib/info_pb";
13-
1413
import { GitpodHostUrl } from "@gitpod/gitpod-protocol/lib/util/gitpod-host-url";
1514

1615
export class SupervisorServiceClient {
@@ -26,10 +25,46 @@ export class SupervisorServiceClient {
2625
readonly ideReady = this.supervisorReady.then(() => this.checkReady("ide"));
2726
readonly contentReady = Promise.all([this.supervisorReady]).then(() => this.checkReady("content"));
2827
readonly getWorkspaceInfoPromise = this.supervisorReady.then(() => this.getWorkspaceInfo());
28+
readonly supervisorWillShutdown = this.supervisorReady.then(() => this.checkWillShutdown());
2929

3030
private constructor() {}
3131

32-
private async checkReady(kind: "content" | "ide" | "supervisor", delay?: boolean): Promise<any> {
32+
private async checkWillShutdown(delay = false): Promise<void> {
33+
if (delay) {
34+
await new Promise(resolve => setTimeout(resolve, 1000));
35+
}
36+
try {
37+
const wsSupervisorStatusUrl = GitpodHostUrl.fromWorkspaceUrl(window.location.href).with((url) => {
38+
return {
39+
pathname: "/_supervisor/v1/status/supervisor/willShutdown/true",
40+
};
41+
});
42+
const response = await fetch(wsSupervisorStatusUrl.toString(), { credentials: "include" });
43+
let result;
44+
if (response.ok) {
45+
result = await response.json();
46+
if ((result as SupervisorStatusResponse.AsObject).ok) {
47+
return;
48+
}
49+
}
50+
if (response.status === 502) {
51+
// bad gateway, supervisor is gone
52+
return
53+
}
54+
console.debug(
55+
`failed to check whether is about to shutdown, trying again...`,
56+
response.status,
57+
response.statusText,
58+
JSON.stringify(result, undefined, 2),
59+
);
60+
} catch (e) {
61+
// network errors
62+
console.debug(`failed to check whether is about to shutdown, trying again...`, e);
63+
}
64+
await this.checkWillShutdown(true);
65+
}
66+
67+
private async checkReady(kind: "content" | "ide" | "supervisor" , delay?: boolean): Promise<any> {
3368
if (delay) {
3469
await new Promise((resolve) => setTimeout(resolve, 1000));
3570
}

components/supervisor/frontend/src/index.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ import * as IDEWorker from "./ide/ide-worker";
5555
import * as IDEWebSocket from "./ide/ide-web-socket";
5656
import { SupervisorServiceClient } from "./ide/supervisor-service-client";
5757
import * as LoadingFrame from "./shared/loading-frame";
58+
import { workspaceUrl } from "./shared/urls";
5859

5960
window.gitpod = {} as any;
6061
IDEWorker.install();
@@ -248,11 +249,15 @@ LoadingFrame.load().then(async (loading) => {
248249
})();
249250

250251
(async () => {
252+
const debugWorkspace = workspaceUrl.debugWorkspace;
251253
//#region ide lifecycle
252254
function isWorkspaceInstancePhase(phase: WorkspaceInstancePhase): boolean {
253255
return frontendDashboardServiceClient.latestInfo?.statusPhase === phase;
254256
}
255257
if (!isWorkspaceInstancePhase("running")) {
258+
if (debugWorkspace && frontendDashboardServiceClient.latestInfo) {
259+
window.open('', '_self')?.close()
260+
}
256261
await new Promise<void>((resolve) => {
257262
frontendDashboardServiceClient.onInfoUpdate((status) => {
258263
if (status.statusPhase === "running") {
@@ -262,6 +267,11 @@ LoadingFrame.load().then(async (loading) => {
262267
});
263268
}
264269
const supervisorServiceClient = SupervisorServiceClient.get();
270+
if (debugWorkspace) {
271+
supervisorServiceClient.supervisorWillShutdown.then(() => {
272+
window.open('', '_self')?.close()
273+
})
274+
}
265275
const [ideStatus] = await Promise.all([
266276
supervisorServiceClient.ideReady,
267277
supervisorServiceClient.contentReady,

components/supervisor/pkg/supervisor/services.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ func (service *ideReadyState) Set(ready bool, info *DesktopIDEStatus) {
9393
}
9494

9595
type statusService struct {
96+
willShutdownCtx context.Context
9697
ContentState ContentState
9798
Ports *ports.Manager
9899
Tasks *tasksManager
@@ -111,7 +112,21 @@ func (s *statusService) RegisterREST(mux *runtime.ServeMux, grpcEndpoint string)
111112
return api.RegisterStatusServiceHandlerFromEndpoint(context.Background(), mux, grpcEndpoint, []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())})
112113
}
113114

114-
func (s *statusService) SupervisorStatus(context.Context, *api.SupervisorStatusRequest) (*api.SupervisorStatusResponse, error) {
115+
func (s *statusService) SupervisorStatus(ctx context.Context, req *api.SupervisorStatusRequest) (*api.SupervisorStatusResponse, error) {
116+
if req.WillShutdown {
117+
if s.willShutdownCtx.Err() != nil {
118+
return &api.SupervisorStatusResponse{Ok: true}, nil
119+
}
120+
select {
121+
case <-ctx.Done():
122+
if errors.Is(ctx.Err(), context.Canceled) {
123+
return nil, status.Error(codes.Canceled, "execution canceled")
124+
}
125+
return nil, status.Error(codes.DeadlineExceeded, ctx.Err().Error())
126+
case <-s.willShutdownCtx.Done():
127+
return &api.SupervisorStatusResponse{Ok: true}, nil
128+
}
129+
}
115130
return &api.SupervisorStatusResponse{Ok: true}, nil
116131
}
117132

components/supervisor/pkg/supervisor/supervisor.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,8 +342,10 @@ func Run(options ...RunOption) {
342342

343343
taskManager := newTasksManager(cfg, termMuxSrv, cstate, nil, ideReady, desktopIdeReady)
344344

345+
willShutdownCtx, fireWillShutdown := context.WithCancel(ctx)
345346
apiServices := []RegisterableService{
346347
&statusService{
348+
willShutdownCtx: willShutdownCtx,
347349
ContentState: cstate,
348350
Ports: portMgmt,
349351
Tasks: taskManager,
@@ -459,6 +461,7 @@ func Run(options ...RunOption) {
459461
}
460462

461463
log.Info("received SIGTERM (or shutdown) - tearing down")
464+
fireWillShutdown()
462465
terminalShutdownCtx, cancelTermination := context.WithTimeout(context.Background(), cfg.GetTerminationGracePeriod())
463466
defer cancelTermination()
464467
cancel()

components/supervisor/validate.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,10 @@ sudo rm -rf "/.supervisor/$COMPONENT" && true
2020
sudo mv ./"$COMPONENT" /.supervisor
2121
echo "$COMPONENT in /.supervisor replaced"
2222

23+
yarn --cwd "$DIR/frontend" run build
24+
25+
sudo rm -rf /.supervisor/frontend && true
26+
sudo ln -s "$DIR/frontend/dist" /.supervisor/frontend
27+
echo "$DIR/frontend/dist linked in /.supervisor/frontend"
28+
2329
gp rebuild --workspace-folder="$ROOT_DIR/dev/ide/example/workspace" --gitpod-env "GITPOD_ANALYTICS_SEGMENT_KEY=YErmvd89wPsrCuGcVnF2XAl846W9WIGl" "$@"

0 commit comments

Comments
 (0)