Skip to content

Commit 634e10f

Browse files
authored
Create Dockerfile to serve oadp-cli binaries (migtools#72)
Signed-off-by: Joseph <[email protected]>
1 parent 5da6872 commit 634e10f

File tree

2 files changed

+124
-0
lines changed

2 files changed

+124
-0
lines changed

Dockerfile.download

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# This Dockerfile is used to cross-build the kubectl-oadp binaries for all platforms
2+
# It also builds a Go server that serves the binaries for download
3+
4+
FROM golang:1.24 AS builder
5+
6+
WORKDIR /app
7+
8+
COPY go.mod go.sum ./
9+
RUN go mod download && go mod verify
10+
11+
COPY . .
12+
13+
# Build everything, create tarballs, and clean up in one layer to reduce size
14+
RUN make release-build && \
15+
CGO_ENABLED=0 go build -o download-server ./cmd/downloads/server.go && \
16+
mkdir -p /archives && \
17+
for binary in kubectl-oadp-*; do \
18+
archive_name=$(echo "$binary" | sed 's/\.exe$//' ).tar.gz; \
19+
tar -czf "/archives/$archive_name" "$binary"; \
20+
echo "Created /archives/$archive_name"; \
21+
done && \
22+
go clean -cache -modcache -testcache && \
23+
rm -rf /root/.cache/go-build && \
24+
rm -rf /go/pkg
25+
26+
FROM registry.access.redhat.com/ubi8/ubi-minimal:latest
27+
28+
# Only copy compressed tarballs (much smaller than raw binaries)
29+
COPY --from=builder /archives /archives
30+
31+
# Copy the download server
32+
COPY --from=builder /app/download-server /usr/local/bin/download-server
33+
34+
EXPOSE 8080
35+
36+
CMD ["/usr/local/bin/download-server"]

cmd/downloads/server.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"log"
6+
"net/http"
7+
"os"
8+
"path/filepath"
9+
"strings"
10+
)
11+
12+
const (
13+
archiveDir = "/archives"
14+
port = "8080"
15+
)
16+
17+
func main() {
18+
// Verify archives exist
19+
files, err := filepath.Glob(filepath.Join(archiveDir, "*.tar.gz"))
20+
if err != nil || len(files) == 0 {
21+
log.Fatal("No archives found in ", archiveDir)
22+
}
23+
log.Printf("Found %d archives", len(files))
24+
25+
http.HandleFunc("/", listBinaries)
26+
http.HandleFunc("/download/", downloadBinary)
27+
28+
log.Printf("Starting server on port %s", port)
29+
log.Printf("Serving archives from %s", archiveDir)
30+
31+
if err := http.ListenAndServe(":"+port, nil); err != nil {
32+
log.Fatal(err)
33+
}
34+
}
35+
36+
func listBinaries(w http.ResponseWriter, r *http.Request) {
37+
files, err := filepath.Glob(filepath.Join(archiveDir, "*.tar.gz"))
38+
if err != nil {
39+
http.Error(w, "Error listing archives", http.StatusInternalServerError)
40+
return
41+
}
42+
43+
w.Header().Set("Content-Type", "text/html")
44+
fmt.Fprintf(w, "<html><head><title>kubectl-oadp Downloads</title></head><body>")
45+
fmt.Fprintf(w, "<h1>kubectl-oadp Binary Downloads</h1>")
46+
fmt.Fprintf(w, "<p>Download pre-built binaries for your platform:</p><ul>")
47+
48+
for _, file := range files {
49+
name := filepath.Base(file)
50+
info, err := os.Stat(file)
51+
if err != nil {
52+
continue
53+
}
54+
size := float64(info.Size()) / (1024 * 1024) // MB
55+
fmt.Fprintf(w, `<li><a href="/download/%s">%s</a> (%.2f MB)</li>`, name, name, size)
56+
}
57+
58+
fmt.Fprintf(w, "</ul>")
59+
fmt.Fprintf(w, "<h3>Installation:</h3>")
60+
fmt.Fprintf(w, "<pre>tar -xzf kubectl-oadp-&lt;platform&gt;.tar.gz\n")
61+
fmt.Fprintf(w, "chmod +x kubectl-oadp\n")
62+
fmt.Fprintf(w, "sudo mv kubectl-oadp /usr/local/bin/</pre>")
63+
fmt.Fprintf(w, "</body></html>")
64+
}
65+
66+
func downloadBinary(w http.ResponseWriter, r *http.Request) {
67+
filename := filepath.Base(r.URL.Path[len("/download/"):])
68+
69+
// Security: ensure filename is just the archive name
70+
if filepath.Dir(filename) != "." || !strings.HasSuffix(filename, ".tar.gz") {
71+
http.Error(w, "Invalid filename", http.StatusBadRequest)
72+
return
73+
}
74+
75+
filePath := filepath.Join(archiveDir, filename)
76+
77+
// Verify file exists
78+
if _, err := os.Stat(filePath); os.IsNotExist(err) {
79+
http.Error(w, "Archive not found", http.StatusNotFound)
80+
return
81+
}
82+
83+
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s", filename))
84+
w.Header().Set("Content-Type", "application/gzip")
85+
86+
http.ServeFile(w, r, filePath)
87+
log.Printf("Downloaded: %s from %s", filename, r.RemoteAddr)
88+
}

0 commit comments

Comments
 (0)