All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.fabric8.kubernetes.api.KubernetesHelper Maven / Gradle / Ivy

There is a newer version: 3.0.12
Show newest version
/**
 *  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.kubernetes.api;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import io.fabric8.kubernetes.api.extensions.Templates;
import io.fabric8.kubernetes.api.model.*;
import io.fabric8.kubernetes.api.model.extensions.HTTPIngressPath;
import io.fabric8.kubernetes.api.model.extensions.HTTPIngressRuleValue;
import io.fabric8.kubernetes.api.model.extensions.Ingress;
import io.fabric8.kubernetes.api.model.extensions.IngressBackend;
import io.fabric8.kubernetes.api.model.extensions.IngressList;
import io.fabric8.kubernetes.api.model.extensions.IngressRule;
import io.fabric8.kubernetes.api.model.extensions.IngressSpec;
import io.fabric8.kubernetes.api.model.extensions.IngressTLS;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.ConfigBuilder;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClientException;
import io.fabric8.kubernetes.client.utils.Utils;
import io.fabric8.openshift.api.model.DeploymentConfig;
import io.fabric8.openshift.api.model.DeploymentConfigSpec;
import io.fabric8.openshift.api.model.OAuthClient;
import io.fabric8.openshift.api.model.Parameter;
import io.fabric8.openshift.api.model.Route;
import io.fabric8.openshift.api.model.RouteList;
import io.fabric8.openshift.api.model.RouteSpec;
import io.fabric8.openshift.api.model.Template;
import io.fabric8.openshift.client.DefaultOpenShiftClient;
import io.fabric8.openshift.client.OpenShiftClient;
import io.fabric8.utils.Files;
import io.fabric8.utils.Filter;
import io.fabric8.utils.Filters;
import io.fabric8.utils.IOHelpers;
import io.fabric8.utils.KubernetesServices;
import io.fabric8.utils.Objects;
import io.fabric8.utils.Strings;
import io.fabric8.utils.Systems;
import io.fabric8.utils.URLUtils;
import io.fabric8.utils.ssl.TrustEverythingSSLTrustManager;
import okhttp3.Credentials;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.logging.HttpLoggingInterceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xbill.DNS.ARecord;
import org.xbill.DNS.Lookup;
import org.xbill.DNS.Record;
import org.xbill.DNS.SRVRecord;
import org.xbill.DNS.TextParseException;
import org.xbill.DNS.Type;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLKeyException;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLProtocolException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.tools.FileObject;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Writer;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.URL;
import java.net.UnknownHostException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;

import static io.fabric8.kubernetes.client.utils.Utils.isNotNullOrEmpty;
import static io.fabric8.utils.Lists.notNullList;
import static io.fabric8.utils.Strings.isNullOrBlank;

/**
 * Kubernetes utility methods.
 */
public final class KubernetesHelper {

    private static final Logger LOGGER = LoggerFactory.getLogger(KubernetesHelper.class);

    public static final String KUBERNETES_NAMESPACE_SYSTEM_PROPERTY = "kubernetes.namespace";
    public static final String KUBERNETES_NAMESPACE_ENV = "KUBERNETES_NAMESPACE";
    public static final String DEFAULT_NAMESPACE = "default";

    private static final transient Logger LOG = LoggerFactory.getLogger(KubernetesHelper.class);
    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    public static final int INTORSTRING_KIND_INT = 0;
    public static final int INTORSTRING_KIND_STRING = 1;


    public static final String DEFAULT_DOCKER_HOST = "tcp://localhost:2375";

    public static final String defaultApiVersion = "v1";
    public static final String defaultOsApiVersion = "v1";

    private static final ConcurrentMap IS_OPENSHIFT = new ConcurrentHashMap<>();
    private static final Config CONFIG = new ConfigBuilder().build();

    private static final String DATE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ssX";

    public static String defaultNamespace() {
        return CONFIG.getNamespace();
    }

    /**
     * Returns the ID of the given object
     */
    public static String getObjectId(Object object) {
        if (object instanceof HasMetadata) {
            return getName((HasMetadata) object);
        } else {
            return object != null ? object.toString() : null;
        }
    }

    public static ObjectMeta getOrCreateMetadata(HasMetadata entity) {
        ObjectMeta metadata = entity.getMetadata();
        if (metadata == null) {
            metadata = new ObjectMeta();
            entity.setMetadata(metadata);
        }
        return metadata;
    }

    /**
     * Returns the resource version for the entity or null if it does not have one
     */
    public static String getResourceVersion(HasMetadata entity) {
        if (entity != null) {
            ObjectMeta metadata = entity.getMetadata();
            if (metadata != null) {
                String resourceVersion = metadata.getResourceVersion();
                if (Strings.isNotBlank(resourceVersion)) {
                    return resourceVersion;
                }
            }
        }
        return null;
    }

    /**
     * Returns true if this entity has a valid non blank resourceVersion in its metadata
     */
    public static boolean hasResourceVersion(HasMetadata entity) {
        return getResourceVersion(entity) != null;
    }

    public static String getName(ObjectMeta entity) {
        if (entity != null) {
            return Strings.firstNonBlank(entity.getName(),
                    getAdditionalPropertyText(entity.getAdditionalProperties(), "id"),
                    entity.getUid());
        } else {
            return null;
        }
    }


    /**
     * Returns the kind of the entity
     */
    public static String getKind(HasMetadata entity) {
        if (entity != null) {
            // TODO use reflection to find the kind?
            if (entity instanceof KubernetesList) {
                return "List";
            } else {
                return entity.getClass().getSimpleName();
            }
        } else {
            return null;
        }
    }

    public static String getQualifiedName(HasMetadata entity) {
        if (entity != null) {
            return "" + getNamespace(entity) + "/" + getName(entity);
        } else {
            return null;
        }
    }

    public static String getName(HasMetadata entity) {
        if (entity != null) {
            return getName(entity.getMetadata());
        } else {
            return null;
        }
    }

    public static void setName(HasMetadata entity, String name) {
        getOrCreateMetadata(entity).setName(name);
    }

    public static void setName(HasMetadata entity, String namespace, String name) {
        ObjectMeta metadata = getOrCreateMetadata(entity);
        metadata.setNamespace(namespace);
        metadata.setName(name);
    }

    public static void setNamespace(HasMetadata entity, String namespace) {
        getOrCreateMetadata(entity).setNamespace(namespace);
    }

    public static String getNamespace(ObjectMeta entity) {
        if (entity != null) {
            return entity.getNamespace();
        } else {
            return null;
        }
    }

    public static String getNamespace(HasMetadata entity) {
        if (entity != null) {
            return getNamespace(entity.getMetadata());
        } else {
            return null;
        }
    }

    public static Map getOrCreateAnnotations(HasMetadata entity) {
        ObjectMeta metadata = getOrCreateMetadata(entity);
        Map answer = metadata.getAnnotations();
        if (answer == null) {
            // use linked so the annotations can be in the FIFO order
            answer = new LinkedHashMap<>();
            metadata.setAnnotations(answer);
        }
        return answer;
    }

    public static Map getOrCreateLabels(HasMetadata entity) {
        ObjectMeta metadata = getOrCreateMetadata(entity);
        Map answer = metadata.getLabels();
        if (answer == null) {
            // use linked so the annotations can be in the FIFO order
            answer = new LinkedHashMap<>();
            metadata.setLabels(answer);
        }
        return answer;
    }

    /**
     * Returns the labels of the given metadata object or an empty map if the metadata or labels are null
     */
    @SuppressWarnings("unchecked")
    public static Map getLabels(ObjectMeta metadata) {
        if (metadata != null) {
            Map labels = metadata.getLabels();
            if (labels != null) {
                return labels;
            }
        }
        return Collections.EMPTY_MAP;
    }

    @SuppressWarnings("unchecked")
    public static Map getLabels(HasMetadata entity) {
        if (entity != null) {
            return getLabels(entity.getMetadata());
        }
        return Collections.EMPTY_MAP;
    }

    public static ServiceSpec getOrCreateSpec(Service entity) {
        ServiceSpec spec = entity.getSpec();
        if (spec == null) {
            spec = new ServiceSpec();
            entity.setSpec(spec);
        }
        return spec;
    }

    public static String getPortalIP(Service entity) {
        String answer = null;
        if (entity != null) {
            ServiceSpec spec = getOrCreateSpec(entity);
            return spec.getClusterIP();
        }
        return answer;
    }

    @SuppressWarnings("unchecked")
    public static Map getSelector(Service entity) {
        Map answer = null;
        if (entity != null) {
            ServiceSpec spec = getOrCreateSpec(entity);
            answer = spec.getSelector();
        }
        return answer != null ? answer : Collections.EMPTY_MAP;
    }

    public static void setSelector(Service entity, Map labels) {
        ServiceSpec spec = getOrCreateSpec(entity);
        spec.setSelector(labels);
    }

    public static Set getPorts(Service entity) {
        Set answer = new HashSet<>();
        if (entity != null) {
            ServiceSpec spec = getOrCreateSpec(entity);
            for (ServicePort port : spec.getPorts()) {
                answer.add(port.getPort());
            }
        }
        return answer;
    }

    protected static String getAdditionalPropertyText(Map additionalProperties, String name) {
        if (additionalProperties != null) {
            Object value = additionalProperties.get(name);
            if (value != null) {
                return value.toString();
            }
        }
        return null;
    }

    protected static Map getMetadata(Map additionalProperties, boolean create) {
        Map answer = getAdditionalPropertyMap(additionalProperties, "metadata");
        if (answer == null) {
            answer = new LinkedHashMap<>();
            if (create) {
                additionalProperties.put("metadata", answer);
            }
        }
        return answer;
    }

    @SuppressWarnings("unchecked")
    protected static Map getAdditionalPropertyMap(Map additionalProperties, String name) {
        if (additionalProperties != null) {
            Object value = additionalProperties.get(name);
            if (value instanceof Map) {
                return (Map) value;
            }
        }
        return null;
    }

    public static String getDockerIp() {
        String url = resolveDockerHost();
        int idx = url.indexOf("://");
        if (idx > 0) {
            url = url.substring(idx + 3);
        }
        idx = url.indexOf(":");
        if (idx > 0) {
            url = url.substring(0, idx);
        }
        return url;
    }

    public static String resolveDockerHost() {
        String dockerHost = System.getenv("DOCKER_HOST");
        if (isNullOrBlank(dockerHost)) {
            dockerHost = System.getProperty("docker.host");
        }
        if (isNullOrBlank(dockerHost)) {
            return DEFAULT_DOCKER_HOST;
        } else {
            return dockerHost;
        }
    }

    public static String toJson(Object dto) throws JsonProcessingException {
        if (dto == null) {
            return "null";
        }
        Class clazz = dto.getClass();
        return OBJECT_MAPPER.writerFor(clazz).writeValueAsString(dto);
    }

    public static String toPrettyJson(Object dto) throws JsonProcessingException {
        if (dto == null) {
            return "null";
        }
        Class clazz = dto.getClass();
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
        return objectMapper.writerFor(clazz).writeValueAsString(dto);
    }

    /**
     * Returns the given json data as a DTO such as
     * {@link Pod}, {@link ReplicationController} or
     * {@link io.fabric8.kubernetes.api.model.Service}
     * from the Kubernetes REST API
     */
    public static Object loadJson(File file) throws IOException {
        byte[] data = Files.readBytes(file);
        return loadJson(data);
    }

    /**
     * Returns the given json data as a DTO such as
     * {@link Pod}, {@link ReplicationController} or
     * {@link io.fabric8.kubernetes.api.model.Service}
     * from the Kubernetes REST API
     */
    public static Object loadJson(InputStream in) throws IOException {
        byte[] data = Files.readBytes(in);
        return loadJson(data);
    }

    public static Object loadJson(String json) throws IOException {
        byte[] data = json.getBytes();
        return loadJson(data);
    }

    /**
     * Returns the given json data as a DTO such as
     * {@link Pod}, {@link ReplicationController} or
     * {@link io.fabric8.kubernetes.api.model.Service}
     * from the Kubernetes REST API
     */
    public static Object loadJson(byte[] json) throws IOException {
        if (json != null && json.length > 0) {
            return OBJECT_MAPPER.readerFor(KubernetesResource.class).readValue(json);
        }
        return null;
    }

    /**
     * Loads the YAML file for the given DTO class
     */
    public static  T loadYaml(InputStream in, Class clazz) throws IOException {
        byte[] data = Files.readBytes(in);
        return loadYaml(data, clazz);
    }

    /**
     * Loads the YAML file for the given DTO class
     */
    public static  T loadYaml(File file, Class clazz) throws IOException {
        byte[] data = Files.readBytes(file);
        return loadYaml(data, clazz);
    }

    /**
     * Loads the YAML file for the kubernetes resource
     */
    public static  T loadYaml(File file) throws IOException {
        return (T) loadYaml(file, KubernetesResource.class);
    }

    /**
     * Loads the YAML text for the given DTO class
     */
    public static  T loadYaml(String text, Class clazz) throws IOException {
        byte[] data = text.getBytes();
        return loadYaml(data, clazz);
    }

    /**
     * Loads the YAML text for the kubernetes resource
     */
    public static  T loadYaml(String text) throws IOException {
        return (T) loadYaml(text, KubernetesResource.class);
    }

    /**
     * Loads the YAML file for the given DTO class
     */
    public static  T loadYaml(byte[] data, Class clazz) throws IOException {
        ObjectMapper mapper = createYamlObjectMapper();
        return mapper.readValue(data, clazz);
    }

    public static void saveYaml(Object data, File file) throws IOException {
        ObjectMapper mapper = createYamlObjectMapper();
        mapper.writeValue(file, data);
    }

    public static void saveYaml(Object data, FileObject fileObject) throws IOException{
        ObjectMapper mapper = createYamlObjectMapper();
        try (Writer writer = fileObject.openWriter()) {
            mapper.writeValue(writer, data);
        }
    }

    public static String toYaml(Object data) throws IOException {
        ObjectMapper mapper = createYamlObjectMapper();
        return mapper.writeValueAsString(data);
    }

    public static ObjectMapper createYamlObjectMapper() {
        ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory());
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
        return objectMapper;
    }

    /**
     * Loads the Kubernetes JSON and converts it to a list of entities
     */
    @SuppressWarnings("unchecked")
    public static List toItemList(Object entity) throws IOException {
        if (entity instanceof List) {
            return (List) entity;
        } else if (entity instanceof HasMetadata[]) {
            HasMetadata[] array = (HasMetadata[]) entity;
            return Arrays.asList(array);
        } else if (entity instanceof KubernetesList) {
            KubernetesList config = (KubernetesList) entity;
            return config.getItems();
        } else if (entity instanceof Template) {
            Template objects = (Template) entity;
            return objects.getObjects();
        } else {
            List answer = new ArrayList<>();
            if (entity instanceof HasMetadata) {
                answer.add((HasMetadata) entity);
            }
            return answer;
        }
    }

    /**
     * Saves the json object to the given file
     */
    public static void saveJson(File json, Object object) throws IOException {
        OBJECT_MAPPER.writer().withDefaultPrettyPrinter().writeValue(json, object);
    }

    /**
     * Returns a map indexed by pod id of the pods
     */
    public static Map toPodMap(PodList podSchema) {
        return toFilteredPodMap(podSchema, Filters.trueFilter());
    }

    protected static Map toFilteredPodMap(PodList podSchema, Filter filter) {
        List list = podSchema != null ? podSchema.getItems() : null;
        List filteredList = Filters.filter(list, filter);
        return toPodMap(filteredList);
    }

    /**
     * Returns a map indexed by pod id of the pods
     */
    public static Map toPodMap(List pods) {
        List list = notNullList(pods);
        Map answer = new HashMap<>();
        for (Pod pod : list) {
            String id = getName(pod);
            if (Strings.isNotBlank(id)) {
                answer.put(id, pod);
            }
        }
        return answer;
    }

    /**
     * Returns a map indexed by service id of the services
     */
    public static Map toServiceMap(ServiceList serviceSchema) {
        return toServiceMap(serviceSchema != null ? serviceSchema.getItems() : null);
    }

    /**
     * Returns a map indexed by service id of the services
     */
    public static Map toServiceMap(List services) {
        List list = notNullList(services);
        Map answer = new HashMap<>();
        for (Service service : list) {
            String id = getName(service);
            if (Strings.isNotBlank(id)) {
                answer.put(id, service);
            }
        }
        return answer;
    }

    public static Map toFilteredServiceMap(ServiceList serviceList, Filter filter) {
        List list = serviceList != null ? serviceList.getItems() : null;
        List filteredList = Filters.filter(list, filter);
        return toServiceMap(filteredList);
    }

    /**
     * Returns a map indexed by replicationController id of the replicationControllers
     */
    public static Map toReplicationControllerMap(ReplicationControllerList replicationControllerSchema) {
        Filter filter = createReplicationControllerFilter((String) null);
        return toFilteredReplicationControllerMap(replicationControllerSchema, filter);
    }

    protected static Map toFilteredReplicationControllerMap(ReplicationControllerList replicationControllerSchema, Filter filter) {
        List list = replicationControllerSchema != null ? replicationControllerSchema.getItems() : null;
        List filteredList = Filters.filter(list, filter);
        return toReplicationControllerMap(filteredList);
    }

    /**
     * Returns a map indexed by replicationController id of the replicationControllers
     */
    public static Map toReplicationControllerMap(List replicationControllers) {
        List list = notNullList(replicationControllers);
        Map answer = new HashMap<>();
        for (ReplicationController replicationControllerSchema : list) {
            String id = getName(replicationControllerSchema);
            if (Strings.isNotBlank(id)) {
                answer.put(id, replicationControllerSchema);
            }
        }
        return answer;
    }

    public static Map getPodMap(KubernetesClient kubernetes) {
        return getPodMap(kubernetes, null);
    }

    public static Map getPodMap(KubernetesClient kubernetes, String namespace) {
        PodList pods = null;
        try {
            pods = kubernetes.pods().inNamespace(namespace).list();
        } catch (KubernetesClientException e) {
            if (e.getCode() == 404) {
                // ignore not found
            } else {
                throw e;
            }
        }
        return toPodMap(pods);
    }

    public static Map getSelectedPodMap(KubernetesClient kubernetes, String selector) {
        return getSelectedPodMap(kubernetes, null, selector);
    }

    public static Map getSelectedPodMap(KubernetesClient kubernetes, String namespace, String selector) {
        return getFilteredPodMap(kubernetes, namespace, createPodFilter(selector));
    }

    public static Map getFilteredPodMap(KubernetesClient kubernetes, Filter filter) {
        return getFilteredPodMap(kubernetes, null, filter);
    }

    public static Map getFilteredPodMap(KubernetesClient kubernetes, String namespace, Filter filter) {
        return toFilteredPodMap(kubernetes.pods().inNamespace(namespace).list(), filter);
    }

    public static Map getServiceMap(KubernetesClient kubernetes) {
        return getServiceMap(kubernetes, null);
    }

    public static Map getServiceMap(KubernetesClient kubernetes, String namespace) {
        return toServiceMap(kubernetes.services().inNamespace(namespace).list());
    }

    public static Map getReplicationControllerMap(KubernetesClient kubernetes) {
        return getReplicationControllerMap(kubernetes, null);
    }

    public static Map getReplicationControllerMap(KubernetesClient kubernetes, String namespace) {
        return toReplicationControllerMap(kubernetes.replicationControllers().inNamespace(namespace).list());
    }

    public static Map getSelectedReplicationControllerMap(KubernetesClient kubernetes, String selector) {
        return getSelectedReplicationControllerMap(kubernetes, null, selector);
    }

    public static Map getSelectedReplicationControllerMap(KubernetesClient kubernetes, String namespace, String selector) {
        return toReplicationControllerMap(kubernetes.replicationControllers().inNamespace(namespace).withLabels(toLabelsMap(selector)).list());
    }

    /**
     * Removes empty pods returned by Kubernetes
     */
    public static void removeEmptyPods(PodList podSchema) {
        List list = notNullList(podSchema.getItems());

        List removeItems = new ArrayList();

        for (Pod pod : list) {
            if (Strings.isNullOrBlank(getName(pod))) {
                removeItems.add(pod);
            }
        }
        list.removeAll(removeItems);
    }

    /**
     * Returns the pod id for the given container id
     */
    public static String containerNameToPodId(String containerName) {
        // TODO use prefix?
        return containerName;
    }

    /**
     * Returns a string for the labels using "," to separate values
     */
    public static String toLabelsString(Map labelMap) {
        StringBuilder buffer = new StringBuilder();
        if (labelMap != null) {
            Set> entries = labelMap.entrySet();
            for (Map.Entry entry : entries) {
                if (buffer.length() > 0) {
                    buffer.append(",");
                }
                buffer.append(entry.getKey());
                buffer.append("=");
                buffer.append(entry.getValue());
            }
        }
        return buffer.toString();
    }

    public static Map toLabelsMap(String labels) {
        Map map = new HashMap<>();
        if (labels != null && !labels.isEmpty()) {
            String[] elements = labels.split(",");
            if (elements.length > 0) {
                for (String str : elements) {
                    String[] keyValue = str.split("=");
                    if (keyValue.length == 2) {
                        String key = keyValue[0];
                        String value = keyValue[1];
                        if (key != null && value != null) {
                            map.put(key.trim(), value.trim());
                        }
                    }
                }
            }
        }
        return map;
    }

    /**
     * Creates a filter on a pod using the given text string
     */
    public static Filter createPodFilter(final String textFilter) {
        if (isNullOrBlank(textFilter)) {
            return Filters.trueFilter();
        } else {
            return new Filter() {
                public String toString() {
                    return "PodFilter(" + textFilter + ")";
                }

                public boolean matches(Pod entity) {
                    return filterMatchesIdOrLabels(textFilter, getName(entity), entity.getMetadata().getLabels());
                }
            };
        }
    }

    /**
     * Creates a filter on a pod using the given set of labels
     */
    public static Filter createPodFilter(final Map labelSelector) {
        if (labelSelector == null || labelSelector.isEmpty()) {
            return Filters.trueFilter();
        } else {
            return new Filter() {
                public String toString() {
                    return "PodFilter(" + labelSelector + ")";
                }

                public boolean matches(Pod entity) {
                    return filterLabels(labelSelector, entity.getMetadata().getLabels());
                }
            };
        }
    }

    /**
     * Creates a filter on a pod annotations using the given set of attribute values
     */
    public static Filter createPodAnnotationFilter(final Map annotationSelector) {
        if (annotationSelector == null || annotationSelector.isEmpty()) {
            return Filters.trueFilter();
        } else {
            return new Filter() {
                public String toString() {
                    return "PodAnnotationFilter(" + annotationSelector + ")";
                }

                public boolean matches(Pod entity) {
                    return filterLabels(annotationSelector, entity.getMetadata().getAnnotations());
                }
            };
        }
    }

    /**
     * Creates a filter on a service using the given text string
     */
    public static Filter createServiceFilter(final String textFilter) {
        if (isNullOrBlank(textFilter)) {
            return Filters.trueFilter();
        } else {
            return new Filter() {
                public String toString() {
                    return "ServiceFilter(" + textFilter + ")";
                }

                public boolean matches(Service entity) {
                    return filterMatchesIdOrLabels(textFilter, getName(entity), entity.getMetadata().getLabels());
                }
            };
        }
    }

    /**
     * Creates a filter on a service if it matches the given namespace
     */
    public static Filter createNamespaceServiceFilter(final String namespace) {
        if (isNullOrBlank(namespace)) {
            return Filters.trueFilter();
        } else {
            return new Filter() {
                public String toString() {
                    return "NamespaceServiceFilter(" + namespace + ")";
                }

                public boolean matches(Service entity) {
                    return Objects.equal(namespace, getNamespace(entity.getMetadata()));
                }
            };
        }
    }

    /**
     * Creates a filter on a service using the given text string
     */
    public static Filter createServiceFilter(final Map labelSelector) {
        if (labelSelector == null || labelSelector.isEmpty()) {
            return Filters.trueFilter();
        } else {
            return new Filter() {
                public String toString() {
                    return "ServiceFilter(" + labelSelector + ")";
                }

                public boolean matches(Service entity) {
                    return filterLabels(labelSelector, entity.getMetadata().getLabels());
                }
            };
        }
    }

    /**
     * Creates a filter on a replicationController using the given text string
     */
    public static Filter createReplicationControllerFilter(final String textFilter) {
        if (isNullOrBlank(textFilter)) {
            return Filters.trueFilter();
        } else {
            return new Filter() {
                public String toString() {
                    return "ReplicationControllerFilter(" + textFilter + ")";
                }

                public boolean matches(ReplicationController entity) {
                    return filterMatchesIdOrLabels(textFilter, getName(entity), entity.getMetadata().getLabels());
                }
            };
        }
    }

    /**
     * Creates a filter on a replicationController using the given text string
     */
    public static Filter createReplicationControllerFilter(final Map labelSelector) {
        if (labelSelector == null || labelSelector.isEmpty()) {
            return Filters.trueFilter();
        } else {
            return new Filter() {
                public String toString() {
                    return "ReplicationControllerFilter(" + labelSelector + ")";
                }

                public boolean matches(ReplicationController entity) {
                    return filterLabels(labelSelector, entity.getMetadata().getLabels());
                }
            };
        }
    }

    /**
     * Returns true if the given textFilter matches either the id or the labels
     */
    public static boolean filterMatchesIdOrLabels(String textFilter, String id, Map labels) {
        String text = toLabelsString(labels);
        boolean result = (text != null && text.contains(textFilter)) || (id != null && id.contains(textFilter));
        if (!result) {
            //labels can be in different order to selector
            Map selectorMap = toLabelsMap(textFilter);

            if (!selectorMap.isEmpty() && labels != null && !labels.isEmpty()) {
                result = true;
                for (Map.Entry entry : selectorMap.entrySet()) {
                    String value = labels.get(entry.getKey());
                    if (value == null || !value.matches(entry.getValue())) {
                        result = false;
                        break;
                    }
                }
            }
        }
        return result;
    }

    /**
     * Returns true if the given filterLabels matches the actual labels
     */
    public static boolean filterLabels(Map filterLabels, Map labels) {
        if (labels == null) {
            return false;
        }
        Set> entries = filterLabels.entrySet();
        for (Map.Entry entry : entries) {
            String key = entry.getKey();
            String expectedValue = entry.getValue();
            String actualValue = labels.get(key);
            if (!Objects.equal(expectedValue, actualValue)) {
                return false;
            }
        }
        return true;
    }


    /**
     * For positive non-zero values return the text of the number or return blank
     */
    public static String toPositiveNonZeroText(Integer port) {
        if (port != null) {
            int value = port;
            if (value > 0) {
                return "" + value;
            }
        }
        return "";
    }

    /**
     * Returns all the containers from the given pod
     */
    @SuppressWarnings("unchecked")
    public static List getContainers(Pod pod) {
        if (pod != null) {
            PodSpec podSpec = pod.getSpec();
            return getContainers(podSpec);

        }
        return Collections.EMPTY_LIST;
    }

    /**
     * Returns all the containers from the given Replication Controller
     */
    @SuppressWarnings("unchecked")
    public static List getContainers(ReplicationController replicationController) {
        if (replicationController != null) {
            ReplicationControllerSpec replicationControllerSpec = replicationController.getSpec();
            return getContainers(replicationControllerSpec);
        }
        return Collections.EMPTY_LIST;
    }

    /**
     * Returns all the containers from the given Replication Controller's replicationControllerSpec
     */
    @SuppressWarnings("unchecked")
    public static List getContainers(ReplicationControllerSpec replicationControllerSpec) {
        if (replicationControllerSpec != null) {
            PodTemplateSpec podTemplateSpec = replicationControllerSpec.getTemplate();
            return getContainers(podTemplateSpec);
        }
        return Collections.EMPTY_LIST;
    }

    @SuppressWarnings("unchecked")
    public static List getContainers(PodSpec podSpec) {
        if (podSpec != null) {
            return podSpec.getContainers();
        }
        return Collections.EMPTY_LIST;
    }

    @SuppressWarnings("unchecked")
    public static List getContainers(PodTemplateSpec podTemplateSpec) {
        if (podTemplateSpec != null) {
            return getContainers(podTemplateSpec.getSpec());
        }
        return Collections.EMPTY_LIST;
    }

    /**
     * Returns all the containers from the given Replication Controller
     */
    @SuppressWarnings("unchecked")
    public static List getCurrentContainers(ReplicationController replicationController) {
        if (replicationController != null) {
            // TODO
        }
        return Collections.EMPTY_LIST;
    }

    /**
     * Returns all the current containers from the given currentState
     */
    @SuppressWarnings("unchecked")
    public static Map getCurrentContainers(Pod pod) {
        if (pod != null) {
            PodStatus currentStatus = pod.getStatus();
            return getCurrentContainers(currentStatus);

        }
        return Collections.EMPTY_MAP;
    }

    /**
     * Returns all the current containers from the given podStatus
     */
    @SuppressWarnings("unchecked")
    public static Map getCurrentContainers(PodStatus podStatus) {
        if (podStatus != null) {
            List containerStatuses = podStatus.getContainerStatuses();
            Map info = new Hashtable<>(containerStatuses.size());
            for (ContainerStatus status : containerStatuses) {
                info.put(status.getContainerID(), status);
            }
            return info;
        }
        return Collections.EMPTY_MAP;
    }

    /**
     * Returns the host of the pod
     */
    public static String getHost(Pod pod) {
        if (pod != null) {
            PodStatus currentState = pod.getStatus();
            if (currentState != null) {
                return currentState.getHostIP();
            }
        }
        return null;
    }

    /**
     * Returns the container port number for the given service
     */
    @SuppressWarnings("unchecked")
    public static Set getContainerPorts(Service service) {
        Set answer = Collections.EMPTY_SET;
        String id = getName(service);
        ServiceSpec spec = service.getSpec();
        if (spec != null) {
            List servicePorts = spec.getPorts();
            Objects.notNull(servicePorts, "servicePorts for service " + id);

            answer = new HashSet<>(servicePorts.size());
            String message = "service " + id;

            for (ServicePort port : servicePorts) {
                IntOrString intOrStringValue = port.getTargetPort();
                Integer intValue = intOrStringToInteger(intOrStringValue, message);
                if (intValue != null) {
                    answer.add(intValue);
                }
            }
        }
        return answer;
    }

    /**
     * Returns the IntOrString converted to an Integer value or throws an exception with the given message
     */
    public static Integer intOrStringToInteger(IntOrString intOrStringValue, String message) {
        Integer intValue = intOrStringValue.getIntVal();
        if (intValue == null) {
            String containerPortText = intOrStringValue.getStrVal();
            if (Strings.isNullOrBlank(containerPortText)) {
                throw new IllegalArgumentException("No port for " +
                        message);
            }
            try {
                intValue = Integer.parseInt(containerPortText);
            } catch (NumberFormatException e) {
                throw new IllegalStateException("Invalid servicePorts expression " + containerPortText + " for " +
                        message + ". " + e, e);
            }
        }
        return intValue;
    }

    /**
     * Returns the container port number for the given service
     */
    @SuppressWarnings("unchecked")
    public static Set getContainerPortsStrings(Service service) {
        Set answer = Collections.EMPTY_SET;
        String id = getName(service);
        ServiceSpec spec = service.getSpec();
        if (spec != null) {
            List servicePorts = spec.getPorts();
            Objects.notNull(servicePorts, "servicePorts for service " + id);

            answer = new HashSet<>(servicePorts.size());

            for (ServicePort port : servicePorts) {
                IntOrString intOrStringValue = port.getTargetPort();
                Integer intValue = intOrStringValue.getIntVal();
                if (intValue != null) {
                    answer.add(intValue.toString());
                } else {
                    String containerPortText = intOrStringValue.getStrVal();
                    if (Strings.isNullOrBlank(containerPortText)) {
                        throw new IllegalArgumentException("No servicePorts for service " + id);
                    }
                    answer.add(containerPortText);
                }
            }
        }
        return answer;
    }

    /**
     * Combines the JSON objects into a config object
     */
    public static Object combineJson(Object... objects) throws IOException {
        KubernetesList list = findOrCreateList(objects);
        List items = list.getItems();
        if (items == null) {
            items = new ArrayList<>();
            list.setItems(items);
        }
        for (Object object : objects) {
            if (object != list) {
                addObjectsToItemArray(items, object);
            }
        }
        KubernetesList fullList = new KubernetesList();
        fullList.setItems(items);
        moveServicesToFrontOfArray(items);
        removeDuplicates(items);
        Object answer = Templates.combineTemplates(fullList);
        items = toItemList(answer);
        removeDuplicates(items);
        return answer;
    }

    /**
     * Lets move all Service resources before any other to avoid ordering issues creating things
     */
    public static void moveServicesToFrontOfArray(List list) {
        int size = list.size();
        int lastNonService = -1;
        for (int i = 0; i < size; i++) {
            HasMetadata item = list.get(i);
            if (item instanceof Service) {
                if (lastNonService >= 0) {
                    HasMetadata nonService = list.get(lastNonService);
                    list.set(i, nonService);
                    list.set(lastNonService, item);
                    lastNonService++;
                }
            } else if (lastNonService < 0) {
                lastNonService = i;
            }
        }
    }

    /**
     * Remove any duplicate resources using the kind and id
     */
    protected static void removeDuplicates(List itemArray) {
        int size = itemArray.size();
        int lastNonService = -1;
        Set keys = new HashSet<>();
        for (int i = 0; i < size; i++) {
            HasMetadata item = itemArray.get(i);
            if (item == null) {
                itemArray.remove(i);
                i--;
                size--;
            } else {
                String id = getObjectId(item);
                String kind = item.getClass().getSimpleName();
                if (Strings.isNotBlank(id)) {
                    String key = kind + ":" + id;
                    if (!keys.add(key)) {
                        // lets remove this one
                        itemArray.remove(i);
                        i--;
                        size--;
                    }
                }

            }
        }
    }

    @SuppressWarnings("unchecked")
    protected static void addObjectsToItemArray(List destinationList, Object object) throws IOException {
        if (object instanceof KubernetesList) {
            KubernetesList kubernetesList = (KubernetesList) object;
            List items = kubernetesList.getItems();
            if (items != null) {
                destinationList.addAll(items);
            }
        } else if (object instanceof Collection) {
            Collection collection = (Collection) object;
            destinationList.addAll(collection);
        } else {
            destinationList.add(object);
        }
    }

    protected static KubernetesList findOrCreateList(Object[] objects) {
        KubernetesList list = null;
        for (Object object : objects) {
            if (object instanceof KubernetesList) {
                list = (KubernetesList) object;
                break;
            }
        }
        if (list == null) {
            list = new KubernetesList();
        }
        return list;
    }

    /**
     * Returns the URL to access the service; using the service clusterIP and port
     */
    public static String getServiceURL(Service service) {
        if (service != null) {
            String answer = getOrCreateAnnotations(service).get(Annotations.Service.EXPOSE_URL);
            if (Strings.isNotBlank(answer)) {
                return answer;
            }
            ServiceSpec spec = service.getSpec();
            if (spec != null) {
                String portalIP = spec.getClusterIP();
                if (portalIP != null) {
                    Integer port = spec.getPorts().iterator().next().getPort();
                    if (port != null && port > 0) {
                        portalIP += ":" + port;
                    }
                    String protocol = "http://";
                    if (KubernetesHelper.isServiceSsl(spec.getClusterIP(), port, Utils.getSystemPropertyOrEnvVar(io.fabric8.kubernetes.client.Config.KUBERNETES_TRUST_CERT_SYSTEM_PROPERTY, false))) {
                        protocol = "https://";
                    }
                    return protocol + portalIP;
                }
            }
        }
        return null;
    }

    /**
     * Returns the URL to access the service; using the environment variables, routes
     * or service clusterIP address
     *
     * @throws IllegalArgumentException if the URL cannot be found for the serviceName and namespace
     */
    public static String getServiceURL(KubernetesClient client, String serviceName, String serviceNamespace, String serviceProtocol, boolean serviceExternal) {
        return getServiceURL(client, serviceName, serviceNamespace, serviceProtocol, null, serviceExternal);
    }

    /**
     * Returns the URL to access the service; using the environment variables, routes
     * or service clusterIP address
     *
     * @throws IllegalArgumentException if the URL cannot be found for the serviceName and namespace
     */
    public static String getServiceURL(KubernetesClient client, String serviceName, String serviceNamespace, String serviceProtocol, String servicePortName, boolean serviceExternal) {
        Service srv = null;
        String serviceHost = KubernetesServices.serviceToHostOrBlank(serviceName);
        String servicePort = KubernetesServices.serviceToPortOrBlank(serviceName, servicePortName);
        String serviceProto = serviceProtocol != null ? serviceProtocol : KubernetesServices.serviceToProtocol(serviceName, servicePort);

        //Use specified or fallback namespace.
        String actualNamespace = Strings.isNotBlank(serviceNamespace) ? serviceNamespace : client.getNamespace();

        //1. Inside Kubernetes: Services as ENV vars
        if (!serviceExternal && Strings.isNotBlank(serviceHost) && Strings.isNotBlank(servicePort) && Strings.isNotBlank(serviceProtocol)) {
            return serviceProtocol + "://" + serviceHost + ":" + servicePort;
            //2. Anywhere: When namespace is passed System / Env var. Mostly needed for integration tests.
        } else if (Strings.isNotBlank(actualNamespace)) {
            try {
                srv = client.services().inNamespace(actualNamespace).withName(serviceName).get();
            } catch (Exception e) {
                LOGGER.warn("Could not lookup service:"+serviceName+" in namespace:"+actualNamespace+", due to: " + e.getMessage());
            }
        }

        if (srv == null) {
            // lets try use environment variables
            String hostAndPort = Systems.getServiceHostAndPort(serviceName, "", "");
            if (!hostAndPort.startsWith(":")) {
                return serviceProto + "://" + hostAndPort;
            }
        }
        if (srv == null) {
            throw new IllegalArgumentException("No kubernetes service could be found for name: " + serviceName + " in namespace: " + actualNamespace);
        }

        String answer = getOrCreateAnnotations(srv).get(Annotations.Service.EXPOSE_URL);
        if (Strings.isNotBlank(answer)) {
            return answer;
        }

        try {
            if (Strings.isNullOrBlank(servicePortName) && isOpenShift(client)) {
                OpenShiftClient openShiftClient = client.adapt(OpenShiftClient.class);
                Route route = openShiftClient.routes().inNamespace(actualNamespace).withName(serviceName).get();
                if (route != null) {
                    return (serviceProto + "://" + route.getSpec().getHost()).toLowerCase();
                }
            }
        } catch (KubernetesClientException e) {
            if (e.getCode() == 403) {
                LOGGER.warn("Could not lookup route:"+serviceName+" in namespace:"+actualNamespace+", due to: " + e.getMessage());
            } else {
                throw e;
            }
        }

        ServicePort port = findServicePortByName(srv, servicePortName);
        if (port == null) {
            throw new RuntimeException("Couldn't find port: " + servicePortName + " for service:" + serviceName);
        }

        String clusterIP = srv.getSpec().getClusterIP();
        if ("None".equals(clusterIP)) {
            throw new IllegalStateException("Service: " + serviceName + " in namespace:" + serviceNamespace + "is head-less. Search for endpoints instead.");
        }

        Integer portNumber = port.getPort();
        if (Strings.isNullOrBlank(clusterIP)) {
            IngressList ingresses = client.extensions().ingresses().inNamespace(serviceNamespace).list();
            if (ingresses != null) {
                List items = ingresses.getItems();
                if (items != null) {
                    for (Ingress item : items) {
                        String ns = getNamespace(item);
                        if (Objects.equal(serviceNamespace, ns)) {
                            IngressSpec spec = item.getSpec();
                            if (spec != null) {
                                List rules = spec.getRules();
                                List tls = spec.getTls();
                                if (rules != null) {
                                    for (IngressRule rule : rules) {
                                        HTTPIngressRuleValue http = rule.getHttp();
                                        if (http != null) {
                                            List paths = http.getPaths();
                                            if (paths != null) {
                                                for (HTTPIngressPath path : paths) {
                                                    IngressBackend backend = path.getBackend();
                                                    if (backend != null) {
                                                        String backendServiceName = backend.getServiceName();
                                                        if (serviceName.equals(backendServiceName) && portsMatch(port, backend.getServicePort())) {
                                                            String pathPostfix = path.getPath();
                                                            if (tls != null) {
                                                                for (IngressTLS tlsHost : tls) {
                                                                    List hosts = tlsHost.getHosts();
                                                                    if (hosts != null) {
                                                                        for (String host : hosts) {
                                                                            if (Strings.isNotBlank(host)) {
                                                                                if (Strings.isNullOrBlank(pathPostfix)) {
                                                                                    pathPostfix = "/";
                                                                                }
                                                                                return "https://" + URLUtils.pathJoin(host, pathPostfix);
                                                                            }
                                                                        }
                                                                    }
                                                                }
                                                            }
                                                            answer = rule.getHost();
                                                            if (Strings.isNotBlank(answer)) {
                                                                if (Strings.isNullOrBlank(pathPostfix)) {
                                                                    pathPostfix = "/";
                                                                }
                                                                return "http://" + URLUtils.pathJoin(answer, pathPostfix);
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }

            // lets try use the status on GKE
            ServiceStatus status = srv.getStatus();
            if (status != null) {
                LoadBalancerStatus loadBalancerStatus = status.getLoadBalancer();
                if (loadBalancerStatus != null) {
                    List loadBalancerIngresses = loadBalancerStatus.getIngress();
                    if (loadBalancerIngresses != null) {
                        for (LoadBalancerIngress loadBalancerIngress : loadBalancerIngresses) {
                            String ip = loadBalancerIngress.getIp();
                            if (Strings.isNotBlank(ip)) {
                                clusterIP = ip;
                                break;
                            }
                        }
                    }
                }
            }
        }

        if (Strings.isNullOrBlank(clusterIP)) {
            // on vanilla kubernetes we can use nodePort to access things externally
            boolean found = false;
            Integer nodePort = port.getNodePort();
            if (nodePort != null) {
                try {
                    NodeList nodeList = client.nodes().list();
                    if (nodeList != null) {
                        List items = nodeList.getItems();
                        if (items != null) {
                            for (Node item : items) {
                                NodeStatus status = item.getStatus();
                                if (!found && status != null) {
                                    List addresses = status.getAddresses();
                                    if (addresses != null) {
                                        for (NodeAddress address : addresses) {
                                            String ip = address.getAddress();
                                            if (Strings.isNotBlank(ip)) {
                                                clusterIP = ip;
                                                portNumber = nodePort;
                                                found = true;
                                                break;
                                            }

                                        }

                                    }
                                }
                                if (!found) {
                                    NodeSpec spec = item.getSpec();
                                    if (spec != null) {
                                        clusterIP = spec.getExternalID();
                                        if (Strings.isNotBlank(clusterIP)) {
                                            portNumber = nodePort;
                                            break;
                                        }
                                    }
                                }
                            }
                        }
                    }
                } catch (Exception e) {
                    // ignore could not find a node!
                    LOG.warn("Could not find a node!: " + e, e);
                }
            }
        }
        return (serviceProto + "://" + clusterIP + ":" + portNumber).toLowerCase();
    }

    /**
     * Returns true if the given servicePort matches the intOrString value
     */
    private static boolean portsMatch(ServicePort servicePort, IntOrString intOrString) {
        if (intOrString != null) {
            Integer port = servicePort.getPort();
            Integer intVal = intOrString.getIntVal();
            String strVal = intOrString.getStrVal();
            if (intVal != null) {
                if (port != null) {
                    return port.intValue() == intVal.intValue();
                } else {
                    /// should we find the port by name now?
                }
            } else if (strVal != null ){
                return Objects.equal(strVal, servicePort.getName());
            }
        }
        return false;
    }

    /**
     * Returns the URL to access the service; using the environment variables, routes
     * or service clusterIP address
     *
     * @throws IllegalArgumentException if the URL cannot be found for the serviceName and namespace
     */
    public static String getServiceURLInCurrentNamespace(KubernetesClient client, String serviceName, String serviceProtocol, String servicePortName, boolean serviceExternal) {
        Service srv = null;
        String serviceHost = KubernetesServices.serviceToHostOrBlank(serviceName);
        String servicePort = KubernetesServices.serviceToPortOrBlank(serviceName, servicePortName);
        String serviceProto = serviceProtocol != null ? serviceProtocol : KubernetesServices.serviceToProtocol(serviceName, servicePort);

        //1. Inside Kubernetes: Services as ENV vars
        if (!serviceExternal && Strings.isNotBlank(serviceHost) && Strings.isNotBlank(servicePort) && Strings.isNotBlank(serviceProtocol)) {
            return serviceProtocol + "://" + serviceHost + ":" + servicePort;
            //2. Anywhere: When namespace is passed System / Env var. Mostly needed for integration tests.
        } else  {
            srv = client.services().withName(serviceName).get();
        }
        if (srv == null) {
            throw new IllegalArgumentException("No kubernetes service could be found for name: " + serviceName);
        }

        if (Strings.isNullOrBlank(servicePortName) && isOpenShift(client)) {
            OpenShiftClient openShiftClient = client.adapt(OpenShiftClient.class);
            RouteList routeList = openShiftClient.routes().list();
            for (Route route : routeList.getItems()) {
                if (route.getSpec().getTo().getName().equals(serviceName)) {
                    return (serviceProto + "://" + route.getSpec().getHost()).toLowerCase();
                }
            }
        }
        ServicePort port = findServicePortByName(srv, servicePortName);
        if (port == null) {
            throw new RuntimeException("Couldn't find port: " + servicePortName + " for service:" + serviceName);
        }

        String clusterIP = srv.getSpec().getClusterIP();
        if ("None".equals(clusterIP)) {
            throw new IllegalStateException("Service: " + serviceName + " in current namespace is head-less. Search for endpoints instead.");
        }

        return (serviceProto + "://" + clusterIP + ":" + port.getPort()).toLowerCase();
    }

    /**
     * Returns the port for the given port number on the pod
     */
    public static ContainerPort findContainerPort(Pod pod, Integer portNumber) {
        List containers = KubernetesHelper.getContainers(pod);
        for (Container container : containers) {
            List ports = container.getPorts();
            for (ContainerPort port : ports) {
                if (Objects.equal(portNumber, port.getContainerPort())) {
                    return port;
                }
            }
        }
        return null;
    }

    public static ServicePort findServicePortByName(Service service, String portName) {
        if (Strings.isNullOrBlank(portName)) {
            return service.getSpec().getPorts().iterator().next();
        }

        for (ServicePort servicePort : service.getSpec().getPorts()) {
            if (Objects.equal(servicePort.getName(), portName)) {
                return servicePort;
            }
        }
        return null;
    }

    /**
     * Returns the port for the given port name
     */
    public static ContainerPort findContainerPortByName(Pod pod, String name) {
        List containers = KubernetesHelper.getContainers(pod);
        for (Container container : containers) {
            List ports = container.getPorts();
            for (ContainerPort port : ports) {
                if (Objects.equal(name, port.getName())) {
                    return port;
                }
            }
        }
        return null;
    }

    /**
     * Returns the port for the given port number or name
     */
    public static ContainerPort findContainerPortByNumberOrName(Pod pod, String numberOrName) {
        Integer portNumber = toOptionalNumber(numberOrName);
        if (portNumber != null) {
            return findContainerPort(pod, portNumber);
        } else {
            return findContainerPortByName(pod, numberOrName);
        }
    }

    /**
     * Returns the number if it can be parsed or null
     */
    protected static Integer toOptionalNumber(String text) {
        if (Strings.isNotBlank(text)) {
            try {
                return Integer.parseInt(text);
            } catch (NumberFormatException e) {
                // ignore parse errors
            }
        }
        return null;
    }

    public static PodStatusType getPodStatus(Pod pod) {
        String text = getPodStatusText(pod);
        if (Strings.isNotBlank(text)) {
            text = text.toLowerCase();
            if (text.startsWith("run")) {
                return PodStatusType.OK;
            } else if (text.startsWith("wait")) {
                return PodStatusType.WAIT;
            } else {
                return PodStatusType.ERROR;
            }
        }
        return PodStatusType.WAIT;
    }

    /**
     * Returns true if the pod is running
     */
    public static boolean isPodRunning(Pod pod) {
        PodStatusType status = getPodStatus(pod);
        return Objects.equal(status, PodStatusType.OK);
    }

    /**
     * Returns true if the pod is running and ready
     */
    public static boolean isPodReady(Pod pod) {
        if (!isPodRunning(pod)) {
            return false;
        }

        PodStatus podStatus = pod.getStatus();
        if (podStatus == null) {
            return true;
        }

        List conditions = podStatus.getConditions();
        if (conditions == null || conditions.isEmpty()) {
            return true;
        }

        // Check "ready" condition
        for (PodCondition condition : conditions) {
            if ("ready".equalsIgnoreCase(condition.getType())) {
                return Boolean.parseBoolean(condition.getStatus());
            }
        }

        return true;
    }

    public static String getPodStatusText(Pod pod) {
        if (pod != null) {
            PodStatus podStatus = pod.getStatus();
            if (podStatus != null) {
                return podStatus.getPhase();
            }
        }
        return null;
    }

    /**
     * Returns the environment variable value for the first container which has a value for it in th epod
     */
    public static String getPodEnvVar(Pod pod, String envVarName) {
        if (pod != null) {
            PodSpec spec = pod.getSpec();
            if (spec != null) {
                List containers = spec.getContainers();
                if (containers != null) {
                    for (Container container : containers) {
                        String answer = getContainerEnvVar(container, envVarName);
                        if (Strings.isNotBlank(answer)) {
                            return answer;
                        }
                    }
                }
            }
        }
        return null;
    }

    /**
     * Returns the environment variable value for the given container and name
     */
    public static String getContainerEnvVar(Container container, String envVarName) {
        if (container != null) {
            List env = container.getEnv();
            if (env != null) {
                for (EnvVar envVar : env) {
                    if (Objects.equal(envVarName, envVar.getName())) {
                        return envVar.getValue();
                    }
                }
            }
        }
        return null;
    }

    /**
     * Returns the pods for the given replication controller
     */
    @SuppressWarnings("unchecked")
    public static List getPodsForReplicationController(ReplicationController replicationController, Iterable pods) {
        ReplicationControllerSpec replicationControllerSpec = replicationController.getSpec();
        if (replicationControllerSpec == null) {
            LOG.warn("Cannot instantiate replication controller: " + getName(replicationController) + " due to missing ReplicationController.Spec!");
        } else {
            Map replicaSelector = replicationControllerSpec.getSelector();
            Filter podFilter = KubernetesHelper.createPodFilter(replicaSelector);
            return Filters.filter(pods, podFilter);
        }
        return Collections.EMPTY_LIST;
    }

    /**
     * Returns the pods for the given service
     */
    public static List getPodsForService(Service service, Iterable pods) {
        Map selector = getSelector(service);
        Filter podFilter = KubernetesHelper.createPodFilter(selector);
        return Filters.filter(pods, podFilter);
    }

    /**
     * Looks up the service endpoints in DNS.
     * 

* Endpoints are registered as SRV records in DNS so this method returns * endpoints in the format "host:port". This is a list as SRV records are ordered * by priority & weight before being returned to the client. *

* See https://github.com/GoogleCloudPlatform/kubernetes/blob/master/cluster/addons/dns/README.md */ public static List lookupServiceEndpointsInDns(String serviceName) throws IllegalArgumentException, UnknownHostException { try { Lookup l = new Lookup(serviceName, Type.SRV); Record[] records = l.run(); if (l.getResult() == Lookup.SUCCESSFUL) { SRVRecord[] srvRecords = Arrays.copyOf(records, records.length, SRVRecord[].class); Arrays.sort(srvRecords, new Comparator() { @Override public int compare(SRVRecord a, SRVRecord b) { int ret = Integer.compare(b.getPriority(), a.getPriority()); if (ret == 0) { ret = Integer.compare(b.getWeight(), a.getWeight()); } return ret; } }); List endpointAddresses = new ArrayList<>(srvRecords.length); for (SRVRecord srvRecord : srvRecords) { endpointAddresses.add(srvRecord.getTarget().toString(true).concat(":").concat(String.valueOf(srvRecord.getPort()))); } return endpointAddresses; } else { LOG.warn("Lookup {} result: {}", serviceName, l.getErrorString()); } } catch (TextParseException e) { LOG.error("Unparseable service name: {}", serviceName, e); } catch (ClassCastException e) { LOG.error("Invalid response from DNS server - should have been A records", e); } return Collections.EMPTY_LIST; } /** * Looks up the service in DNS. * If this is a headless service, this call returns the endpoint IPs from DNS. * If this is a non-headless service, this call returns the service IP only. *

* See https://github.com/GoogleCloudPlatform/kubernetes/blob/master/docs/services.md#headless-services */ public static Set lookupServiceInDns(String serviceName) throws IllegalArgumentException, UnknownHostException { try { Lookup l = new Lookup(serviceName); Record[] records = l.run(); if (l.getResult() == Lookup.SUCCESSFUL) { Set endpointAddresses = new HashSet<>(records.length); for (int i = 0; i < records.length; i++) { ARecord aRecord = (ARecord) records[i]; endpointAddresses.add(aRecord.getAddress().getHostAddress()); } return endpointAddresses; } else { LOG.warn("Lookup {} result: {}", serviceName, l.getErrorString()); } } catch (TextParseException e) { LOG.error("Unparseable service name: {}", serviceName, e); } catch (ClassCastException e) { LOG.error("Invalid response from DNS server - should have been A records", e); } return Collections.EMPTY_SET; } public static boolean isServiceSsl(String host, int port, boolean trustAllCerts) { try { LOG.info("Checking if a service is SSL on " + host + ":" + port); SSLSocketFactory sslsocketfactory; if (trustAllCerts) { sslsocketfactory = TrustEverythingSSLTrustManager.getTrustingSSLSocketFactory(); } else { sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault(); } Socket socket = sslsocketfactory.createSocket(); // Connect, with an explicit timeout value socket.connect(new InetSocketAddress(host, port), 1 * 1000); try { InputStream in = socket.getInputStream(); OutputStream out = socket.getOutputStream(); // Write a test byte to get a reaction :) out.write(1); while (in.available() > 0) { System.out.print(in.read()); } return true; } finally { LOG.info("Checked if a service is SSL on " + host + ":" + port); socket.close(); } } catch (SSLHandshakeException e) { LOG.error("SSL handshake failed - this probably means that you need to trust the kubernetes root SSL certificate or set the environment variable " + Utils.convertSystemPropertyNameToEnvVar(io.fabric8.kubernetes.client.Config.KUBERNETES_TRUST_CERT_SYSTEM_PROPERTY), e); } catch (SSLProtocolException e) { LOG.error("SSL protocol error", e); } catch (SSLKeyException e) { LOG.error("Bad SSL key", e); } catch (SSLPeerUnverifiedException e) { LOG.error("Could not verify server", e); } catch (SSLException e) { LOG.debug("Address does not appear to be SSL-enabled - falling back to http", e); } catch (IOException e) { LOG.debug("Failed to validate service", e); } return false; } /** * Validates that the given value is valid according to the kubernetes ID parsing rules, throwing an exception if not. */ public static String validateKubernetesId(String currentValue, String description) throws IllegalArgumentException { if (isNullOrBlank(currentValue)) { throw new IllegalArgumentException("No " + description + " is specified!"); } int size = currentValue.length(); for (int i = 0; i < size; i++) { char ch = currentValue.charAt(i); if (Character.isUpperCase(ch)) { throw new IllegalArgumentException("Invalid upper case letter '" + ch + "' at index " + i + " for " + description + " value: " + currentValue); } } return currentValue; } public static Date parseDate(String text) { try { return new SimpleDateFormat(DATE_TIME_FORMAT).parse(text); } catch (ParseException e) { LOG.warn("Failed to parse date: " + text + ". Reason: " + e); return null; } } /** * Returns a short summary text message for the given kubernetes resource */ public static String summaryText(Object object) { if (object instanceof Route) { return summaryText((Route) object); } else if (object instanceof Service) { return summaryText((Service) object); } else if (object instanceof ReplicationController) { return summaryText((ReplicationController) object); } else if (object instanceof Pod) { return summaryText((Pod) object); } else if (object instanceof Template) { return summaryText((Template) object); } else if (object instanceof DeploymentConfig) { return summaryText((DeploymentConfig) object); } else if (object instanceof OAuthClient) { return summaryText((OAuthClient) object); } else if (object instanceof String) { return object.toString(); } return ""; } /** * Returns a short summary text message for the given kubernetes resource */ public static String summaryText(Route entity) { RouteSpec spec = entity.getSpec(); if (spec == null) { return "No spec!"; } return "host: " + spec.getHost(); } /** * Returns a short summary text message for the given kubernetes resource */ public static String summaryText(ContainerState entity) { ContainerStateRunning running = entity.getRunning(); if (running != null) { return "Running"; } ContainerStateWaiting waiting = entity.getWaiting(); if (waiting != null) { return "Waiting"; } ContainerStateTerminated termination = entity.getTerminated(); if (termination != null) { return "Terminated"; } return "Unknown"; } /** * Returns a short summary text message for the given kubernetes resource */ public static String summaryText(Template entity) { StringBuilder buffer = new StringBuilder(); List parameters = entity.getParameters(); if (parameters != null) { for (Parameter parameter : parameters) { String name = parameter.getName(); appendText(buffer, name); } } return "parameters: " + buffer; } /** * Returns a short summary text message for the given kubernetes resource */ public static String summaryText(OAuthClient entity) { return "redirectURIs: " + entity.getRedirectURIs(); } /** * Returns a short summary text message for the given kubernetes resource */ public static String summaryText(Service entity) { StringBuilder portText = new StringBuilder(); ServiceSpec spec = entity.getSpec(); if (spec == null) { return "No spec"; } else { List ports = spec.getPorts(); if (ports != null) { for (ServicePort port : ports) { Integer number = port.getPort(); if (number != null) { if (portText.length() > 0) { portText.append(", "); } portText.append("").append(number); } } } return "selector: " + spec.getSelector() + " ports: " + portText; } } /** * Returns a short summary text message for the given kubernetes resource */ public static String summaryText(ReplicationController entity) { StringBuilder buffer = new StringBuilder(); ReplicationControllerSpec spec = entity.getSpec(); if (spec != null) { buffer.append("replicas: ").append(spec.getReplicas()); PodTemplateSpec podTemplateSpec = spec.getTemplate(); if (podTemplateSpec != null) { appendSummaryText(buffer, podTemplateSpec); } } return buffer.toString(); } /** * Returns a short summary text message for the given kubernetes resource */ public static String summaryText(DeploymentConfig entity) { StringBuilder buffer = new StringBuilder(); DeploymentConfigSpec spec = entity.getSpec(); if (spec != null) { buffer.append("replicas: " + spec.getReplicas()); PodTemplateSpec podTemplateSpec = spec.getTemplate(); if (podTemplateSpec != null) { appendSummaryText(buffer, podTemplateSpec); } } return buffer.toString(); } /** * Returns a short summary text message for the given kubernetes resource */ public static String summaryText(Pod entity) { StringBuilder buffer = new StringBuilder(); PodSpec podSpec = entity.getSpec(); appendSummaryText(buffer, podSpec); return buffer.toString(); } protected static void appendSummaryText(StringBuilder buffer, PodTemplateSpec podTemplateSpec) { if (podTemplateSpec != null) { appendSummaryText(buffer, podTemplateSpec.getSpec()); } } protected static void appendSummaryText(StringBuilder buffer, PodSpec podSpec) { if (podSpec != null) { List containers = podSpec.getContainers(); if (containers != null) { for (Container container : containers) { String image = container.getImage(); appendText(buffer, "image: " + image); } } } } protected static void appendText(StringBuilder buffer, String text) { if (buffer.length() > 0) { buffer.append(", "); } buffer.append(text); } /** * Creates an IntOrString from the given string which could be a number or a name */ public static IntOrString createIntOrString(int intVal) { IntOrString answer = new IntOrString(); answer.setIntVal(intVal); answer.setKind(INTORSTRING_KIND_INT); return answer; } /** * Creates an IntOrString from the given string which could be a number or a name */ public static IntOrString createIntOrString(String nameOrNumber) { if (isNullOrBlank(nameOrNumber)) { return null; } else { IntOrString answer = new IntOrString(); Integer intVal = null; try { intVal = Integer.parseInt(nameOrNumber); } catch (Exception e) { // ignore invalid number } if (intVal != null) { answer.setIntVal(intVal); answer.setKind(INTORSTRING_KIND_INT); } else { answer.setStrVal(nameOrNumber); answer.setKind(INTORSTRING_KIND_STRING); } return answer; } } public static String getStatusText(PodStatus podStatus) { String status; List statusList = new ArrayList<>(); List containerStatuses = podStatus.getContainerStatuses(); for (ContainerStatus containerStatus : containerStatuses) { ContainerState state = containerStatus.getState(); String statusText = summaryText(state); if (statusText != null) { statusList.add(statusText); } } if (statusList.size() == 1) { status = statusList.get(0); } else { status = statusList.toString(); } return status; } public static Secret validateSecretExists(KubernetesClient kubernetes, String namespace, String secretName) { Secret secret = null; try { secret = kubernetes.secrets().inNamespace(namespace).withName(secretName).get(); } catch (KubernetesClientException e) { if (e.getCode() == 404 || e.getCode() == 403) { // does not exist or namespace does not exists } else { throw e; } } if (secret == null) { throw new IllegalArgumentException("No secret named: " + secretName + " for namespace " + namespace + " is available on Kubernetes" + ". For how to create secrets see: http://fabric8.io/guide/fabric8OnOpenShift.html#requirements "); } else { return secret; } } /** * Converts the DTO loaded from JSON to a {@link KubernetesList} assuming its not a {@link Template} */ public static KubernetesList asKubernetesList(Object dto) throws IOException { if (dto instanceof KubernetesList) { return (KubernetesList) dto; } else { KubernetesList answer = new KubernetesList(); List items = toItemList(dto); answer.setItems(items); return answer; } } /** * Returns true if this object is a pure kubernetes DTO */ public static boolean isPureKubernetes(HasMetadata item) { if (item != null) { String name = item.getClass().getName(); return name.startsWith("io.fabric8.kubernetes"); } return false; } public static boolean isOpenShift(KubernetesClient client) { URL masterUrl = client.getMasterUrl(); if (IS_OPENSHIFT.containsKey(masterUrl)) { return IS_OPENSHIFT.get(masterUrl); } else { RootPaths rootPaths = client.rootPaths(); if (rootPaths != null) { List paths = rootPaths.getPaths(); if (paths != null) { for (String path : paths) { if (java.util.Objects.equals("/oapi", path) || java.util.Objects.equals("oapi", path)) { IS_OPENSHIFT.putIfAbsent(masterUrl, true); return true; } } } } } IS_OPENSHIFT.putIfAbsent(masterUrl, false); return false; } /** * Returns the kubernetes resources on the classpath of the current project */ public static List findKubernetesResourcesOnClasspath(Controller controller) throws IOException { String resourceName = "kubernetes.yml"; if (controller.getOpenShiftClientOrNull() != null) { resourceName = "openshift.yml"; } URL configUrl = findConfigResource("/META-INF/fabric8/" + resourceName); if (configUrl == null) { configUrl = findConfigResource("kubernetes.json"); } if (configUrl != null) { String configText = IOHelpers.loadFully(configUrl); Object dto = null; String configPath = configUrl.getPath(); if (configPath.endsWith(".yml") || configPath.endsWith(".yaml")) { dto = loadYaml(configText, KubernetesResource.class); } else { dto = loadJson(configText); } KubernetesList kubeList = KubernetesHelper.asKubernetesList(dto); List items = kubeList.getItems(); return items; } else { return new ArrayList<>(); } } private static URL findConfigResource(String resourceName) { return KubernetesHelper.class.getResource(resourceName); } // this method is a workaround until we default to NON_EMPTY on the kubernetes model // see: https://github.com/fabric8io/kubernetes-model/issues/154 public static void saveYamlNotEmpty(HasMetadata entity, File outFile) throws IOException { String yaml = toYaml(entity); ObjectMapper objectMapper = createYamlObjectMapper(); // TODO we must convert to YAML, parse as JsonNode // then remove empty nodes then write to YAML // again which is a hack around this issue: // https://github.com/fabric8io/kubernetes-model/issues/154 JsonNode jsonNode = objectMapper.readTree(yaml); removeNullOrEmptyValues(jsonNode); objectMapper.writeValue(outFile, jsonNode); } private static void removeNullOrEmptyValues(JsonNode jsonNode) { if (jsonNode instanceof ObjectNode) { List removeFields = new ArrayList<>(); ObjectNode object = (ObjectNode) jsonNode; for (Iterator iter = object.fieldNames(); iter.hasNext(); ) { String field = iter.next(); JsonNode value = object.get(field); if (isEmptyValue(value)) { removeFields.add(field); } else { removeNullOrEmptyValues(value);; } } for (String field : removeFields) { object.remove(field); } } else if (jsonNode instanceof ArrayNode) { ArrayNode arrayNode = (ArrayNode) jsonNode; for (int i = 0, size = arrayNode.size(); i < size; i++) { JsonNode value = arrayNode.get(i); removeNullOrEmptyValues(value); } } } private static boolean isEmptyValue(JsonNode value) { if (value.isArray()) { int size = value.size(); return size == 0; } if (value.isTextual()) { String text = value.textValue(); return isNullOrBlank(text); } if (value.isObject()) { removeNullOrEmptyValues(value); Iterator iter = value.fieldNames(); int count = 0; while (iter.hasNext()) { iter.next(); count++; } return count == 0; } return false; } public static OpenShiftClient createJenkinshiftOpenShiftClient(String jenkinshiftUrl) { Config config = createJenkinshiftConfig(jenkinshiftUrl); // TODO until jenkinshift supports HTTPS lets disable HTTPS by default // openShiftClient = new DefaultOpenShiftClient(jenkinshiftUrl); JenkinShiftClient jenkinShiftClient = new JenkinShiftClient(config); jenkinShiftClient.updateHttpClient(config); return jenkinShiftClient; } private static Config createJenkinshiftConfig(String jenkinshiftUrl) { Config config = new Config(); config.setMasterUrl(jenkinshiftUrl); return config; } /** * Returns the default namespace for the given client */ public static String getNamespace(KubernetesClient kubernetesClient) { String answer = kubernetesClient.getNamespace(); if (Strings.isNullOrBlank(answer)) { answer = defaultNamespace(); } return answer; } protected static class JenkinShiftClient extends DefaultOpenShiftClient { public JenkinShiftClient(Config config) throws KubernetesClientException { super(config); updateHttpClient(config); } protected void updateHttpClient(Config config) { this.httpClient = createHttpClient(config); } // TODO until jenkinshift supports HTTPS lets disable HTTPS by default private OkHttpClient createHttpClient(final Config config) { try { OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder(); // Follow any redirects httpClientBuilder.followRedirects(true); httpClientBuilder.followSslRedirects(true); if (config.isTrustCerts()) { httpClientBuilder.hostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String s, SSLSession sslSession) { return true; } }); } if (isNotNullOrEmpty(config.getUsername()) && isNotNullOrEmpty(config.getPassword())) { httpClientBuilder.addInterceptor(new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Request authReq = chain.request().newBuilder().addHeader("Authorization", Credentials.basic(config.getUsername(), config.getPassword())).build(); return chain.proceed(authReq); } }); } else if (config.getOauthToken() != null) { httpClientBuilder.addInterceptor(new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Request authReq = chain.request().newBuilder().addHeader("Authorization", "Bearer " + config.getOauthToken()).build(); return chain.proceed(authReq); } }); } Logger reqLogger = LoggerFactory.getLogger(HttpLoggingInterceptor.class); if (reqLogger.isTraceEnabled()) { HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(); loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); httpClientBuilder.addNetworkInterceptor(loggingInterceptor); } if (config.getConnectionTimeout() > 0) { httpClientBuilder.connectTimeout(config.getConnectionTimeout(), TimeUnit.MILLISECONDS); } if (config.getRequestTimeout() > 0) { httpClientBuilder.readTimeout(config.getRequestTimeout(), TimeUnit.MILLISECONDS); } // Only check proxy if it's a full URL with protocol /* if (config.getMasterUrl().toLowerCase().startsWith(Config.HTTP_PROTOCOL_PREFIX) || config.getMasterUrl().startsWith(Config.HTTPS_PROTOCOL_PREFIX)) { try { URL proxyUrl = getProxyUrl(config); if (proxyUrl != null) { httpClientBuilder.proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyUrl.getHost(), proxyUrl.getPort()))); } } catch (MalformedURLException e) { throw new KubernetesClientException("Invalid proxy server configuration", e); } } */ if (config.getUserAgent() != null && !config.getUserAgent().isEmpty()) { httpClientBuilder.addNetworkInterceptor(new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Request agent = chain.request().newBuilder().header("User-Agent", config.getUserAgent()).build(); return chain.proceed(agent); } }); } return httpClientBuilder.build(); } catch (Exception e) { throw KubernetesClientException.launderThrowable(e); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy