Skip to content

Commit bd645f8

Browse files
authored
Merge pull request #423 from Vlatombe/launcher_watcher
Use a watcher to monitor pod status while launching the agent
2 parents e979756 + c473ddd commit bd645f8

File tree

3 files changed

+223
-167
lines changed

3 files changed

+223
-167
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
package org.csanchez.jenkins.plugins.kubernetes;
2+
3+
import static java.util.stream.Collectors.joining;
4+
5+
import java.util.ArrayList;
6+
import java.util.Collections;
7+
import java.util.List;
8+
import java.util.concurrent.CountDownLatch;
9+
import java.util.concurrent.TimeUnit;
10+
import java.util.concurrent.atomic.AtomicReference;
11+
import java.util.logging.Level;
12+
import java.util.logging.Logger;
13+
14+
import io.fabric8.kubernetes.api.model.ContainerStatus;
15+
import io.fabric8.kubernetes.api.model.Pod;
16+
import io.fabric8.kubernetes.api.model.PodStatus;
17+
import io.fabric8.kubernetes.client.KubernetesClient;
18+
import io.fabric8.kubernetes.client.KubernetesClientException;
19+
import io.fabric8.kubernetes.client.KubernetesClientTimeoutException;
20+
import io.fabric8.kubernetes.client.Watcher;
21+
22+
/**
23+
* A pod watcher reporting when all containers are running
24+
*/
25+
public class AllContainersRunningPodWatcher implements Watcher<Pod> {
26+
private static final Logger LOGGER = Logger.getLogger(AllContainersRunningPodWatcher.class.getName());
27+
28+
private final CountDownLatch latch = new CountDownLatch(1);
29+
private final AtomicReference<Pod> reference = new AtomicReference<>();
30+
31+
private Pod pod;
32+
33+
private KubernetesClient client;
34+
35+
private PodStatus podStatus;
36+
37+
public AllContainersRunningPodWatcher(KubernetesClient client, Pod pod) {
38+
this.client = client;
39+
this.pod = pod;
40+
this.podStatus = pod.getStatus();
41+
updateState(pod);
42+
}
43+
44+
@Override
45+
public void eventReceived(Action action, Pod pod) {
46+
LOGGER.log(Level.FINEST, "[{0}] {1}", new Object[]{action, pod.getMetadata().getName()});
47+
this.podStatus = pod.getStatus();
48+
switch (action) {
49+
case MODIFIED:
50+
updateState(pod);
51+
break;
52+
default:
53+
}
54+
}
55+
56+
private void updateState(Pod pod) {
57+
if (areAllContainersRunning(pod)) {
58+
LOGGER.log(Level.FINE, "All containers are running for pod {0}", new Object[] {pod.getMetadata().getName()});
59+
reference.set(pod);
60+
latch.countDown();
61+
}
62+
}
63+
64+
boolean areAllContainersRunning(Pod pod) {
65+
PodStatus podStatus = pod.getStatus();
66+
if (podStatus == null) {
67+
return false;
68+
}
69+
List<ContainerStatus> containerStatuses = pod.getStatus().getContainerStatuses();
70+
if (containerStatuses.isEmpty()) {
71+
return false;
72+
}
73+
for (ContainerStatus containerStatus : containerStatuses) {
74+
if (containerStatus != null) {
75+
if (containerStatus.getState().getWaiting() != null) {
76+
return false;
77+
}
78+
if (containerStatus.getState().getTerminated() != null) {
79+
return false;
80+
}
81+
if (!containerStatus.getReady()) {
82+
return false;
83+
}
84+
}
85+
}
86+
return true;
87+
}
88+
89+
private List<ContainerStatus> getTerminatedContainers(Pod pod) {
90+
PodStatus podStatus = pod.getStatus();
91+
if (podStatus == null) {
92+
return Collections.emptyList();
93+
}
94+
List<ContainerStatus> containerStatuses = pod.getStatus().getContainerStatuses();
95+
if (containerStatuses.isEmpty()) {
96+
return Collections.emptyList();
97+
}
98+
List<ContainerStatus> result = new ArrayList<>();
99+
for (ContainerStatus containerStatus : containerStatuses) {
100+
if (containerStatus != null) {
101+
if (containerStatus.getState().getTerminated() != null) {
102+
result.add(containerStatus);
103+
}
104+
}
105+
}
106+
return result;
107+
}
108+
109+
@Override
110+
public void onClose(KubernetesClientException cause) {
111+
112+
}
113+
114+
public Pod await(long amount, TimeUnit timeUnit) {
115+
long started = System.currentTimeMillis();
116+
long alreadySpent = System.currentTimeMillis() - started;
117+
long remaining = timeUnit.toMillis(amount) - alreadySpent;
118+
if (remaining <= 0) {
119+
return periodicAwait(0, System.currentTimeMillis(), 0, 0);
120+
}
121+
try {
122+
return periodicAwait(10, System.currentTimeMillis(), Math.max(remaining / 10, 1000L), remaining);
123+
} catch (KubernetesClientTimeoutException e) {
124+
// Wrap using the right timeout
125+
throw new KubernetesClientTimeoutException(pod, amount, timeUnit);
126+
}
127+
}
128+
129+
private Pod awaitWatcher(long amount, TimeUnit timeUnit) {
130+
try {
131+
if (latch.await(amount, timeUnit)) {
132+
return reference.get();
133+
}
134+
throw new KubernetesClientTimeoutException(pod, amount, timeUnit);
135+
} catch (InterruptedException e) {
136+
throw new KubernetesClientTimeoutException(pod, amount, timeUnit);
137+
}
138+
}
139+
140+
private Pod periodicAwait(int i, long started, long interval, long amount) {
141+
Pod pod = client.pods().inNamespace(this.pod.getMetadata().getNamespace())
142+
.withName(this.pod.getMetadata().getName()).get();
143+
if (pod == null) {
144+
throw new IllegalStateException(String.format("Pod is no longer available: %s/%s",
145+
this.pod.getMetadata().getNamespace(), this.pod.getMetadata().getName()));
146+
}
147+
List<ContainerStatus> terminatedContainers = getTerminatedContainers(pod);
148+
if (!terminatedContainers.isEmpty()) {
149+
throw new IllegalStateException(String.format("Pod has terminated containers: %s/%s (%s)",
150+
this.pod.getMetadata().getNamespace(),
151+
this.pod.getMetadata().getName(),
152+
terminatedContainers.stream()
153+
.map(ContainerStatus::getName)
154+
.collect(joining(", ")
155+
)));
156+
}
157+
if (areAllContainersRunning(pod)) {
158+
return pod;
159+
}
160+
try {
161+
return awaitWatcher(interval, TimeUnit.MILLISECONDS);
162+
} catch (KubernetesClientTimeoutException e) {
163+
if (i <= 0) {
164+
throw e;
165+
}
166+
}
167+
168+
long remaining = (started + amount) - System.currentTimeMillis();
169+
long next = Math.max(0, Math.min(remaining, interval));
170+
return periodicAwait(i - 1, started, next, amount);
171+
}
172+
173+
public PodStatus getPodStatus() {
174+
return podStatus;
175+
}
176+
}

0 commit comments

Comments
 (0)