Skip to content

Commit 390ace6

Browse files
committed
docs
1 parent 508cb9a commit 390ace6

File tree

1 file changed

+144
-8
lines changed

1 file changed

+144
-8
lines changed

docs/documentation/workflows.md

Lines changed: 144 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,14 @@ permalink: /docs/workflows
99

1010
Kubernetes (k8s) does not have notion of a resource "depends on" on another k8s resource,
1111
in terms of in what order a set of resources should be reconciled. However, Kubernetes operators are used to manage also
12-
external (non 8s) resources. Typically, when an operator manages a service, where after the service is first deployed
12+
external (non k8s) resources. Typically, when an operator manages a service, after the service is first deployed
1313
some additional API calls are required to configure it. In this case the configuration step depends
1414
on the service and related resources, in other words the configuration needs to be reconciled after the service is
1515
up and running.
1616

1717
The intention behind workflows is to make it easy to describe more complex, almost arbitrary scenarios in a declarative
1818
way. While [dependent resources](https://javaoperatorsdk.io/docs/dependent-resources) describes a logic how a single
19-
resources should be reconciled, workflows describes the process how a set of target resources should be reconciled in
20-
a generic way.
19+
resources should be reconciled, workflows describes the process how a set of target resources should be reconciled.
2120

2221
Workflows are defined as a set of [dependent resources](https://javaoperatorsdk.io/docs/dependent-resources) (DR)
2322
and dependencies between them, along with some conditions that mainly helps define optional resources and
@@ -27,8 +26,8 @@ pre- and post-conditions to describe expected states of a resource at a certain
2726
## Elements of Workflow
2827

2928
- **Dependent resource** (DR) - are the resources which are reconciled.
30-
- **Depends on relation** - if a DR B depends on another DR A, means that B will be reconciled after A is successfully
31-
reconciled.
29+
- **Depends-on relation** - if a DR B depends on another DR A, means that B will be reconciled after A is successfully
30+
reconciled - and ready if readyPostCondition is used.
3231
- **Reconcile precondition** - is a condition that needs to be fulfilled before the DR is reconciled. This allows also
3332
to define optional resources, that for example only created if a flag in a custom resource `.spec` has some
3433
specific value.
@@ -39,23 +38,160 @@ pre- and post-conditions to describe expected states of a resource at a certain
3938

4039
## Defining Workflows
4140

42-
As for dependent resource there are two ways to define workflows, in managed and standalone way.
41+
Similarly to dependent resources, there are two ways to define workflows, in managed and standalone way.
4342

4443
### Managed
4544

45+
Annotation can be used to declaratively define a workflow for the reconciler. In this case the workflow is executed
46+
before the `reconcile` method is called. The result of the reconciliation is accessed through the `context` object.
47+
48+
Following sample shows a hypothetical sample, where there are two resources a Deployment and a ConfigMap, where
49+
the ConfigMap depends on the deployment. Deployment has a ready condition so, the config map is only reconciled after
50+
the Deployment and only if that is ready (see ready postcondition). The ConfigMap has a reconcile precondition, there
51+
only reconciled if that condition holds. In addition to that contains a delete postCondition, do only considered to be
52+
deleted if that condition holds.
53+
54+
```java
55+
@ControllerConfiguration(dependents = {
56+
@Dependent(name = DEPLOYMENT_NAME, type = DeploymentDependentResource.class,
57+
readyPostcondition = DeploymentReadyCondition.class),
58+
@Dependent(type = ConfigMapDependentResource.class,
59+
reconcilePrecondition = ConfigMapReconcileCondition.class,
60+
deletePostcondition = ConfigMapDeletePostCondition.class,
61+
dependsOn = DEPLOYMENT_NAME)
62+
})
63+
public class SampleWorkflowReconciler implements Reconciler<TestCustomResource>,
64+
Cleaner<WorkflowAllFeatureCustomResource> {
65+
66+
public static final String DEPLOYMENT_NAME = "deployment";
67+
68+
@Override
69+
public UpdateControl<WorkflowAllFeatureCustomResource> reconcile(
70+
WorkflowAllFeatureCustomResource resource,
71+
Context<WorkflowAllFeatureCustomResource> context) {
72+
73+
resource.getStatus()
74+
.setReady(
75+
context.managedDependentResourceContext() // accessing workflow reconciliation results
76+
.getWorkflowReconcileResult().orElseThrow()
77+
.allDependentResourcesReady());
78+
return UpdateControl.patchStatus(resource);
79+
}
80+
81+
@Override
82+
public DeleteControl cleanup(WorkflowAllFeatureCustomResource resource,
83+
Context<WorkflowAllFeatureCustomResource> context) {
84+
// emitted code
85+
86+
return DeleteControl.defaultDelete();
87+
}
88+
}
89+
90+
```
4691

4792
### Standalone
4893

94+
In this mode workflow is built manually using [standalone dependent resources](https://javaoperatorsdk.io/docs/dependent-resources#standalone-dependent-resources)
95+
. The workflow is created using a builder, that is explicitly called in the reconciler (from web page sample):
96+
97+
```java
98+
@ControllerConfiguration(
99+
labelSelector = WebPageDependentsWorkflowReconciler.DEPENDENT_RESOURCE_LABEL_SELECTOR)
100+
public class WebPageDependentsWorkflowReconciler
101+
implements Reconciler<WebPage>, ErrorStatusHandler<WebPage>, EventSourceInitializer<WebPage> {
102+
103+
public static final String DEPENDENT_RESOURCE_LABEL_SELECTOR = "!low-level";
104+
private static final Logger log =
105+
LoggerFactory.getLogger(WebPageDependentsWorkflowReconciler.class);
106+
107+
private KubernetesDependentResource<ConfigMap, WebPage> configMapDR;
108+
private KubernetesDependentResource<Deployment, WebPage> deploymentDR;
109+
private KubernetesDependentResource<Service, WebPage> serviceDR;
110+
private KubernetesDependentResource<Ingress, WebPage> ingressDR;
111+
112+
private Workflow<WebPage> workflow;
113+
114+
public WebPageDependentsWorkflowReconciler(KubernetesClient kubernetesClient) {
115+
initDependentResources(kubernetesClient);
116+
workflow = new WorkflowBuilder<WebPage>()
117+
.addDependent(configMapDR).build()
118+
.addDependent(deploymentDR).build()
119+
.addDependent(serviceDR).build()
120+
.addDependent(ingressDR).withReconcileCondition(new IngressCondition()).build()
121+
.build();
122+
}
123+
124+
@Override
125+
public Map<String, EventSource> prepareEventSources(EventSourceContext<WebPage> context) {
126+
return EventSourceInitializer.nameEventSources(
127+
configMapDR.initEventSource(context),
128+
deploymentDR.initEventSource(context),
129+
serviceDR.initEventSource(context),
130+
ingressDR.initEventSource(context));
131+
}
132+
133+
@Override
134+
public UpdateControl<WebPage> reconcile(WebPage webPage, Context<WebPage> context) {
135+
136+
var result = workflow.reconcile(webPage, context);
137+
138+
webPage.setStatus(createStatus(result));
139+
return UpdateControl.patchStatus(webPage);
140+
}
141+
// emitted code
142+
}
143+
144+
```
145+
146+
## Workflow Execution
147+
148+
This section describes how a workflow is executed in details, how is the ordering determined and how condition and
149+
errors effect behavior. The workflow execution as also its API denotes, can be divided to into two parts,
150+
the reconciliation and cleanup. [Cleanup](https://javaoperatorsdk.io/docs/features#the-reconcile-and-cleanup) is
151+
executed if a resource is marked for deletion.
152+
153+
154+
## Common Principles
155+
156+
- **As complete as possible execution** - when a workflow is reconciled, it tries to reconcile as many resources as
157+
possible. Thus is an error happens or a ready condition is not met for a resources, all the other independent resources
158+
will be still reconciled. So this is exactly the opposite of fail-fast approach. The assumption is that in this way
159+
the overall desired state is achieved faster than with a fail fast approach.
160+
- **Concurrent reconciliation of independent resources** - the resources which are not dependent on each are processed
161+
concurrently. The level of concurrency is customizable, could be set to one if required. By default, workflows use
162+
the executor service from [ConfigurationService](https://github.com/java-operator-sdk/java-operator-sdk/blob/6f2a252952d3a91f6b0c3c38e5e6cc28f7c0f7b3/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationService.java#L120-L120)
163+
164+
## Reconciliation
165+
166+
This section describes how a workflow is executed, first the rules are defined, then are explained on samples:
167+
168+
### Rules
169+
170+
1. DR is reconciled if it does not depend on another DR, or ALL the DRs it depends on are ready. In case it
171+
has a reconcile-precondition that must met too. (Ready means that it is successfully reconciled - without any error - and
172+
if it has a ready condition that is met).
173+
2. If a reconcile-precondition of a DR is not met, it is deleted. If there are dependent resources which depends on it
174+
are deleted first too - this applies recursively. That means that DRs are always deleted in revers order compared
175+
how are reconciled.
176+
3. Delete is called on a dependent resource if as described in point 2. it (possibly transitively) depends on A DR which
177+
did not meet it's reconcile condition, and has not DRs depends on it, or if the DR-s which depends on it are
178+
successfully deleted. "Delete is called" means, that the dependent resource is checked if it implements `Deleter` interface,
179+
if implements it but do not implement `GarbageCollected` interface, the `Deleter.delete` method called. If a DR
180+
does not implement `Deleter` interface, it is considered as deleted automatically. Successfully deleted means,
181+
that it is deleted and if a delete-postcondition is present it is met.
182+
183+
### Samples
49184

50-
## Reconciliation
51185

52-
[//]: # (todo mention parallelism)
53186

54187
## Cleanup
55188

189+
## Error Handling
190+
56191
## Notes
57192

58193
- Workflows can be seen as a Directed Acyclic Graph (DAG) - or more precisely a set of DAGs - where nodes are the
59194
dependent resources and edges are the dependencies.
60195

61196
[//]: # (ready vs precondition)
197+
[//]: # (issue with garbage collection)

0 commit comments

Comments
 (0)