> ## Documentation Index
> Fetch the complete documentation index at: https://notes.kodekloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Demo What The Generator Wrote For You

> Guide to Kubebuilder project layout explaining generated files, API types, controller reconciliation, manager wiring, config manifests, and Makefile tasks for building and deploying an operator

When you scaffold a project with Kubebuilder, the generated code feels like a set of labeled drawers: some drawers hold the API shape, others contain the reconciliation/controller logic, and a few contain the Kubernetes YAML used for deployment and testing. This guide walks the project top-to-bottom so you know which file to open when you want to change a specific aspect of the operator.

## Project blueprint — config/project-config.yaml

Open `config/project-config.yaml`. This file is the project blueprint that records domain, repo path, project name, layout, and the API resources Kubebuilder added. Typically you do not edit this file by hand because Kubebuilder updates it when you add APIs or webhooks.

```yaml theme={null}
# Code generated by tool. DO NOT EDIT.
# This file is used to track the info used to scaffold your project
# More info: https://book.kubebuilder.io/reference/project-config.html
cliVersion: 4.13.1
domain: kodekloud.com
layout:
  - go.kubebuilder.io/v4
projectName: webapp-operator
repo: github.com/kodekloud/webapp-operator
resources:
  - api:
      crdVersion: v1
      namespaced: true
      controller: true
      domain: kodekloud.com
      group: webapp
      kind: WebApp
      path: github.com/kodekloud/webapp-operator/api/v1
      version: v1
version: "3"
```

Quick field reference:

| Field         | Meaning                                                                       |
| ------------- | ----------------------------------------------------------------------------- |
| `domain`      | The API domain used to form Group names (e.g., `webapp.kodekloud.com`).       |
| `projectName` | The project directory name scaffolded by Kubebuilder.                         |
| `repo`        | Go module / import path for the project.                                      |
| `resources`   | APIs scaffolded into the project (group, kind, version, controller presence). |

## API shape — api/v1/webapp\_types.go

Open `api/v1/webapp_types.go`. This drawer defines the API surface for your custom resource.

* `WebAppSpec` represents the user-requested (desired) state.
* `WebAppStatus` represents the operator-reported (observed) state.

The scaffold includes a placeholder `Foo` field as an example. In the next implementation steps, replace it with fields such as `image` and `replicas`. The controller will then create child resources (Deployment, Service, ConfigMap, etc.) using either defaults or values from the spec.

Markers beginning with `+kubebuilder:` are directives for controller-gen and other code generators. They generate CRD YAML, print columns, status subresource support, and validation rules. Generated files such as `zz_generated.deepcopy.go` live next to these types — do not edit generated files manually; use `make generate` instead.

```go theme={null}
package v1

import (
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// WebAppSpec defines the desired state of WebApp
type WebAppSpec struct {
	// TODO: replace the starter field with image and replicas fields
	// Foo is an example field of WebApp. Edit WebApp_types.go to remove/update
	Foo string `json:"foo,omitempty"`
}

// WebAppStatus defines the observed state of WebApp
type WebAppStatus struct {
	// The status of each condition is one of True, False, or Unknown.
	// +listType=map
	// +listMapKey=type
	// +optional
	Conditions []metav1.Condition `json:"conditions,omitempty"`
}

// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// WebApp is the Schema for the webapps API
type WebApp struct {
	metav1.TypeMeta `json:",inline"`
	// metadata is standard object metadata
	// +optional
	metav1.ObjectMeta `json:"metadata,omitempty"`
	// spec defines the desired state of WebApp
	// +required
	Spec WebAppSpec `json:"spec"`
	// +optional
	Status WebAppStatus `json:"status,omitempty"`
}
```

## Reconciliation logic — internal/controller/webapp\_controller.go

Open `internal/controller/webapp_controller.go`. This is the controller/reconciler drawer — the place to implement operator behavior.

The `Reconcile` function is the reconciliation loop that Kubernetes calls whenever a WebApp object (or an owned object) changes. In your implementation, the reconciler should:

1. Read the WebApp instance from the API server.
2. Compare the requested state (`Spec`) with the current cluster state.
3. Create, update, or delete child resources (Deployment, Service, ConfigMap, etc.) to converge to the desired state.
4. Update `WebApp.Status` with observed conditions and requeue as required.

The scaffolded `Reconcile` currently returns with no work done — implement the steps above and manage errors/requeue behavior as appropriate.

```go theme={null}
package controller

import (
	"context"

	webappv1 "github.com/kodekloud/webapp-operator/api/v1"
	"k8s.io/apimachinery/pkg/runtime"
	ctrl "sigs.k8s.io/controller-runtime"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/log"
)

// WebAppReconciler reconciles a WebApp object
type WebAppReconciler struct {
	client.Client
	Scheme *runtime.Scheme
}

func (r *WebAppReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
	_ = log.FromContext(ctx)

	// TODO(user): implement reconciliation logic:
	// - Fetch the WebApp instance
	// - Ensure associated Deployment, Service, ConfigMap, etc. exist and match spec
	// - Update status conditions
	// - Requeue as needed

	return ctrl.Result{}, nil
}

// SetupWithManager sets up the controller with the Manager.
func (r *WebAppReconciler) SetupWithManager(mgr ctrl.Manager) error {
	return ctrl.NewControllerManagedBy(mgr).
		For(&webappv1.WebApp{}).
		Named("webapp").
		Complete(r)
}
```

At the top of this file you will also find RBAC markers that generate Role and RoleBinding manifests. Keep them current as you add more owned resource kinds (e.g., Deployments, Services).

```go theme={null}
// +kubebuilder:rbac:groups=webapp.kodekloud.com,resources=webapps,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=webapp.kodekloud.com,resources=webapps/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=webapp.kodekloud.com,resources=webapps/finalizers,verbs=update
```

## Manager and wiring — cmd/main.go

Open `cmd/main.go`. This file wires the manager process that runs the controller(s). The manager owns the shared client, cache, metrics endpoint, webhook server, health/readiness probes, leader election, and controller registration. You typically modify this file only when adding new controllers, changing leader election, or altering probe/metrics settings.

Example: creating the manager instance.

```go theme={null}
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
	Scheme:                 scheme,
	Metrics:                metricsServerOptions,
	WebhookServer:          webhookServer,
	HealthProbeBindAddress: probeAddr,
	LeaderElection:         enableLeaderElection,
	LeaderElectionID:       "43a46c8b.kodekloud.com",
})
if err != nil {
	setupLog.Error(err, "unable to start manager")
	os.Exit(1)
}
```

Wiring the reconciler into the manager:

```go theme={null}
if err := (&controller.WebAppReconciler{
	Client: mgr.GetClient(),
	Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
	setupLog.Error(err, "Failed to create controller", "controller", "WebApp")
	os.Exit(1)
}

// +kubebuilder:scaffold:builder

if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
	setupLog.Error(err, "Failed to set up health check")
	os.Exit(1)
}
if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
	setupLog.Error(err, "Failed to set up ready check")
	os.Exit(1)
}

setupLog.Info("Starting manager")
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
	setupLog.Error(err, "Failed to start manager")
	os.Exit(1)
}
```

## Deployment YAML — config/

The `config/` directory holds Kubernetes manifests used for deploying and testing the operator:

* `config/crd/` — generated CRD YAML for your APIs.
* `config/rbac/` — generated RBAC manifests derived from `+kubebuilder:rbac` markers.
* `config/manager/` — the operator Deployment manifest and related files.
* `config/default/` — kustomize overlay tying deployment pieces together for `make deploy`.
* `config/samples/` — example `WebApp` CR instances for testing.

Example manifests (Namespace + controller Deployment):

```yaml theme={null}
apiVersion: v1
kind: Namespace
metadata:
  labels:
    control-plane: controller-manager
    app.kubernetes.io/name: webapp-operator
    app.kubernetes.io/managed-by: kustomize
  name: system
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: controller-manager
  namespace: system
  labels:
    control-plane: controller-manager
    app.kubernetes.io/name: webapp-operator
    app.kubernetes.io/managed-by: kustomize
spec:
  selector:
    matchLabels:
      control-plane: controller-manager
      app.kubernetes.io/name: webapp-operator
  template:
    metadata:
      labels:
        control-plane: controller-manager
        app.kubernetes.io/name: webapp-operator
    spec:
      containers:
      - name: manager
        image: controller:latest
```

## Development tasks — Makefile

Open the `Makefile`. It contains standard targets for day-to-day work: generating code, building images, running locally, and deploying.

Key variables and snippet:

```makefile theme={null}
# Image URL to use for building/pushing image targets
IMG ?= controller:latest

# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
ifeq ($(shell go env GOBIN),)
GOBIN=$(shell go env GOPATH)/bin
else
GOBIN=$(shell go env GOBIN)
endif

# CONTAINER_TOOL defines the container tool to be used for building images.
CONTAINER_TOOL ?= docker

SHELL = /usr/bin/env bash -o pipefail
.SHELLFLAGS = -ec

.PHONY: all
all: build

.PHONY: help
help: ## Display this help.
	@awk 'BEGIN {FS = ".:##"; printf "\nUsage:\n  make \033[36mtarget\033[0m\n"} \
		/[a-zA-Z0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } \
		/^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
```

Important targets to remember:

* `make generate` — refreshes generated Go code (DeepCopy methods, etc.).
* `make manifests` — regenerates CRDs and RBAC YAML via controller-gen.
* `make run` — runs the operator locally against your kubeconfig.
* `make deploy` — builds and applies manifests to the cluster (kustomize overlay sets the image).

Example targets:

```makefile theme={null}
.PHONY: manifests
manifests: controller-gen ## Generate CRD and RBAC objects.
	"$(CONTROLLER_GEN)" rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts=config/crd/bases

.PHONY: generate
generate: controller-gen ## Generate deepcopy code.
	"$(CONTROLLER_GEN)" object:headerFile="hack/boilerplate.go.txt" paths="./..."

.PHONY: deploy
deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config.
	cd config/manager && "$(KUSTOMIZE)" edit set image controller=$${IMG}
	"$(KUSTOMIZE)" build config/default | "$(KUBECTL)" apply -f -
```

Uninstall/undeploy helpers:

```makefile theme={null}
.PHONY: uninstall
uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config.
	@out="$$( "$(KUSTOMIZE)" build config/crd 2>/dev/null || true )"; \
	if [ -n "$$out" ]; then echo "$$out" | "$(KUBECTL)" delete --ignore-not-found -f -; else echo "No CRDs to delete; skipping."; fi

.PHONY: undeploy
undeploy: kustomize ## Undeploy controller from the K8s cluster specified in ~/.kube/config.
	"$(KUSTOMIZE)" build config/default | "$(KUBECTL)" delete --ignore-not-found -f -
```

<Callout icon="lightbulb" color="#1CB2FE">
  Keep generated and scaffolded files in mind: update `+kubebuilder:rbac` markers and your `api` types as you add real resources. Use `make generate` and `make manifests` to refresh generated code and YAML so the on-disk artifacts match your source.
</Callout>

## Quick lookup — where to change what

| Concern                                  | File / Location                            |
| ---------------------------------------- | ------------------------------------------ |
| API shape (spec/status)                  | `api/v1/webapp_types.go`                   |
| Reconciliation logic (operator behavior) | `internal/controller/webapp_controller.go` |
| Manager wiring, leader election, probes  | `cmd/main.go`                              |
| CRDs, RBAC, Deployment manifests         | `config/`                                  |
| Dev tasks (generate, manifests, deploy)  | `Makefile`                                 |

Once you have this map, the generated project becomes a set of predictable drawers rather than a wall of files. Next, use this map to implement the reconciliation logic that ensures WebApp instances are translated into the proper cluster resources.

## Links and references

* [Kubebuilder book — Project layout and concepts](https://book.kubebuilder.io/)
* [Controller Runtime (controller-runtime)](https://pkg.go.dev/sigs.k8s.io/controller-runtime)
* [Kubernetes API conventions](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md)
* [Kustomize](https://kustomize.io/)
* [controller-gen (controller-tools)](https://github.com/kubernetes-sigs/controller-tools)
* [Go module docs](https://golang.org/ref/mod)

<CardGroup>
  <Card title="Watch Video" icon="video" cta="Learn more" href="https://learn.kodekloud.com/user/courses/kubernetes-operators/module/20a4ec01-fde8-466d-83c7-2f74f6def1f0/lesson/a665532d-8f0d-41b7-8b7e-ab39671b08cb" />
</CardGroup>
