/**
* Copyright 2005-2016 Red Hat, Inc.
*
* Red Hat 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.arquillian.utils;
import io.fabric8.arquillian.kubernetes.Configuration;
import io.fabric8.arquillian.kubernetes.Constants;
import io.fabric8.arquillian.kubernetes.Session;
import io.fabric8.arquillian.kubernetes.log.Logger;
import io.fabric8.kubernetes.api.Controller;
import io.fabric8.kubernetes.api.KubernetesHelper;
import io.fabric8.kubernetes.api.model.HasMetadata;
import io.fabric8.kubernetes.api.model.KubernetesList;
import io.fabric8.kubernetes.api.model.Pod;
import io.fabric8.kubernetes.api.model.Service;
import io.fabric8.kubernetes.api.model.extensions.Deployment;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClientException;
import io.fabric8.openshift.api.model.DeploymentConfig;
import io.fabric8.openshift.client.OpenShiftClient;
import io.fabric8.utils.GitHelpers;
import io.fabric8.utils.IOHelpers;
import io.fabric8.utils.MultiException;
import io.fabric8.utils.Objects;
import io.fabric8.utils.Strings;
import io.fabric8.utils.Systems;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import static io.fabric8.kubernetes.api.KubernetesHelper.getPortalIP;
import static io.fabric8.kubernetes.api.KubernetesHelper.getPorts;
import static io.fabric8.utils.Lists.notNullList;
public class Util {
public static String readAsString(URL url) {
try {
return IOHelpers.readFully(url);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static void displaySessionStatus(KubernetesClient client, Session session) throws MultiException {
if (client == null) {
session.getLogger().warn("No KubernetesClient for session: " + session.getId());
return;
}
if (client.isAdaptable(OpenShiftClient.class)){
OpenShiftClient oClient = client.adapt(OpenShiftClient.class);
List<DeploymentConfig> deploymentConfigs = oClient.deploymentConfigs().inNamespace(session.getNamespace()).list().getItems();
if (deploymentConfigs == null) {
throw new MultiException("No deployment configs found in namespace" + session.getNamespace());
}
for (DeploymentConfig deploymentConfig : deploymentConfigs) {
session.getLogger().info("Deployment config:" + KubernetesHelper.getName(deploymentConfig));
}
} else {
List<Deployment> deployments = client.extensions().deployments().inNamespace(session.getNamespace()).list().getItems();
if (deployments == null) {
throw new MultiException("No deployments found in namespace" + session.getNamespace());
}
for (Deployment deployment : deployments) {
session.getLogger().info("Deployment:" + KubernetesHelper.getName(deployment));
}
}
List<Pod> pods = client.pods().inNamespace(session.getNamespace()).list().getItems();
if (pods == null) {
throw new MultiException("No pods found in namespace" + session.getNamespace());
}
for (Pod pod : pods) {
session.getLogger().info("Pod:" + KubernetesHelper.getName(pod) + " Status:" + pod.getStatus());
}
List<Service> svcs = client.services().inNamespace(session.getNamespace()).list().getItems();
if (svcs == null) {
throw new MultiException("No services found in namespace" + session.getNamespace());
}
for (Service service : svcs) {
session.getLogger().info("Service:" + KubernetesHelper.getName(service) + " IP:" + getPortalIP(service) + " Port:" + getPorts(service));
}
}
public static void cleanupSession(KubernetesClient client, Controller controller, Configuration configuration, Session session, List<KubernetesList> kubeConfigs, String status) throws MultiException {
if (configuration.isNamespaceCleanupEnabled()) {
waitUntilWeCanDestroyNamespace(session);
List<Throwable> errors = new ArrayList<>();
if (configuration.isDeleteAllResourcesOnExit()) {
cleanupAllResources(client, session, errors);
} else {
cleanupAllMatching(client, session, errors, kubeConfigs);
}
if (configuration.isCreateNamespaceForTest()) {
try {
controller.deleteNamespace(session.getNamespace());
} catch (Exception e) {
errors.add(e);
}
}
List<Throwable> exceptions = new ArrayList<>();
for (Throwable exception : errors) {
if (exception instanceof KubernetesClientException) {
if (((KubernetesClientException) exception).getCode() == 403) {
// Log the exception message if that's a permission issue during clean-up
session.getLogger().warn(exception.getMessage());
continue;
}
}
exceptions.add(exception);
}
if (!exceptions.isEmpty()) {
throw new MultiException("Error while cleaning up session.", exceptions);
}
} else {
ConfigMaps.updateConfigMapStatus(client, session, status);
}
}
protected static void waitUntilWeCanDestroyNamespace(Session session) {
final Logger log = session.getLogger();
String confirmDestroy = Systems.getEnvVarOrSystemProperty(Constants.NAMESPACE_CLEANUP_CONFIRM_ENABLED, "false");
if (Objects.equal(confirmDestroy, "true")) {
showErrorsBeforePause(session);
System.out.println();
System.out.println("Waiting to destroy the namespace.");
System.out.println("Please type: [Q] to terminate the namespace.");
while (true) {
try {
int ch = System.in.read();
if (ch < 0 || ch == 'Q') {
System.out.println("\nStopping...");
break;
} else {
System.out.println("Found character: " + Character.toString((char) ch));
}
} catch (IOException e) {
log.warn("Failed to read from input. " + e);
break;
}
}
} else {
String timeoutText = Systems.getEnvVarOrSystemProperty(Constants.NAMESPACE_CLEANUP_TIMEOUT, "0");
Long timeout = null;
if (Strings.isNotBlank(timeoutText)) {
try {
timeout = Long.parseLong(timeoutText);
} catch (NumberFormatException e) {
log.warn("Failed to parse timeout value '" + timeoutText + "' for $Constants.NAMESPACE_CLEANUP_TIMEOUT. " + e);
}
}
if (timeout != null && timeout > 0L) {
showErrorsBeforePause(session);
System.out.println();
System.out.println("Sleeping for " + timeout + " seconds until destroying the namespace");
try {
Thread.sleep(timeout * 1000);
} catch (InterruptedException e) {
log.info("Interupted sleeping to GC the namespace: " + e);
}
}
}
System.out.println("Now destroying the Fabric8 Arquillian test case namespace");
}
protected static void showErrorsBeforePause(Session session) {
// TODO lets try dump the current errors so that the user can noodle into the system before its destroyed
}
public static void cleanupAllMatching(KubernetesClient client, Session session, List<Throwable> errors, List<KubernetesList> kubeConfigs) throws MultiException {
String sessionNamespace = session.getNamespace();
session.getLogger().info("Removing provisioned resources in namespace " + sessionNamespace);
/**
* Lets use a loop to ensure we really do delete all the matching resources
*/
for (int i = 0; i < 10; i++) {
for (KubernetesList list : kubeConfigs) {
List<HasMetadata> items = list.getItems();
if (items != null) {
for (HasMetadata item : items) {
cleanupItem(client, session, item, errors);
}
}
}
}
}
private static void cleanupItem(KubernetesClient client, Session session, HasMetadata item, List<Throwable> errors) {
String sessionNamespace = session.getNamespace();
KubernetesHelper.getOrCreateMetadata(item).setNamespace(sessionNamespace);
client.resource(item).inNamespace(sessionNamespace).cascading(true).delete();
}
public static void cleanupAllResources(KubernetesClient client, Session session, List<Throwable> errors) throws MultiException {
String sessionNamespace = session.getNamespace();
session.getLogger().info("Removing all resources in namespace " + sessionNamespace);
/**
* Lets use a loop to ensure we really do delete all the matching resources
*/
for (int i = 0; i < 10; i++) {
OpenShiftClient openShiftClient = new Controller(client).getOpenShiftClientOrNull();
if (openShiftClient != null) {
try {
openShiftClient.deploymentConfigs().inNamespace(sessionNamespace).delete();
} catch (KubernetesClientException e) {
errors.add(e);
}
try {
openShiftClient.routes().inNamespace(sessionNamespace).delete();
} catch (KubernetesClientException e) {
errors.add(e);
}
}
try {
client.extensions().deployments().inNamespace(sessionNamespace).delete();
} catch (KubernetesClientException e) {
errors.add(e);
}
try {
client.extensions().replicaSets().inNamespace(sessionNamespace).delete();
} catch (KubernetesClientException e) {
errors.add(e);
}
try {
client.replicationControllers().inNamespace(sessionNamespace).delete();
} catch (KubernetesClientException e) {
errors.add(e);
}
try {
client.pods().inNamespace(sessionNamespace).delete();
} catch (KubernetesClientException e) {
errors.add(e);
}
try {
client.extensions().ingresses().inNamespace(sessionNamespace).delete();
} catch (KubernetesClientException e) {
errors.add(e);
}
try {
client.services().inNamespace(sessionNamespace).delete();
} catch (KubernetesClientException e) {
errors.add(e);
}
try {
client.securityContextConstraints().withName(sessionNamespace).delete();
} catch (KubernetesClientException e) {
errors.add(e);
}
// lets see if there are any matching podList left
List<Pod> filteredPods = notNullList(client.pods().inNamespace(sessionNamespace).list().getItems());
if (filteredPods.isEmpty()) {
return;
} else {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static String findGitUrl(Session session, File dir) {
try {
return GitHelpers.extractGitUrl(dir);
} catch (IOException e) {
session.getLogger().warn("Could not detect git url from directory: " + dir + ". " + e);
return null;
}
}
public static File getProjectBaseDir(Session session) {
String basedir = System.getProperty("basedir", ".");
return new File(basedir);
}
public static String getSessionStatus(Session session) {
if (session.getFailed().get() > 0) {
return "FAILED";
} else {
return "PASSED";
}
}
}