io.fabric8.kubernetes.assertions.support.PodWatcher Maven / Gradle / Ivy
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.fabric8.kubernetes.assertions.support;
import io.fabric8.kubernetes.api.KubernetesHelper;
import io.fabric8.kubernetes.api.model.Container;
import io.fabric8.kubernetes.api.model.Pod;
import io.fabric8.kubernetes.api.model.PodSpec;
import io.fabric8.kubernetes.assertions.PodSelectionAssert;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClientException;
import io.fabric8.kubernetes.client.Watcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static io.fabric8.kubernetes.api.KubernetesHelper.getName;
import static io.fabric8.kubernetes.assertions.support.LogHelpers.getRestartCount;
import static org.assertj.core.api.Assertions.fail;
import static org.fusesource.jansi.Ansi.Color.GREEN;
import static org.fusesource.jansi.Ansi.ansi;
/**
*/
public class PodWatcher implements Watcher, Closeable {
private static final transient Logger LOG = LoggerFactory.getLogger(PodWatcher.class);
private final PodSelectionAssert podSelectionAssert;
private final long readyTimeoutMS;
private final long readyPeriodMS;
private Map podAsserts = new ConcurrentHashMap<>();
private Map podLogWatchers = new ConcurrentHashMap<>();
private CountDownLatch podReady = new CountDownLatch(1);
private CountDownLatch podReadyForEntireDuration = new CountDownLatch(1);
private File basedir;
public PodWatcher(PodSelectionAssert podSelectionAssert, long readyTimeoutMS, long readyPeriodMS) {
this.podSelectionAssert = podSelectionAssert;
this.readyTimeoutMS = readyTimeoutMS;
this.readyPeriodMS = readyPeriodMS;
}
public KubernetesClient getClient() {
return podSelectionAssert.getClient();
}
public String getDescription() {
return podSelectionAssert.getDescription();
}
public long getReadyTimeoutMS() {
return readyTimeoutMS;
}
public long getReadyPeriodMS() {
return readyPeriodMS;
}
/**
* Lets load the current pods as we don't get watch events for current pods
*/
public void loadCurrentPods() {
List pods = podSelectionAssert.getPods();
for (Pod pod : pods) {
String name = getName(pod);
if (!podAsserts.containsKey(name)) {
onPod(name, pod);
}
}
}
@Override
public void eventReceived(Action action, Pod pod) {
String name = getName(pod);
if (action.equals(Action.ERROR)) {
LOG.warn("Got error for pod " + name);
} else if (action.equals(Action.DELETED)) {
closeCloser(name, this.podAsserts);
closeCloser(name, this.podLogWatchers);
} else {
onPod(name, pod);
}
}
protected void onPod(String name, Pod pod) {
PodAsserter asserter = podAsserts.get(name);
if (asserter == null) {
asserter = new PodAsserter(this, name, pod);
podAsserts.put(name, asserter);
} else {
asserter.updated(pod);
}
int restartCount = getRestartCount(pod);
PodSpec spec = pod.getSpec();
if (spec != null) {
if (KubernetesHelper.isPodRunning(pod)) {
List containers = spec.getContainers();
for (Container container : containers) {
File logFileName = LogHelpers.getLogFileName(getBaseDir(), name, container, restartCount);
String key = logFileName.getName();
PodLogWatcher logWatcher = podLogWatchers.get(key);
if (logWatcher == null) {
try {
String containerName = container.getName();
logWatcher = new PodLogWatcher(this, name, pod, containerName, logFileName);
podLogWatchers.put(key, logWatcher);
LOG.info("Watching pod " + name + " container " + containerName + " log at file: " + logFileName.getAbsolutePath());
} catch (Exception e) {
LOG.warn("Failed to create PodLogWatcher: " + e, e);
}
}
}
}
}
File yamlFile = new File(getBaseDir(), "target/test-pod-status/" + name + ".yml");
yamlFile.getParentFile().mkdirs();
try {
KubernetesHelper.saveYaml(pod, yamlFile);
} catch (IOException e) {
LOG.warn("Failed to write " + yamlFile + ". " + e, e);
}
}
public File getBaseDir() {
if (basedir == null) {
basedir = new File(System.getProperty("basedir", "."));
}
return basedir;
}
public void setBasedir(File basedir) {
this.basedir = basedir;
}
@Override
public void onClose(KubernetesClientException e) {
LOG.info("onClose: " + e);
}
public void close() {
closeAllClosers(podAsserts);
closeAllClosers(podLogWatchers);
}
protected void closeAllClosers(Map closers) {
while (!closers.isEmpty()) {
Set keys = closers.keySet();
for (String key : keys) {
closeCloser(key, closers);
}
}
}
private void closeCloser(String name, Map closers) {
Closeable closer = closers.remove(name);
if (closer != null) {
try {
closer.close();
} catch (Exception e) {
LOG.warn("Failed to close " + closer + ". " + e, e);
}
}
}
public void waitForPodReady() {
boolean ready = false;
try {
ready = podReady.await(readyTimeoutMS, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
LOG.warn("Interupted waiting for podReady: " + e);
ready = podReady.getCount() == 0L;
}
if (!ready) {
fail(podSelectionAssert.getDescription() + " did not have a pod become Ready within " + readyTimeoutMS + " millis");
}
try {
ready = podReadyForEntireDuration.await(readyPeriodMS * 2, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
LOG.warn("Interupted waiting for podReadyForEntireDuration: " + e);
ready = podReadyForEntireDuration.getCount() == 0L;
}
if (!ready) {
fail(podSelectionAssert.getDescription() + " did not have a pod Ready for a duration of " + readyPeriodMS + " millis");
}
}
public void podIsReadyForEntireDuration(String name, Pod pod) {
String message = "Pod " + name + " has been Ready now for " + getReadyPeriodMS() + " millis!";
LOG.info(ansi().fg(GREEN).a(message).reset().toString());
podReadyForEntireDuration.countDown();
}
public void podIsReady(String name, Pod pod) {
if (podReady.getCount() > 0) {
String message = "Pod " + name + " is Ready!";
LOG.info(ansi().fg(GREEN).a(message).reset().toString());
podReady.countDown();
}
}
}