change layout of login
This commit is contained in:
parent
6063b18052
commit
6ac4efbf9e
23 changed files with 875 additions and 7 deletions
2
.github/workflows/build-test-push.yml
vendored
2
.github/workflows/build-test-push.yml
vendored
|
|
@ -7,7 +7,7 @@ on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
IMAGE_NAME: batty1039.startdedicated.net:5000/cms-demo
|
IMAGE_NAME: batty1039.startdedicated.net:5000/cms
|
||||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
|
||||||
47
.github/workflows/ci.yml
vendored
Normal file
47
.github/workflows/ci.yml
vendored
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Check out source
|
||||||
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v6
|
||||||
|
with:
|
||||||
|
go-version-file: go.mod
|
||||||
|
cache: true
|
||||||
|
|
||||||
|
- name: Set up Node.js
|
||||||
|
uses: actions/setup-node@v5
|
||||||
|
with:
|
||||||
|
node-version: 22
|
||||||
|
cache: npm
|
||||||
|
cache-dependency-path: ui/app/package-lock.json
|
||||||
|
|
||||||
|
- name: Set up Helm
|
||||||
|
uses: azure/setup-helm@v4
|
||||||
|
with:
|
||||||
|
version: v3.18.6
|
||||||
|
|
||||||
|
- name: Install frontend dependencies
|
||||||
|
run: npm ci
|
||||||
|
working-directory: ui/app
|
||||||
|
|
||||||
|
- name: Build frontend
|
||||||
|
run: npm run build
|
||||||
|
working-directory: ui/app
|
||||||
|
|
||||||
|
- name: Run Go checks
|
||||||
|
run: make check
|
||||||
|
|
||||||
|
- name: Validate Helm environments
|
||||||
|
run: make helm-lint
|
||||||
148
.github/workflows/deploy-rancher.yml
vendored
Normal file
148
.github/workflows/deploy-rancher.yml
vendored
Normal file
|
|
@ -0,0 +1,148 @@
|
||||||
|
name: Deploy to Rancher
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_run:
|
||||||
|
workflows:
|
||||||
|
- Build, test, and publish
|
||||||
|
types:
|
||||||
|
- completed
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
environment:
|
||||||
|
description: Deployment environment
|
||||||
|
required: true
|
||||||
|
default: development
|
||||||
|
type: choice
|
||||||
|
options:
|
||||||
|
- development
|
||||||
|
- production
|
||||||
|
image_tag:
|
||||||
|
description: Image tag (defaults to latest)
|
||||||
|
required: false
|
||||||
|
default: latest
|
||||||
|
type: string
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: cms-${{ github.event_name == 'workflow_dispatch' && inputs.environment || 'production' }}
|
||||||
|
cancel-in-progress: false
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
if: >-
|
||||||
|
github.event_name == 'workflow_dispatch' ||
|
||||||
|
(github.event.workflow_run.conclusion == 'success' &&
|
||||||
|
github.event.workflow_run.head_branch == 'main')
|
||||||
|
name: Deploy ${{ github.event_name == 'workflow_dispatch' && inputs.environment || 'production' }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
environment: ${{ github.event_name == 'workflow_dispatch' && inputs.environment || 'production' }}
|
||||||
|
env:
|
||||||
|
TARGET_ENVIRONMENT: ${{ github.event_name == 'workflow_dispatch' && inputs.environment || 'production' }}
|
||||||
|
KUBE_CONFIG_BASE64: ${{ secrets.RANCHER_KUBE_CONFIG_BASE64 }}
|
||||||
|
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
|
||||||
|
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
|
||||||
|
CMS_SSO_TOKEN: ${{ secrets.CMS_SSO_TOKEN }}
|
||||||
|
CMS_ALLOWED_ORIGINS: ${{ vars.CMS_ALLOWED_ORIGINS }}
|
||||||
|
CMS_SSO_HEADER: ${{ vars.CMS_SSO_HEADER }}
|
||||||
|
CMS_SSO_COOKIE: ${{ vars.CMS_SSO_COOKIE }}
|
||||||
|
RANCHER_NAMESPACE: ${{ vars.RANCHER_NAMESPACE }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check out source
|
||||||
|
uses: actions/checkout@v5
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event_name == 'workflow_run' && github.event.workflow_run.head_sha || github.sha }}
|
||||||
|
|
||||||
|
- name: Set up Helm
|
||||||
|
uses: azure/setup-helm@v4
|
||||||
|
with:
|
||||||
|
version: v3.18.6
|
||||||
|
|
||||||
|
- name: Set up kubectl
|
||||||
|
uses: azure/setup-kubectl@v4
|
||||||
|
with:
|
||||||
|
version: v1.34.1
|
||||||
|
|
||||||
|
- name: Resolve deployment settings
|
||||||
|
env:
|
||||||
|
REQUESTED_TAG: ${{ inputs.image_tag }}
|
||||||
|
PUBLISHED_SHA: ${{ github.event.workflow_run.head_sha }}
|
||||||
|
run: |
|
||||||
|
set -eu
|
||||||
|
if [ -z "$KUBE_CONFIG_BASE64" ]; then
|
||||||
|
echo "RANCHER_KUBE_CONFIG_BASE64 is required" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ -z "$REGISTRY_USERNAME" ] || [ -z "$REGISTRY_PASSWORD" ]; then
|
||||||
|
echo "REGISTRY_USERNAME and REGISTRY_PASSWORD are required" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
namespace="${RANCHER_NAMESPACE:-cms-$TARGET_ENVIRONMENT}"
|
||||||
|
if [ -n "$PUBLISHED_SHA" ]; then
|
||||||
|
image_tag="$PUBLISHED_SHA"
|
||||||
|
else
|
||||||
|
image_tag="${REQUESTED_TAG:-latest}"
|
||||||
|
fi
|
||||||
|
allowed_origins="$CMS_ALLOWED_ORIGINS"
|
||||||
|
if [ -z "$allowed_origins" ]; then
|
||||||
|
if [ "$TARGET_ENVIRONMENT" = "production" ]; then
|
||||||
|
allowed_origins="https://cms.urpi.be"
|
||||||
|
else
|
||||||
|
allowed_origins="http://localhost:30081"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
{
|
||||||
|
echo "DEPLOY_NAMESPACE=$namespace"
|
||||||
|
echo "IMAGE_TAG=$image_tag"
|
||||||
|
echo "RESOLVED_ALLOWED_ORIGINS=$allowed_origins"
|
||||||
|
} >> "$GITHUB_ENV"
|
||||||
|
|
||||||
|
- name: Configure Rancher cluster access
|
||||||
|
run: |
|
||||||
|
set -eu
|
||||||
|
mkdir -p "$HOME/.kube"
|
||||||
|
printf '%s' "$KUBE_CONFIG_BASE64" | base64 --decode > "$HOME/.kube/config"
|
||||||
|
chmod 600 "$HOME/.kube/config"
|
||||||
|
kubectl cluster-info
|
||||||
|
|
||||||
|
- name: Configure registry credentials
|
||||||
|
run: |
|
||||||
|
set -eu
|
||||||
|
kubectl create namespace "$DEPLOY_NAMESPACE" \
|
||||||
|
--dry-run=client -o yaml | kubectl apply -f -
|
||||||
|
kubectl -n "$DEPLOY_NAMESPACE" create secret docker-registry cms-registry \
|
||||||
|
--docker-server=batty1039.startdedicated.net:5000 \
|
||||||
|
--docker-username="$REGISTRY_USERNAME" \
|
||||||
|
--docker-password="$REGISTRY_PASSWORD" \
|
||||||
|
--dry-run=client -o yaml | kubectl apply -f -
|
||||||
|
|
||||||
|
- name: Deploy Helm release
|
||||||
|
run: |
|
||||||
|
set -eu
|
||||||
|
values_file="deploy/helm/cms/values-$TARGET_ENVIRONMENT.yaml"
|
||||||
|
set -- \
|
||||||
|
--set-string "image.tag=$IMAGE_TAG" \
|
||||||
|
--set "imagePullSecrets[0].name=cms-registry" \
|
||||||
|
--set-string "app.allowedOrigins=$RESOLVED_ALLOWED_ORIGINS" \
|
||||||
|
--set-string "secrets.ssoToken=$CMS_SSO_TOKEN"
|
||||||
|
if [ -n "$CMS_SSO_HEADER" ]; then
|
||||||
|
set -- "$@" --set-string "app.ssoHeader=$CMS_SSO_HEADER"
|
||||||
|
fi
|
||||||
|
if [ -n "$CMS_SSO_COOKIE" ]; then
|
||||||
|
set -- "$@" --set-string "app.ssoCookie=$CMS_SSO_COOKIE"
|
||||||
|
fi
|
||||||
|
helm upgrade --install cms deploy/helm/cms \
|
||||||
|
--namespace "$DEPLOY_NAMESPACE" \
|
||||||
|
--values "$values_file" \
|
||||||
|
--atomic \
|
||||||
|
--wait \
|
||||||
|
--timeout 10m \
|
||||||
|
"$@"
|
||||||
|
|
||||||
|
- name: Verify deployment
|
||||||
|
run: |
|
||||||
|
kubectl -n "$DEPLOY_NAMESPACE" rollout status deployment/cms \
|
||||||
|
--timeout=5m
|
||||||
|
helm test cms --namespace "$DEPLOY_NAMESPACE" --logs
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -12,6 +12,7 @@ cmd/server/web/dist/*
|
||||||
.env
|
.env
|
||||||
data/
|
data/
|
||||||
*.json.tmp
|
*.json.tmp
|
||||||
|
deploy/local.yaml
|
||||||
|
|
||||||
# Node
|
# Node
|
||||||
node_modules/
|
node_modules/
|
||||||
|
|
|
||||||
33
Makefile
33
Makefile
|
|
@ -1,10 +1,15 @@
|
||||||
SHELL := /bin/sh
|
SHELL := /bin/sh
|
||||||
|
|
||||||
IMAGE ?= cms-demo
|
IMAGE ?= cms
|
||||||
TAG ?= latest
|
TAG ?= latest
|
||||||
REGISTRY_IMAGE ?= batty1039.startdedicated.net:5000/cms-demo
|
REGISTRY_IMAGE ?= batty1039.startdedicated.net:5000/cms
|
||||||
|
HELM ?= helm
|
||||||
|
HELM_CHART := deploy/helm/cms
|
||||||
|
HELM_RELEASE ?= cms
|
||||||
|
HELM_NAMESPACE ?= cms
|
||||||
|
KUBECONFIG ?= $(HOME)/.config/kubectl/rancher.urpi.local.yaml
|
||||||
|
|
||||||
.PHONY: help ui frontend backend demo production run test check docker docker-push docker-run clean
|
.PHONY: help ui frontend backend demo production run test check docker docker-push docker-run clean rancher-storage helm-lint helm-deploy-development helm-deploy-production
|
||||||
|
|
||||||
help:
|
help:
|
||||||
@echo "make ui build the React app into cmd/server/web/dist"
|
@echo "make ui build the React app into cmd/server/web/dist"
|
||||||
|
|
@ -71,3 +76,25 @@ clean:
|
||||||
rm -rf cmd/server/web/dist ui/dist
|
rm -rf cmd/server/web/dist ui/dist
|
||||||
mkdir -p cmd/server/web/dist
|
mkdir -p cmd/server/web/dist
|
||||||
printf 'Placeholder so the Go embed directive resolves before the frontend is built.\nRun `make ui` to replace this directory with the real React build.\n' > cmd/server/web/dist/placeholder.txt
|
printf 'Placeholder so the Go embed directive resolves before the frontend is built.\nRun `make ui` to replace this directory with the real React build.\n' > cmd/server/web/dist/placeholder.txt
|
||||||
|
rancher-storage:
|
||||||
|
KUBECONFIG="$(KUBECONFIG)" kubectl apply -f deploy/rancher/local-path.yaml
|
||||||
|
|
||||||
|
helm-lint:
|
||||||
|
$(HELM) lint $(HELM_CHART) -f $(HELM_CHART)/values-development.yaml
|
||||||
|
$(HELM) lint $(HELM_CHART) -f $(HELM_CHART)/values-production.yaml
|
||||||
|
$(HELM) template $(HELM_RELEASE) $(HELM_CHART) \
|
||||||
|
-f $(HELM_CHART)/values-development.yaml >/dev/null
|
||||||
|
$(HELM) template $(HELM_RELEASE) $(HELM_CHART) \
|
||||||
|
-f $(HELM_CHART)/values-production.yaml >/dev/null
|
||||||
|
|
||||||
|
helm-deploy-development:
|
||||||
|
KUBECONFIG="$(KUBECONFIG)" $(HELM) upgrade --install $(HELM_RELEASE) $(HELM_CHART) \
|
||||||
|
--namespace $(HELM_NAMESPACE)-development --create-namespace \
|
||||||
|
-f $(HELM_CHART)/values-development.yaml \
|
||||||
|
--atomic --wait --timeout 10m
|
||||||
|
|
||||||
|
helm-deploy-production:
|
||||||
|
KUBECONFIG="$(KUBECONFIG)" $(HELM) upgrade --install $(HELM_RELEASE) $(HELM_CHART) \
|
||||||
|
--namespace $(HELM_NAMESPACE)-production --create-namespace \
|
||||||
|
-f $(HELM_CHART)/values-production.yaml \
|
||||||
|
--atomic --wait --timeout 10m
|
||||||
5
deploy/helm/cms/.helmignore
Normal file
5
deploy/helm/cms/.helmignore
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
.git/
|
||||||
|
.github/
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
*.tgz
|
||||||
6
deploy/helm/cms/Chart.yaml
Normal file
6
deploy/helm/cms/Chart.yaml
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
apiVersion: v2
|
||||||
|
name: cms
|
||||||
|
description: CMS application
|
||||||
|
type: application
|
||||||
|
version: 0.1.0
|
||||||
|
appVersion: "0.1.0"
|
||||||
12
deploy/helm/cms/templates/NOTES.txt
Normal file
12
deploy/helm/cms/templates/NOTES.txt
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
CMS has been installed in namespace {{ .Release.Namespace }}.
|
||||||
|
|
||||||
|
{{- if .Values.ingress.enabled }}
|
||||||
|
Open the host configured in ingress.hosts.
|
||||||
|
{{- else if eq .Values.service.type "NodePort" }}
|
||||||
|
The application is exposed on cluster node port {{ .Values.service.nodePort }}.
|
||||||
|
Open it through the configured external reverse proxy.
|
||||||
|
{{- else }}
|
||||||
|
Run:
|
||||||
|
kubectl -n {{ .Release.Namespace }} port-forward service/{{ include "cms.fullname" . }} 8080:{{ .Values.service.port }}
|
||||||
|
Then open http://127.0.0.1:8080.
|
||||||
|
{{- end }}
|
||||||
33
deploy/helm/cms/templates/_helpers.tpl
Normal file
33
deploy/helm/cms/templates/_helpers.tpl
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
{{- define "cms.name" -}}
|
||||||
|
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{- define "cms.fullname" -}}
|
||||||
|
{{- if .Values.fullnameOverride }}
|
||||||
|
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
|
||||||
|
{{- else }}
|
||||||
|
{{- $name := default .Chart.Name .Values.nameOverride }}
|
||||||
|
{{- if contains $name .Release.Name }}
|
||||||
|
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
|
||||||
|
{{- else }}
|
||||||
|
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{- define "cms.labels" -}}
|
||||||
|
helm.sh/chart: {{ printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" }}
|
||||||
|
app.kubernetes.io/name: {{ include "cms.name" . }}
|
||||||
|
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||||
|
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
||||||
|
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{- define "cms.selectorLabels" -}}
|
||||||
|
app.kubernetes.io/name: {{ include "cms.name" . }}
|
||||||
|
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{- define "cms.secretName" -}}
|
||||||
|
{{- printf "%s-secrets" (include "cms.fullname" .) }}
|
||||||
|
{{- end }}
|
||||||
108
deploy/helm/cms/templates/app-deployment.yaml
Normal file
108
deploy/helm/cms/templates/app-deployment.yaml
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: {{ include "cms.fullname" . }}
|
||||||
|
labels:
|
||||||
|
{{- include "cms.labels" . | nindent 4 }}
|
||||||
|
spec:
|
||||||
|
replicas: {{ .Values.replicaCount }}
|
||||||
|
strategy:
|
||||||
|
type: {{ .Values.strategy.type }}
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
{{- include "cms.selectorLabels" . | nindent 6 }}
|
||||||
|
app.kubernetes.io/component: application
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
checksum/secrets: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }}
|
||||||
|
{{- with .Values.podAnnotations }}
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
labels:
|
||||||
|
{{- include "cms.selectorLabels" . | nindent 8 }}
|
||||||
|
app.kubernetes.io/component: application
|
||||||
|
{{- with .Values.podLabels }}
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
spec:
|
||||||
|
automountServiceAccountToken: false
|
||||||
|
{{- with .Values.imagePullSecrets }}
|
||||||
|
imagePullSecrets:
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
securityContext:
|
||||||
|
{{- toYaml .Values.podSecurityContext | nindent 8 }}
|
||||||
|
containers:
|
||||||
|
- name: cms
|
||||||
|
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
|
||||||
|
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||||
|
securityContext:
|
||||||
|
{{- toYaml .Values.securityContext | nindent 12 }}
|
||||||
|
env:
|
||||||
|
- name: CMS_PROFILE
|
||||||
|
value: {{ .Values.app.environment | quote }}
|
||||||
|
- name: CMS_ALLOWED_ORIGINS
|
||||||
|
value: {{ .Values.app.allowedOrigins | quote }}
|
||||||
|
- name: CMS_CONTENT_FILE
|
||||||
|
value: {{ ternary .Values.app.contentFile "" .Values.persistence.enabled | quote }}
|
||||||
|
- name: CMS_POSTGRES_URL
|
||||||
|
value: {{ .Values.app.postgresURL | quote }}
|
||||||
|
- name: CMS_SSO_HEADER
|
||||||
|
value: {{ .Values.app.ssoHeader | quote }}
|
||||||
|
- name: CMS_SSO_COOKIE
|
||||||
|
value: {{ .Values.app.ssoCookie | quote }}
|
||||||
|
- name: CMS_SSO_TOKEN
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "cms.secretName" . }}
|
||||||
|
key: sso-token
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
containerPort: 8080
|
||||||
|
protocol: TCP
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /health
|
||||||
|
port: http
|
||||||
|
initialDelaySeconds: 5
|
||||||
|
periodSeconds: 5
|
||||||
|
timeoutSeconds: 3
|
||||||
|
failureThreshold: 12
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /health
|
||||||
|
port: http
|
||||||
|
initialDelaySeconds: 20
|
||||||
|
periodSeconds: 10
|
||||||
|
timeoutSeconds: 3
|
||||||
|
failureThreshold: 3
|
||||||
|
resources:
|
||||||
|
{{- toYaml .Values.resources | nindent 12 }}
|
||||||
|
volumeMounts:
|
||||||
|
- name: temporary-files
|
||||||
|
mountPath: /tmp
|
||||||
|
{{- if .Values.persistence.enabled }}
|
||||||
|
- name: content
|
||||||
|
mountPath: /data
|
||||||
|
{{- end }}
|
||||||
|
volumes:
|
||||||
|
- name: temporary-files
|
||||||
|
emptyDir: {}
|
||||||
|
{{- if .Values.persistence.enabled }}
|
||||||
|
- name: content
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: {{ include "cms.fullname" . }}
|
||||||
|
{{- end }}
|
||||||
|
{{- with .Values.nodeSelector }}
|
||||||
|
nodeSelector:
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
{{- with .Values.affinity }}
|
||||||
|
affinity:
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
{{- with .Values.tolerations }}
|
||||||
|
tolerations:
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
19
deploy/helm/cms/templates/app-service.yaml
Normal file
19
deploy/helm/cms/templates/app-service.yaml
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: {{ include "cms.fullname" . }}
|
||||||
|
labels:
|
||||||
|
{{- include "cms.labels" . | nindent 4 }}
|
||||||
|
spec:
|
||||||
|
type: {{ .Values.service.type }}
|
||||||
|
selector:
|
||||||
|
{{- include "cms.selectorLabels" . | nindent 4 }}
|
||||||
|
app.kubernetes.io/component: application
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
port: {{ .Values.service.port }}
|
||||||
|
targetPort: http
|
||||||
|
protocol: TCP
|
||||||
|
{{- if and (eq .Values.service.type "NodePort") .Values.service.nodePort }}
|
||||||
|
nodePort: {{ .Values.service.nodePort }}
|
||||||
|
{{- end }}
|
||||||
35
deploy/helm/cms/templates/ingress.yaml
Normal file
35
deploy/helm/cms/templates/ingress.yaml
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
{{- if .Values.ingress.enabled }}
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: {{ include "cms.fullname" . }}
|
||||||
|
labels:
|
||||||
|
{{- include "cms.labels" . | nindent 4 }}
|
||||||
|
{{- with .Values.ingress.annotations }}
|
||||||
|
annotations:
|
||||||
|
{{- toYaml . | nindent 4 }}
|
||||||
|
{{- end }}
|
||||||
|
spec:
|
||||||
|
{{- with .Values.ingress.className }}
|
||||||
|
ingressClassName: {{ . }}
|
||||||
|
{{- end }}
|
||||||
|
{{- with .Values.ingress.tls }}
|
||||||
|
tls:
|
||||||
|
{{- toYaml . | nindent 4 }}
|
||||||
|
{{- end }}
|
||||||
|
rules:
|
||||||
|
{{- range .Values.ingress.hosts }}
|
||||||
|
- host: {{ .host | quote }}
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
{{- range .paths }}
|
||||||
|
- path: {{ .path }}
|
||||||
|
pathType: {{ .pathType }}
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: {{ include "cms.fullname" $ }}
|
||||||
|
port:
|
||||||
|
number: {{ $.Values.service.port }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
9
deploy/helm/cms/templates/secret.yaml
Normal file
9
deploy/helm/cms/templates/secret.yaml
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: {{ include "cms.secretName" . }}
|
||||||
|
labels:
|
||||||
|
{{- include "cms.labels" . | nindent 4 }}
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
sso-token: {{ .Values.secrets.ssoToken | quote }}
|
||||||
20
deploy/helm/cms/templates/tests/test-connection.yaml
Normal file
20
deploy/helm/cms/templates/tests/test-connection.yaml
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: "{{ include "cms.fullname" . }}-test-connection"
|
||||||
|
labels:
|
||||||
|
{{- include "cms.labels" . | nindent 4 }}
|
||||||
|
annotations:
|
||||||
|
helm.sh/hook: test
|
||||||
|
helm.sh/hook-delete-policy: before-hook-creation
|
||||||
|
spec:
|
||||||
|
automountServiceAccountToken: false
|
||||||
|
restartPolicy: Never
|
||||||
|
containers:
|
||||||
|
- name: wget
|
||||||
|
image: busybox:1.37
|
||||||
|
command:
|
||||||
|
- wget
|
||||||
|
args:
|
||||||
|
- -qO-
|
||||||
|
- "http://{{ include "cms.fullname" . }}:{{ .Values.service.port }}/health"
|
||||||
26
deploy/helm/cms/values-development.yaml
Normal file
26
deploy/helm/cms/values-development.yaml
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
image:
|
||||||
|
tag: latest
|
||||||
|
pullPolicy: Always
|
||||||
|
|
||||||
|
app:
|
||||||
|
environment: demo
|
||||||
|
allowedOrigins: http://localhost:30081
|
||||||
|
contentFile: ""
|
||||||
|
|
||||||
|
service:
|
||||||
|
type: NodePort
|
||||||
|
nodePort: 30081
|
||||||
|
|
||||||
|
ingress:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 50m
|
||||||
|
memory: 96Mi
|
||||||
|
limits:
|
||||||
|
cpu: 300m
|
||||||
|
memory: 384Mi
|
||||||
|
|
||||||
|
persistence:
|
||||||
|
enabled: false
|
||||||
27
deploy/helm/cms/values-production.yaml
Normal file
27
deploy/helm/cms/values-production.yaml
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
image:
|
||||||
|
tag: latest
|
||||||
|
pullPolicy: IfNotPresent
|
||||||
|
|
||||||
|
app:
|
||||||
|
environment: production
|
||||||
|
allowedOrigins: https://cms.urpi.be
|
||||||
|
contentFile: /data/content.json
|
||||||
|
|
||||||
|
service:
|
||||||
|
type: NodePort
|
||||||
|
nodePort: 31373
|
||||||
|
|
||||||
|
ingress:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 200m
|
||||||
|
memory: 256Mi
|
||||||
|
limits:
|
||||||
|
cpu: "1"
|
||||||
|
memory: 768Mi
|
||||||
|
|
||||||
|
persistence:
|
||||||
|
enabled: true
|
||||||
|
size: 5Gi
|
||||||
76
deploy/helm/cms/values.yaml
Normal file
76
deploy/helm/cms/values.yaml
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
nameOverride: ""
|
||||||
|
fullnameOverride: ""
|
||||||
|
|
||||||
|
replicaCount: 1
|
||||||
|
|
||||||
|
image:
|
||||||
|
repository: batty1039.startdedicated.net:5000/cms
|
||||||
|
tag: latest
|
||||||
|
pullPolicy: IfNotPresent
|
||||||
|
|
||||||
|
imagePullSecrets: []
|
||||||
|
|
||||||
|
app:
|
||||||
|
environment: production
|
||||||
|
allowedOrigins: http://localhost:8080
|
||||||
|
contentFile: /data/content.json
|
||||||
|
postgresURL: ""
|
||||||
|
ssoHeader: X-SSO-Token
|
||||||
|
ssoCookie: ""
|
||||||
|
|
||||||
|
secrets:
|
||||||
|
ssoToken: ""
|
||||||
|
|
||||||
|
service:
|
||||||
|
type: ClusterIP
|
||||||
|
port: 80
|
||||||
|
nodePort: null
|
||||||
|
|
||||||
|
ingress:
|
||||||
|
enabled: false
|
||||||
|
className: nginx
|
||||||
|
annotations: {}
|
||||||
|
hosts:
|
||||||
|
- host: cms.local
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
tls: []
|
||||||
|
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 100m
|
||||||
|
memory: 128Mi
|
||||||
|
limits:
|
||||||
|
cpu: 500m
|
||||||
|
memory: 512Mi
|
||||||
|
|
||||||
|
podAnnotations: {}
|
||||||
|
podLabels: {}
|
||||||
|
|
||||||
|
podSecurityContext:
|
||||||
|
fsGroup: 10001
|
||||||
|
fsGroupChangePolicy: OnRootMismatch
|
||||||
|
|
||||||
|
securityContext:
|
||||||
|
allowPrivilegeEscalation: false
|
||||||
|
capabilities:
|
||||||
|
drop:
|
||||||
|
- ALL
|
||||||
|
readOnlyRootFilesystem: true
|
||||||
|
runAsNonRoot: true
|
||||||
|
runAsUser: 10001
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
type: Recreate
|
||||||
|
|
||||||
|
persistence:
|
||||||
|
enabled: true
|
||||||
|
size: 2Gi
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
storageClass: ""
|
||||||
|
|
||||||
|
nodeSelector: {}
|
||||||
|
tolerations: []
|
||||||
|
affinity: {}
|
||||||
51
deploy/nginx/README.md
Normal file
51
deploy/nginx/README.md
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
# Dedicated-server reverse proxy
|
||||||
|
|
||||||
|
Production exposes the CMS application through Kubernetes NodePort `31373`.
|
||||||
|
|
||||||
|
## DNS
|
||||||
|
|
||||||
|
In one.com, create an `A` record for `cms.urpi.be` pointing to the dedicated
|
||||||
|
server's public IPv4 address. Add an `AAAA` record only when the server and its
|
||||||
|
firewall are configured for public IPv6. Wait for:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dig +short cms.urpi.be
|
||||||
|
```
|
||||||
|
|
||||||
|
to return the dedicated server address before requesting a certificate.
|
||||||
|
|
||||||
|
## Nginx and Certbot
|
||||||
|
|
||||||
|
The included virtual host assumes Nginx runs on the Kubernetes node and can
|
||||||
|
reach the NodePort at `127.0.0.1:31373`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo install -m 0644 deploy/nginx/cms.urpi.be.conf \
|
||||||
|
/etc/nginx/sites-available/cms.urpi.be.conf
|
||||||
|
sudo ln -s /etc/nginx/sites-available/cms.urpi.be.conf \
|
||||||
|
/etc/nginx/sites-enabled/cms.urpi.be.conf
|
||||||
|
sudo nginx -t
|
||||||
|
sudo systemctl reload nginx
|
||||||
|
sudo certbot --nginx -d cms.urpi.be
|
||||||
|
sudo certbot renew --dry-run
|
||||||
|
```
|
||||||
|
|
||||||
|
Certbot adds the HTTPS listener, certificate paths, and HTTP-to-HTTPS redirect
|
||||||
|
to this virtual host. Allow inbound TCP ports `80` and `443`. Do not expose
|
||||||
|
NodePort `31373` to the public internet. If Nginx is not on a Kubernetes node, replace
|
||||||
|
`127.0.0.1` in `proxy_pass` with a private cluster-node address and allow
|
||||||
|
`31373` only from the Nginx server.
|
||||||
|
|
||||||
|
## Deploy and verify
|
||||||
|
|
||||||
|
Deploy the production profile:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make helm-deploy-production
|
||||||
|
kubectl -n cms-production get pods
|
||||||
|
kubectl -n cms-production get service cms
|
||||||
|
curl --fail http://127.0.0.1:31373/health
|
||||||
|
curl --fail https://cms.urpi.be/health
|
||||||
|
```
|
||||||
|
|
||||||
|
The `cms` service should show `80:31373/TCP`.
|
||||||
21
deploy/nginx/cms.urpi.be.conf
Normal file
21
deploy/nginx/cms.urpi.be.conf
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
listen [::]:80;
|
||||||
|
server_name cms.urpi.be;
|
||||||
|
|
||||||
|
client_max_body_size 10m;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://127.0.0.1:31373;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_set_header Connection "";
|
||||||
|
proxy_buffering off;
|
||||||
|
proxy_request_buffering off;
|
||||||
|
proxy_read_timeout 3600s;
|
||||||
|
proxy_send_timeout 3600s;
|
||||||
|
}
|
||||||
|
}
|
||||||
188
deploy/rancher/local-path.yaml
Normal file
188
deploy/rancher/local-path.yaml
Normal file
|
|
@ -0,0 +1,188 @@
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: local-path-storage
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: local-path-provisioner-service-account
|
||||||
|
namespace: local-path-storage
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: Role
|
||||||
|
metadata:
|
||||||
|
name: local-path-provisioner-role
|
||||||
|
namespace: local-path-storage
|
||||||
|
rules:
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["pods"]
|
||||||
|
verbs: ["get", "list", "watch", "create", "patch", "update", "delete"]
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
name: local-path-provisioner-role
|
||||||
|
rules:
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["nodes", "persistentvolumeclaims", "configmaps", "pods", "pods/log"]
|
||||||
|
verbs: ["get", "list", "watch"]
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["persistentvolumes"]
|
||||||
|
verbs: ["get", "list", "watch", "create", "patch", "update", "delete"]
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["events"]
|
||||||
|
verbs: ["create", "patch"]
|
||||||
|
- apiGroups: ["storage.k8s.io"]
|
||||||
|
resources: ["storageclasses"]
|
||||||
|
verbs: ["get", "list", "watch"]
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: RoleBinding
|
||||||
|
metadata:
|
||||||
|
name: local-path-provisioner-bind
|
||||||
|
namespace: local-path-storage
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: Role
|
||||||
|
name: local-path-provisioner-role
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: local-path-provisioner-service-account
|
||||||
|
namespace: local-path-storage
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRoleBinding
|
||||||
|
metadata:
|
||||||
|
name: local-path-provisioner-bind
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: ClusterRole
|
||||||
|
name: local-path-provisioner-role
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: local-path-provisioner-service-account
|
||||||
|
namespace: local-path-storage
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: local-path-provisioner
|
||||||
|
namespace: local-path-storage
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: local-path-provisioner
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: local-path-provisioner
|
||||||
|
spec:
|
||||||
|
serviceAccountName: local-path-provisioner-service-account
|
||||||
|
containers:
|
||||||
|
- name: local-path-provisioner
|
||||||
|
image: docker.io/rancher/local-path-provisioner:v0.0.37
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
command:
|
||||||
|
- local-path-provisioner
|
||||||
|
- --debug
|
||||||
|
- start
|
||||||
|
- --config
|
||||||
|
- /etc/config/config.json
|
||||||
|
ports:
|
||||||
|
- name: health
|
||||||
|
containerPort: 8080
|
||||||
|
protocol: TCP
|
||||||
|
startupProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /health
|
||||||
|
port: health
|
||||||
|
initialDelaySeconds: 5
|
||||||
|
periodSeconds: 5
|
||||||
|
failureThreshold: 12
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /health
|
||||||
|
port: health
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 10
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /ready
|
||||||
|
port: health
|
||||||
|
initialDelaySeconds: 5
|
||||||
|
periodSeconds: 5
|
||||||
|
volumeMounts:
|
||||||
|
- name: config-volume
|
||||||
|
mountPath: /etc/config/
|
||||||
|
env:
|
||||||
|
- name: POD_NAMESPACE
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: metadata.namespace
|
||||||
|
- name: CONFIG_MOUNT_PATH
|
||||||
|
value: /etc/config/
|
||||||
|
- name: HEALTH_PORT
|
||||||
|
value: "8080"
|
||||||
|
volumes:
|
||||||
|
- name: config-volume
|
||||||
|
configMap:
|
||||||
|
name: local-path-config
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: storage.k8s.io/v1
|
||||||
|
kind: StorageClass
|
||||||
|
metadata:
|
||||||
|
name: local-path
|
||||||
|
annotations:
|
||||||
|
storageclass.kubernetes.io/is-default-class: "true"
|
||||||
|
provisioner: rancher.io/local-path
|
||||||
|
volumeBindingMode: WaitForFirstConsumer
|
||||||
|
reclaimPolicy: Retain
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: ConfigMap
|
||||||
|
apiVersion: v1
|
||||||
|
metadata:
|
||||||
|
name: local-path-config
|
||||||
|
namespace: local-path-storage
|
||||||
|
data:
|
||||||
|
config.json: |-
|
||||||
|
{
|
||||||
|
"nodePathMap":[
|
||||||
|
{
|
||||||
|
"node":"DEFAULT_PATH_FOR_NON_LISTED_NODES",
|
||||||
|
"paths":["/var/lib/rancher/k3s/storage"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
setup: |-
|
||||||
|
#!/bin/sh
|
||||||
|
set -eu
|
||||||
|
mkdir -m 0777 -p "$VOL_DIR"
|
||||||
|
teardown: |-
|
||||||
|
#!/bin/sh
|
||||||
|
set -eu
|
||||||
|
rm -rf "$VOL_DIR"
|
||||||
|
helperPod.yaml: |-
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: helper-pod
|
||||||
|
spec:
|
||||||
|
priorityClassName: system-node-critical
|
||||||
|
tolerations:
|
||||||
|
- key: node.kubernetes.io/disk-pressure
|
||||||
|
operator: Exists
|
||||||
|
effect: NoSchedule
|
||||||
|
containers:
|
||||||
|
- name: helper-pod
|
||||||
|
image: docker.io/library/busybox
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
9
deploy/rancher/registries.yaml
Normal file
9
deploy/rancher/registries.yaml
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
mirrors:
|
||||||
|
"batty1039.startdedicated.net:5000":
|
||||||
|
endpoint:
|
||||||
|
- "http://batty1039.startdedicated.net:5000"
|
||||||
|
|
||||||
|
configs:
|
||||||
|
"batty1039.startdedicated.net:5000":
|
||||||
|
tls:
|
||||||
|
insecure_skip_verify: true
|
||||||
|
|
@ -54,7 +54,7 @@ export async function requestSession(): Promise<SessionResponse | null> {
|
||||||
}
|
}
|
||||||
|
|
||||||
function demoAuthHeaders(): Record<string, string> {
|
function demoAuthHeaders(): Record<string, string> {
|
||||||
const token = window.sessionStorage.getItem('cms-demo-token')
|
const token = window.sessionStorage.getItem('cms-token')
|
||||||
return token ? {Authorization: `Bearer ${token}`} : {}
|
return token ? {Authorization: `Bearer ${token}`} : {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -47,13 +47,13 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
||||||
const signIn = useCallback(() => { signInWithGoogle() }, [])
|
const signIn = useCallback(() => { signInWithGoogle() }, [])
|
||||||
const sudo = useCallback(async (username: string, password: string) => {
|
const sudo = useCallback(async (username: string, password: string) => {
|
||||||
const result = await sudoLogin(username, password)
|
const result = await sudoLogin(username, password)
|
||||||
window.sessionStorage.setItem('cms-demo-token', result.token)
|
window.sessionStorage.setItem('cms-token', result.token)
|
||||||
setAuthenticated(true)
|
setAuthenticated(true)
|
||||||
setCanEdit(true)
|
setCanEdit(true)
|
||||||
setUsername(result.user.username)
|
setUsername(result.user.username)
|
||||||
}, [])
|
}, [])
|
||||||
const signOut = useCallback(() => {
|
const signOut = useCallback(() => {
|
||||||
window.sessionStorage.removeItem('cms-demo-token')
|
window.sessionStorage.removeItem('cms-token')
|
||||||
setAuthenticated(false)
|
setAuthenticated(false)
|
||||||
setCanEdit(false)
|
setCanEdit(false)
|
||||||
setUsername(undefined)
|
setUsername(undefined)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue