PlatformEngr® Fleet GitOps course

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.yaml and the deployment config, and review the plan. Watch the platform build everything from one file, then onboard a second product yourself.
  • You end with: checkout runs 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 the fleet.gitops/environment label). 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 (an elementsYaml list), then times the clusters whose label matches. One file in, checkout-dev and checkout-prod out.

Files to know

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

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

Open the pull request ↗

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.

Open the onboarding form ↗

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-checkout is a name here; on kind there is no identity provider, so module 08 impersonates the team with --as-group. In production owner is 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), and repository: 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 required validate check.
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

  1. 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
  2. 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
  3. 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
  1. products/<name>/product.yaml (plus its own deployment config) One declaration; the platform generates the rest.
  2. Clusters labelled fleet.gitops/environment=prod, from the inventory Products name environments; the platform maps environments to clusters.
  3. 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.

Report a problem with this module