Fix deployment timestamp always showing 'less than a minute ago' in k8s
Previously, the deployment timestamp was set to DateTime.utc_now() when the application started, which meant it would always show as recent in k8s environments where pods are recreated on each deployment. This commit: - Updates Release.init() to check for DEPLOYED_AT environment variable - Falls back to current time if env var is not set (backwards compatible) - Adds proper ISO8601 timestamp parsing with error handling - Updates GitHub Actions deploy workflow to set DEPLOYED_AT during deployment - Creates k8s patch file for future manual deployments The deployment timestamp will now persist across pod restarts in k8s, showing the actual deployment time rather than pod startup time. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
ece5c82e99
commit
7e4bbbaff5
6 changed files with 77 additions and 23 deletions
27
.github/workflows/deploy.yml
vendored
27
.github/workflows/deploy.yml
vendored
|
|
@ -74,7 +74,32 @@ jobs:
|
|||
|
||||
- name: Deploy to K3s
|
||||
run: |
|
||||
# Get current timestamp in ISO8601 format
|
||||
DEPLOYED_AT=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
||||
|
||||
# Create a temporary patch file with the deployment timestamp
|
||||
cat > /tmp/deployed-at-patch.yaml <<EOF
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: aprs
|
||||
namespace: aprs
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: aprs
|
||||
env:
|
||||
- name: DEPLOYED_AT
|
||||
value: "$DEPLOYED_AT"
|
||||
EOF
|
||||
|
||||
# Apply the patch to set the deployment timestamp
|
||||
kubectl patch statefulset aprs -n aprs --patch-file=/tmp/deployed-at-patch.yaml
|
||||
|
||||
# Update the image
|
||||
kubectl set image statefulset/aprs aprs=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest -n aprs
|
||||
|
||||
# Force a rollout to ensure latest image is pulled
|
||||
kubectl rollout restart statefulset/aprs -n aprs
|
||||
echo "Deployment initiated - not waiting for rollout to complete"
|
||||
echo "Deployment initiated with timestamp $DEPLOYED_AT - not waiting for rollout to complete"
|
||||
|
|
|
|||
|
|
@ -57,9 +57,8 @@ ENV LANG=en_US.UTF-8 LANGUAGE=en_US:en LC_ALL=en_US.UTF-8
|
|||
|
||||
WORKDIR /app
|
||||
|
||||
# Create deployment timestamp before switching users
|
||||
RUN date -u +"%Y-%m-%dT%H:%M:%SZ" > /app/deployed_at.txt && \
|
||||
chown elixir:root /app/deployed_at.txt
|
||||
# Ensure proper ownership
|
||||
RUN chown -R elixir:root /app
|
||||
|
||||
COPY --from=builder --chown=elixir:root /app/_build/prod/rel/aprsme ./
|
||||
|
||||
|
|
|
|||
13
k8s/statefulset-deployed-at-patch.yaml
Normal file
13
k8s/statefulset-deployed-at-patch.yaml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: aprs
|
||||
namespace: aprs
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: aprs
|
||||
env:
|
||||
- name: DEPLOYED_AT
|
||||
value: "TIMESTAMP_PLACEHOLDER"
|
||||
|
|
@ -9,6 +9,9 @@ defmodule Aprsme.Application do
|
|||
|
||||
@impl true
|
||||
def start(_type, _args) do
|
||||
# Initialize deployment timestamp
|
||||
Aprsme.Release.init()
|
||||
|
||||
# Run migrations on startup
|
||||
migrate()
|
||||
|
||||
|
|
|
|||
|
|
@ -75,10 +75,29 @@ defmodule Aprsme.Release do
|
|||
This is called during application startup.
|
||||
"""
|
||||
def init do
|
||||
# Read deployment timestamp from file
|
||||
deployed_at = read_deployment_timestamp()
|
||||
# Check if deployment timestamp is provided via environment variable
|
||||
# This allows k8s deployments to set a persistent timestamp
|
||||
deployed_at =
|
||||
case System.get_env("DEPLOYED_AT") do
|
||||
nil ->
|
||||
# Fallback to current time for local development or non-k8s deployments
|
||||
DateTime.utc_now()
|
||||
|
||||
# Add to application config
|
||||
timestamp_str ->
|
||||
# Parse the ISO8601 timestamp from environment variable
|
||||
case DateTime.from_iso8601(timestamp_str) do
|
||||
{:ok, datetime, _offset} ->
|
||||
datetime
|
||||
|
||||
{:error, _} ->
|
||||
require Logger
|
||||
|
||||
Logger.warning("Invalid DEPLOYED_AT timestamp: #{timestamp_str}, using current time")
|
||||
DateTime.utc_now()
|
||||
end
|
||||
end
|
||||
|
||||
# Store in application config
|
||||
Application.put_env(:aprsme, :deployed_at, deployed_at)
|
||||
|
||||
deployed_at
|
||||
|
|
@ -88,7 +107,16 @@ defmodule Aprsme.Release do
|
|||
Get the deployment timestamp.
|
||||
"""
|
||||
def deployed_at do
|
||||
Application.get_env(:aprsme, :deployed_at) || DateTime.utc_now()
|
||||
case Application.get_env(:aprsme, :deployed_at) do
|
||||
nil ->
|
||||
# If not initialized yet, initialize now
|
||||
timestamp = DateTime.utc_now()
|
||||
Application.put_env(:aprsme, :deployed_at, timestamp)
|
||||
timestamp
|
||||
|
||||
timestamp ->
|
||||
timestamp
|
||||
end
|
||||
end
|
||||
|
||||
defp run_migrations_with_timeout(timeout) do
|
||||
|
|
@ -113,18 +141,4 @@ defmodule Aprsme.Release do
|
|||
timeout: timeout
|
||||
)
|
||||
end
|
||||
|
||||
defp read_deployment_timestamp do
|
||||
case File.read("/app/deployed_at.txt") do
|
||||
{:ok, timestamp} ->
|
||||
case DateTime.from_iso8601(String.trim(timestamp)) do
|
||||
{:ok, datetime, _} -> datetime
|
||||
_ -> DateTime.utc_now()
|
||||
end
|
||||
|
||||
_ ->
|
||||
# Fallback to current time if file doesn't exist
|
||||
DateTime.utc_now()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -671,7 +671,7 @@ defmodule AprsmeWeb.MapLive.Index do
|
|||
end)
|
||||
|> Enum.filter(& &1)
|
||||
|
||||
socket =
|
||||
socket =
|
||||
if Enum.any?(station_packets) do
|
||||
# Build packet data for the RF path stations
|
||||
packet_data_list = DataBuilder.build_packet_data_list(station_packets)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue