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
48 changes: 37 additions & 11 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,18 @@ name: CI
on:
push:
branches:
- "**"
- main
tags:
- "v*"
pull_request:
branches:
- main
workflow_dispatch:

concurrency:
group: ci-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
tests:
name: Python ${{ matrix.python-version }} on ${{ matrix.os }}
Expand Down Expand Up @@ -55,29 +63,47 @@ jobs:
docker-build-push:
name: Build & Push Docker Image
runs-on: ubuntu-latest
if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/'))
needs: tests
permissions:
contents: 'read'
id-token: 'write'
env:
ACR_NAME: gabby
REGISTRY: gabby.azurecr.io
IMAGE_NAME: python-dsa

steps:
- name: Checkout
uses: actions/checkout@v4

- id: auth
uses: google-github-actions/auth@v2
- name: Azure Login
uses: azure/login@v2
with:
workload_identity_provider: projects/316518955652/locations/global/workloadIdentityPools/github-pool/providers/github-oidc
service_account: kame-house-oidc@kame-457417.iam.gserviceaccount.com
create_credentials_file: true
export_environment_variables: true
creds: ${{ secrets.AZURE_CREDENTIALS }}

- name: Login to Azure Container Registry
run: az acr login --name $ACR_NAME

- name: Configure Docker for Artifact Registry
run: gcloud auth configure-docker us-central1-docker.pkg.dev --quiet
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=tag
type=sha
type=raw,value=latest,enable={{is_default_branch}}

- name: Build and Push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: us-central1-docker.pkg.dev/kame-457417/python-dsa/python-dsa:latest
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
51 changes: 30 additions & 21 deletions docs/GITHUB_ACTIONS_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
# GitHub Actions Workflow Notes

## CI Workflow Overview

The repository uses a GitHub Actions workflow defined in [`.github/workflows/ci.yml`](../.github/workflows/ci.yml).

### Triggers
- Runs on **all branches** (`push`).
- Runs on **pull requests**.
- Can be triggered manually via **workflow_dispatch**.

- `push` on the **`main` branch** and version tags (`v*`).
- `pull_request` targeting **`main`**.
- Manual runs via **workflow_dispatch** when needed.

> The workflow also uses `concurrency` to ensure only the latest run per ref/PR stays active, which keeps pending check counts low.

### Jobs

1. **Tests**
- Runs on Python 3.10, 3.11, and 3.12.
- Installs dependencies with pip.
Expand All @@ -17,28 +22,32 @@ The repository uses a GitHub Actions workflow defined in [`.github/workflows/ci.
- Uploads `coverage.xml` as an artifact.

2. **Docker Build & Push**
- Runs after tests succeed.
- Authenticates to Google Cloud using **Workload Identity Federation (WIF)**:
- Workload Identity Provider:
`projects/316518955652/locations/global/workloadIdentityPools/github-pool/providers/github-oidc`
- Service Account:
`kame-house-oidc@kame-457417.iam.gserviceaccount.com`
- Configures Docker for Artifact Registry.
- Builds and pushes the Docker image to:
`us-central1-docker.pkg.dev/kame-457417/python-dsa/python-dsa:latest`

### Workload Identity Federation (WIF) Setup
- The service account is bound with:

- Runs only for `push` events on `main` (or tagged releases) after tests succeed.
- Authenticates to Azure using [`azure/login`](https://github.com/Azure/login) with the `AZURE_CREDENTIALS` secret (JSON output from `az ad sp create-for-rbac --sdk-auth`).
- Logs in to Azure Container Registry (ACR) `gabby`.
- Uses `docker/setup-buildx-action` + `docker/metadata-action` for reproducible builds with cache reuse.
- Builds and pushes the Docker image to `gabby.azurecr.io/python-dsa` with tags: `latest` (default branch), branch/tag refs, and the commit SHA.

### Azure Service Principal Setup

- **Recommended**: configure GitHub OIDC federation with Azure AD so the workflow can exchange short-lived tokens without storing long-lived client secrets. This avoids the deprecated `--sdk-auth` flow and removes the need for the `AZURE_CREDENTIALS` secret. Follow the [federated credential guide](https://learn.microsoft.com/azure/active-directory/develop/workload-identity-federation-create-trust-github) to create an app registration, add a federated credential for your repo, and assign the `AcrPush` role to the app.

- **Legacy fallback**: if you must keep using a client secret, create (or reuse) a service principal with `AcrPush` permissions on the `gabby` registry and store the JSON output as the `AZURE_CREDENTIALS` secret. Note that `--sdk-auth` is deprecated and may be removed in future CLI versions, so migrate when possible.

```bash
gcloud iam service-accounts add-iam-policy-binding \
${{ GCP_SERVICE_ACCOUNT }} \
--role="roles/iam.workloadIdentityUser" \
--member="principalSet://iam.googleapis.com/projects/${{ GCP_PROJECT_NUMBER }}/locations/global/workloadIdentityPools/${{ GCP_WIF_POOL }}/attribute.repository/ianlintner/python_dsa"
az ad sp create-for-rbac \
--name python-dsa-github-ci \
--role AcrPush \
--scopes /subscriptions/79307c77-54c3-4738-be2a-dc96da7464d9/resourceGroups/nekoc/providers/Microsoft.ContainerRegistry/registries/gabby \
--sdk-auth
```

### Notes
- If you encounter `unauthorized_client` errors, check the **attribute condition** on the WIF provider in GCP.
- Ensure the service account has the correct roles (e.g., `roles/artifactregistry.writer`).

- If you encounter `AADSTS700016`/`invalid_client` errors, confirm the JSON in `AZURE_CREDENTIALS` matches the service principal used for `azure/login`.
- The service principal must have at least `AcrPush` on the target registry.
- Use `workflow_dispatch` for manual runs:

```bash
gh workflow run CI --ref main
11 changes: 0 additions & 11 deletions k8s/base/auth.yaml

This file was deleted.

32 changes: 0 additions & 32 deletions k8s/base/deployment.yaml

This file was deleted.

17 changes: 0 additions & 17 deletions k8s/base/dns.yaml

This file was deleted.

13 changes: 0 additions & 13 deletions k8s/base/istio-certificate.yaml

This file was deleted.

12 changes: 0 additions & 12 deletions k8s/base/kustomization.yaml

This file was deleted.

15 changes: 0 additions & 15 deletions k8s/base/service.yaml

This file was deleted.

6 changes: 0 additions & 6 deletions k8s/base/serviceaccount.yaml

This file was deleted.

77 changes: 77 additions & 0 deletions k8s/overlays/azure/app.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: python-dsa-workload-identity
labels:
app: python-dsa
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: python-dsa
labels:
app: python-dsa
spec:
replicas: 1
selector:
matchLabels:
app: python-dsa
template:
metadata:
labels:
app: python-dsa
annotations:
sidecar.istio.io/inject: "true"
spec:
serviceAccountName: python-dsa-workload-identity
containers:
- name: python-dsa
image: gabby.azurecr.io/python-dsa:latest
imagePullPolicy: Always
ports:
- containerPort: 5000
env:
- name: FLASK_ENV
value: production
- name: PYTHONUNBUFFERED
value: "1"
- name: GUNICORN_WORKERS
value: "4"
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "512Mi"
---
apiVersion: v1
kind: Service
metadata:
name: python-dsa
labels:
app: python-dsa
spec:
selector:
app: python-dsa
ports:
- protocol: TCP
port: 80
targetPort: 5000
type: ClusterIP
---
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: python-dsa-policy
namespace: default
spec:
selector:
matchLabels:
app: python-dsa
action: ALLOW
rules:
- to:
- operation:
methods:
- "*"
21 changes: 21 additions & 0 deletions k8s/overlays/azure/certificate-dsa-cat-herding.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: python-dsa-tls-cert
namespace: default
spec:
secretName: python-dsa-tls-cert
dnsNames:
- dsa.cat-herding.net
# Optionally include legacy host if you want a combined cert:
# - dsa.hugecat.net
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
privateKey:
rotationPolicy: Always
algorithm: RSA
size: 2048
usages:
- digital signature
- key encipherment
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
apiVersion: networking.istio.io/v1beta1
apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
name: python-dsa-gateway
namespace: prod
spec:
selector:
istio: ingressgateway
istio: aks-istio-ingressgateway-external
servers:
- port:
number: 443
name: https
protocol: HTTPS
hosts:
- "dsa.hugecat.net"
- dsa.cat-herding.net
tls:
mode: SIMPLE
credentialName: python-dsa-tls-cert
Expand All @@ -21,4 +20,4 @@ spec:
name: http
protocol: HTTP
hosts:
- "dsa.hugecat.net"
- dsa.cat-herding.net
Loading
Loading