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-teamcheck and a test, and add a runbook link. As the platform team, prove it. - You end with: Seven checks,
checkoutat 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.
portal/plugins/readiness-backend-module-ownership/src/module.ts(reference, your repo): The solution's module.
Steps
1. team-checkout: owned by a team (Challenge)
Done when:
- A package
plugins/readiness-backend-module-ownershipwith a module for pluginreadiness, module idownership - A check
owner-is-team, title Owned by a team: passes when the owner is a Group withspec.type: teamin the catalog - A test that starts the plugin with the module and proves a team passes and a person fails
/checkslists seven checks
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:
checkout'scatalog-info.yamllinks its runbook, with the title Runbook- Merged in the
checkoutrepo through a pull request - The Production readiness card on
checkoutsays100% Ready
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:
yarn tscis clean- The tests of all five plugin packages with tests pass
/readinesslistscheckoutat 100%- A user outside
platformcannot evaluate; the API answers 403Not allowed: readiness.evaluate
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
- What did team-checkout need from the platform team to add
owner-is-team?- Nothing: the extension point,
yarn newand a review of their own folder - A change to
defaultChecksin the readiness backend - A new permission that lets their module add checks
- Nothing: the extension point,
- Why does
owner-is-teamcall 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
- 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
- Nothing: the extension point,
yarn newand a review of their own folder That is what the extension point is for. - It runs in the scheduled job too, where there is no user Background work acts as the plugin.
- 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.