Skip to content

[supervisor] support shutdown task #11287

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions components/gitpod-protocol/data/gitpod-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@
"type": "string",
"description": "The main shell command to run after `before` and `init`. This command is executed last on every start and doesn't have to terminate."
},
"shutdown": {
"type": "string",
"description": "A shell command to run before a workspace stops. This command needs to finish with `terminationGracePeriodSeconds` and will be force killed if it runs longer."
},
"env": {
"type": "object",
"description": "Environment variables to set."
Expand Down Expand Up @@ -114,6 +118,10 @@
"additionalProperties": false
}
},
"terminationGracePeriodSeconds": {
"type": "number",
"description": "The grace period for all processes to stop before beeing force killed. Default is 15 seconds."
},
"image": {
"type": [
"object",
Expand Down
2 changes: 2 additions & 0 deletions components/gitpod-protocol/src/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -827,6 +827,7 @@ export interface WorkspaceConfig {
image?: ImageConfig;
ports?: PortConfig[];
tasks?: TaskConfig[];
terminationGracePeriodSeconds?: number;
checkoutLocation?: string;
workspaceLocation?: string;
gitConfig?: { [config: string]: string };
Expand Down Expand Up @@ -993,6 +994,7 @@ export interface TaskConfig {
init?: string;
prebuild?: string;
command?: string;
shutdown?: string;
env?: { [env: string]: any };
openIn?: "bottom" | "main" | "left" | "right";
openMode?: "split-top" | "split-left" | "split-right" | "split-bottom" | "tab-before" | "tab-after";
Expand Down
8 changes: 0 additions & 8 deletions components/ide/code/startup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,6 @@
# Licensed under the GNU Affero General Public License (AGPL).
# See License-AGPL.txt in the project root for license information.


# DO NOT REMOVE THE SPACES AT THE BEGINNING OF THESE LINES
# The spaces at the beginning of the line prevent those lines from being added to
# the bash history.
set +o history
history -c
truncate -s 0 "$HISTFILE"

# This is the main entrypoint to workspace container in Gitpod. It is called (and controlled) by the supervisor
# container root process.
# To mimic a regular login shell on a local computer we execute this with "bash -li" (interactive login shell):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export class BitbucketFileProvider implements FileProvider {
).data;
return contents as string;
} catch (err) {
log.debug({ userId: user.id }, err);
log.error({ userId: user.id }, err);
}
}
}
1 change: 1 addition & 0 deletions components/supervisor/pkg/supervisor/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@ type TaskConfig struct {
Init *string `json:"init,omitempty"`
Prebuild *string `json:"prebuild,omitempty"`
Command *string `json:"command,omitempty"`
Shutdown *string `json:"shutdown,omitempty"`
Env *map[string]interface{} `json:"env,omitempty"`
OpenIn *string `json:"openIn,omitempty"`
OpenMode *string `json:"openMode,omitempty"`
Expand Down
46 changes: 45 additions & 1 deletion components/supervisor/pkg/supervisor/supervisor.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package supervisor

import (
"bufio"
"context"
"crypto/rand"
"crypto/rsa"
Expand Down Expand Up @@ -50,6 +51,7 @@ import (
"github.com/gitpod-io/gitpod/content-service/pkg/executor"
"github.com/gitpod-io/gitpod/content-service/pkg/git"
"github.com/gitpod-io/gitpod/content-service/pkg/initializer"
"github.com/gitpod-io/gitpod/content-service/pkg/logs"
gitpod "github.com/gitpod-io/gitpod/gitpod-protocol"
"github.com/gitpod-io/gitpod/supervisor/api"
"github.com/gitpod-io/gitpod/supervisor/pkg/activation"
Expand Down Expand Up @@ -388,19 +390,61 @@ func Run(options ...RunOption) {
}

log.Info("received SIGTERM (or shutdown) - tearing down")
cancel()

err = termMux.Close()
if err != nil {
log.WithError(err).Error("terminal closure failed")
}

cancel()
// terminate all child processes once the IDE is gone
ideWG.Wait()
terminateChildProcesses()

wg.Wait()
}

func runTask(ctx context.Context, termMuxSrv *terminal.MuxTerminalService, task string, taskName string) {
if task == "" {
return
}
openTerminalResponse, err := termMuxSrv.Open(ctx, &api.OpenTerminalRequest{})
if err != nil {
log.WithError(err).Errorf("error running '%q' task", taskName)
}
term, ok := termMuxSrv.Mux.Get(openTerminalResponse.Terminal.Alias)
if !ok {
log.Errorf("cannot obtain terminal for '%q'.", taskName)
} else {
term.PTY.Write([]byte(task + "; exit\n"))
}
stdout := term.Stdout.ListenWithOptions(terminal.TermListenOptions{
// ensure logging of entire task output
ReadTimeout: terminal.NoTimeout,
})

fileName := logs.TerminalStoreLocation + "/" + taskName + ".log"
file, err := os.Create(fileName)
var fileWriter *bufio.Writer
if err != nil {
log.WithError(err).Errorf("cannot create an '%q' log file", taskName)
fileWriter = bufio.NewWriter(io.Discard)
} else {
defer file.Close()
log.Info("Writing build output to " + fileName)
fileWriter = bufio.NewWriter(file)
defer fileWriter.Flush()
}
_, err = io.Copy(fileWriter, stdout)
if err != nil {
log.WithError(err).Errorf("cannot copy from '%q' task", taskName)
}
_, err = term.Wait()
if err != nil {
log.WithError(err).Errorf("terminal exited errorneous")
}
}

func isShallowRepository(rootDir string, env []string) bool {
cmd := runAsGitpodUser(exec.Command("git", "rev-parse", "--is-shallow-repository"))
cmd.Env = env
Expand Down
3 changes: 3 additions & 0 deletions components/supervisor/pkg/supervisor/tasks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ func TestTaskManager(t *testing.T) {
ExpectedReporter: testHeadlessTaskProgressReporter{
Done: true,
Success: true,
Data: "foo",
},
},
{
Expand Down Expand Up @@ -243,9 +244,11 @@ func TestTaskManager(t *testing.T) {
type testHeadlessTaskProgressReporter struct {
Done bool
Success bool
Data string
}

func (r *testHeadlessTaskProgressReporter) write(data string, task *task, terminal *terminal.Term) {
r.Data = r.Data + data
}

func (r *testHeadlessTaskProgressReporter) done(success taskSuccess) {
Expand Down
2 changes: 1 addition & 1 deletion components/supervisor/pkg/terminal/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ func (srv *MuxTerminalService) OpenWithOptions(ctx context.Context, req *api.Ope
if cmd.Dir == "" {
cmd.Dir = srv.DefaultWorkdir
}
cmd.Env = append(srv.Env, "TERM=xterm-256color")
cmd.Env = append(srv.Env, "TERM=xterm-256color", "PROMPT_COMMAND='history -a'")
for key, value := range req.Env {
cmd.Env = append(cmd.Env, fmt.Sprintf("%v=%v", key, value))
}
Expand Down