@@ -33,6 +33,7 @@ public abstract class AbstractDependentResource<R, P extends HasMetadata>
|
33 | 33 | private final boolean updatable = this instanceof Updater;
|
34 | 34 | private final boolean deletable = this instanceof Deleter;
|
35 | 35 | private final DependentResourceReconciler<R, P> dependentResourceReconciler;
|
| 36 | +private final ThreadLocal<R> desiredCache = new ThreadLocal<>(); |
36 | 37 | protected Creator<R, P> creator;
|
37 | 38 | protected Updater<R, P> updater;
|
38 | 39 | protected String name;
|
@@ -67,52 +68,56 @@ public ReconcileResult<R> reconcile(P primary, Context<P> context) {
|
67 | 68 | }
|
68 | 69 |
|
69 | 70 | protected ReconcileResult<R> reconcile(P primary, R actualResource, Context<P> context) {
|
70 |
| -if (creatable() || updatable()) { |
71 |
| -if (actualResource == null) { |
72 |
| -if (creatable) { |
73 |
| -var desired = desired(primary, context); |
74 |
| -throwIfNull(desired, primary, "Desired"); |
75 |
| -logForOperation("Creating", primary, desired); |
76 |
| -var createdResource = handleCreate(desired, primary, context); |
77 |
| -return ReconcileResult.resourceCreated(createdResource); |
78 |
| -} |
79 |
| -} else { |
80 |
| -if (updatable()) { |
81 |
| -final Matcher.Result<R> match = match(actualResource, primary, context); |
82 |
| -if (!match.matched()) { |
83 |
| -final var desired = match.computedDesired().orElseGet(() -> desired(primary, context)); |
| 71 | +try { |
| 72 | +if (creatable() || updatable()) { |
| 73 | +if (actualResource == null) { |
| 74 | +if (creatable) { |
| 75 | +var desired = cachedDesired(primary, context); |
84 | 76 | throwIfNull(desired, primary, "Desired");
|
85 |
| -logForOperation("Updating", primary, desired); |
86 |
| -var updatedResource = handleUpdate(actualResource, desired, primary, context); |
87 |
| -return ReconcileResult.resourceUpdated(updatedResource); |
| 77 | +logForOperation("Creating", primary, desired); |
| 78 | +var createdResource = handleCreate(desired, primary, context); |
| 79 | +return ReconcileResult.resourceCreated(createdResource); |
| 80 | +} |
| 81 | +} else { |
| 82 | +if (updatable()) { |
| 83 | +final Result<R> match = match(actualResource, primary, context); |
| 84 | +if (!match.matched()) { |
| 85 | +final var desired = |
| 86 | +match.computedDesired().orElseGet(() -> cachedDesired(primary, context)); |
| 87 | +throwIfNull(desired, primary, "Desired"); |
| 88 | +logForOperation("Updating", primary, desired); |
| 89 | +var updatedResource = handleUpdate(actualResource, desired, primary, context); |
| 90 | +return ReconcileResult.resourceUpdated(updatedResource); |
| 91 | +} else { |
| 92 | +log.debug( |
| 93 | +"Update skipped for dependent {} as it matched the existing one", |
| 94 | +actualResource instanceof HasMetadata |
| 95 | +? ResourceID.fromResource((HasMetadata) actualResource) |
| 96 | +: getClass().getSimpleName()); |
| 97 | +} |
88 | 98 | } else {
|
89 | 99 | log.debug(
|
90 |
| -"Update skipped for dependent {} as it matched the existing one", |
| 100 | +"Update skipped for dependent {} implement Updater interface to modify it", |
91 | 101 | actualResource instanceof HasMetadata
|
92 | 102 | ? ResourceID.fromResource((HasMetadata) actualResource)
|
93 | 103 | : getClass().getSimpleName());
|
94 | 104 | }
|
95 |
| -} else { |
96 |
| -log.debug( |
97 |
| -"Update skipped for dependent {} implement Updater interface to modify it", |
98 |
| -actualResource instanceof HasMetadata |
99 |
| -? ResourceID.fromResource((HasMetadata) actualResource) |
100 |
| -: getClass().getSimpleName()); |
101 | 105 | }
|
| 106 | +} else { |
| 107 | +log.debug( |
| 108 | +"Dependent {} is read-only, implement Creator and/or Updater interfaces to modify it", |
| 109 | +getClass().getSimpleName()); |
102 | 110 | }
|
103 |
| -} else { |
104 |
| -log.debug( |
105 |
| -"Dependent {} is read-only, implement Creator and/or Updater interfaces to modify it", |
106 |
| -getClass().getSimpleName()); |
| 111 | +return ReconcileResult.noOperation(actualResource); |
| 112 | +} finally { |
| 113 | +desiredCache.remove(); |
107 | 114 | }
|
108 |
| -return ReconcileResult.noOperation(actualResource); |
109 | 115 | }
|
110 | 116 |
|
111 | 117 | public abstract Result<R> match(R resource, P primary, Context<P> context);
|
112 | 118 |
|
113 | 119 | @Override
|
114 | 120 | public Optional<R> getSecondaryResource(P primary, Context<P> context) {
|
115 |
| - |
116 | 121 | var secondaryResources = context.getSecondaryResources(resourceType());
|
117 | 122 | if (secondaryResources.isEmpty()) {
|
118 | 123 | return Optional.empty();
|
@@ -136,7 +141,7 @@ public Optional<R> getSecondaryResource(P primary, Context<P> context) {
|
136 | 141 | */
|
137 | 142 | protected Optional<R> selectTargetSecondaryResource(
|
138 | 143 | Set<R> secondaryResources, P primary, Context<P> context) {
|
139 |
| -R desired = desired(primary, context); |
| 144 | +R desired = cachedDesired(primary, context); |
140 | 145 | var targetResources = secondaryResources.stream().filter(r -> r.equals(desired)).toList();
|
141 | 146 | if (targetResources.size() > 1) {
|
142 | 147 | throw new IllegalStateException(
|
@@ -199,6 +204,16 @@ protected R handleUpdate(R actual, R desired, P primary, Context<P> context) {
|
199 | 204 | return updated;
|
200 | 205 | }
|
201 | 206 |
|
| 207 | +protected R cachedDesired(P primary, Context<P> context) { |
| 208 | +var desired = desiredCache.get(); |
| 209 | +if (desired != null) { |
| 210 | +return desired; |
| 211 | +} |
| 212 | +desired = desired(primary, context); |
| 213 | +desiredCache.set(desired); |
| 214 | +return desired; |
| 215 | +} |
| 216 | + |
202 | 217 | protected R desired(P primary, Context<P> context) {
|
203 | 218 | throw new IllegalStateException(
|
204 | 219 | "desired method must be implemented if this DependentResource can be created and/or"
|
|
0 commit comments