Administration guide for Red Hat Developer Hub
Administration guide for Red Hat Developer Hub Red Hat Developer Hub 1.2 Red Hat Customer Content Services
kind: ConfigMap apiVersion: v1 metadata: name: app-config-rhdh data: app-config-rhdh.yaml: | app: title: {product} ... other Red Hat Developer Hub Helm Chart configurations upstream: backstage: extraAppConfig: - configMapRef: app-config-rhdh filename: app-config-rhdh.yaml ... other Red Hat Developer Hub Helm Chart configurations kind: Backstage apiVersion: rhdh.redhat.com/v1alpha1 metadata: name: app-config-rhdh data: "app-config-rhdh.yaml": | app: title: Red Hat Developer Hub baseUrl: <RHDH_URL> 1 backend: auth: keys: - secret: "USD{BACKEND_SECRET}" 2 baseUrl: <RHDH_URL> 3 cors: origin: <RHDH_URL> 4 node -p 'require("crypto").randomBytes(24).toString("base64")' apiVersion: rhdh.redhat.com/v1alpha1 kind: Backstage metadata: name: developer-hub spec: application: appConfig: mountPath: /opt/app-root/src configMaps: - name: app-config-rhdh extraEnvs: secrets: - name: secrets-rhdh extraFiles: mountPath: /opt/app-root/src replicas: 1 route: enabled: true database: enableLocalDb: true cat <<EOF | oc -n <your-namespace> create -f - apiVersion: v1 kind: Secret metadata: name: <crt-secret> 1 type: Opaque stringData: postgres-ca.pem: |- -----BEGIN CERTIFICATE----- <ca-certificate-key> 2 postgres-key.key: |- -----BEGIN CERTIFICATE----- <tls-private-key> 3 postgres-crt.pem: |- -----BEGIN CERTIFICATE----- <tls-certificate-key> 4 # EOF cat <<EOF | oc -n <your-namespace> create -f - apiVersion: v1 kind: Secret metadata: name: <cred-secret> 1 type: Opaque stringData: 2 POSTGRES_PASSWORD: <password> POSTGRES_PORT: "<db-port>" POSTGRES_USER: <username> POSTGRES_HOST: <db-host> PGSSLMODE: <ssl-mode> # for TLS connection 3 NODE_EXTRA_CA_CERTS: <abs-path-to-pem-file> # for TLS connection, e.g. /opt/app-root/src/postgres-crt.pem 4 EOF cat <<EOF | oc -n <your-namespace> create -f - apiVersion: rhdh.redhat.com/v1alpha1 kind: Backstage metadata: name: <backstage-instance-name> spec: database: enableLocalDb: false 1 application: extraFiles: mountPath: <path> # e g /opt/app-root/src secrets: - name: <crt-secret> 2 key: postgres-crt.pem, postgres-ca.pem, postgres-key.key # key name as in <crt-secret> Secret extraEnvs: secrets: - name: <cred-secret> 3 # cat <<EOF | oc -n <your-namespace> create -f - apiVersion: v1 kind: Secret metadata: name: <crt-secret> 1 type: Opaque stringData: postgres-ca.pem: |- -----BEGIN CERTIFICATE----- <ca-certificate-key> 2 postgres-key.key: |- -----BEGIN CERTIFICATE----- <tls-private-key> 3 postgres-crt.pem: |- -----BEGIN CERTIFICATE----- <tls-certificate-key> 4 # EOF cat <<EOF | oc -n <your-namespace> create -f - apiVersion: v1 kind: Secret metadata: name: <cred-secret> 1 type: Opaque stringData: 2 POSTGRES_PASSWORD: <password> POSTGRES_PORT: "<db-port>" POSTGRES_USER: <username> POSTGRES_HOST: <db-host> PGSSLMODE: <ssl-mode> # for TLS connection 3 NODE_EXTRA_CA_CERTS: <abs-path-to-pem-file> # for TLS connection, e.g. /opt/app-root/src/postgres-crt.pem 4 EOF upstream: postgresql: enabled: false # disable PostgreSQL instance creation 1 auth: existingSecret: <cred-secret> # inject credentials secret to Backstage 2 backstage: appConfig: backend: database: connection: # configure Backstage DB connection parameters host: USD{POSTGRES_HOST} port: USD{POSTGRES_PORT} user: USD{POSTGRES_USER} password: USD{POSTGRES_PASSWORD} ssl: rejectUnauthorized: true, ca: USDfile: /opt/app-root/src/postgres-ca.pem key: USDfile: /opt/app-root/src/postgres-key.key cert: USDfile: /opt/app-root/src/postgres-crt.pem extraEnvVarsSecrets: - <cred-secret> # inject credentials secret to Backstage 3 extraEnvVars: - name: BACKEND_SECRET valueFrom: secretKeyRef: key: backend-secret name: '{{ include "janus-idp.backend-secret-name" USD }}' extraVolumeMounts: - mountPath: /opt/app-root/src/dynamic-plugins-root name: dynamic-plugins-root - mountPath: /opt/app-root/src/postgres-crt.pem name: postgres-crt # inject TLS certificate to Backstage cont. 4 subPath: postgres-crt.pem - mountPath: /opt/app-root/src/postgres-ca.pem name: postgres-ca # inject CA certificate to Backstage cont. 5 subPath: postgres-ca.pem - mountPath: /opt/app-root/src/postgres-key.key name: postgres-key # inject TLS private key to Backstage cont. 6 subPath: postgres-key.key extraVolumes: - ephemeral: volumeClaimTemplate: spec: accessModes: - ReadWriteOnce resources: requests: storage: 1Gi name: dynamic-plugins-root - configMap: defaultMode: 420 name: dynamic-plugins optional: true name: dynamic-plugins - name: dynamic-plugins-npmrc secret: defaultMode: 420 optional: true secretName: dynamic-plugins-npmrc - name: postgres-crt secret: secretName: <crt-secret> 7 # helm upgrade -n <your-namespace> <your-deploy-name> openshift-helm-charts/redhat-developer-hub -f values.yaml --version 1.2.6 port-forward -n <your-namespace> <pgsql-pod-name> <forward-to-port>:<forward-from-port> port-forward -n developer-hub backstage-psql-developer-hub-0 15432:5432 #!/bin/bash to_host=<db-service-host> 1 to_port=5432 2 to_user=postgres 3 from_host=127.0.0.1 4 from_port=15432 5 from_user=postgres 6 allDB=("backstage_plugin_app" "backstage_plugin_auth" "backstage_plugin_catalog" "backstage_plugin_permission" "backstage_plugin_scaffolder" "backstage_plugin_search") 7 for db in USD{!allDB[@]}; do db=USD{allDB[USDdb]} echo Copying database: USDdb PGPASSWORD=USDTO_PSW psql -h USDto_host -p USDto_port -U USDto_user -c "create database USDdb;" pg_dump -h USDfrom_host -p USDfrom_port -U USDfrom_user -d USDdb | PGPASSWORD=USDTO_PSW psql -h USDto_host -p USDto_port -U USDto_user -d USDdb done /bin/bash TO_PSW=<destination-db-password> /path/to/db_copy.sh 1 spec: database: enableLocalDb: false application: # extraFiles: secrets: - name: <crt-secret> key: postgres-crt.pem # key name as in <crt-secret> Secret extraEnvs: secrets: - name: <cred-secret> -n developer-hub delete pvc <local-psql-pvc-name> get pods -n <your-namespace> apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: backstage-read-only rules: - apiGroups: - '*' resources: - pods - configmaps - services - deployments - replicasets - horizontalpodautoscalers - ingresses - statefulsets - limitranges - resourcequotas - daemonsets verbs: - get - list - watch # kind: ConfigMap apiVersion: v1 metadata: name: dynamic-plugins-rhdh data: dynamic-plugins.yaml: | includes: - dynamic-plugins.default.yaml plugins: - package: ./dynamic-plugins/dist/backstage-plugin-kubernetes-backend-dynamic disabled: false 1 - package: ./dynamic-plugins/dist/backstage-plugin-kubernetes disabled: false 2 # kind: ConfigMap apiVersion: v1 metadata: name: app-config-rhdh data: "app-config-rhdh.yaml": | # catalog: rules: - allow: [Component, System, API, Resource, Location] providers: kubernetes: openshift: cluster: openshift processor: namespaceOverride: default defaultOwner: guests schedule: frequency: seconds: 30 timeout: seconds: 5 kubernetes: serviceLocatorMethod: type: 'multiTenant' clusterLocatorMethods: - type: 'config' clusters: - url: <target-cluster-api-server-url> 1 name: openshift authProvider: 'serviceAccount' skipTLSVerify: false 2 skipMetricsLookup: true dashboardUrl: <target-cluster-console-url> 3 dashboardApp: openshift serviceAccountToken: USD{K8S_SERVICE_ACCOUNT_TOKEN} 4 caData: USD{K8S_CONFIG_CA_DATA} 5 # -n rhdh-operator get pods -w global: dynamic: plugins: - package: './dynamic-plugins/dist/janus-idp-backstage-plugin-analytics-provider-segment' disabled: true kind: ConfigMap apiVersion: v1 metadata: name: dynamic-plugins-rhdh data: dynamic-plugins.yaml: | includes: - dynamic-plugins.default.yaml plugins: - package: './dynamic-plugins/dist/janus-idp-backstage-plugin-analytics-provider-segment' disabled: true spec: application: dynamicPluginsConfigMapName: dynamic-plugins-rhdh global: dynamic: plugins: - package: './dynamic-plugins/dist/janus-idp-backstage-plugin-analytics-provider-segment' disabled: false kind: ConfigMap apiVersion: v1 metadata: name: dynamic-plugins-rhdh data: dynamic-plugins.yaml: | includes: - dynamic-plugins.default.yaml plugins: - package: './dynamic-plugins/dist/janus-idp-backstage-plugin-analytics-provider-segment' disabled: false spec: application: dynamicPluginsConfigMapName: dynamic-plugins-rhdh upstream: backstage: extraEnvVars: - name: SEGMENT_WRITE_KEY value: <segment_key> 1 spec: application: extraEnvs: envs: - name: SEGMENT_WRITE_KEY value: <segment_key> 1 upstream: metrics: serviceMonitor: enabled: true path: /metrics apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: name: <custom_resource_name> 1 namespace: <project_name> 2 labels: app.kubernetes.io/instance: <custom_resource_name> app.kubernetes.io/name: backstage spec: namespaceSelector: matchNames: - <project_name> selector: matchLabels: rhdh.redhat.com/app: backstage-<custom_resource_name> endpoints: - port: backend path: '/metrics' apply -f <filename> upstream: backstage: extraEnvVars: - name: HTTP_PROXY value: '<http_proxy_url>' - name: HTTPS_PROXY value: '<https_proxy_url>' - name: NO_PROXY value: '<no_proxy_settings>' upstream: backstage: extraEnvVars: - name: HTTP_PROXY value: 'http://10.10.10.105:3128' - name: HTTPS_PROXY value: 'http://10.10.10.106:3128' - name: NO_PROXY value: 'localhost,example.org' Other fields omitted deployment.yaml: |- apiVersion: apps/v1 kind: Deployment spec: template: spec: # Other fields omitted initContainers: - name: install-dynamic-plugins # command omitted env: - name: NPM_CONFIG_USERCONFIG value: /opt/app-root/src/.npmrc.dynamic-plugins - name: HTTP_PROXY value: 'http://10.10.10.105:3128' - name: HTTPS_PROXY value: 'http://10.10.10.106:3128' - name: NO_PROXY value: 'localhost,example.org' # Other fields omitted containers: - name: backstage-backend # Other fields omitted env: - name: APP_CONFIG_backend_listen_port value: "7007" - name: HTTP_PROXY value: 'http://10.10.10.105:3128' - name: HTTPS_PROXY value: 'http://10.10.10.106:3128' - name: NO_PROXY value: 'localhost,example.org' spec: # Other fields omitted application: extraEnvs: envs: - name: HTTP_PROXY value: 'http://10.10.10.105:3128' - name: HTTPS_PROXY value: 'http://10.10.10.106:3128' - name: NO_PROXY value: 'localhost,example.org' upstream: backstage: # --- TRUNCATED --- podAnnotations: prometheus.io/scrape: 'true' prometheus.io/path: '/metrics' prometheus.io/port: '7007' prometheus.io/scheme: 'http' Update OPERATOR_NS accordingly OPERATOR_NS=rhdh-operator edit configmap backstage-default-config -n "USD{OPERATOR_NS}" deployment.yaml: |- apiVersion: apps/v1 kind: Deployment # --- truncated --- spec: template: # --- truncated --- metadata: labels: rhdh.redhat.com/app: # placeholder for 'backstage-<cr-name>' # --- truncated --- annotations: prometheus.io/scrape: 'true' prometheus.io/path: '/metrics' prometheus.io/port: '7007' prometheus.io/scheme: 'http' # --- truncated --- --namespace=prometheus port-forward deploy/prometheus-server 9090 upstream: backstage: # --- Truncated --- extraEnvVars: - name: LOG_LEVEL value: debug spec: # Other fields omitted application: extraEnvs: envs: - name: LOG_LEVEL value: debug fields @timestamp, @message, kubernetes.container_name | filter kubernetes.container_name in ["install-dynamic-plugins", "backstage-backend"] apiVersion: v1 kind: ConfigMap metadata: name: app-config-rhdh data: "app-config-rhdh.yaml": | # --- Truncated --- app: title: Red Hat Developer Hub signInPage: oidc auth: environment: production session: secret: USD{AUTH_SESSION_SECRET} providers: oidc: production: clientId: USD{AWS_COGNITO_APP_CLIENT_ID} clientSecret: USD{AWS_COGNITO_APP_CLIENT_SECRET} metadataUrl: USD{AWS_COGNITO_APP_METADATA_URL} callbackUrl: USD{AWS_COGNITO_APP_CALLBACK_URL} scope: 'openid profile email' prompt: auto apiVersion: v1 kind: Secret metadata: name: secrets-rhdh stringData: AUTH_SESSION_SECRET: "my super auth session secret - change me!!!" AWS_COGNITO_APP_CLIENT_ID: "my-aws-cognito-app-client-id" AWS_COGNITO_APP_CLIENT_SECRET: "my-aws-cognito-app-client-secret" AWS_COGNITO_APP_METADATA_URL: "https://cognito-idp.[region].amazonaws.com/[userPoolId]/.well-known/openid-configuration" AWS_COGNITO_APP_CALLBACK_URL: "https://[rhdh_dns]/api/auth/oidc/handler/frame" upstream: backstage: image: pullSecrets: - rhdh-pull-secret podSecurityContext: fsGroup: 2000 extraAppConfig: - filename: app-config-rhdh.yaml configMapRef: app-config-rhdh extraEnvVarsSecrets: - secrets-rhdh helm upgrade rhdh openshift-helm-charts/redhat-developer-hub [--version 1.2.6] --values /path/to/values.yaml apiVersion: v1 kind: ConfigMap metadata: name: app-config-rhdh data: "app-config-rhdh.yaml": | # --- Truncated --- signInPage: oidc auth: # Production to disable guest user login environment: production # Providing an auth.session.secret is needed because the oidc provider requires session support. session: secret: USD{AUTH_SESSION_SECRET} providers: oidc: production: # See https://github.com/backstage/backstage/blob/master/plugins/auth-backend-module-oidc-provider/config.d.ts clientId: USD{AWS_COGNITO_APP_CLIENT_ID} clientSecret: USD{AWS_COGNITO_APP_CLIENT_SECRET} metadataUrl: USD{AWS_COGNITO_APP_METADATA_URL} callbackUrl: USD{AWS_COGNITO_APP_CALLBACK_URL} # Minimal set of scopes needed. Feel free to add more if needed. scope: 'openid profile email' # Note that by default, this provider will use the 'none' prompt which assumes that your are already logged on in the IDP. # You should set prompt to: # - auto: will let the IDP decide if you need to log on or if you can skip login when you have an active SSO session # - login: will force the IDP to always present a login form to the user prompt: auto apiVersion: v1 kind: Secret metadata: name: secrets-rhdh stringData: # --- Truncated --- # TODO: Change auth session secret. AUTH_SESSION_SECRET: "my super auth session secret - change me!!!" # TODO: user pool app client ID AWS_COGNITO_APP_CLIENT_ID: "my-aws-cognito-app-client-id" # TODO: user pool app client Secret AWS_COGNITO_APP_CLIENT_SECRET: "my-aws-cognito-app-client-secret" # TODO: Replace region and user pool ID AWS_COGNITO_APP_METADATA_URL: "https://cognito-idp.[region].amazonaws.com/[userPoolId]/.well-known/openid-configuration" # TODO: Replace <rhdh_dns> AWS_COGNITO_APP_CALLBACK_URL: "https://[rhdh_dns]/api/auth/oidc/handler/frame" apiVersion: rhdh.redhat.com/v1alpha1 kind: Backstage metadata: # TODO: this the name of your Developer Hub instance name: my-rhdh spec: application: imagePullSecrets: - "rhdh-pull-secret" route: enabled: false appConfig: configMaps: - name: "app-config-rhdh" extraEnvs: secrets: - name: "secrets-rhdh" delete deployment -l app.kubernetes.io/instance=<CR_NAME> az aks create/update --resource-group <your-ResourceGroup> --name <your-Cluster> --enable-azure-monitor-metrics auth: environment: production providers: microsoft: production: clientId: USD{AZURE_CLIENT_ID} clientSecret: USD{AZURE_CLIENT_SECRET} tenantId: USD{AZURE_TENANT_ID} domainHint: USD{AZURE_TENANT_ID} additionalScopes: - Mail.Send -n <your_namespace> apply -f <app-config>.yaml stringData: AZURE_CLIENT_ID: <value-of-clientId> AZURE_CLIENT_SECRET: <value-of-clientSecret> AZURE_TENANT_ID: <value-of-tenantId> -n <your_namespace> apply -f <azure-secrets>.yaml upstream: backstage: extraAppConfig: - filename: configMapRef: <app-config-containing-azure> extraEnvVarsSecrets: - <secret-containing-azure> helm -n <your_namespace> upgrade -f <your-values.yaml> <your_deploy_name> redhat-developer/backstage --version 1.2.6 -n <your_namespace> delete pods -l backstage.io/app=backstage-<your-rhdh-cr> auth: environment: production providers: microsoft: production: clientId: USD{AZURE_CLIENT_ID} clientSecret: USD{AZURE_CLIENT_SECRET} tenantId: USD{AZURE_TENANT_ID} domainHint: USD{AZURE_TENANT_ID} additionalScopes: - Mail.Send -n <your_namespace> apply -f <app-config>.yaml stringData: AZURE_CLIENT_ID: <value-of-clientId> AZURE_CLIENT_SECRET: <value-of-clientSecret> AZURE_TENANT_ID: <value-of-tenantId> -n <your_namespace> apply -f <azure-secrets>.yaml apiVersion: rhdh.redhat.com/v1alpha1 kind: Backstage metadata: name: <your-rhdh-cr> spec: application: imagePullSecrets: - rhdh-pull-secret route: enabled: false appConfig: configMaps: - name: <app-config-containing-azure> extraEnvs: secrets: - name: <secret-containing-azure> -n <your_namespace> apply -f rhdh.yaml -n <your_namespace> delete pods -l backstage.io/app=backstage-<your-rhdh-cr> ui:options: allowedHosts: - github.com apiVersion: scaffolder.backstage.io/v1beta3 kind: Template metadata: name: template-name 1 title: Example template 2 description: An example template for v1beta3 scaffolder. 3 spec: owner: backstage/techdocs-core 4 type: service 5 parameters: 6 - title: Fill in some steps required: - name properties: name: title: Name type: string description: Unique name of the component owner: title: Owner type: string description: Owner of the component - title: Choose a location required: - repoUrl properties: repoUrl: title: Repository Location type: string steps: 7 - id: fetch-base name: Fetch Base action: fetch:template # output: 8 links: - title: Repository 9 url: USD{{ steps['publish'].output.remoteUrl }} - title: Open in catalog 10 icon: catalog entityRef: USD{{ steps['register'].output.entityRef }} catalog: rules: - allow: [Template] 1 locations: - type: url 2 target: https://<repository_url>/example-template.yaml 3 apiVersion: objectbucket.io/v1alpha1 kind: ObjectBucketClaim metadata: name: <rhdh_bucket_claim_name> spec: generateBucketName: <rhdh_bucket_claim_name> storageClassName: openshift-storage.noobaa.io upstream: backstage: extraEnvVarsSecrets: - <rhdh_bucket_claim_name> extraEnvVarsCM: - <rhdh_bucket_claim_name> global: dynamic: includes: - 'dynamic-plugins.default.yaml' plugins: - disabled: false package: ./dynamic-plugins/dist/backstage-plugin-techdocs-backend-dynamic pluginConfig: techdocs: builder: external generator: runIn: local publisher: awsS3: bucketName: 'USD{BUCKET_NAME}' credentials: accessKeyId: 'USD{AWS_ACCESS_KEY_ID}' secretAccessKey: 'USD{AWS_SECRET_ACCESS_KEY}' endpoint: 'https://USD{BUCKET_HOST}' region: 'USD{BUCKET_REGION}' s3ForcePathStyle: true type: awsS3 apiVersion: objectbucket.io/v1alpha1 kind: Backstage metadata: name: <name> spec: application: extraEnvs: configMaps: - name: <rhdh_bucket_claim_name> secrets: - name: <rhdh_bucket_claim_name> kind: ConfigMap apiVersion: v1 metadata: name: dynamic-plugins-rhdh data: dynamic-plugins.yaml: | includes: - dynamic-plugins.default.yaml plugins: - disabled: false package: ./dynamic-plugins/dist/backstage-plugin-techdocs-backend-dynamic pluginConfig: techdocs: builder: external generator: runIn: local publisher: awsS3: bucketName: 'USD{BUCKET_NAME}' credentials: accessKeyId: 'USD{AWS_ACCESS_KEY_ID}' secretAccessKey: 'USD{AWS_SECRET_ACCESS_KEY}' endpoint: 'https://USD{BUCKET_HOST}' region: 'USD{BUCKET_REGION}' s3ForcePathStyle: true type: awsS3 Prepare REPOSITORY_URL='https://github.com/org/repo' git clone USDREPOSITORY_URL cd repo Install @techdocs/cli, mkdocs and mkdocs plugins npm install -g @techdocs/cli pip install "mkdocs-techdocs-core==1.*" Generate techdocs-cli generate --no-docker Publish techdocs-cli publish --publisher-type awsS3 --storage-name <bucket/container> --entity <Namespace/Kind/Name> git clone <https://path/to/docs-repository/> npm install -g npx npm install -g @techdocs/cli pip install "mkdocs-techdocs-core==1.*" npx @techdocs/cli generate --no-docker --source-dir <path_to_repo> --output-dir ./site npx @techdocs/cli publish --publisher-type <awsS3|googleGcs> --storage-name <bucket/container> --entity <namespace/kind/name> --directory ./site name: Publish TechDocs Site on: push: branches: [main] # You can even set it to run only when TechDocs related files are updated. # paths: # - "docs/**" # - "mkdocs.yml" jobs: publish-techdocs-site: runs-on: ubuntu-latest # The following secrets are required in your CI environment for publishing files to AWS S3. # e.g. You can use GitHub Organization secrets to set them for all existing and new repositories. env: TECHDOCS_S3_BUCKET_NAME: USD{{ secrets.TECHDOCS_S3_BUCKET_NAME }} AWS_ACCESS_KEY_ID: USD{{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: USD{{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_REGION: USD{{ secrets.AWS_REGION }} ENTITY_NAMESPACE: 'default' ENTITY_KIND: 'Component' ENTITY_NAME: 'my-doc-entity' # In a Software template, Scaffolder will replace {{cookiecutter.component_id | jsonify}} # with the correct entity name. This is same as metadata.name in the entity's catalog-info.yaml # ENTITY_NAME: '{{ cookiecutter.component_id | jsonify }}' steps: - name: Checkout code uses: actions/checkout@v3 - uses: actions/setup-node@v3 - uses: actions/setup-python@v4 with: python-version: '3.9' - name: Install techdocs-cli run: sudo npm install -g @techdocs/cli - name: Install mkdocs and mkdocs plugins run: python -m pip install mkdocs-techdocs-core==1.* - name: Generate docs site run: techdocs-cli generate --no-docker --verbose - name: Publish docs site run: techdocs-cli publish --publisher-type awsS3 --storage-name USDTECHDOCS_S3_BUCKET_NAME --entity USDENTITY_NAMESPACE/USDENTITY_KIND/USDENTITY_NAME