Skip to content

Commit 1f518cd

Browse files
n3rdc4ptnmaximilianbraundattito
committed
Initial open-source commit
Co-authored-by: Maximilian Braun <[email protected]> Co-authored-by: David Siregar <[email protected]>
1 parent 509aa78 commit 1f518cd

38 files changed

+2357
-3
lines changed

.dockerignore

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
main
2+
kubeconfig.yaml
3+
kube-api-reflection
4+
.null-ls*

.github/workflows/build.yml

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
name: Build
2+
on:
3+
push:
4+
branches:
5+
- main
6+
tags:
7+
- "*"
8+
9+
pull_request:
10+
branches:
11+
- main
12+
13+
workflow_dispatch:
14+
15+
env:
16+
DOCKER_IMAGE: ghcr.io/${{ github.repository }}
17+
18+
jobs:
19+
build:
20+
runs-on: ubuntu-latest
21+
permissions:
22+
id-token: write
23+
packages: write
24+
contents: read
25+
attestations: write
26+
27+
steps:
28+
- uses: actions/checkout@v4
29+
30+
- name: Docker meta
31+
id: meta
32+
uses: docker/metadata-action@v5
33+
with:
34+
# list of Docker images to use as base name for tags
35+
images: |
36+
${{ env.DOCKER_IMAGE }}
37+
# generate Docker tags based on the following events/attributes
38+
tags: |
39+
type=schedule
40+
type=ref,event=branch
41+
type=ref,event=pr
42+
type=semver,pattern={{version}}
43+
type=semver,pattern={{major}}.{{minor}}
44+
type=semver,pattern={{major}}
45+
type=raw,value=latest,enable={{is_default_branch}}
46+
47+
- name: Login to Docker Registry
48+
if: github.event_name != 'pull_request'
49+
uses: docker/login-action@v3
50+
with:
51+
registry: ghcr.io
52+
username: ${{ github.repository_owner }}
53+
password: ${{ secrets.GITHUB_TOKEN }}
54+
55+
- name: Set up QEMU
56+
uses: docker/setup-qemu-action@v3
57+
58+
- name: Set up Docker Buildx
59+
uses: docker/setup-buildx-action@v3
60+
61+
- name: Build and push
62+
uses: docker/build-push-action@v6
63+
with:
64+
context: .
65+
platforms: linux/amd64,linux/arm64
66+
push: ${{ github.event_name != 'pull_request' }}
67+
tags: ${{ steps.meta.outputs.tags }}
68+
labels: ${{ steps.meta.outputs.labels }}
69+
cache-from: type=gha
70+
cache-to: type=gha,mode=max
71+
72+
# Only for public repositories
73+
# - name: Attest
74+
# uses: actions/attest-build-provenance@v1
75+
# id: attest
76+
# with:
77+
# subject-name: ghcr.io/${{ github.repository }}
78+
# subject-digest: ${{ steps.push.outputs.digest }}
79+
# push-to-registry: true

.gitignore

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
.idea
2+
main
3+
kubeconfig.yaml
4+
kube-api-reflection
5+
.null-ls*
6+
tmp
7+
.env
8+
9+
coverage.html
10+
coverage.out

Dockerfile

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
FROM golang:1.23-bookworm AS build
2+
3+
WORKDIR /app
4+
5+
COPY go.mod ./
6+
COPY go.sum ./
7+
RUN go mod download
8+
9+
COPY . ./
10+
11+
RUN CGO_ENABLED=0 go build -o /bin/app cmd/server/main.go
12+
13+
## Deploy
14+
FROM scratch
15+
16+
WORKDIR /
17+
18+
COPY --from=build /bin/app /bin/app
19+
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
20+
21+
EXPOSE 3000
22+
23+
ENTRYPOINT ["/bin/app"]

Makefile

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
GO := go
2+
PKG := ./...
3+
COVERAGE_FILE := coverage.out
4+
COVERAGE_HTML := coverage.html
5+
6+
# Default goal
7+
.PHONY: all
8+
all: clean build test
9+
10+
# Build the project
11+
.PHONY: build
12+
build:
13+
$(GO) build -v $(PKG)
14+
15+
# Run tests with coverage
16+
.PHONY: test
17+
test:
18+
$(GO) test -v -coverprofile=$(COVERAGE_FILE) $(PKG)
19+
$(GO) tool cover -html=$(COVERAGE_FILE) -o $(COVERAGE_HTML)
20+
@echo "Coverage report generated at $(COVERAGE_HTML)"
21+
22+
# Clean up generated files
23+
.PHONY: clean
24+
clean:
25+
$(GO) clean
26+
rm -f $(COVERAGE_FILE) $(COVERAGE_HTML)
27+
28+
# Format the code
29+
.PHONY: fmt
30+
fmt:
31+
$(GO) fmt $(PKG)
32+
33+
# Lint the code
34+
.PHONY: lint
35+
lint:
36+
golangci-lint run
37+
38+
# Run all checks
39+
.PHONY: check
40+
check: fmt lint test
41+
42+
# Install dependencies
43+
.PHONY: deps
44+
deps:
45+
$(GO) mod tidy
46+
$(GO) mod vendor
47+
48+
.PHONY: start
49+
start:
50+
$(GO) run cmd/server/main.go

README.md

+67-3
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,84 @@
1-
[![REUSE status](https://api.reuse.software/badge/github.com/openmcp-project/ui-backend)](https://api.reuse.software/info/github.com/openmcp-project/ui-backend)
2-
31
# ui-backend
42

3+
[![REUSE status](https://api.reuse.software/badge/github.com/openmcp-project/ui-backend)](https://api.reuse.software/info/github.com/openmcp-project/ui-backend)
4+
55
## About this project
66

77
UI backend for @openmcp-project
88

99
## Requirements and Setup
1010

11-
*Insert a short description what is required to get your project running...*
11+
Setup: Requires the CRATE_KUBECONFIG environment variable.
12+
Start: Instructions to run the server using go run cmd/server/main.go.
13+
Call UI-backend: Details on how to make requests to the ui-backend with necessary headers for authorization and target API server configuration.
14+
Parsing JSON: Support for jsonpath and jq to parse JSON before sending it to the client.
15+
16+
### Use Case
17+
18+
#### Problem
19+
20+
We want to call the kubernetes api server directly from the browser, but we have several problems preventing us from calling the api from the browser:
21+
22+
- TLS certificate is not signed from a well-known CA
23+
- CORS is not configured most of the time
24+
25+
#### Solution
26+
27+
The `ui-backend` server acts like a proxy when talking to the Crate-Cluster or MCPs from the browser.
28+
The browser sends the request to the `ui-backend`, with authorization data and optionally the project, workspace and controlplane name of the MCP in header data.
29+
30+
- If requesting the Crate: The request will get send to the crate cluster with the authorization data in the headers
31+
- If requesting an MCP: The `ui-backend` will call the Crate to get the `kubeconfig` of the MCP and then calls the MCP with that kubeconfig
32+
33+
There are only some modifications done when piping the request to the api server, preventing some headers from going through.
34+
35+
### Usage
36+
37+
#### Setup
38+
39+
The service needs only one mandatory environment variable, `CRATE_KUBECONFIG`, with the kubeconfig-yaml as a string. The `.users` field does not has to be set, as this part will get overwritten with the data from the requests.
40+
41+
#### Start
42+
43+
Run `go run cmd/server/main.go`
44+
45+
#### Call UI-backend
46+
47+
Call `ui-backend` with the http-method and path as you would directly to the api server.
48+
Put the authorization data in the following headers:
49+
50+
- `X-Client-Certificate-Data`
51+
- `X-Client-Key-Data`
52+
53+
or (for OIDC):
54+
55+
- `Authorization`
56+
57+
Also configure the api-server you want to call:
58+
59+
- Crate: Add the header `X-Use-Crate-Cluster: true`
60+
- MCP: Add the headers `X-Project-Name`, `X-Workspace-Name` and `X-Control-Plane-Name`
61+
62+
#### Parsing JSON
63+
64+
`ui-backend` support jsonpath (kubectl version) and jq (gojq) to parse json before sending it to the client, reducing the data transfered to the client.
65+
66+
Usage:
67+
68+
- JsonPath: Add a header `X-jsonpath` with the jsonpath query
69+
- JQ: Add a header `X-jq` with the jq query
70+
71+
### Deployment
72+
73+
- Docker-Image: deploy-releases-hyperspace-docker.common.repositories.cloud.sap/cloud-orchestration/cola/ui-backend
74+
- Helm-Chart: oci://europe-docker.pkg.dev/sap-gcp-cp-k8s-stable-hub/cola/charts/ui-backend (is manually pushed)
1275

1376
## Support, Feedback, Contributing
1477

1578
This project is open to feature requests/suggestions, bug reports etc. via [GitHub issues](https://github.com/openmcp-project/ui-backend/issues). Contribution and feedback are encouraged and always welcome. For more information about how to contribute, the project structure, as well as additional contribution information, see our [Contribution Guidelines](CONTRIBUTING.md).
1679

1780
## Security / Disclosure
81+
1882
If you find any bug that may be a security problem, please follow our instructions at [in our security policy](https://github.com/openmcp-project/ui-backend/security/policy) on how to report it. Please do not create GitHub issues for security-related doubts or problems.
1983

2084
## Code of Conduct

chart/ui-backend/.helmignore

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Patterns to ignore when building packages.
2+
# This supports shell glob matching, relative path matching, and
3+
# negation (prefixed with !). Only one pattern per line.
4+
.DS_Store
5+
# Common VCS dirs
6+
.git/
7+
.gitignore
8+
.bzr/
9+
.bzrignore
10+
.hg/
11+
.hgignore
12+
.svn/
13+
# Common backup files
14+
*.swp
15+
*.bak
16+
*.tmp
17+
*.orig
18+
*~
19+
# Various IDEs
20+
.project
21+
.idea/
22+
*.tmproj
23+
.vscode/

chart/ui-backend/Chart.yaml

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
apiVersion: v2
2+
name: ui-backend
3+
description: A Helm chart for the ui-backend
4+
type: application
5+
version: 0.3.0

chart/ui-backend/templates/NOTES.txt

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
1. Get the application URL by running these commands:
2+
{{- if .Values.ingress.enabled }}
3+
{{- range $host := .Values.ingress.hosts }}
4+
{{- range .paths }}
5+
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}
6+
{{- end }}
7+
{{- end }}
8+
{{- else if contains "NodePort" .Values.service.type }}
9+
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "ui-backend.fullname" . }})
10+
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
11+
echo http://$NODE_IP:$NODE_PORT
12+
{{- else if contains "LoadBalancer" .Values.service.type }}
13+
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
14+
You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "ui-backend.fullname" . }}'
15+
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "ui-backend.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
16+
echo http://$SERVICE_IP:{{ .Values.service.port }}
17+
{{- else if contains "ClusterIP" .Values.service.type }}
18+
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "ui-backend.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
19+
export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
20+
echo "Visit http://127.0.0.1:8080 to use your application"
21+
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
22+
{{- end }}
+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
{{/*
2+
Expand the name of the chart.
3+
*/}}
4+
{{- define "ui-backend.name" -}}
5+
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
6+
{{- end }}
7+
8+
{{/*
9+
Create a default fully qualified app name.
10+
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
11+
If release name contains chart name it will be used as a full name.
12+
*/}}
13+
{{- define "ui-backend.fullname" -}}
14+
{{- if .Values.fullnameOverride }}
15+
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
16+
{{- else }}
17+
{{- $name := default .Chart.Name .Values.nameOverride }}
18+
{{- if contains $name .Release.Name }}
19+
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
20+
{{- else }}
21+
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
22+
{{- end }}
23+
{{- end }}
24+
{{- end }}
25+
26+
{{/*
27+
Create chart name and version as used by the chart label.
28+
*/}}
29+
{{- define "ui-backend.chart" -}}
30+
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
31+
{{- end }}
32+
33+
{{/*
34+
Common labels
35+
*/}}
36+
{{- define "ui-backend.labels" -}}
37+
helm.sh/chart: {{ include "ui-backend.chart" . }}
38+
{{ include "ui-backend.selectorLabels" . }}
39+
{{- if .Chart.AppVersion }}
40+
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
41+
{{- end }}
42+
app.kubernetes.io/managed-by: {{ .Release.Service }}
43+
{{- end }}
44+
45+
{{/*
46+
Selector labels
47+
*/}}
48+
{{- define "ui-backend.selectorLabels" -}}
49+
app.kubernetes.io/name: {{ include "ui-backend.name" . }}
50+
app.kubernetes.io/instance: {{ .Release.Name }}
51+
{{- end }}

0 commit comments

Comments
 (0)