Skip to content

Commit 28891d2

Browse files
committed
Add unit test for configmaphelper
1 parent 7a67820 commit 28891d2

File tree

7 files changed

+502
-34
lines changed

7 files changed

+502
-34
lines changed

operator/src/main/java/oracle/kubernetes/operator/helpers/ConfigMapHelper.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public class ConfigMapHelper {
3838

3939
private static final String SCRIPTS = "scripts";
4040
private static final String SCRIPT_LOCATION = "/" + SCRIPTS;
41+
private static final ConfigMapComparator COMPARATOR = new ConfigMapComparatorImpl();
4142

4243
private ConfigMapHelper() {}
4344

@@ -128,7 +129,7 @@ public NextAction onSuccess(
128129
return doNext(create, packet);
129130
} else if (VersionHelper.matchesResourceVersion(
130131
result.getMetadata(), VersionConstants.DOMAIN_V1)
131-
&& result.getData().entrySet().containsAll(cm.getData().entrySet())) {
132+
&& COMPARATOR.containsAll(result, cm)) {
132133
// existing config map has correct data
133134
LOGGER.fine(MessageKeys.CM_EXISTS, domainNamespace);
134135
packet.put(ProcessingConstants.SCRIPT_CONFIG_MAP, result);
@@ -248,4 +249,16 @@ private static byte[] read(Path path) {
248249
return null;
249250
}
250251
}
252+
253+
interface ConfigMapComparator {
254+
/** Returns true if the actual map contains all of the entries from the expected map. */
255+
boolean containsAll(V1ConfigMap actual, V1ConfigMap expected);
256+
}
257+
258+
static class ConfigMapComparatorImpl implements ConfigMapComparator {
259+
@Override
260+
public boolean containsAll(V1ConfigMap actual, V1ConfigMap expected) {
261+
return actual.getData().entrySet().containsAll(expected.getData().entrySet());
262+
}
263+
}
251264
}
Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
// Copyright 2018, Oracle Corporation and/or its affiliates. All rights reserved.
2+
// Licensed under the Universal Permissive License v 1.0 as shown at
3+
// http://oss.oracle.com/licenses/upl.
4+
5+
package oracle.kubernetes.operator.helpers;
6+
7+
import static com.meterware.simplestub.Stub.createStrictStub;
8+
import static oracle.kubernetes.LogMatcher.containsFine;
9+
import static oracle.kubernetes.LogMatcher.containsInfo;
10+
import static oracle.kubernetes.operator.ProcessingConstants.SCRIPT_CONFIG_MAP;
11+
import static oracle.kubernetes.operator.logging.MessageKeys.CM_CREATED;
12+
import static oracle.kubernetes.operator.logging.MessageKeys.CM_EXISTS;
13+
import static oracle.kubernetes.operator.logging.MessageKeys.CM_REPLACED;
14+
import static org.hamcrest.Matchers.hasEntry;
15+
import static org.hamcrest.Matchers.sameInstance;
16+
import static org.hamcrest.junit.MatcherAssert.assertThat;
17+
18+
import com.meterware.simplestub.Memento;
19+
import com.meterware.simplestub.StaticStubSupport;
20+
import io.kubernetes.client.ApiException;
21+
import io.kubernetes.client.models.V1ConfigMap;
22+
import io.kubernetes.client.models.V1ObjectMeta;
23+
import java.net.HttpURLConnection;
24+
import java.util.ArrayList;
25+
import java.util.List;
26+
import java.util.Map;
27+
import java.util.logging.Level;
28+
import java.util.logging.LogRecord;
29+
import java.util.stream.Collectors;
30+
import java.util.stream.Stream;
31+
import oracle.kubernetes.TestUtils;
32+
import oracle.kubernetes.operator.KubernetesConstants;
33+
import oracle.kubernetes.operator.LabelConstants;
34+
import oracle.kubernetes.operator.VersionConstants;
35+
import oracle.kubernetes.operator.work.AsyncCallTestSupport;
36+
import oracle.kubernetes.operator.work.BodyMatcher;
37+
import oracle.kubernetes.operator.work.Packet;
38+
import oracle.kubernetes.operator.work.Step;
39+
import org.junit.After;
40+
import org.junit.Before;
41+
import org.junit.Test;
42+
43+
public class ConfigMapHelperTest {
44+
private static final String DOMAIN_NS = "namespace";
45+
private static final String OPERATOR_NS = "operator";
46+
private static final String[] SCRIPT_NAMES = {
47+
"livenessProbe.sh",
48+
"readinessProbe.sh",
49+
"readState.sh",
50+
"start-server.py",
51+
"startServer.sh",
52+
"stop-server.py",
53+
"stopServer.sh"
54+
};
55+
56+
private static final String[] PARTIAL_SCRIPT_NAMES = {"livenessProbe.sh", "additional.sh"};
57+
private static final String[] COMBINED_SCRIPT_NAMES = combine(SCRIPT_NAMES, PARTIAL_SCRIPT_NAMES);
58+
59+
private final V1ConfigMap defaultConfigMap = defineDefaultConfigMap();
60+
private RetryStrategyStub retryStrategy = createStrictStub(RetryStrategyStub.class);
61+
62+
private AsyncCallTestSupport testSupport = new AsyncCallTestSupport();
63+
private List<Memento> mementos = new ArrayList<>();
64+
private List<LogRecord> logRecords = new ArrayList<>();
65+
66+
private V1ConfigMap defineDefaultConfigMap() {
67+
return defineConfigMap(SCRIPT_NAMES);
68+
}
69+
70+
private V1ConfigMap defineConfigMap(String... scriptNames) {
71+
return new V1ConfigMap()
72+
.apiVersion("v1")
73+
.kind("ConfigMap")
74+
.metadata(createMetadata())
75+
.data(nameOnlyScriptMap(scriptNames));
76+
}
77+
78+
@SuppressWarnings("SameParameterValue")
79+
private static String[] combine(String[] first, String[] second) {
80+
return Stream.of(first, second).flatMap(Stream::of).distinct().toArray(String[]::new);
81+
}
82+
83+
private static Map<String, String> nameOnlyScriptMap(String... scriptNames) {
84+
return Stream.of(scriptNames).collect(Collectors.toMap(s -> s, s -> ""));
85+
}
86+
87+
private V1ObjectMeta createMetadata() {
88+
return new V1ObjectMeta()
89+
.name(KubernetesConstants.DOMAIN_CONFIG_MAP_NAME)
90+
.namespace(DOMAIN_NS)
91+
.putLabelsItem(LabelConstants.RESOURCE_VERSION_LABEL, VersionConstants.DOMAIN_V1)
92+
.putLabelsItem(LabelConstants.OPERATORNAME_LABEL, OPERATOR_NS)
93+
.putLabelsItem(LabelConstants.CREATEDBYOPERATOR_LABEL, "true");
94+
}
95+
96+
@Before
97+
public void setUp() throws Exception {
98+
mementos.add(
99+
TestUtils.silenceOperatorLogger()
100+
.collectLogMessages(logRecords, CM_CREATED, CM_EXISTS, CM_REPLACED)
101+
.withLogLevel(Level.FINE));
102+
mementos.add(testSupport.installRequestStepFactory());
103+
mementos.add(TestComparator.install());
104+
}
105+
106+
@After
107+
public void tearDown() throws Exception {
108+
for (Memento memento : mementos) memento.revert();
109+
110+
testSupport.throwOnCompletionFailure();
111+
testSupport.verifyAllDefinedResponsesInvoked();
112+
}
113+
114+
@Test
115+
public void whenNoConfigMap_createIt() {
116+
expectReadConfigMap().failingWithStatus(HttpURLConnection.HTTP_NOT_FOUND);
117+
expectSuccessfulCreateConfigMap(defaultConfigMap);
118+
119+
testSupport.runSteps(ConfigMapHelper.createScriptConfigMapStep(OPERATOR_NS, DOMAIN_NS));
120+
121+
assertThat(logRecords, containsInfo(CM_CREATED));
122+
}
123+
124+
@Test
125+
public void whenNoConfigMap_retryOnFailure() {
126+
testSupport.addRetryStrategy(retryStrategy);
127+
expectReadConfigMap().failingWithStatus(HttpURLConnection.HTTP_NOT_FOUND);
128+
expectCreateConfigMap(defaultConfigMap).failingWithStatus(401);
129+
130+
Step scriptConfigMapStep = ConfigMapHelper.createScriptConfigMapStep(OPERATOR_NS, DOMAIN_NS);
131+
testSupport.runSteps(scriptConfigMapStep);
132+
133+
testSupport.verifyCompletionThrowable(ApiException.class);
134+
assertThat(retryStrategy.getConflictStep(), sameInstance(scriptConfigMapStep));
135+
}
136+
137+
@SuppressWarnings("unchecked")
138+
@Test
139+
public void whenMatchingConfigMapExists_addToPacket() {
140+
expectReadConfigMap().returning(defaultConfigMap);
141+
142+
Packet packet =
143+
testSupport.runSteps(ConfigMapHelper.createScriptConfigMapStep(OPERATOR_NS, DOMAIN_NS));
144+
145+
assertThat(packet, hasEntry(SCRIPT_CONFIG_MAP, defaultConfigMap));
146+
assertThat(logRecords, containsFine(CM_EXISTS));
147+
}
148+
149+
@SuppressWarnings("unchecked")
150+
@Test
151+
public void whenExistingConfigMapIsMissingData_replaceIt() {
152+
expectReadConfigMap().returning(defineConfigMap(PARTIAL_SCRIPT_NAMES));
153+
expectSuccessfulReplaceConfigMap(defineConfigMap(COMBINED_SCRIPT_NAMES));
154+
155+
testSupport.runSteps(ConfigMapHelper.createScriptConfigMapStep(OPERATOR_NS, DOMAIN_NS));
156+
157+
assertThat(logRecords, containsInfo(CM_REPLACED));
158+
}
159+
160+
@SuppressWarnings("unchecked")
161+
@Test
162+
public void whenReplaceFails_scheduleRetry() {
163+
testSupport.addRetryStrategy(retryStrategy);
164+
expectReadConfigMap().returning(defineConfigMap(PARTIAL_SCRIPT_NAMES));
165+
expectReplaceConfigMap(defineConfigMap(COMBINED_SCRIPT_NAMES)).failingWithStatus(401);
166+
167+
Step scriptConfigMapStep = ConfigMapHelper.createScriptConfigMapStep(OPERATOR_NS, DOMAIN_NS);
168+
testSupport.runSteps(scriptConfigMapStep);
169+
170+
testSupport.verifyCompletionThrowable(ApiException.class);
171+
assertThat(retryStrategy.getConflictStep(), sameInstance(scriptConfigMapStep));
172+
}
173+
174+
private AsyncCallTestSupport.CannedResponse expectReadConfigMap() {
175+
return testSupport
176+
.createCannedResponse("readConfigMap")
177+
.withNamespace(DOMAIN_NS)
178+
.withName(KubernetesConstants.DOMAIN_CONFIG_MAP_NAME);
179+
}
180+
181+
@SuppressWarnings("unchecked")
182+
private void expectSuccessfulCreateConfigMap(V1ConfigMap expectedConfig) {
183+
expectCreateConfigMap(expectedConfig).returning(expectedConfig);
184+
}
185+
186+
private AsyncCallTestSupport.CannedResponse expectCreateConfigMap(V1ConfigMap expectedConfig) {
187+
return testSupport
188+
.createCannedResponse("createConfigMap")
189+
.withNamespace(DOMAIN_NS)
190+
.withBody(new V1ConfigMapMatcher(expectedConfig));
191+
}
192+
193+
@SuppressWarnings("unchecked")
194+
private void expectSuccessfulReplaceConfigMap(V1ConfigMap expectedConfig) {
195+
expectReplaceConfigMap(expectedConfig).returning(expectedConfig);
196+
}
197+
198+
private AsyncCallTestSupport.CannedResponse expectReplaceConfigMap(V1ConfigMap expectedConfig) {
199+
return testSupport
200+
.createCannedResponse("replaceConfigMap")
201+
.withNamespace(DOMAIN_NS)
202+
.withName(KubernetesConstants.DOMAIN_CONFIG_MAP_NAME)
203+
.withBody(new V1ConfigMapMatcher(expectedConfig));
204+
}
205+
206+
class V1ConfigMapMatcher implements BodyMatcher {
207+
private V1ConfigMap expected;
208+
209+
V1ConfigMapMatcher(V1ConfigMap expected) {
210+
this.expected = expected;
211+
}
212+
213+
@Override
214+
public boolean matches(Object actualBody) {
215+
return actualBody instanceof V1ConfigMap && matches((V1ConfigMap) actualBody);
216+
}
217+
218+
private boolean matches(V1ConfigMap actualBody) {
219+
return hasExpectedKeys(actualBody) && adjustedBody(actualBody).equals(actualBody);
220+
}
221+
222+
private boolean hasExpectedKeys(V1ConfigMap actualBody) {
223+
return expected.getData().keySet().equals(actualBody.getData().keySet());
224+
}
225+
226+
private V1ConfigMap adjustedBody(V1ConfigMap actualBody) {
227+
return new V1ConfigMap()
228+
.apiVersion(expected.getApiVersion())
229+
.kind(expected.getKind())
230+
.metadata(expected.getMetadata())
231+
.data(actualBody.getData());
232+
}
233+
}
234+
235+
// An implementation of the comparator that tests only the keys in the maps
236+
static class TestComparator implements ConfigMapHelper.ConfigMapComparator {
237+
static Memento install() throws NoSuchFieldException {
238+
return StaticStubSupport.install(ConfigMapHelper.class, "COMPARATOR", new TestComparator());
239+
}
240+
241+
@Override
242+
public boolean containsAll(V1ConfigMap actual, V1ConfigMap expected) {
243+
return actual.getData().keySet().containsAll(expected.getData().keySet());
244+
}
245+
}
246+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright 2018, Oracle Corporation and/or its affiliates. All rights reserved.
2+
// Licensed under the Universal Permissive License v 1.0 as shown at
3+
// http://oss.oracle.com/licenses/upl.
4+
5+
package oracle.kubernetes.operator.helpers;
6+
7+
import io.kubernetes.client.ApiException;
8+
import java.util.List;
9+
import java.util.Map;
10+
import oracle.kubernetes.operator.calls.RetryStrategy;
11+
import oracle.kubernetes.operator.work.NextAction;
12+
import oracle.kubernetes.operator.work.Packet;
13+
import oracle.kubernetes.operator.work.Step;
14+
15+
abstract class RetryStrategyStub implements RetryStrategy {
16+
private Step conflictStep;
17+
18+
Step getConflictStep() {
19+
return conflictStep;
20+
}
21+
22+
@Override
23+
public NextAction doPotentialRetry(
24+
Step conflictStep,
25+
Packet packet,
26+
ApiException e,
27+
int statusCode,
28+
Map<String, List<String>> responseHeaders) {
29+
this.conflictStep = conflictStep;
30+
return null;
31+
}
32+
}

operator/src/test/java/oracle/kubernetes/operator/helpers/ServiceHelperTest.java

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,12 @@
4949
import java.util.ArrayList;
5050
import java.util.Collections;
5151
import java.util.List;
52-
import java.util.Map;
5352
import java.util.logging.Level;
5453
import java.util.logging.LogRecord;
5554
import oracle.kubernetes.TestUtils;
5655
import oracle.kubernetes.operator.VersionConstants;
57-
import oracle.kubernetes.operator.calls.RetryStrategy;
5856
import oracle.kubernetes.operator.wlsconfig.NetworkAccessPoint;
5957
import oracle.kubernetes.operator.work.AsyncCallTestSupport;
60-
import oracle.kubernetes.operator.work.NextAction;
61-
import oracle.kubernetes.operator.work.Packet;
6258
import oracle.kubernetes.operator.work.Step;
6359
import oracle.kubernetes.operator.work.TerminalStep;
6460
import oracle.kubernetes.weblogic.domain.v1.Domain;
@@ -676,23 +672,4 @@ private V1ServiceSpec createExternalChannelServiceSpec() {
676672
private AsyncCallTestSupport.CannedResponse expectReadService(String serviceName) {
677673
return testSupport.createCannedResponse("readService").withNamespace(NS).withName(serviceName);
678674
}
679-
680-
abstract static class RetryStrategyStub implements RetryStrategy {
681-
private Step conflictStep;
682-
683-
Step getConflictStep() {
684-
return conflictStep;
685-
}
686-
687-
@Override
688-
public NextAction doPotentialRetry(
689-
Step conflictStep,
690-
Packet packet,
691-
ApiException e,
692-
int statusCode,
693-
Map<String, List<String>> responseHeaders) {
694-
this.conflictStep = conflictStep;
695-
return null;
696-
}
697-
}
698675
}

0 commit comments

Comments
 (0)