PlatformEngr® Backstage plugins course

Module 09 · Secure and extend · 60 min · You are: Platform and product

Capstone: team-checkout's ownership check

In this module

  • You learn: Nothing new: the capstone checks you can use the backend system, the frontend and Git together.
  • You do: As team-checkout, write a module with an owner-is-team check and a test, and add a runbook link. As the platform team, prove it.
  • You end with: Seven checks, checkout at 100%, every test green, all merged.

A plugin is done when other teams can extend it without asking you. If team-checkout can ship a check that calls the catalog on their own, the extension point works.

Open this module in the interactive course to track progress.

Concepts

Acceptance criteria
Each challenge lists what must be true at the end. Check them yourself before you open the solution.

Files to know

Open them in your editor or on GitHub; the bullets say what to look at.

Steps

1. team-checkout: owned by a team (Challenge)

Done when:

Hint 1

Copy readiness-backend-module-api and its yarn new command.

Hint 2

The module can ask for catalogServiceRef and coreServices.auth; call the catalog with auth.getOwnServiceCredentials().

Hint 3

parseEntityRef(owner, { defaultKind: 'group' }) turns team-checkout into a full ref.

One solution

A module like module 04's, with two services.

cd ~/work/portal && yarn new --select backend-plugin-module --option pluginId=readiness --option moduleId=ownership --option pluginPackage=@internal/backstage-plugin-readiness-backend
cd ~/work/portal && yarn --cwd plugins/readiness-backend-module-ownership add @backstage/catalog-model@^1.10.2 @backstage/plugin-catalog-node@^2.2.6 @internal/backstage-plugin-readiness-node@workspace:^
yarn --cwd plugins/readiness-backend-module-ownership add -D @internal/backstage-plugin-readiness-backend@workspace:^ supertest@^7.3.1 @types/supertest@^7.2.1
cp ~/work/backstage-workshop/portal/plugins/readiness-backend-module-ownership/src/module*.ts plugins/readiness-backend-module-ownership/src/
yarn tsc && yarn --cwd plugins/readiness-backend-module-ownership test --watchAll=false

Restart the portal, then expect seven checks, the last owner-is-team:

cd ~/work/portal && set -a && . ./.env && set +a
curl -s -H "Authorization: Bearer $PORTAL_API_TOKEN" localhost:7007/api/readiness/checks | jq -r '.items[].id'
cd ~/work/portal

Propose it as a pull request. Put it on its own branch and push it:

git switch -c readiness-ownership && git add -A && git commit -qm "Readiness: owner-is-team check, owned by team-checkout"
git push -qu origin HEAD

Open the pull request. It prints a link:

gh pr create --fill

Open that link. Files changed shows exactly your diff.

Merge it on GitHub (Squash and merge, then Delete branch), or from the terminal:

gh pr merge --squash --delete-branch

Bring your copy up to date:

git switch -q main && git pull -q

2. team-checkout: get to 100 (Challenge)

Done when:

Hint 1

The runbook page exists since the first course: docs/runbook.md.

Hint 2

The catalog reads the repo again within minutes; the catalog refresh API and Evaluate now make it immediate.

One solution

One link, then refresh and evaluate.

Add to ~/work/checkout/catalog-info.yaml under metadata.links, after the Source link

    - url: https://github.com/YOUR_GITHUB_USER/checkout/blob/main/docs/runbook.md
      title: Runbook
cd ~/work/checkout

Propose it as a pull request. Put it on its own branch and push it:

git switch -c runbook-link && git add -A && git commit -qm "Link the runbook"
git push -qu origin HEAD

Open the pull request. It prints a link:

gh pr create --fill

Open that link. Files changed shows exactly your diff.

Merge it on GitHub (Squash and merge, then Delete branch), or from the terminal:

gh pr merge --squash --delete-branch

Bring your copy up to date:

git switch -q main && git pull -q
cd ~/work/portal && set -a && . ./.env && set +a
curl -s -H "Authorization: Bearer $PORTAL_API_TOKEN" -X POST -H 'Content-Type: application/json' localhost:7007/api/catalog/refresh -d '{"entityRef":"component:default/checkout"}'
curl -s -H "Authorization: Bearer $PORTAL_API_TOKEN" -X POST -H 'Content-Type: application/json' localhost:7007/api/readiness/evaluate -d '{"entityRef":"component:default/checkout"}' | jq .score

3. Platform: prove it (Challenge)

Done when:

Hint 1

Run each package's tests with yarn --cwd plugins/<name> test --watchAll=false.

Hint 2

The 403 case is covered by the test you wrote in module 08.

One solution

Type-check and every test, in one go.

cd ~/work/portal && yarn tsc && for p in readiness readiness-backend readiness-backend-module-api readiness-backend-module-ownership permission-backend-module-acme; do yarn --cwd plugins/$p test --watchAll=false || break; done

4. Tear down

Stop the portal (Ctrl+C) and Postgres. Your repos keep everything.

docker stop portal-db

Quiz

  1. What did team-checkout need from the platform team to add owner-is-team?
    • Nothing: the extension point, yarn new and a review of their own folder
    • A change to defaultChecks in the readiness backend
    • A new permission that lets their module add checks
  2. Why does owner-is-team call the catalog with the plugin's own credentials?
    • Because user credentials expire before the check finishes
    • It runs in the scheduled job too, where there is no user
    • Because the catalog refuses user credentials from modules
  3. What would you build next for this plugin?
    • A second backend plugin that copies the checks for systems
    • A frontend setting that lets each user pick their own pass score
    • Notifications to the owning team when a score drops below the pass score
Answers
  1. Nothing: the extension point, yarn new and a review of their own folder That is what the extension point is for.
  2. It runs in the scheduled job too, where there is no user Background work acts as the plugin.
  3. Notifications to the owning team when a score drops below the pass score A score only helps when the owner hears about it.

Field note

If you did this without the solutions, you can build Backstage plugins the way the core team does. The next step is reading theirs: the catalog and scaffolder backends use every pattern from this course.

Report a problem with this module