Skip to content

Commit 756055f

Browse files
authored
Add endpoint that returns the EC binary from the EC data directory (#5290)
* Add endpoint that returns the EC binary from the EC data directory
1 parent 4260662 commit 756055f

File tree

9 files changed

+129
-10
lines changed

9 files changed

+129
-10
lines changed

.github/actions/copy-assets/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM golang:1.24 as builder
1+
FROM golang:1.24.2 as builder
22
WORKDIR /action
33
COPY . /action
44

dev/dockerfiles/kotsadm/Dockerfile.local

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
FROM golang:1.24-alpine AS dlv-builder
1+
FROM golang:1.24.2-alpine AS dlv-builder
22

33
RUN go install github.com/go-delve/delve/cmd/[email protected]
44

5-
FROM golang:1.24-alpine
5+
FROM golang:1.24.2-alpine
66

77
RUN apk add --no-cache ca-certificates s3cmd curl git make bash
88

dev/dockerfiles/kurl-proxy/Dockerfile.local

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM golang:1.24-alpine
1+
FROM golang:1.24.2-alpine
22

33
RUN apk add --no-cache ca-certificates curl git make bash
44

dev/scripts/common.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ function populate() {
2727
-e GOCACHE=/replicatedhq/kots/dev/.gocache \
2828
-e GOMODCACHE=/replicatedhq/kots/dev/.gomodcache \
2929
-w /replicatedhq/kots \
30-
golang:1.24-alpine \
30+
golang:1.24.2-alpine \
3131
/bin/sh -c "apk add make bash git && make kots build"
3232
;;
3333
"kotsadm-web")
@@ -44,7 +44,7 @@ function populate() {
4444
-e GOCACHE=/replicatedhq/kots/dev/.gocache \
4545
-e GOMODCACHE=/replicatedhq/kots/dev/.gomodcache \
4646
-w /replicatedhq/kots/kurl_proxy \
47-
golang:1.24-alpine \
47+
golang:1.24.2-alpine \
4848
/bin/sh -c "apk add make bash git && make build"
4949
;;
5050
esac

e2e/playwright/tests/@target-kots-version-online/test.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ const validateOnlineUpdateRestrictive = async (page: Page, expect: Expect) => {
5252
await onlineCheckForUpdates(page, expect);
5353

5454
const availableUpdateCard = page.getByTestId("available-updates-card");
55-
await expect(availableUpdateCard).toContainText(constants.VENDOR_RESTRICTIVE_RELEASE_SEMVER);
55+
await expect(availableUpdateCard).toContainText(constants.VENDOR_RESTRICTIVE_RELEASE_SEMVER, { timeout: 30 * 1000 }); // 30 seconds
5656

5757
await expect(footer).not.toContainText(`${constants.PERMISSIVE_TARGET_KOTS_VERSION} available.`);
5858
};

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/replicatedhq/kots
22

3-
go 1.24.1
3+
go 1.24.2
44

55
require (
66
cloud.google.com/go/storage v1.45.0
@@ -250,7 +250,7 @@ require (
250250
github.com/k0sproject/k0s v1.30.10-0.20250117153350-dcf3c22bb568 // indirect
251251
github.com/kevinburke/ssh_config v1.2.0 // indirect
252252
github.com/klauspost/compress v1.17.11 // indirect
253-
github.com/klauspost/pgzip v1.2.6 // indirect
253+
github.com/klauspost/pgzip v1.2.6
254254
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
255255
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
256256
github.com/letsencrypt/boulder v0.0.0-20240620165639-de9c06129bec // indirect

kurl_proxy/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/replicatedhq/kots/kurl_proxy
22

3-
go 1.24.0
3+
go 1.24.2
44

55
require (
66
github.com/gin-gonic/gin v1.10.0
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
package handlers
2+
3+
import (
4+
"archive/tar"
5+
"fmt"
6+
"io"
7+
"net/http"
8+
"os"
9+
"path/filepath"
10+
11+
"github.com/klauspost/pgzip"
12+
"github.com/pkg/errors"
13+
"github.com/replicatedhq/kots/pkg/embeddedcluster"
14+
"github.com/replicatedhq/kots/pkg/logger"
15+
"github.com/replicatedhq/kots/pkg/util"
16+
)
17+
18+
// GetEmbeddedClusterBinary returns the embedded cluster binary as a .tgz file
19+
func (h *Handler) GetEmbeddedClusterBinary(w http.ResponseWriter, r *http.Request) {
20+
if !util.IsEmbeddedCluster() {
21+
logger.Error(errors.New("not an embedded cluster"))
22+
w.WriteHeader(http.StatusBadRequest)
23+
return
24+
}
25+
26+
// Get kubeclient
27+
kbClient, err := h.GetKubeClient(r.Context())
28+
if err != nil {
29+
logger.Error(errors.Wrap(err, "failed to get kubeclient"))
30+
w.WriteHeader(http.StatusInternalServerError)
31+
return
32+
}
33+
34+
// Get current installation
35+
installation, err := embeddedcluster.GetCurrentInstallation(r.Context(), kbClient)
36+
if err != nil {
37+
logger.Error(errors.Wrap(err, "failed to get current installation"))
38+
w.WriteHeader(http.StatusInternalServerError)
39+
return
40+
}
41+
42+
// Get data directory path from runtime config
43+
dataDir := ""
44+
if installation.Spec.RuntimeConfig != nil {
45+
dataDir = installation.Spec.RuntimeConfig.DataDir
46+
}
47+
if dataDir == "" {
48+
logger.Error(errors.New("data directory not found in runtime config"))
49+
w.WriteHeader(http.StatusInternalServerError)
50+
return
51+
}
52+
53+
// Get binary name from installation
54+
binaryName := installation.Spec.BinaryName
55+
if binaryName == "" {
56+
logger.Error(errors.New("binary name not found in installation"))
57+
w.WriteHeader(http.StatusInternalServerError)
58+
return
59+
}
60+
61+
// Path to EC binary
62+
binaryPath := filepath.Join(dataDir, "bin", binaryName)
63+
64+
// Check if binary exists
65+
binaryStat, err := os.Stat(binaryPath)
66+
if os.IsNotExist(err) {
67+
logger.Error(errors.Wrap(err, "binary file not found"))
68+
w.WriteHeader(http.StatusNotFound)
69+
return
70+
} else if err != nil {
71+
logger.Error(errors.Wrap(err, "failed to stat binary file"))
72+
w.WriteHeader(http.StatusInternalServerError)
73+
return
74+
}
75+
76+
// Open binary file
77+
binaryFile, err := os.Open(binaryPath)
78+
if err != nil {
79+
logger.Error(errors.Wrap(err, "failed to open binary file"))
80+
w.WriteHeader(http.StatusInternalServerError)
81+
return
82+
}
83+
defer binaryFile.Close()
84+
85+
// Set response headers for the .tgz file
86+
filename := fmt.Sprintf("%s.tgz", binaryName)
87+
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s", filename))
88+
w.Header().Set("Content-Type", "application/gzip")
89+
90+
// Create pgzip writer
91+
gzipWriter := pgzip.NewWriter(w)
92+
defer gzipWriter.Close()
93+
94+
// Create tar writer
95+
tarWriter := tar.NewWriter(gzipWriter)
96+
defer tarWriter.Close()
97+
98+
// Add binary file to tar archive
99+
header := &tar.Header{
100+
Name: binaryName,
101+
Mode: 0755, // Executable permission
102+
Size: binaryStat.Size(),
103+
ModTime: binaryStat.ModTime(),
104+
}
105+
106+
if err := tarWriter.WriteHeader(header); err != nil {
107+
logger.Error(errors.Wrap(err, "failed to write tar header"))
108+
return
109+
}
110+
111+
// Copy binary content to tar archive
112+
if _, err := io.Copy(tarWriter, binaryFile); err != nil {
113+
logger.Error(errors.Wrap(err, "failed to write binary to tar archive"))
114+
return
115+
}
116+
}

pkg/handlers/handlers.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,9 @@ func RegisterUnauthenticatedRoutes(handler *Handler, kotsStore store.Store, debu
387387

388388
// This handler requires a valid token in the query
389389
loggingRouter.Path("/api/v1/embedded-cluster/join").Methods("GET").HandlerFunc(handler.GetEmbeddedClusterNodeJoinCommand)
390+
391+
// This endpoint returns the embedded cluster binary as a .tgz file
392+
loggingRouter.Path("/api/v1/embedded-cluster/binary").Methods("GET").HandlerFunc(handler.GetEmbeddedClusterBinary)
390393
}
391394

392395
func StreamJSON(c *gwebsocket.Conn, payload interface{}) {

0 commit comments

Comments
 (0)