Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 17 additions & 10 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,22 +1,29 @@
# Local Compose defaults. Never commit the real .env file or production values.
# Local Compose configuration. Copy to .env; never commit the real .env file.
POSTGRES_USER=admin
POSTGRES_PASSWORD=replace-with-a-local-password
POSTGRES_DB=platform
JWT_SECRET=replace-with-at-least-32-random-characters
INTERNAL_SERVICE_SECRET=replace-with-a-random-service-secret
ACCESS_TOKEN_EXPIRATION_SECONDS=900
DISPLAY_TIME_ZONE=UTC
# Services receive CACHE_REDIS_URL; set this to Redis Cloud or another
# reachable Redis deployment when local Redis is not used.
CACHE_REDIS_URL=redis://redis:6379
# Optional: defaults to the local Compose mock. Set both for a real provider.
EXCHANGE_PROVIDER_URL=http://exchange-provider-mock:8080
EXCHANGE_PROVIDER_API_KEY=local-development

# Optional immutable images for local Compose. Leave unset to use latest.
ACCOUNT_IMAGE=ghcr.io/microsserv52/account:latest
AUTH_IMAGE=ghcr.io/microsserv52/auth:latest
GATEWAY_IMAGE=ghcr.io/microsserv52/gateway:latest
ORDER_IMAGE=ghcr.io/microsserv52/order-service:latest
# Product and Exchange are external service images and must be available locally
# or in a registry accessible by Docker.
ACCOUNT_IMAGE=microsserv52-account:local
AUTH_IMAGE=microsserv52-auth:local
EXCHANGE_IMAGE=microsserv52-exchange:local
GATEWAY_IMAGE=microsserv52-gateway:local
ORDER_IMAGE=microsserv52-order:local
PRODUCT_IMAGE=microsserv52-product:local

# AWS/EKS deployment secrets (configure these as GitHub Actions secrets).
# Production EKS values are configured as GitHub Actions secrets/variables,
# not in the local .env file.
AWS_REGION=us-east-1
EKS_CLUSTER_NAME=platform
ECR_REGISTRY=123456789012.dkr.ecr.us-east-1.amazonaws.com
SPRING_DATASOURCE_URL=jdbc:postgresql://your-rds-endpoint:5432/platform
SPRING_DATASOURCE_USERNAME=platform
SPRING_DATASOURCE_PASSWORD=replace-in-github-secrets
14 changes: 12 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,19 @@ jobs:
- uses: actions/checkout@v7
- uses: actions/setup-python@v6
with:
python-version: '3.13'
python-version: "3.13"
- run: python -m unittest discover -s tests/frontend -p 'test_*.py'

infrastructure:
name: Infrastructure mock contracts
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v6
with:
python-version: "3.13"
- run: python -m unittest discover -s tests/infrastructure -p 'test_*.py'

java:
name: ${{ matrix.service }} tests and coverage
runs-on: ubuntu-latest
Expand All @@ -42,7 +52,7 @@ jobs:
- uses: actions/setup-java@v5
with:
distribution: temurin
java-version: '25'
java-version: "25"
cache: maven
- name: Install reusable Order contract
if: matrix.service == 'order-service'
Expand Down
25 changes: 22 additions & 3 deletions .github/workflows/deploy-eks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@ jobs:
- uses: actions/setup-java@v5
with:
distribution: temurin
java-version: '25'
java-version: "25"
cache: maven
- name: Verify before deployment
run: |
(cd api/order && ./mvnw --batch-mode --no-transfer-progress install -DskipTests -Dspring-boot.repackage.skip=true)
for service in account auth gateway order order-service; do
(cd "api/$service" && ./mvnw --batch-mode --no-transfer-progress verify)
done
Expand Down Expand Up @@ -59,21 +60,39 @@ jobs:
POSTGRES_PASSWORD: ${{ secrets.SPRING_DATASOURCE_PASSWORD }}
SPRING_DATASOURCE_URL: ${{ secrets.SPRING_DATASOURCE_URL }}
SPRING_DATASOURCE_USERNAME: ${{ secrets.SPRING_DATASOURCE_USERNAME }}
PRODUCT_URL: ${{ vars.PRODUCT_URL }}
EXCHANGE_URL: ${{ vars.EXCHANGE_URL }}
CACHE_REDIS_URL: ${{ secrets.CACHE_REDIS_URL }}
run: |
test -n "$PRODUCT_URL" || { echo "PRODUCT_URL repository variable is required"; exit 1; }
test -n "$EXCHANGE_URL" || { echo "EXCHANGE_URL repository variable is required"; exit 1; }
test -n "$CACHE_REDIS_URL" || { echo "CACHE_REDIS_URL repository secret is required"; exit 1; }
aws eks update-kubeconfig --region "$AWS_REGION" --name "$EKS_CLUSTER_NAME"
kubectl create namespace platform --dry-run=client -o yaml | kubectl apply -f -
kubectl -n platform create secret generic platform-secrets \
--from-literal=JWT_SECRET="$JWT_SECRET" \
--from-literal=INTERNAL_SERVICE_SECRET="$INTERNAL_SERVICE_SECRET" \
--from-literal=CACHE_REDIS_URL="$CACHE_REDIS_URL" \
--from-literal=POSTGRES_PASSWORD="$POSTGRES_PASSWORD" \
--from-literal=SPRING_DATASOURCE_URL="$SPRING_DATASOURCE_URL" \
--from-literal=SPRING_DATASOURCE_USERNAME="$SPRING_DATASOURCE_USERNAME" \
--dry-run=client -o yaml | kubectl apply -f -
kubectl apply -f k8s/platform.yaml
kubectl apply -f k8s/00-namespace-config.yaml
jq -n --arg product "$PRODUCT_URL" --arg exchange "$EXCHANGE_URL" \
'{data: {PRODUCT_URL: $product, EXCHANGE_URL: $exchange}}' > "$RUNNER_TEMP/platform-config-patch.json"
kubectl -n platform patch configmap platform-config --type merge \
--patch-file "$RUNNER_TEMP/platform-config-patch.json"
kubectl apply -f k8s/20-account.yaml
kubectl -n platform set image deployment/account account="$REGISTRY/account:$TAG"
kubectl apply -f k8s/30-auth.yaml
kubectl -n platform set image deployment/auth auth="$REGISTRY/auth:$TAG"
kubectl -n platform set image deployment/gateway gateway="$REGISTRY/gateway:$TAG"
kubectl apply -f k8s/40-order.yaml
kubectl -n platform set image deployment/order order="$REGISTRY/order:$TAG"
kubectl apply -f k8s/50-gateway.yaml
kubectl -n platform set image deployment/gateway gateway="$REGISTRY/gateway:$TAG"
kubectl apply -f k8s/60-ingress.yaml
kubectl apply -f k8s/70-hpa.yaml
kubectl apply -f k8s/71-pdb.yaml
kubectl -n platform rollout status deployment/account --timeout=180s
kubectl -n platform rollout status deployment/auth --timeout=180s
kubectl -n platform rollout status deployment/gateway --timeout=180s
Expand Down
79 changes: 60 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,43 @@ Open `http://localhost:8081/`, `/store/` or `/admin/`. This separate local-only
mode visibly labels disposable API fixtures. See the [local testing guide](docs/frontend/delivery.md)
for fixture inputs, real-service mode and verification limits.

## Which environment should I run?

| Environment | Purpose | Database | Exchange provider | Entry point |
| -------------- | ----------------------------------------- | ----------------------- | ----------------------------------- | -------------------------------- |
| Docker Compose | Local development and integration testing | PostgreSQL container | Local deterministic mock by default | `http://localhost:8080` |
| MicroK8s | Local Kubernetes validation | `k8s/10-database.yaml` | External Exchange service URL | Ingress address |
| AWS EKS | Production backend | External PostgreSQL/RDS | External Exchange service URL | Production Ingress/load balancer |

Compose and Kubernetes are separate runtime environments. Do not use the
Compose mock or its local defaults in EKS.

### Run local Compose

```bash
cp .env.example .env
docker compose up --build -d
docker compose ps
docker compose down
```

Product and Exchange images must be available locally or in a registry because
their source repositories are not included here. Compose supplies PostgreSQL,
the checked-out Java services, and the local Exchange provider mock.

### Run local MicroK8s

Follow the [Kubernetes runbook](k8s/README.md). It requires MicroK8s with the
Nginx ingress addon, a `platform-secrets` Secret, reachable Product/Exchange
services, and local images loaded into MicroK8s.

### Run production EKS

Configure the required GitHub Actions secrets and `PRODUCT_URL`/
`EXCHANGE_URL` repository variables, then run the manual **Deploy to AWS EKS**
workflow. EKS uses the production database URL and does not deploy the local
PostgreSQL manifest.

## CI/CD and environment variables

Each service repository owns lint, format, tests, JaCoCo quality gates, CodeQL,
Expand All @@ -58,28 +95,32 @@ real `.env` file or any secret values.
Configure repository secrets in **Settings → Secrets and variables → Actions**
for each repository:

| Repository | Required secrets |
| --- | --- |
| `Microsserv52/platform` | `AWS_REGION`, `AWS_DEPLOY_ROLE_ARN`, `EKS_CLUSTER_NAME`, `JWT_SECRET`, `INTERNAL_SERVICE_SECRET`, `SPRING_DATASOURCE_URL`, `SPRING_DATASOURCE_USERNAME`, `SPRING_DATASOURCE_PASSWORD` |
| `Microsserv52/account` | `JWT_SECRET`, `INTERNAL_SERVICE_SECRET`, `SPRING_DATASOURCE_URL`, `SPRING_DATASOURCE_USERNAME`, `SPRING_DATASOURCE_PASSWORD` |
| `Microsserv52/auth` | `JWT_SECRET`, `INTERNAL_SERVICE_SECRET`, `ACCOUNT_URL`, `ACCESS_TOKEN_EXPIRATION_SECONDS` |
| `Microsserv52/gateway` | `JWT_SECRET`, `AUTH_URL`, `ACCOUNT_URL`, `ORDER_URL`, `PRODUCT_URL`, `EXCHANGE_URL` |
| `Microsserv52/order-service` | `JWT_SECRET`, `SPRING_DATASOURCE_URL`, `SPRING_DATASOURCE_USERNAME`, `SPRING_DATASOURCE_PASSWORD`, `PRODUCT_URL` |
| `Microsserv52/order` | No runtime secrets; this repository contains the reusable API contract. |

For local Compose, put the values in the root `.env`. For MicroK8s, create
the `platform-secrets` Kubernetes secret as described in
[`k8s/README.md`](k8s/README.md). For EKS, use an RDS PostgreSQL URL and add
the values to the **platform** repository's Actions secrets.
| Repository | Required secrets |
| ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Microsserv52/platform` | `AWS_REGION`, `AWS_DEPLOY_ROLE_ARN`, `EKS_CLUSTER_NAME`, `JWT_SECRET`, `INTERNAL_SERVICE_SECRET`, `SPRING_DATASOURCE_URL`, `SPRING_DATASOURCE_USERNAME`, `SPRING_DATASOURCE_PASSWORD`, `CACHE_REDIS_URL` |
| `Microsserv52/account` | `JWT_SECRET`, `INTERNAL_SERVICE_SECRET`, `SPRING_DATASOURCE_URL`, `SPRING_DATASOURCE_USERNAME`, `SPRING_DATASOURCE_PASSWORD` |
| `Microsserv52/auth` | `JWT_SECRET`, `INTERNAL_SERVICE_SECRET`, `ACCOUNT_URL`, `ACCESS_TOKEN_EXPIRATION_SECONDS` |
| `Microsserv52/gateway` | `JWT_SECRET`, `AUTH_URL`, `ACCOUNT_URL`, `ORDER_URL`, `PRODUCT_URL`, `EXCHANGE_URL` |
| `Microsserv52/order-service` | `JWT_SECRET`, `SPRING_DATASOURCE_URL`, `SPRING_DATASOURCE_USERNAME`, `SPRING_DATASOURCE_PASSWORD`, `PRODUCT_URL` |
| `Microsserv52/order` | No runtime secrets; this repository contains the reusable API contract. |

For local Compose, copy `.env.example` to `.env`; Compose provisions the local
PostgreSQL database and defaults internal service settings. Product and
Exchange images plus Exchange provider settings are the only external local
inputs. For MicroK8s, create the `platform-secrets` Kubernetes secret as
described in [`k8s/README.md`](k8s/README.md). For EKS, use an RDS PostgreSQL
URL, add secret values to the **platform** repository's Actions secrets, and
configure `PRODUCT_URL` and `EXCHANGE_URL` as Actions variables and
`CACHE_REDIS_URL` as an Actions secret.

### Environment variable locations

| Variable type | Local development | GitHub Actions | Kubernetes/EKS |
| --- | --- | --- | --- |
| Service URLs and image tags | Root `.env` | Repository variables or workflow defaults | ConfigMap/deployment values |
| JWT and internal credentials | Root `.env` (never commit) | Repository **Secrets** | `platform-secrets` |
| Database URL and credentials | Root `.env` | Platform or service **Secrets** | `platform-secrets` / AWS Secrets Manager |
| AWS deployment values | Not required | Platform **Secrets** (`AWS_REGION`, `AWS_DEPLOY_ROLE_ARN`, `EKS_CLUSTER_NAME`) | IAM/OIDC and cluster configuration |
| Variable type | Local development | GitHub Actions | Kubernetes/EKS |
| ---------------------------- | -------------------------- | ------------------------------------------------------------------------------ | ---------------------------------------- |
| Service URLs and image tags | Root `.env` | Repository variables or workflow defaults | ConfigMap/deployment values |
| JWT and internal credentials | Root `.env` (never commit) | Repository **Secrets** | `platform-secrets` |
| Database URL and credentials | Root `.env` | Platform or service **Secrets** | `platform-secrets` / AWS Secrets Manager |
| AWS deployment values | Not required | Platform **Secrets** (`AWS_REGION`, `AWS_DEPLOY_ROLE_ARN`, `EKS_CLUSTER_NAME`) | IAM/OIDC and cluster configuration |

The service workflows need `packages: write` to publish GHCR images. The
platform workflow needs `packages: read` to pull them. Keep GHCR packages
Expand Down
Loading
Loading