+ * This inspects /proc/self/cgroup looking for a Kubernetes Control Group. Once it finds one it attempts + * to isolate just the docker container id. There doesn't appear to be a standard way to do this, but + * it seems to be the only way to determine what the current container is in a multi-container pod. It would have + * been much nicer if Kubernetes would just put the container id in a standard environment variable. + *
+ * + * @param path Path to a {@code /proc/pid/cgroup} file. + * @return A container id or {@code null} if not found. + */ + public static String getContainerId(Path path) { + try { + if (Files.exists(path)) { + try (final Stream+ * Based on + * ControlGroup.java + *
+ * + * @param cgroupPath a slash-separated hierarchy of CGroups. + * @return a Docker ID + */ + private static String getDockerId(String cgroupPath) { + String[] elements = cgroupPath.split("/", -1); + String dockerId = null; + for (String element : elements) { + Matcher matcher = DOCKER_ID_PATTERN.matcher(element); + if (matcher.find()) { + dockerId = matcher.group(); + } + } + return dockerId; + } + + /** + * Retrieves the full hierarchy of CGroups the process belongs + *+ * See /proc/pid/cgroups + *
+ * + * @param line A line from a {@code /proc/pid/cgroups} file + */ + private static String getCGroupPath(String line) { + String[] fields = line.split(":", -1); + return fields.length > 2 ? fields[2] : null; + } +} diff --git a/log4j/src/main/java/io/fabric8/kubernetes/log4j/lookup/KubernetesLookup.java b/log4j/src/main/java/io/fabric8/kubernetes/log4j/lookup/KubernetesLookup.java new file mode 100644 index 00000000000..216156aa067 --- /dev/null +++ b/log4j/src/main/java/io/fabric8/kubernetes/log4j/lookup/KubernetesLookup.java @@ -0,0 +1,468 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * + * Licensed 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.log4j.lookup; + +import io.fabric8.kubernetes.api.model.Container; +import io.fabric8.kubernetes.api.model.ContainerStatus; +import io.fabric8.kubernetes.api.model.Namespace; +import io.fabric8.kubernetes.api.model.ObjectMeta; +import io.fabric8.kubernetes.api.model.Pod; +import io.fabric8.kubernetes.api.model.PodSpec; +import io.fabric8.kubernetes.api.model.PodStatus; +import io.fabric8.kubernetes.client.KubernetesClient; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.config.plugins.Plugin; +import org.apache.logging.log4j.core.lookup.AbstractLookup; +import org.apache.logging.log4j.core.lookup.StrLookup; +import org.apache.logging.log4j.status.StatusLogger; +import org.apache.logging.log4j.util.LoaderUtil; + +import java.net.InetAddress; +import java.net.URL; +import java.net.UnknownHostException; +import java.nio.file.Path; +import java.util.List; +import java.util.Map; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +/** + * Retrieves various attributes from the Kubernetes server. + *+ * The supported keys are listed in the following table: + *
+ *Key | + *Description | + *
---|---|
{@value ACCOUNT_NAME} | + *the name of the account | + *
{@value ANNOTATIONS} | + *the annotations of the Kubernetes pod | + *
{@value CONTAINER_ID} | + *the id of the Kubernetes container | + *
{@value CONTAINER_NAME} | + *the name of the Kubernetes container | + *
{@value HOST} | + *the host name of the Kubernetes pod | + *
{@value HOST_IP} | + *the IP of the Kubernetes pod | + *
{@value IMAGE_ID} | + *the id of the Kubernetes container image | + *
{@value IMAGE_NAME} | + *the name of the Kubernetes container image | + *
{@value LABELS} | + *the labels of the Kubernetes pod | + *
"labels.<name>" | + *the value of the "<name>" label of the Kubernetes pod | + *
{@value MASTER_URL} | + *the master URL of the Kubernetes cluster | + *
{@value NAMESPACE_ANNOTATIONS} | + *the annotations of the namespace | + *
{@value NAMESPACE_ID} | + *the id of the namespace | + *
{@value NAMESPACE_LABELS} | + *the labels of the namespace | + *
{@value NAMESPACE_NAME} | + *the name of the namespace | + *
{@value POD_ID} | + *the id of the pod | + *
{@value POD_IP} | + *the IP of the pod | + *
{@value POD_NAME} | + *the name of the pod | + *
+ * Used in tests to provide a mock client. + *
+ * + * @return A Kubernetes client. + */ + protected KubernetesClient createClient() { + return ClientBuilder.createClient(); + } + + private static Pod getCurrentPod(final KubernetesClient kubernetesClient) { + final String hostName = getHostName(); + try { + if (hostName != null && !hostName.isEmpty()) { + return kubernetesClient.pods().withName(hostName).get(); + } + } catch (Exception e) { + LOGGER.debug("Unable to locate pod with name {}.", hostName, e); + } + return null; + } + + static String getHostName() { + String hostName = null; + try { + hostName = InetAddress.getLocalHost().getHostName(); + } catch (final UnknownHostException ignored) { + // NOP + } + return hostName != null && !"localhost".equals(hostName) ? hostName : System.getenv(HOSTNAME); + } + + private static Namespace getNamespace(KubernetesClient client, Pod pod) { + return client.namespaces() + .withName(pod.getMetadata().getNamespace()) + .get(); + } + + private static void fillNamespaceData(Namespace namespace, KubernetesInfo kubernetesInfo) { + final ObjectMeta namespaceMetadata = namespace.getMetadata(); + if (namespaceMetadata != null) { + kubernetesInfo.namespaceAnnotations = namespaceMetadata.getAnnotations(); + kubernetesInfo.namespaceId = namespaceMetadata.getUid(); + kubernetesInfo.namespaceLabels = namespaceMetadata.getLabels(); + } + } + + private static void fillPodData(Pod pod, KubernetesInfo kubernetesInfo) { + final ObjectMeta podMetadata = pod.getMetadata(); + if (podMetadata != null) { + kubernetesInfo.annotations = podMetadata.getAnnotations(); + kubernetesInfo.labels = podMetadata.getLabels(); + kubernetesInfo.namespace = podMetadata.getNamespace(); + kubernetesInfo.podId = podMetadata.getUid(); + kubernetesInfo.podName = podMetadata.getName(); + } + fillStatuses(pod, kubernetesInfo); + // The container name is filled as a result + String containerName = kubernetesInfo.containerName; + + final PodSpec podSpec = pod.getSpec(); + if (podSpec != null) { + kubernetesInfo.hostName = podSpec.getNodeName(); + kubernetesInfo.accountName = podSpec.getServiceAccountName(); + + Container container = getContainer(podSpec, containerName); + if (container != null) { + kubernetesInfo.containerName = container.getName(); + kubernetesInfo.imageName = container.getImage(); + } + } + } + + private static void fillStatuses(Pod pod, KubernetesInfo kubernetesInfo) { + final PodStatus podStatus = pod.getStatus(); + if (podStatus != null) { + kubernetesInfo.hostIp = podStatus.getHostIP(); + kubernetesInfo.podIp = podStatus.getPodIP(); + + ContainerStatus containerStatus = getContainerStatus(podStatus); + if (containerStatus != null) { + kubernetesInfo.containerId = containerStatus.getContainerID(); + kubernetesInfo.imageId = containerStatus.getImageID(); + kubernetesInfo.containerName = containerStatus.getName(); + } + } + } + + private static ContainerStatus getContainerStatus(PodStatus podStatus) { + List