towerops/lib/towerops/application.ex
Graham McIntire 9fcf19b304
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.
2026-01-17 11:14:15 -06:00

89 lines
2.6 KiB
Elixir

defmodule Towerops.Application do
# See https://hexdocs.pm/elixir/Application.html
# for more information on OTP Applications
@moduledoc false
use Application
# Capture the build timestamp at compile time (fallback for development)
@build_timestamp DateTime.utc_now()
@impl true
def start(_type, _args) do
# Run migrations on startup (Ecto handles locking for concurrent runs)
_ =
if Application.get_env(:towerops, Towerops.Repo)[:database] != "towerops_test" do
Towerops.Release.migrate()
end
topologies = Application.get_env(:libcluster, :topologies, [])
children = [
{Cluster.Supervisor, [topologies, [name: Towerops.ClusterSupervisor]]},
ToweropsWeb.Telemetry,
Towerops.Repo,
{DNSCluster, query: Application.get_env(:towerops, :dns_cluster_query) || :ignore},
{Phoenix.PubSub, name: Towerops.PubSub},
# Start event logger (subscribes to PubSub)
Towerops.Equipment.EventLogger,
# Start monitoring supervisor
Towerops.Monitoring.Supervisor,
# Start a worker by calling: Towerops.Worker.start_link(arg)
# {Towerops.Worker, arg},
# Start to serve requests, typically the last entry
ToweropsWeb.Endpoint
]
# See https://hexdocs.pm/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: Towerops.Supervisor]
Supervisor.start_link(children, opts)
end
# Tell Phoenix to update the endpoint configuration
# whenever the application is updated.
@impl true
def config_change(changed, _new, removed) do
ToweropsWeb.Endpoint.config_change(changed, removed)
:ok
end
@doc """
Returns the deployment timestamp.
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
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