Skip to content

Commit fae8f3b

Browse files
committed
cleanup
1 parent 5f0cfbe commit fae8f3b

13 files changed

Lines changed: 220 additions & 156 deletions

File tree

Dockerfile

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -12,32 +12,6 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
FROM golang:1.24.0 AS go-build-env
16-
WORKDIR /app
17-
18-
# Accept build arguments for multi-arch support
19-
ARG TARGETARCH
20-
ARG TARGETOS
21-
ARG LDFLAGS
22-
23-
RUN apt-get update && apt-get install -y make jq
24-
25-
# <- COPY go.mod and go.sum files to the workspace
26-
COPY go.mod .
27-
COPY go.sum .
28-
29-
# COPY the source code as the last step
30-
COPY . .
31-
32-
# Build with custom LDFLAGS if provided, otherwise use make build
33-
# Use TARGETOS and TARGETARCH for cross-compilation
34-
RUN if [ -n "$LDFLAGS" ]; then \
35-
echo "Building with LDFLAGS: $LDFLAGS for $TARGETOS/$TARGETARCH"; \
36-
CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -ldflags="$LDFLAGS" -o bin/backend ./cmd/backend; \
37-
else \
38-
GOOS=$TARGETOS GOARCH=$TARGETARCH make build; \
39-
fi
40-
4115
# Use node:lts-alpine for better compatibility and smaller size
4216
FROM node:20.18.0-alpine3.20 AS ui-build-env
4317
WORKDIR /app
@@ -64,6 +38,35 @@ ENV VITE_BUILD_TARGET=docker
6438
# Building UI with Docker-specific config
6539
RUN npm run build
6640

41+
# Build Go binary with embedded UI assets
42+
FROM golang:1.24.0 AS go-build-env
43+
WORKDIR /app
44+
45+
# Accept build arguments for multi-arch support
46+
ARG TARGETARCH
47+
ARG TARGETOS
48+
ARG LDFLAGS
49+
50+
RUN apt-get update && apt-get install -y make jq
51+
52+
# Copy go.mod and go.sum files first for better caching
53+
COPY go.mod .
54+
COPY go.sum .
55+
56+
# Copy the source code
57+
COPY . .
58+
59+
# Copy built UI assets for embedding
60+
COPY --from=ui-build-env /app/dist ./backend/static/web/dist
61+
62+
# Build with embedded assets
63+
RUN if [ -n "$LDFLAGS" ]; then \
64+
echo "Building with LDFLAGS: $LDFLAGS for $TARGETOS/$TARGETARCH"; \
65+
CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -ldflags="$LDFLAGS" -o bin/backend ./cmd/backend; \
66+
else \
67+
CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH make build; \
68+
fi
69+
6770
FROM alpine:3.22.1
6871
RUN apk --update add ca-certificates
6972

Makefile

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -409,11 +409,15 @@ image-local:
409409
@echo " $(IMAGE_REPO)/konnector:$(REV) ($(PLATFORMS))"
410410
@echo " $(IMAGE_REPO)/backend:$(REV) ($(PLATFORMS))"
411411

412+
# Kind cluster configuration
413+
KIND_CLUSTER ?= kube-bind
414+
DOCKER_REPO ?= $(IMAGE_REPO)
415+
412416
.PHONY: kind-load
413417
kind-load:
414418
@echo "Loading images into kind cluster '$(KIND_CLUSTER)'"
415-
kind load docker-image $(KO_DOCKER_REPO)/konnector:$(REV) --name $(KIND_CLUSTER)
416-
kind load docker-image $(KO_DOCKER_REPO)/backend:$(REV) --name $(KIND_CLUSTER)
419+
kind load docker-image $(DOCKER_REPO)/konnector:$(REV) --name $(KIND_CLUSTER)
420+
kind load docker-image $(DOCKER_REPO)/backend:$(REV) --name $(KIND_CLUSTER)
417421
@echo "Successfully loaded images into kind cluster '$(KIND_CLUSTER)'"
418422

419423
.PHONY: helm-build-local

backend/http/handler.go

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import (
3838
"github.com/kube-bind/kube-bind/backend/kubernetes"
3939
"github.com/kube-bind/kube-bind/backend/session"
4040
"github.com/kube-bind/kube-bind/backend/spaserver"
41+
"github.com/kube-bind/kube-bind/backend/static"
4142
bindversion "github.com/kube-bind/kube-bind/pkg/version"
4243
kubebindv1alpha2 "github.com/kube-bind/kube-bind/sdk/apis/kubebind/v1alpha2"
4344
)
@@ -93,7 +94,7 @@ func NewHandler(
9394
}, nil
9495
}
9596

96-
func (h *handler) AddRoutes(mux *mux.Router) {
97+
func (h *handler) AddRoutes(mux *mux.Router) error {
9798
// API routes - Server contains double routes for when backend is multi-cluster aware or single cluster.
9899
// When called multi-cluster aware route in single cluster mode, it will ignore cluster parameter.
99100
mux.HandleFunc("/api/clusters/{cluster}/exports", h.handleServiceExport).Methods(http.MethodGet)
@@ -120,16 +121,24 @@ func (h *handler) AddRoutes(mux *mux.Router) {
120121
mux.HandleFunc("/clusters/{cluster}/exports", h.handleExportsRedirect).Methods(http.MethodGet)
121122
mux.HandleFunc("/exports", h.handleExportsRedirect).Methods(http.MethodGet)
122123

123-
if strings.HasPrefix(h.frontend, "http://") {
124+
switch {
125+
case strings.HasPrefix(h.frontend, "http://"):
126+
// Development mode: proxy to frontend dev server
124127
spaserver, err := spaserver.NewSPAReverseProxyServer(h.frontend)
125128
if err != nil {
126-
panic(fmt.Sprintf("failed to create SPA reverse proxy server: %v", err)) // Development only.
129+
return err
127130
}
128131
mux.PathPrefix("/").Handler(spaserver)
129-
} else {
132+
case h.frontend == "embedded":
133+
// Production mode: serve embedded static files
134+
fileSystem := static.GetFileSystem()
135+
mux.PathPrefix("/").Handler(spaserver.NewSPAFileServer(fileSystem))
136+
default:
137+
// Custom filesystem path
130138
fileSystem := http.Dir(h.frontend)
131139
mux.PathPrefix("/").Handler(spaserver.NewSPAFileServer(fileSystem))
132140
}
141+
return nil
133142
}
134143

135144
func (h *handler) handleExportsRedirect(w http.ResponseWriter, r *http.Request) {
@@ -579,10 +588,25 @@ func (h *handler) handleBindPost(w http.ResponseWriter, r *http.Request) {
579588
}
580589

581590
// Parse JSON request body
591+
// Parse JSON request body
592+
const maxBodySize = 1 << 20 // 1 MB
593+
r.Body = http.MaxBytesReader(w, r.Body, maxBodySize)
582594
var bindRequest kubebindv1alpha2.BindableResourcesRequest
583595
if err := json.NewDecoder(r.Body).Decode(&bindRequest); err != nil {
584596
logger.Error(err, "failed to parse JSON request body")
585-
http.Error(w, "invalid JSON request body", http.StatusBadRequest)
597+
var maxBytesError *http.MaxBytesError
598+
if errors.As(err, &maxBytesError) {
599+
http.Error(w, "request body too large", http.StatusRequestEntityTooLarge)
600+
} else {
601+
http.Error(w, "invalid JSON request body", http.StatusBadRequest)
602+
}
603+
return
604+
}
605+
606+
err = bindRequest.Validate()
607+
if err != nil {
608+
logger.Error(err, "validation failed for bind request")
609+
http.Error(w, fmt.Sprintf("invalid bind request: %v", err), http.StatusBadRequest)
586610
return
587611
}
588612

backend/options/options.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ func NewOptions() *Options {
9999
ClusterScopedIsolation: string(kubebindv1alpha2.IsolationPrefixed),
100100
ServerURL: "",
101101
SchemaSource: CustomResourceDefinitionSource.String(),
102-
Frontend: "/www",
102+
Frontend: "embedded",
103103
},
104104
}
105105
}

backend/server.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,10 @@ func NewServer(ctx context.Context, c *Config) (*Server, error) {
123123
if err != nil {
124124
return nil, fmt.Errorf("error setting up HTTP Handler: %w", err)
125125
}
126-
handler.AddRoutes(s.WebServer.Router)
126+
err = handler.AddRoutes(s.WebServer.Router)
127+
if err != nil {
128+
return nil, fmt.Errorf("error adding routes to HTTP Server: %w", err)
129+
}
127130

128131
opts := controller.TypedOptions[mcreconcile.Request]{
129132
SkipNameValidation: ptr.To(c.Options.TestingSkipNameValidation),

backend/static/embed.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
Copyright 2025 The Kube Bind Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package static
18+
19+
import (
20+
"embed"
21+
"io/fs"
22+
"net/http"
23+
)
24+
25+
// Static files embedded in the binary.
26+
//
27+
//go:embed web/dist/*
28+
var StaticFiles embed.FS
29+
30+
// GetFileSystem returns the embedded file system for serving static files.
31+
func GetFileSystem() http.FileSystem {
32+
// Create a sub-filesystem starting from web/dist
33+
subFS, err := fs.Sub(StaticFiles, "web/dist")
34+
if err != nil {
35+
panic("failed to create sub filesystem: " + err.Error())
36+
}
37+
return http.FS(subFS)
38+
}

cli/pkg/kubectl/bind/plugin/bind.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ import (
3131
"strings"
3232
"time"
3333

34-
"github.com/davecgh/go-spew/spew"
3534
"github.com/spf13/cobra"
3635
"github.com/spf13/pflag"
3736
corev1 "k8s.io/api/core/v1"
@@ -158,7 +157,7 @@ func (b *BindOptions) Run(ctx context.Context, urlCh chan<- string) error {
158157
if err != nil {
159158
return fmt.Errorf("failed to fetch provider: %q: %v", exportURL, err)
160159
}
161-
spew.Dump(provider)
160+
162161
if provider.APIVersion != kubebindv1alpha2.GroupVersion {
163162
return fmt.Errorf("unsupported binding provider version: %q != %q", provider.APIVersion, kubebindv1alpha2.GroupVersion)
164163
}

hack/run-frontend.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/bin/bash
22

33
# Build frontend script for kube-bind
4-
# This script builds the Vue.js frontend and embeds it into the Go binary
4+
# This script builds the Vue.js frontend and serves it using a development server.
55

66
set -e
77

sdk/apis/kubebind/v1alpha2/bindingresponse_types.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,13 @@ limitations under the License.
1717
package v1alpha2
1818

1919
import (
20+
"errors"
21+
"fmt"
22+
"strings"
23+
2024
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2125
"k8s.io/apimachinery/pkg/runtime"
26+
"k8s.io/apimachinery/pkg/util/validation"
2227
)
2328

2429
// BindingResponse is a non-CRUD resource that is returned by the server after
@@ -166,3 +171,52 @@ type BindableResourcesRequest struct {
166171
// +kubebuilder:validation:Optional
167172
Namespaces []Namespaces `json:"namespaces,omitempty"`
168173
}
174+
175+
func (r *BindableResourcesRequest) Validate() error {
176+
if len(r.Resources) == 0 {
177+
return errors.New("at least one resource must be specified")
178+
}
179+
for i, res := range r.Resources {
180+
if res.Kind == "" {
181+
return fmt.Errorf("resource at index %d has no kind", i)
182+
}
183+
if res.Scope == "" {
184+
return fmt.Errorf("resource at index %d has no scope", i)
185+
}
186+
if res.APIVersion == "" {
187+
return fmt.Errorf("resource at index %d has no apiVersion", i)
188+
}
189+
if res.Group == "" {
190+
return fmt.Errorf("resource at index %d has no group", i)
191+
}
192+
if res.Resource == "" {
193+
return fmt.Errorf("resource at index %d has no resource", i)
194+
}
195+
}
196+
if len(r.Namespaces) > 0 {
197+
for i, ns := range r.Namespaces {
198+
if ns.Name == "" {
199+
return fmt.Errorf("namespace at index %d has no name", i)
200+
}
201+
}
202+
}
203+
if r.Name == "" {
204+
return errors.New("name is required")
205+
}
206+
207+
// Validate DNS name format for the request name
208+
if errs := validation.IsDNS1123Label(r.Name); len(errs) > 0 {
209+
return fmt.Errorf("name %q is not a valid DNS label: %s", r.Name, strings.Join(errs, ", "))
210+
}
211+
212+
// Validate namespace names
213+
if len(r.Namespaces) > 0 {
214+
for i, ns := range r.Namespaces {
215+
if errs := validation.IsDNS1123Label(ns.Name); len(errs) > 0 {
216+
return fmt.Errorf("namespace at index %d has invalid name %q: %s", i, ns.Name, strings.Join(errs, ", "))
217+
}
218+
}
219+
}
220+
221+
return nil
222+
}

web/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ Frontend routes handle browser-based redirects for:
4848
#### Development with Hot Reload (Recommended)
4949
```bash
5050
# Terminal 1: Start Go backend
51-
go run ./cmd/backend --listen-port=8080 --frontent http://localhost:3000
51+
go run ./cmd/backend --listen-port=8080 --frontend http://localhost:3000
5252

5353
# Terminal 2: Start frontend dev server with hot reload
5454
cd web
@@ -73,7 +73,7 @@ npm run dev
7373

7474
## Project Structure
7575

76-
```
76+
```text
7777
src/
7878
├── main.ts # Application entry point and routing
7979
├── App.vue # Root component

0 commit comments

Comments
 (0)