Challenge
DifficultyEasy
Learner activity No attempts yet

Create a Custom Resource Definition and Verify it in the Kubernetes API

Extend the Kubernetes API by defining a custom resource type for managing website configurations. Then bind the correct RBAC permissions so the default service account can query the new endpoint, confirm it is live with curl, and save the response to disk.

Scenario

Your team is building an internal platform that manages website configurations inside Kubernetes. Rather than storing these configs in ConfigMaps, the platform team wants a proper custom resource type — so operators can work with Website objects the same way they work with any built-in resource.

Your job is to register the Website custom resource type with the cluster, grant the default service account the RBAC permissions it needs to query the new endpoint, confirm the endpoint is live with curl, and save the response to disk for the platform audit trail.


Task

  1. Create a CRD with the following specification:
    FieldValue
    Resource name (plural)websites
    Resource name (singular)website
    Short namews
    Groupapps.acme.com
    Versionv1
    ScopeNamespaced
    KindWebsite

    The v1 schema must accept these fields under spec:
    FieldTypeRequired
    urlstringyes
    replicasintegerno
  2. Confirm the CRD is Established — meaning the API server has registered and activated the new resource type.
  3. Create the following RBAC resources so the default service account in the default namespace can query the websites endpoint:
    • A ClusterRole named website-reader that grants get and list on websites in the apps.acme.com API group.
    • A ClusterRoleBinding named website-reader-binding that binds website-reader to the default service account in the default namespace.
  4. Query the live API endpoint using curl and save the JSON response to /home/laborant/crd-live.json.

Hint 1 — CRD Manifest Structure

A CRD is itself a Kubernetes resource — apply it with a manifest like any other object. The key fields to get right are the group, versions, names, and scope:

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: <plural>.<group>        # must match names.plural + spec.group exactly
spec:
  group: <group>
  scope: Namespaced
  names:
    plural: <plural>
    singular: <singular>
    shortNames:
      - <short>
    kind: <Kind>
  versions:
    - name: v1
      served: true
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                # your fields here

The metadata.name must be exactly <plural>.<group> — Kubernetes enforces this and will reject the manifest if they don't match.

Documentation

Hint 2 — Confirming the CRD is Established

After applying the manifest, Kubernetes takes a moment to register the new API endpoint. Inspect the CRD status to check its conditions:

kubectl describe crd websites.apps.acme.com

Look for this in the Conditions section of the output:

Type:    Established
Status:  True

If NamesAccepted shows False, the metadata.name does not match the combination of spec.group and names.plural — fix the manifest and reapply.

Documentation

Hint 3 — Creating RBAC for the Custom Resource

RBAC for custom resources works exactly like RBAC for built-in resources. Use the custom resource's API group (apps.acme.com) and plural name (websites) in the rules block of your ClusterRole:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: website-reader
rules:
- apiGroups:
  - apps.acme.com
  resources:
  - websites
  verbs:
  - get
  - list
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: website-reader-binding
subjects:
- kind: ServiceAccount
  name: default
  namespace: default
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: website-reader

Apply both resources in one step with kubectl apply -f <file>. You can also verify the permission took effect with:

kubectl auth can-i list websites.apps.acme.com \
  --as=system:serviceaccount:default:default

Documentation

Hint 4 — Querying the API with curl

The Kubernetes API server listens on port 6443 on the control plane node. Requests must be authenticated with a Bearer token. You can create a short-lived token for the default service account:

TOKEN=$(kubectl create token default --duration=60s)

Then query the endpoint and save the output in one step using curl's -o flag:

curl -sk \
  --header "Authorization: Bearer $TOKEN" \
  https://localhost:6443/apis/apps.acme.com/v1/websites \
  -o /home/laborant/crd-live.json
  • -s suppresses the progress bar
  • -k skips TLS certificate verification (the cluster uses a self-signed cert)
  • -o writes the response body directly to the file

Documentation


⚒ Test Cases

Problem
Solution
Start