5
5
package cmd
6
6
7
7
import (
8
+ "bufio"
8
9
"context"
10
+ "encoding/json"
9
11
"fmt"
12
+ "io"
13
+ "net/url"
10
14
"os"
11
15
"os/exec"
12
16
"path/filepath"
13
17
"strings"
14
18
"time"
15
19
20
+ "github.com/gitpod-io/gitpod/common-go/log"
16
21
"github.com/gitpod-io/gitpod/gitpod-cli/pkg/supervisor"
17
22
"github.com/gitpod-io/gitpod/gitpod-cli/pkg/utils"
18
23
"github.com/gitpod-io/gitpod/supervisor/api"
24
+ "github.com/sirupsen/logrus"
19
25
"github.com/spf13/cobra"
26
+
27
+ supervisorapi "github.com/gitpod-io/gitpod/supervisor/api"
28
+ prefixed "github.com/x-cray/logrus-prefixed-formatter"
20
29
)
21
30
22
31
func TerminateExistingContainer () error {
@@ -54,14 +63,19 @@ func runRebuild(ctx context.Context, supervisorClient *supervisor.SupervisorClie
54
63
return err
55
64
}
56
65
66
+ workspaceLocation := rebuildOpts .Workspace
67
+ if workspaceLocation == "" {
68
+ workspaceLocation = wsInfo .CheckoutLocation
69
+ }
70
+
57
71
tmpDir , err := os .MkdirTemp ("" , "gp-rebuild-*" )
58
72
if err != nil {
59
73
event .Set ("ErrorCode" , utils .SystemErrorCode )
60
74
return err
61
75
}
62
76
defer os .RemoveAll (tmpDir )
63
77
64
- gitpodConfig , err := utils .ParseGitpodConfig (wsInfo . CheckoutLocation )
78
+ gitpodConfig , err := utils .ParseGitpodConfig (workspaceLocation )
65
79
if err != nil {
66
80
fmt .Println ("The .gitpod.yml file cannot be parsed: please check the file and try again" )
67
81
fmt .Println ("" )
@@ -89,7 +103,7 @@ func runRebuild(ctx context.Context, supervisorClient *supervisor.SupervisorClie
89
103
case string :
90
104
baseimage = "FROM " + img
91
105
case map [interface {}]interface {}:
92
- dockerfilePath := filepath .Join (wsInfo . CheckoutLocation , img ["file" ].(string ))
106
+ dockerfilePath := filepath .Join (workspaceLocation , img ["file" ].(string ))
93
107
94
108
if _ , err := os .Stat (dockerfilePath ); os .IsNotExist (err ) {
95
109
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
127
141
return err
128
142
}
129
143
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 )
131
146
if err != nil {
132
147
fmt .Println ("Could not write the temporary Dockerfile" )
133
148
event .Set ("ErrorCode" , utils .RebuildErrorCode_DockerfileCannotWirte )
@@ -141,10 +156,9 @@ func runRebuild(ctx context.Context, supervisorClient *supervisor.SupervisorClie
141
156
return err
142
157
}
143
158
144
- tag := "gp-rebuild-temp-build"
159
+ imageTag := "gp-rebuild-temp-build"
145
160
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 )
148
162
dockerCmd .Stdout = os .Stdout
149
163
dockerCmd .Stderr = os .Stderr
150
164
@@ -168,47 +182,191 @@ func runRebuild(ctx context.Context, supervisorClient *supervisor.SupervisorClie
168
182
return err
169
183
}
170
184
171
- messages := []string {
172
- "\n \n You 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
175
188
}
189
+ workspaceUrl .Host = "debug-" + workspaceUrl .Host
176
190
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
+ }
178
196
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 ,
180
231
dockerPath ,
181
232
"run" ,
182
233
"--rm" ,
234
+ "--user" , "root" ,
235
+ "--privileged" ,
183
236
"--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" ,
189
256
)
190
257
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 )
194
324
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 {
199
327
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" )
203
334
return err
204
335
}
336
+ _ = runCmd .Wait ()
205
337
206
338
return nil
207
339
}
208
340
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 {
210
368
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 )" ,
212
370
Hidden : false ,
213
371
Run : func (cmd * cobra.Command , args []string ) {
214
372
ctx := context .Background ()
@@ -237,5 +395,6 @@ var buildCmd = &cobra.Command{
237
395
}
238
396
239
397
func init () {
240
- rootCmd .AddCommand (buildCmd )
398
+ rootCmd .AddCommand (rebuildCmd )
399
+ rebuildCmd .PersistentFlags ().StringVarP (& rebuildOpts .Workspace , "workspace" , "w" , "" , "Path to the workspace directory" )
241
400
}
0 commit comments