feat: Azure Container Apps deployment for tools/httpserver - #306
Merged
Merged
Conversation
…rin's own store subscriptions, the customer-tenant map, webhook idempotency keys, and enterprise inquiries lived only in in-process maps, so a restart or redeploy silently wiped every paying customer's subscription. Back DefaultBillingService with a new JoltrinBillingStore (this repo's own embedded B-Tree engine) instead of adding an external dependency; the in-memory maps stay as a read cache hydrated from the store at startup.
- Point the billing store at the server's own data path so it survives restarts, and isolate httpserver tests from it via TestMain (they were about to leak billing state across separate go test runs through a relative _billing path in the source tree). - Wire the existing, previously-unused governance.TierRateLimiter into the checkout/portal endpoints, keyed by client IP, so a flood can't run up the Stripe API bill. The webhook endpoint stays unthrottled by IP deliberately: Stripe's webhook senders share IP ranges, and signature verification plus the 1MB body cap already guard it. - Retry Stripe API calls with bounded backoff on 429/5xx so a single Stripe blip doesn't fail a customer's checkout outright; 4xx errors other than 429 still fail immediately.
Resource group scoped stack: Container Registry (Basic, admin disabled, pull via managed identity only), Key Vault (RBAC, holds the Stripe secret/webhook keys), Log Analytics (30-day retention), a Container Apps environment, the Container App itself, a monthly cost budget with 50/75/90/100% alerts, and Azure Monitor metric alerts on CPU/memory/ restart-loop. Pinned to minReplicas=maxReplicas=1: joltrin's embedded B-Tree engine has no documented multi-process write-safety guarantee, and this deployment optimizes for lowest cost over horizontal scale (it does ship a Redis-backed distributed lock in adapters/redis that would make scaling safe, deliberately not wired in here). Alerting uses native Azure Monitor metric alerts rather than Managed Prometheus/Grafana for the same cost reason.
Deploys only on push to master (never on a PR, since it provisions real billed infra), authenticated via GitHub OIDC federated credentials rather than a stored client secret. Flow: deploy the Bicep stack first (it has a public placeholder image so it stands up before a real image exists), build and Trivy-scan the runtime image using the same critical/high gate as security.yml, push it to the now-existing ACR, then roll the Container App onto it with az containerapp update, which blocks until the new revision's probes pass.
Mirrors the existing security-scan target's style: validate Bicep syntax locally when az is installed (skip with a hint otherwise), plus build/vet/test the billing persistence code path and an optional az deployment what-if dry run, so problems surface before a push instead of after deploy-azure.yml runs.
…eaks The AcrPull and Key Vault Secrets User role definition IDs added in infra/azure/modules/*.bicep are public, Microsoft-documented constants, not credentials, but tripped the generic-api-key high-entropy rule.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Deploys
tools/httpserver(the Data Manager UI + commercial Stripe billing surface) to Azure Container Apps, built around one constraint: keep it least-cost, since this runs on a personal account.DefaultBillingServicewith a newgovernance.JoltrinBillingStore— this repo's own embedded B-Tree engine — instead of adding Redis or another external dependency. SeeTestDefaultBillingService_SurvivesRestartfor the regression test.governance.TierRateLimiterinto the checkout/portal endpoints (keyed by client IP), and added bounded retry-with-backoff on Stripe 429/5xx responses.infra/azure/(Container Registry, Key Vault, Log Analytics, Container Apps environment/app, cost budget with 50/75/90/100% alerts, Azure Monitor metric alerts)..github/workflows/deploy-azure.yml, OIDC-authenticated (no stored client secret), gated by the same Trivy critical/high check assecurity.yml.make deploy-check/make lint-infra.Architecture decisions (made with the repo owner before implementing)
minReplicas = maxReplicas = 1. joltrin's embedded engine has no documented multi-process write-safety guarantee. It does ship a Redis-backed distributed lock (adapters/redis) that would make horizontal scaling safe, but that's a new billed Azure resource, so it's deliberately not wired in — least cost over replica fan-out.Release sequence
This merges to
masterfirst;v5.5.0gets tagged after this is in and CI is green, covering both the DevSecOps pipeline (already onmastervia #305) and this Azure Container Apps work together.Test plan
go build ./...go vet ./governance/... ./tools/httpserver/...go test ./governance/... ./tools/httpserver/... -count=1(full suite, including new persistence/retry/rate-limit tests)make deploy-check(Bicep syntax validated in CI whereazis available; YAML validated locally)deploy-azure.ymlrun on merge (provisions real Azure infra — needsAZURE_CLIENT_ID/AZURE_TENANT_ID/AZURE_SUBSCRIPTION_IDrepo variables andSTRIPE_SECRET_KEY/STRIPE_WEBHOOK_SECRET/ALERT_EMAILrepo secrets configured first; seeinfra/azure/README.md)