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

org.jolokia.kubernetes.client.KubernetesJmxConnector Maven / Gradle / Ivy

The newest version!
package org.jolokia.kubernetes.client;

import io.fabric8.kubernetes.api.model.ObjectMeta;
import io.fabric8.kubernetes.api.model.Pod;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClientBuilder;
import io.fabric8.kubernetes.client.KubernetesClientException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXServiceURL;

import okhttp3.Response;
import org.jolokia.client.J4pClient;
import org.jolokia.client.jmxadapter.JolokiaJmxConnector;
import org.jolokia.client.jmxadapter.RemoteJmxAdapter;

public class KubernetesJmxConnector extends JolokiaJmxConnector {

  private static final Pattern POD_PATTERN = Pattern
      .compile(
          "/?(?[^/]+)/(?https?:)?(?[^/^:]+)(?:[^/]+)?/(?.+)");
  private static final Map apiClients = Collections.synchronizedMap(new HashMap<>());
  public static String KUBERNETES_CLIENT_CONTEXT ="kubernetes.client.context";

  public KubernetesJmxConnector(JMXServiceURL serviceURL,
      Map environment) {
    super(serviceURL, environment);
  }

  @Override
  public void connect(Map env) throws IOException {
    if (!"kubernetes".equals(this.serviceUrl.getProtocol())) {
      throw new MalformedURLException(String
          .format("Invalid URL %s : Only protocol \"kubernetes\" is supported (not %s)", serviceUrl,
              serviceUrl.getProtocol()));
    }
    final Map mergedEnvironment = this.mergedEnvironment(env);
    KubernetesClient client = getApiClient((String) env.get(KUBERNETES_CLIENT_CONTEXT));

    this.adapter = createAdapter(expandAndProbeUrl(client, mergedEnvironment));
    this.postCreateAdapter();
  }

  protected RemoteJmxAdapter createAdapter(J4pClient client) throws IOException {
    return new RemoteJmxAdapter(client);
  }

  /**
   * Get a kubernetes client for the specified local context (in ~/.kube/config)
   * @param context , specify context, null for current context
   * @return client configured for the specified context - potentially recycled
   * as the setup is expensive (YAML parsing is amazingly slow)
   */
  public static KubernetesClient getApiClient(String context) {
    final String key = String.valueOf(context);
    KubernetesClient client = apiClients.get(key);

    if(client == null){
      client=new KubernetesClientBuilder().withConfig(Config.autoConfigure(context)).build();
      apiClients.put(key, client);
    }
    return client;
  }

  /**
   * Manually reset any cached config. To be uses in case you have changed your kubeconfig
   */
  public static void resetKubernetesConfig() {
	  apiClients.clear();
  }

  /**
   * @return a connection if successful
   */
  protected J4pClient expandAndProbeUrl(KubernetesClient client,
      Map env) throws MalformedURLException {
    String proxyPath = this.serviceUrl.getURLPath();
    J4pClient connection;
    final HashMap headersForProbe = createHeadersForProbe(env);
    try {
      if (POD_PATTERN.matcher(proxyPath).matches()) {
        final Matcher matcher = POD_PATTERN.matcher(proxyPath);
        if (matcher.find()) {
          String namespace = matcher.group("namespace");
          String podPattern = matcher.group("podPattern");
          String path = matcher.group("path");
          String protocol = matcher.group("protocol");
          String port = matcher.group("port");
          final Pod exactPod = client.pods().inNamespace(namespace).withName(podPattern).get();
          //check if podname pans out directly
          if (exactPod != null
              && (connection = probeProxyPath(env, client, buildProxyPath(exactPod, protocol, port, path),
              headersForProbe)) != null) {
            return connection;
          } else { //scan through pods in namespace if podname is a pattern

            for (final Pod pod :
                client.pods().inNamespace(namespace).list().getItems()) {
              if (pod.getMetadata()
                  .getName().matches(podPattern)) {
                if ((connection = probeProxyPath(env, client, buildProxyPath(pod, protocol, port, path),
                    headersForProbe)) != null) {
                  return connection;
                }
              }
            }
          }
        }
      }
    } catch (KubernetesClientException ignore) {
    }
    throw new MalformedURLException("Unable to connect to proxypath " + proxyPath);
  }

  public static StringBuilder buildProxyPath(Pod pod, String protocol, String port, String path) {
    final ObjectMeta metadata = pod.getMetadata();
    final StringBuilder url = new StringBuilder("/api/").append(pod.getApiVersion()).append("/namespaces/").append(metadata.getNamespace()).append("/pods/");
    if (protocol != null && !protocol.equals("http:")) {
      url.append(protocol);
    }
    url.append(metadata.getName());
    if(port!=null){
      url.append(port);
    }
    url.append("/proxy");

    if(!path.startsWith("/")) {
      url.append('/');
    }
    url.append(path);
    return url;
  }

  private static HashMap createHeadersForProbe(
      Map env) {
    final HashMap headers = new HashMap<>();
    String[] credentials = (String[]) env.get(JMXConnector.CREDENTIALS);
    if (credentials != null) {
      MinimalHttpClientAdapter.authenticate(headers, credentials[0], credentials[1]);
    }
    return headers;
  }

  /**
   * Probe whether we find Jolokia in given namespace, pod and path
   */
  public static J4pClient probeProxyPath(Map env, KubernetesClient client,
      StringBuilder url,
      HashMap headers) {
    try {
      final String proxyPath = url.toString();
      Response response = MinimalHttpClientAdapter
          .performRequest(client, proxyPath, "{\"type\":\"version\"}".getBytes(), null
              , headers);
      if (response.body() != null) {
        response.body().close();
      }
      if (response.isSuccessful()) {
        return new J4pClient(
            proxyPath, new MinimalHttpClientAdapter(client, proxyPath, env));
      }
    } catch (IOException ignore) {
    }
    return null;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy