Module 06 · Product self-service · 60 min · You are: Product team
Onboard a product, declaratively
In this module
- You learn: How a product team gets its own project, namespace baseline and Applications from a single
product.yaml, without touching a cluster. And why a product names environments (dev, prod) while the platform decides which clusters those are. - You do: As the platform team, switch on product self-service. As team-checkout, request onboarding, propose
product.yamland the deployment config, and review theplan. Watch the platform build everything from one file, then onboard a second product yourself. - You end with:
checkoutruns in dev (6.7.1) and prod (6.7.0, two replicas), each in a namespace with quota, limits, network policies and access rules, inside its own Argo CD project. A pull request for a second product,catalog, is open.
This is the module the platform exists for. The product team describes what it needs in one file; the platform turns that into resources on every matching cluster. No ticket queue, no copy-paste of namespace YAML, and the platform team still reviews every new product.
Open this module in the interactive course to track progress and switch tracks.
Change journey practised: Requested → Proposed → Validated → Previewed → Approved → Merged → Reconciled → Healthy
Concepts
- Product declaration
products/<name>/product.yaml: owner, cost center, repository and path, Pod Security level, and a list of environments with a size each.- Environments, not clusters
- A product asks for
prod; the platform decides which clusters are prod (by thefleet.gitops/environmentlabel). A second prod cluster needs no product change. - Three ApplicationSets
product-projects(AppProject on the hub),product-namespaces(baseline per environment and cluster),product-apps(the workload, e.g.checkout-dev).- Nested matrix
- products times their
environments(anelementsYamllist), then times the clusters whose label matches. One file in,checkout-devandcheckout-prodout.
Files to know
Open them in your editor or on GitHub; the bullets say what to look at.
products/checkout/product.yaml(reference, your repo): The whole onboarding request. This is the file product teams write.products/schema.json(reference, your repo): What CI accepts. Two lines are enough.applicationsets/product-apps.yaml(reference, your repo): How one product file becomes checkout-dev and checkout-prod.product-deployments/checkout/dev/kustomization.yaml(reference, your repo): Team-owned config for dev.
Steps
1. Platform team: enable self-service once
The three product ApplicationSets and the schema are platform code. Propose, merge. The plan shows nothing yet: there are no products.
cd ~/work/fleet-gitops && git switch main && git pull -q
git switch -c product-onboarding
Copy the schema (the rules a product file must follow) and the three ApplicationSets:
git checkout reference/main -- products/schema.json products/README.md \
applicationsets/product-projects.yaml applicationsets/product-namespaces.yaml applicationsets/product-apps.yaml
scripts/set-repo-url.sh
Run the checks. Expect them to pass:
scripts/validate.sh
git add -A && git commit -m "Enable product self-service"
git push -u origin HEAD
2. team-checkout: request onboarding
Fill in the Onboard a product form: name checkout, owner team-checkout, cost center cc-4711, dev and prod, size small. Note the issue number.
3. team-checkout: propose the product
Take the declaration and config from the reference, and point request: at your issue so the namespace links back to it.
cd ~/work/fleet-gitops && git switch main && git pull -q
git switch -c onboard-checkout
Copy the product declaration and its deployment config from the reference:
git checkout reference/main -- products/checkout product-deployments/checkout
scripts/set-repo-url.sh
Change in ~/work/fleet-gitops/products/checkout/product.yaml
Use your onboarding issue's number instead of 2.
- request: https://github.com/platformengr/fleet-gitops/issues/1
+ request: https://github.com/YOUR_GITHUB_USER/fleet-gitops/issues/2
List what the platform will generate. Expect the product Applications checkout-dev and checkout-prod among them:
python3 scripts/fleet_check.py
git add -A && git commit -m "Onboard checkout"
git push -u origin HEAD
Open the pull request (write Closes #your-issue) ↗
4. Review it like the platform team
Read the plan comment: five new Applications (one project, two baselines, two apps). production-gate waits because a product declaration can reach prod: click Review deployments, approve, then merge.
5. Watch the platform translate one file
You wrote one file. Look at what came out of it.
Applications. Watch the product's Applications appear. Wait until they are Synced and Healthy, then press Ctrl-C.
kubectl --context kind-platform -n argocd get applications -l fleet.gitops/product=checkout -w
The fence. The product's Argo CD project may only deploy into its own namespace. Expect checkout as the namespace.
kubectl --context kind-platform -n argocd get appproject checkout -o jsonpath='{.spec.destinations}'; echo
The baseline in prod. Expect a quota, a limit range, three network policies and a role binding, none of which you wrote:
kubectl --context kind-prod -n checkout get resourcequota,limitrange,networkpolicy,rolebinding
The labels. Expect owner, cost center and the product name, copied from your product.yaml:
kubectl --context kind-prod get namespace checkout --show-labels
6. See it run
Run the port-forward in a new terminal and leave it running. Then open http://localhost:9898: the banner says checkout (dev) and the version is 6.7.1. Prod runs 6.7.0 with two replicas.
kubectl --context kind-dev -n checkout port-forward svc/checkout 9898:80
7. Your turn: onboard a second product
Onboard catalog for team-catalog yourself, by copying checkout. Push before adding the CODEOWNERS line and read what CI says: ownership is required metadata.
cd ~/work/fleet-gitops && git switch main && git pull -q
git switch -c onboard-catalog
Copy checkout as a starting point:
cp -r products/checkout products/catalog
cp -r product-deployments/checkout product-deployments/catalog
Change in ~/work/fleet-gitops/products/catalog/product.yaml, ~/work/fleet-gitops/product-deployments/catalog/base/*.yaml, ~/work/fleet-gitops/product-deployments/catalog/dev/kustomization.yaml, ~/work/fleet-gitops/product-deployments/catalog/prod/kustomization.yaml
Every occurrence, in all of these files: your editor's find and replace across files does it in one go. Then read each file once.
- checkout
+ catalog
Change in ~/work/fleet-gitops/products/catalog/product.yaml
- costCenter: cc-4711
+ costCenter: cc-4712
Run the check before the CODEOWNERS line exists. Expect it to fail: every product folder needs an owner.
python3 scripts/fleet_check.py --quiet
Add to ~/work/fleet-gitops/.github/CODEOWNERS at the end
/products/catalog/ @YOUR_GITHUB_USER
/product-deployments/catalog/ @YOUR_GITHUB_USER
Run it again. Expect no errors:
python3 scripts/fleet_check.py --quiet
git add -A && git commit -m "Onboard catalog"
git push -u origin HEAD
Simplified for the workshop
team-checkoutis a name here; on kind there is no identity provider, so module 08 impersonates the team with--as-group. In productionowneris a group from your SSO, and Argo CD RBAC lets each team see only its own project.The product's deployment config is a folder in the fleet repo. In production each team has its own repository (
checkout-deployment), andrepository:in product.yaml points there.
Break it on purpose: Ask for an environment that does not exist
team-checkout wants to test before prod and adds staging to its product declaration. There is no staging cluster.
You practise: reading a check that protects the contract between teams, and turning "we need X" into a request to the team that owns X.
1. Break it
cd ~/work/fleet-gitops && git switch main && git pull -q
git switch -c checkout-staging
Add to ~/work/fleet-gitops/products/checkout/product.yaml at the end of the environments: list
- environment: staging
size: small
python3 scripts/fleet_check.py --quiet
2. What you see
fleet_check.py fails and names staging. The same check is required on every pull request, so this could never merge. Expected (by team-checkout): a staging namespace.
3. Fix it yourself
Find out what the check is protecting, get back to a green check, and ask for staging the way this platform expects.
Hint 1
Where do environments come from? Look at environment: in clusters/*.yaml.
Hint 2
A new environment means a new cluster: platform work. The repo has an issue form for that.
The fix
git checkout -- . && git switch main && git branch -D checkout-staging
Expect no errors:
python3 scripts/fleet_check.py --quiet
Then ask: open a new issue and pick Add or change a cluster, with the environment, the reason and who needs it. Once the platform team adds a staging cluster to the inventory, team-checkout adds staging to its product and a product-deployments/checkout/staging folder.
After you fixed it: the five questions
- What failed?
- A product referenced an environment no cluster serves, and a config folder that does not exist.
- How was it detected?
fleet_check.py, locally and as the requiredvalidatecheck.- Who responds?
- team-checkout asks; the platform team adds clusters.
- How is it corrected?
- Remove the entry and open an "Add or change a cluster" issue.
- How is it prevented?
- Products can only use environments from the inventory, and the check enforces it on every pull request.
Quiz
- Which file does a product team write to onboard?
- products/<name>/product.yaml (plus its own deployment config)
- A Namespace manifest for every cluster the product runs on, committed next to the others
- An Argo CD Application for each environment, written by hand and applied by the team
- What decides which clusters receive checkout-prod?
- product.yaml lists the clusters by name, and the ApplicationSet copies that list
- Clusters labelled fleet.gitops/environment=prod, from the inventory
- Argo CD picks clusters at random until the replica count in the product file is met
- Can checkout's Application create a ClusterRole?
- Yes, any Application can, as long as the manifest is committed through a pull request
- Only in dev, where the AppProject is relaxed, but never in prod where Kyverno blocks it
- No: its AppProject allows no cluster-scoped resources and only the checkout namespace
Answers
- products/<name>/product.yaml (plus its own deployment config) One declaration; the platform generates the rest.
- Clusters labelled fleet.gitops/environment=prod, from the inventory Products name environments; the platform maps environments to clusters.
- No: its AppProject allows no cluster-scoped resources and only the checkout namespace The per-product AppProject is the fence.
Field note
The first time I ran this with real teams, the platform team was nervous about "self-service". The plan comment won them over: they could see exactly what a new product would create before approving it.