diff --git a/client/kubernetes/src/main/java/org/jolokia/kubernetes/client/KubernetesJmxConnectionProvider.java b/client/kubernetes/src/main/java/org/jolokia/kubernetes/client/KubernetesJmxConnectionProvider.java
index d9d94fcfc..558c0a4ed 100644
--- a/client/kubernetes/src/main/java/org/jolokia/kubernetes/client/KubernetesJmxConnectionProvider.java
+++ b/client/kubernetes/src/main/java/org/jolokia/kubernetes/client/KubernetesJmxConnectionProvider.java
@@ -10,7 +10,16 @@
/**
* I provide support for handling JMX urls for the Jolokia protocol
- * Syntax service:jmx:jolokia://host:port/path/to/jolokia/with/slash/suffix/
+ * Syntax examples
+ *
+ * - service:jmx:kubernetes:///api/v1/namespaces/mynamespace/pods/mypodname-.+/actuator/jolokia/
+ * - service:jmx:kubernetes:///api/v1/namespaces/mynamespace/services/myservice-.+/actuator/jolokia/
+ *
+ *
+ * Regular expressions in service url is supported so you can have working URLs across deploys.
+ * Regular expression URLs will connect to the first pod/service that matches expession.
+ * Prerequesite: You should have kubectl
installed and have valid credentiatls for k8s cluster
+ * readily stored under $HOME/.kube/config
* My Jar contains a service loader, so that Jolokia JMX protocol is supported
* as long as my jar (jmx-adapter-version-standalone.jar) is on the classpath
*
@@ -19,8 +28,7 @@
* //NB: include trailing slash
* https will be used if port number fits the pattern *443 or connect env map contains "jmx.remote.x.check.stub"->"true"
* JMXConnector connector = JMXConnectorFactory
- * .connect(new JMXServiceURL("service:jmx:kubernetes://host:port/jolokia/"), Collections.singletonMap(JMXConnector.CREDENTIALS, Arrays
- * .asList("user", "password")));
+ * .connect(new JMXServiceURL("service:jmx:kubernetes:///api/v1/namespaces/mynamespace/pods/mypodname-.+/actuator/jolokia/")));
* connector.connect();
* connector.getMBeanServerConnection();
*
@@ -32,7 +40,7 @@ public JMXConnector newJMXConnector(JMXServiceURL serviceURL, Map env
//the exception will be handled by JMXConnectorFactory so that other handlers are allowed to handle
//other protocols
if(!"kubernetes".equals(serviceURL.getProtocol())) {
- throw new MalformedURLException("I only serve Kubernetes connections");
+ throw new MalformedURLException(String.format("Invalid URL %s : Only protocol \"kubernetes\" is supported (not %s)", serviceURL, serviceURL.getProtocol()));
}
return new KubernetesJmxConnector(serviceURL, environment);
}
diff --git a/client/kubernetes/src/main/java/org/jolokia/kubernetes/client/KubernetesJmxConnector.java b/client/kubernetes/src/main/java/org/jolokia/kubernetes/client/KubernetesJmxConnector.java
index 5dad76899..3a6f27688 100644
--- a/client/kubernetes/src/main/java/org/jolokia/kubernetes/client/KubernetesJmxConnector.java
+++ b/client/kubernetes/src/main/java/org/jolokia/kubernetes/client/KubernetesJmxConnector.java
@@ -1,5 +1,6 @@
package org.jolokia.kubernetes.client;
+import com.squareup.okhttp.Credentials;
import com.squareup.okhttp.Response;
import io.kubernetes.client.ApiClient;
import io.kubernetes.client.ApiException;
@@ -19,8 +20,10 @@
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 org.jolokia.client.J4pClient;
+import org.jolokia.client.J4pClientBuilderFactory;
import org.jolokia.client.jmxadapter.JolokiaJmxConnector;
import org.jolokia.client.jmxadapter.RemoteJmxAdapter;
@@ -30,6 +33,7 @@ public class KubernetesJmxConnector extends JolokiaJmxConnector {
.compile("/api/v1/namespaces/([^/]+)/pods/([^/]+)/proxy/(.+)");
private static Pattern SERVICE_PATTERN = Pattern
.compile("/api/v1/namespaces/([^/]+)/services/([^/]+)/proxy/(.+)");
+ private static ApiClient apiClient;
public KubernetesJmxConnector(JMXServiceURL serviceURL,
Map environment) {
@@ -39,37 +43,47 @@ public KubernetesJmxConnector(JMXServiceURL serviceURL,
@Override
public void connect(Map env) throws IOException {
if (!"kubernetes".equals(this.serviceUrl.getProtocol())) {
- throw new MalformedURLException("Only Kubernetes urls are supported");
+ throw new MalformedURLException(String.format("Invalid URL %s : Only protocol \"kubernetes\" is supported (not %s)", serviceUrl, serviceUrl.getProtocol()));
}
- ApiClient client = getApiClient(env);
+ final Map mergedEnvironment = this.mergedEnvironment(env);
+ ApiClient client = getApiClient(mergedEnvironment);
- this.adapter = createAdapter(client);
+ this.adapter = createAdapter(expandAndProbeUrl(client, mergedEnvironment));
this.postCreateAdapter();
}
- protected RemoteJmxAdapter createAdapter(ApiClient client) throws IOException {
- return new RemoteJmxAdapter(expandAndProbeUrl(client));
+ protected RemoteJmxAdapter createAdapter(J4pClient client) throws IOException {
+ return new RemoteJmxAdapter(client);
}
- protected ApiClient getApiClient(Map env) throws IOException {
+ public static ApiClient getApiClient(Map env) throws IOException {
+ if(apiClient != null){
+ return apiClient;
+ }
+ return buildApiClient(env);
+ }
+
+ public static ApiClient buildApiClient(Map env) throws IOException {
// file path to your KubeConfig
final Object configPath = env != null ? env.get("kube.config.path") : null;
String kubeConfigPath = configPath != null ? configPath.toString()
: String.format("%s/.kube/config", System.getProperty("user.home"));
// loading the out-of-cluster config, a kubeconfig from file-system
- return ClientBuilder
+ return apiClient = ClientBuilder
.kubeconfig(KubeConfig.loadKubeConfig(new FileReader(kubeConfigPath))).build();
}
/**
* @return a connection if successful
*/
- protected J4pClient expandAndProbeUrl(ApiClient client) throws MalformedURLException {
+ protected J4pClient expandAndProbeUrl(ApiClient client,
+ Map env) throws MalformedURLException {
Configuration.setDefaultApiClient(client);
CoreV1Api api = new CoreV1Api();
String proxyPath = this.serviceUrl.getURLPath();
+ final HashMap headersForProbe = createHeadersForProbe(env);
try {
if (SERVICE_PATTERN.matcher(proxyPath).matches()) {
final Matcher matcher = SERVICE_PATTERN.matcher(proxyPath);
@@ -100,10 +114,12 @@ protected J4pClient expandAndProbeUrl(ApiClient client) throws MalformedURLExcep
}
//probe a request to this URL via proxy
try {
- final Response response = probeProxyPath(client, proxyPath);
+ final Response response = probeProxyPath(client, proxyPath,
+ headersForProbe);
+ response.body().close();
if (response.isSuccessful()) {
return new J4pClient(
- proxyPath, new MinimalHttpClientAdapter(client, proxyPath));
+ proxyPath, new MinimalHttpClientAdapter(client, proxyPath, env));
}
} catch (IOException ignore) {
}
@@ -123,7 +139,6 @@ protected J4pClient expandAndProbeUrl(ApiClient client) throws MalformedURLExcep
String podPattern = matcher.group(2);
String actualNamespace = null;
String actualPodName = null;
- V1Pod actualPod = null;
for (final V1Pod pod : api
.listPodForAllNamespaces(null, null, false, null, null, null, null, 5, null)
.getItems()) {
@@ -132,7 +147,6 @@ protected J4pClient expandAndProbeUrl(ApiClient client) throws MalformedURLExcep
.getName().matches(podPattern)) {
actualNamespace = pod.getMetadata().getNamespace();
actualPodName = pod.getMetadata().getName();
- actualPod = pod;
break;
}
}
@@ -148,26 +162,14 @@ protected J4pClient expandAndProbeUrl(ApiClient client) throws MalformedURLExcep
}
//probe a request to this URL via proxy
try {
- final Response response = probeProxyPath(client, proxyPath);
+ final Response response = probeProxyPath(client, proxyPath,
+ headersForProbe);
+ response.body().close();
if (response.isSuccessful()) {
return new J4pClient(
- proxyPath, new MinimalHttpClientAdapter(client, proxyPath));
+ proxyPath, new MinimalHttpClientAdapter(client, proxyPath, env));
}
-/* port forward stragegy
- else if (response.code() == 403 ) {//could be proxy is not allowed try a port forward workaround instead
- String path = matcher.group(3);
- final Integer port = actualPod.getSpec().getContainers().get(0).getPorts()
- .get(0).getContainerPort();
- final String scheme = "http";
- final Response forwardResponse = api
- .connectGetNamespacedPodPortforwardCall(actualPodName, actualNamespace,
- port, null, null).execute();
- if (forwardResponse.isSuccessful()) {
- //test both http and https
- return new RemoteJmxAdapter(
- new J4pClient(String.format("%s://localhost:%d/%s", scheme, port, path)));
- }
- }*/
+
} catch (IOException ignore) {
}
}
@@ -176,10 +178,17 @@ else if (response.code() == 403 ) {//could be proxy is not allowed try a port fo
} catch (ApiException ignore) {
}
- throw new
-
- MalformedURLException("Unable to connect to proxypath " + proxyPath);
+ throw new MalformedURLException("Unable to connect to proxypath " + proxyPath);
+ }
+ private HashMap createHeadersForProbe(
+ Map env) {
+ final HashMap headers = new HashMap();
+ String[] credentials= (String[]) env.get(JMXConnector.CREDENTIALS);
+ if(credentials != null) {
+ headers.put("Authorization", Credentials.basic(credentials[0], credentials[1]));
+ }
+ return headers;
}
/**
@@ -210,10 +219,11 @@ private String findPodPathIfAnyForService(String actualName, String actualNamesp
/**
*/
- public static Response probeProxyPath(ApiClient client, String proxyPath)
+ public static Response probeProxyPath(ApiClient client, String proxyPath,
+ HashMap headers)
throws IOException, ApiException {
return MinimalHttpClientAdapter
.performRequest(client, proxyPath, Collections.singletonMap("type", "version"),
- Collections.emptyList(), "POST", new HashMap());
+ Collections.emptyList(), "POST", headers);
}
}
diff --git a/client/kubernetes/src/main/java/org/jolokia/kubernetes/client/MinimalHttpClientAdapter.java b/client/kubernetes/src/main/java/org/jolokia/kubernetes/client/MinimalHttpClientAdapter.java
index a26f0fcb8..4c259dbd0 100644
--- a/client/kubernetes/src/main/java/org/jolokia/kubernetes/client/MinimalHttpClientAdapter.java
+++ b/client/kubernetes/src/main/java/org/jolokia/kubernetes/client/MinimalHttpClientAdapter.java
@@ -1,5 +1,6 @@
package org.jolokia.kubernetes.client;
+import com.squareup.okhttp.Credentials;
import com.squareup.okhttp.Protocol;
import com.squareup.okhttp.Response;
import io.kubernetes.client.ApiClient;
@@ -14,6 +15,7 @@
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
+import javax.management.remote.JMXConnector;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpHeaders;
@@ -34,16 +36,24 @@
/**
* I implement a minimum support of the HttpClient interface
- * based on what is used by J4PClient
+ * based on what is used by J4PClient hence I need to adapt
+ * One HTTP client to another HTTP client API
*/
public class MinimalHttpClientAdapter implements HttpClient {
private final ApiClient client;
private final String urlPath;
+ private String user;
+ private String password;
- public MinimalHttpClientAdapter(ApiClient client, String urlPath) {
+ public MinimalHttpClientAdapter(ApiClient client, String urlPath, Map env) {
this.client = client;
this.urlPath = urlPath;
+ String[] credentials= (String[]) env.get(JMXConnector.CREDENTIALS);
+ if(credentials != null) {
+ this.user=credentials[0];
+ this.password=credentials[1];
+ }
}
@Override
@@ -58,8 +68,8 @@ public ClientConnectionManager getConnectionManager() {
@Override
public HttpResponse execute(HttpUriRequest httpUriRequest)
- throws IOException, ClientProtocolException {
- Map headers= new HashMap();
+ throws IOException {
+ Map headers= createHeaders();
try {
final Response response = performRequest(client, urlPath,
extractBody(httpUriRequest, headers), extractQueryParameters(httpUriRequest),
@@ -72,6 +82,14 @@ public HttpResponse execute(HttpUriRequest httpUriRequest)
}
+ public HashMap createHeaders() {
+ final HashMap headers = new HashMap();
+ if(this.user != null) {
+ headers.put("Authorization", Credentials.basic(this.user, this.password));
+ }
+ return headers;
+ }
+
public static Response performRequest(ApiClient client, String urlPath, Object body,
List queryParams, String method, Map headers) throws IOException, ApiException {
return client
diff --git a/client/kubernetes/src/test/script/testJConsole.sh b/client/kubernetes/src/test/script/testJConsole.sh
index de6f2fbbc..663f94d5b 100755
--- a/client/kubernetes/src/test/script/testJConsole.sh
+++ b/client/kubernetes/src/test/script/testJConsole.sh
@@ -1,6 +1,6 @@
#!/bin/bash
-#for instance test with: service:jmx:jolokia://localhost:8779/jolokia/
+#for instance test with: service:jmx:kubernetes:///api/v1/namespaces/mynamespace/pods/mypodname-.*/actuator/jolokia/
if [ "$JAVA_HOME" = "" ]
then
echo "JAVA_HOME must be set and valid to run $0" ; exit 1