Skip to content
Open
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
67 changes: 66 additions & 1 deletion Makefile-quickstart
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ SSH_PUBKEY = $(shell cat "$(SSH_KEY_PATH).pub" 2>/dev/null)
keycloak keycloak-issuer login whoami logout delete-keycloak \
openshell-saw-create openshell-saw-list openshell-saw-logs openshell-saw-ssh ssh-secret \
openshell-saw-configure-gateway openshell-saw-tui openshell-saw-gui test \
openshell-saw-delete delete-all status \
openshell-saw-delete delete-all status deploy-rhdh delete-rhdh \
governance-list-profiles governance-add-profile governance-remove-profile governance-create-profile governance-demo \
.check-saw-name .check-ssh-key .check-prereqs

Expand Down Expand Up @@ -376,6 +376,71 @@ delete-all: ## Uninstall everything (keycloak + imagestreams + gateway image)

# --- Status ---

RHDH_CHART := charts/openshell-rhdh
RHDH_NS ?= rhdh
RHDH_APPS_DOMAIN ?=
RHDH_BASE_URL ?=

RHDH_REPO_URL ?=

deploy-rhdh: ## Deploy RHDH self-service portal
@if [ -z "$(RHDH_APPS_DOMAIN)" ]; then \
RHDH_APPS_DOMAIN=$$(oc get ingresses.config.openshift.io cluster -o jsonpath='{.spec.domain}' 2>/dev/null); \
else \
RHDH_APPS_DOMAIN="$(RHDH_APPS_DOMAIN)"; \
fi; \
if [ -z "$(RHDH_BASE_URL)" ]; then \
RHDH_BASE_URL="https://backstage-developer-hub-$(RHDH_NS).$${RHDH_APPS_DOMAIN}"; \
else \
RHDH_BASE_URL="$(RHDH_BASE_URL)"; \
fi; \
KC_HOST=$$(oc get route -n keycloak-system -l app=keycloak -o jsonpath='{.items[0].spec.host}' 2>/dev/null); \
if [ -z "$${KC_HOST}" ]; then \
KC_HOST=$$(oc get route -n $(NS) -l app=keycloak -o jsonpath='{.items[0].spec.host}' 2>/dev/null); \
fi; \
RHDH_CLIENT_SECRET=$$(oc get secret rhdh-oidc-credentials -n $(RHDH_NS) -o jsonpath='{.data.AUTH_OIDC_CLIENT_SECRET}' 2>/dev/null | base64 -d); \
if [ -z "$${RHDH_CLIENT_SECRET}" ]; then \
RHDH_CLIENT_SECRET=$$(head -c 32 /dev/urandom | base64 | tr -d '/+=' | head -c 32); \
echo "Generated new OIDC client secret."; \
else \
echo "Reusing existing OIDC client secret."; \
fi; \
REPO_OPTS=""; \
if [ -n "$(RHDH_REPO_URL)" ]; then \
REPO_OPTS="--set pipelines.repoUrl=$(RHDH_REPO_URL)"; \
fi; \
echo "Deploying RHDH chart to namespace $(RHDH_NS)..."; \
echo " Apps domain: $${RHDH_APPS_DOMAIN}"; \
echo " Keycloak: $${KC_HOST}"; \
KC_SECRET_FILE=$$(mktemp); \
printf '%s' "$${RHDH_CLIENT_SECRET}" > "$${KC_SECRET_FILE}"; \
helm upgrade --install openshell-rhdh $(RHDH_CHART) \
--namespace $(RHDH_NS) --create-namespace \
--set appsDomain="$${RHDH_APPS_DOMAIN}" \
--set baseUrl="$${RHDH_BASE_URL}" \
--set keycloak.host="$${KC_HOST}" \
--set-file keycloak.clientSecret="$${KC_SECRET_FILE}" \
--set pipelines.namespace="$(NS)" \
$${REPO_OPTS}; \
rm -f "$${KC_SECRET_FILE}"; \
echo "Creating K8s backend tokens..."; \
K8S_TOKEN=$$(oc -n $(RHDH_NS) create token rhdh-k8s-backend --duration=8760h); \
CATALOG_TOKEN=$$(oc -n $(RHDH_NS) create token rhdh-catalog-proxy --duration=8760h); \
oc -n $(RHDH_NS) create secret generic rhdh-k8s-tokens \
--from-literal=K8S_API_TOKEN="$${K8S_TOKEN}" \
--from-literal=K8S_CATALOG_TOKEN="$${CATALOG_TOKEN}" \
--from-literal=K8S_API_URL="https://kubernetes.default.svc" \
--dry-run=client -o yaml | oc apply -f - >/dev/null; \
oc rollout restart deployment backstage-developer-hub -n $(RHDH_NS) 2>/dev/null || true
@echo ""
@echo "RHDH deployed. Access at:"
@echo " $$(oc get route backstage-developer-hub -n $(RHDH_NS) -o jsonpath='https://{.spec.host}' 2>/dev/null || echo '(waiting for route...)')"

delete-rhdh: ## Remove RHDH self-service portal
-helm uninstall openshell-rhdh --namespace $(RHDH_NS) 2>/dev/null
-oc -n $(RHDH_NS) delete secret rhdh-k8s-tokens --ignore-not-found 2>/dev/null
@echo "RHDH uninstalled."

status: ## Show status of all OpenShell resources
@echo "=== Helm releases ===" && helm list -n $(NS) 2>/dev/null || true
@echo "" && echo "=== VMs ===" && oc -n $(NS) get vm 2>/dev/null || true
Expand Down
39 changes: 39 additions & 0 deletions charts/openshell-keycloak/templates/realm-import.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,45 @@ spec:
included.client.audience: {{ .Values.keycloak.clients.cli.clientId | quote }}
id.token.claim: "false"
access.token.claim: "true"
{{- if .Values.keycloak.clients.rhdh }}
- clientId: {{ .Values.keycloak.clients.rhdh.clientId | quote }}
name: Red Hat Developer Hub
enabled: true
publicClient: false
clientAuthenticatorType: client-secret
secret: {{ .Values.keycloak.clients.rhdh.clientSecret | quote }}
standardFlowEnabled: true
directAccessGrantsEnabled: false
serviceAccountsEnabled: false
attributes:
pkce.code.challenge.method: S256
redirectUris:
{{- range .Values.keycloak.clients.rhdh.redirectUris }}
- {{ . | quote }}
{{- end }}
webOrigins:
{{- range .Values.keycloak.clients.rhdh.webOrigins }}
- {{ . | quote }}
{{- end }}
protocolMappers:
- name: realm-roles
protocol: openid-connect
protocolMapper: oidc-usermodel-realm-role-mapper
config:
multivalued: "true"
claim.name: realm_access.roles
jsonType.label: String
id.token.claim: "true"
access.token.claim: "true"
userinfo.token.claim: "true"
- name: audience
protocol: openid-connect
protocolMapper: oidc-audience-mapper
config:
included.client.audience: {{ .Values.keycloak.clients.rhdh.clientId | quote }}
id.token.claim: "true"
access.token.claim: "true"
{{- end }}
users:
{{- range .Values.keycloak.testUsers }}
- username: {{ .username | quote }}
Expand Down
7 changes: 6 additions & 1 deletion charts/openshell-keycloak/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ keycloak:
hostname: ""

realm: openshell
registrationAllowed: true
registrationAllowed: false

clients:
cli:
Expand All @@ -22,6 +22,11 @@ keycloak:
# 86400 = 24 hours. Sandboxes run for days, so short-lived tokens cause
# ssh-proxy and dashboard failures.
accessTokenLifespan: 86400
rhdh:
clientId: rhdh
clientSecret: ""
redirectUris: []
webOrigins: []
dashboard:
clientId: openshell-dashboard
public: true
Expand Down
7 changes: 7 additions & 0 deletions charts/openshell-rhdh/.helmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.git
.gitignore
.DS_Store
*.swp
*.bak
*.tmp
*.orig
6 changes: 6 additions & 0 deletions charts/openshell-rhdh/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
apiVersion: v2
name: openshell-rhdh
description: Red Hat Developer Hub for OpenShell self-service sandbox provisioning
type: application
version: 0.1.0
appVersion: "1.5"
80 changes: 80 additions & 0 deletions charts/openshell-rhdh/files/check-sandbox-status.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
name: check-saw-sandbox-status
title: Check Sandbox Status
description: Check the provisioning status of your agent sandbox
tags:
- openshell
- sandbox
- status
spec:
owner: infrastructure
type: service
parameters:
- title: Select Sandbox
required:
- name
properties:
name:
title: Sandbox Name
type: string
maxLength: 19
pattern: "^[a-z][a-z0-9-]*$"
description: "Name of the sandbox to check"
steps:
- id: get-pipelinerun-status
name: Check Tekton PipelineRun
action: http:backstage:request
continueOnError: true
input:
method: GET
path: /proxy/k8s-pipelines?labelSelector=openshell.io/sandbox=${{ parameters.name }},tekton.dev/pipeline=saw-create-sandbox&limit=1
headers:
Content-Type: application/json

- id: get-vm-status
name: Check VM status
action: http:backstage:request
continueOnError: true
input:
method: GET
path: /proxy/k8s-status-vm/saw-${{ parameters.name }}
headers:
Content-Type: application/json

- id: get-setup-job
name: Check setup job
action: http:backstage:request
continueOnError: true
input:
method: GET
path: /proxy/k8s-status-jobs/saw-${{ parameters.name }}-setup
headers:
Content-Type: application/json

- id: get-routes
name: Check routes
action: http:backstage:request
continueOnError: true
input:
method: GET
path: /proxy/k8s-status-routes?labelSelector=app.kubernetes.io/instance=saw-${{ parameters.name }}
headers:
Content-Type: application/json

output:
text:
- title: Sandbox Status
content: |
**Sandbox: ${{ parameters.name }}**

**PipelineRun (helm install):** ${{ steps['get-pipelinerun-status'].output.body.items[0].status.conditions[0].reason | default('Not found') }} — ${{ steps['get-pipelinerun-status'].output.body.items[0].status.conditions[0].message | default('No PipelineRun found for this sandbox') }}

**VM:** ${{ steps['get-vm-status'].output.body.status.printableStatus | default('Not found') }}

**Setup Job:** ${{ steps['get-setup-job'].output.body.status.conditions[0].type | default('Not started') }} — succeeded: ${{ steps['get-setup-job'].output.body.status.succeeded | default('0') }}, failed: ${{ steps['get-setup-job'].output.body.status.failed | default('0') }}

**Gateway:** https://saw-${{ parameters.name }}-gateway-__PIPELINES_NS__.__APPS_DOMAIN__

**Dashboard:** https://saw-${{ parameters.name }}-dashboard-__PIPELINES_NS__.__APPS_DOMAIN__
Loading