Skip to content

Commit bd953ed

Browse files
authored
refactor: directly download workspace (#1795)
1 parent 0681a45 commit bd953ed

19 files changed

+409
-81
lines changed

Makefile

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
GOOS := $(shell go env GOOS)
22
GOARCH := $(shell go env GOARCH)
3+
SKIP_INSTALL := false
34

45
# Platform host
56
PLATFORM_HOST := localhost:8080
67

78
# Build the CLI and Desktop
89
.PHONY: build
910
build:
10-
BUILD_PLATFORMS=$(GOOS) BUILD_ARCHS=$(GOARCH) ./hack/rebuild.sh
11+
SKIP_INSTALL=$(SKIP_INSTALL) BUILD_PLATFORMS=$(GOOS) BUILD_ARCHS=$(GOARCH) ./hack/rebuild.sh
1112

1213
# Run the desktop app
1314
.PHONY: run-desktop
@@ -17,12 +18,15 @@ run-desktop: build
1718
# Run the daemon against loft host
1819
.PHONY: run-daemon
1920
run-daemon: build
20-
devpod pro daemon start --host $(PLATFORM_HOST)
21+
devpod pro daemon start --host $(PLATFORM_HOST)
22+
23+
# Namespace to use for the platform
24+
NAMESPACE := loft
2125

2226
# Copy the devpod binary to the platform pod
2327
.PHONY: cp-to-platform
2428
cp-to-platform:
2529
SKIP_INSTALL=true BUILD_PLATFORMS=linux BUILD_ARCHS=$(GOARCH) ./hack/rebuild.sh
26-
POD=$$(kubectl get pod -n loft -l app=loft,release=loft -o jsonpath='{.items[0].metadata.name}'); \
30+
POD=$$(kubectl get pod -n $(NAMESPACE) -l app=loft,release=loft -o jsonpath='{.items[0].metadata.name}'); \
2731
echo "Copying ./test/devpod-linux-$(GOARCH) to pod $$POD"; \
28-
kubectl cp -n loft ./test/devpod-linux-$(GOARCH) $$POD:/usr/local/bin/devpod
32+
kubectl cp -n $(NAMESPACE) ./test/devpod-linux-$(GOARCH) $$POD:/usr/local/bin/devpod

cmd/agent/container/setup.go

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ package container
44

55
import (
66
"context"
7+
"crypto/tls"
78
"encoding/json"
89
"fmt"
910
"io"
10-
"net"
1111
"net/http"
1212
"net/url"
1313
"os"
@@ -48,7 +48,6 @@ import (
4848
"github.com/pkg/errors"
4949
"github.com/sirupsen/logrus"
5050
"github.com/spf13/cobra"
51-
"k8s.io/apimachinery/pkg/util/wait"
5251
)
5352

5453
var DockerlessImageConfigOutput = "/.dockerless/image.json"
@@ -585,37 +584,40 @@ func streamMount(ctx context.Context, workspaceInfo *provider2.ContainerWorkspac
585584
// if we have a platform workspace socket we connect directly to it
586585
if workspaceInfo.CLIOptions.Platform.Enabled {
587586
// check if the runner proxy socket exists
588-
logger.Infof("Waiting for runner proxy socket to be created")
589-
var err error
590-
waitErr := wait.PollUntilContextTimeout(ctx, time.Second, 30*time.Second, true, func(ctx context.Context) (done bool, err error) {
591-
if _, err = os.Stat(filepath.Join(RootDir, ts.RunnerProxySocket)); err != nil {
592-
return false, nil
593-
}
594-
595-
return true, nil
596-
})
597-
if waitErr != nil {
598-
return fmt.Errorf("finding runner proxy socket: %w", err)
599-
}
600-
601587
httpClient := &http.Client{
602588
Transport: &http.Transport{
603-
DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
604-
return net.Dial("unix", filepath.Join(RootDir, ts.RunnerProxySocket))
589+
TLSClientConfig: &tls.Config{
590+
InsecureSkipVerify: true,
605591
},
606592
},
607593
}
608594

595+
// build the url
609596
logger.Infof("Download %s into DevContainer %s", m.Source, m.Target)
610-
resp, err := httpClient.Get("http://runner-proxy/workspace-download?path=" + url.QueryEscape(m.Source))
597+
url := fmt.Sprintf(
598+
"https://%s/kubernetes/management/apis/management.loft.sh/v1/namespaces/%s/devpodworkspaceinstances/%s/download?path=%s",
599+
ts.RemoveProtocol(workspaceInfo.CLIOptions.Platform.PlatformHost),
600+
workspaceInfo.CLIOptions.Platform.InstanceNamespace,
601+
workspaceInfo.CLIOptions.Platform.InstanceName,
602+
url.QueryEscape(m.Source),
603+
)
604+
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
605+
if err != nil {
606+
return fmt.Errorf("create request: %w", err)
607+
}
608+
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", workspaceInfo.CLIOptions.Platform.AccessKey))
609+
610+
// send the request
611+
resp, err := httpClient.Do(req)
611612
if err != nil {
612613
return fmt.Errorf("download workspace: %w", err)
613614
}
614615
defer resp.Body.Close()
615616

616617
// check if the response is ok
617618
if resp.StatusCode != http.StatusOK {
618-
return fmt.Errorf("download workspace: %s", resp.Status)
619+
body, _ := io.ReadAll(resp.Body)
620+
return fmt.Errorf("download workspace: body = %s, status = %s", string(body), resp.Status)
619621
}
620622

621623
// create progress reader

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ require (
2929
github.com/gorilla/websocket v1.5.3
3030
github.com/joho/godotenv v1.5.1
3131
github.com/julienschmidt/httprouter v1.3.0
32-
github.com/loft-sh/agentapi/v4 v4.3.0-devpod.alpha.16
32+
github.com/loft-sh/agentapi/v4 v4.3.0-devpod.alpha.19
3333
github.com/loft-sh/analytics-client v0.0.0-20240219162240-2f4c64b2494e
34-
github.com/loft-sh/api/v4 v4.3.0-devpod.alpha.16
34+
github.com/loft-sh/api/v4 v4.3.0-devpod.alpha.19
3535
github.com/loft-sh/apiserver v0.0.0-20250206205835-422f1d472459
3636
github.com/loft-sh/log v0.0.0-20240219160058-26d83ffb46ac
3737
github.com/loft-sh/programming-language-detection v0.0.5

go.sum

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -422,12 +422,12 @@ github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhn
422422
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE=
423423
github.com/loft-sh/admin-apis v0.0.0-20250221182517-7499d86167d2 h1:om1MqUdW84ZQc0GMGGgFfPI6xpTbrF+6DwKVq+76R44=
424424
github.com/loft-sh/admin-apis v0.0.0-20250221182517-7499d86167d2/go.mod h1:WHCqWfljfD1hkwk41hLeqBhW2yeLvWipB1sH6vfnR7U=
425-
github.com/loft-sh/agentapi/v4 v4.3.0-devpod.alpha.16 h1:a7iemRlezfhsmSWJYmhPpeMNIkRZFHhrJL538nLCqlM=
426-
github.com/loft-sh/agentapi/v4 v4.3.0-devpod.alpha.16/go.mod h1:UteiUdc6QeGt2uXIo5K5qlReRa366GbPY5QdDuPyFCs=
425+
github.com/loft-sh/agentapi/v4 v4.3.0-devpod.alpha.19 h1:SApHDj5CL+uuwL3Yu3d8RGzaMM4BAnBdGrWD5bS58yA=
426+
github.com/loft-sh/agentapi/v4 v4.3.0-devpod.alpha.19/go.mod h1:UteiUdc6QeGt2uXIo5K5qlReRa366GbPY5QdDuPyFCs=
427427
github.com/loft-sh/analytics-client v0.0.0-20240219162240-2f4c64b2494e h1:JcPnMaoczikvpasi8OJ47dCkWZjfgFubWa4V2SZo7h0=
428428
github.com/loft-sh/analytics-client v0.0.0-20240219162240-2f4c64b2494e/go.mod h1:FFWcGASyM2QlWTDTCG/WBVM/XYr8btqYt335TFNRCFg=
429-
github.com/loft-sh/api/v4 v4.3.0-devpod.alpha.16 h1:tvVofFbGDND2ifiAQeA9gHCUhFR1GHBntlltSDs9fTk=
430-
github.com/loft-sh/api/v4 v4.3.0-devpod.alpha.16/go.mod h1:QvDqPCWhP8VG8GH8OnbJ3ps8SmdDiF6f2qHDI2mvqBI=
429+
github.com/loft-sh/api/v4 v4.3.0-devpod.alpha.19 h1:0451oc0fkDFtlOqW0sRHYW1E6gZ3DuO/u88gNxMYNk0=
430+
github.com/loft-sh/api/v4 v4.3.0-devpod.alpha.19/go.mod h1:tzvr5pIOmX9omKw0pjqTQTLj9n05WR8Llk2XuQ8sC+4=
431431
github.com/loft-sh/apiserver v0.0.0-20250206205835-422f1d472459 h1:6SrgBtT1S9ANsQMoO/O0Mq+hs9EbC5te5kPqOBfg5UI=
432432
github.com/loft-sh/apiserver v0.0.0-20250206205835-422f1d472459/go.mod h1:rung3jsKjaVAtykQN0vWmFHhx2A/umpRyAae8BJVSeE=
433433
github.com/loft-sh/log v0.0.0-20240219160058-26d83ffb46ac h1:Gz/7Lb7WgdgIv+KJz87ORA1zvQW52tUqKPGyunlp4dQ=

pkg/extract/extract.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ func Extract(origReader io.Reader, destFolder string, options ...Option) error {
3737
}
3838

3939
// read ahead
40-
bufioReader := bufio.NewReaderSize(origReader, 10*1024*1024)
40+
bufioReader := bufio.NewReaderSize(origReader, 1024*1024)
4141
testBytes, err := bufioReader.Peek(2) // read 2 bytes
4242
if err != nil {
4343
return err

pkg/ts/workspace_server.go

Lines changed: 0 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -240,9 +240,6 @@ func (s *WorkspaceServer) startListeners(ctx context.Context, projectName, works
240240
mux.HandleFunc("/git-credentials", func(w http.ResponseWriter, r *http.Request) {
241241
s.gitCredentialsHandler(w, r, lc, transport, projectName, workspaceName)
242242
})
243-
mux.HandleFunc("/workspace-download", func(w http.ResponseWriter, r *http.Request) {
244-
s.workspaceDownloadHandler(w, r, lc, transport, projectName, workspaceName)
245-
})
246243
if err := http.Serve(runnerProxyListener, mux); err != nil && err != http.ErrServerClosed {
247244
s.log.Errorf("HTTP runner proxy server error: %v", err)
248245
}
@@ -318,45 +315,6 @@ func (s *WorkspaceServer) gitCredentialsHandler(w http.ResponseWriter, r *http.R
318315
proxy.ServeHTTP(w, r)
319316
}
320317

321-
// httpPortForwardHandler is the HTTP reverse proxy handler for workspace.
322-
// It reconstructs the target URL using custom headers and forwards the request.
323-
func (s *WorkspaceServer) workspaceDownloadHandler(w http.ResponseWriter, r *http.Request, lc *tailscale.LocalClient, transport *http.Transport, projectName, workspaceName string) {
324-
s.log.Infof("Received workspace download request from %s", r.RemoteAddr)
325-
326-
// create a new http client with a custom transport
327-
discoveredRunner, err := s.discoverRunner(r.Context(), lc)
328-
if err != nil {
329-
http.Error(w, "failed to discover runner", http.StatusInternalServerError)
330-
return
331-
}
332-
333-
// get the path from the query
334-
path := r.URL.Query().Get("path")
335-
if path == "" {
336-
http.Error(w, "missing path", http.StatusBadRequest)
337-
return
338-
}
339-
340-
// build the runner URL
341-
runnerURL := fmt.Sprintf("http://%s.ts.loft/devpod/%s/%s/workspace-download?path=%s", discoveredRunner, projectName, workspaceName, url.QueryEscape(r.URL.Query().Get("path")))
342-
parsedURL, err := url.Parse(runnerURL)
343-
if err != nil {
344-
http.Error(w, "failed to parse runner URL", http.StatusInternalServerError)
345-
return
346-
}
347-
348-
// Build the reverse proxy with a custom Director.
349-
proxy := httputil.NewSingleHostReverseProxy(parsedURL)
350-
proxy.Director = func(req *http.Request) {
351-
dest := *parsedURL
352-
req.URL = &dest
353-
req.Host = dest.Host
354-
req.Header.Set("Authorization", "Bearer "+s.config.AccessKey)
355-
}
356-
proxy.Transport = transport
357-
proxy.ServeHTTP(w, r)
358-
}
359-
360318
// httpPortForwardHandler is the HTTP reverse proxy handler for workspace.
361319
// It reconstructs the target URL using custom headers and forwards the request.
362320
func (s *WorkspaceServer) httpPortForwardHandler(w http.ResponseWriter, r *http.Request) {

vendor/github.com/loft-sh/api/v4/pkg/apis/management/install/install.go

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/github.com/loft-sh/api/v4/pkg/apis/management/install/zz_generated.api.register.go

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/github.com/loft-sh/api/v4/pkg/apis/management/types.go

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/github.com/loft-sh/api/v4/pkg/apis/management/v1/devpodworkspaceinstance_download_types.go

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)