fix(mobile-infra): render push grant lifetimes as decimal in chart 0.3.2 (#7820)

A valid 30-day push gateway grant lifetime could render in scientific
notation (e.g. `2.592e+06`) in the chart, which the gateway binary does
not accept.

Always ormat the integer as decimal text, preserving the strict gateway
parser.

### Testing

Added regression coverage that feeds actual Helm-rendered environment
values into the production config parser. Covers defaults, values files,
command-line overrides, exact lifetime boundaries, and invalid input
rejection. The default case failed before the fix and passes afterward.

Signed-off-by: Tom Brow <tomb@block.xyz>
This commit is contained in:
Tom Brow
2026-09-23 08:47:21 -07:00
committed by GitHub
parent 0cc63fe3e6
commit 0ef7a2222e
7 changed files with 84 additions and 4 deletions

View File

@@ -3,6 +3,6 @@ apiVersion: v2
# branches (see docs/push-gateway-deployment.md, "Gateway chart release").
name: buzz-push-gateway
description: Public capability-gated APNs last-hop gateway for Buzz
version: 0.3.1
version: 0.3.2
appVersion: "0.1.0"
type: application

View File

@@ -33,7 +33,8 @@ spec:
- { name: BUZZ_PUSH_BIND_ADDR, value: "0.0.0.0:8080" }
- { name: BUZZ_PUSH_HEALTH_ADDR, value: "0.0.0.0:8081" }
- { name: BUZZ_PUSH_GATEWAY_ORIGIN, value: {{ required "gatewayOrigin is required" .Values.gatewayOrigin | quote }} }
- { name: BUZZ_PUSH_MAX_GRANT_LIFETIME_SECONDS, value: {{ .Values.maxGrantLifetimeSeconds | quote }} }
{{/* Schema bounds this integer; normalize YAML numbers before quote can emit scientific notation. */}}
- { name: BUZZ_PUSH_MAX_GRANT_LIFETIME_SECONDS, value: {{ .Values.maxGrantLifetimeSeconds | int64 | quote }} }
- { name: BUZZ_PUSH_APP_ATTEST_ROOT_CERT_PATH, value: /run/buzz/app-attest/root.pem }
- { name: BUZZ_PUSH_DOGFOOD_APP_ATTEST_APP_ID, value: {{ .Values.profiles.dogfood.appAttestAppId | quote }} }
- { name: BUZZ_PUSH_DOGFOOD_APNS_TOPIC, value: {{ .Values.profiles.dogfood.apnsTopic | quote }} }

View File

@@ -0,0 +1,46 @@
#!/usr/bin/env bash
set -euo pipefail
fixture=$(mktemp)
trap 'rm -f "$fixture"' EXIT
# Use real Helm renders, including YAML values (not just --set, which can
# preserve a different numeric type). No deployment or real secrets are needed.
env -u GEM_HOME -u GEM_PATH -u RUBYLIB -u RUBYOPT ruby -rjson -ryaml -ropen3 \
- "$fixture" <<'RUBY'
chart = 'deploy/charts/buzz-push-gateway'
command = ['helm', 'template', 'push', chart, '--set', 'gatewayOrigin=https://push.example']
cases = []
render = lambda do |label, expected, args, values|
output, error, status = Open3.capture3(*command, *args, stdin_data: values)
raise "#{label}: Helm failed: #{error}" unless status.success?
deployment = YAML.load_stream(output).compact.find { |x| x['kind'] == 'Deployment' }
entries = deployment.fetch('spec').fetch('template').fetch('spec').fetch('containers')[0].fetch('env')
# Secret references are intentionally excluded; Config's fixture supplies
# synthetic keyrings and database configuration in memory.
literal_env = entries.select { |entry| entry.key?('value') }.to_h do |entry|
value = entry.fetch('value')
raise "#{label}: #{entry['name']} is not a string" unless value.is_a?(String)
[entry.fetch('name'), value]
end
raise "#{label}: lifetime missing" unless literal_env.key?('BUZZ_PUSH_MAX_GRANT_LIFETIME_SECONDS')
cases << [label, expected, literal_env]
end
render.call('default', 2592000, [], '')
[1, 2592000, 2592001, 31536000].each do |seconds|
render.call("values #{seconds}", seconds, ['-f', '-'], "maxGrantLifetimeSeconds: #{seconds}\n")
render.call("set #{seconds}", seconds, ['--set', "maxGrantLifetimeSeconds=#{seconds}"], '')
end
# Integer conversion must never hide invalid inputs. Helm's schema remains
# authoritative, including rejecting strings that merely look numeric.
['0', '-1', '31536001', '2592000.5', '"2592000"', 'true'].each do |value|
_, error, status = Open3.capture3(*command, '-f', '-', stdin_data: "maxGrantLifetimeSeconds: #{value}\n")
unless !status.success? && error.include?('maxGrantLifetimeSeconds')
raise "expected schema rejection for #{value}: #{error}"
end
end
File.write(ARGV.fetch(0), JSON.generate(cases))
RUBY
BUZZ_TEST_HELM_ENV_FILE="$fixture" cargo test -p buzz-push-gateway --lib \
config::tests::helm_rendered_grant_lifetimes -- --ignored --exact

View File

@@ -1,5 +1,6 @@
#!/usr/bin/env bash
set -euo pipefail
bash deploy/charts/buzz-push-gateway/tests/grant-lifetime.sh
out=$(mktemp); production_out=$(mktemp); route_out=$(mktemp); datadog_out=$(mktemp)
trap 'rm -f "$out" "$production_out" "$route_out" "$datadog_out" "${monitoring_out:-}"' EXIT
gateway_origin_arg=(--set 'gatewayOrigin=https://push.example')