Skip to content

Commit cdaaeb0

Browse files
committed
feat: expectation pattern support
Signed-off-by: Attila Mészáros <[email protected]>
1 parent 205f001 commit cdaaeb0

File tree

5 files changed

+127
-0
lines changed

5 files changed

+127
-0
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package io.javaoperatorsdk.operator.processing.expectation;
2+
3+
import java.util.function.BiPredicate;
4+
5+
import io.fabric8.kubernetes.api.model.HasMetadata;
6+
import io.javaoperatorsdk.operator.api.reconciler.Context;
7+
8+
public interface Expectation<P extends HasMetadata> {
9+
10+
String UNNAMED = "unnamed";
11+
12+
boolean isFulfilled(P primary, Context<P> context);
13+
14+
default String name() {
15+
return UNNAMED;
16+
}
17+
18+
static <P extends HasMetadata> Expectation<P> createExpectation(
19+
String name, BiPredicate<P, Context<P>> predicate) {
20+
return new Expectation<>() {
21+
@Override
22+
public String name() {
23+
return name;
24+
}
25+
26+
@Override
27+
public boolean isFulfilled(P primary, Context<P> context) {
28+
return predicate.test(primary, context);
29+
}
30+
};
31+
}
32+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package io.javaoperatorsdk.operator.processing.expectation;
2+
3+
import java.time.Duration;
4+
import java.time.LocalDateTime;
5+
import java.util.Optional;
6+
import java.util.concurrent.ConcurrentHashMap;
7+
8+
import io.fabric8.kubernetes.api.model.HasMetadata;
9+
import io.javaoperatorsdk.operator.api.reconciler.Context;
10+
import io.javaoperatorsdk.operator.processing.event.ResourceID;
11+
12+
public class ExpectationManager<P extends HasMetadata> {
13+
14+
private final ConcurrentHashMap<ResourceID, RegisteredExpectation<P>> registeredExpectations =
15+
new ConcurrentHashMap<>();
16+
17+
public void setExpectation(P primary, Expectation<P> expectation, Duration timeout) {
18+
registeredExpectations.put(
19+
ResourceID.fromResource(primary),
20+
new RegisteredExpectation<>(LocalDateTime.now(), timeout, expectation));
21+
}
22+
23+
/**
24+
* Checks if provided expectation is fulfilled. Return the expectation result. If the result of
25+
* expectation is fulfilled or timeout, the expectation is automatically removed;
26+
*/
27+
public Optional<ExpectationResult<P>> checkOnExpectation(P primary, Context<P> context) {
28+
var resourceID = ResourceID.fromResource(primary);
29+
var regExp = registeredExpectations.get(ResourceID.fromResource(primary));
30+
if (regExp == null) {
31+
return Optional.empty();
32+
}
33+
if (regExp.expectation().isFulfilled(primary, context)) {
34+
registeredExpectations.remove(resourceID);
35+
return Optional.of(
36+
new ExpectationResult<>(regExp.expectation(), ExpectationStatus.FULFILLED));
37+
} else if (regExp.isTimedOut()) {
38+
registeredExpectations.remove(resourceID);
39+
return Optional.of(
40+
new ExpectationResult<>(regExp.expectation(), ExpectationStatus.TIMED_OUT));
41+
} else {
42+
return Optional.of(
43+
new ExpectationResult<>(regExp.expectation(), ExpectationStatus.NOT_FULFILLED));
44+
}
45+
}
46+
47+
public boolean isExpectationPresent(P primary) {
48+
return registeredExpectations.containsKey(ResourceID.fromResource(primary));
49+
}
50+
51+
public Optional<Expectation<P>> getExpectation(P primary) {
52+
var regExp = registeredExpectations.get(ResourceID.fromResource(primary));
53+
return Optional.ofNullable(regExp).map(RegisteredExpectation::expectation);
54+
}
55+
56+
public void cleanup(P primary) {
57+
registeredExpectations.remove(ResourceID.fromResource(primary));
58+
}
59+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package io.javaoperatorsdk.operator.processing.expectation;
2+
3+
import io.fabric8.kubernetes.api.model.HasMetadata;
4+
5+
public record ExpectationResult<P extends HasMetadata>(
6+
Expectation<P> expectation, ExpectationStatus status) {
7+
8+
public boolean isFulfilled() {
9+
return status == ExpectationStatus.FULFILLED;
10+
}
11+
12+
public boolean isTimedOut() {
13+
return status == ExpectationStatus.TIMED_OUT;
14+
}
15+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package io.javaoperatorsdk.operator.processing.expectation;
2+
3+
public enum ExpectationStatus {
4+
FULFILLED,
5+
NOT_FULFILLED,
6+
TIMED_OUT
7+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package io.javaoperatorsdk.operator.processing.expectation;
2+
3+
import java.time.Duration;
4+
import java.time.LocalDateTime;
5+
6+
import io.fabric8.kubernetes.api.model.HasMetadata;
7+
8+
record RegisteredExpectation<P extends HasMetadata>(
9+
LocalDateTime registeredAt, Duration timeout, Expectation<P> expectation) {
10+
11+
public boolean isTimedOut() {
12+
return LocalDateTime.now().isAfter(registeredAt.plus(timeout));
13+
}
14+
}

0 commit comments

Comments
 (0)