Skip to content
Merged
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
5 changes: 4 additions & 1 deletion components/ws-proxy/BUILD.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,7 @@ packages:
- CGO_ENABLED=0
- GOOS=linux
config:
packaging: library
packaging: library
# it's already tested in :app and running both tests for :app and :lib in
# parallel leads to port already in use errors
dontTest: true
10 changes: 7 additions & 3 deletions components/ws-proxy/pkg/proxy/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
validation "github.com/go-ozzo/ozzo-validation"
"golang.org/x/xerrors"

"github.com/gitpod-io/gitpod/common-go/log"
"github.com/gitpod-io/gitpod/common-go/util"
)

Expand Down Expand Up @@ -70,8 +71,9 @@ func (c *HostBasedIngressConfig) Validate() error {

// WorkspacePodConfig contains config around the workspace pod.
type WorkspacePodConfig struct {
TheiaPort uint16 `json:"theiaPort"`
SupervisorPort uint16 `json:"supervisorPort"`
TheiaPort uint16 `json:"theiaPort"`
SupervisorPort uint16 `json:"supervisorPort"`
// SupervisorImage is deprecated
SupervisorImage string `json:"supervisorImage"`
}

Expand All @@ -84,8 +86,10 @@ func (c *WorkspacePodConfig) Validate() error {
err := validation.ValidateStruct(c,
validation.Field(&c.TheiaPort, validation.Required),
validation.Field(&c.SupervisorPort, validation.Required),
validation.Field(&c.SupervisorImage, validation.Required),
)
if len(c.SupervisorImage) > 0 {
log.Warn("config value 'workspacePodConfig.supervisorImage' is deprected, use it only to be backwards compatible")
}
if err != nil {
return err
}
Expand Down
22 changes: 12 additions & 10 deletions components/ws-proxy/pkg/proxy/infoprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ type WorkspaceInfo struct {
InstanceID string
URL string

IDEImage string
IDEImage string
SupervisorImage string

// (parsed from URL)
IDEPublicPort string
Expand Down Expand Up @@ -148,15 +149,16 @@ func mapPodToWorkspaceInfo(pod *corev1.Pod) *WorkspaceInfo {
workspaceURL := pod.Annotations[kubernetes.WorkspaceURLAnnotation]

return &WorkspaceInfo{
WorkspaceID: pod.Labels[kubernetes.MetaIDLabel],
InstanceID: pod.Labels[kubernetes.WorkspaceIDLabel],
URL: workspaceURL,
IDEImage: imageSpec.IdeRef,
IDEPublicPort: getPortStr(workspaceURL),
IPAddress: pod.Status.PodIP,
Ports: extractExposedPorts(pod).Ports,
Auth: &wsapi.WorkspaceAuthentication{Admission: admission, OwnerToken: ownerToken},
StartedAt: pod.CreationTimestamp.Time,
WorkspaceID: pod.Labels[kubernetes.MetaIDLabel],
InstanceID: pod.Labels[kubernetes.WorkspaceIDLabel],
URL: workspaceURL,
IDEImage: imageSpec.IdeRef,
IDEPublicPort: getPortStr(workspaceURL),
SupervisorImage: imageSpec.SupervisorRef,
IPAddress: pod.Status.PodIP,
Ports: extractExposedPorts(pod).Ports,
Auth: &wsapi.WorkspaceAuthentication{Admission: admission, OwnerToken: ownerToken},
StartedAt: pod.CreationTimestamp.Time,
}
}

Expand Down
57 changes: 45 additions & 12 deletions components/ws-proxy/pkg/proxy/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ func (ir *ideRoutes) HandleSupervisorFrontendRoute(route *mux.Route) {

r := route.Subrouter()
r.Use(logRouteHandlerHandler("SupervisorIDEHostHandler"))
r.Use(ir.workspaceMustExistHandler)
// strip the frontend prefix, just for good measure
r.Use(func(h http.Handler) http.Handler {
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
Expand All @@ -171,17 +172,27 @@ func (ir *ideRoutes) HandleSupervisorFrontendRoute(route *mux.Route) {
})
})
// always hit the blobserver to ensure that blob is downloaded
r.NewRoute().HandlerFunc(proxyPass(ir.Config, ir.InfoProvider, func(cfg *Config, _ WorkspaceInfoProvider, req *http.Request) (*url.URL, error) {
return resolveSupervisorURL(cfg), nil
r.NewRoute().HandlerFunc(proxyPass(ir.Config, ir.InfoProvider, func(cfg *Config, infoProvider WorkspaceInfoProvider, req *http.Request) (*url.URL, error) {
info := getWorkspaceInfoFromContext(req.Context())
return resolveSupervisorURL(cfg, info, req)
}, func(h *proxyPassConfig) {
h.Transport = &blobserveTransport{
transport: h.Transport,
Config: ir.Config.Config,
resolveImage: func(t *blobserveTransport, req *http.Request) string {
var (
image = ir.Config.Config.WorkspacePodConfig.SupervisorImage
path = strings.TrimPrefix(req.URL.Path, "/"+image)
)
info := getWorkspaceInfoFromContext(req.Context())
if info == nil && len(ir.Config.Config.WorkspacePodConfig.SupervisorImage) == 0 {
// no workspace information available - cannot resolve supervisor image
return ""
}

// use the config value for backwards compatibility when info.SupervisorImage is not set
image := ir.Config.Config.WorkspacePodConfig.SupervisorImage
if info != nil && len(info.SupervisorImage) > 0 {
image = info.SupervisorImage
}

path := strings.TrimPrefix(req.URL.Path, "/"+image)
if path == "/worker-proxy.js" {
// worker must be served from the same origin
return ""
Expand All @@ -192,12 +203,23 @@ func (ir *ideRoutes) HandleSupervisorFrontendRoute(route *mux.Route) {
}))
}

func resolveSupervisorURL(cfg *Config) *url.URL {
func resolveSupervisorURL(cfg *Config, info *WorkspaceInfo, req *http.Request) (*url.URL, error) {
if info == nil && len(cfg.WorkspacePodConfig.SupervisorImage) == 0 {
log.WithFields(log.OWI("", getWorkspaceCoords(req).ID, "")).Warn("no workspace info available - cannot resolve supervisor route")
return nil, xerrors.Errorf("no workspace information available - cannot resolve supervisor route")
}

// use the config value for backwards compatibility when info.SupervisorImage is not set
supervisorImage := cfg.WorkspacePodConfig.SupervisorImage
if info != nil && len(info.SupervisorImage) > 0 {
supervisorImage = info.SupervisorImage
}

var dst url.URL
dst.Scheme = cfg.BlobServer.Scheme
dst.Host = cfg.BlobServer.Host
dst.Path = "/" + cfg.WorkspacePodConfig.SupervisorImage
return &dst
dst.Path = "/" + supervisorImage
return &dst, nil
}

type BlobserveInlineVars struct {
Expand Down Expand Up @@ -234,8 +256,13 @@ func (ir *ideRoutes) HandleRoot(route *mux.Route) {
// but it has to know exposed URLs in the context of current workspace cluster
// so first we ask blobserve to preload the supervisor image
// and if it is successful we pass exposed URLs to IDE and supervisor to blobserve for inlining
supervisorURL := resolveSupervisorURL(t.Config).String() + "/main.js"
preloadSupervisorReq, err := http.NewRequest("GET", supervisorURL, nil)
supervisorURL, err := resolveSupervisorURL(t.Config, info, req)
if err != nil {
log.WithError(err).Error("could not preload supervisor")
return image
}
supervisorURLString := supervisorURL.String() + "/main.js"
preloadSupervisorReq, err := http.NewRequest("GET", supervisorURLString, nil)
if err != nil {
log.WithField("supervisorURL", supervisorURL).WithError(err).Error("could not preload supervisor")
return image
Expand All @@ -251,9 +278,15 @@ func (ir *ideRoutes) HandleRoot(route *mux.Route) {
return image
}

// use the config value for backwards compatibility when info.SupervisorImage is not set
supervisorImage := t.Config.WorkspacePodConfig.SupervisorImage
if len(info.SupervisorImage) > 0 {
supervisorImage = info.SupervisorImage
}

inlineVars := &BlobserveInlineVars{
IDE: t.asBlobserveURL(image, ""),
SupervisorImage: t.asBlobserveURL(t.Config.WorkspacePodConfig.SupervisorImage, ""),
SupervisorImage: t.asBlobserveURL(supervisorImage, ""),
}
inlinveVarsValue, err := json.Marshal(inlineVars)
if err != nil {
Expand Down
9 changes: 4 additions & 5 deletions components/ws-proxy/pkg/proxy/routes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ const (
var (
workspaces = []WorkspaceInfo{
{
IDEImage: "gitpod-io/ide:latest",
IDEImage: "gitpod-io/ide:latest",
SupervisorImage: "gitpod-io/supervisor:latest",
Auth: &api.WorkspaceAuthentication{
Admission: api.AdmissionLevel_ADMIT_OWNER_ONLY,
OwnerToken: "owner-token",
Expand Down Expand Up @@ -72,9 +73,8 @@ var (
Scheme: "http",
},
WorkspacePodConfig: &WorkspacePodConfig{
TheiaPort: workspacePort,
SupervisorPort: supervisorPort,
SupervisorImage: "gitpod-io/supervisor:latest",
TheiaPort: workspacePort,
SupervisorPort: supervisorPort,
},
BuiltinPages: BuiltinPagesConfig{
Location: "../../public",
Expand Down Expand Up @@ -203,7 +203,6 @@ func TestRoutes(t *testing.T) {
Desc string
Config *Config
Request *http.Request
Workspaces []WorkspaceInfo
Router RouterFactory
Targets *Targets
IgnoreBody bool
Expand Down