Skip to content

Commit c8142cc

Browse files
committed
initial
1 parent aa0b0a9 commit c8142cc

File tree

28 files changed

+480
-97
lines changed

28 files changed

+480
-97
lines changed

components/gitpod-cli/cmd/rebuild.go

Lines changed: 189 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,27 @@
55
package cmd
66

77
import (
8+
"bufio"
89
"context"
10+
"encoding/json"
911
"fmt"
12+
"io"
13+
"net/url"
1014
"os"
1115
"os/exec"
1216
"path/filepath"
1317
"strings"
1418
"time"
1519

20+
"github.com/gitpod-io/gitpod/common-go/log"
1621
"github.com/gitpod-io/gitpod/gitpod-cli/pkg/supervisor"
1722
"github.com/gitpod-io/gitpod/gitpod-cli/pkg/utils"
1823
"github.com/gitpod-io/gitpod/supervisor/api"
24+
"github.com/sirupsen/logrus"
1925
"github.com/spf13/cobra"
26+
27+
supervisorapi "github.com/gitpod-io/gitpod/supervisor/api"
28+
prefixed "github.com/x-cray/logrus-prefixed-formatter"
2029
)
2130

2231
func TerminateExistingContainer() error {
@@ -54,14 +63,19 @@ func runRebuild(ctx context.Context, supervisorClient *supervisor.SupervisorClie
5463
return err
5564
}
5665

66+
workspaceLocation := rebuildOpts.Workspace
67+
if workspaceLocation == "" {
68+
workspaceLocation = wsInfo.CheckoutLocation
69+
}
70+
5771
tmpDir, err := os.MkdirTemp("", "gp-rebuild-*")
5872
if err != nil {
5973
event.Set("ErrorCode", utils.SystemErrorCode)
6074
return err
6175
}
6276
defer os.RemoveAll(tmpDir)
6377

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

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

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

144-
tag := "gp-rebuild-temp-build"
159+
imageTag := "gp-rebuild-temp-build"
145160

146-
dockerCmd := exec.Command(dockerPath, "build", "-t", tag, "--progress=tty", ".")
147-
dockerCmd.Dir = tmpDir
161+
dockerCmd := exec.Command(dockerPath, "build", "-t", imageTag, "-f", dockerFile, workspaceLocation)
148162
dockerCmd.Stdout = os.Stdout
149163
dockerCmd.Stderr = os.Stderr
150164

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

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

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

179-
dockerRunCmd := exec.Command(
197+
// TODO run under sudo - a hack to get tokens properly
198+
supervisorEnvs, err := exec.CommandContext(ctx, "sudo", "/.supervisor/supervisor", "debug-env").CombinedOutput()
199+
if err != nil {
200+
return err
201+
}
202+
203+
var debugEnvs []string
204+
debugEnvs = append(debugEnvs, "SUPERVISOR_DEBUG_WORKSPACE=true")
205+
debugEnvs = append(debugEnvs, fmt.Sprintf("GITPOD_TASKS=%s", string(tasks)))
206+
// TODO pass prebuild option or gp rebuild prebuild?
207+
debugEnvs = append(debugEnvs, fmt.Sprintf("GITPOD_HEADLESS=%s", "false"))
208+
debugEnvs = append(debugEnvs, fmt.Sprintf("GITPOD_PREVENT_METADATA_ACCESS=%s", "false"))
209+
debugEnvs = append(debugEnvs, fmt.Sprintf("GITPOD_WORKSPACE_URL=%s", workspaceUrl))
210+
211+
// TODO project? - should not it be covered by gp env
212+
userEnvs, err := exec.CommandContext(ctx, "gp", "env").CombinedOutput()
213+
if err != nil {
214+
return err
215+
}
216+
217+
envs := string(supervisorEnvs)
218+
for _, env := range debugEnvs {
219+
envs += env + "\n"
220+
}
221+
envs += string(userEnvs)
222+
223+
envFile := filepath.Join(tmpDir, ".env")
224+
err = os.WriteFile(envFile, []byte(envs), 0644)
225+
if err != nil {
226+
return err
227+
}
228+
229+
runCmd := exec.CommandContext(
230+
ctx,
180231
dockerPath,
181232
"run",
182233
"--rm",
234+
"--user", "root",
235+
"--privileged",
183236
"--label", "gp-rebuild=true",
184-
"-it",
185-
tag,
186-
"bash",
187-
"-c",
188-
fmt.Sprintf("echo '%s'; bash", welcomeMessage),
237+
"--env-file", envFile,
238+
239+
// ports
240+
"-p", "24999:22999", // supervisor
241+
"-p", "25000:23000", // Web IDE
242+
"-p", "25001:23001", // SSH
243+
// 23002 dekstop IDE port, but it is covered by debug workspace proxy
244+
"-p", "25003:23003", // debug workspace proxy
245+
246+
// volumes
247+
"-v", "/workspace:/workspace",
248+
"-v", "/.supervisor:/.supervisor",
249+
"-v", "/var/run/docker.sock:/var/run/docker.sock",
250+
"-v", "/ide:/ide",
251+
"-v", "/ide-desktop:/ide-desktop",
252+
"-v", "/ide-desktop-plugins:/ide-desktop-plugins", // TODO refactor to keep all IDE deps under ide or ide-desktop
253+
254+
imageTag,
255+
"/.supervisor/supervisor", "init",
189256
)
190257

191-
dockerRunCmd.Stdout = os.Stdout
192-
dockerRunCmd.Stderr = os.Stderr
193-
dockerRunCmd.Stdin = os.Stdin
258+
debugSupervisor, err := supervisor.New(ctx, &supervisor.SupervisorClientOption{
259+
Address: "localhost:24999",
260+
})
261+
if err != nil {
262+
return err
263+
}
264+
go func() {
265+
debugSupervisor.WaitForIDEReady(ctx)
266+
if ctx.Err() != nil {
267+
return
268+
}
269+
err := notify(supervisorClient, workspaceUrl.String())
270+
if err != nil && ctx.Err() == nil {
271+
log.WithError(err).Error("failed to notify")
272+
}
273+
}()
274+
275+
pipeLogs := func(input io.Reader, output io.Writer) {
276+
pipeLog := log.New()
277+
pipeLog.Logger.SetFormatter(&prefixed.TextFormatter{
278+
TimestampFormat: "2006-01-02 15:04:05",
279+
FullTimestamp: true,
280+
ForceFormatting: true,
281+
ForceColors: true,
282+
})
283+
pipeLog.Logger.SetOutput(output)
284+
285+
reader := bufio.NewReader(input)
286+
for {
287+
line, _, err := reader.ReadLine()
288+
if err != nil {
289+
return
290+
}
291+
msg := make(logrus.Fields)
292+
err = json.Unmarshal(line, &msg)
293+
if err != nil {
294+
pipeLog.Info(string(line))
295+
} else {
296+
message := fmt.Sprintf("%v", msg["message"])
297+
level, err := logrus.ParseLevel(fmt.Sprintf("%v", msg["level"]))
298+
if err != nil {
299+
level = logrus.DebugLevel
300+
}
301+
if level == logrus.FatalLevel {
302+
level = logrus.ErrorLevel
303+
}
304+
305+
delete(msg, "message")
306+
delete(msg, "level")
307+
delete(msg, "file")
308+
delete(msg, "func")
309+
delete(msg, "serviceContext")
310+
delete(msg, "time")
311+
delete(msg, "severity")
312+
delete(msg, "@type")
313+
314+
pipeLog.WithFields(msg).Log(level, message)
315+
}
316+
}
317+
}
318+
319+
stdout, err := runCmd.StdoutPipe()
320+
if err != nil {
321+
return err
322+
}
323+
go pipeLogs(stdout, os.Stdout)
194324

195-
err = dockerRunCmd.Run()
196-
if _, ok := err.(*exec.ExitError); ok {
197-
fmt.Println("Docker Run Command Failed")
198-
event.Set("ErrorCode", utils.RebuildErrorCode_DockerRunFailed)
325+
stderr, err := runCmd.StderrPipe()
326+
if err != nil {
199327
return err
200-
} else if err != nil {
201-
fmt.Println("Docker error")
202-
event.Set("ErrorCode", utils.RebuildErrorCode_DockerErr)
328+
}
329+
go pipeLogs(stderr, os.Stdout)
330+
331+
err = runCmd.Start()
332+
if err != nil {
333+
fmt.Println("Docker Run Command Failed")
203334
return err
204335
}
336+
_ = runCmd.Wait()
205337

206338
return nil
207339
}
208340

209-
var buildCmd = &cobra.Command{
341+
func notify(supervisorClient *supervisor.SupervisorClient, workspaceUrl string) error {
342+
response, err := supervisorClient.Notification.Notify(context.Background(), &supervisorapi.NotifyRequest{
343+
Level: supervisorapi.NotifyRequest_INFO,
344+
Message: fmt.Sprintf("The debug workspace is available on: %s.", workspaceUrl),
345+
Actions: []string{"Open Browser"},
346+
})
347+
if err != nil {
348+
return err
349+
}
350+
if response.Action == "Open Browser" {
351+
gpPath, err := exec.LookPath("gp")
352+
if err != nil {
353+
return err
354+
}
355+
gpCmd := exec.Command(gpPath, "preview", "--external", workspaceUrl)
356+
gpCmd.Stdout = os.Stdout
357+
gpCmd.Stderr = os.Stderr
358+
return gpCmd.Run()
359+
}
360+
return nil
361+
}
362+
363+
var rebuildOpts struct {
364+
Workspace string
365+
}
366+
367+
var rebuildCmd = &cobra.Command{
210368
Use: "rebuild",
211-
Short: "Re-builds the workspace image (useful to debug a workspace custom image)",
369+
Short: "Re-builds the workspace (useful to debug a workspace configuration)",
212370
Hidden: false,
213371
Run: func(cmd *cobra.Command, args []string) {
214372
ctx := context.Background()
@@ -237,5 +395,6 @@ var buildCmd = &cobra.Command{
237395
}
238396

239397
func init() {
240-
rootCmd.AddCommand(buildCmd)
398+
rootCmd.AddCommand(rebuildCmd)
399+
rebuildCmd.PersistentFlags().StringVarP(&rebuildOpts.Workspace, "workspace", "w", "", "Path to the workspace directory")
241400
}

components/gitpod-cli/go.mod

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ require (
1010
github.com/gitpod-io/gitpod/supervisor/api v0.0.0-00010101000000-000000000000
1111
github.com/go-errors/errors v1.4.2
1212
github.com/golang/mock v1.6.0
13-
github.com/google/go-cmp v0.5.8
13+
github.com/google/go-cmp v0.5.9
1414
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf
1515
github.com/google/tcpproxy v0.0.0-20180808230851-dfa16c61dad2
1616
github.com/gorilla/handlers v1.5.1
@@ -20,13 +20,23 @@ require (
2020
github.com/sirupsen/logrus v1.8.1
2121
github.com/sourcegraph/jsonrpc2 v0.0.0-20200429184054-15c2290dcb37
2222
github.com/spf13/cobra v1.1.3
23-
golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d
24-
golang.org/x/term v0.0.0-20220526004731-065cf7ba2467
23+
golang.org/x/sys v0.3.0
24+
golang.org/x/term v0.3.0
2525
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f
2626
google.golang.org/grpc v1.49.0
2727
gopkg.in/yaml.v2 v2.4.0
2828
)
2929

30+
require (
31+
github.com/mattn/go-colorable v0.0.9 // indirect
32+
github.com/mattn/go-isatty v0.0.3 // indirect
33+
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
34+
github.com/onsi/ginkgo v1.16.5 // indirect
35+
github.com/onsi/gomega v1.24.2 // indirect
36+
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 // indirect
37+
google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc // indirect
38+
)
39+
3040
require (
3141
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
3242
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect
@@ -37,9 +47,9 @@ require (
3747
github.com/inconshreveable/mousetrap v1.0.0 // indirect
3848
github.com/mattn/go-runewidth v0.0.9 // indirect
3949
github.com/spf13/pflag v1.0.5 // indirect
40-
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e // indirect
41-
golang.org/x/text v0.3.7 // indirect
42-
google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc // indirect
50+
github.com/x-cray/logrus-prefixed-formatter v0.5.2
51+
golang.org/x/net v0.4.0 // indirect
52+
golang.org/x/text v0.5.0 // indirect
4353
google.golang.org/protobuf v1.28.1 // indirect
4454
)
4555

0 commit comments

Comments
 (0)