diff --git a/.github/workflows/platform-validate.yml b/.github/workflows/platform-validate.yml index 3ea1580..db34b57 100644 --- a/.github/workflows/platform-validate.yml +++ b/.github/workflows/platform-validate.yml @@ -92,17 +92,44 @@ jobs: helm lint standardized-path/app -f platform/apps/app-b/stage/values.yaml helm lint standardized-path/app -f platform/apps/app-b/prod/values.yaml + - name: Enforce immutable tags and revisions + run: | + set +e + has_error=0 + for f in platform/apps/**/values.yaml argocd/**/*.yaml; do + [ -f "$f" ] || continue + if grep -qE 'tag:\s*"?latest"?' "$f" 2>/dev/null; then + echo "::error file=$f::Found 'latest' image tag — use an immutable version tag instead" + has_error=1 + fi + if grep -qE 'targetRevision:\s*"?HEAD"?' "$f" 2>/dev/null; then + echo "::error file=$f::Found 'HEAD' revision — pin to an explicit branch or tag" + has_error=1 + fi + done + # Also check per-environment values files explicitly + for f in platform/apps/dev/values.yaml platform/apps/stage/values.yaml platform/apps/prod/values.yaml platform/apps/app-b/dev/values.yaml platform/apps/app-b/stage/values.yaml platform/apps/app-b/prod/values.yaml; do + [ -f "$f" ] || continue + tag_val=$(grep -E '^\s+tag:' "$f" | sed 's/.*tag:\s*"\{0,1\}\([^"]*\)"\{0,1\}/\1/' | xargs) + if [ "$tag_val" = "latest" ]; then + echo "::error file=$f::Found 'latest' image tag — use an immutable version tag instead" + has_error=1 + fi + done + exit $has_error + - name: Helm unit tests run: helm unittest standardized-path/app - name: Render manifests run: | - helm template simple-app-dev standardized-path/app -f platform/apps/dev/values.yaml > /tmp/simple-app-dev.yaml - helm template simple-app-stage standardized-path/app -f platform/apps/stage/values.yaml > /tmp/simple-app-stage.yaml - helm template simple-app-prod standardized-path/app -f platform/apps/prod/values.yaml > /tmp/simple-app-prod.yaml - helm template app-b-dev standardized-path/app -f platform/apps/app-b/dev/values.yaml > /tmp/app-b-dev.yaml - helm template app-b-stage standardized-path/app -f platform/apps/app-b/stage/values.yaml > /tmp/app-b-stage.yaml - helm template app-b-prod standardized-path/app -f platform/apps/app-b/prod/values.yaml > /tmp/app-b-prod.yaml + IMG="--set image.repository=${{ vars.IMAGE_REPO || 'repo' }}" + helm template simple-app-dev standardized-path/app -f platform/apps/dev/values.yaml $IMG > /tmp/simple-app-dev.yaml + helm template simple-app-stage standardized-path/app -f platform/apps/stage/values.yaml $IMG > /tmp/simple-app-stage.yaml + helm template simple-app-prod standardized-path/app -f platform/apps/prod/values.yaml $IMG > /tmp/simple-app-prod.yaml + helm template app-b-dev standardized-path/app -f platform/apps/app-b/dev/values.yaml $IMG > /tmp/app-b-dev.yaml + helm template app-b-stage standardized-path/app -f platform/apps/app-b/stage/values.yaml $IMG > /tmp/app-b-stage.yaml + helm template app-b-prod standardized-path/app -f platform/apps/app-b/prod/values.yaml $IMG > /tmp/app-b-prod.yaml - name: Validate manifests with kubeconform run: | @@ -128,12 +155,13 @@ jobs: - name: Validate EKS manifests with OPA policies run: | - helm template simple-app-dev standardized-path/app -f platform/apps/dev/values.yaml | tr -d '\r' | conftest test -p policy/eks/ --no-color - - helm template simple-app-stage standardized-path/app -f platform/apps/stage/values.yaml | tr -d '\r' | conftest test -p policy/eks/ --no-color - - helm template simple-app-prod standardized-path/app -f platform/apps/prod/values.yaml | tr -d '\r' | conftest test -p policy/eks/ --no-color - - helm template app-b-dev standardized-path/app -f platform/apps/app-b/dev/values.yaml | tr -d '\r' | conftest test -p policy/eks/ --no-color - - helm template app-b-stage standardized-path/app -f platform/apps/app-b/stage/values.yaml | tr -d '\r' | conftest test -p policy/eks/ --no-color - - helm template app-b-prod standardized-path/app -f platform/apps/app-b/prod/values.yaml | tr -d '\r' | conftest test -p policy/eks/ --no-color - + IMG="--set image.repository=${{ vars.IMAGE_REPO || 'repo' }}" + helm template simple-app-dev standardized-path/app -f platform/apps/dev/values.yaml $IMG | tr -d '\r' | conftest test -p policy/eks/ --no-color - + helm template simple-app-stage standardized-path/app -f platform/apps/stage/values.yaml $IMG | tr -d '\r' | conftest test -p policy/eks/ --no-color - + helm template simple-app-prod standardized-path/app -f platform/apps/prod/values.yaml $IMG | tr -d '\r' | conftest test -p policy/eks/ --no-color - + helm template app-b-dev standardized-path/app -f platform/apps/app-b/dev/values.yaml $IMG | tr -d '\r' | conftest test -p policy/eks/ --no-color - + helm template app-b-stage standardized-path/app -f platform/apps/app-b/stage/values.yaml $IMG | tr -d '\r' | conftest test -p policy/eks/ --no-color - + helm template app-b-prod standardized-path/app -f platform/apps/app-b/prod/values.yaml $IMG | tr -d '\r' | conftest test -p policy/eks/ --no-color - openshift-validate: runs-on: ubuntu-latest @@ -148,6 +176,7 @@ jobs: - name: Render with OpenShift flags run: | helm template test standardized-path/app \ + --set image.repository=${{ vars.IMAGE_REPO || 'repo' }} \ --set openshift.enabled=true \ --set openshift.route.enabled=true \ --set ingress.enabled=false \ @@ -174,6 +203,7 @@ jobs: - name: Validate OpenShift manifests with OPA policies run: | helm template test standardized-path/app \ + --set image.repository=${{ vars.IMAGE_REPO || 'repo' }} \ --set openshift.enabled=true \ --set openshift.route.enabled=true \ --set ingress.enabled=false \ diff --git a/.github/workflows/terraform-scan.yml b/.github/workflows/terraform-scan.yml index ab8d088..8fa2a6c 100644 --- a/.github/workflows/terraform-scan.yml +++ b/.github/workflows/terraform-scan.yml @@ -26,9 +26,12 @@ jobs: - name: Install Terrascan run: | - TERRASCAN_VERSION=$(curl -s https://api.github.com/repos/tenable/terrascan/releases/latest | jq -r '.tag_name') - TERRASCAN_VERSION=${TERRASCAN_VERSION#v} - wget https://github.com/tenable/terrascan/releases/download/v${TERRASCAN_VERSION}/terrascan_${TERRASCAN_VERSION}_Linux_x86_64.tar.gz -O terrascan.tar.gz + TERRASCAN_VERSION=$(curl -s https://api.github.com/repos/tenable/terrascan/releases/latest | jq -r '.tag_name' 2>/dev/null | sed 's/^v//' || true) + if [ -z "$TERRASCAN_VERSION" ] || [ "$TERRASCAN_VERSION" = "null" ]; then + TERRASCAN_VERSION="1.18.0" + echo "API rate limited; falling back to Terrascan v${TERRASCAN_VERSION}" + fi + wget "https://github.com/tenable/terrascan/releases/download/v${TERRASCAN_VERSION}/terrascan_${TERRASCAN_VERSION}_Linux_x86_64.tar.gz" -O terrascan.tar.gz tar -xf terrascan.tar.gz terrascan rm terrascan.tar.gz sudo install terrascan /usr/local/bin diff --git a/Makefile b/Makefile index 80a40c2..be8a36a 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ help: @echo ' kind-up Create Kind cluster' @echo ' kind-down Delete Kind cluster' @echo ' argocd-install Install Argo CD and apply AppProjects' - @echo ' deploy Apply platform bootstrap + add-ons + demo app' + @echo ' deploy Apply platform bootstrap (Argo CD reconciles add-ons and apps via GitOps)' @echo ' test Run Helm unit tests' @echo ' validate Run CI checks locally (Helm lint + test + conftest)' @echo ' clean kind-down + remove tmp files' @@ -43,13 +43,8 @@ argocd-install: kind-up .PHONY: deploy deploy: - @echo "=== Applying platform bootstrap ===" - kubectl apply -f platform/bootstrap/namespaces.yaml - kubectl apply -f platform/bootstrap/resource-quota.yaml - kubectl apply -f platform/bootstrap/limit-range.yaml - kubectl apply -f platform/bootstrap/network-policy.yaml - kubectl apply -f platform/bootstrap/rbac-readonly.yaml - kubectl apply -f platform/bootstrap/cluster-secret-store.yaml + @echo "=== Applying platform bootstrap (foundation for Argo CD) ===" + kubectl apply -f platform/bootstrap/ @echo "=== Creating platform secrets namespace ===" kubectl create namespace platform-secrets --dry-run=client -o yaml | kubectl apply -f - @@ -59,27 +54,25 @@ deploy: --from-literal=db-password=$${DEV_DB_PASSWORD:-changeme} \ -n platform-secrets --dry-run=client -o yaml | kubectl apply -f - - @echo "=== Applying platform add-ons ===" - kubectl apply -f platform/addons/metrics-server.yaml - kubectl apply -f platform/addons/nginx-ingress.yaml - kubectl apply -f platform/addons/external-secrets.yaml - kubectl apply -f platform/addons/kube-prometheus-stack.yaml - kubectl apply -f platform/addons/grafana-dashboards-platform.yaml + @echo "=== Argo CD manages add-ons and apps via GitOps (app-of-apps) ===" + @echo " Root app: kubectl get application/root -n argocd" + @echo " Child apps: kubectl get applications -n argocd" + @echo "" - @echo "=== Deploying simple-app ===" - helm template simple-app-dev standardized-path/app \ - -f platform/apps/dev/values.yaml \ - --set image.repository=$(IMAGE_REPO) \ - | kubectl apply -f - 2>&1 | grep -v 'unchanged' || true + @echo "=== Waiting for Argo CD to reconcile add-ons ===" + @for i in 1 2 3 4 5; do \ + ready=$$(kubectl -n argocd get applications 2>/dev/null | tail -n+2 | wc -l); \ + [ "$$ready" -gt 0 ] && break; \ + sleep 3; \ + done + kubectl get applications -n argocd 2>/dev/null || echo "(Argo CD syncing add-ons and apps)" - @echo "=== Deploying app-b ===" - helm template app-b-dev standardized-path/app \ - -f platform/apps/app-b/dev/values.yaml \ - --set image.repository=$(IMAGE_REPO) \ - | kubectl apply -f - 2>&1 | grep -v 'unchanged' || true - - @echo "=== App status ===" - kubectl get pods -n dev --show-labels + @echo "" + @echo "=== Pod status ===" + @for ns in dev stage prod; do \ + echo "--- $$ns ---"; \ + kubectl get pods -n $$ns --show-labels 2>/dev/null || echo "(no pods yet - Argo CD is syncing)"; \ + done .PHONY: test test: @@ -88,6 +81,22 @@ test: .PHONY: validate validate: test + @echo "=== Immutable tags and revisions ===" + @for f in platform/apps/**/values.yaml; do \ + [ -f "$$f" ] || continue; \ + tag_val=$$(grep -E '^\s+tag:' "$$f" | sed 's/.*tag:\s*"\{0,1\}\([^"]*\)"\{0,1\}/\1/' | xargs); \ + if [ "$$tag_val" = "latest" ]; then \ + echo " FAIL: $$f uses 'latest' tag"; exit 1; \ + fi; \ + done + @for f in argocd/**/*.yaml; do \ + [ -f "$$f" ] || continue; \ + if grep -qE 'targetRevision:\s*"?HEAD"?' "$$f" 2>/dev/null; then \ + echo " FAIL: $$f uses 'HEAD' revision"; exit 1; \ + fi; \ + done + @echo " PASS" + @echo "=== Helm lint ===" helm lint standardized-path/app -f platform/apps/dev/values.yaml helm lint standardized-path/app -f platform/apps/stage/values.yaml @@ -95,10 +104,12 @@ validate: test @echo "=== ServiceMonitor renders ===" helm template simple-app-dev standardized-path/app -f platform/apps/dev/values.yaml \ + --set image.repository=$(IMAGE_REPO) \ | grep -q "kind: ServiceMonitor" && echo " PASS: ServiceMonitor present" @echo "=== OpenShift path renders correctly ===" helm template test standardized-path/app \ + --set image.repository=$(IMAGE_REPO) \ --set openshift.enabled=true \ --set openshift.route.enabled=true \ --set ingress.enabled=false \ @@ -106,10 +117,12 @@ validate: test @echo "=== OPA policy: EKS manifests ===" helm template simple-app-dev standardized-path/app -f platform/apps/dev/values.yaml \ + --set image.repository=$(IMAGE_REPO) \ | tr -d '\r' | conftest test -p policy/eks/ --no-color - @echo "=== OPA policy: OpenShift manifests ===" helm template test standardized-path/app \ + --set image.repository=$(IMAGE_REPO) \ --set openshift.enabled=true \ --set openshift.route.enabled=true \ --set ingress.enabled=false \ diff --git a/README.md b/README.md index 92c4607..be15da1 100644 --- a/README.md +++ b/README.md @@ -140,7 +140,7 @@ Every app deployed through the platform contract automatically gets a ServiceMon ## What I'd do next - Add OPA/Gatekeeper policies for platform-level validation beyond what SCC provides -- Replace `legacy/` assets with a clean `examples/` directory +- Replaced `legacy/` assets with a clean `archive/` directory --- diff --git a/archive/legacy/ansible/ansible b/archive/legacy/ansible/ansible new file mode 100644 index 0000000..e69de29 diff --git a/archive/legacy/ansible/ansible-inventory.tf b/archive/legacy/ansible/ansible-inventory.tf new file mode 100644 index 0000000..f0f2a77 --- /dev/null +++ b/archive/legacy/ansible/ansible-inventory.tf @@ -0,0 +1,29 @@ +resource "local_file" "ansible_inventory" { + count = var.enable_ansible ? 1 : 0 + + content = <<-EOT +[eks_nodes] +%{for ip in module.eks.worker_node_ips} +${ip} ansible_user=ec2-user +%{endfor} + +[eks_nodes:vars] +ansible_ssh_common_args='-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null' + EOT + + filename = "${path.module}/inventory" +} + +resource "null_resource" "ansible_playbook_runner" { + count = var.enable_ansible ? 1 : 0 + + depends_on = [local_file.ansible_inventory, module.eks] + + provisioner "local-exec" { + command = "ansible-playbook -i inventory ansible-playbook.yml" + } + + triggers = { + always_run = "${timestamp()}" + } +} \ No newline at end of file diff --git a/archive/legacy/ansible/ansible-playbook.yml b/archive/legacy/ansible/ansible-playbook.yml new file mode 100644 index 0000000..b73a171 --- /dev/null +++ b/archive/legacy/ansible/ansible-playbook.yml @@ -0,0 +1,65 @@ +--- +- name: Configure EKS Worker Nodes + hosts: eks_nodes + become: yes + + tasks: + - name: Update all packages + yum: + name: '*' + state: latest + update_only: yes + + - name: Install required packages + yum: + name: + - docker + - git + - jq + - python3 + - python3-pip + state: present + + - name: Start and enable Docker service + systemd: + name: docker + state: started + enabled: yes + + - name: Install AWS CLI + pip: + name: awscli + executable: pip3 + state: present + + - name: Install kubectl + get_url: + url: https://storage.googleapis.com/kubernetes-release/release/v1.23.6/bin/linux/amd64/kubectl + dest: /usr/local/bin/kubectl + mode: '0755' + + - name: Create .kube directory + file: + path: /home/ec2-user/.kube + state: directory + owner: ec2-user + group: ec2-user + mode: '0755' + + - name: Configure kubelet service + copy: + content: | + [Service] + Environment="KUBELET_EXTRA_ARGS=--node-labels=node.kubernetes.io/worker=true" + dest: /etc/systemd/system/kubelet.service.d/20-worker-labels.conf + owner: root + group: root + mode: '0644' + notify: Restart kubelet + + handlers: + - name: Restart kubelet + systemd: + name: kubelet + state: restarted + daemon_reload: yes \ No newline at end of file diff --git a/archive/legacy/ansible/ansible.cfg b/archive/legacy/ansible/ansible.cfg new file mode 100644 index 0000000..115b640 --- /dev/null +++ b/archive/legacy/ansible/ansible.cfg @@ -0,0 +1,6 @@ +[defaults] +inventory = ./inventory +host_key_checking = False +remote_user = ec2-user +private_key_file = ~/.ssh/id_rsa +roles_path = ./roles \ No newline at end of file diff --git a/archive/legacy/ansible/configure-nodes.yml b/archive/legacy/ansible/configure-nodes.yml new file mode 100644 index 0000000..b73a171 --- /dev/null +++ b/archive/legacy/ansible/configure-nodes.yml @@ -0,0 +1,65 @@ +--- +- name: Configure EKS Worker Nodes + hosts: eks_nodes + become: yes + + tasks: + - name: Update all packages + yum: + name: '*' + state: latest + update_only: yes + + - name: Install required packages + yum: + name: + - docker + - git + - jq + - python3 + - python3-pip + state: present + + - name: Start and enable Docker service + systemd: + name: docker + state: started + enabled: yes + + - name: Install AWS CLI + pip: + name: awscli + executable: pip3 + state: present + + - name: Install kubectl + get_url: + url: https://storage.googleapis.com/kubernetes-release/release/v1.23.6/bin/linux/amd64/kubectl + dest: /usr/local/bin/kubectl + mode: '0755' + + - name: Create .kube directory + file: + path: /home/ec2-user/.kube + state: directory + owner: ec2-user + group: ec2-user + mode: '0755' + + - name: Configure kubelet service + copy: + content: | + [Service] + Environment="KUBELET_EXTRA_ARGS=--node-labels=node.kubernetes.io/worker=true" + dest: /etc/systemd/system/kubelet.service.d/20-worker-labels.conf + owner: root + group: root + mode: '0644' + notify: Restart kubelet + + handlers: + - name: Restart kubelet + systemd: + name: kubelet + state: restarted + daemon_reload: yes \ No newline at end of file diff --git a/archive/legacy/ansible/inventory b/archive/legacy/ansible/inventory new file mode 100644 index 0000000..e7e693e --- /dev/null +++ b/archive/legacy/ansible/inventory @@ -0,0 +1,5 @@ +[eks_nodes] +# This will be dynamically populated by Terraform + +[eks_nodes:vars] +ansible_ssh_common_args='-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null' \ No newline at end of file diff --git a/archive/legacy/ansible/roles b/archive/legacy/ansible/roles new file mode 100644 index 0000000..e69de29 diff --git a/archive/legacy/ansible/templates b/archive/legacy/ansible/templates new file mode 100644 index 0000000..c11e4f0 --- /dev/null +++ b/archive/legacy/ansible/templates @@ -0,0 +1 @@ +# This directory contains templates for Terraform \ No newline at end of file diff --git a/archive/legacy/ansible/terraform-ansible-integration.tf b/archive/legacy/ansible/terraform-ansible-integration.tf new file mode 100644 index 0000000..2658c93 --- /dev/null +++ b/archive/legacy/ansible/terraform-ansible-integration.tf @@ -0,0 +1,25 @@ +resource "local_file" "ansible_inventory_template" { + content = <<-EOT +[eks_nodes] +%{for ip in module.eks.worker_node_ips} +${ip} ansible_user=ec2-user +%{endfor} + +[eks_nodes:vars] +ansible_ssh_common_args='-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null' + EOT + + filename = "${path.module}/inventory" +} + +resource "null_resource" "ansible_executor" { + depends_on = [local_file.ansible_inventory_template, module.eks] + + provisioner "local-exec" { + command = "ansible-playbook -i inventory ansible-playbook.yml" + } + + triggers = { + always_run = "${timestamp()}" + } +} \ No newline at end of file diff --git a/archive/legacy/helm/simple-app/Chart.yaml b/archive/legacy/helm/simple-app/Chart.yaml new file mode 100644 index 0000000..869d788 --- /dev/null +++ b/archive/legacy/helm/simple-app/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: simple-app +description: A Helm chart for the simple application +type: application +version: 0.1.0 +appVersion: "1.0.0" diff --git a/archive/legacy/helm/simple-app/README.md b/archive/legacy/helm/simple-app/README.md new file mode 100644 index 0000000..fbd1168 --- /dev/null +++ b/archive/legacy/helm/simple-app/README.md @@ -0,0 +1,11 @@ +# Deprecated Chart + +This chart is a deprecated transitional asset. + +The single supported application deployment path for this repository is the Helm chart in: + +- `../../standardized-path/app/` + +Do not add new features or environment configuration to `helm/simple-app/`. + +Argo CD does not target this chart. diff --git a/archive/legacy/helm/simple-app/templates/_helpers.tpl b/archive/legacy/helm/simple-app/templates/_helpers.tpl new file mode 100644 index 0000000..c417313 --- /dev/null +++ b/archive/legacy/helm/simple-app/templates/_helpers.tpl @@ -0,0 +1,51 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "simple-app.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "simple-app.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 }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "simple-app.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "simple-app.labels" -}} +helm.sh/chart: {{ include "simple-app.chart" . }} +{{ include "simple-app.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "simple-app.selectorLabels" -}} +app.kubernetes.io/name: {{ include "simple-app.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} diff --git a/archive/legacy/helm/simple-app/templates/deployment.yaml b/archive/legacy/helm/simple-app/templates/deployment.yaml new file mode 100644 index 0000000..6a43eea --- /dev/null +++ b/archive/legacy/helm/simple-app/templates/deployment.yaml @@ -0,0 +1,24 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "simple-app.fullname" . }} + labels: + {{- include "simple-app.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + {{- include "simple-app.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "simple-app.selectorLabels" . | nindent 8 }} + spec: + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: {{ .Values.service.targetPort }} + protocol: TCP diff --git a/archive/legacy/helm/simple-app/templates/ingress.yaml b/archive/legacy/helm/simple-app/templates/ingress.yaml new file mode 100644 index 0000000..ab628b1 --- /dev/null +++ b/archive/legacy/helm/simple-app/templates/ingress.yaml @@ -0,0 +1,31 @@ +{{- if .Values.ingress.enabled -}} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "simple-app.fullname" . }} + labels: + {{- include "simple-app.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if .Values.ingress.className }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + pathType: {{ .pathType }} + backend: + service: + name: {{ include "simple-app.fullname" $ }} + port: + number: {{ $.Values.service.port }} + {{- end }} + {{- end }} +{{- end }} diff --git a/archive/legacy/helm/simple-app/templates/service.yaml b/archive/legacy/helm/simple-app/templates/service.yaml new file mode 100644 index 0000000..84a2c14 --- /dev/null +++ b/archive/legacy/helm/simple-app/templates/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "simple-app.fullname" . }} + labels: + {{- include "simple-app.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: {{ .Values.service.targetPort }} + protocol: TCP + name: http + selector: + {{- include "simple-app.selectorLabels" . | nindent 4 }} diff --git a/archive/legacy/helm/simple-app/values.yaml b/archive/legacy/helm/simple-app/values.yaml new file mode 100644 index 0000000..e380e9d --- /dev/null +++ b/archive/legacy/helm/simple-app/values.yaml @@ -0,0 +1,43 @@ +replicaCount: 3 + +image: + repository: REPLACE_ME_WITH_ECR_URI + pullPolicy: Always + tag: "latest" + +service: + type: LoadBalancer + port: 80 + targetPort: 8080 + +ingress: + enabled: true + className: "" + annotations: {} + hosts: + - host: my-app.simpleapp.com + paths: + - path: / + pathType: Prefix + tls: [] + +resources: {} + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 10 + targetCPUUtilizationPercentage: 80 + # targetMemoryUtilizationPercentage: 80 + +nodeSelector: {} + +tolerations: [] + +affinity: {} diff --git a/archive/legacy/kubernetes/README.md b/archive/legacy/kubernetes/README.md new file mode 100644 index 0000000..1b8c734 --- /dev/null +++ b/archive/legacy/kubernetes/README.md @@ -0,0 +1,14 @@ +# Transitional Kubernetes Manifests + +This directory is no longer the supported application deployment source. + +Current intent: + +- `argocd-application.yaml` remains as the transitional Argo CD entry point +- raw application manifests in this directory are deprecated + +The single supported application deployment source for the app is: + +- `../../standardized-path/app/` + +Do not treat the raw application manifests in this directory as the steady-state source of truth for application delivery. diff --git a/archive/legacy/kubernetes/deployment-test.yaml b/archive/legacy/kubernetes/deployment-test.yaml new file mode 100644 index 0000000..9842d1d --- /dev/null +++ b/archive/legacy/kubernetes/deployment-test.yaml @@ -0,0 +1,20 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: my-app-test +spec: + replicas: 1 + selector: + matchLabels: + app: my-app-test + template: + metadata: + labels: + app: my-app-test + spec: + containers: + - name: my-app-test + image: ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/mynoderepo:test + imagePullPolicy: Always + ports: + - containerPort: 8080 \ No newline at end of file diff --git a/archive/legacy/kubernetes/deployment.yaml b/archive/legacy/kubernetes/deployment.yaml new file mode 100644 index 0000000..6f8609f --- /dev/null +++ b/archive/legacy/kubernetes/deployment.yaml @@ -0,0 +1,20 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: my-app +spec: + replicas: 3 + selector: + matchLabels: + app: my-app + template: + metadata: + labels: + app: my-app + spec: + containers: + - name: my-app + image: ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/mynoderepo:latest + imagePullPolicy: Always + ports: + - containerPort: 8080 \ No newline at end of file diff --git a/archive/legacy/kubernetes/forwardservice.yaml b/archive/legacy/kubernetes/forwardservice.yaml new file mode 100644 index 0000000..734d17c --- /dev/null +++ b/archive/legacy/kubernetes/forwardservice.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: my-app +spec: + type: LoadBalancer + selector: + app: my-app + ports: + - protocol: TCP + port: 80 + targetPort: 5005 \ No newline at end of file diff --git a/archive/legacy/kubernetes/ingress.yaml b/archive/legacy/kubernetes/ingress.yaml new file mode 100644 index 0000000..183c824 --- /dev/null +++ b/archive/legacy/kubernetes/ingress.yaml @@ -0,0 +1,16 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: my-app-ingress +spec: + rules: + - host: my-app.simpleapp.com + http: + paths: + - pathType: Prefix + path: "/" + backend: + service: + name: my-app + port: + number: 8080 \ No newline at end of file diff --git a/archive/legacy/kubernetes/loadbalancer.yaml b/archive/legacy/kubernetes/loadbalancer.yaml new file mode 100644 index 0000000..4009a4f --- /dev/null +++ b/archive/legacy/kubernetes/loadbalancer.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: my-app +spec: + type: LoadBalancer + selector: + app: my-app + ports: + - protocol: TCP + port: 80 + targetPort: 8080 \ No newline at end of file diff --git a/archive/legacy/kubernetes/service.yaml b/archive/legacy/kubernetes/service.yaml new file mode 100644 index 0000000..5d81d2f --- /dev/null +++ b/archive/legacy/kubernetes/service.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Service +metadata: + name: my-app +spec: + selector: + app: my-app + ports: + - protocol: TCP + port: 80 + targetPort: 8080 \ No newline at end of file diff --git a/archive/legacy/plan_outputekscluster b/archive/legacy/plan_outputekscluster new file mode 100644 index 0000000..97a3c14 Binary files /dev/null and b/archive/legacy/plan_outputekscluster differ diff --git a/docs/architecture.md b/docs/architecture.md index 27431ef..55c3332 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -7,7 +7,7 @@ This repository models a small internal developer platform on Amazon EKS. - `infra/` provisions AWS resources such as VPC networking, IAM, and EKS. - `platform/` represents the declarative cluster state reconciled by Argo CD. - `standardized-path/app/` is the single supported application contract for tenant workloads. -- `legacy/` contains historical assets that are intentionally outside the supported path. +- `archive/` contains historical assets that are intentionally outside the supported path. ## Ownership Boundaries diff --git a/platform/apps/app-b/dev/values.yaml b/platform/apps/app-b/dev/values.yaml index 8068e7c..5c5df16 100644 --- a/platform/apps/app-b/dev/values.yaml +++ b/platform/apps/app-b/dev/values.yaml @@ -1,7 +1,6 @@ replicaCount: 1 image: - repository: 944684220857.dkr.ecr.eu-north-1.amazonaws.com/app tag: "2.0.0-dev.1" ingress: diff --git a/platform/apps/app-b/prod/values.yaml b/platform/apps/app-b/prod/values.yaml index a8812e1..ad3df3a 100644 --- a/platform/apps/app-b/prod/values.yaml +++ b/platform/apps/app-b/prod/values.yaml @@ -1,7 +1,6 @@ replicaCount: 3 image: - repository: 944684220857.dkr.ecr.eu-north-1.amazonaws.com/app tag: "2.0.0" ingress: diff --git a/platform/apps/app-b/stage/values.yaml b/platform/apps/app-b/stage/values.yaml index e5fff17..8e86a9f 100644 --- a/platform/apps/app-b/stage/values.yaml +++ b/platform/apps/app-b/stage/values.yaml @@ -1,7 +1,6 @@ replicaCount: 2 image: - repository: 944684220857.dkr.ecr.eu-north-1.amazonaws.com/app tag: "2.0.0-rc1" ingress: diff --git a/platform/apps/dev/values.yaml b/platform/apps/dev/values.yaml index 1cbf646..4316e50 100644 --- a/platform/apps/dev/values.yaml +++ b/platform/apps/dev/values.yaml @@ -1,7 +1,6 @@ replicaCount: 1 image: - repository: 944684220857.dkr.ecr.eu-north-1.amazonaws.com/app tag: "1.0.0-dev.1" env: diff --git a/platform/apps/prod/values.yaml b/platform/apps/prod/values.yaml index faf20d5..0cc04c1 100644 --- a/platform/apps/prod/values.yaml +++ b/platform/apps/prod/values.yaml @@ -1,7 +1,6 @@ replicaCount: 3 image: - repository: 944684220857.dkr.ecr.eu-north-1.amazonaws.com/app tag: "1.0.0" env: diff --git a/platform/apps/stage/values.yaml b/platform/apps/stage/values.yaml index 6d019e7..e6262b1 100644 --- a/platform/apps/stage/values.yaml +++ b/platform/apps/stage/values.yaml @@ -1,7 +1,6 @@ replicaCount: 2 image: - repository: 944684220857.dkr.ecr.eu-north-1.amazonaws.com/app tag: "1.0.0-rc1" env: diff --git a/platform/bootstrap/rbac-readonly.yaml b/platform/bootstrap/rbac-readonly.yaml index 12f97f1..0e5b54c 100644 --- a/platform/bootstrap/rbac-readonly.yaml +++ b/platform/bootstrap/rbac-readonly.yaml @@ -24,3 +24,57 @@ roleRef: kind: Role name: platform-readonly apiGroup: rbac.authorization.k8s.io +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: platform-readonly + namespace: stage +rules: + - apiGroups: [""] + resources: ["pods", "services", "configmaps"] + verbs: ["get", "list", "watch"] + - apiGroups: ["apps"] + resources: ["deployments", "replicasets"] + verbs: ["get", "list", "watch"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: platform-readonly-binding + namespace: stage +subjects: + - kind: Group + name: app-developers + apiGroup: rbac.authorization.k8s.io +roleRef: + kind: Role + name: platform-readonly + apiGroup: rbac.authorization.k8s.io +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: platform-readonly + namespace: prod +rules: + - apiGroups: [""] + resources: ["pods", "services", "configmaps"] + verbs: ["get", "list", "watch"] + - apiGroups: ["apps"] + resources: ["deployments", "replicasets"] + verbs: ["get", "list", "watch"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: platform-readonly-binding + namespace: prod +subjects: + - kind: Group + name: app-developers + apiGroup: rbac.authorization.k8s.io +roleRef: + kind: Role + name: platform-readonly + apiGroup: rbac.authorization.k8s.io diff --git a/platform/bootstrap/resource-quota.yaml b/platform/bootstrap/resource-quota.yaml index 01f4bbb..41f517e 100644 --- a/platform/bootstrap/resource-quota.yaml +++ b/platform/bootstrap/resource-quota.yaml @@ -11,3 +11,31 @@ spec: limits.memory: 40Gi pods: "50" services: "20" +--- +apiVersion: v1 +kind: ResourceQuota +metadata: + name: stage-quota + namespace: stage +spec: + hard: + requests.cpu: "15" + requests.memory: 30Gi + limits.cpu: "30" + limits.memory: 60Gi + pods: "30" + services: "15" +--- +apiVersion: v1 +kind: ResourceQuota +metadata: + name: prod-quota + namespace: prod +spec: + hard: + requests.cpu: "20" + requests.memory: 40Gi + limits.cpu: "40" + limits.memory: 80Gi + pods: "20" + services: "10" diff --git a/standardized-path/app/templates/NOTES.txt b/standardized-path/app/templates/NOTES.txt index be59ec4..a1072aa 100644 --- a/standardized-path/app/templates/NOTES.txt +++ b/standardized-path/app/templates/NOTES.txt @@ -6,16 +6,16 @@ {{- end }} {{- end }} {{- else if contains "NodePort" .Values.service.type }} - export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "my-app.fullname" . }}) + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "app.fullname" . }}) export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") echo http://$NODE_IP:$NODE_PORT {{- else if contains "LoadBalancer" .Values.service.type }} NOTE: It may take a few minutes for the LoadBalancer IP to be available. - You can watch its status by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "my-app.fullname" . }}' - export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "my-app.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + You can watch its status by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "app.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "app.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") echo http://$SERVICE_IP:{{ .Values.service.port }} {{- else if contains "ClusterIP" .Values.service.type }} - export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "my-app.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "app.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") echo "Visit http://127.0.0.1:8080 to use your application" kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT diff --git a/standardized-path/app/templates/_helpers.tpl b/standardized-path/app/templates/_helpers.tpl index afcc44b..20c6889 100644 --- a/standardized-path/app/templates/_helpers.tpl +++ b/standardized-path/app/templates/_helpers.tpl @@ -1,7 +1,7 @@ {{/* Expand the name of the chart. */}} -{{- define "my-app.name" -}} +{{- define "app.name" -}} {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} {{- end }} @@ -10,7 +10,7 @@ Create a default fully qualified app name. We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). If release name contains chart name it will be used as a full name. */}} -{{- define "my-app.fullname" -}} +{{- define "app.fullname" -}} {{- if .Values.fullnameOverride }} {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} {{- else }} @@ -26,16 +26,16 @@ If release name contains chart name it will be used as a full name. {{/* Create chart name and version as used by the chart label. */}} -{{- define "my-app.chart" -}} +{{- define "app.chart" -}} {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} {{- end }} {{/* Common labels */}} -{{- define "my-app.labels" -}} -helm.sh/chart: {{ include "my-app.chart" . }} -{{ include "my-app.selectorLabels" . }} +{{- define "app.labels" -}} +helm.sh/chart: {{ include "app.chart" . }} +{{ include "app.selectorLabels" . }} {{- if .Chart.AppVersion }} app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} {{- end }} @@ -45,17 +45,17 @@ app.kubernetes.io/managed-by: {{ .Release.Service }} {{/* Selector labels */}} -{{- define "my-app.selectorLabels" -}} -app.kubernetes.io/name: {{ include "my-app.name" . }} +{{- define "app.selectorLabels" -}} +app.kubernetes.io/name: {{ include "app.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} {{- end }} {{/* Create the name of the service account to use */}} -{{- define "my-app.serviceAccountName" -}} +{{- define "app.serviceAccountName" -}} {{- if .Values.serviceAccount.create }} -{{- default (include "my-app.fullname" .) .Values.serviceAccount.name }} +{{- default (include "app.fullname" .) .Values.serviceAccount.name }} {{- else }} {{- default "default" .Values.serviceAccount.name }} {{- end }} diff --git a/standardized-path/app/templates/deployment.yaml b/standardized-path/app/templates/deployment.yaml index 3a2120e..6d7dce4 100644 --- a/standardized-path/app/templates/deployment.yaml +++ b/standardized-path/app/templates/deployment.yaml @@ -1,16 +1,16 @@ apiVersion: apps/v1 kind: Deployment metadata: - name: {{ include "my-app.fullname" . }} + name: {{ include "app.fullname" . }} labels: - {{- include "my-app.labels" . | nindent 4 }} + {{- include "app.labels" . | nindent 4 }} spec: {{- if not .Values.autoscaling.enabled }} replicas: {{ .Values.replicaCount }} {{- end }} selector: matchLabels: - {{- include "my-app.selectorLabels" . | nindent 6 }} + {{- include "app.selectorLabels" . | nindent 6 }} template: metadata: {{- with .Values.podAnnotations }} @@ -18,7 +18,7 @@ spec: {{- toYaml . | nindent 8 }} {{- end }} labels: - {{- include "my-app.labels" . | nindent 8 }} + {{- include "app.labels" . | nindent 8 }} {{- with .Values.podLabels }} {{- toYaml . | nindent 8 }} {{- end }} @@ -27,7 +27,7 @@ spec: imagePullSecrets: {{- toYaml . | nindent 8 }} {{- end }} - serviceAccountName: {{ include "my-app.serviceAccountName" . }} + serviceAccountName: {{ include "app.serviceAccountName" . }} {{- if not .Values.openshift.enabled }} {{- with .Values.podSecurityContext }} securityContext: diff --git a/standardized-path/app/templates/externalsecret.yaml b/standardized-path/app/templates/externalsecret.yaml index 96eebd5..28f01ec 100644 --- a/standardized-path/app/templates/externalsecret.yaml +++ b/standardized-path/app/templates/externalsecret.yaml @@ -3,16 +3,16 @@ apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: - name: {{ include "my-app.fullname" $ }} + name: {{ include "app.fullname" $ }} labels: - {{- include "my-app.labels" $ | nindent 4 }} + {{- include "app.labels" $ | nindent 4 }} spec: refreshInterval: {{ .refreshInterval }} secretStoreRef: name: {{ .store.name }} kind: {{ .store.kind }} target: - name: {{ include "my-app.fullname" $ }} + name: {{ include "app.fullname" $ }} creationPolicy: Owner data: {{- range .data }} diff --git a/standardized-path/app/templates/hpa.yaml b/standardized-path/app/templates/hpa.yaml index 3d51d69..658ccd0 100644 --- a/standardized-path/app/templates/hpa.yaml +++ b/standardized-path/app/templates/hpa.yaml @@ -2,14 +2,14 @@ apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: - name: {{ include "my-app.fullname" . }} + name: {{ include "app.fullname" . }} labels: - {{- include "my-app.labels" . | nindent 4 }} + {{- include "app.labels" . | nindent 4 }} spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment - name: {{ include "my-app.fullname" . }} + name: {{ include "app.fullname" . }} minReplicas: {{ .Values.autoscaling.minReplicas }} maxReplicas: {{ .Values.autoscaling.maxReplicas }} metrics: diff --git a/standardized-path/app/templates/ingress.yaml b/standardized-path/app/templates/ingress.yaml index 696112d..eaa94cc 100644 --- a/standardized-path/app/templates/ingress.yaml +++ b/standardized-path/app/templates/ingress.yaml @@ -2,9 +2,9 @@ apiVersion: networking.k8s.io/v1 kind: Ingress metadata: - name: {{ include "my-app.fullname" . }} + name: {{ include "app.fullname" . }} labels: - {{- include "my-app.labels" . | nindent 4 }} + {{- include "app.labels" . | nindent 4 }} {{- with .Values.ingress.annotations }} annotations: {{- toYaml . | nindent 4 }} @@ -35,7 +35,7 @@ spec: {{- end }} backend: service: - name: {{ include "my-app.fullname" $ }} + name: {{ include "app.fullname" $ }} port: number: {{ $.Values.service.port }} {{- end }} diff --git a/standardized-path/app/templates/openshift-route.yaml b/standardized-path/app/templates/openshift-route.yaml index 6c3e91a..ac93335 100644 --- a/standardized-path/app/templates/openshift-route.yaml +++ b/standardized-path/app/templates/openshift-route.yaml @@ -2,9 +2,9 @@ apiVersion: route.openshift.io/v1 kind: Route metadata: - name: {{ include "my-app.fullname" . }} + name: {{ include "app.fullname" . }} labels: - {{- include "my-app.labels" . | nindent 4 }} + {{- include "app.labels" . | nindent 4 }} {{- with .Values.openshift.route.annotations }} annotations: {{- toYaml . | nindent 4 }} @@ -15,7 +15,7 @@ spec: {{- end }} to: kind: Service - name: {{ include "my-app.fullname" . }} + name: {{ include "app.fullname" . }} port: targetPort: http {{- with .Values.openshift.route.tls }} diff --git a/standardized-path/app/templates/pdb.yaml b/standardized-path/app/templates/pdb.yaml new file mode 100644 index 0000000..587b067 --- /dev/null +++ b/standardized-path/app/templates/pdb.yaml @@ -0,0 +1,21 @@ +{{- if .Values.podDisruptionBudget.enabled }} +{{- if and (not .Values.podDisruptionBudget.minAvailable) (not .Values.podDisruptionBudget.maxUnavailable) }} +{{- fail "podDisruptionBudget.minAvailable or podDisruptionBudget.maxUnavailable must be set when podDisruptionBudget.enabled is true" }} +{{- end }} +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: {{ include "app.fullname" . }} + labels: + {{- include "app.labels" . | nindent 4 }} +spec: + {{- with .Values.podDisruptionBudget.minAvailable }} + minAvailable: {{ . }} + {{- end }} + {{- with .Values.podDisruptionBudget.maxUnavailable }} + maxUnavailable: {{ . }} + {{- end }} + selector: + matchLabels: + {{- include "app.selectorLabels" . | nindent 6 }} +{{- end }} diff --git a/standardized-path/app/templates/service.yaml b/standardized-path/app/templates/service.yaml index 7565f17..f674843 100644 --- a/standardized-path/app/templates/service.yaml +++ b/standardized-path/app/templates/service.yaml @@ -1,9 +1,9 @@ apiVersion: v1 kind: Service metadata: - name: {{ include "my-app.fullname" . }} + name: {{ include "app.fullname" . }} labels: - {{- include "my-app.labels" . | nindent 4 }} + {{- include "app.labels" . | nindent 4 }} spec: type: {{ .Values.service.type }} ports: @@ -12,4 +12,4 @@ spec: protocol: TCP name: http selector: - {{- include "my-app.selectorLabels" . | nindent 4 }} + {{- include "app.selectorLabels" . | nindent 4 }} diff --git a/standardized-path/app/templates/serviceaccount.yaml b/standardized-path/app/templates/serviceaccount.yaml index 91be91a..3c0f54f 100644 --- a/standardized-path/app/templates/serviceaccount.yaml +++ b/standardized-path/app/templates/serviceaccount.yaml @@ -2,9 +2,9 @@ apiVersion: v1 kind: ServiceAccount metadata: - name: {{ include "my-app.serviceAccountName" . }} + name: {{ include "app.serviceAccountName" . }} labels: - {{- include "my-app.labels" . | nindent 4 }} + {{- include "app.labels" . | nindent 4 }} {{- with .Values.serviceAccount.annotations }} annotations: {{- toYaml . | nindent 4 }} diff --git a/standardized-path/app/templates/servicemonitor.yaml b/standardized-path/app/templates/servicemonitor.yaml index c73a8af..934633e 100644 --- a/standardized-path/app/templates/servicemonitor.yaml +++ b/standardized-path/app/templates/servicemonitor.yaml @@ -2,15 +2,15 @@ apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: - name: {{ include "my-app.fullname" . }} + name: {{ include "app.fullname" . }} labels: - app.kubernetes.io/name: {{ include "my-app.name" . }} + app.kubernetes.io/name: {{ include "app.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} app.kubernetes.io/managed-by: platform spec: selector: matchLabels: - {{- include "my-app.selectorLabels" . | nindent 6 }} + {{- include "app.selectorLabels" . | nindent 6 }} endpoints: - port: http interval: {{ .Values.serviceMonitor.interval }} diff --git a/standardized-path/app/templates/tests/test-connection.yaml b/standardized-path/app/templates/tests/test-connection.yaml index 582e801..d0394f1 100644 --- a/standardized-path/app/templates/tests/test-connection.yaml +++ b/standardized-path/app/templates/tests/test-connection.yaml @@ -1,15 +1,15 @@ apiVersion: v1 kind: Pod metadata: - name: "{{ include "my-app.fullname" . }}-test-connection" + name: "{{ include "app.fullname" . }}-test-connection" labels: - {{- include "my-app.labels" . | nindent 4 }} + {{- include "app.labels" . | nindent 4 }} annotations: "helm.sh/hook": test spec: containers: - name: wget - image: busybox + image: alpine:3.19 command: ['wget'] - args: ['{{ include "my-app.fullname" . }}:{{ .Values.service.port }}'] + args: ['{{ include "app.fullname" . }}:{{ .Values.service.port }}'] restartPolicy: Never diff --git a/standardized-path/app/tests/deployment_test.yaml b/standardized-path/app/tests/deployment_test.yaml index 09a5101..11814a2 100644 --- a/standardized-path/app/tests/deployment_test.yaml +++ b/standardized-path/app/tests/deployment_test.yaml @@ -18,11 +18,13 @@ tests: path: spec.replicas value: 2 - - it: should use the correct image + - it: should use the provided image repository and tag + set: + image.repository: my-registry.example.com/my-app asserts: - matchRegex: path: spec.template.spec.containers[0].image - pattern: 944684220857\.dkr\.ecr\.eu-north-1\.amazonaws\.com/app:1\.0\.0 + pattern: my-registry\.example\.com/my-app:1\.0\.0 - it: should set securityContext on EKS (default) asserts: diff --git a/standardized-path/app/tests/pdb_test.yaml b/standardized-path/app/tests/pdb_test.yaml new file mode 100644 index 0000000..ecd0d8f --- /dev/null +++ b/standardized-path/app/tests/pdb_test.yaml @@ -0,0 +1,36 @@ +suite: PodDisruptionBudget +templates: + - pdb.yaml +values: + - ../values.yaml +tests: + - it: should not render when PDB is disabled (default) + asserts: + - hasDocuments: + count: 0 + + - it: should render with minAvailable when enabled + set: + podDisruptionBudget.enabled: true + podDisruptionBudget.minAvailable: 1 + asserts: + - isKind: + of: PodDisruptionBudget + - equal: + path: spec.minAvailable + value: 1 + - notExists: + path: spec.maxUnavailable + + - it: should render with maxUnavailable when enabled + set: + podDisruptionBudget.enabled: true + podDisruptionBudget.maxUnavailable: 1 + asserts: + - isKind: + of: PodDisruptionBudget + - equal: + path: spec.maxUnavailable + value: 1 + - notExists: + path: spec.minAvailable diff --git a/standardized-path/app/values.yaml b/standardized-path/app/values.yaml index c77f331..6718048 100644 --- a/standardized-path/app/values.yaml +++ b/standardized-path/app/values.yaml @@ -7,7 +7,10 @@ replicaCount: 2 # This sets the container image more information can be found here: https://kubernetes.io/docs/concepts/containers/images/ image: - repository: 944684220857.dkr.ecr.eu-north-1.amazonaws.com/app + # Must be set per environment via values files, CI --set, or Argo CD parameters. + # Format: .dkr.ecr..amazonaws.com/ + # Configurable via Makefile: AWS_ACCOUNT_ID and AWS_REGION + repository: "" # This sets the pull policy for images. pullPolicy: IfNotPresent # Overrides the image tag whose default is the chart appVersion. @@ -136,6 +139,12 @@ openshift: insecureEdgeTerminationPolicy: Redirect wildcardPolicy: None +# PodDisruptionBudget for high availability +podDisruptionBudget: + enabled: false + minAvailable: + maxUnavailable: + nodeSelector: {} tolerations: []