Skip to content

Commit 34a249f

Browse files
committed
integration test
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
1 parent e82b963 commit 34a249f

File tree

4 files changed

+269
-0
lines changed

4 files changed

+269
-0
lines changed
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
/*
2+
* Copyright Java Operator SDK Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.javaoperatorsdk.operator.baseapi.configloader;
17+
18+
import java.util.Map;
19+
import java.util.Optional;
20+
import java.util.concurrent.TimeUnit;
21+
import java.util.function.Consumer;
22+
23+
import org.junit.jupiter.api.Nested;
24+
import org.junit.jupiter.api.Test;
25+
import org.junit.jupiter.api.extension.RegisterExtension;
26+
27+
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
28+
import io.javaoperatorsdk.operator.api.config.ControllerConfigurationOverrider;
29+
import io.javaoperatorsdk.operator.config.loader.ConfigLoader;
30+
import io.javaoperatorsdk.operator.config.loader.ConfigProvider;
31+
import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension;
32+
import io.javaoperatorsdk.operator.support.TestUtils;
33+
34+
import static org.assertj.core.api.Assertions.assertThat;
35+
import static org.awaitility.Awaitility.await;
36+
37+
/**
38+
* Integration tests that verify {@link ConfigLoader} property overrides take effect when wiring up
39+
* a real operator instance via {@link LocallyRunOperatorExtension}.
40+
*
41+
* <p>Each nested class exercises a distinct group of properties so that failures are easy to
42+
* pinpoint.
43+
*/
44+
class ConfigLoaderIT {
45+
46+
/** Builds a {@link ConfigProvider} backed by a plain map. */
47+
private static ConfigProvider mapProvider(Map<String, Object> values) {
48+
return new ConfigProvider() {
49+
@Override
50+
@SuppressWarnings("unchecked")
51+
public <T> Optional<T> getValue(String key, Class<T> type) {
52+
return Optional.ofNullable((T) values.get(key));
53+
}
54+
};
55+
}
56+
57+
// ---------------------------------------------------------------------------
58+
// Operator-level properties
59+
// ---------------------------------------------------------------------------
60+
61+
@Nested
62+
class OperatorLevelProperties {
63+
64+
/**
65+
* Verifies that {@code josdk.reconciliation.concurrent-threads} loaded via {@link ConfigLoader}
66+
* and applied through {@code withConfigurationService} actually changes the operator's thread
67+
* pool size.
68+
*/
69+
@RegisterExtension
70+
LocallyRunOperatorExtension operator =
71+
LocallyRunOperatorExtension.builder()
72+
.withReconciler(new ConfigLoaderTestReconciler(0))
73+
.withConfigurationService(
74+
new ConfigLoader(mapProvider(Map.of("josdk.reconciliation.concurrent-threads", 2)))
75+
.applyConfigs())
76+
.build();
77+
78+
@Test
79+
void concurrentReconciliationThreadsIsAppliedFromConfigLoader() {
80+
assertThat(operator.getOperator().getConfigurationService().concurrentReconciliationThreads())
81+
.isEqualTo(2);
82+
}
83+
}
84+
85+
// ---------------------------------------------------------------------------
86+
// Controller-level retry
87+
// ---------------------------------------------------------------------------
88+
89+
@Nested
90+
class ControllerRetryProperties {
91+
92+
static final int FAILS = 2;
93+
// controller name is the lower-cased simple class name by default
94+
static final String CTRL_NAME = ConfigLoaderTestReconciler.class.getSimpleName().toLowerCase();
95+
96+
/**
97+
* Verifies that retry properties read by {@link ConfigLoader} for a specific controller name
98+
* are applied when registering the reconciler via a {@code configurationOverrider} consumer,
99+
* and that the resulting operator actually retries and eventually succeeds.
100+
*/
101+
@RegisterExtension
102+
LocallyRunOperatorExtension operator =
103+
LocallyRunOperatorExtension.builder()
104+
.withReconciler(
105+
new ConfigLoaderTestReconciler(FAILS),
106+
// applyControllerConfigs returns Consumer<ControllerConfigurationOverrider<R>>;
107+
// withReconciler takes the raw Consumer<ControllerConfigurationOverrider>
108+
(Consumer<ControllerConfigurationOverrider>)
109+
(Consumer<?>)
110+
new ConfigLoader(
111+
mapProvider(
112+
Map.of(
113+
"josdk.controller." + CTRL_NAME + ".retry.max-attempts",
114+
5,
115+
"josdk.controller." + CTRL_NAME + ".retry.initial-interval",
116+
100L)))
117+
.applyControllerConfigs(CTRL_NAME))
118+
.build();
119+
120+
@Test
121+
void retryConfigFromConfigLoaderIsAppliedAndReconcilerEventuallySucceeds() {
122+
var resource = createResource("1");
123+
operator.create(resource);
124+
125+
await("reconciler succeeds after retries")
126+
.atMost(10, TimeUnit.SECONDS)
127+
.pollInterval(100, TimeUnit.MILLISECONDS)
128+
.untilAsserted(
129+
() -> {
130+
assertThat(TestUtils.getNumberOfExecutions(operator)).isEqualTo(FAILS + 1);
131+
var updated =
132+
operator.get(
133+
ConfigLoaderTestCustomResource.class, resource.getMetadata().getName());
134+
assertThat(updated.getStatus()).isNotNull();
135+
assertThat(updated.getStatus().getState())
136+
.isEqualTo(ConfigLoaderTestCustomResourceStatus.State.SUCCESS);
137+
});
138+
}
139+
140+
private ConfigLoaderTestCustomResource createResource(String id) {
141+
var resource = new ConfigLoaderTestCustomResource();
142+
resource.setMetadata(new ObjectMetaBuilder().withName("cfgloader-retry-" + id).build());
143+
return resource;
144+
}
145+
}
146+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Copyright Java Operator SDK Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.javaoperatorsdk.operator.baseapi.configloader;
17+
18+
import io.fabric8.kubernetes.api.model.Namespaced;
19+
import io.fabric8.kubernetes.client.CustomResource;
20+
import io.fabric8.kubernetes.model.annotation.Group;
21+
import io.fabric8.kubernetes.model.annotation.Kind;
22+
import io.fabric8.kubernetes.model.annotation.ShortNames;
23+
import io.fabric8.kubernetes.model.annotation.Version;
24+
25+
@Group("sample.javaoperatorsdk")
26+
@Version("v1")
27+
@Kind("ConfigLoaderSample")
28+
@ShortNames("cls")
29+
public class ConfigLoaderTestCustomResource
30+
extends CustomResource<Void, ConfigLoaderTestCustomResourceStatus> implements Namespaced {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright Java Operator SDK Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.javaoperatorsdk.operator.baseapi.configloader;
17+
18+
public class ConfigLoaderTestCustomResourceStatus {
19+
20+
public enum State {
21+
SUCCESS,
22+
ERROR
23+
}
24+
25+
private State state;
26+
27+
public State getState() {
28+
return state;
29+
}
30+
31+
public ConfigLoaderTestCustomResourceStatus setState(State state) {
32+
this.state = state;
33+
return this;
34+
}
35+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright Java Operator SDK Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.javaoperatorsdk.operator.baseapi.configloader;
17+
18+
import java.util.concurrent.atomic.AtomicInteger;
19+
20+
import io.javaoperatorsdk.operator.api.reconciler.Context;
21+
import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration;
22+
import io.javaoperatorsdk.operator.api.reconciler.Reconciler;
23+
import io.javaoperatorsdk.operator.api.reconciler.UpdateControl;
24+
import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider;
25+
26+
/**
27+
* A reconciler that fails for the first {@code numberOfFailures} invocations and then succeeds,
28+
* setting the status to {@link ConfigLoaderTestCustomResourceStatus.State#SUCCESS}.
29+
*/
30+
@ControllerConfiguration
31+
public class ConfigLoaderTestReconciler
32+
implements Reconciler<ConfigLoaderTestCustomResource>, TestExecutionInfoProvider {
33+
34+
private final AtomicInteger numberOfExecutions = new AtomicInteger(0);
35+
private final int numberOfFailures;
36+
37+
public ConfigLoaderTestReconciler(int numberOfFailures) {
38+
this.numberOfFailures = numberOfFailures;
39+
}
40+
41+
@Override
42+
public UpdateControl<ConfigLoaderTestCustomResource> reconcile(
43+
ConfigLoaderTestCustomResource resource, Context<ConfigLoaderTestCustomResource> context) {
44+
int execution = numberOfExecutions.incrementAndGet();
45+
if (execution <= numberOfFailures) {
46+
throw new RuntimeException("Simulated failure on execution " + execution);
47+
}
48+
var status = new ConfigLoaderTestCustomResourceStatus();
49+
status.setState(ConfigLoaderTestCustomResourceStatus.State.SUCCESS);
50+
resource.setStatus(status);
51+
return UpdateControl.patchStatus(resource);
52+
}
53+
54+
@Override
55+
public int getNumberOfExecutions() {
56+
return numberOfExecutions.get();
57+
}
58+
}

0 commit comments

Comments
 (0)