From f18dc7531bac2c4d4ac59cdb845327c78b01157b Mon Sep 17 00:00:00 2001 From: shahargl Date: Wed, 11 Dec 2024 21:17:36 +0200 Subject: [PATCH] feat: traefik --- .github/workflows/test-traefik-ingress.yml | 107 ++++++++++++ charts/keep/templates/backend.yaml | 2 +- .../templates/ingress-traefik-middleware.yaml | 14 ++ charts/keep/templates/ingress-traefik.yaml | 96 +++++++++++ local_test_haproxy.sh | 2 +- local_test_nginx.sh | 2 +- local_test_traefik.sh | 162 ++++++++++++++++++ 7 files changed, 382 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/test-traefik-ingress.yml create mode 100644 charts/keep/templates/ingress-traefik-middleware.yaml create mode 100644 charts/keep/templates/ingress-traefik.yaml create mode 100644 local_test_traefik.sh diff --git a/.github/workflows/test-traefik-ingress.yml b/.github/workflows/test-traefik-ingress.yml new file mode 100644 index 0000000..9079559 --- /dev/null +++ b/.github/workflows/test-traefik-ingress.yml @@ -0,0 +1,107 @@ +name: Test Traefik Ingress + +on: + pull_request: + paths: + - 'charts/**' + - '.github/workflows/test-traefik-ingress.yml' + push: + branches: + - main + paths: + - 'charts/**' + - '.github/workflows/test-traefik-ingress.yml' + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Set up Helm + uses: azure/setup-helm@v3 + with: + version: v3.12.1 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.9' + check-latest: true + + - name: Set up chart-testing + uses: helm/chart-testing-action@v2.6.1 + + - name: Run chart-testing (list-changed) + id: list-changed + run: | + changed=$(ct list-changed --config ct.yaml) + if [[ -n "$changed" ]]; then + echo "changed=true" >> $GITHUB_OUTPUT + fi + + - name: Create Kind cluster with port mappings + if: steps.list-changed.outputs.changed == 'true' + uses: helm/kind-action@v1.8.0 + with: + config: | + kind: Cluster + apiVersion: kind.x-k8s.io/v1alpha4 + nodes: + - role: control-plane + extraPortMappings: + - containerPort: 30080 + hostPort: 30080 + protocol: TCP + - containerPort: 443 + hostPort: 443 + protocol: TCP + + - name: Install Traefik + if: steps.list-changed.outputs.changed == 'true' + run: | + helm repo add traefik https://traefik.github.io/charts + helm repo update + helm install traefik traefik/traefik \ + --set installCRDs=true \ + --set ports.web.port=8000 \ + --set ports.web.exposedPort=80 \ + --set ports.web.nodePort=30080 \ + --set service.type=NodePort \ + --set ingressClass.enabled=true \ + --set ingressClass.isDefaultClass=true + + - name: Wait for Traefik + if: steps.list-changed.outputs.changed == 'true' + run: | + kubectl wait --namespace default \ + --for=condition=ready pod \ + --selector=app.kubernetes.io/name=traefik \ + --timeout=90s + + - name: Wait for CRDs + if: steps.list-changed.outputs.changed == 'true' + run: | + kubectl wait --for condition=established --timeout=60s crd/middlewares.traefik.io + + - name: Run chart-testing (install) + if: steps.list-changed.outputs.changed == 'true' + run: | + ct install \ + --config ct.yaml \ + --helm-extra-set-args "--set global.ingress.className=traefik --set global.ingress.classType=traefik" + + - name: Test endpoints + if: steps.list-changed.outputs.changed == 'true' + run: | + echo "Testing frontend endpoint..." + curl -v --retry 5 --retry-delay 10 http://localhost:30080/ + + echo "Testing backend endpoint..." + curl -v --retry 5 --retry-delay 10 http://localhost:30080/v2/docs + + echo "Testing websocket endpoint..." + curl -v --retry 5 --retry-delay 10 -H "Upgrade: websocket" -H "Connection: Upgrade" http://localhost:30080/websocket/ \ No newline at end of file diff --git a/charts/keep/templates/backend.yaml b/charts/keep/templates/backend.yaml index 847925e..ebc0b75 100644 --- a/charts/keep/templates/backend.yaml +++ b/charts/keep/templates/backend.yaml @@ -102,7 +102,7 @@ spec: {{- if .Values.backend.waitForDatabase }} - name: wait-for-database image: busybox - command: ['sh', '-c', 'until nc -z keep-database 3306; do sleep 1; done;'] + command: ['sh', '-c', 'until nc -z {{ include "keep.findEnvVar" (list "DATABASE_NAME" .) | default "keep-database" }} 3306; do sleep 1; done;'] {{- end }} {{- range .Values.backend.extraInitContainers }} - name: {{ .name }} diff --git a/charts/keep/templates/ingress-traefik-middleware.yaml b/charts/keep/templates/ingress-traefik-middleware.yaml new file mode 100644 index 0000000..5681bc2 --- /dev/null +++ b/charts/keep/templates/ingress-traefik-middleware.yaml @@ -0,0 +1,14 @@ +{{- if and .Values.global.ingress.enabled (or (eq .Values.global.ingress.className "traefik") (eq .Values.global.ingress.classType "traefik")) }} +apiVersion: traefik.io/v1alpha1 +kind: Middleware +metadata: + name: strip-prefix + namespace: {{ .Release.Namespace }} + labels: + {{- include "keep.labels" . | nindent 4 }} +spec: + stripPrefix: + prefixes: + - {{ .Values.global.ingress.backendPrefix }} + - {{ .Values.global.ingress.websocketPrefix }} +{{- end }} \ No newline at end of file diff --git a/charts/keep/templates/ingress-traefik.yaml b/charts/keep/templates/ingress-traefik.yaml new file mode 100644 index 0000000..ebd6735 --- /dev/null +++ b/charts/keep/templates/ingress-traefik.yaml @@ -0,0 +1,96 @@ +{{- if and .Values.global.ingress.enabled (or (eq .Values.global.ingress.className "traefik") (eq .Values.global.ingress.classType "traefik")) }} +{{- $fullName := include "keep.fullname" . }} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ $fullName }}-ingress + labels: + {{- include "keep.labels" . | nindent 4 }} + annotations: + traefik.ingress.kubernetes.io/router.middlewares: {{ .Release.Namespace }}-strip-prefix@kubernetescrd + traefik.ingress.kubernetes.io/router.entrypoints: web + traefik.ingress.kubernetes.io/timeouts.idle: "3600s" + traefik.ingress.kubernetes.io/timeouts.connect: "3600s" + traefik.ingress.kubernetes.io/timeouts.read: "3600s" + traefik.ingress.kubernetes.io/timeouts.write: "3600s" + + # WebSocket configuration + traefik.ingress.kubernetes.io/websockets.enabled: "true" + {{- with .Values.global.ingress.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + +spec: + ingressClassName: traefik + {{- if .Values.global.ingress.tls }} + tls: + {{- range .Values.global.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- if .Values.global.ingress.hosts }} + {{- range .Values.global.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- if $.Values.websocket.enabled }} + - path: {{ $.Values.global.ingress.websocketPrefix }} + pathType: Prefix + backend: + service: + name: {{ $fullName }}-websocket + port: + number: {{ $.Values.websocket.service.port }} + {{- end }} + {{- if $.Values.backend.enabled }} + - path: {{ $.Values.global.ingress.backendPrefix }} + pathType: Prefix + backend: + service: + name: {{ $fullName }}-backend + port: + number: {{ $.Values.backend.service.port }} + {{- end }} + - path: "/" + pathType: Prefix + backend: + service: + name: {{ $fullName }}-frontend + port: + number: {{ $.Values.frontend.service.port }} + {{- end }} + {{- else }} + - http: + paths: + {{- if $.Values.websocket.enabled }} + - path: {{ $.Values.global.ingress.websocketPrefix }} + pathType: Prefix + backend: + service: + name: {{ $fullName }}-websocket + port: + number: {{ $.Values.websocket.service.port }} + {{- end }} + {{- if $.Values.backend.enabled }} + - path: {{ $.Values.global.ingress.backendPrefix }} + pathType: Prefix + backend: + service: + name: {{ $fullName }}-backend + port: + number: {{ $.Values.backend.service.port }} + {{- end }} + - path: "/" + pathType: Prefix + backend: + service: + name: {{ $fullName }}-frontend + port: + number: {{ $.Values.frontend.service.port }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/local_test_haproxy.sh b/local_test_haproxy.sh index 7d3daf7..0ba45bf 100644 --- a/local_test_haproxy.sh +++ b/local_test_haproxy.sh @@ -10,7 +10,7 @@ global: annotations: ingress.class: haproxy websocketPrefix: /websocket - backendPrefix: /v2/docs + backendPrefix: /v2 frontendPrefix: / frontend: enabled: true diff --git a/local_test_nginx.sh b/local_test_nginx.sh index 457e10c..ea4eeca 100644 --- a/local_test_nginx.sh +++ b/local_test_nginx.sh @@ -9,7 +9,7 @@ global: className: nginx classType: nginx websocketPrefix: /websocket - backendPrefix: /v2/docs + backendPrefix: /v2 frontendPrefix: / frontend: enabled: true diff --git a/local_test_traefik.sh b/local_test_traefik.sh new file mode 100644 index 0000000..f252f25 --- /dev/null +++ b/local_test_traefik.sh @@ -0,0 +1,162 @@ +#!/bin/bash + +# Create a debug folder and values file +mkdir -p debug +cat < debug/traefik-values.yaml +global: + ingress: + enabled: true + className: traefik + classType: traefik + websocketPrefix: /websocket + backendPrefix: /v2 + frontendPrefix: / +frontend: + enabled: true +backend: + enabled: true +websocket: + enabled: true +EOF + +# Create Kind cluster with ingress ports exposed +cat < debug/kind-config.yaml +kind: Cluster +apiVersion: kind.x-k8s.io/v1alpha4 +nodes: +- role: control-plane + extraPortMappings: + - containerPort: 30080 + hostPort: 30080 + protocol: TCP + - containerPort: 443 + hostPort: 443 + protocol: TCP +EOF + +# Function to check service/pod status +check_status() { + echo "๐Ÿ” Checking all resources..." + kubectl get pods -A + kubectl get svc -A + kubectl get ingress -A +} + +# Function to check if a command exists +command_exists() { + command -v "$1" >/dev/null 2>&1 +} + +# Check required tools +for tool in kind kubectl helm wscat curl; do + if ! command_exists "$tool"; then + echo "โŒ Required tool not found: $tool" + exit 1 + fi +done + +# Create cluster +echo "๐Ÿš€ Creating Kind cluster..." +kind create cluster --config debug/kind-config.yaml + +# Install Traefik +echo "๐Ÿ“ฆ Adding Traefik helm repo..." +helm repo add traefik https://traefik.github.io/charts +helm repo update + +echo "๐Ÿ”ง Installing Traefik..." +helm install traefik traefik/traefik \ + --set installCRDs=true \ + --set ports.web.port=8000 \ + --set ports.web.exposedPort=80 \ + --set ports.web.nodePort=30080 \ + --set service.type=NodePort \ + --set ingressClass.enabled=true \ + --set ingressClass.isDefaultClass=true + +# Wait for Traefik controller +echo "โณ Waiting for Traefik controller..." +kubectl wait --namespace default \ + --for=condition=ready pod \ + --selector=app.kubernetes.io/name=traefik \ + --timeout=90s + +# Create middleware for path stripping +echo "๐Ÿ”ง Creating Traefik middleware..." +echo "โณ Waiting for Traefik CRDs to be available..." +kubectl wait --for condition=established --timeout=60s crd/middlewares.traefik.io + +# Install your chart +echo "๐Ÿ“Š Installing Keep chart..." +helm install keep ./charts/keep -f debug/traefik-values.yaml + +# Wait for all Keep components to be ready +for component in frontend backend websocket database; do + echo "โณ Waiting for Keep $component to be ready..." + kubectl wait --namespace default \ + --for=condition=ready pod \ + --selector=app.kubernetes.io/instance=keep,keep-component=$component \ + --timeout=120s +done + +# Check status +check_status + +# Function to test endpoint with retry +test_endpoint() { + local url=$1 + local expected=$2 + local max_attempts=5 + local attempt=1 + + while [ $attempt -le $max_attempts ]; do + echo "Attempt $attempt of $max_attempts..." + RESP=$(curl -s "$url") + if [[ $RESP == *"$expected"* ]]; then + echo "โœ… Response contains expected content" + return 0 + fi + echo "โณ Waiting before retry..." + sleep 5 + ((attempt++)) + done + echo "โŒ Failed to get expected response after $max_attempts attempts" + return 1 +} + +# Test endpoints +echo "๐Ÿงช Testing endpoints..." + +echo -e "\nFrontend (/) - Testing redirect:" +RESP=$(curl -s -I http://localhost/) +echo "$RESP" | grep "HTTP" +LOCATION=$(echo "$RESP" | grep -i "location") +echo "$LOCATION" + +echo -e "\nBackend (/v2/docs) - Testing API docs:" +test_endpoint "http://localhost/v2/docs" "Rest API powering" + +echo -e "\nWebSocket (/websocket) - Testing connection:" +timeout 5 wscat -c "ws://localhost/websocket/app/keepappkey?protocol=7&client=js&version=8.3.0&flash=false" || true + +# Show Traefik dashboard +echo -e "\n๐Ÿ“Š Traefik Dashboard available at: http://localhost:8080/dashboard/" + +# Show recent logs +echo -e "\n๐Ÿ“œ Traefik controller logs:" +kubectl logs -l app.kubernetes.io/name=traefik --tail=50 + +# Keep script running for debugging +echo -e "\n๐Ÿ”„ Debug session active. Press Ctrl+C to cleanup." +echo "๐Ÿ“ Quick commands:" +echo " - kubectl get pods -A" +echo " - kubectl describe ingress" +echo " - kubectl logs " +echo " - kubectl port-forward svc/traefik 8080:8080" +echo " - curl -v http://localhost/" +echo " - curl -v http://localhost/v2/docs/" +echo " - curl -v -H \"Upgrade: websocket\" -H \"Connection: Upgrade\" http://localhost/websocket/" + +# Cleanup on exit +trap "kind delete cluster" EXIT +read -r -d '' _