Skip to content

Commit 07b6ac4

Browse files
committed
Better volume mount error test
1 parent 517b7f5 commit 07b6ac4

File tree

5 files changed

+160
-123
lines changed

5 files changed

+160
-123
lines changed

flink-kubernetes-operator/pom.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,13 @@ under the License.
184184
</exclusions>
185185
</dependency>
186186

187+
<dependency>
188+
<groupId>io.fabric8</groupId>
189+
<artifactId>kube-api-test-client-inject</artifactId>
190+
<version>${fabric8.version}</version>
191+
<scope>test</scope>
192+
</dependency>
193+
187194
<dependency>
188195
<groupId>com.squareup.okhttp3</groupId>
189196
<artifactId>mockwebserver</artifactId>

flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/observer/deployment/AbstractFlinkDeploymentObserver.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,8 +183,7 @@ private void checkContainerErrors(FlinkResourceContext<FlinkDeployment> ctx) {
183183
.forEach(AbstractFlinkDeploymentObserver::checkContainerError);
184184

185185
// No obvious errors were found, check for volume mount issues
186-
EventUtils.checkForVolumeMountErrors(
187-
pod, () -> EventUtils.getPodEvents(ctx.getKubernetesClient(), pod));
186+
EventUtils.checkForVolumeMountErrors(ctx.getKubernetesClient(), pod);
188187
}
189188
}
190189

flink-kubernetes-operator/src/main/java/org/apache/flink/kubernetes/operator/utils/EventUtils.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@
4444
import java.util.function.Consumer;
4545
import java.util.function.Function;
4646
import java.util.function.Predicate;
47-
import java.util.function.Supplier;
4847
import java.util.stream.Collectors;
4948

5049
/**
@@ -240,7 +239,7 @@ private static Optional<Event> createOrReplaceEvent(KubernetesClient client, Eve
240239
return Optional.empty();
241240
}
242241

243-
public static List<Event> getPodEvents(KubernetesClient client, Pod pod) {
242+
private static List<Event> getPodEvents(KubernetesClient client, Pod pod) {
244243
var ref = getObjectReference(pod);
245244

246245
var eventList =
@@ -276,10 +275,10 @@ protected static ObjectReference getObjectReference(HasMetadata resource) {
276275
* Check that pod is stuck during volume mount stage and throw {@link DeploymentFailedException}
277276
* with the right reason message if that's the case.
278277
*
278+
* @param client Kubernetes client
279279
* @param pod Pod to be checked
280-
* @param podEventSupplier supplier for Pod event list. For easy testability
281280
*/
282-
public static void checkForVolumeMountErrors(Pod pod, Supplier<List<Event>> podEventSupplier) {
281+
public static void checkForVolumeMountErrors(KubernetesClient client, Pod pod) {
283282
var conditions = pod.getStatus().getConditions();
284283
if (conditions == null) {
285284
return;
@@ -300,7 +299,7 @@ public static void checkForVolumeMountErrors(Pod pod, Supplier<List<Event>> podE
300299
boolean notReady = checkStatusWasAlways(pod, conditionMap.get("Ready"), "False");
301300

302301
if (notReady && failedInitialization) {
303-
podEventSupplier.get().stream()
302+
getPodEvents(client, pod).stream()
304303
.filter(e -> e.getReason().equals("FailedMount"))
305304
.findAny()
306305
.ifPresent(

flink-kubernetes-operator/src/test/java/org/apache/flink/kubernetes/operator/utils/EventUtilsTest.java

Lines changed: 0 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,9 @@
1818
package org.apache.flink.kubernetes.operator.utils;
1919

2020
import org.apache.flink.kubernetes.operator.TestUtils;
21-
import org.apache.flink.kubernetes.operator.exception.DeploymentFailedException;
2221

2322
import io.fabric8.kubernetes.api.model.Event;
2423
import io.fabric8.kubernetes.api.model.EventBuilder;
25-
import io.fabric8.kubernetes.api.model.Pod;
26-
import io.fabric8.kubernetes.api.model.PodBuilder;
27-
import io.fabric8.kubernetes.api.model.PodCondition;
28-
import io.fabric8.kubernetes.api.model.PodConditionBuilder;
2924
import io.fabric8.kubernetes.client.KubernetesClient;
3025
import io.fabric8.kubernetes.client.server.mock.EnableKubernetesMockClient;
3126
import io.fabric8.kubernetes.client.server.mock.KubernetesMockServer;
@@ -38,17 +33,10 @@
3833

3934
import java.net.HttpURLConnection;
4035
import java.time.Duration;
41-
import java.time.Instant;
42-
import java.util.ArrayList;
43-
import java.util.HashMap;
44-
import java.util.List;
4536
import java.util.Map;
4637
import java.util.function.Consumer;
4738
import java.util.function.Predicate;
4839

49-
import static org.junit.jupiter.api.Assertions.assertEquals;
50-
import static org.junit.jupiter.api.Assertions.fail;
51-
5240
/** Test for {@link EventUtils}. */
5341
@EnableKubernetesMockClient(crud = true)
5442
public class EventUtilsTest {
@@ -584,108 +572,4 @@ public void accept(Event event) {
584572
null));
585573
Assertions.assertNull(eventConsumed);
586574
}
587-
588-
@Test
589-
public void testVolumeMountErrors() {
590-
var pod =
591-
new PodBuilder()
592-
.withNewMetadata()
593-
.withName("test")
594-
.withNamespace("default")
595-
.endMetadata()
596-
.withNewStatus()
597-
.endStatus()
598-
.build();
599-
600-
var events =
601-
List.of(
602-
createPodEvent("e1", "reason1", "msg1", pod),
603-
createPodEvent("e2", "FailedMount", "mountErr", pod));
604-
605-
// No conditions, no error expected
606-
EventUtils.checkForVolumeMountErrors(pod, () -> events);
607-
608-
var conditions = new ArrayList<PodCondition>();
609-
pod.getStatus().setConditions(conditions);
610-
611-
// No conditions, no error expected
612-
EventUtils.checkForVolumeMountErrors(pod, () -> events);
613-
614-
var conditionMap = new HashMap<String, String>();
615-
616-
// Pod initialized completely, shouldn't check events
617-
conditionMap.put("Initialized", "True");
618-
conditionMap.put("Ready", "False");
619-
620-
conditions.clear();
621-
conditionMap.forEach(
622-
(t, s) ->
623-
conditions.add(
624-
new PodConditionBuilder().withType(t).withStatus(s).build()));
625-
EventUtils.checkForVolumeMountErrors(pod, () -> events);
626-
627-
// Pod initialized completely, shouldn't check events
628-
conditionMap.put("PodReadyToStartContainers", "True");
629-
conditionMap.put("Initialized", "False");
630-
631-
conditions.clear();
632-
conditionMap.forEach(
633-
(t, s) ->
634-
conditions.add(
635-
new PodConditionBuilder().withType(t).withStatus(s).build()));
636-
EventUtils.checkForVolumeMountErrors(pod, () -> events);
637-
638-
// Check event only when not ready to start
639-
conditionMap.put("PodReadyToStartContainers", "False");
640-
conditions.clear();
641-
conditionMap.forEach(
642-
(t, s) ->
643-
conditions.add(
644-
new PodConditionBuilder().withType(t).withStatus(s).build()));
645-
646-
try {
647-
EventUtils.checkForVolumeMountErrors(pod, () -> events);
648-
fail("Exception not thrown");
649-
} catch (DeploymentFailedException dfe) {
650-
assertEquals("FailedMount", dfe.getReason());
651-
assertEquals("mountErr", dfe.getMessage());
652-
}
653-
654-
// Old kubernetes without PodReadyToStartContainers
655-
conditionMap.remove("PodReadyToStartContainers");
656-
conditionMap.put("Initialized", "False");
657-
conditions.clear();
658-
conditionMap.forEach(
659-
(t, s) ->
660-
conditions.add(
661-
new PodConditionBuilder().withType(t).withStatus(s).build()));
662-
663-
try {
664-
EventUtils.checkForVolumeMountErrors(pod, () -> events);
665-
fail("Exception not thrown");
666-
} catch (DeploymentFailedException dfe) {
667-
assertEquals("FailedMount", dfe.getReason());
668-
assertEquals("mountErr", dfe.getMessage());
669-
}
670-
}
671-
672-
private Event createPodEvent(String name, String reason, String msg, Pod pod) {
673-
return new EventBuilder()
674-
.withApiVersion("v1")
675-
.withInvolvedObject(EventUtils.getObjectReference(pod))
676-
.withType("type")
677-
.withReason(reason)
678-
.withFirstTimestamp(Instant.now().toString())
679-
.withLastTimestamp(Instant.now().toString())
680-
.withNewSource()
681-
.withComponent("pod")
682-
.endSource()
683-
.withCount(1)
684-
.withMessage(msg)
685-
.withNewMetadata()
686-
.withName(name)
687-
.withNamespace(pod.getMetadata().getNamespace())
688-
.endMetadata()
689-
.build();
690-
}
691575
}
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.flink.kubernetes.operator.utils;
19+
20+
import org.apache.flink.kubernetes.operator.exception.DeploymentFailedException;
21+
22+
import io.fabric8.kubeapitest.junit.EnableKubeAPIServer;
23+
import io.fabric8.kubernetes.api.model.EventBuilder;
24+
import io.fabric8.kubernetes.api.model.Pod;
25+
import io.fabric8.kubernetes.api.model.PodBuilder;
26+
import io.fabric8.kubernetes.api.model.PodCondition;
27+
import io.fabric8.kubernetes.api.model.PodConditionBuilder;
28+
import io.fabric8.kubernetes.client.KubernetesClient;
29+
import org.junit.jupiter.api.Test;
30+
31+
import java.time.Instant;
32+
import java.util.ArrayList;
33+
import java.util.HashMap;
34+
35+
import static org.junit.jupiter.api.Assertions.assertEquals;
36+
import static org.junit.jupiter.api.Assertions.fail;
37+
38+
/** Test for {@link EventUtils}. */
39+
@EnableKubeAPIServer
40+
public class PodErrorTest {
41+
42+
static KubernetesClient client;
43+
44+
@Test
45+
public void testVolumeMountErrors() {
46+
var pod =
47+
new PodBuilder()
48+
.withNewMetadata()
49+
.withName("test")
50+
.withNamespace("default")
51+
.endMetadata()
52+
.withNewStatus()
53+
.endStatus()
54+
.build();
55+
56+
// No conditions, no error expected
57+
EventUtils.checkForVolumeMountErrors(client, pod);
58+
59+
var conditions = new ArrayList<PodCondition>();
60+
pod.getStatus().setConditions(conditions);
61+
62+
// No conditions, no error expected
63+
EventUtils.checkForVolumeMountErrors(client, pod);
64+
65+
// Create error events
66+
createPodEvent("e1", "reason1", "msg1", pod);
67+
createPodEvent("e2", "FailedMount", "mountErr", pod);
68+
69+
var conditionMap = new HashMap<String, String>();
70+
71+
// Pod initialized completely, shouldn't check events
72+
conditionMap.put("Initialized", "True");
73+
conditionMap.put("Ready", "False");
74+
75+
conditions.clear();
76+
conditionMap.forEach(
77+
(t, s) ->
78+
conditions.add(
79+
new PodConditionBuilder().withType(t).withStatus(s).build()));
80+
EventUtils.checkForVolumeMountErrors(client, pod);
81+
82+
// Pod initialized completely, shouldn't check events
83+
conditionMap.put("PodReadyToStartContainers", "True");
84+
conditionMap.put("Initialized", "False");
85+
86+
conditions.clear();
87+
conditionMap.forEach(
88+
(t, s) ->
89+
conditions.add(
90+
new PodConditionBuilder().withType(t).withStatus(s).build()));
91+
EventUtils.checkForVolumeMountErrors(client, pod);
92+
93+
// Check event only when not ready to start
94+
conditionMap.put("PodReadyToStartContainers", "False");
95+
conditions.clear();
96+
conditionMap.forEach(
97+
(t, s) ->
98+
conditions.add(
99+
new PodConditionBuilder().withType(t).withStatus(s).build()));
100+
101+
try {
102+
EventUtils.checkForVolumeMountErrors(client, pod);
103+
fail("Exception not thrown");
104+
} catch (DeploymentFailedException dfe) {
105+
assertEquals("FailedMount", dfe.getReason());
106+
assertEquals("mountErr", dfe.getMessage());
107+
}
108+
109+
// Old kubernetes without PodReadyToStartContainers
110+
conditionMap.remove("PodReadyToStartContainers");
111+
conditionMap.put("Initialized", "False");
112+
conditions.clear();
113+
conditionMap.forEach(
114+
(t, s) ->
115+
conditions.add(
116+
new PodConditionBuilder().withType(t).withStatus(s).build()));
117+
118+
try {
119+
EventUtils.checkForVolumeMountErrors(client, pod);
120+
fail("Exception not thrown");
121+
} catch (DeploymentFailedException dfe) {
122+
assertEquals("FailedMount", dfe.getReason());
123+
assertEquals("mountErr", dfe.getMessage());
124+
}
125+
}
126+
127+
private void createPodEvent(String name, String reason, String msg, Pod pod) {
128+
var event =
129+
new EventBuilder()
130+
.withApiVersion("v1")
131+
.withInvolvedObject(EventUtils.getObjectReference(pod))
132+
.withType("type")
133+
.withReason(reason)
134+
.withFirstTimestamp(Instant.now().toString())
135+
.withLastTimestamp(Instant.now().toString())
136+
.withNewSource()
137+
.withComponent("pod")
138+
.endSource()
139+
.withCount(1)
140+
.withMessage(msg)
141+
.withNewMetadata()
142+
.withName(name)
143+
.withNamespace(pod.getMetadata().getNamespace())
144+
.endMetadata()
145+
.build();
146+
client.resource(event).create();
147+
}
148+
}

0 commit comments

Comments
 (0)