feature/backstage #13
@@ -34,6 +34,7 @@ jobs:
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.AI_REVIEW_TOKEN }}
|
||||
|
||||
- name: Run inline review
|
||||
uses: docker://nikitafilonov/ai-review:v0.64.0
|
||||
|
||||
43
cluster-resources/backstage-keycloak-client-config.yaml
Normal file
@@ -0,0 +1,43 @@
|
||||
# Self-service Keycloak client config for Backstage.
|
||||
# Kyverno clones this to the keycloak namespace, where the
|
||||
# keycloak-client-registrar CronJob processes it and creates
|
||||
# the backstage-oidc-credentials Secret in the backstage namespace.
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: keycloak-client-backstage
|
||||
namespace: backstage
|
||||
labels:
|
||||
keycloak.forteapps.net/client-config: "true"
|
||||
stringData:
|
||||
client.json: |
|
||||
{
|
||||
"clientId": "backstage",
|
||||
"name": "Backstage Developer Portal",
|
||||
"redirectUris": ["https://backstage.forteapps.net/api/auth/oidc/handler/frame"],
|
||||
|
Ghost marked this conversation as resolved
|
||||
"webOrigins": ["https://backstage.forteapps.net"],
|
||||
|
Ghost marked this conversation as resolved
gitea_admin
commented
Consider using a variable or ConfigMap for the domain to make this configuration environment-agnostic. #ai-review-inline Consider using a variable or ConfigMap for the domain to make this configuration environment-agnostic.
#ai-review-inline
|
||||
"defaultClientScopes": ["openid", "email", "profile"],
|
||||
"protocolMappers": [
|
||||
{
|
||||
"name": "email_verified",
|
||||
"protocol": "openid-connect",
|
||||
"protocolMapper": "oidc-hardcoded-claim-mapper",
|
||||
"config": {
|
||||
"claim.name": "email_verified",
|
||||
"claim.value": "true",
|
||||
"jsonType.label": "boolean",
|
||||
"id.token.claim": "true",
|
||||
"access.token.claim": "true",
|
||||
"userinfo.token.claim": "true"
|
||||
}
|
||||
}
|
||||
],
|
||||
"secret": {
|
||||
"namespace": "backstage",
|
||||
"name": "backstage-oidc-credentials",
|
||||
"keys": {
|
||||
"clientId": "AUTH_OIDC_CLIENT_ID",
|
||||
"clientSecret": "AUTH_OIDC_CLIENT_SECRET"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -965,6 +965,83 @@ ignore:
|
||||
- Check Gitea Actions tab for workflow run status and logs
|
||||
- Monitor Anthropic usage dashboard for token consumption
|
||||
|
||||
### Backstage / RHDH (Developer Portal)
|
||||
|
||||
**Chart**: `backstage` (RHDH — Red Hat Developer Hub)
|
||||
**Version**: `5.8.0`
|
||||
**Namespace**: `backstage`
|
||||
**Helm Repo**: `https://redhat-developer.github.io/rhdh-chart`
|
||||
**Image**: `quay.io/rhdh-community/rhdh:next`
|
||||
|
||||
**Purpose**: Internal developer portal where teams register and broadcast themselves, their applications, APIs, and systems. Provides a unified catalog, templates, and documentation hub.
|
||||
|
||||
**Why RHDH over vanilla Backstage**: Ships 27+ plugins pre-bundled (ArgoCD, Kubernetes, Keycloak, GitHub, GitLab, Jira, SonarQube, Tekton, Jenkins, Quay, and more). Supports dynamic plugin installation at runtime — no image rebuilds needed.
|
||||
|
||||
**Configuration** (`infra/values/base/backstage-values.yaml`):
|
||||
- OpenShift Route disabled (`route.enabled: false`) — uses Traefik ingress instead
|
||||
- PostgreSQL subchart enabled for persistence (2Gi)
|
||||
- SecurityContext configured for vanilla Kubernetes (non-OpenShift)
|
||||
- Traefik ingress with `websecure` entrypoint
|
||||
- App title: "Forte Developer Portal"
|
||||
- Dynamic plugins: loads `dynamic-plugins.default.yaml` (all 27+ bundled plugins)
|
||||
- Catalog rules: Component, System, API, Resource, Location, Template, Group, User, Domain
|
||||
|
||||
**Authentication** (Keycloak OIDC):
|
||||
- Uses the self-service registrar pattern (see [Keycloak Client Registrar](#keycloak-client-registrar))
|
||||
- Config Secret: `cluster-resources/backstage-keycloak-client-config.yaml`
|
||||
- Kyverno clones it → registrar creates `backstage-oidc-credentials` Secret in `backstage` namespace
|
||||
- Credential keys: `AUTH_OIDC_CLIENT_ID`, `AUTH_OIDC_CLIENT_SECRET` (loaded via `extraEnvVarsSecrets`)
|
||||
- Redirect URI: `https://backstage.forteapps.net/api/auth/oidc/handler/frame`
|
||||
- Sign-in resolver: `emailMatchingUserEntityProfileEmail`
|
||||
|
||||
**Catalog Discovery** (Gitea):
|
||||
- Auto-discovers `catalog-info.yaml` from all repos in the `Forte` organization
|
||||
- Scans every 30 minutes via the Gitea catalog provider plugin
|
||||
- Gitea SCM integration configured for URL resolution (`git.forteapps.net`)
|
||||
|
||||
**Catalog Registration**:
|
||||
Teams register services by adding a `catalog-info.yaml` to their repo root:
|
||||
```yaml
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: my-service
|
||||
description: My service description
|
||||
annotations:
|
||||
backstage.io/source-location: url:https://git.forteapps.net/Forte/my-service
|
||||
spec:
|
||||
type: service
|
||||
lifecycle: production
|
||||
owner: team-name
|
||||
```
|
||||
|
||||
Repos with this file are auto-discovered — no manual registration needed.
|
||||
|
||||
**Dynamic Plugins**:
|
||||
Add plugins at runtime via `global.dynamic.plugins` in values — no image rebuild:
|
||||
```yaml
|
||||
global:
|
||||
dynamic:
|
||||
plugins:
|
||||
- package: "@scope/my-plugin@1.0.0"
|
||||
integrity: "sha512-..."
|
||||
```
|
||||
|
||||
**Per-cluster Configuration** (`infra/values/upc-dev/backstage-values.yaml`):
|
||||
```yaml
|
||||
global:
|
||||
host: backstage.forteapps.net
|
||||
upstream:
|
||||
backstage:
|
||||
appConfig:
|
||||
app:
|
||||
baseUrl: https://backstage.forteapps.net
|
||||
backend:
|
||||
baseUrl: https://backstage.forteapps.net
|
||||
ingress:
|
||||
host: backstage.forteapps.net
|
||||
```
|
||||
|
||||
### Keycloak Client Registrar
|
||||
|
||||
**Type**: CronJob (deployed via Keycloak Helm chart `extraDeploy`)
|
||||
|
||||
43
infra/base/backstage.yaml
Normal file
@@ -0,0 +1,43 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: backstage
|
||||
namespace: argocd
|
||||
annotations:
|
||||
argocd.argoproj.io/sync-wave: "1"
|
||||
labels:
|
||||
app.kubernetes.io/name: backstage
|
||||
app.kubernetes.io/part-of: developer-portal
|
||||
app.kubernetes.io/managed-by: argocd
|
||||
finalizers:
|
||||
- resources-finalizer.argocd.argoproj.io
|
||||
spec:
|
||||
project: default
|
||||
|
||||
sources:
|
||||
- repoURL: https://redhat-developer.github.io/rhdh-chart
|
||||
|
Ghost marked this conversation as resolved
gitea_admin
commented
External Helm repository should be verified for security and reliability. #ai-review-inline External Helm repository should be verified for security and reliability.
#ai-review-inline
|
||||
chart: backstage
|
||||
targetRevision: "5.8.0"
|
||||
|
Ghost marked this conversation as resolved
gitea_admin
commented
Consider pinning to a specific chart version digest instead of semantic version for better security and reproducibility. #ai-review-inline Consider pinning to a specific chart version digest instead of semantic version for better security and reproducibility.
#ai-review-inline
|
||||
helm:
|
||||
releaseName: backstage
|
||||
valueFiles:
|
||||
- $values/infra/values/base/backstage-values.yaml
|
||||
- $values/infra/values/upc-dev/backstage-values.yaml
|
||||
|
||||
- repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
|
||||
|
Ghost marked this conversation as resolved
gitea_admin
commented
SSH Git URL exposes internal infrastructure details and may create dependency issues. #ai-review-inline SSH Git URL exposes internal infrastructure details and may create dependency issues.
#ai-review-inline
|
||||
targetRevision: HEAD
|
||||
|
Ghost marked this conversation as resolved
gitea_admin
commented
Using HEAD for targetRevision can cause unexpected deployments when main branch changes. #ai-review-inline Using HEAD for targetRevision can cause unexpected deployments when main branch changes.
```suggestion
targetRevision: "main"
```
#ai-review-inline
|
||||
ref: values
|
||||
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: backstage
|
||||
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
allowEmpty: false
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
- Validate=true
|
||||
- ServerSideApply=true
|
||||
@@ -22,3 +22,4 @@ resources:
|
||||
- tempo.yaml
|
||||
- grafana-dashboards.yaml
|
||||
- network-policies-application.yaml
|
||||
- backstage.yaml
|
||||
|
Ghost marked this conversation as resolved
gitea_admin
commented
Consider verifying that backstage.yaml exists in the same directory to avoid kustomize build errors. #ai-review-inline Consider verifying that backstage.yaml exists in the same directory to avoid kustomize build errors.
#ai-review-inline
|
||||
|
||||
128
infra/values/base/backstage-values.yaml
Normal file
@@ -0,0 +1,128 @@
|
||||
# Red Hat Developer Hub (RHDH) - Internal Developer Portal
|
||||
# Helm chart: https://github.com/redhat-developer/rhdh-chart
|
||||
# Includes 27+ plugins out of the box: ArgoCD, Kubernetes, Keycloak,
|
||||
# GitHub, GitLab, Jira, SonarQube, Tekton, Jenkins, and more.
|
||||
|
||||
global:
|
||||
auth:
|
||||
backend:
|
||||
enabled: true
|
||||
dynamic:
|
||||
includes:
|
||||
- dynamic-plugins.default.yaml
|
||||
plugins: []
|
||||
|
||||
# Disable OpenShift Route (not on OpenShift)
|
||||
route:
|
||||
enabled: false
|
||||
|
||||
upstream:
|
||||
backstage:
|
||||
image:
|
||||
registry: quay.io
|
||||
repository: rhdh-community/rhdh
|
||||
tag: next
|
||||
|
Ghost marked this conversation as resolved
gitea_admin
commented
Using 'next' tag is unstable for production deployments; prefer a specific version tag or digest. #ai-review-inline Using 'next' tag is unstable for production deployments; prefer a specific version tag or digest.
```suggestion
tag: "1.0.0" # Replace with specific stable version
```
#ai-review-inline
|
||||
|
||||
podSecurityContext:
|
||||
runAsUser: 1001
|
||||
runAsGroup: 1001
|
||||
fsGroup: 1001
|
||||
|
||||
resources:
|
||||
requests:
|
||||
cpu: 250m
|
||||
memory: 1Gi
|
||||
limits:
|
||||
cpu: 1000m
|
||||
memory: 2560Mi
|
||||
|
||||
extraEnvVarsSecrets:
|
||||
- backstage-oidc-credentials
|
||||
|
||||
appConfig:
|
||||
app:
|
||||
title: "Forte Backstage"
|
||||
baseUrl: http://localhost:7007
|
||||
|
Ghost marked this conversation as resolved
gitea_admin
commented
Hardcoded localhost URL will break in Kubernetes deployment; use proper ingress URL or environment variable. #ai-review-inline Hardcoded localhost URL will break in Kubernetes deployment; use proper ingress URL or environment variable.
```suggestion
baseUrl: https://backstage.forteapps.net
```
#ai-review-inline
|
||||
|
||||
backend:
|
||||
baseUrl: http://localhost:7007
|
||||
|
Ghost marked this conversation as resolved
gitea_admin
commented
Hardcoded localhost URL will break in Kubernetes deployment; use proper service URL or environment variable. #ai-review-inline Hardcoded localhost URL will break in Kubernetes deployment; use proper service URL or environment variable.
```suggestion
baseUrl: https://backstage.forteapps.net
```
#ai-review-inline
|
||||
|
||||
# -- Keycloak OIDC authentication
|
||||
|
Ghost marked this conversation as resolved
Outdated
gitea_admin
commented
SQLite in-memory database will lose data on pod restart; use PostgreSQL for production. #ai-review-inline SQLite in-memory database will lose data on pod restart; use PostgreSQL for production.
```suggestion
client: pg
connection:
host: ${POSTGRES_HOST}
port: 5432
user: ${POSTGRES_USER}
password: ${POSTGRES_PASSWORD}
database: ${POSTGRES_DB}
```
#ai-review-inline
|
||||
signInPage: oidc
|
||||
auth:
|
||||
environment: production
|
||||
providers:
|
||||
oidc:
|
||||
production:
|
||||
metadataUrl: https://id.forteapps.net/realms/forte/.well-known/openid-configuration
|
||||
clientId: ${AUTH_OIDC_CLIENT_ID}
|
||||
clientSecret: ${AUTH_OIDC_CLIENT_SECRET}
|
||||
prompt: auto
|
||||
signIn:
|
||||
resolvers:
|
||||
- resolver: emailMatchingUserEntityProfileEmail
|
||||
|
||||
# -- Gitea SCM integration (for catalog URL resolution)
|
||||
integrations:
|
||||
gitea:
|
||||
- host: git.forteapps.net
|
||||
|
||||
# -- Software catalog
|
||||
catalog:
|
||||
rules:
|
||||
- allow:
|
||||
- Component
|
||||
- System
|
||||
- API
|
||||
- Resource
|
||||
- Location
|
||||
- Template
|
||||
- Group
|
||||
- User
|
||||
- Domain
|
||||
providers:
|
||||
# Auto-discover catalog-info.yaml from all Forte org repos
|
||||
gitea:
|
||||
forte:
|
||||
organization: Forte
|
||||
host: git.forteapps.net
|
||||
catalogPath: catalog-info.yaml
|
||||
schedule:
|
||||
frequency: { minutes: 30 }
|
||||
timeout: { minutes: 3 }
|
||||
locations:
|
||||
# Backstage's own org data (bootstrap teams, systems, domains)
|
||||
# - type: url
|
||||
# target: https://git.forteapps.net/Forte/backstage-catalog/raw/branch/main/org.yaml
|
||||
# rules:
|
||||
# - allow: [Group, User, System, Domain]
|
||||
|
||||
ingress:
|
||||
enabled: true
|
||||
className: traefik
|
||||
annotations:
|
||||
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
||||
|
||||
postgresql:
|
||||
enabled: true
|
||||
|
gitea_admin
commented
Missing TLS redirect configuration for secure HTTPS access. #ai-review-inline Missing TLS redirect configuration for secure HTTPS access.
```suggestion
traefik.ingress.kubernetes.io/router.entrypoints: websecure
traefik.ingress.kubernetes.io/router.tls: "true"
```
#ai-review-inline
|
||||
image:
|
||||
registry: docker.io
|
||||
repository: library/postgres
|
||||
tag: "15"
|
||||
primary:
|
||||
persistence:
|
||||
enabled: true
|
||||
size: 2Gi
|
||||
podSecurityContext:
|
||||
enabled: true
|
||||
fsGroup: 26
|
||||
runAsUser: 26
|
||||
resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 128Mi
|
||||
limits:
|
||||
cpu: 250m
|
||||
memory: 512Mi
|
||||
volumePermissions:
|
||||
enabled: true
|
||||
12
infra/values/upc-dev/backstage-values.yaml
Normal file
@@ -0,0 +1,12 @@
|
||||
global:
|
||||
host: backstage.forteapps.net
|
||||
|
Ghost marked this conversation as resolved
gitea_admin
commented
Host configuration appears twice which may cause conflicts or confusion in the Helm chart. #ai-review-inline Host configuration appears twice which may cause conflicts or confusion in the Helm chart.
#ai-review-inline
|
||||
|
||||
upstream:
|
||||
backstage:
|
||||
appConfig:
|
||||
app:
|
||||
baseUrl: https://backstage.forteapps.net
|
||||
|
Ghost marked this conversation as resolved
gitea_admin
commented
Backend baseUrl should typically use a different path or port than the frontend app baseUrl to avoid routing conflicts. #ai-review-inline Backend baseUrl should typically use a different path or port than the frontend app baseUrl to avoid routing conflicts.
```suggestion
baseUrl: https://backstage.forteapps.net/api
```
#ai-review-inline
|
||||
backend:
|
||||
baseUrl: https://backstage.forteapps.net
|
||||
ingress:
|
||||
host: backstage.forteapps.net
|
||||
Consider using a variable or ConfigMap for the domain to make this configuration environment-agnostic.
#ai-review-inline