feature/backstage #13

Merged
gitea_admin merged 5 commits from feature/backstage into main 2026-04-23 18:45:57 +00:00
5 changed files with 125 additions and 23 deletions
Showing only changes of commit 34ff37edbf - Show all commits

View 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
Review

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
"webOrigins": ["https://backstage.forteapps.net"],
Ghost marked this conversation as resolved
Review

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"
}
}
}

View File

@@ -986,15 +986,18 @@ ignore:
- Dynamic plugins: loads `dynamic-plugins.default.yaml` (all 27+ bundled plugins)
- Catalog rules: Component, System, API, Resource, Location, Template, Group, User, Domain
**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-..."
```
**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:
@@ -1012,22 +1015,31 @@ spec:
owner: team-name
```
Then add the location to `backstage-values.yaml` under `upstream.backstage.appConfig.catalog.locations`.
Repos with this file are auto-discovered — no manual registration needed.
**Per-cluster Configuration**:
To set the ingress hostname, create a per-cluster overlay values file (e.g., `infra/values/upc-dev/backstage-values.yaml`) with:
**Dynamic Plugins**:
Add plugins at runtime via `global.dynamic.plugins` in values — no image rebuild:
```yaml
global:
host: backstage.example.com
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.example.com
baseUrl: https://backstage.forteapps.net
backend:
baseUrl: https://backstage.example.com
baseUrl: https://backstage.forteapps.net
ingress:
host: backstage.example.com
host: backstage.forteapps.net
```
### Keycloak Client Registrar

View File

@@ -22,6 +22,7 @@ spec:
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
Review

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
Review

Using HEAD for targetRevision can cause unexpected deployments when main branch changes.

    targetRevision: "main"

#ai-review-inline

Using HEAD for targetRevision can cause unexpected deployments when main branch changes. ```suggestion targetRevision: "main" ``` #ai-review-inline

View File

@@ -36,9 +36,12 @@ upstream:
cpu: 1000m
memory: 2560Mi
extraEnvVarsSecrets:
- backstage-oidc-credentials
appConfig:
app:
title: "Forte Developer Portal"
title: "Forte Backstage"
baseUrl: http://localhost:7007
Ghost marked this conversation as resolved
Review

Hardcoded localhost URL will break in Kubernetes deployment; use proper ingress URL or environment variable.

        baseUrl: https://backstage.forteapps.net

#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:
@@ -47,6 +50,27 @@ upstream:
client: better-sqlite3
connection: ":memory:"
# -- Keycloak OIDC authentication
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:
@@ -59,12 +83,22 @@ upstream:
- Group
- User
- Domain
locations: []
# Register components from Gitea repositories by adding:
# - type: url
# target: https://git.forteapps.net/Forte/my-repo/raw/branch/main/catalog-info.yaml
# rules:
# - allow: [Component, System, API]
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

View File

@@ -0,0 +1,12 @@
global:
host: backstage.forteapps.net
Ghost marked this conversation as resolved
Review

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
Review

Backend baseUrl should typically use a different path or port than the frontend app baseUrl to avoid routing conflicts.

        baseUrl: https://backstage.forteapps.net/api

#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