Skip to content
This repository was archived by the owner on Aug 5, 2026. It is now read-only.
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
40 changes: 40 additions & 0 deletions hack/apiref-e2e/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# apiRef end-to-end test (real authn + real snowplow on kind)

Proves the CDC `apiRef` status source works against the **real** Krateo platform — not stubs:

```
projected SA token (file)
─ authn.Client.Token ─▶ authn POST /serviceaccount/login (TokenReview → JWT + clientconfig)
─ snowplow.Client.Resolve(Bearer JWT, extras) ─▶ snowplow GET /call (resolves a RESTAction)
─▶ RESTAction echoes the request extras ─▶ .api.echo.args
```

The test exercises the actual CDC client code (`internal/authn`, `internal/snowplow`,
`internal/composition.SnowplowAPIResolver`) and asserts that both the **static** extras
(`region`, from the CompositionDefinition's `apiRef.extras`) and the **per-instance** extras
(`compositionName`/`compositionNamespace`/`compositionId`, injected by the resolver, request-wins)
round-trip through the authn-issued JWT and snowplow's RESTAction resolution.

## Run

```bash
# needs: docker, kind, kubectl, go; ../authn (main) and ../snowplow checkouts
hack/apiref-e2e/run.sh
```

`run.sh` creates a kind cluster, builds authn + snowplow:1.1.1 images from source, deploys them
(+ an in-cluster `go-httpbin` echo server) with a shared `JWT_SIGN_KEY`, applies the fixtures
(test ServiceAccount, its `serviceaccount.authn.krateo.io` allowlist mapping, the `status-sources`
RESTAction, group RBAC), mints an audience-`authn` token via TokenRequest, port-forwards both
services, and runs the tagged test:

```bash
go test -tags e2e ./internal/composition/ -run TestE2E_ApiRefChain -v
```

## What success looks like

- authn log: `serviceaccount auth succeeded username=cdc-e2e groups=krateo:cdc-e2e`
- snowplow log: `base dict for api resolver dict={compositionId,compositionName,compositionNamespace,region}`
then `RESTAction successfully resolved name=status-sources`
- test: `.api.echo.args` = `{cn: demo-app, cns: apps, cid: uid-e2e-123, region: eu}`
88 changes: 88 additions & 0 deletions hack/apiref-e2e/manifests/authn-deploy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
---
apiVersion: v1
kind: Namespace
metadata:
name: demo-system
---
kind: ServiceAccount
apiVersion: v1
metadata:
name: authn
namespace: demo-system
---
apiVersion: v1
kind: Service
metadata:
name: authn
namespace: demo-system
spec:
selector:
app: authn
type: NodePort
ports:
- name: http
port: 8082
targetPort: http
protocol: TCP
nodePort: 30082
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: secrets-admin
namespace: demo-system
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: secrets-admin-binding
namespace: demo-system
subjects:
- kind: ServiceAccount
name: authn
namespace: demo-system
roleRef:
kind: Role
name: secrets-admin
apiGroup: rbac.authorization.k8s.io
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: authn
namespace: demo-system
labels:
app: authn
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: authn
template:
metadata:
labels:
app: authn
spec:
serviceAccountName: authn
containers:
- name: authn
image: authn:e2e
imagePullPolicy: Never
args:
- --debug=true
- --kubeconfig-server-url=https://kubernetes.default.svc
- --namespace=demo-system
- --jwt-sign-key=AbbraCadabbra
- --serviceaccount-audience=authn
ports:
- name: http
containerPort: 8082
36 changes: 36 additions & 0 deletions hack/apiref-e2e/manifests/authn-rbac.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: csr-admin
rules:
- apiGroups: ["certificates.k8s.io"]
resources: ["certificatesigningrequests"]
verbs: ["create", "get", "list", "watch", "approve", "delete", "update"]
- apiGroups: ["certificates.k8s.io"]
resources: ["certificatesigningrequests/approval"]
verbs: ["update"]
- apiGroups: ["certificates.k8s.io"]
resources: ["signers"]
resourceNames: ["kubernetes.io/kube-apiserver-client"]
verbs: ["approve"]
# Kubernetes intra-service auth (/serviceaccount/login): validate caller SA tokens.
- apiGroups: ["authentication.k8s.io"]
resources: ["tokenreviews"]
verbs: ["create"]
# Resolve the ServiceAccount allowlist mapping for the presented SA.
- apiGroups: ["serviceaccount.authn.krateo.io"]
resources: ["serviceaccounts"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: csr-admin-binding
subjects:
- kind: ServiceAccount
name: authn
namespace: demo-system
roleRef:
kind: ClusterRole
name: csr-admin
apiGroup: rbac.authorization.k8s.io
106 changes: 106 additions & 0 deletions hack/apiref-e2e/manifests/fixtures.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
---
# In-cluster echo server (multi-arch). /get returns {"args": {...query...}, ...},
# so extras templated into the query come back in the response — the assertion vehicle.
apiVersion: apps/v1
kind: Deployment
metadata:
name: echo
namespace: demo-system
labels: { app: echo }
spec:
replicas: 1
selector: { matchLabels: { app: echo } }
template:
metadata: { labels: { app: echo } }
spec:
containers:
- name: echo
image: ghcr.io/mccutchen/go-httpbin:latest
imagePullPolicy: Never
ports: [ { name: http, containerPort: 8080 } ]
---
apiVersion: v1
kind: Service
metadata:
name: echo
namespace: demo-system
spec:
selector: { app: echo }
ports: [ { name: http, port: 8080, targetPort: http } ]
---
# Endpoint the RESTAction calls (the in-cluster echo server).
apiVersion: v1
kind: Secret
type: Opaque
metadata:
name: echo-endpoint
namespace: demo-system
stringData:
server-url: http://echo.demo-system.svc.cluster.local:8080
---
# The RESTAction the CDC resolves via apiRef. Its path templates the request extras
# (.compositionName/.compositionNamespace/.compositionId injected per-instance by the CDC,
# plus the static .region) into the echo call, which reflects them back under .api.echo.args.
apiVersion: templates.krateo.io/v1
kind: RESTAction
metadata:
name: status-sources
namespace: demo-system
spec:
api:
- name: echo
path: ${ "/get?cn=" + (.compositionName) + "&cns=" + (.compositionNamespace) + "&cid=" + (.compositionId) + "&region=" + (.region) }
endpointRef:
name: echo-endpoint
namespace: demo-system
---
# The CDC's own ServiceAccount (what core-provider would create per composition).
apiVersion: v1
kind: ServiceAccount
metadata:
name: cdc-e2e-sa
namespace: demo-system
---
# authn allowlist mapping: authorizes cdc-e2e-sa to exchange its token; issues identity
# username=cdc-e2e, groups=[krateo:cdc-e2e]. Lives in the authn operator namespace.
apiVersion: serviceaccount.authn.krateo.io/v1alpha1
kind: ServiceAccount
metadata:
name: cdc-e2e
namespace: demo-system
spec:
serviceAccountRef:
namespace: demo-system
name: cdc-e2e-sa
groups:
- krateo:cdc-e2e
displayName: "CDC e2e"
---
# RBAC for the issued identity's group: read the RESTAction + endpoint Secret in demo-system,
# so snowplow authorizes the user to resolve status-sources.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: cdc-e2e-restaction-read
namespace: demo-system
rules:
- apiGroups: ["templates.krateo.io"]
resources: ["restactions"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["secrets", "configmaps"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: cdc-e2e-restaction-read
namespace: demo-system
subjects:
- kind: Group
name: krateo:cdc-e2e
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: cdc-e2e-restaction-read
apiGroup: rbac.authorization.k8s.io
Loading