|
| 1 | +--- |
| 2 | +title: Dependent Resources Feature |
| 3 | +description: Dependent Resources Feature |
| 4 | +layout: docs |
| 5 | +permalink: /docs/dependent-resources |
| 6 | +--- |
| 7 | + |
| 8 | +# Dependent Resources |
| 9 | + |
| 10 | +DISCLAIMER: The Dependent Resource support is relatively new and, while we strove to cover what we |
| 11 | +anticipate will be the most common use cases, the implementation is not simple and might still |
| 12 | +evolve. As a result, some APIs could be still a subject of change in the future. However, |
| 13 | +non-backwards compatible changes are expected to be trivial to adapt to. |
| 14 | + |
| 15 | +## Motivations and Goals |
| 16 | + |
| 17 | +Most operators need to deal with secondary resources when trying to realize the desired state |
| 18 | +described by the primary resource it is in charge of. For example, the Kubernetes-native |
| 19 | +`Deployment` controller needs to manage `ReplicaSet` instances as part of a `Deployment`'s |
| 20 | +reconciliation process. In this instance, `ReplicatSet` is considered a secondary resource for |
| 21 | +the `Deployment` controller. |
| 22 | + |
| 23 | +Controllers that deal with secondary resources typically need to perform the following steps, for |
| 24 | +each secondary resource: |
| 25 | + |
| 26 | +```mermaid |
| 27 | +flowchart TD |
| 28 | +
|
| 29 | +compute[Compute desired secondary resource based on primary state] --> A |
| 30 | +A{Secondary resource exists?} |
| 31 | +A -- Yes --> match |
| 32 | +A -- No --> Create --> Done |
| 33 | +
|
| 34 | +match{Matches desired state as defined by primary?} |
| 35 | +match -- Yes --> Done |
| 36 | +match -- No --> Update --> Done |
| 37 | +
|
| 38 | +``` |
| 39 | + |
| 40 | +While these steps are not difficult in and of themselves, there are some subtleties that can lead to |
| 41 | +bugs or sub-optimal code if not done right. As this process is pretty much similar for each |
| 42 | +dependent resource, it makes sense for the SDK to offer some level of support to remove the |
| 43 | +boilerplate code of these repetitive actions. It should be possible to handle common cases (such as |
| 44 | +dealing with Kubernetes-native secondary resources) in a semi-declarative way with only a minimal |
| 45 | +amount of code, JOSDK taking care of wiring everything accordingly. |
| 46 | + |
| 47 | +Moreover, in order for your reconciler to get informed of events on these secondary resources, you |
| 48 | +need to configure and create event sources and maintain them. JOSDK already makes it rather easy to |
| 49 | +deal with these, but dependent resources makes it even simpler. |
| 50 | + |
| 51 | +Finally, there are also opportunities for the SDK to transparently add features that are even |
| 52 | +trickier to get right, such as immediate caching of updated or created resources (so that your |
| 53 | +reconciler doesn't need to wait for a cluster roundtrip to continue its work) and associated event |
| 54 | +filtering (so that something your reconciler just changed doesn't re-trigger a reconciliation, for |
| 55 | +example). |
| 56 | + |
| 57 | +## Design |
| 58 | + |
| 59 | +### `DependentResource` vs. `AbstractDependentResource` |
| 60 | + |
| 61 | +The new `DependentResource` interface lies at the core of the design and strives to encapsulate the |
| 62 | +logic that is required to reconcile the state of the associated secondary resource based on the |
| 63 | +state of the primary one. For most cases, this logic will follow the flow expressed above and JOSDK |
| 64 | +provides a very convenient implementation of this logic in the form of the |
| 65 | +`AbstractDependentResource` class. If your logic doesn't fit this pattern, though, you can still |
| 66 | +provide your own `reconcile` method implementation. While the benefits of using dependent resources |
| 67 | +are less obvious in that case, this allows you to separate the logic necessary to deal with each |
| 68 | +secondary resource in its own class that can then be tested in isolation via unit tests. You can |
| 69 | +also use the declarative support with your own implementations as we shall see later on. |
| 70 | + |
| 71 | +`AbstractDependentResource` is designed so that classes extending it specify which functionality |
| 72 | +they support by implementing trait interfaces. This design has been selected to express the fact |
| 73 | +that not all secondary resources are completely under the control of the primary reconciler: some |
| 74 | +dependent resources are only ever created or updated for example and we needed a way to let JOSDK |
| 75 | +know when that is the case. We therefore provide trait interfaces: `Creator`, |
| 76 | +`Updater` and `Deleter` to express that the `DependentResource` implementation will provide custom |
| 77 | +functionality to create, update and delete its associated secondary resources, respectively. If |
| 78 | +these traits are not implemented then parts of the logic described above is never triggered: if your |
| 79 | +implementation doesn't implement `Creator`, for example, |
| 80 | +`AbstractDependentResource` will never try to create the associated secondary resource, even if it |
| 81 | +doesn't exist. It is possible to not implement any of these traits and therefore create read-only dependent resources that will trigger your |
| 82 | +reconciler whenever a user interacts with them but that are never modified by your reconciler |
| 83 | +itself. |
| 84 | + |
| 85 | +### Batteries included: convenient `DependentResource` implementations! |
| 86 | + |
| 87 | +JOSDK also offers several other convenient implementations building on top of |
| 88 | +`AbstractDependentResource` that you can use as starting points for your own implementations. |
| 89 | + |
| 90 | +One such implementation is the `KubernetesDependentResource` class that makes it really easy to work |
| 91 | +with Kubernetes-native resources. In this case, you usually only need to provide an |
| 92 | +implementation for the `desired` method to tell JOSDK what the desired state of your secondary |
| 93 | +resource should be based on the specified primary resource state. JOSDK takes care of everything |
| 94 | +else using default implementations that you can override in case you need more precise control of |
| 95 | +what's going on. |
| 96 | + |
| 97 | +We also provide implementations that makes it very easy to cache |
| 98 | +(`AbstractCachingDependentResource`) or make it easy to poll for changes in external |
| 99 | +resources (`PollingDependentResource`, `PerResourcePollingDependentResource`). All the provided |
| 100 | +implementations can be found in the `io/javaoperatorsdk/operator/processing/dependent` package of |
| 101 | +the `operator-framework-core` module. |
| 102 | + |
| 103 | +## Managed Dependent Resources |
| 104 | + |
| 105 | +As mentioned previously, one goal of this implementation is to make it possible to |
| 106 | +semi-declaratively create and wire dependent resources. You can annotate your reconciler with |
| 107 | +`@Dependent` annotations that specify which `DependentResource` implementation it depends upon. |
| 108 | +JOSDK will take the appropriate steps to wire everything together and call your |
| 109 | +`DependentResource` implementations `reconcile` method before your primary resource is reconciled. |
| 110 | +This makes sense in most use cases where the logic associated with the primary resource is usually |
| 111 | +limited to status handling based on the state of the secondary resources. This behavior and |
| 112 | +automated handling is referred to as "managed" because the `DependentResource` |
| 113 | +implementations are managed by JOSDK. |
| 114 | + |
| 115 | +## Standalone Dependent Resources |
| 116 | + |
| 117 | +## Other Dependent Resources features |
0 commit comments