CRD reference (pattern resources)
This module is optional read-only reference — you do not need it to complete the workshop flow. It summarizes Kubernetes custom resources (CRs) used in the AI Computer Vision at the Edge Validated Pattern. Helm templates under charts/all/ are the source of truth; examples below are simplified for workshop reading.
Use it when you want a single place to look up CR names, relationships, and chart locations after completing Module 07 — Review.
How a self-service OIDC client becomes a rate-limit tier
When a workshop user runs the OIDC credentials self-service scaffolder template in Developer Hub, the chosen plan tier flows through the gateway stack in five steps:
-
The user selects
planTier(freeorgold) on the template form. -
The template creates a Keycloak client with an
oidc-hardcoded-claim-mapperprotocol mapper that embedsplan: <tier>in every JWT. -
The client obtains a token from realm
cv(client_credentialsor Bearer flow). -
Kuadrant
AuthPolicy(authpolicy-cvonneuroface-cv-lb) validates the JWT issuer (keycloak.<hub-domain>/realms/cv) and admits the request. -
RateLimitPolicyandPlanPolicyonneuroface-cv-lbmatchauth.identity.plan == "free"or"gold"and apply/v1/predictlimits (100/h or 500/h).
The MaaS route (ai-maas) uses the same dual-auth pattern (APIKEY or OIDC JWT) with PlanPolicy and TokenRateLimitPolicy tiers on /v1/chat/completions instead of per-request limits.
Red Hat Connectivity Link (Kuadrant)
Documentation: Red Hat Connectivity Link
OIDCPolicy
Purpose: Enforce browser redirect OIDC login on Gateway API HTTPRoutes (interactive sessions).
Chart: charts/all/neuroface-gateway/templates/oidcpolicy-realms.yaml
|
|
apiVersion: extensions.kuadrant.io/v1alpha1
kind: OIDCPolicy
metadata:
name: oidc-neuroface
namespace: neuroface-gateway-system
spec:
provider:
issuerURL: https://sso.apps.cluster.example.com/realms/neuroface
clientID: client-neuroface-user1
authorizationEndpoint: https://sso.apps.cluster.example.com/realms/neuroface/protocol/openid-connect/auth
tokenEndpoint: https://sso.apps.cluster.example.com/realms/neuroface/protocol/openid-connect/token
redirectURI: https://neuroface.apps.cluster.example.com/auth/callback
targetRef:
group: gateway.networking.k8s.io
kind: HTTPRoute
name: neuroface-app-lb
auth:
tokenSource:
authorizationHeader:
prefix: Bearer
name: Authorization
cookie:
name: jwt
Connection: OIDCPolicy → HTTPRoute → Gateway → backend Service (via Skupper on spokes). Per-user scaffolded NeuroFace instances on spokes use the same redirect pattern against their local RHBK realm.
AuthPolicy
Purpose: API-key and/or JWT authentication on Gateway API HTTPRoutes. A single AuthPolicy can declare multiple identity sources; Authorino admits the request if any one succeeds ("dual auth").
apiVersion: kuadrant.io/v1
kind: AuthPolicy
metadata:
name: authpolicy-cv
namespace: neuroface-gateway-system
spec:
targetRef:
group: gateway.networking.k8s.io
kind: HTTPRoute
name: neuroface-cv-lb
when:
- predicate: request.method != "OPTIONS"
rules:
authentication:
jwt-users:
jwt:
issuerUrl: https://keycloak.apps.cluster.example.com/realms/cv
Connection: AuthPolicy → HTTPRoute → Keycloak JWT issuer. Workshop APIs (for example ai-maas) add an api-key-users source alongside jwt-users for dual auth.
Chart: charts/all/workshop-kuadrant-apis/templates/policies.yaml
RateLimitPolicy
Purpose: Per-route request limits keyed off authenticated identity (JWT claims or client IP).
apiVersion: kuadrant.io/v1
kind: RateLimitPolicy
metadata:
name: neuroface-cv-ratelimit
namespace: neuroface-gateway-system
spec:
targetRef:
group: gateway.networking.k8s.io
kind: HTTPRoute
name: neuroface-cv-lb
limits:
predict-free:
when:
- predicate: 'request.path.matches("^/v1/predict")'
- predicate: 'has(auth.identity) && has(auth.identity.plan) && auth.identity.plan == "free"'
counters:
- expression: auth.identity.sub
rates:
- limit: 100
window: 1h
predict-gold:
when:
- predicate: 'request.path.matches("^/v1/predict")'
- predicate: 'has(auth.identity) && has(auth.identity.plan) && auth.identity.plan == "gold"'
counters:
- expression: auth.identity.sub
rates:
- limit: 500
window: 1h
predict-default:
when:
- predicate: 'request.path.matches("^/v1/predict")'
- predicate: '!(has(auth.identity) && has(auth.identity.plan))'
counters:
- expression: request.headers['x-forwarded-for']
rates:
- limit: 30
window: 1m
Connection: RateLimitPolicy → HTTPRoute. The plan JWT claim (from the self-service template’s planTier parameter) selects the predict-free or predict-gold tier.
Chart: charts/all/neuroface-gateway/templates/ratelimit-policy.yaml
PlanPolicy
Purpose: Publish discoverable rate-limit tiers in the Kuadrant DevPortal plugin (Developer Hub API entity Kuadrant tab).
apiVersion: extensions.kuadrant.io/v1alpha1
kind: PlanPolicy
metadata:
name: neuroface-cv-plans
namespace: neuroface-gateway-system
spec:
targetRef:
group: gateway.networking.k8s.io
kind: HTTPRoute
name: neuroface-cv-lb
plans:
- tier: free
predicate: |
has(auth.identity) && has(auth.identity.plan) && auth.identity.plan == "free"
limits:
custom:
- limit: 100
window: 1h
- tier: gold
predicate: |
has(auth.identity) && has(auth.identity.plan) && auth.identity.plan == "gold"
limits:
custom:
- limit: 500
window: 1h
Connection: PlanPolicy → HTTPRoute → DevPortal discovered-plans UI. Pairs with RateLimitPolicy on the same route.
Chart: charts/all/neuroface-gateway/templates/plan-policy.yaml
TokenRateLimitPolicy
Purpose: Token-budget rate limits for LLM endpoints (counts tokens, not requests).
apiVersion: kuadrant.io/v1alpha1
kind: TokenRateLimitPolicy
metadata:
name: ai-maas-token-limits
namespace: ai-gateway-system
spec:
targetRef:
group: gateway.networking.k8s.io
kind: HTTPRoute
name: ai-maas
limits:
free:
rates:
- limit: 50
window: 1m
when:
- predicate: request.path == "/v1/chat/completions"
- predicate: |
auth.identity.metadata.annotations["secret.kuadrant.io/plan-id"] == "free" ||
auth.identity.groups.split(",").exists(g, g == "free")
counters:
- expression: auth.identity.userid
oidc:
rates:
- limit: 50
window: 1m
when:
- predicate: request.path == "/v1/chat/completions"
- predicate: has(auth.identity.iss)
counters:
- expression: auth.identity.sub
Connection: TokenRateLimitPolicy → HTTPRoute (ai-maas). Works with dual-auth AuthPolicy and PlanPolicy tiers (free, gold, oidc).
Chart: charts/all/workshop-kuadrant-apis/templates/policies.yaml
APIProduct
Purpose: Expose an HTTPRoute as an API entry in Developer Hub (Kuadrant DevPortal plugin).
|
|
apiVersion: devportal.kuadrant.io/v1alpha1
kind: APIProduct
metadata:
name: neuroface-cv-openapi
namespace: neuroface-gateway-system
spec:
displayName: Computer Vision API
description: YOLO PPE detection API protected by realm-cv OIDC JWTs.
publishStatus: Published
approvalMode: manual
version: v1
tags: [neuroface, cv, oidc, ppe, edge, gateway-api]
documentation:
docsURL: https://developer-hub.apps.cluster.example.com/catalog/default/api/neuroface-cv-openapi
contact:
email: platform@example.com
targetRef:
group: gateway.networking.k8s.io
kind: HTTPRoute
name: neuroface-cv-lb
Chart: charts/all/neuroface-gateway/templates/apiproducts.yaml
Kubernetes Gateway API
Documentation: Gateway API specification
Red Hat Build of Keycloak (RHBK)
Documentation: Red Hat Build of Keycloak
KeycloakRealmImport
Purpose: Declaratively create realms (cv, maas, neuroface) with users, clients, roles, and groups.
Client secrets are injected via spec.placeholders referencing Kubernetes Secrets (sourced from Vault through ESO).
Chart: charts/all/rhbk-iam/templates/realm-import.yaml
apiVersion: k8s.keycloak.org/v2alpha1
kind: KeycloakRealmImport
metadata:
name: realm-cv
namespace: keycloak-system
spec:
keycloakCRName: keycloak
placeholders:
CLIENT_SECRET_USER1:
secret:
name: keycloak-client-cv-user1
key: clientSecret
realm:
realm: cv
enabled: true
displayName: Computer Vision
verifyEmail: false
requiredActions:
- alias: VERIFY_PROFILE
enabled: false
clients:
- clientId: client-cv-user1
secret: $(CLIENT_SECRET_USER1)
serviceAccountsEnabled: true
users:
- username: user1
firstName: Workshop
lastName: user1
credentials:
- type: password
value: Welcome123!
temporary: false
clientRoles:
realm-management:
- view-clients
- manage-clients
Connection: KeycloakRealmImport → Keycloak CR → ExternalSecret (Vault client secrets).
External Secrets Operator
Documentation: External Secrets Operator
Purpose: Sync secrets from HashiCorp Vault into Kubernetes Secrets.
Each ExternalSecret maps a Vault path to a K8s Secret key.
Chart: charts/all/rhbk-iam/templates/realm-import.yaml (generates ExternalSecrets for each client)
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: keycloak-client-cv-user1
namespace: keycloak-system
spec:
secretStoreRef:
name: vault-backend
kind: ClusterSecretStore
refreshInterval: 1h
data:
- secretKey: clientSecret
remoteRef:
key: secret/data/hub/keycloak/realms/cv/user1
property: clientSecret
target:
name: keycloak-client-cv-user1
creationPolicy: Owner
Red Hat Service Interconnect (Skupper)
Documentation: Red Hat Service Interconnect
Site
Purpose: Declare a cluster as part of the Skupper network.
apiVersion: skupper.io/v2alpha1
kind: Site
metadata:
name: hub-site
namespace: service-interconnect
OpenShift Service Mesh (Istio ambient)
Documentation: OpenShift Service Mesh
Istio
apiVersion: sailoperator.io/v1alpha1
kind: Istio
metadata:
name: default
namespace: istio-system
spec:
profile: ambient
Connection: Istio control plane → Gateway controller → ZTunnel data plane (ambient L4 mTLS and mesh telemetry).
Chart: VP servicemesh-config application (servicemesh chart, profile: ambient)
ZTunnel
Purpose: Deploy the ambient mesh data plane (ztunnel DaemonSet). Required for L4 mTLS and Istio metrics (istio_requests_total, istio_tcp_*) consumed by Kiali and Grafana.
apiVersion: sailoperator.io/v1
kind: ZTunnel
metadata:
name: default
namespace: istio-system
spec:
namespace: ztunnel
version: v1.28.8
Connection: ZTunnel → ztunnel namespace DaemonSet on every node. Without this CR, oc get istio default -n istio-system reports ZTunnelNotFound and mesh telemetry panels show no data.
Chart: charts/all/istio-ztunnel/templates/ztunnel.yaml
Red Hat Developer Hub
Documentation: Red Hat Developer Hub
Backstage CR
Purpose: Deploy and configure the Developer Hub instance.
Catalog entities are mounted as ConfigMaps via spec.application.extraFiles.configMaps and registered in catalog.locations.
Chart: charts/all/developer-hub/templates/backstage-developer-hub.yaml
apiVersion: rhdh.redhat.com/v1alpha5
kind: Backstage
metadata:
name: developer-hub
namespace: developer-hub
spec:
application:
appConfig:
configMaps:
- name: app-config-rhdh
- name: app-config-auth-rhdh
dynamicPluginsConfigMapName: dynamic-plugins-rhdh
extraFiles:
configMaps:
- name: developer-hub-catalog-users
key: users.yaml
mountPath: /opt/app-root/src/catalog-data
- name: developer-hub-catalog-neuroface-cv
key: neuroface-cv-system.yaml
mountPath: /opt/app-root/src/catalog-data/neuroface-cv
- name: developer-hub-catalog-iam-realms
key: iam-realms.yaml
mountPath: /opt/app-root/src/catalog-data/iam-realms
- name: developer-hub-catalog-kuadrant-apis
key: workshop-kuadrant-apis.yaml
mountPath: /opt/app-root/src/catalog-data/kuadrant-apis
route:
enabled: true
host: developer-hub.apps.<hub-domain>
deployment:
patch:
spec:
template:
spec:
serviceAccountName: developer-hub
containers:
- name: backstage-backend
envFrom:
- secretRef:
name: developer-hub-oidc-auth
volumeMounts:
- name: rhdh-rbac-policy
mountPath: /opt/app-root/src/rbac-policy.csv
subPath: rbac-policy.csv
Catalog ConfigMaps
| ConfigMap | Content |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Software Templates (Scaffolder)
Purpose: Self-service workflows in Developer Hub — deploy personal NeuroFace instances, create/revoke OIDC clients, and register components in the catalog.
Chart: charts/all/developer-hub/files/software-templates/
| Template | Purpose |
|---|---|
|
Scaffold a personal NeuroFace instance on east or west spoke (GitLab publish + catalog register) |
|
Create an OIDC client in Keycloak realm |
|
Delete a client created by the self-service template (same API + client label) |
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
name: ai-computer-vision
title: "AI Computer Vision at the Edge"
spec:
owner: platform-engineering
type: service
parameters:
- title: AI CV instance
properties:
owner:
title: Owner
ui:field: OwnerPicker
spoke:
title: Target spoke
type: string
enum: [east, west]
steps:
- id: fetch
name: Fetch skeleton
action: fetch:template
- id: publish
name: Publish to GitLab
action: publish:gitlab
- id: register
name: Register in catalog
action: catalog:register
Red Hat OpenShift AI
Documentation: Red Hat OpenShift AI
OpenShift GitOps (Argo CD)
Documentation: Red Hat OpenShift GitOps
ApplicationSet
Purpose: Automatically generate Argo CD Application resources based on discovered GitLab repositories.
In this workshop, an ApplicationSet watches the ws-workshop GitLab group and automatically deploys any repository matching the ^neuroface- prefix to the appropriate spoke cluster.
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: user-neuroface-apps
namespace: vp-gitops
spec:
generators:
- matrix:
generators:
- scmProvider:
gitlab:
group: "ws-workshop"
api: https://gitlab.apps.<hub-domain>/api/v4
filters:
- repositoryMatch: "^neuroface-"
- git:
repoURL: "https://gitlab.apps.<hub-domain>/ws-workshop/{{ '{{ .repository }}' }}.git"
revision: HEAD
files:
- path: k8s/target-cluster.yaml
template:
metadata:
name: "{{ '{{ .repository }}' }}"
spec:
project: ai
source:
repoURL: "https://gitlab.apps.<hub-domain>/ws-workshop/{{ '{{ .repository }}' }}.git"
targetRevision: HEAD
path: k8s
destination:
name: "{{ '{{ .cluster }}' }}"
namespace: "{{ '{{ .repository }}' }}"
syncPolicy:
automated:
prune: true
selfHeal: true
OpenShift Pipelines (Tekton)
Documentation: Red Hat OpenShift Pipelines
Pipeline
Purpose: Defines a reusable series of Task resources that execute in a specific order.
The scaffolded NeuroFace repositories include a Tekton pipeline that builds the backend container image using buildah.
apiVersion: tekton.dev/v1
kind: Pipeline
metadata:
name: neuroface-build
spec:
params:
- name: image-tag
type: string
default: latest
workspaces:
- name: source
- name: dockerconfig
tasks:
- name: build-backend
taskRef:
kind: ClusterTask
name: buildah
params:
- name: IMAGE
value: image-registry.openshift-image-registry.svc:5000/$(context.pipelineRun.namespace)/neuroface-backend:$(params.image-tag)
OpenShift Virtualization
|
Not included in this pattern installation. OpenShift Virtualization / KubeVirt is not deployed by the AI Computer Vision Validated Pattern, and there is no |
Documentation (for future extensions): OpenShift Virtualization
VirtualMachine (reference only)
Purpose: Run legacy VM-based workloads alongside containers. Not provisioned by this workshop pattern.
# Reference example — not deployed by ia-computer-vision
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
name: my-vm-lab
spec:
running: true
template:
spec:
domain:
cpu:
cores: 1
memory:
guest: 512Mi
HashiCorp Vault / External Secrets
ClusterSecretStore
Purpose: Provides a cluster-wide connection definition for the External Secrets Operator to securely authenticate and fetch secrets from Vault.
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
name: vault-backend
spec:
provider:
vault:
server: "http://vault.vault.svc.cluster.local:8200"
path: "secret"
version: "v2"
auth:
kubernetes:
mountPath: "kubernetes"
role: "validatedPatternDefaultPolicy"
Full pattern reference
See the pattern documentation: CRD reference on GitHub Pages.
End of the lab guide
You reached the last module in the canonical workshop order. Return to Welcome or use the sidebar to revisit any module.