Skip to content

Commit c00db6e

Browse files
authored
Merge pull request kubernetes-sigs#4170 from camilamacedo86/predicates-doc
📖 Revamp "Watching Resources" documentation for accuracy and clarifty
2 parents 333170b + 498c4fd commit c00db6e

File tree

12 files changed

+568
-573
lines changed

12 files changed

+568
-573
lines changed

docs/book/src/SUMMARY.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,9 @@
7676
- [Good Practices](./reference/good-practices.md)
7777
- [Raising Events](./reference/raising-events.md)
7878
- [Watching Resources](./reference/watching-resources.md)
79-
- [Resources Managed by the Operator](./reference/watching-resources/operator-managed.md)
80-
- [Externally Managed Resources](./reference/watching-resources/externally-managed.md)
79+
- [Owned Resources](./reference/watching-resources/secondary-owned-resources.md)
80+
- [Not Owned Resources](./reference/watching-resources/secondary-resources-not-owned.md)
81+
- [Using Predicates](./reference/watching-resources/predicates-with-watch.md)
8182
- [Kind for Dev & CI](reference/kind.md)
8283
- [What's a webhook?](reference/webhook-overview.md)
8384
- [Admission webhook](reference/admission-webhook.md)

docs/book/src/reference/reference.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
Kubernetes cluster.
88
- [Watching Resources](watching-resources.md)
99
Watch resources in the Kubernetes cluster to be informed and take actions on changes.
10-
- [Resources Managed by the Operator](watching-resources/operator-managed.md)
11-
- [Externally Managed Resources](watching-resources/externally-managed.md)
12-
Controller Runtime provides the ability to watch additional resources relevant to the controlled ones.
10+
- [Watching Secondary Resources that are `Owned` ](watching-resources/secondary-owned-resources.md)
11+
- [Watching Secondary Resources that are NOT `Owned`](watching-resources/secondary-resources-not-owned)
12+
- [Using Predicates to Refine Watches](watching-resources/predicates-with-watch.md)
1313
- [Kind cluster](kind.md)
1414
- [What's a webhook?](webhook-overview.md)
1515
Webhooks are HTTP callbacks, there are 3
Lines changed: 172 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,177 @@
11
# Watching Resources
22

3-
Inside a `Reconcile()` control loop, you are looking to do a collection of operations until it has the desired state on the cluster.
4-
Therefore, it can be necessary to know when a resource that you care about is changed.
5-
In the case that there is an action (create, update, edit, delete, etc.) on a watched resource, `Reconcile()` should be called for the resources watching it.
3+
When extending the Kubernetes API, we aim to ensure that our solutions behave consistently with Kubernetes itself.
4+
For example, consider a `Deployment` resource, which is managed by a controller. This controller is responsible
5+
for responding to changes in the cluster—such as when a `Deployment` is created, updated, or deleted—by triggering
6+
reconciliation to ensure the resource’s state matches the desired state.
67

7-
[Controller Runtime libraries](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/builder) provide many ways for resources to be managed and watched.
8-
This ranges from the easy and obvious use cases, such as watching the resources which were created and managed by the controller, to more unique and advanced use cases.
8+
Similarly, when developing our controllers, we want to watch for relevant changes in resources that are crucial
9+
to our solution. These changes—whether creations, updates, or deletions—should trigger the reconciliation
10+
loop to take appropriate actions and maintain consistency across the cluster.
911

10-
See each subsection for explanations and examples of the different ways in which your controller can _Watch_ the resources it cares about.
12+
The [controller-runtime][controller-runtime] library provides several ways to watch and manage resources.
1113

12-
- [Watching Operator Managed Resources](watching-resources/operator-managed.md) -
13-
These resources are created and managed by the same operator as the resource watching them.
14-
This section covers both if they are managed by the same controller or separate controllers.
15-
- [Watching Externally Managed Resources](watching-resources/externally-managed.md) -
16-
These resources could be manually created, or managed by other operators/controllers or the Kubernetes control plane.
14+
## Primary Resources
15+
16+
The **Primary Resource** is the resource that your controller is responsible
17+
for managing. For example, if you create a custom resource definition (CRD) for `MyApp`,
18+
the corresponding controller is responsible for managing instances of `MyApp`.
19+
20+
In this case, `MyApp` is the **Primary Resource** for that controller, and your controller’s
21+
reconciliation loop focuses on ensuring the desired state of these primary resources is maintained.
22+
23+
When you create a new API using Kubebuilder, the following default code is scaffolded,
24+
ensuring that the controller watches all relevant events—such as creations, updates, and
25+
deletions—for (`For()`) the new API.
26+
27+
This setup guarantees that the reconciliation loop is triggered whenever an instance
28+
of the API is created, updated, or deleted:
29+
30+
```go
31+
// Watches the primary resource (e.g., MyApp) for create, update, delete events
32+
if err := ctrl.NewControllerManagedBy(mgr).
33+
For(&<YourAPISpec>{}). <-- See there that the Controller is For this API
34+
Complete(r); err != nil {
35+
return err
36+
}
37+
```
38+
39+
## Secondary Resources
40+
41+
Your controller will likely also need to manage **Secondary Resources**,
42+
which are the resources required on the cluster to support the **Primary Resource**.
43+
44+
Changes to these **Secondary Resources** can directly impact the **Primary Resource**,
45+
so the controller must watch and reconcile these resources accordingly.
46+
47+
### Which are Owned by the Controller
48+
49+
These **Secondary Resources**, such as `Services`, `ConfigMaps`, or `Deployments`,
50+
when `Owned` by the controllers, are created and managed by the specific controller
51+
and are tied to the **Primary Resource** via [OwnerReferences][owner-ref-k8s-docs].
52+
53+
For example, if we have a controller to manage our CR(s) of the Kind `MyApp`
54+
on the cluster, which represents our application solution, all resources required
55+
to ensure that `MyApp` is up and running with the desired number of instances
56+
will be **Secondary Resources**. The code responsible for creating, deleting,
57+
and updating these resources will be part of the `MyApp` Controller.
58+
We would add the appropriate [OwnerReferences][owner-ref-k8s-docs]
59+
using the [controllerutil.SetControllerReference][cr-owner-ref-doc]
60+
function to indicate that these resources are owned by the same controller
61+
responsible for managing `MyApp` instances, which will be reconciled by the `MyAppReconciler`.
62+
63+
Additionally, if the **Primary Resource** is deleted, Kubernetes' garbage collection mechanism
64+
ensures that all associated **Secondary Resources** are automatically deleted in a
65+
cascading manner.
66+
67+
### Which are NOT `Owned` by the Controller
68+
69+
Note that **Secondary Resources** can either be APIs/CRDs defined in your project or in other projects that are
70+
relevant to the **Primary Resources**, but which the specific controller is not responsible for creating or managing.
71+
72+
For example, if we have a CRD that represents a backup solution (i.e. `MyBackup`) for our `MyApp`,
73+
it might need to watch changes in the `MyApp` resource to trigger reconciliation in `MyBackup`
74+
to ensure the desired state. Similarly, `MyApp`'s behavior might also be impacted by
75+
CRDs/APIs defined in other projects.
76+
77+
In both scenarios, these resources are treated as **Secondary Resources**, even if they are not `Owned`
78+
(i.e., not created or managed) by the `MyAppController`.
79+
80+
In Kubebuilder, resources that are not defined in the project itself and are not
81+
a **Core Type** (those not defined in the Kubernetes API) are called **External Types**.
82+
83+
An **External Type** refers to a resource that is not defined in your
84+
project but one that you need to watch and respond to.
85+
For example, if **Operator A** manages a `MyApp` CRD for application deployment,
86+
and **Operator B** handles backups, **Operator B** can watch the `MyApp` CRD as an external type
87+
to trigger backup operations based on changes in `MyApp`.
88+
89+
In this scenario, **Operator B** could define a `BackupConfig` CRD that relies on the state of `MyApp`.
90+
By treating `MyApp` as a **Secondary Resource**, **Operator B** can watch and reconcile changes in **Operator A**'s `MyApp`,
91+
ensuring that backup processes are initiated whenever `MyApp` is updated or scaled.
92+
93+
## General Concept of Watching Resources
94+
95+
Whether a resource is defined within your project or comes from an external project, the concept of **Primary**
96+
and **Secondary Resources** remains the same:
97+
- The **Primary Resource** is the resource the controller is primarily responsible for managing.
98+
- **Secondary Resources** are those that are required to ensure the primary resource works as desired.
99+
100+
Therefore, regardless of whether the resource was defined by your project or by another project,
101+
your controller can watch, reconcile, and manage changes to these resources as needed.
102+
103+
## Why does watching the secondary resources matter?
104+
105+
When building a Kubernetes controller, it’s crucial to not only focus
106+
on **Primary Resources** but also to monitor **Secondary Resources**.
107+
Failing to track these resources can lead to inconsistencies in your
108+
controller's behavior and the overall cluster state.
109+
110+
Secondary resources may not be directly managed by your controller,
111+
but changes to these resources can still significantly
112+
impact the primary resource and your controller's functionality.
113+
Here are the key reasons why it's important to watch them:
114+
115+
- **Ensuring Consistency**:
116+
- Secondary resources (e.g., child objects or external dependencies) may diverge from their desired state.
117+
For instance, a secondary resource may be modified or deleted, causing the system to fall out of sync.
118+
- Watching secondary resources ensures that any changes are detected immediately, allowing the controller to
119+
reconcile and restore the desired state.
120+
121+
- **Avoiding Random Self-Healing**:
122+
- Without watching secondary resources, the controller may "heal" itself only upon restart or when specific events
123+
are triggered. This can cause unpredictable or delayed reactions to issues.
124+
- Monitoring secondary resources ensures that inconsistencies are addressed promptly, rather than waiting for a
125+
controller restart or external event to trigger reconciliation.
126+
127+
- **Effective Lifecycle Management**:
128+
- Secondary resources might not be owned by the controller directly, but their state still impacts the behavior
129+
of primary resources. Without watching these, you risk leaving orphaned or outdated resources.
130+
- Watching non-owned secondary resources lets the controller respond to lifecycle events (create, update, delete)
131+
that might affect the primary resource, ensuring consistent behavior across the system.
132+
133+
## Why not use `RequeueAfter X` for all scenarios instead of watching resources?
134+
135+
Kubernetes controllers are fundamentally **event-driven**. When creating a controller,
136+
the **Reconciliation Loop** is typically triggered by **events** such as `create`, `update`, or
137+
`delete` actions on resources. This event-driven approach is more efficient and responsive
138+
compared to constantly requeuing or polling resources using `RequeueAfter`. This ensures that
139+
the system only takes action when necessary, maintaining both performance and efficiency.
140+
141+
In many cases, **watching resources** is the preferred approach for ensuring Kubernetes resources
142+
remain in the desired state. It is more efficient, responsive, and aligns with Kubernetes' event-driven architecture.
143+
However, there are scenarios where `RequeueAfter` is appropriate and necessary, particularly for managing external
144+
systems that do not emit events or for handling resources that take time to converge, such as long-running processes.
145+
Relying solely on `RequeueAfter` for all scenarios can lead to unnecessary overhead and
146+
delayed reactions. Therefore, it is essential to prioritize **event-driven reconciliation** by configuring
147+
your controller to **watch resources** whenever possible, and reserving `RequeueAfter` for situations
148+
where periodic checks are required.
149+
150+
### When `RequeueAfter X` is Useful
151+
152+
While `RequeueAfter` is not the primary method for triggering reconciliations, there are specific cases where it is
153+
necessary, such as:
154+
155+
- **Observing External Systems**: When working with external resources that do not generate events
156+
(e.g., external databases or third-party services), `RequeueAfter` allows the
157+
controller to periodically check the status of these resources.
158+
- **Time-Based Operations**: Some tasks, such as rotating secrets or
159+
renewing certificates, must happen at specific intervals. `RequeueAfter` ensures these operations
160+
are performed on schedule, even when no other changes occur.
161+
- **Handling Errors or Delays**: When managing resources that encounter errors or require time to self-heal,
162+
`RequeueAfter` ensures the controller waits for a specified duration before checking the resource’s status again,
163+
avoiding constant reconciliation attempts.
164+
165+
## Usage of Predicates
166+
167+
For more complex use cases, [Predicates][cr-predicates] can be used to fine-tune
168+
when your controller should trigger reconciliation. Predicates allow you to filter
169+
events based on specific conditions, such as changes to particular fields, labels, or annotations,
170+
ensuring that your controller only responds to relevant events and operates efficiently.
171+
172+
[controller-runtime]: https://github.com/kubernetes-sigs/controller-runtime
173+
[owner-ref-k8s-docs]: https://kubernetes.io/docs/concepts/overview/working-with-objects/owners-dependents/
174+
[cr-predicates]: https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/predicate
175+
[secondary-resources-doc]: watching-resources/secondary-owned-resources
176+
[predicates-with-external-type-doc]: watching-resources/predicates-with-watch
177+
[cr-owner-ref-doc]: https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/controller/controllerutil#SetOwnerReference

docs/book/src/reference/watching-resources/externally-managed.md

Lines changed: 0 additions & 31 deletions
This file was deleted.

docs/book/src/reference/watching-resources/operator-managed.md

Lines changed: 0 additions & 25 deletions
This file was deleted.

0 commit comments

Comments
 (0)