Skip to content

Instantly share code, notes, and snippets.

@vrabbi
Last active June 12, 2025 17:54
Show Gist options
  • Save vrabbi/c8a38b63027c726b6dd62da4d7ba05ac to your computer and use it in GitHub Desktop.
Save vrabbi/c8a38b63027c726b6dd62da4d7ba05ac to your computer and use it in GitHub Desktop.
Crossplane and Backstage POC

This works with backstage 1.33

Plugin overviews

There are 2 plugins in this POC:

  1. Kubernetes Ingestor - this plugin is a catalog entity provider which creates catalog entities directly from kubernetes resources. it has the ability to ingest by default all standard k8s workload types, allows supplying custom GVKs, and has the ability to auto-ingest all crossplane claims automatically as components. There are numerous annotations which can be put on the kubernetes workloads to influence the creation of the component in backstage. It also supports creating backstage templates and registers them in the catalog for every XRD in your cluster for the Claim resource type. currently this supports adding via a PR to a GitHub repo or providing a download link to the generated yaml without pushing to git.
  2. Crossplane Resource Frontend - this is a frontend plugin which provides visibility into the crossplane claim, composite resource and managed resources associated with a component. This relies heavily on system generated annotations from the Kubernetes Ingestor but technically does not require it if you add all the needed annotations manually. The plugin exposes general data, provides a YAML viewer for each resource including the ability to copy to clipboard the content or download the yaml file. It also supports viewing the events related to a specific resource. It also includes a graph view of the resources related to a claim.

Add The Kubernetes Ingestor Plugin

  • Add the package
yarn add --cwd packages/backend @vrabbi/backstage-plugin-kubernetes-ingestor
  • Add to backend (packages/backend/src/index.ts)
backend.add(import('@vrabbi/backstage-plugin-kubernetes-ingestor'));

Add The Crossplane Visualizer Plugin

  • Add the package
yarn add --cwd packages/app @vrabbi/backstage-plugin-crossplane-resources-frontend
  • Add to Entity Page (packages/app/src/components/catalog/EntityPage.tsx)
import { CrossplaneAllResourcesTable, CrossplaneResourceGraph, isCrossplaneAvailable } from '@vrabbi/backstage-plugin-crossplane-resources-frontend';

const serviceEntityPage = (
<EntityLayout>
  ...
  
  <EntityLayout.Route if={isCrossplaneAvailable} path="/crossplane-resources" title="Crossplane Resources">
    <CrossplaneAllResourcesTable />
  </EntityLayout.Route>
  <EntityLayout.Route if={isCrossplaneAvailable} path="/crossplane-graph" title="Crossplane Graph">
    <CrossplaneResourceGraph />
  </EntityLayout.Route>
</EntityLayout>
);

const componentPage = (
<EntitySwitch>
  ...
  <EntitySwitch.Case if={isComponentType('crossplane-claim')}>
    {serviceEntityPage}
  </EntitySwitch.Case>
  ...
);

Install Komoplane In Your Cluster/s (optional)

  • Add the helm repo
helm repo add komodorio https://helm-charts.komodor.io
  • Install the chart
helm upgrade --install komoplane komodorio/komoplane -n komoplane --create-namespace --set service.type=LoadBalancer

Configure Your app-config.yaml As Needed

  • you must have the kubernetes backend plugin installed
  • you currently need to use serviceAccount authentication with these plugins for the kubernetes plugin
  • available config options:
kubernetesIngestor:
  components:
    # Whether to enable creation of backstage components for Kubernetes workloads
    enabled: true
    taskRunner:
      # How often to query the clusters for data
      frequency: 10
      # Max time to process the data per cycle
      timeout: 600 
    # Namespaces to exclude the resources from
    excludedNamespaces: 
      - kube-public
      - kube-system
    # Custom Resource Types to also generate components for
    customWorkloadTypes:
      - group: pkg.crossplane.io
        apiVersion: v1
        plural: providers
    # By default all standard kubernetes workload types are ingested. This allows you to disable this behavior
    disableDefaultWorkloadTypes: false
    # Allows ingestion to be opt-in or opt-out by either requiring or not a dedicated annotation to ingest a resource (terasky.backstage.io/add-to-catalog or terasky.backstage.io/exclude-from-catalog)
    onlyIngestAnnotatedResources: false
  crossplane:
    claims:
      # Whether to create components for all claim resources in your cluster
      ingestAllClaims: true
    # If integrated with Komoplane, a link will be added to each crossplane component within backstage to the claims URL in Komoplane.
    komoplane:
        # The name of the cluster in the Kubernetes plugin
      - cluster: ${KUBERNETES_CLUSTER_NAME}
        # The base URL of Komoplane for this cluster
        baseUrl: "http://x.x.x.x:8090"
    xrds:
      # Settings related to the final steps of a software template
      publishPhase:
        # Whether the user should be able to select the repo they want to push the manifest to or not
        allowRepoSelection: true
        # What to publish to. currently supports GitHub and YAML (provides a link to download the file)
        target: github
        # If using GitHub as the target and allowRepoSelection is set to false, these values must be set to the hardcoded values.
        github:
          # Follows the backstage standard format which is github.com?owner=<REPO OWNER>&repo=<REPO NAME>
          repoUrl:
          targetBranch: main
      # Whether to enable the creation of software templates for all XRDs
      enabled: true
      taskRunner:
        # How often to query the clusters for data
        frequency: 10
        # Max time to process the data per cycle
        timeout: 600 
      # Allows ingestion to be opt-in or opt-out by either requiring or not a dedicated annotation to ingest a xrd (terasky.backstage.io/add-to-catalog or terasky.backstage.io/exclude-from-catalog)
      ingestAllXRDs: true
  • Supported annotations for auto ingestion of resources
General Annotations:
- terasky.backstage.io/add-to-catalog: Defaults to false. this is used when onlyIngestAnnotatedResources is set to true and or when ingestAllXRDs is set to false in the app-config.yaml
- terasky.backstage.io/exclude-from-catalog: Defaults to true. this is used when onlyIngestAnnotatedResources is set to false and or when ingestAllXRDs is set to true in the app-config.yaml
- terasky.backstage.io/system: Defaults to the kubernetes namespace of the resource
- terasky.backstage.io/backstage-namespace: Defaults to default
- terasky.backstage.io/owner: Defaults to kubernetes-auto-ingested
Namespace Annotations:
- terasky.backstage.io/system-type: Defaults to product
- terasky.backstage.io/domain: no default
Workload Resource Annotations:
- terasky.backstage.io/source-code-repo-url: no default
- terasky.backstage.io/source-branch: Defaults to main
- terasky.backstage.io/techdocs-path: no default
- terasky.backstage.io/kubernetes-label-selector: Only needed for non crossplane claims
- terasky.backstage.io/component-type: Defaults to service
- terasky.backstage.io/lifecycle: Defaults to production
- terasky.backstage.io/dependsOn: no default
- terasky.backstage.io/providesApis: no default
- terasky.backstage.io/consumesApis: no default
- terasky.backstage.io/component-annotations: no default - allows supplying nested annotation key value pairs to be added to components
apiVersion: v1
kind: Namespace
metadata:
name: yelb
---
apiVersion: v1
kind: Service
metadata:
name: redis-server
labels:
app: redis-server
tier: cache
spec:
type: ClusterIP
ports:
- port: 6379
selector:
app: redis-server
tier: cache
---
apiVersion: v1
kind: Service
metadata:
name: yelb-db
labels:
app: yelb-db
tier: backenddb
spec:
type: ClusterIP
ports:
- port: 5432
selector:
app: yelb-db
tier: backenddb
---
apiVersion: v1
kind: Service
metadata:
name: yelb-appserver
labels:
app: yelb-appserver
tier: middletier
spec:
type: ClusterIP
ports:
- port: 4567
selector:
app: yelb-appserver
tier: middletier
---
apiVersion: v1
kind: Service
metadata:
name: yelb-ui
labels:
app: yelb-ui
tier: frontend
spec:
type: LoadBalancer
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: yelb-ui
tier: frontend
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: yelb-ui
labels:
app: yelb-ui
tier: frontend
annotations:
terasky.backstage.io/add-to-catalog: 'true'
terasky.backstage.io/owner: dev-team
terasky.backstage.io/component-type: service
terasky.backstage.io/lifecycle: production
terasky.backstage.io/dependsOn: 'Component:yelb-appserver'
terasky.backstage.io/kubernetes-label-selector: 'app=yelb-ui,tier=frontend'
terasky.backstage.io/source-code-repo-url: "https://github.com/dambor/yelb-catalog"
terasky.backstage.io/source-branch: "main"
terasky.backstage.io/techdocs-path: "components/yelb-ui"
spec:
replicas: 1
selector:
matchLabels:
app: yelb-ui
tier: frontend
template:
metadata:
labels:
app: yelb-ui
tier: frontend
spec:
containers:
- name: yelb-ui
image: mreferre/yelb-ui:0.10
ports:
- containerPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: yelb-redis
labels:
app: redis-server
tier: cache
annotations:
terasky.backstage.io/add-to-catalog: 'true'
terasky.backstage.io/owner: dev-team
terasky.backstage.io/component-type: service
terasky.backstage.io/lifecycle: production
terasky.backstage.io/dependsOn: 'Component:yelb-redis'
terasky.backstage.io/kubernetes-label-selector: 'app=redis-server,tier=cache'
terasky.backstage.io/source-code-repo-url: "https://github.com/dambor/yelb-catalog"
terasky.backstage.io/source-branch: "main"
terasky.backstage.io/techdocs-path: "components/redis-server"
spec:
replicas: 1
selector:
matchLabels:
app: redis-server
tier: cache
template:
metadata:
labels:
app: redis-server
tier: cache
spec:
containers:
- name: redis-server
image: redis:4.0.2
ports:
- containerPort: 6379
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: yelb-db
labels:
app: yelb-db
tier: backenddb
annotations:
terasky.backstage.io/add-to-catalog: 'true'
terasky.backstage.io/owner: dev-team
terasky.backstage.io/component-type: service
terasky.backstage.io/lifecycle: experimental
terasky.backstage.io/kubernetes-label-selector: 'app=yelb-db,tier=backenddb'
terasky.backstage.io/source-code-repo-url: "https://github.com/dambor/yelb-catalog"
terasky.backstage.io/source-branch: "main"
terasky.backstage.io/techdocs-path: "components/yelb-db"
spec:
replicas: 1
selector:
matchLabels:
app: yelb-db
tier: backenddb
template:
metadata:
labels:
app: yelb-db
tier: backenddb
spec:
containers:
- name: yelb-db
image: mreferre/yelb-db:0.6
ports:
- containerPort: 5432
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: yelb-appserver
labels:
app: yelb-appserver
tier: middletier
annotations:
terasky.backstage.io/add-to-catalog: 'true'
terasky.backstage.io/owner: dev-team
terasky.backstage.io/component-type: service
terasky.backstage.io/lifecycle: experimental
terasky.backstage.io/dependsOn: 'Component:yelb-db,Component:yelb-redis'
terasky.backstage.io/kubernetes-label-selector: 'app=yelb-appserver,tier=middletier'
terasky.backstage.io/source-code-repo-url: "https://github.com/dambor/yelb-catalog"
terasky.backstage.io/source-branch: "main"
terasky.backstage.io/techdocs-path: "components/yelb-appserver"
spec:
replicas: 1
selector:
matchLabels:
app: yelb-appserver
tier: middletier
template:
metadata:
labels:
app: yelb-appserver
tier: middletier
spec:
containers:
- name: yelb-appserver
image: mreferre/yelb-appserver:0.7
ports:
- containerPort: 4567
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment