Skip to content
This repository was archived by the owner on Jun 26, 2026. It is now read-only.

Commit 6b40579

Browse files
matejvasekclaude
authored andcommitted
refactor: replace nginx with Go backend
Replace the nginx reverse proxy with a custom Go server that embeds and serves static files. Supports TLS with automatic fallback to HTTP-only when certs are absent. Switches the Dockerfile to a multi-stage build (Node → Go → ubi9-micro) and removes the nginx ConfigMap from the Helm chart. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Matej Vašek <matejvasek@gmail.com>
1 parent 5e9f8bf commit 6b40579

9 files changed

Lines changed: 123 additions & 38 deletions

File tree

Dockerfile

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,33 @@
1-
FROM registry.access.redhat.com/ubi9/nodejs-22:latest AS build
1+
FROM --platform=$BUILDPLATFORM registry.access.redhat.com/ubi9/nodejs-22:latest AS build
22
USER root
33
ENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0
44
RUN npm i -g corepack && corepack enable
55

6-
ADD . /usr/src/app
76
WORKDIR /usr/src/app
8-
RUN yarn install --immutable && yarn build
97

10-
FROM registry.access.redhat.com/ubi9/nginx-120:latest
8+
COPY package.json yarn.lock .yarnrc.yml ./
9+
COPY .yarn/ .yarn/
10+
RUN yarn install --immutable
1111

12-
COPY --from=build /usr/src/app/dist /usr/share/nginx/html
12+
COPY --exclude=node_modules --exclude=backend . .
13+
RUN yarn build
14+
15+
FROM --platform=$BUILDPLATFORM registry.access.redhat.com/ubi9/go-toolset:1.24 AS go-build
16+
ARG TARGETOS
17+
ARG TARGETARCH
18+
19+
WORKDIR /opt/app-root/src/backend
20+
21+
COPY --chown=1001:0 backend/go.mod backend/go.sum /opt/app-root/src/backend/
22+
RUN go mod download
23+
24+
COPY --chown=1001:0 --from=build /usr/src/app/dist /opt/app-root/src/backend/static
25+
COPY --chown=1001:0 backend/ /opt/app-root/src/backend/
26+
RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -ldflags="-s -w" -o plugin-backend .
27+
28+
FROM registry.access.redhat.com/ubi9-micro:latest
29+
30+
COPY --from=go-build /opt/app-root/src/backend/plugin-backend /usr/bin/plugin-backend
1331
USER 1001
1432

15-
ENTRYPOINT ["nginx", "-g", "daemon off;"]
33+
ENTRYPOINT ["plugin-backend"]

backend/go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module github.com/openshift/console-plugin-template/backend
2+
3+
go 1.23.0

backend/go.sum

Whitespace-only changes.

backend/main.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package main
2+
3+
import (
4+
"crypto/tls"
5+
"embed"
6+
"flag"
7+
"fmt"
8+
"io/fs"
9+
"log"
10+
"net"
11+
"net/http"
12+
"os"
13+
)
14+
15+
//go:embed static/*
16+
var staticFiles embed.FS
17+
18+
func main() {
19+
httpPort := flag.Int("http-port", 8080, "HTTP server port")
20+
httpsPort := flag.Int("https-port", 8443, "HTTPS server port")
21+
certFile := flag.String("cert", "/var/cert/tls.crt", "TLS certificate file")
22+
keyFile := flag.String("key", "/var/cert/tls.key", "TLS key file")
23+
flag.Parse()
24+
25+
static, err := fs.Sub(staticFiles, "static")
26+
if err != nil {
27+
log.Fatalf("Failed to create sub filesystem: %v", err)
28+
}
29+
30+
handler := loggingMiddleware(http.FileServer(http.FS(static)))
31+
32+
_, certErr := os.Stat(*certFile)
33+
_, keyErr := os.Stat(*keyFile)
34+
if certErr == nil && keyErr == nil {
35+
go func() {
36+
ln, err := net.Listen("tcp", fmt.Sprintf(":%d", *httpPort))
37+
if err != nil {
38+
log.Fatal(err)
39+
}
40+
log.Printf("Listening on http://%s", ln.Addr())
41+
log.Fatal(http.Serve(ln, handler))
42+
}()
43+
44+
cert, err := tls.LoadX509KeyPair(*certFile, *keyFile)
45+
if err != nil {
46+
log.Fatalf("Failed to load TLS certificate: %v", err)
47+
}
48+
ln, err := net.Listen("tcp", fmt.Sprintf(":%d", *httpsPort))
49+
if err != nil {
50+
log.Fatal(err)
51+
}
52+
tlsLn := tls.NewListener(ln, &tls.Config{
53+
Certificates: []tls.Certificate{cert},
54+
})
55+
log.Printf("Listening on https://%s", ln.Addr())
56+
log.Fatal(http.Serve(tlsLn, handler))
57+
} else {
58+
ln, err := net.Listen("tcp", fmt.Sprintf(":%d", *httpPort))
59+
if err != nil {
60+
log.Fatal(err)
61+
}
62+
log.Printf("TLS certificate not found, listening on http://%s", ln.Addr())
63+
log.Fatal(http.Serve(ln, handler))
64+
}
65+
}
66+
67+
func loggingMiddleware(next http.Handler) http.Handler {
68+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
69+
log.Printf("%s %s %s", r.RemoteAddr, r.Method, r.URL.Path)
70+
next.ServeHTTP(w, r)
71+
})
72+
}

backend/static/.gitkeep

Whitespace-only changes.

charts/openshift-console-plugin/templates/configmap.yaml

Lines changed: 0 additions & 24 deletions
This file was deleted.

charts/openshift-console-plugin/templates/deployment.yaml

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ spec:
2626
ports:
2727
- containerPort: {{ .Values.plugin.port }}
2828
protocol: TCP
29+
args:
30+
- "--https-port={{ .Values.plugin.port }}"
2931
imagePullPolicy: {{ .Values.plugin.imagePullPolicy }}
3032
{{- if and (.Values.plugin.securityContext.enabled) (.Values.plugin.containerSecurityContext) }}
3133
securityContext: {{ tpl (toYaml (omit .Values.plugin.containerSecurityContext "enabled")) $ | nindent 12 }}
@@ -36,19 +38,11 @@ spec:
3638
- name: {{ template "openshift-console-plugin.certificateSecret" . }}
3739
readOnly: true
3840
mountPath: /var/cert
39-
- name: nginx-conf
40-
readOnly: true
41-
mountPath: /etc/nginx/nginx.conf
42-
subPath: nginx.conf
4341
volumes:
4442
- name: {{ template "openshift-console-plugin.certificateSecret" . }}
4543
secret:
4644
secretName: {{ template "openshift-console-plugin.certificateSecret" . }}
4745
defaultMode: 420
48-
- name: nginx-conf
49-
configMap:
50-
name: {{ template "openshift-console-plugin.name" . }}
51-
defaultMode: 420
5246
restartPolicy: Always
5347
dnsPolicy: ClusterFirst
5448
{{- if and (.Values.plugin.securityContext.enabled) (.Values.plugin.podSecurityContext) }}

docs/claude-progress.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
11
# Claude Progress Log
22
# Newest entries first. Agents: append your entry at the top after the header.
33

4+
---
5+
## 2026-03-30 | Session: Replace nginx with Go backend
6+
Worked on: Replaced nginx reverse proxy with custom Go backend server
7+
Completed:
8+
- Go backend serving static files with embed.FS
9+
- Multi-stage Dockerfile (Node build → Go build → ubi9-micro runtime)
10+
- TLS support with automatic fallback to HTTP-only
11+
- Removed nginx ConfigMap from Helm chart
12+
Left off: Function list page with data entries
13+
Blockers: None
14+
415
---
516
## 2026-03-30 | Session: Empty state feature and docs restructure
617
Worked on: Implemented function list empty state, restructured docs, added init.sh

docs/features.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,17 @@
4242
],
4343
"passes": false
4444
},
45+
{
46+
"category": "technical",
47+
"description": "Go backend: custom Go server replaces nginx for static file serving",
48+
"steps": [
49+
"Go server embeds and serves webpack-built static files",
50+
"Multi-stage Dockerfile: Node build, Go build, ubi9-micro runtime",
51+
"TLS support with automatic fallback to HTTP-only",
52+
"Nginx ConfigMap removed from Helm chart"
53+
],
54+
"passes": true
55+
},
4556
{
4657
"category": "functional",
4758
"description": "Function Create Page renders form with all fields",

0 commit comments

Comments
 (0)