Skip to content

Commit

Permalink
initial
Browse files Browse the repository at this point in the history
  • Loading branch information
akosyakov committed Jan 17, 2023
1 parent aa0b0a9 commit c8142cc
Show file tree
Hide file tree
Showing 28 changed files with 480 additions and 97 deletions.
219 changes: 189 additions & 30 deletions components/gitpod-cli/cmd/rebuild.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,27 @@
package cmd

import (
"bufio"
"context"
"encoding/json"
"fmt"
"io"
"net/url"
"os"
"os/exec"
"path/filepath"
"strings"
"time"

"github.com/gitpod-io/gitpod/common-go/log"
"github.com/gitpod-io/gitpod/gitpod-cli/pkg/supervisor"
"github.com/gitpod-io/gitpod/gitpod-cli/pkg/utils"
"github.com/gitpod-io/gitpod/supervisor/api"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"

supervisorapi "github.com/gitpod-io/gitpod/supervisor/api"
prefixed "github.com/x-cray/logrus-prefixed-formatter"
)

func TerminateExistingContainer() error {
Expand Down Expand Up @@ -54,14 +63,19 @@ func runRebuild(ctx context.Context, supervisorClient *supervisor.SupervisorClie
return err
}

workspaceLocation := rebuildOpts.Workspace
if workspaceLocation == "" {
workspaceLocation = wsInfo.CheckoutLocation
}

tmpDir, err := os.MkdirTemp("", "gp-rebuild-*")
if err != nil {
event.Set("ErrorCode", utils.SystemErrorCode)
return err
}
defer os.RemoveAll(tmpDir)

gitpodConfig, err := utils.ParseGitpodConfig(wsInfo.CheckoutLocation)
gitpodConfig, err := utils.ParseGitpodConfig(workspaceLocation)
if err != nil {
fmt.Println("The .gitpod.yml file cannot be parsed: please check the file and try again")
fmt.Println("")
Expand Down Expand Up @@ -89,7 +103,7 @@ func runRebuild(ctx context.Context, supervisorClient *supervisor.SupervisorClie
case string:
baseimage = "FROM " + img
case map[interface{}]interface{}:
dockerfilePath := filepath.Join(wsInfo.CheckoutLocation, img["file"].(string))
dockerfilePath := filepath.Join(workspaceLocation, img["file"].(string))

if _, err := os.Stat(dockerfilePath); os.IsNotExist(err) {
fmt.Println("Your .gitpod.yml points to a Dockerfile that doesn't exist: " + dockerfilePath)
Expand Down Expand Up @@ -127,7 +141,8 @@ func runRebuild(ctx context.Context, supervisorClient *supervisor.SupervisorClie
return err
}

err = os.WriteFile(filepath.Join(tmpDir, "Dockerfile"), []byte(baseimage), 0644)
dockerFile := filepath.Join(tmpDir, "Dockerfile")
err = os.WriteFile(dockerFile, []byte(baseimage), 0644)
if err != nil {
fmt.Println("Could not write the temporary Dockerfile")
event.Set("ErrorCode", utils.RebuildErrorCode_DockerfileCannotWirte)
Expand All @@ -141,10 +156,9 @@ func runRebuild(ctx context.Context, supervisorClient *supervisor.SupervisorClie
return err
}

tag := "gp-rebuild-temp-build"
imageTag := "gp-rebuild-temp-build"

dockerCmd := exec.Command(dockerPath, "build", "-t", tag, "--progress=tty", ".")
dockerCmd.Dir = tmpDir
dockerCmd := exec.Command(dockerPath, "build", "-t", imageTag, "-f", dockerFile, workspaceLocation)
dockerCmd.Stdout = os.Stdout
dockerCmd.Stderr = os.Stderr

Expand All @@ -168,47 +182,191 @@ func runRebuild(ctx context.Context, supervisorClient *supervisor.SupervisorClie
return err
}

messages := []string{
"\n\nYou are now connected to the container",
"You can inspect the container and make sure the necessary tools & libraries are installed.",
"When you are done, just type exit to return to your Gitpod workspace\n",
workspaceUrl, err := url.Parse(wsInfo.WorkspaceUrl)
if err != nil {
return err
}
workspaceUrl.Host = "debug-" + workspaceUrl.Host

welcomeMessage := strings.Join(messages, "\n")
// TODO what about auto derived by server, i.e. JB for prebuilds? we should move them into the workspace then
tasks, err := json.Marshal(gitpodConfig.Tasks)
if err != nil {
return err
}

dockerRunCmd := exec.Command(
// TODO run under sudo - a hack to get tokens properly
supervisorEnvs, err := exec.CommandContext(ctx, "sudo", "/.supervisor/supervisor", "debug-env").CombinedOutput()
if err != nil {
return err
}

var debugEnvs []string
debugEnvs = append(debugEnvs, "SUPERVISOR_DEBUG_WORKSPACE=true")
debugEnvs = append(debugEnvs, fmt.Sprintf("GITPOD_TASKS=%s", string(tasks)))
// TODO pass prebuild option or gp rebuild prebuild?
debugEnvs = append(debugEnvs, fmt.Sprintf("GITPOD_HEADLESS=%s", "false"))
debugEnvs = append(debugEnvs, fmt.Sprintf("GITPOD_PREVENT_METADATA_ACCESS=%s", "false"))
debugEnvs = append(debugEnvs, fmt.Sprintf("GITPOD_WORKSPACE_URL=%s", workspaceUrl))

// TODO project? - should not it be covered by gp env
userEnvs, err := exec.CommandContext(ctx, "gp", "env").CombinedOutput()
if err != nil {
return err
}

envs := string(supervisorEnvs)
for _, env := range debugEnvs {
envs += env + "\n"
}
envs += string(userEnvs)

envFile := filepath.Join(tmpDir, ".env")
err = os.WriteFile(envFile, []byte(envs), 0644)
if err != nil {
return err
}

runCmd := exec.CommandContext(
ctx,
dockerPath,
"run",
"--rm",
"--user", "root",
"--privileged",
"--label", "gp-rebuild=true",
"-it",
tag,
"bash",
"-c",
fmt.Sprintf("echo '%s'; bash", welcomeMessage),
"--env-file", envFile,

// ports
"-p", "24999:22999", // supervisor
"-p", "25000:23000", // Web IDE
"-p", "25001:23001", // SSH
// 23002 dekstop IDE port, but it is covered by debug workspace proxy
"-p", "25003:23003", // debug workspace proxy

// volumes
"-v", "/workspace:/workspace",
"-v", "/.supervisor:/.supervisor",
"-v", "/var/run/docker.sock:/var/run/docker.sock",
"-v", "/ide:/ide",
"-v", "/ide-desktop:/ide-desktop",
"-v", "/ide-desktop-plugins:/ide-desktop-plugins", // TODO refactor to keep all IDE deps under ide or ide-desktop

imageTag,
"/.supervisor/supervisor", "init",
)

dockerRunCmd.Stdout = os.Stdout
dockerRunCmd.Stderr = os.Stderr
dockerRunCmd.Stdin = os.Stdin
debugSupervisor, err := supervisor.New(ctx, &supervisor.SupervisorClientOption{
Address: "localhost:24999",
})
if err != nil {
return err
}
go func() {
debugSupervisor.WaitForIDEReady(ctx)
if ctx.Err() != nil {
return
}
err := notify(supervisorClient, workspaceUrl.String())
if err != nil && ctx.Err() == nil {
log.WithError(err).Error("failed to notify")
}
}()

pipeLogs := func(input io.Reader, output io.Writer) {
pipeLog := log.New()
pipeLog.Logger.SetFormatter(&prefixed.TextFormatter{
TimestampFormat: "2006-01-02 15:04:05",
FullTimestamp: true,
ForceFormatting: true,
ForceColors: true,
})
pipeLog.Logger.SetOutput(output)

reader := bufio.NewReader(input)
for {
line, _, err := reader.ReadLine()
if err != nil {
return
}
msg := make(logrus.Fields)
err = json.Unmarshal(line, &msg)
if err != nil {
pipeLog.Info(string(line))
} else {
message := fmt.Sprintf("%v", msg["message"])
level, err := logrus.ParseLevel(fmt.Sprintf("%v", msg["level"]))
if err != nil {
level = logrus.DebugLevel
}
if level == logrus.FatalLevel {
level = logrus.ErrorLevel
}

delete(msg, "message")
delete(msg, "level")
delete(msg, "file")
delete(msg, "func")
delete(msg, "serviceContext")
delete(msg, "time")
delete(msg, "severity")
delete(msg, "@type")

pipeLog.WithFields(msg).Log(level, message)
}
}
}

stdout, err := runCmd.StdoutPipe()
if err != nil {
return err
}
go pipeLogs(stdout, os.Stdout)

err = dockerRunCmd.Run()
if _, ok := err.(*exec.ExitError); ok {
fmt.Println("Docker Run Command Failed")
event.Set("ErrorCode", utils.RebuildErrorCode_DockerRunFailed)
stderr, err := runCmd.StderrPipe()
if err != nil {
return err
} else if err != nil {
fmt.Println("Docker error")
event.Set("ErrorCode", utils.RebuildErrorCode_DockerErr)
}
go pipeLogs(stderr, os.Stdout)

err = runCmd.Start()
if err != nil {
fmt.Println("Docker Run Command Failed")
return err
}
_ = runCmd.Wait()

return nil
}

var buildCmd = &cobra.Command{
func notify(supervisorClient *supervisor.SupervisorClient, workspaceUrl string) error {
response, err := supervisorClient.Notification.Notify(context.Background(), &supervisorapi.NotifyRequest{
Level: supervisorapi.NotifyRequest_INFO,
Message: fmt.Sprintf("The debug workspace is available on: %s.", workspaceUrl),
Actions: []string{"Open Browser"},
})
if err != nil {
return err
}
if response.Action == "Open Browser" {
gpPath, err := exec.LookPath("gp")
if err != nil {
return err
}
gpCmd := exec.Command(gpPath, "preview", "--external", workspaceUrl)
gpCmd.Stdout = os.Stdout
gpCmd.Stderr = os.Stderr
return gpCmd.Run()
}
return nil
}

var rebuildOpts struct {
Workspace string
}

var rebuildCmd = &cobra.Command{
Use: "rebuild",
Short: "Re-builds the workspace image (useful to debug a workspace custom image)",
Short: "Re-builds the workspace (useful to debug a workspace configuration)",
Hidden: false,
Run: func(cmd *cobra.Command, args []string) {
ctx := context.Background()
Expand Down Expand Up @@ -237,5 +395,6 @@ var buildCmd = &cobra.Command{
}

func init() {
rootCmd.AddCommand(buildCmd)
rootCmd.AddCommand(rebuildCmd)
rebuildCmd.PersistentFlags().StringVarP(&rebuildOpts.Workspace, "workspace", "w", "", "Path to the workspace directory")
}
22 changes: 16 additions & 6 deletions components/gitpod-cli/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ require (
github.com/gitpod-io/gitpod/supervisor/api v0.0.0-00010101000000-000000000000
github.com/go-errors/errors v1.4.2
github.com/golang/mock v1.6.0
github.com/google/go-cmp v0.5.8
github.com/google/go-cmp v0.5.9
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf
github.com/google/tcpproxy v0.0.0-20180808230851-dfa16c61dad2
github.com/gorilla/handlers v1.5.1
Expand All @@ -20,13 +20,23 @@ require (
github.com/sirupsen/logrus v1.8.1
github.com/sourcegraph/jsonrpc2 v0.0.0-20200429184054-15c2290dcb37
github.com/spf13/cobra v1.1.3
golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d
golang.org/x/term v0.0.0-20220526004731-065cf7ba2467
golang.org/x/sys v0.3.0
golang.org/x/term v0.3.0
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f
google.golang.org/grpc v1.49.0
gopkg.in/yaml.v2 v2.4.0
)

require (
github.com/mattn/go-colorable v0.0.9 // indirect
github.com/mattn/go-isatty v0.0.3 // indirect
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
github.com/onsi/ginkgo v1.16.5 // indirect
github.com/onsi/gomega v1.24.2 // indirect
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 // indirect
google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc // indirect
)

require (
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect
Expand All @@ -37,9 +47,9 @@ require (
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/mattn/go-runewidth v0.0.9 // indirect
github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e // indirect
golang.org/x/text v0.3.7 // indirect
google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc // indirect
github.com/x-cray/logrus-prefixed-formatter v0.5.2
golang.org/x/net v0.4.0 // indirect
golang.org/x/text v0.5.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
)

Expand Down
Loading

0 comments on commit c8142cc

Please sign in to comment.