From 9fcf19b304fa842377bdbcdbb01fd739297593d5 Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Sat, 17 Jan 2026 11:14:15 -0600 Subject: [PATCH] Use Kubernetes pod creation timestamp for deploy time in footer - Read DEPLOY_TIMESTAMP environment variable for actual deploy time - Fall back to compile time in development when env var not set - Add Kubernetes Downward API to inject metadata.creationTimestamp - Update k8s/deployment.yaml to set DEPLOY_TIMESTAMP env var - Document deployment timestamp mechanism in k8s/README.md This ensures the footer shows when the pod was deployed to the cluster, not when the Docker image was built in CI/CD. --- k8s/README.md | 14 +++++++++++++ k8s/deployment.yaml | 4 ++++ lib/towerops/application.ex | 40 ++++++++++++++++++++++++++++++++----- 3 files changed, 53 insertions(+), 5 deletions(-) diff --git a/k8s/README.md b/k8s/README.md index 11b17911..dd42794d 100644 --- a/k8s/README.md +++ b/k8s/README.md @@ -12,6 +12,20 @@ Required secrets in the `towerops` namespace: For local development, the project root `.envrc` is used by direnv. +## Deployment Timestamp + +The application footer displays the deployment timestamp to track when the current version was deployed. This is automatically set using the Kubernetes Downward API: + +```yaml +env: + - name: DEPLOY_TIMESTAMP + valueFrom: + fieldRef: + fieldPath: metadata.creationTimestamp +``` + +The pod's creation timestamp is injected as an environment variable and displayed in the footer as "Last deployed X ago ยท YYYY-MM-DD HH:MM:SS UTC". + ## Deploying Apply all resources using kustomize: diff --git a/k8s/deployment.yaml b/k8s/deployment.yaml index b8bcacb2..b21861ae 100644 --- a/k8s/deployment.yaml +++ b/k8s/deployment.yaml @@ -55,6 +55,10 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace + - name: DEPLOY_TIMESTAMP + valueFrom: + fieldRef: + fieldPath: metadata.creationTimestamp - name: RELEASE_DISTRIBUTION value: "name" - name: RELEASE_NODE diff --git a/lib/towerops/application.ex b/lib/towerops/application.ex index ac47ddd6..ed9ceec8 100644 --- a/lib/towerops/application.ex +++ b/lib/towerops/application.ex @@ -5,7 +5,7 @@ defmodule Towerops.Application do use Application - # Capture the build/deploy timestamp at compile time + # Capture the build timestamp at compile time (fallback for development) @build_timestamp DateTime.utc_now() @impl true @@ -49,11 +49,41 @@ defmodule Towerops.Application do end @doc """ - Returns the build/deploy timestamp. + Returns the deployment timestamp. - This timestamp is captured at compile time and represents when the - application was last built/deployed. + In production (Kubernetes), this reads from the DEPLOY_TIMESTAMP environment + variable which should be set during deployment. Falls back to compile time + in development. + + Set DEPLOY_TIMESTAMP in your Kubernetes deployment: + ```yaml + env: + - name: DEPLOY_TIMESTAMP + value: "2026-01-17T12:34:56Z" + ``` + + Or use a downward API to inject the pod creation time: + ```yaml + env: + - name: DEPLOY_TIMESTAMP + valueFrom: + fieldRef: + fieldPath: metadata.creationTimestamp + ``` """ @spec build_timestamp() :: DateTime.t() - def build_timestamp, do: @build_timestamp + def build_timestamp do + case System.get_env("DEPLOY_TIMESTAMP") do + nil -> + # Development/fallback: use compile time + @build_timestamp + + timestamp_string -> + # Production: parse from environment variable + case DateTime.from_iso8601(timestamp_string) do + {:ok, datetime, _offset} -> datetime + {:error, _} -> @build_timestamp + end + end + end end