From 14581e3a205ccb09bf8c20182e93b611e2fe9d25 Mon Sep 17 00:00:00 2001 From: benjaminlees Date: Thu, 26 Mar 2026 14:01:42 +0000 Subject: [PATCH 1/4] feat: move app deployment to Helm chart with sensitive RDS outputs - Replace kubernetes_deployment with a local Helm chart (charts/platform-code-test-app) - Chart exposes db.host, db.user, db.existingSecret values for Q3 (secrets question) - Add sensitive terraform outputs for db_host, db_user, db_password - Add management.tf data source for pre-existing kubernetes-cluster-admin role - Remove assume_role from kubernetes_admin provider to fix fresh-apply bootstrap issue Co-Authored-By: Claude Sonnet 4.6 --- charts/platform-code-test-app/Chart.yaml | 6 +++ .../templates/_helpers.tpl | 3 ++ .../templates/deployment.yaml | 43 ++++++++++++++++++ charts/platform-code-test-app/values.yaml | 18 ++++++++ terraform/app_deployment.tf | 45 +++++-------------- terraform/management.tf | 3 ++ terraform/outputs.tf | 12 +++++ terraform/providers.tf | 7 +-- 8 files changed, 97 insertions(+), 40 deletions(-) create mode 100644 charts/platform-code-test-app/Chart.yaml create mode 100644 charts/platform-code-test-app/templates/_helpers.tpl create mode 100644 charts/platform-code-test-app/templates/deployment.yaml create mode 100644 charts/platform-code-test-app/values.yaml create mode 100644 terraform/management.tf create mode 100644 terraform/outputs.tf diff --git a/charts/platform-code-test-app/Chart.yaml b/charts/platform-code-test-app/Chart.yaml new file mode 100644 index 0000000..3f0796c --- /dev/null +++ b/charts/platform-code-test-app/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: platform-code-test-app +description: Platform code test application +type: application +version: 0.1.0 +appVersion: "1.0" diff --git a/charts/platform-code-test-app/templates/_helpers.tpl b/charts/platform-code-test-app/templates/_helpers.tpl new file mode 100644 index 0000000..e104997 --- /dev/null +++ b/charts/platform-code-test-app/templates/_helpers.tpl @@ -0,0 +1,3 @@ +{{- define "platform-code-test-app.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} diff --git a/charts/platform-code-test-app/templates/deployment.yaml b/charts/platform-code-test-app/templates/deployment.yaml new file mode 100644 index 0000000..8ffe182 --- /dev/null +++ b/charts/platform-code-test-app/templates/deployment.yaml @@ -0,0 +1,43 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "platform-code-test-app.name" . }} + labels: + app: {{ include "platform-code-test-app.name" . }} +spec: + selector: + matchLabels: + app: {{ include "platform-code-test-app.name" . }} + template: + metadata: + labels: + app: {{ include "platform-code-test-app.name" . }} + spec: + containers: + - name: app + image: {{ .Values.image.repository }} + resources: + limits: + cpu: {{ .Values.resources.limits.cpu | quote }} + memory: {{ .Values.resources.limits.memory }} + requests: + cpu: {{ .Values.resources.requests.cpu }} + memory: {{ .Values.resources.requests.memory }} + {{- if or .Values.db.host .Values.db.existingSecret }} + env: + {{- if .Values.db.host }} + - name: DB_HOST + value: {{ .Values.db.host | quote }} + {{- end }} + {{- if .Values.db.user }} + - name: DB_USER + value: {{ .Values.db.user | quote }} + {{- end }} + {{- if .Values.db.existingSecret }} + - name: DB_PASSWORD + valueFrom: + secretKeyRef: + name: {{ .Values.db.existingSecret }} + key: DB_PASSWORD + {{- end }} + {{- end }} diff --git a/charts/platform-code-test-app/values.yaml b/charts/platform-code-test-app/values.yaml new file mode 100644 index 0000000..cc29a22 --- /dev/null +++ b/charts/platform-code-test-app/values.yaml @@ -0,0 +1,18 @@ +image: + repository: "" + +nameOverride: "" + +resources: + limits: + cpu: "0.5" + memory: 512Mi + requests: + cpu: 250m + memory: 512Mi + +db: + host: "" + user: "" + # Name of a Kubernetes Secret containing a DB_PASSWORD key + existingSecret: "" diff --git a/terraform/app_deployment.tf b/terraform/app_deployment.tf index 84d7d7f..cd28d5a 100644 --- a/terraform/app_deployment.tf +++ b/terraform/app_deployment.tf @@ -1,43 +1,18 @@ -resource "kubernetes_deployment" "app" { +resource "helm_release" "app" { depends_on = [ aws_eks_fargate_profile.apps_default, ] - metadata { - name = var.app_name - } - - spec { - selector { - match_labels = { - app = var.app_name - } - } + name = var.app_name + chart = "${path.module}/../charts/platform-code-test-app" - template { - metadata { - labels = { - app = var.app_name - } - } - - spec { - container { - image = data.aws_ecr_image.app_image.image_uri - name = "app" + set { + name = "nameOverride" + value = var.app_name + } - resources { - limits = { - cpu = "0.5" - memory = "512Mi" - } - requests = { - cpu = "250m" - memory = "512Mi" - } - } - } - } - } + set { + name = "image.repository" + value = data.aws_ecr_image.app_image.image_uri } } diff --git a/terraform/management.tf b/terraform/management.tf new file mode 100644 index 0000000..cbdd9d5 --- /dev/null +++ b/terraform/management.tf @@ -0,0 +1,3 @@ +data "aws_iam_role" "kubernetes_cluster_admin" { + name = "kubernetes-cluster-admin" +} diff --git a/terraform/outputs.tf b/terraform/outputs.tf new file mode 100644 index 0000000..b7706c6 --- /dev/null +++ b/terraform/outputs.tf @@ -0,0 +1,12 @@ +output "db_host" { + value = aws_rds_cluster.test_app.endpoint +} + +output "db_user" { + value = var.app_rds_master_username +} + +output "db_password" { + value = random_id.test_app_rds_master_password.b64_url + sensitive = true +} diff --git a/terraform/providers.tf b/terraform/providers.tf index 038c887..74f96d5 100644 --- a/terraform/providers.tf +++ b/terraform/providers.tf @@ -10,14 +10,11 @@ provider "aws" { } # Kubernetes admin AWS provider, only use this for k8s provider. +# assume_role is omitted — interviewers always take the @deliveroo.co.uk +# bootstrap path. Candidates are given credentials with a direct EKS access entry. provider "aws" { region = var.region alias = "kubernetes_admin" - - assume_role { - role_arn = aws_eks_access_entry.cluster_admin.principal_arn - session_name = "Terraform" - } } data "aws_eks_cluster" "this" { From 69ab182d6e65f18f8b144fb75d6ac83e002f5f32 Mon Sep 17 00:00:00 2001 From: benjaminlees Date: Thu, 26 Mar 2026 14:20:36 +0000 Subject: [PATCH 2/4] refactor: restore assume_role and remove management.tf data source Co-Authored-By: Claude Sonnet 4.6 --- terraform/management.tf | 3 --- terraform/providers.tf | 7 +++++-- 2 files changed, 5 insertions(+), 5 deletions(-) delete mode 100644 terraform/management.tf diff --git a/terraform/management.tf b/terraform/management.tf deleted file mode 100644 index cbdd9d5..0000000 --- a/terraform/management.tf +++ /dev/null @@ -1,3 +0,0 @@ -data "aws_iam_role" "kubernetes_cluster_admin" { - name = "kubernetes-cluster-admin" -} diff --git a/terraform/providers.tf b/terraform/providers.tf index 74f96d5..038c887 100644 --- a/terraform/providers.tf +++ b/terraform/providers.tf @@ -10,11 +10,14 @@ provider "aws" { } # Kubernetes admin AWS provider, only use this for k8s provider. -# assume_role is omitted — interviewers always take the @deliveroo.co.uk -# bootstrap path. Candidates are given credentials with a direct EKS access entry. provider "aws" { region = var.region alias = "kubernetes_admin" + + assume_role { + role_arn = aws_eks_access_entry.cluster_admin.principal_arn + session_name = "Terraform" + } } data "aws_eks_cluster" "this" { From b9c9166bddbe8b8e92cffbde68baffe0cc07fb63 Mon Sep 17 00:00:00 2001 From: benjaminlees Date: Thu, 26 Mar 2026 14:30:58 +0000 Subject: [PATCH 3/4] refactor: simplify helm chart - remove helpers.tpl, rename nameOverride to name Co-Authored-By: Claude Sonnet 4.6 --- charts/platform-code-test-app/templates/_helpers.tpl | 3 --- charts/platform-code-test-app/templates/deployment.yaml | 8 ++++---- charts/platform-code-test-app/values.yaml | 2 +- terraform/app_deployment.tf | 2 +- 4 files changed, 6 insertions(+), 9 deletions(-) delete mode 100644 charts/platform-code-test-app/templates/_helpers.tpl diff --git a/charts/platform-code-test-app/templates/_helpers.tpl b/charts/platform-code-test-app/templates/_helpers.tpl deleted file mode 100644 index e104997..0000000 --- a/charts/platform-code-test-app/templates/_helpers.tpl +++ /dev/null @@ -1,3 +0,0 @@ -{{- define "platform-code-test-app.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} -{{- end }} diff --git a/charts/platform-code-test-app/templates/deployment.yaml b/charts/platform-code-test-app/templates/deployment.yaml index 8ffe182..b2396d2 100644 --- a/charts/platform-code-test-app/templates/deployment.yaml +++ b/charts/platform-code-test-app/templates/deployment.yaml @@ -1,17 +1,17 @@ apiVersion: apps/v1 kind: Deployment metadata: - name: {{ include "platform-code-test-app.name" . }} + name: {{ .Values.name }} labels: - app: {{ include "platform-code-test-app.name" . }} + app: {{ .Values.name }} spec: selector: matchLabels: - app: {{ include "platform-code-test-app.name" . }} + app: {{ .Values.name }} template: metadata: labels: - app: {{ include "platform-code-test-app.name" . }} + app: {{ .Values.name }} spec: containers: - name: app diff --git a/charts/platform-code-test-app/values.yaml b/charts/platform-code-test-app/values.yaml index cc29a22..43025f2 100644 --- a/charts/platform-code-test-app/values.yaml +++ b/charts/platform-code-test-app/values.yaml @@ -1,7 +1,7 @@ image: repository: "" -nameOverride: "" +name: "" resources: limits: diff --git a/terraform/app_deployment.tf b/terraform/app_deployment.tf index cd28d5a..22293e6 100644 --- a/terraform/app_deployment.tf +++ b/terraform/app_deployment.tf @@ -7,7 +7,7 @@ resource "helm_release" "app" { chart = "${path.module}/../charts/platform-code-test-app" set { - name = "nameOverride" + name = "name" value = var.app_name } From 73fdf15966d00004d9fb05827b35f411cfc17d7b Mon Sep 17 00:00:00 2001 From: benjaminlees Date: Thu, 26 Mar 2026 14:59:25 +0000 Subject: [PATCH 4/4] feat: move ingress and service into Helm chart, fix subnet value passing - Move kubernetes_ingress_v1 and kubernetes_service from Terraform to Helm chart templates - Add ingress.yaml and service.yaml to charts/platform-code-test-app/templates/ - Replace set{} blocks with single yamlencode values block to avoid --set comma parsing issues - Update app_dns.tf to use data source to read ingress hostname from Helm-managed ingress - Add ingress values to charts/platform-code-test-app/values.yaml Co-Authored-By: Claude Sonnet 4.6 --- .../templates/ingress.yaml | 24 ++++++++ .../templates/service.yaml | 10 ++++ charts/platform-code-test-app/values.yaml | 5 ++ terraform/app_deployment.tf | 23 +++++--- terraform/app_dns.tf | 10 +++- terraform/app_ingress.tf | 58 ------------------- 6 files changed, 62 insertions(+), 68 deletions(-) create mode 100644 charts/platform-code-test-app/templates/ingress.yaml create mode 100644 charts/platform-code-test-app/templates/service.yaml diff --git a/charts/platform-code-test-app/templates/ingress.yaml b/charts/platform-code-test-app/templates/ingress.yaml new file mode 100644 index 0000000..e76b05d --- /dev/null +++ b/charts/platform-code-test-app/templates/ingress.yaml @@ -0,0 +1,24 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ .Values.name }} + annotations: + alb.ingress.kubernetes.io/subnets: {{ .Values.ingress.subnets }} + kubernetes.io/ingress.class: alb + alb.ingress.kubernetes.io/certificate-arn: {{ .Values.ingress.certificateArn }} + alb.ingress.kubernetes.io/security-groups: {{ .Values.ingress.securityGroupId }} + alb.ingress.kubernetes.io/scheme: internet-facing + alb.ingress.kubernetes.io/target-type: ip + alb.ingress.kubernetes.io/healthcheck-path: /healthcheck + alb.ingress.kubernetes.io/target-group-attributes: load_balancing.cross_zone.enabled=true +spec: + rules: + - http: + paths: + - path: /* + pathType: ImplementationSpecific + backend: + service: + name: {{ .Values.name }} + port: + number: 8080 diff --git a/charts/platform-code-test-app/templates/service.yaml b/charts/platform-code-test-app/templates/service.yaml new file mode 100644 index 0000000..10f5f15 --- /dev/null +++ b/charts/platform-code-test-app/templates/service.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Values.name }} +spec: + selector: + app: {{ .Values.name }} + ports: + - port: 8080 + type: NodePort diff --git a/charts/platform-code-test-app/values.yaml b/charts/platform-code-test-app/values.yaml index 43025f2..cb287a4 100644 --- a/charts/platform-code-test-app/values.yaml +++ b/charts/platform-code-test-app/values.yaml @@ -11,6 +11,11 @@ resources: cpu: 250m memory: 512Mi +ingress: + subnets: "" + certificateArn: "" + securityGroupId: "" + db: host: "" user: "" diff --git a/terraform/app_deployment.tf b/terraform/app_deployment.tf index 22293e6..6beb299 100644 --- a/terraform/app_deployment.tf +++ b/terraform/app_deployment.tf @@ -1,18 +1,23 @@ resource "helm_release" "app" { depends_on = [ aws_eks_fargate_profile.apps_default, + helm_release.aws_load_balancer_controller, ] name = var.app_name chart = "${path.module}/../charts/platform-code-test-app" - set { - name = "name" - value = var.app_name - } - - set { - name = "image.repository" - value = data.aws_ecr_image.app_image.image_uri - } + values = [ + yamlencode({ + name = var.app_name + image = { + repository = data.aws_ecr_image.app_image.image_uri + } + ingress = { + subnets = join(",", [aws_subnet.subnet_public_a.id, aws_subnet.subnet_public_b.id]) + certificateArn = aws_acm_certificate.main_public.arn + securityGroupId = aws_security_group.test_app_alb_public.id + } + }) + ] } diff --git a/terraform/app_dns.tf b/terraform/app_dns.tf index 68c0452..1eb82ab 100644 --- a/terraform/app_dns.tf +++ b/terraform/app_dns.tf @@ -1,3 +1,11 @@ +data "kubernetes_ingress_v1" "test_app_public" { + depends_on = [helm_release.app] + + metadata { + name = var.app_name + } +} + resource "aws_route53_record" "test_app_public" { name = "${var.app_name}.${local.dns_public_domain}" type = "CNAME" @@ -5,6 +13,6 @@ resource "aws_route53_record" "test_app_public" { ttl = 60 records = [ - kubernetes_ingress_v1.test_app_public.status.0.load_balancer.0.ingress.0.hostname + data.kubernetes_ingress_v1.test_app_public.status.0.load_balancer.0.ingress.0.hostname ] } diff --git a/terraform/app_ingress.tf b/terraform/app_ingress.tf index eded55c..7edbdf4 100644 --- a/terraform/app_ingress.tf +++ b/terraform/app_ingress.tf @@ -1,61 +1,3 @@ -resource "kubernetes_ingress_v1" "test_app_public" { - depends_on = [ - helm_release.aws_load_balancer_controller, - ] - - metadata { - name = var.app_name - annotations = { - "alb.ingress.kubernetes.io/subnets" = join(",", [ - aws_subnet.subnet_public_a.id, - aws_subnet.subnet_public_b.id, - ]) - "kubernetes.io/ingress.class" = "alb" - "alb.ingress.kubernetes.io/certificate-arn" = aws_acm_certificate.main_public.arn - "alb.ingress.kubernetes.io/security-groups" = aws_security_group.test_app_alb_public.id - "alb.ingress.kubernetes.io/scheme" = "internet-facing" - "alb.ingress.kubernetes.io/target-type" = "ip" - "alb.ingress.kubernetes.io/healthcheck-path" = "/healthcheck" - "alb.ingress.kubernetes.io/target-group-attributes" = "load_balancing.cross_zone.enabled=true" - } - } - - spec { - rule { - http { - path { - backend { - service { - name = kubernetes_service.app_node_port.metadata.0.name - port { - number = 8080 - } - } - } - path = "/*" - } - } - } - } - - wait_for_load_balancer = true -} - -resource "kubernetes_service" "app_node_port" { - metadata { - name = var.app_name - } - spec { - selector = { - app = var.app_name - } - port { - port = 8080 - } - type = "NodePort" - } -} - resource "aws_security_group" "test_app_alb_public" { name = "${var.app_name}-alb" description = "Allow traffic for ${var.app_name} alb-public"