Logo

dev-resources.site

for different kinds of informations.

Easier Relationship Mapping in the Backstage Catalog

Published at
10/7/2024
Categories
backstage
developerportal
platformengineering
relationships
Author
sam_blausten_ad9891c7a528
Author
25 person written this
sam_blausten_ad9891c7a528
open
Easier Relationship Mapping in the Backstage Catalog

In the Backstage software catalog, relationships provide a glue between the different software, systems, resources and people in your organisation. They can allow you to easily answer questions which otherwise might be much harder to find out in a large or rapidly growing organisation.

Plugins like the Catalog Graph Plugin allow powerful visualisation of these relationship graphs.

Catalog Graph Plugin displaying relationships

Similarly, relationships can become a core piece of information displayed on entity Overview pages.

Relationships list

Relationships can be added in one of two ways - via a YAML file update in an SCM repository, or in a third party source like GitHub Teams or Okta. A YAML update looks something like this:

kind: Component
metadata:
    name: identity-backend
spec:
    ...
+   dependsOn:
+       - component:default/users-backend
+       - resource:aws/identity-backend-ec2
Enter fullscreen mode Exit fullscreen mode

Restrictive Relationships

There are a very limited and restrictive set of allowed relationships between different Kinds in Backstage by default. Knowing what is allowed requires looking up the Backstage documentation each time to check the schemas.

For instance, a Domain cannot “depend on” anything in the default Backstage relationship model. However, maybe you have a domain like Shipping that depends on another like Identity. The Identity domain exposes APIs for customer addresses that the Shipping domain breaks without access to. You might even want to directly model the hard dependency on specific external API’s inside the Identity domain so that its easy to identify issues and notify the right people in charge of the Shipping domain when something breaks in those APIs.

Confusingly, many of the default relationships terms that can be used in the YAML spec definition don not map directly to the actual relationship in the catalog. For instance if you want to add a partOf relationship for a Domain to another Domain, you would need to use subdomainOf . Adding it as an explicit partOf field would not work unless you add that in the extended processor as we’ll see shortly. Each Kind has its own differing semantics for each relationship type, which needs to be referenced and checked to ensure a relationship is correctly added.

Expanding available relationships

Luckily its an easy engineering task to add a processor that can handle more expansive relationships and even new terms for those relationships such as manages/managedBy for Users. This processor would run in addition to the BuiltinKindsEntityProcessor that does the default relationship processing for the catalog and would look something like this:

import { CatalogProcessor, CatalogProcessorEmit, processingResult } from '@backstage/plugin-catalog-node';
import { Entity, getCompoundEntityRef, parseEntityRef, RELATION_DEPENDENCY_OF, RELATION_DEPENDS_ON } from '@backstage/catalog-model';
import { LocationSpec } from '@backstage/plugin-catalog-common';
import { RELATIONSHIP_MANAGED_BY, RELATIONSHIP_MANAGES } from '../constants';

export class ExtendedRelationshipsProcessor implements CatalogProcessor {
  getProcessorName(): string {
    return 'ExtendedRelationshipsProcessor';
  }

  postProcessEntity(
    entity: Entity,
    _location: LocationSpec,
    emit: CatalogProcessorEmit,
  ): Promise<Entity> {
    const selfRef = getCompoundEntityRef(entity);
    function doEmit(
      targets: string | string[] | undefined | null,
      context: { defaultKind?: string; defaultNamespace: string },
      outgoingRelation: string,
      incomingRelation: string,
    ): void {
      if (!targets) {
        return;
      }
      for (const target of [targets].flat()) {
        const targetRef = parseEntityRef(target, context);
        emit(
          processingResult.relation({
            source: selfRef,
            type: outgoingRelation,
            target: {
              kind: targetRef.kind,
              namespace: targetRef.namespace,
              name: targetRef.name,
            },
          }),
        );
        emit(
          processingResult.relation({
            source: {
              kind: targetRef.kind,
              namespace: targetRef.namespace,
              name: targetRef.name,
            },
            type: incomingRelation,
            target: selfRef,
          }),
        );
      }
    }

    if (entity.kind === 'Domain') {
      doEmit(
        entity.spec?.dependsOn as string[],
        { defaultKind: 'System', defaultNamespace: selfRef.namespace },
        RELATION_DEPENDS_ON,
        RELATION_DEPENDENCY_OF,
      );
      doEmit(
        entity.spec?.dependencyOf as string[],
        { defaultNamespace: selfRef.namespace },
        RELATION_DEPENDENCY_OF,
        RELATION_DEPENDS_ON,
      );
    }

    if (entity.kind === 'User') {
      doEmit(
        entity.spec?.managedBy as string,
        { defaultKind: 'User', defaultNamespace: selfRef.namespace },
        RELATIONSHIP_MANAGED_BY,
        RELATIONSHIP_MANAGES,
      );
      doEmit(
        entity.spec?.manages as string[],
        { defaultKind: 'User', defaultNamespace: selfRef.namespace },
        RELATIONSHIP_MANAGES,
        RELATIONSHIP_MANAGED_BY,
      );
    }

    if (entity.kind === 'Group') {
      doEmit(
        entity.spec?.managedBy as string,
        { defaultKind: 'User', defaultNamespace: selfRef.namespace },
        RELATIONSHIP_MANAGED_BY,
        RELATIONSHIP_MANAGES,
      );
    }

    return Promise.resolve(entity);
  }
}
Enter fullscreen mode Exit fullscreen mode

.

Questions to consider

There is a rational that restricting the available relationships for each kind in the catalog prevents misuse of relationships by users adding things to the catalog. However it is worth bearing in mind that relationships in the catalog are mostly just syntactic sugar over the same link between two entities. There is nothing different about dependsOn and partOf in Backstage except the perceived meaning of these terms.

Educating users

Easily accessible documentation on standards and definitions is the best way to educate contributing users to your Backstage catalog. Backstage has its own schema docs and descriptions on existing relationships but you can also host internal documentation in a more compact format that is easier to reference quickly.

What we did to fix it

At Roadie, we’ve added expanded relationships for all kinds in the catalog to make it easier for people to correctly add relationships using the terms they understand. We’ve documented them in a compact format and are working on a UI based editor for these relationships so that users do not have to manually edit each YAML file and go through a PR process to build up a comprehensive mapping of dependencies in an organization.

We've also added a new card that can show all relationships for an entity, regardless of what kind of relationship it is in list format.

Relationships list

Contact us via Discord on on this website's chat to find out more about what Roadie can help with or to talk to our engineers about this topic or other issues you might be having adopting Backstage.

platformengineering Article's
30 articles in total
Favicon
Internal Developer Platform or Internal Developer Portal? Which is Right For You?
Favicon
Understanding the Backstage System Model
Favicon
Rely.io October 2024 Product Update Roundup
Favicon
Kubecon Salt Lake City - Themes and Highlights
Favicon
Platform Engineering : découvrez la puissance de Backstage.io
Favicon
Top Backstage alternatives
Favicon
Enhanced Observability for Amazon EKS with CloudWatch Container Insights
Favicon
Rely.io October 2024 Product Update Roundup
Favicon
DORA: Only 10% of developers see big productivity boost by AI
Favicon
Simplify Authorization Management with Cedar by AWS
Favicon
Microplatforms
Favicon
Simplify Kubernetes Resource Management with KRO by AWS
Favicon
Rely.io September 2024 Product Update Roundup
Favicon
How Amplemarket increased their product velocity and drove operational excellence with Rely.io
Favicon
Optimize Cloud Migration with Kubernetes
Favicon
The Ultimate Guide to Backstage Software Catalog Completeness
Favicon
The Wrong Way to Use DORA Metrics
Favicon
Easier Relationship Mapping in the Backstage Catalog
Favicon
Internal Developer Portals: Autonomy, Governance and the Golden Path
Favicon
Amazon EKS add-ons: A curated set of software for managing your Kubernetes clusters
Favicon
Manage secrets in AWS EKS with AWS Secrets Manager securely
Favicon
Adopting Backstage - Documentation and Support
Favicon
Limitations in Measuring Platform Engineering with DORA Metrics
Favicon
7 Reasons Why Developer Experience Is a Strategic Priority
Favicon
Improving Backstage performance (by up to 48x)
Favicon
15-day free trial available...now!
Favicon
Data on Kubernetes: Part 8 - Exploring TiDB, a cloud-native database
Favicon
Data on Kubernetes: Part 8 - Exploring TiDB, a cloud-native database
Favicon
Data on Kubernetes: Part 7 - K8ssandra: Bring Apache Cassandra to AWS EKS
Favicon
How to Be an Effective Platform Engineering Team

Featured ones: