Skip to content

Commit c6bea97

Browse files
committed
initial
1 parent aa0b0a9 commit c6bea97

File tree

23 files changed

+243
-60
lines changed

23 files changed

+243
-60
lines changed

components/gitpod-cli/cmd/rebuild.go

Lines changed: 97 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ package cmd
66

77
import (
88
"context"
9+
"encoding/json"
910
"fmt"
11+
"io/ioutil"
12+
"net/url"
1013
"os"
1114
"os/exec"
1215
"path/filepath"
@@ -141,9 +144,9 @@ func runRebuild(ctx context.Context, supervisorClient *supervisor.SupervisorClie
141144
return err
142145
}
143146

144-
tag := "gp-rebuild-temp-build"
147+
imageTag := "gp-rebuild-temp-build"
145148

146-
dockerCmd := exec.Command(dockerPath, "build", "-t", tag, "--progress=tty", ".")
149+
dockerCmd := exec.Command(dockerPath, "build", "-t", imageTag, "--progress=tty", ".")
147150
dockerCmd.Dir = tmpDir
148151
dockerCmd.Stdout = os.Stdout
149152
dockerCmd.Stderr = os.Stderr
@@ -168,31 +171,108 @@ func runRebuild(ctx context.Context, supervisorClient *supervisor.SupervisorClie
168171
return err
169172
}
170173

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",
174+
workspaceUrl, err := url.Parse(wsInfo.WorkspaceUrl)
175+
if err != nil {
176+
return err
175177
}
178+
workspaceUrl.Host = "debug-" + workspaceUrl.Host
179+
180+
// TODO what about auto derived by server, i.e. JB for prebuilds? we should move them into the workspace then
181+
tasks, err := json.Marshal(gitpodConfig.Tasks)
182+
if err != nil {
183+
return err
184+
}
185+
186+
var envVars []string
176187

177-
welcomeMessage := strings.Join(messages, "\n")
188+
// TODO run under sudo - a hack to get tokens properly
189+
// supervisor - hardcode for now, but how to separate from workspace envs?
190+
// are there any other env vars which are not picked up by supervisor, but has to propagated from ws-manager to support docker or something?
191+
initEnvs, err := ioutil.ReadFile("/proc/1/environ")
192+
if err != nil {
193+
return err
194+
}
195+
initEnviron := strings.Split(string(initEnvs), "\x00")
196+
for _, env := range initEnviron {
197+
if env == "" {
198+
continue
199+
}
200+
parts := strings.SplitN(env, "=", 2)
201+
key := parts[0]
202+
if key == "THEIA_SUPERVISOR_TOKENS" || strings.HasPrefix(key, "GITPOD_") {
203+
envVars = append(envVars, env)
204+
}
205+
}
206+
207+
envVars = append(envVars, "SUPERVISOR_DEBUG_WORKSPACE=true")
208+
envVars = append(envVars, fmt.Sprintf("GITPOD_TASKS=%s", string(tasks)))
209+
// TODO pass prebuild option or gp rebuild prebuild?
210+
envVars = append(envVars, fmt.Sprintf("GITPOD_HEADLESS=%s", "false"))
211+
envVars = append(envVars, fmt.Sprintf("GITPOD_PREVENT_METADATA_ACCESS=%s", "false"))
212+
envVars = append(envVars, fmt.Sprintf("GITPOD_WORKSPACE_URL=%s", workspaceUrl))
213+
214+
// TODO IDE - get rid of env vars in images, use supervisor api as a mediator to support many IDEs running in the same worksapce
215+
// TODO PATH - use well defined locations to pick up binaries, i.e. /ide/bin or /ide-desktop/bin in supervisor
216+
envVars = append(envVars, fmt.Sprintf("VSX_REGISTRY_URL=%s", os.Getenv("VSX_REGISTRY_URL")))
217+
envVars = append(envVars, fmt.Sprintf("EDITOR=%s", os.Getenv("EDITOR")))
218+
envVars = append(envVars, fmt.Sprintf("VISUAL=%s", os.Getenv("VISUAL")))
219+
envVars = append(envVars, fmt.Sprintf("GP_OPEN_EDITOR=%s", os.Getenv("GP_OPEN_EDITOR")))
220+
envVars = append(envVars, fmt.Sprintf("GIT_EDITOR=%s", os.Getenv("GIT_EDITOR")))
221+
envVars = append(envVars, fmt.Sprintf("GP_PREVIEW_BROWSER=%s", os.Getenv("GP_PREVIEW_BROWSER")))
222+
envVars = append(envVars, fmt.Sprintf("GP_EXTERNAL_BROWSER=%s", os.Getenv("GP_EXTERNAL_BROWSER")))
223+
224+
userEnvs, err := exec.CommandContext(ctx, "gp", "env").CombinedOutput()
225+
if err != nil {
226+
return err
227+
}
228+
229+
// TODO project? - should not it be covered by gp env
230+
231+
var envs string
232+
for _, env := range envVars {
233+
envs += env + "\n"
234+
}
235+
envs += string(userEnvs)
236+
237+
envFile := filepath.Join(tmpDir, ".env")
238+
err = os.WriteFile(envFile, []byte(envs), 0644)
239+
if err != nil {
240+
return err
241+
}
178242

179-
dockerRunCmd := exec.Command(
243+
runCmd := exec.Command(
180244
dockerPath,
181245
"run",
182246
"--rm",
247+
"--user", "root",
248+
"--privileged",
183249
"--label", "gp-rebuild=true",
184-
"-it",
185-
tag,
186-
"bash",
187-
"-c",
188-
fmt.Sprintf("echo '%s'; bash", welcomeMessage),
250+
"--env-file", envFile,
251+
252+
// ports
253+
"-p", "24999:22999", // supervisor
254+
"-p", "25000:23000", // Web IDE
255+
"-p", "25001:23001", // SSH
256+
// 23002 dekstop IDE port, but it is covered by debug workspace proxy
257+
"-p", "25003:23003", // debug workspace proxy
258+
259+
// volumes
260+
"-v", "/workspace:/workspace",
261+
"-v", "/.supervisor:/.supervisor",
262+
"-v", "/var/run/docker.sock:/var/run/docker.sock",
263+
"-v", "/ide:/ide",
264+
"-v", "/ide-desktop:/ide-desktop",
265+
"-v", "/ide-desktop-plugins:/ide-desktop-plugins", // TODO refactor to keep all IDE deps under ide or ide-desktop
266+
267+
imageTag,
268+
"/.supervisor/supervisor", "init",
189269
)
190270

191-
dockerRunCmd.Stdout = os.Stdout
192-
dockerRunCmd.Stderr = os.Stderr
193-
dockerRunCmd.Stdin = os.Stdin
271+
runCmd.Stdout = os.Stdout
272+
runCmd.Stderr = os.Stderr
273+
runCmd.Stdin = os.Stdin
194274

195-
err = dockerRunCmd.Run()
275+
err = runCmd.Run()
196276
if _, ok := err.(*exec.ExitError); ok {
197277
fmt.Println("Docker Run Command Failed")
198278
event.Set("ErrorCode", utils.RebuildErrorCode_DockerRunFailed)

components/supervisor/cmd/init.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,18 @@ var initCmd = &cobra.Command{
6363
return
6464
}
6565

66+
debugProxyCtx, stopDebugProxy := context.WithCancel(context.Background())
67+
if os.Getenv("SUPERVISOR_DEBUG_WORKSPACE") == "true" {
68+
err = exec.CommandContext(debugProxyCtx, supervisorPath, "proxy").Start()
69+
if err != nil {
70+
log.WithError(err).Error("cannot run debug workspace proxy")
71+
}
72+
}
73+
6674
supervisorDone := make(chan struct{})
6775
go func() {
6876
defer close(supervisorDone)
77+
defer stopDebugProxy()
6978

7079
err := runCommand.Wait()
7180
if err != nil && !(strings.Contains(err.Error(), "signal: ") || strings.Contains(err.Error(), "no child processes")) {

components/supervisor/cmd/proxy.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Copyright (c) 2023 Gitpod GmbH. All rights reserved.
2+
// Licensed under the GNU Affero General Public License (AGPL).
3+
// See License.AGPL.txt in the project root for license information.
4+
5+
package cmd
6+
7+
import (
8+
"fmt"
9+
"net/http"
10+
"net/http/httputil"
11+
"net/url"
12+
"strconv"
13+
14+
"github.com/gitpod-io/gitpod/common-go/log"
15+
"github.com/spf13/cobra"
16+
)
17+
18+
func NewSingleHostReverseProxy(target *url.URL) *httputil.ReverseProxy {
19+
director := func(req *http.Request) {
20+
req.URL.Scheme = target.Scheme
21+
req.URL.Host = target.Host
22+
if _, ok := req.Header["User-Agent"]; !ok {
23+
// explicitly disable User-Agent so it's not set to default value
24+
req.Header.Set("User-Agent", "")
25+
}
26+
req.Header.Del("X-WS-Proxy-Debug-Port")
27+
}
28+
return &httputil.ReverseProxy{Director: director}
29+
}
30+
31+
var proxyCmd = &cobra.Command{
32+
Use: "proxy",
33+
Short: "forward request to debug workspace",
34+
Run: func(cmd *cobra.Command, args []string) {
35+
log.Fatal(http.ListenAndServe(":23003", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
36+
portStr := r.Header.Get("X-WS-Proxy-Debug-Port")
37+
port, err := strconv.Atoi(portStr)
38+
if err != nil || port < 1 || port > 65535 {
39+
w.WriteHeader(502)
40+
return
41+
}
42+
dst, err := url.Parse("http://10.0.6.2:" + portStr)
43+
if err != nil {
44+
w.WriteHeader(502)
45+
return
46+
}
47+
fmt.Printf("%+v\n", dst)
48+
proxy := NewSingleHostReverseProxy(dst)
49+
proxy.ServeHTTP(w, r)
50+
})))
51+
},
52+
}
53+
54+
func init() {
55+
rootCmd.AddCommand(proxyCmd)
56+
}

components/ws-proxy/pkg/proxy/config.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,11 @@ func (c *HostBasedIngressConfig) Validate() error {
7171

7272
// WorkspacePodConfig contains config around the workspace pod.
7373
type WorkspacePodConfig struct {
74-
TheiaPort uint16 `json:"theiaPort"`
75-
IDEDebugPort uint16 `json:"ideDebugPort"`
76-
SupervisorPort uint16 `json:"supervisorPort"`
77-
SupervisorDebugPort uint16 `json:"supervisorDebugPort"`
74+
TheiaPort uint16 `json:"theiaPort"`
75+
IDEDebugPort uint16 `json:"ideDebugPort"`
76+
SupervisorPort uint16 `json:"supervisorPort"`
77+
SupervisorDebugPort uint16 `json:"supervisorDebugPort"`
78+
DebugWorkspaceProxyPort uint16 `json:"debugWorkspaceProxyPort"`
7879
// SupervisorImage is deprecated
7980
SupervisorImage string `json:"supervisorImage"`
8081
}
@@ -90,6 +91,7 @@ func (c *WorkspacePodConfig) Validate() error {
9091
validation.Field(&c.IDEDebugPort, validation.Required),
9192
validation.Field(&c.SupervisorPort, validation.Required),
9293
validation.Field(&c.SupervisorDebugPort, validation.Required),
94+
validation.Field(&c.DebugWorkspaceProxyPort, validation.Required),
9395
)
9496
if len(c.SupervisorImage) > 0 {
9597
log.Warn("config value 'workspacePodConfig.supervisorImage' is deprected, use it only to be backwards compatible")

components/ws-proxy/pkg/proxy/routes.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,12 @@ func installWorkspacePortRoutes(r *mux.Router, config *RouteHandlerConfig, infoP
419419
r.Header.Add("X-Forwarded-Proto", "https")
420420
r.Header.Add("X-Forwarded-Host", r.Host)
421421
r.Header.Add("X-Forwarded-Port", "443")
422+
423+
coords := getWorkspaceCoords(r)
424+
if coords.Debug {
425+
r.Header.Add("X-WS-Proxy-Debug-Port", coords.Port)
426+
}
427+
422428
proxyPass(
423429
config,
424430
infoProvider,
@@ -449,7 +455,13 @@ func workspacePodResolver(config *Config, infoProvider WorkspaceInfoProvider, re
449455
func workspacePodPortResolver(config *Config, infoProvider WorkspaceInfoProvider, req *http.Request) (url *url.URL, err error) {
450456
coords := getWorkspaceCoords(req)
451457
workspaceInfo := infoProvider.WorkspaceInfo(coords.ID)
452-
return buildWorkspacePodURL(workspaceInfo.IPAddress, coords.Port)
458+
var port string
459+
if coords.Debug {
460+
port = fmt.Sprint(config.WorkspacePodConfig.DebugWorkspaceProxyPort)
461+
} else {
462+
port = coords.Port
463+
}
464+
return buildWorkspacePodURL(workspaceInfo.IPAddress, port)
453465
}
454466

455467
// workspacePodSupervisorResolver resolves to the workspace pods Supervisor url from the given request.

components/ws-proxy/pkg/proxy/routes_test.go

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -56,14 +56,15 @@ var (
5656
},
5757
}
5858

59-
ideServerHost = "localhost:20000"
60-
workspacePort = uint16(20001)
61-
supervisorPort = uint16(20002)
62-
workspaceDebugPort = uint16(20004)
63-
supervisorDebugPort = uint16(20005)
64-
workspaceHost = fmt.Sprintf("localhost:%d", workspacePort)
65-
portServeHost = fmt.Sprintf("localhost:%d", workspaces[0].Ports[0].Port)
66-
blobServeHost = "localhost:20003"
59+
ideServerHost = "localhost:20000"
60+
workspacePort = uint16(20001)
61+
supervisorPort = uint16(20002)
62+
workspaceDebugPort = uint16(20004)
63+
supervisorDebugPort = uint16(20005)
64+
debugWorkspaceProxyPort = uint16(20006)
65+
workspaceHost = fmt.Sprintf("localhost:%d", workspacePort)
66+
portServeHost = fmt.Sprintf("localhost:%d", workspaces[0].Ports[0].Port)
67+
blobServeHost = "localhost:20003"
6768

6869
config = Config{
6970
TransportConfig: &TransportConfig{
@@ -82,10 +83,11 @@ var (
8283
Scheme: "http",
8384
},
8485
WorkspacePodConfig: &WorkspacePodConfig{
85-
TheiaPort: workspacePort,
86-
SupervisorPort: supervisorPort,
87-
IDEDebugPort: workspaceDebugPort,
88-
SupervisorDebugPort: supervisorDebugPort,
86+
TheiaPort: workspacePort,
87+
SupervisorPort: supervisorPort,
88+
IDEDebugPort: workspaceDebugPort,
89+
SupervisorDebugPort: supervisorDebugPort,
90+
DebugWorkspaceProxyPort: debugWorkspaceProxyPort,
8991
},
9092
BuiltinPages: BuiltinPagesConfig{
9193
Location: "../../public",

components/ws-proxy/pkg/proxy/workspacerouter.go

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ type hostHeaderProvider func(req *http.Request) string
8080
func matchWorkspaceHostHeader(wsHostSuffix string, headerProvider hostHeaderProvider, matchPort bool) mux.MatcherFunc {
8181
var regexPrefix string
8282
if matchPort {
83-
regexPrefix = workspacePortRegex + workspaceIDRegex
83+
regexPrefix = workspacePortRegex + debugWorkspaceRegex + workspaceIDRegex
8484
} else {
8585
regexPrefix = debugWorkspaceRegex + workspaceIDRegex
8686
}
@@ -95,17 +95,23 @@ func matchWorkspaceHostHeader(wsHostSuffix string, headerProvider hostHeaderProv
9595

9696
var workspaceID, workspacePort, debugWorkspace string
9797
matches := r.FindStringSubmatch(hostname)
98-
if len(matches) < 3 {
99-
return false
100-
}
10198
if matchPort {
102-
// https://3000-coral-dragon-ilr0r6eq.ws-eu10.gitpod.io/index.html
103-
// debugWorkspace:
99+
if len(matches) < 4 {
100+
return false
101+
}
102+
if matches[2] != "" {
103+
debugWorkspace = "true"
104+
}
105+
// https://3000-debug-coral-dragon-ilr0r6eq.ws-eu10.gitpod.io/index.html
106+
// debugWorkspace: true
104107
// workspaceID: coral-dragon-ilr0r6eq
105108
// workspacePort: 3000
106-
workspaceID = matches[2]
109+
workspaceID = matches[3]
107110
workspacePort = matches[1]
108111
} else {
112+
if len(matches) < 3 {
113+
return false
114+
}
109115
// https://debug-coral-dragon-ilr0r6eq.ws-eu10.gitpod.io/index.html
110116
// debugWorkspace: true
111117
// workspaceID: coral-dragon-ilr0r6eq

install/installer/cmd/testdata/render/aws-setup/output.golden

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

install/installer/cmd/testdata/render/azure-setup/output.golden

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)