From fb352ad4fa37c2599172b007fbe1a5148545e68f Mon Sep 17 00:00:00 2001 From: Martin Skarsaune Date: Tue, 28 May 2024 21:38:53 +0200 Subject: [PATCH 01/20] JMC-8237: Connect with Jolokia protocol over kubernetes HTTP proxy --- .../org.openjdk.jmc.feature.core/feature.xml | 7 + .../org.openjdk.jmc.kubernetes/.classpath | 16 + .../META-INF/MANIFEST.MF | 19 + .../OSGI-INF/l10n/bundle.properties | 3 + .../build.properties | 7 + .../org.openjdk.jmc.kubernetes/plugin.xml | 74 ++++ .../org.openjdk.jmc.kubernetes/pom.xml | 49 +++ .../JmcKubernetesJmxConnection.java | 83 +++++ .../JmcKubernetesJmxConnectionProvider.java | 55 +++ .../kubernetes/JmcKubernetesJmxConnector.java | 55 +++ .../jmc/kubernetes/JmcKubernetesPlugin.java | 160 +++++++++ .../KubernetesDiscoveryListener.java | 340 ++++++++++++++++++ .../kubernetes/KubernetesJvmDescriptor.java | 101 ++++++ .../org/openjdk/jmc/kubernetes/Messages.java | 56 +++ .../jmc/kubernetes/messages.properties | 9 + .../JmcKubernetesPreferenceForm.java | 188 ++++++++++ .../KubernetesScanningParameters.java | 59 +++ .../jmc/kubernetes/preferences/Messages.java | 62 ++++ .../preferences/PreferenceConstants.java | 48 +++ .../preferences/PreferenceInitializer.java | 60 ++++ .../preferences/messages.properties | 17 + application/pom.xml | 1 + .../.classpath | 21 ++ .../META-INF/MANIFEST.MF | 20 ++ .../build.properties | 36 ++ .../org.openjdk.jmc.kubernetes.test/pom.xml | 58 +++ .../jmc/kubernetes/JmcKubernetesTest.java | 313 ++++++++++++++++ .../resources/__files/jolokia-attribute.json | 10 + .../__files/jolokia-basic-auth-secret.json | 19 + .../test/resources/__files/jolokia-exec.json | 10 + .../__files/jolokia-properties-secret.json | 15 + .../resources/__files/jolokia-version.json | 41 +++ .../test/resources/__files/memory-list.json | 46 +++ .../resources/__files/mock-kube-config.yml | 17 + .../src/test/resources/__files/pod.json | 11 + .../src/test/resources/__files/pods.json | 19 + .../test/resources/__files/runtime-list.json | 104 ++++++ .../src/test/resources/__files/secrets.json | 43 +++ .../resources/__files/system-attributes.json | 101 ++++++ .../test/resources/mappings/jolokia-exec.json | 15 + .../mappings/jolokia-list-memory.json | 15 + .../mappings/jolokia-list-runtime.json | 15 + .../resources/mappings/jolokia-probe.json | 15 + .../resources/mappings/jolokia-probe2.json | 15 + .../mappings/jolokia-read-attribute.json | 15 + .../resources/mappings/jvm-properties.json | 15 + .../test/resources/mappings/kube-config.json | 13 + .../src/test/resources/mappings/pod.json | 10 + .../src/test/resources/mappings/pods.json | 10 + .../src/test/resources/mappings/secrets.json | 10 + .../resources/mappings/system-attributes.json | 15 + application/uitests/pom.xml | 3 +- .../platform-definition-2024-03.target | 1 + releng/third-party/pom.xml | 4 + 54 files changed, 2523 insertions(+), 1 deletion(-) create mode 100644 application/org.openjdk.jmc.kubernetes/.classpath create mode 100644 application/org.openjdk.jmc.kubernetes/META-INF/MANIFEST.MF create mode 100644 application/org.openjdk.jmc.kubernetes/OSGI-INF/l10n/bundle.properties create mode 100644 application/org.openjdk.jmc.kubernetes/build.properties create mode 100644 application/org.openjdk.jmc.kubernetes/plugin.xml create mode 100644 application/org.openjdk.jmc.kubernetes/pom.xml create mode 100644 application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnection.java create mode 100644 application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnectionProvider.java create mode 100644 application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnector.java create mode 100644 application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesPlugin.java create mode 100644 application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/KubernetesDiscoveryListener.java create mode 100644 application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/KubernetesJvmDescriptor.java create mode 100644 application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/Messages.java create mode 100644 application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/messages.properties create mode 100644 application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/JmcKubernetesPreferenceForm.java create mode 100644 application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/KubernetesScanningParameters.java create mode 100644 application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/Messages.java create mode 100644 application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/PreferenceConstants.java create mode 100644 application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/PreferenceInitializer.java create mode 100644 application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/messages.properties create mode 100644 application/uitests/org.openjdk.jmc.kubernetes.test/.classpath create mode 100644 application/uitests/org.openjdk.jmc.kubernetes.test/META-INF/MANIFEST.MF create mode 100644 application/uitests/org.openjdk.jmc.kubernetes.test/build.properties create mode 100644 application/uitests/org.openjdk.jmc.kubernetes.test/pom.xml create mode 100644 application/uitests/org.openjdk.jmc.kubernetes.test/src/test/java/org/openjdk/jmc/kubernetes/JmcKubernetesTest.java create mode 100644 application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-attribute.json create mode 100644 application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-basic-auth-secret.json create mode 100644 application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-exec.json create mode 100644 application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-properties-secret.json create mode 100644 application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-version.json create mode 100644 application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/memory-list.json create mode 100644 application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/mock-kube-config.yml create mode 100644 application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/pod.json create mode 100644 application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/pods.json create mode 100644 application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/runtime-list.json create mode 100644 application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/secrets.json create mode 100644 application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/system-attributes.json create mode 100644 application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-exec.json create mode 100644 application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-list-memory.json create mode 100644 application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-list-runtime.json create mode 100644 application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-probe.json create mode 100644 application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-probe2.json create mode 100644 application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-read-attribute.json create mode 100644 application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jvm-properties.json create mode 100644 application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/kube-config.json create mode 100644 application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/pod.json create mode 100644 application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/pods.json create mode 100644 application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/secrets.json create mode 100644 application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/system-attributes.json diff --git a/application/org.openjdk.jmc.feature.core/feature.xml b/application/org.openjdk.jmc.feature.core/feature.xml index 143656a909..8b35cbc6f4 100644 --- a/application/org.openjdk.jmc.feature.core/feature.xml +++ b/application/org.openjdk.jmc.feature.core/feature.xml @@ -236,4 +236,11 @@ install-size="0" version="0.0.0" unpack="false"/> + + diff --git a/application/org.openjdk.jmc.kubernetes/.classpath b/application/org.openjdk.jmc.kubernetes/.classpath new file mode 100644 index 0000000000..5f95c14715 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/.classpath @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/application/org.openjdk.jmc.kubernetes/META-INF/MANIFEST.MF b/application/org.openjdk.jmc.kubernetes/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..cce5a82a0d --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/META-INF/MANIFEST.MF @@ -0,0 +1,19 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: %Bundle-Name +Bundle-SymbolicName: org.openjdk.jmc.kubernetes;singleton:=true +Bundle-Version: 9.1.0.qualifier +Automatic-Module-Name: org.openjdk.jmc.kubernetes +Bundle-RequiredExecutionEnvironment: JavaSE-17 +Require-Bundle: org.openjdk.jmc.jolokia, + org.eclipse.core.runtime, + org.eclipse.ui, + org.openjdk.jmc.ui, + org.eclipse.swt, + org.jolokia.client-kubernetes.standalone;bundle-version="2.0.2", + org.openjdk.jmc.rjmx +Export-Package: org.openjdk.jmc.kubernetes, + org.openjdk.jmc.kubernetes.preferences +Import-Package: org.apache.commons.logging;version="1.2.0" +Bundle-Activator: org.openjdk.jmc.kubernetes.JmcKubernetesPlugin +Bundle-ActivationPolicy: lazy diff --git a/application/org.openjdk.jmc.kubernetes/OSGI-INF/l10n/bundle.properties b/application/org.openjdk.jmc.kubernetes/OSGI-INF/l10n/bundle.properties new file mode 100644 index 0000000000..83ca6439dd --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/OSGI-INF/l10n/bundle.properties @@ -0,0 +1,3 @@ +#Properties file for org.openjdk.jmc.kubernetes +page.name = Kubernetes +Bundle-Name = Kubernetes JMC extensions \ No newline at end of file diff --git a/application/org.openjdk.jmc.kubernetes/build.properties b/application/org.openjdk.jmc.kubernetes/build.properties new file mode 100644 index 0000000000..25bd92d046 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/build.properties @@ -0,0 +1,7 @@ +source.. = src/main/java/ +output.. = target/classes/ +bin.includes = META-INF/,\ + OSGI-INF/,\ + .,\ + OSGI-INF/l10n/bundle.properties,\ + plugin.xml diff --git a/application/org.openjdk.jmc.kubernetes/plugin.xml b/application/org.openjdk.jmc.kubernetes/plugin.xml new file mode 100644 index 0000000000..44032f68a7 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/plugin.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/application/org.openjdk.jmc.kubernetes/pom.xml b/application/org.openjdk.jmc.kubernetes/pom.xml new file mode 100644 index 0000000000..8593216ed0 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/pom.xml @@ -0,0 +1,49 @@ + + + + 4.0.0 + + org.openjdk.jmc + missioncontrol.application + ${revision}${changelist} + + org.openjdk.jmc.kubernetes + eclipse-plugin + + ${project.basedir}/../../configuration + + diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnection.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnection.java new file mode 100644 index 0000000000..6aae451116 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnection.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Kantega AS. All rights reserved. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The contents of this file are subject to the terms of either the Universal Permissive License + * v 1.0 as shown at http://oss.oracle.com/licenses/upl + * + * or the following license: + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.openjdk.jmc.kubernetes; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; +import java.util.regex.Pattern; + +import javax.management.InstanceNotFoundException; + +import org.jolokia.client.J4pClient; +import org.jolokia.client.exception.J4pException; +import org.jolokia.client.exception.J4pRemoteException; +import org.jolokia.client.request.J4pResponse; +import org.openjdk.jmc.jolokia.JmcJolokiaJmxConnection; +import org.openjdk.jmc.rjmx.common.ConnectionException; + +/** + * Jolokia based MBeanServerConnector tailored for JMC needs + */ +public class JmcKubernetesJmxConnection extends JmcJolokiaJmxConnection { + + static final Collection DISCONNECT_SIGNS = Arrays.asList(Pattern.compile("Error: pods \".+\" not found")); //$NON-NLS-1$ + + public JmcKubernetesJmxConnection(J4pClient client) throws IOException { + super(client); + } + + @SuppressWarnings("rawtypes") + @Override + protected J4pResponse unwrapException(J4pException e) throws IOException, InstanceNotFoundException { + // recognize signs of disconnect and signal to the application for better + // handling + if (isKnownDisconnectException(e)) { + throw new ConnectionException(e.getMessage()); + } else { + return super.unwrapException(e); + } + } + + private boolean isKnownDisconnectException(J4pException e) { + if (!(e instanceof J4pRemoteException)) { + return false; + } + if (!"io.fabric8.kubernetes.client.KubernetesClientException".equals(((J4pRemoteException) e).getErrorType())) { //$NON-NLS-1$ + return false; + } + return DISCONNECT_SIGNS.stream().anyMatch(pattern -> pattern.matcher(e.getMessage()).matches()); + } + +} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnectionProvider.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnectionProvider.java new file mode 100644 index 0000000000..b74b999f68 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnectionProvider.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Kantega AS. All rights reserved. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The contents of this file are subject to the terms of either the Universal Permissive License + * v 1.0 as shown at http://oss.oracle.com/licenses/upl + * + * or the following license: + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.openjdk.jmc.kubernetes; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.util.Map; + +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorProvider; +import javax.management.remote.JMXServiceURL; + +/** + * This {@code JMXConnectionProvider} handles the "kubernetes" protocol. + */ +public class JmcKubernetesJmxConnectionProvider implements JMXConnectorProvider { + @Override + public JMXConnector newJMXConnector(JMXServiceURL serviceURL, Map environment) throws IOException { + if (!"kubernetes".equals(serviceURL.getProtocol())) { //$NON-NLS-1$ + throw new MalformedURLException("I only serve Kubernetes connections"); //$NON-NLS-1$ + } + return new JmcKubernetesJmxConnector(serviceURL, environment); + } +} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnector.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnector.java new file mode 100644 index 0000000000..0d1d98264a --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnector.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Kantega AS. All rights reserved. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The contents of this file are subject to the terms of either the Universal Permissive License + * v 1.0 as shown at http://oss.oracle.com/licenses/upl + * + * or the following license: + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.openjdk.jmc.kubernetes; + +import java.io.IOException; +import java.util.Map; + +import javax.management.remote.JMXServiceURL; + +import org.jolokia.client.J4pClient; +import org.jolokia.client.jmxadapter.RemoteJmxAdapter; +import org.jolokia.kubernetes.client.KubernetesJmxConnector; + +public class JmcKubernetesJmxConnector extends KubernetesJmxConnector { + + public JmcKubernetesJmxConnector(JMXServiceURL serviceURL, Map environment) { + super(serviceURL, environment); + } + + @Override + protected RemoteJmxAdapter createAdapter(J4pClient client) throws IOException { + return new JmcKubernetesJmxConnection(client); + } +} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesPlugin.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesPlugin.java new file mode 100644 index 0000000000..a0cd408f67 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesPlugin.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Kantega AS. All rights reserved. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The contents of this file are subject to the terms of either the Universal Permissive License + * v 1.0 as shown at http://oss.oracle.com/licenses/upl + * + * or the following license: + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.openjdk.jmc.kubernetes; + +import org.eclipse.core.runtime.Platform; +import org.openjdk.jmc.kubernetes.preferences.KubernetesScanningParameters; +import org.openjdk.jmc.kubernetes.preferences.PreferenceConstants; +import org.openjdk.jmc.ui.MCAbstractUIPlugin; +import org.openjdk.jmc.common.security.ICredentials; +import org.openjdk.jmc.common.security.PersistentCredentials; +import org.openjdk.jmc.common.security.SecurityException; +import org.openjdk.jmc.common.security.SecurityManagerFactory; +import org.openjdk.jmc.ui.misc.DisplayToolkit; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; + +public class JmcKubernetesPlugin extends MCAbstractUIPlugin + implements KubernetesScanningParameters, PreferenceConstants { + + public final static String PLUGIN_ID = "org.openjdk.jmc.kubernetes"; //$NON-NLS-1$ + + // The shared instance. + private static JmcKubernetesPlugin plugin; + + /** + * The constructor. + */ + public JmcKubernetesPlugin() { + super(PLUGIN_ID); + plugin = this; + } + + /** + * @return the shared instance. + */ + public static JmcKubernetesPlugin getDefault() { + return plugin; + } + + private void ensureNeededCredentialsAreUnlocked() { + if (getScanningCredentials() != null && SecurityManagerFactory.getSecurityManager().isLocked()) { + DisplayToolkit.safeAsyncExec(() -> { + try { + SecurityManagerFactory.getSecurityManager().unlock(); + } catch (SecurityException e) { + logError("Error unlocking credentials needed for kubernetes scanning", e);//$NON-NLS-1$ + } + }); + } + } + + @Override + public boolean scanForInstances() { + // If credentials are locked and credentials are required, the scanner thread + // will get hung + // therefore await credentials store to be unlocked before proceeding to scan + return getPreferenceStore().getBoolean(P_SCAN_FOR_INSTANCES) + && (getScanningCredentials() == null || !SecurityManagerFactory.getSecurityManager().isLocked()); + + } + + @Override + public boolean scanAllContexts() { + return getPreferenceStore().getBoolean(P_SCAN_ALL_CONTEXTS); + } + + @Override + public String jolokiaPort() { + return getPreferenceStore().getString(P_JOLOKIA_PORT); + } + + private PersistentCredentials getScanningCredentials() { + String key = getPreferenceStore().getString(P_CREDENTIALS_KEY); + return key == null ? null : new PersistentCredentials(key); + } + + public ICredentials storeCredentials(String username, String password) throws SecurityException { + PersistentCredentials credentials = new PersistentCredentials(username, password, "kubernetes");//$NON-NLS-1$ + getPreferenceStore().setValue(P_CREDENTIALS_KEY, credentials.getExportedId()); + return credentials; + } + + @Override + public String username() throws SecurityException { + final PersistentCredentials cred = getScanningCredentials(); + if (cred == null) { + return "";//$NON-NLS-1$ + } else { + return cred.getUsername(); + } + } + + @Override + public String password() throws SecurityException { + final PersistentCredentials cred = getScanningCredentials(); + if (cred == null) { + return "";//$NON-NLS-1$ + } else { + return cred.getPassword(); + } + } + + @Override + public String jolokiaPath() { + return getPreferenceStore().getString(P_JOLOKIA_PATH); + } + + @Override + public String requireLabel() { + return getPreferenceStore().getString(P_REQUIRE_LABEL); + } + + @Override + public String jolokiaProtocol() { + return getPreferenceStore().getString(P_JOLOKIA_PROTOCOL); + } + + @Override + public void logError(String message, Throwable error) { + Platform.getLog(FrameworkUtil.getBundle(getClass())).error(message, error); + } + + @Override + public void start(BundleContext context) throws Exception { + super.start(context); + this.ensureNeededCredentialsAreUnlocked(); + } + +} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/KubernetesDiscoveryListener.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/KubernetesDiscoveryListener.java new file mode 100644 index 0000000000..bcb2420ac3 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/KubernetesDiscoveryListener.java @@ -0,0 +1,340 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Kantega AS. All rights reserved. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The contents of this file are subject to the terms of either the Universal Permissive License + * v 1.0 as shown at http://oss.oracle.com/licenses/upl + * + * or the following license: + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.openjdk.jmc.kubernetes; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.NoSuchElementException; +import java.util.Properties; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXServiceURL; + +import org.apache.commons.codec.binary.Base64; +import org.jolokia.client.J4pClient; +import org.jolokia.kubernetes.client.KubernetesJmxConnector; +import org.jolokia.server.core.osgi.security.AuthorizationHeaderParser; +import org.jolokia.server.core.util.Base64Util; +import org.openjdk.jmc.jolokia.AbstractCachedDescriptorProvider; +import org.openjdk.jmc.jolokia.JolokiaAgentDescriptor; +import org.openjdk.jmc.jolokia.ServerConnectionDescriptor; +import org.openjdk.jmc.kubernetes.preferences.KubernetesScanningParameters; +import org.openjdk.jmc.common.jvm.JVMDescriptor; +import org.openjdk.jmc.common.security.SecurityException; + +import io.fabric8.kubernetes.api.model.NamedContext; +import io.fabric8.kubernetes.api.model.ObjectMeta; +import io.fabric8.kubernetes.api.model.Pod; +import io.fabric8.kubernetes.api.model.PodList; +import io.fabric8.kubernetes.api.model.Secret; +import io.fabric8.kubernetes.client.Config; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.internal.KubeConfigUtils; +import io.fabric8.kubernetes.client.utils.Utils; +import io.fabric8.kubernetes.client.dsl.AnyNamespaceOperation; +import io.fabric8.kubernetes.client.dsl.PodResource; + +/** + * This class attempts to connect to JVMs in pods running in kubernetes in a background thread. + * Enablement and parameters for the scanning is given by user preferences. + */ +public class KubernetesDiscoveryListener extends AbstractCachedDescriptorProvider { + + private final static Pattern SECRET_PATTERN = Pattern + .compile("\\$\\{kubernetes/secret/(?[^/]+)/(?[^\\}]+)}"); //$NON-NLS-1$ + private final static Pattern ATTRIBUTE_PATTERN = Pattern + .compile("\\$\\{kubernetes/annotation/(?[^/]+)}"); //$NON-NLS-1$ + private final static Set VALID_JOLOKIA_PROTOCOLS = new HashSet<>(Arrays.asList("http", "https")); //$NON-NLS-1$ //$NON-NLS-2$ + + KubernetesScanningParameters settings; + + public KubernetesDiscoveryListener() { + this(JmcKubernetesPlugin.getDefault()); + } + + //Public constructor in order for test plugin to be able to rig tests in an easier manner + public KubernetesDiscoveryListener(KubernetesScanningParameters parameters) { + this.settings = parameters; + } + + public final String getDescription() { + return Messages.KubernetesDiscoveryListener_Description; + } + + @Override + public String getName() { + return "kubernetes"; //$NON-NLS-1$ + } + + boolean notEmpty(String value) { + return value != null && value.length() > 0; + } + + private List contexts; + private long contextsCached = 0L; + + private List allContexts() throws IOException { + final String path = Utils.getSystemPropertyOrEnvVar(Config.KUBERNETES_KUBECONFIG_FILE, + new File(System.getProperty("user.home"), ".kube" + File.separator + "config").toString()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + File configPath = new File(path); + if (contexts != null && contextsCached > configPath.lastModified()) {// the YAML parsing is soo incredibly + // sloow, hence cache context names for + // later + // runs + return contexts; + } + // reload config if kubeconfig has been modified since we cached the config + io.fabric8.kubernetes.api.model.Config config = KubeConfigUtils.parseConfig(configPath); + this.contextsCached = System.currentTimeMillis(); + KubernetesJmxConnector.resetKubernetesConfig(); + return contexts = config.getContexts().stream().map(NamedContext::getName).collect(Collectors.toList()); + } + + @Override + protected Map discoverJvms() { + return scanForJvms(); + } + + Map scanForJvms() { + Map found = new HashMap<>(); + if (!isEnabled()) { + return found; + } + boolean hasScanned = false; + + if (settings.scanAllContexts()) { + try { + for (final String context : allContexts()) { + hasScanned = true; + scanContext(found, settings, context); + } + } catch (IOException e) { + settings.logError(Messages.KubernetesDiscoveryListener_UnableToFindContexts, e); + } + } + if (!hasScanned) {// scan default context + return scanContext(found, settings, null); + } + return found; + } + + private Map scanContext( + Map found, KubernetesScanningParameters parameters, String context) { + try { + scanContextUnsafe(found, parameters, context); + } catch (Exception e) { + parameters.logError(Messages.KubernetesDiscoveryListener_UnableToScan + context, e); + } + return found; + } + + private Map scanContextUnsafe( + Map found, KubernetesScanningParameters parameters, String context) { + String pathLabel = parameters.requireLabel(); + KubernetesClient client = KubernetesJmxConnector.getApiClient(context); + + AnyNamespaceOperation query = client.pods().inAnyNamespace(); + List podList; + boolean hasPathLabel = notEmpty(pathLabel); + if (hasPathLabel) { + podList = query.withLabel(pathLabel).list().getItems(); + } else { + podList = query.list().getItems(); + } + // can consider parallelization for big contexts, however since it is the + // background await the situation a bit + podList.stream().forEach(pod -> scanPod(found, parameters, context, client, pod)); + return found; + } + + private void scanPod( + Map found, KubernetesScanningParameters parameters, String context, + KubernetesClient client, Pod pod) { + + final ObjectMeta metadata = pod.getMetadata(); + HashMap headers = new HashMap<>(); + Map env = new HashMap<>(); + try { + if (notEmpty(parameters.username())) { + if (!notEmpty(parameters.password())) { + throw new IllegalArgumentException(Messages.KubernetesDiscoveryListener_MustProvidePassword); + } + authorize(headers, client, parameters.username(), parameters.password(), metadata.getNamespace(), env); + } + } catch (SecurityException e) { + // skipping authorization if anything fails + } + final StringBuilder url = new StringBuilder("/api/").append(pod.getApiVersion()).append("/namespaces/") + .append(metadata.getNamespace()).append("/pods/"); + // JMX url must be reverse constructed, so that we can connect from the + // resulting node in the JVM browser + final StringBuilder jmxUrl = new StringBuilder("service:jmx:kubernetes:///").append(metadata.getNamespace()) //$NON-NLS-1$ + .append('/'); + + final String protocol = getValueOrAttribute(parameters.jolokiaProtocol(), metadata); + final String podName = metadata.getName(); + if (notEmpty(protocol)) { + if (!VALID_JOLOKIA_PROTOCOLS.contains(protocol)) { + throw new IllegalArgumentException(Messages.KubernetesDiscoveryListener_JolokiaProtocol + protocol + + Messages.KubernetesDiscoveryListener_HttpOrHttps); + } + // a bit clumsy, need to inject protocol _before_ podname in selflink + url.append(protocol).append(':'); + jmxUrl.append(protocol).append(':'); + } + + jmxUrl.append(podName); + url.append(podName); + + final String port = getValueOrAttribute(parameters.jolokiaPort(), metadata); + if (port != null) { + url.append(":").append(port); //$NON-NLS-1$ + jmxUrl.append(':').append(port); + } + + url.append("/proxy"); //$NON-NLS-1$ + + final String path = getValueOrAttribute(parameters.jolokiaPath(), metadata); + + if (!path.startsWith("/")) { //$NON-NLS-1$ + url.append('/'); + jmxUrl.append('/'); + } + url.append(path); + jmxUrl.append(path); + + if (context != null) { + env.put(KubernetesJmxConnector.KUBERNETES_CLIENT_CONTEXT, context); + } + J4pClient jvmClient = KubernetesJmxConnector.probeProxyPath(env, client, url, headers); + if (jvmClient != null) { + JmcKubernetesJmxConnection connection; + try { + connection = new JmcKubernetesJmxConnection(jvmClient); + JVMDescriptor jvmDescriptor = JolokiaAgentDescriptor.attemptToGetJvmInfo(connection); + JMXServiceURL jmxServiceURL = new JMXServiceURL(jmxUrl.toString()); + KubernetesJvmDescriptor descriptor = new KubernetesJvmDescriptor(metadata, jvmDescriptor, jmxServiceURL, + env); + found.put(descriptor.getGUID(), descriptor); + } catch (IOException e) { + parameters.logError(Messages.KubernetesDiscoveryListener_ErrConnectingToJvm, e); + + } + } + } + + private String getValueOrAttribute(String configValue, ObjectMeta metadata) { + if (notEmpty(configValue)) { + Matcher pattern = ATTRIBUTE_PATTERN.matcher(configValue); + if (pattern.find()) { + return metadata.getAnnotations().get(pattern.group("annotationName")); //$NON-NLS-1$ + } else { + return configValue;// the default is to use config value as is + } + } + return null; + } + + private void authorize( + HashMap headers, KubernetesClient client, String username, String password, String namespace, + Map jmxEnv) { + + final Matcher userNameMatcher = SECRET_PATTERN.matcher(username); + String secretName = null; + Map secretValues = null; + if (userNameMatcher.find()) { + secretName = userNameMatcher.group("secretName"); //$NON-NLS-1$ + secretValues = findSecret(client, namespace, secretName); + username = secretValues.get(userNameMatcher.group("itemName")); //$NON-NLS-1$ + } + + final Matcher passwordMatcher = SECRET_PATTERN.matcher(password); + if (passwordMatcher.find()) { + if (!secretName.equals(passwordMatcher.group("secretName"))) { //$NON-NLS-1$ + secretValues = findSecret(client, namespace, passwordMatcher.group("secretName")); //$NON-NLS-1$ + } + password = secretValues.get(passwordMatcher.group("itemName")); //$NON-NLS-1$ + } + + headers.put(AuthorizationHeaderParser.JOLOKIA_ALTERNATE_AUTHORIZATION_HEADER, + "Basic " + Base64Util.encode((username + ":" + password).getBytes())); //$NON-NLS-1$ //$NON-NLS-2$ + jmxEnv.put(JMXConnector.CREDENTIALS, new String[] {username, password}); + + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + private Map findSecret(KubernetesClient client, String namespace, String secretName) { + + for (Secret secret : client.secrets().inNamespace(namespace).list().getItems()) { + if (secret.getMetadata().getName().equals(secretName)) { + if ("kubernetes.io/basic-auth".equals(secret.getType())) { //$NON-NLS-1$ + Map data = secret.getData(); + data.replaceAll((key, value) -> new String(Base64.decodeBase64(value))); + return data; + } else if ("Opaque".equals(secret.getType())) { //$NON-NLS-1$ + for (Entry entry : secret.getData().entrySet()) { + if (entry.getKey().endsWith(".properties")) { //$NON-NLS-1$ + try { + Properties properties = new Properties(); + properties.load(new ByteArrayInputStream(Base64.decodeBase64(entry.getValue()))); + return (Map) properties; + } catch (IOException ignore) { + } + } + } + } + } + + } + throw new NoSuchElementException(Messages.KubernetesDiscoveryListener_CouldNotFindSecret + secretName + + Messages.KubernetesDiscoveryListener_InNamespace + namespace); + + } + + @Override + protected boolean isEnabled() { + return this.settings.scanForInstances(); + } +} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/KubernetesJvmDescriptor.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/KubernetesJvmDescriptor.java new file mode 100644 index 0000000000..a646a0fcc7 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/KubernetesJvmDescriptor.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Kantega AS. All rights reserved. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The contents of this file are subject to the terms of either the Universal Permissive License + * v 1.0 as shown at http://oss.oracle.com/licenses/upl + * + * or the following license: + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.openjdk.jmc.kubernetes; + +import java.io.IOException; +import java.util.Map; + +import javax.management.remote.JMXServiceURL; + +import org.jolokia.kubernetes.client.KubernetesJmxConnector; +import org.openjdk.jmc.jolokia.ServerConnectionDescriptor; +import org.openjdk.jmc.common.jvm.JVMDescriptor; + +import io.fabric8.kubernetes.api.model.ObjectMeta; + +public class KubernetesJvmDescriptor implements ServerConnectionDescriptor { + + private final JVMDescriptor jvmDescriptor; + private final ObjectMeta metadata; + private final Map env; + private final JMXServiceURL connectUrl; + + public KubernetesJvmDescriptor(ObjectMeta metadata, JVMDescriptor jvmDescriptor, JMXServiceURL connectUrl, + Map env) { + this.jvmDescriptor = jvmDescriptor; + this.metadata = metadata; + this.env = env; + this.connectUrl = connectUrl; + } + + @Override + public String getGUID() { + return this.metadata.getName(); + } + + @Override + public String getDisplayName() { + return this.metadata.getName(); + } + + @Override + public JVMDescriptor getJvmInfo() { + return this.jvmDescriptor; + } + + public String getPath() { + String namespace = metadata.getNamespace(); + final Object context = this.env.get(KubernetesJmxConnector.KUBERNETES_CLIENT_CONTEXT); + if (context != null) { + return context + "/" + namespace; //$NON-NLS-1$ + } + return namespace; + } + + @Override + public JMXServiceURL createJMXServiceURL() throws IOException { + return this.connectUrl; + } + + @Override + public Map getEnvironment() { + return this.env; + } + + @Override + public JMXServiceURL serviceUrl() { + return this.connectUrl; + } + +} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/Messages.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/Messages.java new file mode 100644 index 0000000000..9dcff25a26 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/Messages.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Kantega AS. All rights reserved. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The contents of this file are subject to the terms of either the Universal Permissive License + * v 1.0 as shown at http://oss.oracle.com/licenses/upl + * + * or the following license: + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.openjdk.jmc.kubernetes; + +import org.eclipse.osgi.util.NLS; + +public class Messages extends NLS { + private static final String BUNDLE_NAME = "org.openjdk.jmc.kubernetes.messages"; //$NON-NLS-1$ + public static String KubernetesDiscoveryListener_CouldNotFindSecret; + public static String KubernetesDiscoveryListener_Description; + public static String KubernetesDiscoveryListener_ErrConnectingToJvm; + public static String KubernetesDiscoveryListener_HttpOrHttps; + public static String KubernetesDiscoveryListener_InNamespace; + public static String KubernetesDiscoveryListener_JolokiaProtocol; + public static String KubernetesDiscoveryListener_MustProvidePassword; + public static String KubernetesDiscoveryListener_UnableToFindContexts; + public static String KubernetesDiscoveryListener_UnableToScan; + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + private Messages() { + } +} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/messages.properties b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/messages.properties new file mode 100644 index 0000000000..ea0166360a --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/messages.properties @@ -0,0 +1,9 @@ +KubernetesDiscoveryListener_CouldNotFindSecret=Could not find secret named +KubernetesDiscoveryListener_Description=List JVM in kubernetes cluster +KubernetesDiscoveryListener_ErrConnectingToJvm=Error connecting to JVM in pod +KubernetesDiscoveryListener_HttpOrHttps=' must be either 'http' or 'https' +KubernetesDiscoveryListener_InNamespace=\ in namespace +KubernetesDiscoveryListener_JolokiaProtocol=Jolokia protocol ' +KubernetesDiscoveryListener_MustProvidePassword=Password must be specified when username is specified +KubernetesDiscoveryListener_UnableToFindContexts=Unable to find all kubernetes contexts +KubernetesDiscoveryListener_UnableToScan=Unable to scan kubernetes context diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/JmcKubernetesPreferenceForm.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/JmcKubernetesPreferenceForm.java new file mode 100644 index 0000000000..5b2198933a --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/JmcKubernetesPreferenceForm.java @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Kantega AS. All rights reserved. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The contents of this file are subject to the terms of either the Universal Permissive License + * v 1.0 as shown at http://oss.oracle.com/licenses/upl + * + * or the following license: + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.openjdk.jmc.kubernetes.preferences; + +import java.util.Map; +import java.util.WeakHashMap; +import java.util.logging.Level; + +import org.eclipse.jface.preference.BooleanFieldEditor; +import org.eclipse.jface.preference.FieldEditor; +import org.eclipse.jface.preference.FieldEditorPreferencePage; +import org.eclipse.jface.preference.StringFieldEditor; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPreferencePage; +import org.openjdk.jmc.common.security.CredentialsNotAvailableException; +import org.openjdk.jmc.common.security.SecurityException; +import org.openjdk.jmc.kubernetes.JmcKubernetesPlugin; + +/** + * This class represents a preference page that is contributed to the Preferences dialog. By + * subclassing FieldEditorPreferencePage, we can use the field support built into JFace + * that allows us to create a page that is small and knows how to save, restore and apply itself. + *

+ * This page is used to modify preferences only. They are stored in the preference store that + * belongs to the main plug-in class. That way, preferences can be accessed directly via the + * preference store. + */ +public class JmcKubernetesPreferenceForm extends FieldEditorPreferencePage + implements IWorkbenchPreferencePage, PreferenceConstants { + + private Map dependantControls = new WeakHashMap<>(); + private Text userField; + private Text passwordField; + private boolean credentialsDirty; + + public JmcKubernetesPreferenceForm() { + super(GRID); + setPreferenceStore(JmcKubernetesPlugin.getDefault().getPreferenceStore()); + setDescription(Messages.JmcKubernetesPreferenceForm_FormDescription); + } + + /** + * Creates the field editors. Field editors are abstractions of the common GUI blocks needed to + * manipulate various types of preferences. Each field editor knows how to save and restore + * itself. + */ + public void createFieldEditors() { + BooleanFieldEditor mainEnabler = new BooleanFieldEditor(P_SCAN_FOR_INSTANCES, + Messages.JmcKubernetesPreferenceForm_ScanForPods, getFieldEditorParent()) { + @Override + protected void valueChanged(boolean oldValue, boolean newValue) { + super.valueChanged(oldValue, newValue); + enableDependantFields(newValue); + } + }; + addField(mainEnabler); + + BooleanFieldEditor scanContextsEditor = new BooleanFieldEditor(P_SCAN_ALL_CONTEXTS, + Messages.JmcKubernetesPreferenceForm_AllContexts, getFieldEditorParent()); + this.addDependantField(scanContextsEditor, scanContextsEditor.getDescriptionControl(getFieldEditorParent())); + this.addTextField(new StringFieldEditor(P_REQUIRE_LABEL, Messages.JmcKubernetesPreferenceForm_RequireLabel, + getFieldEditorParent()), Messages.JmcKubernetesPreferenceForm_LabelToolTip); + this.addTextField(new StringFieldEditor(P_JOLOKIA_PATH, Messages.JmcKubernetesPreferenceForm_PathLabel, + getFieldEditorParent()), Messages.JmcKubernetesPreferenceForm_PathTooltip); + this.addTextField(new StringFieldEditor(P_JOLOKIA_PORT, Messages.JmcKubernetesPreferenceForm_PortLabel, + getFieldEditorParent()), Messages.JmcKubernetesPreferenceForm_PortTooltip); + this.addTextField(new StringFieldEditor(P_JOLOKIA_PROTOCOL, Messages.JmcKubernetesPreferenceForm_ProtocolLabel, + getFieldEditorParent()), Messages.JmcKubernetesPreferenceForm_ProtocolTooltip); + createCredentialFields(); + // set initial enablement + enableDependantFields(JmcKubernetesPlugin.getDefault().scanForInstances()); + + } + + private void createCredentialFields() { + Label userLabel = new Label(getFieldEditorParent(), SWT.NONE); + userLabel.setText(Messages.JmcKubernetesPreferenceForm_UsernameLabel); + userLabel.setLayoutData(new GridData()); + this.userField = new Text(getFieldEditorParent(), SWT.SINGLE | SWT.BORDER); + userField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + userField.setToolTipText(Messages.JmcKubernetesPreferenceForm_UsernameTooltip); + this.dependantControls.put(userField, null); + + Label passLabel = new Label(getFieldEditorParent(), SWT.NONE); + passLabel.setText(Messages.JmcKubernetesPreferenceForm_PasswordLabel); + passLabel.setLayoutData(new GridData()); + this.passwordField = new Text(getFieldEditorParent(), SWT.PASSWORD | SWT.SINGLE | SWT.BORDER); + passwordField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + + JmcKubernetesPlugin plugin = JmcKubernetesPlugin.getDefault(); + + try { + userField.setText(plugin.username()); + passwordField.setText(plugin.password()); + } catch (CredentialsNotAvailableException ignore) { + // ignore if credentials are not stored + } catch (SecurityException e) { + plugin.getLogger().log(Level.WARNING, "Could not load kubernetes credentials", e); //$NON-NLS-1$ + } + + ModifyListener markCredentials = e -> credentialsDirty = true; + this.userField.addModifyListener(markCredentials); + this.passwordField.addModifyListener(markCredentials); + } + + private void addTextField(StringFieldEditor field, String tooltip) { + Text textControl = field.getTextControl(getFieldEditorParent()); + this.addDependantField(field, textControl); + textControl.setToolTipText(tooltip); + field.getLabelControl(getFieldEditorParent()).setToolTipText(tooltip); + + } + + private void addDependantField(FieldEditor field, Control control) { + this.dependantControls.put(control, null); + addField(field); + } + + private void enableDependantFields(boolean enabled) { + for (Control field : this.dependantControls.keySet()) { + field.setEnabled(enabled); + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.IWorkbenchPreferencePage#init(org.eclipse.ui.IWorkbench) + */ + public void init(IWorkbench workbench) { + } + + @Override + public boolean performOk() { + updateCredentialsIfApplicable(); + return super.performOk(); + } + + private void updateCredentialsIfApplicable() { + if (this.credentialsDirty) { + try { + JmcKubernetesPlugin.getDefault().storeCredentials(userField.getText(), passwordField.getText()); + this.credentialsDirty = false; + } catch (SecurityException ex) { + JmcKubernetesPlugin.getDefault().getLogger().log(Level.WARNING, + "Could not store kubernetes credentials", ex); //$NON-NLS-1$ + } + } + } + +} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/KubernetesScanningParameters.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/KubernetesScanningParameters.java new file mode 100644 index 0000000000..a67697d42b --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/KubernetesScanningParameters.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Kantega AS. All rights reserved. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The contents of this file are subject to the terms of either the Universal Permissive License + * v 1.0 as shown at http://oss.oracle.com/licenses/upl + * + * or the following license: + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.openjdk.jmc.kubernetes.preferences; + +import org.openjdk.jmc.common.security.ICredentials; +import org.openjdk.jmc.common.security.SecurityException; + +public interface KubernetesScanningParameters { + boolean scanForInstances(); + + boolean scanAllContexts(); + + String jolokiaPort(); + + String username() throws SecurityException; + + String password() throws SecurityException; + + String jolokiaPath(); + + String jolokiaProtocol(); + + String requireLabel(); + + ICredentials storeCredentials(String username, String password) throws SecurityException; + + void logError(String message, Throwable error); +} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/Messages.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/Messages.java new file mode 100644 index 0000000000..a718015331 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/Messages.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Kantega AS. All rights reserved. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The contents of this file are subject to the terms of either the Universal Permissive License + * v 1.0 as shown at http://oss.oracle.com/licenses/upl + * + * or the following license: + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.openjdk.jmc.kubernetes.preferences; + +import org.eclipse.osgi.util.NLS; + +public class Messages extends NLS { + private static final String BUNDLE_NAME = "org.openjdk.jmc.kubernetes.preferences.messages"; //$NON-NLS-1$ + public static String JmcKubernetesPreferenceForm_AllContexts; + public static String JmcKubernetesPreferenceForm_FormDescription; + public static String JmcKubernetesPreferenceForm_LabelToolTip; + public static String JmcKubernetesPreferenceForm_PasswordLabel; + public static String JmcKubernetesPreferenceForm_PasswordTooltip; + public static String JmcKubernetesPreferenceForm_PathLabel; + public static String JmcKubernetesPreferenceForm_PathTooltip; + public static String JmcKubernetesPreferenceForm_PortLabel; + public static String JmcKubernetesPreferenceForm_PortTooltip; + public static String JmcKubernetesPreferenceForm_ProtocolLabel; + public static String JmcKubernetesPreferenceForm_ProtocolTooltip; + public static String JmcKubernetesPreferenceForm_RequireLabel; + public static String JmcKubernetesPreferenceForm_ScanForPods; + public static String JmcKubernetesPreferenceForm_UsernameTooltip; + public static String JmcKubernetesPreferenceForm_UsernameLabel; + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + private Messages() { + } +} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/PreferenceConstants.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/PreferenceConstants.java new file mode 100644 index 0000000000..876d65a1b9 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/PreferenceConstants.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Kantega AS. All rights reserved. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The contents of this file are subject to the terms of either the Universal Permissive License + * v 1.0 as shown at http://oss.oracle.com/licenses/upl + * + * or the following license: + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.openjdk.jmc.kubernetes.preferences; + +/** + * Constant definitions for plug-in preferences + */ +public interface PreferenceConstants { + + public static final String P_SCAN_FOR_INSTANCES = "scanForInstances"; //$NON-NLS-1$ + public static final String P_SCAN_ALL_CONTEXTS = "scanAllContexts"; //$NON-NLS-1$ + public static final String P_REQUIRE_LABEL = "requireLabel"; //$NON-NLS-1$ + public static final String P_JOLOKIA_PATH = "jolokiaPath"; //$NON-NLS-1$ + public static final String P_JOLOKIA_PORT = "jolokiaPort"; //$NON-NLS-1$ + public static final String P_JOLOKIA_PROTOCOL = "jolokiaProtocol"; //$NON-NLS-1$ + public static final String P_CREDENTIALS_KEY = "kubernetes.scanning.credentials"; //$NON-NLS-1$ +} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/PreferenceInitializer.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/PreferenceInitializer.java new file mode 100644 index 0000000000..3aac64c2c2 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/PreferenceInitializer.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Kantega AS. All rights reserved. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The contents of this file are subject to the terms of either the Universal Permissive License + * v 1.0 as shown at http://oss.oracle.com/licenses/upl + * + * or the following license: + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.openjdk.jmc.kubernetes.preferences; + +import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer; +import org.eclipse.jface.preference.IPreferenceStore; +import org.openjdk.jmc.kubernetes.JmcKubernetesPlugin; + +/** + * Class used to initialize default preference values. + */ +public class PreferenceInitializer extends AbstractPreferenceInitializer implements PreferenceConstants { + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer# + * initializeDefaultPreferences() + */ + public void initializeDefaultPreferences() { + IPreferenceStore store = JmcKubernetesPlugin.getDefault().getPreferenceStore(); + store.setDefault(P_SCAN_FOR_INSTANCES, false); + store.setDefault(P_SCAN_ALL_CONTEXTS, false); + store.setDefault(P_REQUIRE_LABEL, "jolokia"); //$NON-NLS-1$ + store.setDefault(P_JOLOKIA_PATH, "/jolokia/"); //$NON-NLS-1$ + store.setDefault(P_JOLOKIA_PORT, "8778"); //$NON-NLS-1$ + } + +} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/messages.properties b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/messages.properties new file mode 100644 index 0000000000..44b18279a0 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/messages.properties @@ -0,0 +1,17 @@ +JmcKubernetesPreferenceForm_AllContexts=Scan pods from all locally configured &contexts, if false: only scan the current contexts +JmcKubernetesPreferenceForm_FormDescription=Options that allows you to scan kubernetes for JVMs running Jolokia\ +\n\nThe Kubernetes Proxy API will be used to attempt to access Jolokia like this (parenthesis denote optional components): \ +\n\n$kubernetesBaseUrl/api/v1/namespaces/podNamespace/pods/(jolokiaProtocol:)podname(:jolokiaPort)/proxy/jolokiaPath\n\n +JmcKubernetesPreferenceForm_LabelToolTip=Only attempt to connect to pods with this label set, leave empty to try to scan all pods +JmcKubernetesPreferenceForm_PasswordLabel=Require pass&word +JmcKubernetesPreferenceForm_PasswordTooltip=Password , alternatively use ${kubernetes/secret/secretName/secretItem} where the secret is in the same namespace as the pod and the type is either kubernetes.io/basic-auth or Opaque with java.util.Properties compatible values +JmcKubernetesPreferenceForm_PathLabel=Jolokia &path in pods +JmcKubernetesPreferenceForm_PathTooltip=Use this path for jolokia, or specify ${kubernetes/annotation/annotationName} to be able to to get port name from pod metadata annotation of your choice +JmcKubernetesPreferenceForm_PortLabel=Jolokia p&ort in pods +JmcKubernetesPreferenceForm_PortTooltip=Port to use, leave empty to use default port of Kubernetes proxy, hardcode a port value, or alternatively ${kubernetes/annotation/annotationName} to get port name from pod metadata annotation of your choice +JmcKubernetesPreferenceForm_ProtocolLabel=Jolokia pro&tocol in pods +JmcKubernetesPreferenceForm_ProtocolTooltip=Protocol to use (optional), will infer http if not set, or hardcode to https, or alternatively ${kubernetes/annotation/annotationName} to get port name from pod metadata annotation of your choice +JmcKubernetesPreferenceForm_RequireLabel=Require &label to scan pod +JmcKubernetesPreferenceForm_ScanForPods=&Scan for kubernetes pods with Jolokia support +JmcKubernetesPreferenceForm_UsernameTooltip=Username , alternatively use ${kubernetes/secret/secretName/secretItem} where the secret is in the same namespace as the pod and the type is either kubernetes.io/basic-auth or Opaque with java.util.Properties compatible values +JmcKubernetesPreferenceForm_UsernameLabel=Require &username diff --git a/application/pom.xml b/application/pom.xml index 90dc2c96f2..e40f01a4a3 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -112,6 +112,7 @@ org.openjdk.jmc.updatesite.ide org.openjdk.jmc.updatesite.rcp org.openjdk.jmc.jolokia + org.openjdk.jmc.kubernetes l10n tests diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/.classpath b/application/uitests/org.openjdk.jmc.kubernetes.test/.classpath new file mode 100644 index 0000000000..cb04fe10dc --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/.classpath @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/META-INF/MANIFEST.MF b/application/uitests/org.openjdk.jmc.kubernetes.test/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..46a9f1b9fc --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/META-INF/MANIFEST.MF @@ -0,0 +1,20 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: RJMX Test +Bundle-SymbolicName: org.openjdk.jmc.kubernetes.test;singleton:=true +Bundle-Version: 9.1.0.qualifier +Import-Package: io.fabric8.kubernetes.api.model +Bundle-Vendor: Oracle Corporation +Bundle-RequiredExecutionEnvironment: JavaSE-17 +Bundle-ActivationPolicy: lazy +Require-Bundle: org.junit, + org.openjdk.jmc.jolokia, + org.eclipse.osgi;bundle-version="3.16.200", + org.eclipse.ui.workbench, + org.openjdk.jmc.rjmx;bundle-version="9.0.0", + org.eclipse.ui, + org.hamcrest;bundle-version="2.1", + org.openjdk.jmc.kubernetes;bundle-version="9.0.0", + com.github.tomakehurst.wiremock-standalone;bundle-version="2.27.2", + org.awaitility;bundle-version="4.0.0" +Automatic-Module-Name: org.openjdk.jmc.jolokia.test diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/build.properties b/application/uitests/org.openjdk.jmc.kubernetes.test/build.properties new file mode 100644 index 0000000000..b4608c8522 --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/build.properties @@ -0,0 +1,36 @@ +# +# Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2024, Kantega AS. All rights reserved. +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# The contents of this file are subject to the terms of either the Universal Permissive License +# v 1.0 as shown at http://oss.oracle.com/licenses/upl +# +# or the following license: +# +# Redistribution and use in source and binary forms, with or without modification, are permitted +# provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions +# and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of +# conditions and the following disclaimer in the documentation and/or other materials provided with +# the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to +# endorse or promote products derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY +# WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +source.. = src/test/java/,\ + src/test/resources/ +output.. = target/test-classes/ +bin.includes = META-INF/,. diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/pom.xml b/application/uitests/org.openjdk.jmc.kubernetes.test/pom.xml new file mode 100644 index 0000000000..652ddbc5a6 --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/pom.xml @@ -0,0 +1,58 @@ + + + + 4.0.0 + + org.openjdk.jmc + missioncontrol.application.uitests + ${revision}${changelist} + + org.openjdk.jmc.kubernetes.test + eclipse-test-plugin + + + ${project.basedir}/../../../configuration + + + + + org.awaitility + awaitility + 4.0.0 + + + diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/java/org/openjdk/jmc/kubernetes/JmcKubernetesTest.java b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/java/org/openjdk/jmc/kubernetes/JmcKubernetesTest.java new file mode 100644 index 0000000000..46d3c5ea8b --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/java/org/openjdk/jmc/kubernetes/JmcKubernetesTest.java @@ -0,0 +1,313 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Kantega AS. All rights reserved. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The contents of this file are subject to the terms of either the Universal Permissive License + * v 1.0 as shown at http://oss.oracle.com/licenses/upl + * + * or the following license: + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.openjdk.jmc.kubernetes; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.MalformedURLException; +import java.time.Duration; +import java.util.Base64; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import javax.management.AttributeNotFoundException; +import javax.management.InstanceNotFoundException; +import javax.management.InvalidAttributeValueException; +import javax.management.MBeanException; +import javax.management.MBeanServerConnection; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; +import javax.management.ReflectionException; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXServiceURL; + +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.HttpClients; +import org.awaitility.Awaitility; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; +import org.openjdk.jmc.common.IDescribable; +import org.openjdk.jmc.common.jvm.JVMType; +import org.openjdk.jmc.common.security.ICredentials; +import org.openjdk.jmc.common.security.InMemoryCredentials; +import org.openjdk.jmc.common.security.SecurityException; +import org.openjdk.jmc.kubernetes.preferences.KubernetesScanningParameters; +import org.openjdk.jmc.rjmx.common.IConnectionDescriptor; +import org.openjdk.jmc.rjmx.common.IServerDescriptor; +import org.openjdk.jmc.rjmx.descriptorprovider.IDescriptorListener; + +import com.github.tomakehurst.wiremock.client.WireMock; +import com.github.tomakehurst.wiremock.core.WireMockConfiguration; +import com.github.tomakehurst.wiremock.extension.responsetemplating.ResponseTemplateTransformer; +import com.github.tomakehurst.wiremock.junit.WireMockRule; + +/** + * Test that JMX connections done with JmcKubernetesJmxConnectionProvider are functional. In order + * to be able to test this in a contained environment, the kubernetes API is mocked with wiremock. + */ +@SuppressWarnings("restriction") +public class JmcKubernetesTest { + + static class TestParameters implements KubernetesScanningParameters { + public boolean scanForInstances, scanAllContexts; + public String jolokiaPort, jolokiaPath = "/jolokia/", jolokiaProtocol, requireLabel; + public InMemoryCredentials credentials; + + @Override + public boolean scanForInstances() { + return this.scanForInstances; + } + + @Override + public boolean scanAllContexts() { + return this.scanAllContexts; + } + + @Override + public String jolokiaPort() { + return this.jolokiaPort; + } + + @Override + public String username() throws SecurityException { + return this.credentials == null ? null : this.credentials.getUsername(); + } + + @Override + public String password() throws SecurityException { + return this.credentials == null ? null : this.credentials.getPassword(); + } + + @Override + public String jolokiaPath() { + return this.jolokiaPath; + } + + @Override + public String jolokiaProtocol() { + return this.jolokiaProtocol; + } + + @Override + public String requireLabel() { + return this.requireLabel; + } + + @Override + public ICredentials storeCredentials(String username, String password) throws SecurityException { + return this.credentials = new InMemoryCredentials(username, password); + } + + @Override + public void logError(String message, Throwable error) { + System.out.println(message); + error.printStackTrace(System.out); + } + } + + @ClassRule + public static WireMockRule wiremock = new WireMockRule( + WireMockConfiguration.options().extensions(new ResponseTemplateTransformer(false)).port(0)); + + static final String jolokiaUrl = "service:jmx:kubernetes:///ns1/pod-abcdef/jolokia"; + + private static MBeanServerConnection jolokiaConnection; + + @BeforeClass + public static void connect() throws Exception { + CloseableHttpResponse configResponse = HttpClients.createDefault() + .execute(new HttpGet(wiremock.baseUrl() + "/mock-kube-config.yml")); + Assert.assertEquals(configResponse.getStatusLine().getStatusCode(), 200); + File configFile = File.createTempFile("mock-kube-config", ".yml"); + configResponse.getEntity().writeTo(new FileOutputStream(configFile)); + // we set this so the KubernetesDiscoveryListener will work + //Setting taken from: https://github.com/fabric8io/kubernetes-client/blob/77a65f7d40f31a5dc37492cd9de3c317c2702fb4/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/Config.java#L120, unlikely to change + System.setProperty("kubeconfig", configFile.getAbsolutePath()); + jolokiaConnection = getKubernetesMBeanConnector(); + } + + @Test + public void testExecuteOperation() throws InstanceNotFoundException, MalformedObjectNameException, MBeanException, + ReflectionException, MalformedURLException, IOException { + jolokiaConnection.invoke(new ObjectName("java.lang:type=Memory"), "gc", new Object[0], new String[0]); + } + + @Test + public void testReadAttribute() + throws InstanceNotFoundException, AttributeNotFoundException, InvalidAttributeValueException, + MalformedObjectNameException, MBeanException, ReflectionException, MalformedURLException, IOException { + MBeanServerConnection jmxConnection = jolokiaConnection; + assertOneSingleAttribute(jmxConnection); + + } + + private void assertOneSingleAttribute(MBeanServerConnection jmxConnection) throws MalformedObjectNameException, + MBeanException, AttributeNotFoundException, InstanceNotFoundException, ReflectionException, IOException { + ObjectName objectName = new ObjectName("java.lang:type=Memory"); + String attribute = "Verbose"; + Assert.assertEquals(false, jmxConnection.getAttribute(objectName, attribute)); + } + + @Before + public void reset() { + wiremock.resetAll(); + wiremock.resetRequests(); + } + + private static MBeanServerConnection getKubernetesMBeanConnector() throws IOException, MalformedURLException { + JMXConnector connector = new JmcKubernetesJmxConnectionProvider().newJMXConnector(new JMXServiceURL(jolokiaUrl), + Collections.emptyMap()); + connector.connect(); + MBeanServerConnection connection = connector.getMBeanServerConnection(); + return connection; + } + + @Test + public void testDiscoverWithMostlyDefaultSettings() throws Exception { + + TestParameters parameters = new TestParameters(); + // Set config so that scanning takes place + parameters.scanForInstances = true; + testThatJvmIsFound(parameters); + } + + @Test + public void testDiscoverWithPathFromAnnotation() throws Exception { + TestParameters parameters = new TestParameters(); + parameters.scanForInstances = true; + parameters.jolokiaPath = "${kubernetes/annotation/jolokiaPath}"; + testThatJvmIsFound(parameters); + } + + @Test + public void testDiscoverWithPortFromAnnotation() throws Exception { + TestParameters parameters = new TestParameters(); + parameters.scanForInstances = true; + parameters.jolokiaPort = "${kubernetes/annotation/jolokiaPort}"; + + testThatJvmIsFound(parameters); + } + + @Test + public void testDiscoverWithBasicAuthFromSecret() throws Exception { + TestParameters parameters = new TestParameters(); + parameters.scanForInstances = true; + parameters.credentials = new InMemoryCredentials("${kubernetes/secret/jolokia-auth/username}", + "${kubernetes/secret/jolokia-auth/password}"); + + testThatJvmIsFound(parameters); + // Verify that the expected authorization was picked up + WireMock.verify(WireMock + .postRequestedFor(WireMock.urlPathMatching("/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*")) + .withHeader("X-jolokia-authorization", + WireMock.equalTo("Basic " + Base64.getEncoder().encodeToString("admin:admin".getBytes())))); + } + + @Test + public void testDiscoverWithAuthFromProperties() throws Exception { + + TestParameters parameters = new TestParameters(); + parameters.scanForInstances = true; + parameters.credentials = new InMemoryCredentials("${kubernetes/secret/jolokia-properties/user}", + "${kubernetes/secret/jolokia-properties/password}"); + + testThatJvmIsFound(parameters); + // Verify that the expected authorization was picked up + WireMock.verify(WireMock + .postRequestedFor(WireMock.urlPathMatching("/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*")) + .withHeader("X-jolokia-authorization", + WireMock.equalTo("Basic " + Base64.getEncoder().encodeToString("admin:secret".getBytes())))); + } + + @Test + public void testDiscoverWithAuthDirectlyFromSettings() throws Exception { + + TestParameters parameters = new TestParameters(); + parameters.scanForInstances = true; + parameters.credentials = new InMemoryCredentials("user", "***"); + testThatJvmIsFound(parameters); + // Verify that the expected authorization was picked up + WireMock.verify(WireMock + .postRequestedFor(WireMock.urlPathMatching("/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*")) + .withHeader("X-jolokia-authorization", + WireMock.equalTo("Basic " + Base64.getEncoder().encodeToString("user:***".getBytes())))); + } + + private void testThatJvmIsFound(TestParameters parameters) throws Exception { + + final KubernetesDiscoveryListener scanner = new KubernetesDiscoveryListener(parameters); + final Map foundVms = new HashMap<>(); + IDescriptorListener descriptorListener = new IDescriptorListener() { + public void onDescriptorDetected( + IServerDescriptor serverDescriptor, String path, JMXServiceURL url, + IConnectionDescriptor connectionDescriptor, IDescribable provider) { + foundVms.put(serverDescriptor.getGUID(), serverDescriptor); + } + + public void onDescriptorRemoved(String descriptorId) { + foundVms.remove(descriptorId); + } + }; + scanner.addDescriptorListener(descriptorListener); + + try { + // Test that at least one VM (the one running the test was discovered) + + Awaitility.await().atMost(Duration.ofSeconds(5)).until(() -> !foundVms.isEmpty()); + IServerDescriptor descriptor = foundVms.get("pod-abcdef"); + Assert.assertNotNull(descriptor); + Assert.assertEquals( + "[JVMDescriptor] Java command: /Users/marska/Downloads/hawtio-app-2.9.1.jar --port 9090 PID: 88774", + descriptor.getJvmInfo().toString()); + Assert.assertEquals(JVMType.HOTSPOT, descriptor.getJvmInfo().getJvmType()); + Assert.assertEquals("18.0.1", descriptor.getJvmInfo().getJavaVersion()); + Assert.assertTrue(descriptor instanceof IConnectionDescriptor); + IConnectionDescriptor connectDescriptor = (IConnectionDescriptor) descriptor; + JMXConnector connector = new JmcKubernetesJmxConnectionProvider() + .newJMXConnector(connectDescriptor.createJMXServiceURL(), connectDescriptor.getEnvironment()); + connector.connect(); + assertOneSingleAttribute(connector.getMBeanServerConnection()); + + } finally { + // Tell scanner thread to exit + scanner.shutdown(); + } + } +} diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-attribute.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-attribute.json new file mode 100644 index 0000000000..4230121a7e --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-attribute.json @@ -0,0 +1,10 @@ +{ + "request" : { + "attribute" : "Verbose", + "mbean" : "java.lang:type=Memory", + "type" : "read" + }, + "status" : 200, + "timestamp" : 1658563869, + "value" : false +} diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-basic-auth-secret.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-basic-auth-secret.json new file mode 100644 index 0000000000..b5bb351b7e --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-basic-auth-secret.json @@ -0,0 +1,19 @@ +{ + "apiVersion": "v1", + "data": { + "password": "YWRtaW4=", + "username": "YWRtaW4=" + }, + "kind": "Secret", + "metadata": { + "annotations": { + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Secret\",\"metadata\":{\"annotations\":{},\"name\":\"jolokia-auth\",\"namespace\":\"jfr\"},\"stringData\":{\"password\":\"admin\",\"username\":\"admin\"},\"type\":\"kubernetes.io/basic-auth\"}\n" + }, + "creationTimestamp": "2022-05-20T13:59:12Z", + "name": "jolokia-auth", + "namespace": "jfr", + "resourceVersion": "130", + "uid": "e1563217-ef08-481d-b2e3-233fa3040b56" + }, + "type": "kubernetes.io/basic-auth" +} diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-exec.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-exec.json new file mode 100644 index 0000000000..5bd35a69f2 --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-exec.json @@ -0,0 +1,10 @@ +{ + "request" : { + "mbean" : "java.lang:type=Memory", + "operation" : "gc()", + "type" : "exec" + }, + "status" : 200, + "timestamp" : 1658567003, + "value" : null +} diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-properties-secret.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-properties-secret.json new file mode 100644 index 0000000000..e12bbd16c1 --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-properties-secret.json @@ -0,0 +1,15 @@ +{ + "apiVersion": "v1", + "data": { + "jolokia.properties": "aG9zdD0wLjAuMC4wCnBhc3N3b3JkPXNlY3JldAp1c2VyPWFkbWluCnBvcnQ9ODc3OApkaXNjb3ZlcnlFbmFibGVkPXRydWUKZGlzY292ZXJ5QWdlbnRVcmw9aHR0cDovLyR7aG9zdH06ODc3OC9qb2xva2lhLwpwb2xpY3lMb2NhdGlvbj1jbGFzc3BhdGg6L0JPT1QtSU5GL2NsYXNzZXMvam9sb2tpYS1hY2Nlc3MueG1s" + }, + "kind": "Secret", + "metadata": { + "creationTimestamp": "2022-05-20T13:59:12Z", + "name": "jolokia-properties", + "namespace": "jfr", + "resourceVersion": "147", + "uid": "025bf17a-cff5-46e6-8499-dd9d6de43fa9" + }, + "type": "Opaque" +} diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-version.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-version.json new file mode 100644 index 0000000000..6299d0407a --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-version.json @@ -0,0 +1,41 @@ +{ + "request": { + "type": "version" + }, + "value": { + "agent": "1.7.1", + "protocol": "7.2", + "config": { + "listenForHttpService": "true", + "authIgnoreCerts": "false", + "agentId": "192.168.1.104-28660-5bd82fed-servlet", + "debug": "false", + "agentType": "servlet", + "policyLocation": "classpath:\/jolokia-access.xml", + "agentContext": "\/jolokia", + "serializeException": "false", + "mimeType": "text\/plain", + "dispatcherClasses": "org.jolokia.http.Jsr160ProxyNotEnabledByDefaultAnymoreDispatcher", + "authMode": "basic", + "authMatch": "any", + "streaming": "true", + "canonicalNaming": "true", + "historyMaxEntries": "10", + "allowErrorDetails": "false", + "allowDnsReverseLookup": "true", + "realm": "jolokia", + "includeStackTrace": "false", + "restrictorClass": "io.hawt.system.RBACRestrictor", + "mbeanQualifier": "qualifier=hawtio", + "useRestrictorService": "false", + "debugMaxEntries": "100" + }, + "info": { + "product": "jetty", + "vendor": "Eclipse", + "version": "9.4.z-SNAPSHOT" + } + }, + "timestamp": 1658556959, + "status": 200 +} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/memory-list.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/memory-list.json new file mode 100644 index 0000000000..46d117f2e4 --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/memory-list.json @@ -0,0 +1,46 @@ +{ + "request" : { + "path" : "java.lang/type=Memory", + "type" : "list" + }, + "status" : 200, + "timestamp" : 1658566168, + "value" : { + "attr" : { + "HeapMemoryUsage" : { + "desc" : "HeapMemoryUsage", + "rw" : false, + "type" : "javax.management.openmbean.CompositeData" + }, + "NonHeapMemoryUsage" : { + "desc" : "NonHeapMemoryUsage", + "rw" : false, + "type" : "javax.management.openmbean.CompositeData" + }, + "ObjectName" : { + "desc" : "ObjectName", + "rw" : false, + "type" : "javax.management.ObjectName" + }, + "ObjectPendingFinalizationCount" : { + "desc" : "ObjectPendingFinalizationCount", + "rw" : false, + "type" : "int" + }, + "Verbose" : { + "desc" : "Verbose", + "rw" : true, + "type" : "boolean" + } + }, + "class" : "sun.management.MemoryImpl", + "desc" : "Information on the management interface of the MBean", + "op" : { + "gc" : { + "args" : [], + "desc" : "gc", + "ret" : "void" + } + } + } +} diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/mock-kube-config.yml b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/mock-kube-config.yml new file mode 100644 index 0000000000..f222f471a7 --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/mock-kube-config.yml @@ -0,0 +1,17 @@ +apiVersion: v1 +clusters: +- cluster: + server: {{request.baseUrl}}/ + name: test +contexts: +- context: + cluster: test + user: test + name: test +current-context: test +kind: Config +preferences: {} +users: +- name: test + user: + token: foobar diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/pod.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/pod.json new file mode 100644 index 0000000000..77a61a3e32 --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/pod.json @@ -0,0 +1,11 @@ +{ + "metadata": { + "clusterName": "test", + "labels": { + "jolokia": true + }, + "name": "pod-abcdef", + "namespace": "ns1", + "selfLink": "/api/v1/namespaces/ns1/pods/pod-abcdef" + } +} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/pods.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/pods.json new file mode 100644 index 0000000000..114edd3640 --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/pods.json @@ -0,0 +1,19 @@ +{ + "items": [ + { + "metadata": { + "clusterName": "test", + "labels": { + "jolokia": true + }, + "annotations": { + "jolokiaPort": 8778, + "jolokiaPath": "jolokia" + }, + "name": "pod-abcdef", + "namespace": "ns1", + "selfLink": "/api/v1/namespaces/ns1/pods/pod-abcdef" + } + } + ] +} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/runtime-list.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/runtime-list.json new file mode 100644 index 0000000000..c096d78c52 --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/runtime-list.json @@ -0,0 +1,104 @@ +{ + "request" : { + "path" : "java.lang/type=Runtime", + "type" : "list" + }, + "status" : 200, + "timestamp" : 1658564737, + "value" : { + "attr" : { + "BootClassPath" : { + "desc" : "BootClassPath", + "rw" : false, + "type" : "java.lang.String" + }, + "BootClassPathSupported" : { + "desc" : "BootClassPathSupported", + "rw" : false, + "type" : "boolean" + }, + "ClassPath" : { + "desc" : "ClassPath", + "rw" : false, + "type" : "java.lang.String" + }, + "InputArguments" : { + "desc" : "InputArguments", + "rw" : false, + "type" : "[Ljava.lang.String;" + }, + "LibraryPath" : { + "desc" : "LibraryPath", + "rw" : false, + "type" : "java.lang.String" + }, + "ManagementSpecVersion" : { + "desc" : "ManagementSpecVersion", + "rw" : false, + "type" : "java.lang.String" + }, + "Name" : { + "desc" : "Name", + "rw" : false, + "type" : "java.lang.String" + }, + "ObjectName" : { + "desc" : "ObjectName", + "rw" : false, + "type" : "javax.management.ObjectName" + }, + "Pid" : { + "desc" : "Pid", + "rw" : false, + "type" : "long" + }, + "SpecName" : { + "desc" : "SpecName", + "rw" : false, + "type" : "java.lang.String" + }, + "SpecVendor" : { + "desc" : "SpecVendor", + "rw" : false, + "type" : "java.lang.String" + }, + "SpecVersion" : { + "desc" : "SpecVersion", + "rw" : false, + "type" : "java.lang.String" + }, + "StartTime" : { + "desc" : "StartTime", + "rw" : false, + "type" : "long" + }, + "SystemProperties" : { + "desc" : "SystemProperties", + "rw" : false, + "type" : "javax.management.openmbean.TabularData" + }, + "Uptime" : { + "desc" : "Uptime", + "rw" : false, + "type" : "long" + }, + "VmName" : { + "desc" : "VmName", + "rw" : false, + "type" : "java.lang.String" + }, + "VmVendor" : { + "desc" : "VmVendor", + "rw" : false, + "type" : "java.lang.String" + }, + "VmVersion" : { + "desc" : "VmVersion", + "rw" : false, + "type" : "java.lang.String" + } + }, + "class" : "sun.management.RuntimeImpl", + "desc" : "Information on the management interface of the MBean" + } +} diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/secrets.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/secrets.json new file mode 100644 index 0000000000..ac68f94b51 --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/secrets.json @@ -0,0 +1,43 @@ +{ + "apiVersion": "v1", + "items": [ + { + "apiVersion": "v1", + "data": { + "password": "YWRtaW4=", + "username": "YWRtaW4=" + }, + "kind": "Secret", + "metadata": { + "annotations": { + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Secret\",\"metadata\":{\"annotations\":{},\"name\":\"jolokia-auth\",\"namespace\":\"ns1\"},\"stringData\":{\"password\":\"admin\",\"username\":\"admin\"},\"type\":\"kubernetes.io/basic-auth\"}\n" + }, + "creationTimestamp": "2022-05-20T13:59:12Z", + "name": "jolokia-auth", + "namespace": "ns1", + "resourceVersion": "130", + "uid": "e1563217-ef08-481d-b2e3-233fa3040b56" + }, + "type": "kubernetes.io/basic-auth" + }, + { + "apiVersion": "v1", + "data": { + "jolokia.properties": "aG9zdD0wLjAuMC4wCnBhc3N3b3JkPXNlY3JldAp1c2VyPWFkbWluCnBvcnQ9ODc3OApkaXNjb3ZlcnlFbmFibGVkPXRydWUKZGlzY292ZXJ5QWdlbnRVcmw9aHR0cDovLyR7aG9zdH06ODc3OC9qb2xva2lhLwpwb2xpY3lMb2NhdGlvbj1jbGFzc3BhdGg6L0JPT1QtSU5GL2NsYXNzZXMvam9sb2tpYS1hY2Nlc3MueG1s" + }, + "kind": "Secret", + "metadata": { + "creationTimestamp": "2022-05-20T13:59:12Z", + "name": "jolokia-properties", + "namespace": "ns1", + "resourceVersion": "147", + "uid": "025bf17a-cff5-46e6-8499-dd9d6de43fa9" + }, + "type": "Opaque" + } + ], + "kind": "List", + "metadata": { + "resourceVersion": "" + } +} diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/system-attributes.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/system-attributes.json new file mode 100644 index 0000000000..dc2ff55433 --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/system-attributes.json @@ -0,0 +1,101 @@ +[ + { + "request" : { + "attribute" : "Pid", + "mbean" : "java.lang:type=Runtime", + "type" : "read" + }, + "status" : 200, + "timestamp" : 1658586915, + "value" : 88774 + }, + { + "request" : { + "attribute" : "Name", + "mbean" : "java.lang:type=Runtime", + "type" : "read" + }, + "status" : 200, + "timestamp" : 1658586915, + "value" : "88774@Martins-MacBook-Pro-2.local" + }, + { + "request" : { + "attribute" : "InputArguments", + "mbean" : "java.lang:type=Runtime", + "type" : "read" + }, + "status" : 200, + "timestamp" : 1658586915, + "value" : [] + }, + { + "request" : { + "attribute" : "SystemProperties", + "mbean" : "java.lang:type=Runtime", + "type" : "read" + }, + "status" : 200, + "timestamp" : 1658586915, + "value" : { + "apple.awt.application.name" : "App", + "file.encoding" : "UTF-8", + "file.separator" : "/", + "ftp.nonProxyHosts" : "local|*.local|169.254/16|*.169.254/16", + "hawtio.authenticationEnabled" : "false", + "hawtio.redirect.scheme" : "http", + "hawtio.url" : "http://localhost:9090/hawtio", + "http.nonProxyHosts" : "local|*.local|169.254/16|*.169.254/16", + "java.class.path" : "/Users/marska/Downloads/hawtio-app-2.9.1.jar", + "java.class.version" : "62.0", + "java.home" : "/Library/Java/JavaVirtualMachines/temurin-18.jdk/Contents/Home", + "java.io.tmpdir" : "/var/folders/1f/cdm0073x1mj1swnhtw181_4m0000gn/T/", + "java.library.path" : "/Users/marska/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:.", + "java.runtime.name" : "OpenJDK Runtime Environment", + "java.runtime.version" : "18.0.1+10", + "java.specification.name" : "Java Platform API Specification", + "java.specification.vendor" : "Oracle Corporation", + "java.specification.version" : "18", + "java.vendor" : "Eclipse Adoptium", + "java.vendor.url" : "https://adoptium.net/", + "java.vendor.url.bug" : "https://github.com/adoptium/adoptium-support/issues", + "java.vendor.version" : "Temurin-18.0.1+10", + "java.version" : "18.0.1", + "java.version.date" : "2022-04-19", + "java.vm.compressedOopsMode" : "Zero based", + "java.vm.info" : "mixed mode, sharing", + "java.vm.name" : "OpenJDK 64-Bit Server VM", + "java.vm.specification.name" : "Java Virtual Machine Specification", + "java.vm.specification.vendor" : "Oracle Corporation", + "java.vm.specification.version" : "18", + "java.vm.vendor" : "Eclipse Adoptium", + "java.vm.version" : "18.0.1+10", + "jdk.debug" : "release", + "line.separator" : "\n", + "native.encoding" : "UTF-8", + "org.eclipse.jetty.util.log.class" : "org.eclipse.jetty.util.log.Slf4jLog", + "os.arch" : "x86_64", + "os.name" : "Mac OS X", + "os.version" : "11.6.6", + "path.separator" : ":", + "socksNonProxyHosts" : "local|*.local|169.254/16|*.169.254/16", + "sun.arch.data.model" : "64", + "sun.awt.enableExtraMouseButtons" : "true", + "sun.boot.library.path" : "/Library/Java/JavaVirtualMachines/temurin-18.jdk/Contents/Home/lib", + "sun.cpu.endian" : "little", + "sun.io.unicode.encoding" : "UnicodeBig", + "sun.java.command" : "/Users/marska/Downloads/hawtio-app-2.9.1.jar --port 9090", + "sun.java.launcher" : "SUN_STANDARD", + "sun.jnu.encoding" : "UTF-8", + "sun.management.compiler" : "HotSpot 64-Bit Tiered Compilers", + "sun.stderr.encoding" : "UTF-8", + "sun.stdout.encoding" : "UTF-8", + "user.country" : "NO", + "user.dir" : "/Users/marska/Documents/dev/jmc/jmc8/releng/third-party", + "user.home" : "/Users/marska", + "user.language" : "nb", + "user.name" : "marska", + "user.timezone" : "Asia/Taipei" + } + } +] diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-exec.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-exec.json new file mode 100644 index 0000000000..6bb03ececf --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-exec.json @@ -0,0 +1,15 @@ +{ + "request": { + "method": "POST", + "urlPath": "/api/v1/namespaces/ns1/pods/pod-abcdef/proxy/jolokia", + "bodyPatterns": [ + { + "equalToJson": "{\"operation\":\"gc()\",\"mbean\":\"java.lang:type=Memory\",\"type\":\"EXEC\"}" + } + ] + }, + "response": { + "status": 200, + "bodyFileName": "jolokia-exec.json" + } +} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-list-memory.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-list-memory.json new file mode 100644 index 0000000000..e4170948cb --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-list-memory.json @@ -0,0 +1,15 @@ +{ + "request": { + "method": "POST", + "urlPathPattern": "/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*", + "bodyPatterns": [ + { + "equalToJson": "{\"path\":\"java.lang/type=Memory\",\"type\":\"LIST\"}" + } + ] + }, + "response": { + "status": 200, + "bodyFileName": "memory-list.json" + } +} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-list-runtime.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-list-runtime.json new file mode 100644 index 0000000000..f936e7eb0f --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-list-runtime.json @@ -0,0 +1,15 @@ +{ + "request": { + "method": "POST", + "urlPathPattern": "/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*", + "bodyPatterns": [ + { + "equalToJson": "{\"path\":\"java.lang/type=Runtime\",\"type\":\"LIST\"}" + } + ] + }, + "response": { + "status": 200, + "bodyFileName": "runtime-list.json" + } +} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-probe.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-probe.json new file mode 100644 index 0000000000..5b26da423d --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-probe.json @@ -0,0 +1,15 @@ +{ + "request": { + "method": "POST", + "urlPathPattern": "/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*", + "bodyPatterns": [ + { + "equalToJson": "{\"type\":\"version\"}" + } + ] + }, + "response": { + "status": 200, + "bodyFileName": "jolokia-version.json" + } +} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-probe2.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-probe2.json new file mode 100644 index 0000000000..98670016fd --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-probe2.json @@ -0,0 +1,15 @@ +{ + "request": { + "method": "POST", + "urlPathPattern": "/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*", + "bodyPatterns": [ + { + "equalToJson": "{\"type\":\"VERSION\"}" + } + ] + }, + "response": { + "status": 200, + "bodyFileName": "jolokia-version.json" + } +} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-read-attribute.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-read-attribute.json new file mode 100644 index 0000000000..1584e3b4a3 --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-read-attribute.json @@ -0,0 +1,15 @@ +{ + "request": { + "method": "POST", + "urlPathPattern": "/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*", + "bodyPatterns": [ + { + "equalToJson": "{\"attribute\":\"Verbose\",\"mbean\":\"java.lang:type=Memory\",\"type\":\"READ\"}" + } + ] + }, + "response": { + "status": 200, + "bodyFileName": "jolokia-attribute.json" + } +} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jvm-properties.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jvm-properties.json new file mode 100644 index 0000000000..5b26da423d --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jvm-properties.json @@ -0,0 +1,15 @@ +{ + "request": { + "method": "POST", + "urlPathPattern": "/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*", + "bodyPatterns": [ + { + "equalToJson": "{\"type\":\"version\"}" + } + ] + }, + "response": { + "status": 200, + "bodyFileName": "jolokia-version.json" + } +} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/kube-config.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/kube-config.json new file mode 100644 index 0000000000..418a887637 --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/kube-config.json @@ -0,0 +1,13 @@ +{ + "request": { + "method": "GET", + "url": "/mock-kube-config.yml" + }, + "response": { + "status": 200, + "bodyFileName": "mock-kube-config.yml", + "transformers": [ + "response-template" + ] + } +} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/pod.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/pod.json new file mode 100644 index 0000000000..ebcbd7c5e3 --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/pod.json @@ -0,0 +1,10 @@ +{ + "request": { + "method": "GET", + "url": "/api/v1/namespaces/ns1/pods/pod-abcdef" + }, + "response": { + "status": 200, + "bodyFileName": "pod.json" + } +} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/pods.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/pods.json new file mode 100644 index 0000000000..061fb0f53b --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/pods.json @@ -0,0 +1,10 @@ +{ + "request": { + "method": "GET", + "urlPath": "/api/v1/pods" + }, + "response": { + "status": 200, + "bodyFileName": "pods.json" + } +} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/secrets.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/secrets.json new file mode 100644 index 0000000000..113bb91e1f --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/secrets.json @@ -0,0 +1,10 @@ +{ + "request": { + "method": "GET", + "url": "/api/v1/namespaces/ns1/secrets" + }, + "response": { + "status": 200, + "bodyFileName": "secrets.json" + } +} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/system-attributes.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/system-attributes.json new file mode 100644 index 0000000000..e3d1147b2f --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/system-attributes.json @@ -0,0 +1,15 @@ +{ + "request": { + "method": "POST", + "urlPathPattern": "/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*", + "bodyPatterns": [ + { + "equalToJson": "[{\"attribute\":\"Pid\",\"mbean\":\"java.lang:type=Runtime\",\"type\":\"READ\"},{\"attribute\":\"Name\",\"mbean\":\"java.lang:type=Runtime\",\"type\":\"READ\"},{\"attribute\":\"InputArguments\",\"mbean\":\"java.lang:type=Runtime\",\"type\":\"READ\"},{\"attribute\":\"SystemProperties\",\"mbean\":\"java.lang:type=Runtime\",\"type\":\"READ\"}]" + } + ] + }, + "response": { + "status": 200, + "bodyFileName": "system-attributes.json" + } +} \ No newline at end of file diff --git a/application/uitests/pom.xml b/application/uitests/pom.xml index 98327fd9a8..f6496c214d 100644 --- a/application/uitests/pom.xml +++ b/application/uitests/pom.xml @@ -1,6 +1,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/application/org.openjdk.jmc.kubernetes/pom.xml b/application/org.openjdk.jmc.kubernetes/pom.xml deleted file mode 100644 index 8593216ed0..0000000000 --- a/application/org.openjdk.jmc.kubernetes/pom.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - - 4.0.0 - - org.openjdk.jmc - missioncontrol.application - ${revision}${changelist} - - org.openjdk.jmc.kubernetes - eclipse-plugin - - ${project.basedir}/../../configuration - - diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnection.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnection.java deleted file mode 100644 index 6aae451116..0000000000 --- a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnection.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2024, Kantega AS. All rights reserved. - * - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * The contents of this file are subject to the terms of either the Universal Permissive License - * v 1.0 as shown at http://oss.oracle.com/licenses/upl - * - * or the following license: - * - * Redistribution and use in source and binary forms, with or without modification, are permitted - * provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list of - * conditions and the following disclaimer in the documentation and/or other materials provided with - * the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors may be used to - * endorse or promote products derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY - * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.openjdk.jmc.kubernetes; - -import java.io.IOException; -import java.util.Arrays; -import java.util.Collection; -import java.util.regex.Pattern; - -import javax.management.InstanceNotFoundException; - -import org.jolokia.client.J4pClient; -import org.jolokia.client.exception.J4pException; -import org.jolokia.client.exception.J4pRemoteException; -import org.jolokia.client.request.J4pResponse; -import org.openjdk.jmc.jolokia.JmcJolokiaJmxConnection; -import org.openjdk.jmc.rjmx.common.ConnectionException; - -/** - * Jolokia based MBeanServerConnector tailored for JMC needs - */ -public class JmcKubernetesJmxConnection extends JmcJolokiaJmxConnection { - - static final Collection DISCONNECT_SIGNS = Arrays.asList(Pattern.compile("Error: pods \".+\" not found")); //$NON-NLS-1$ - - public JmcKubernetesJmxConnection(J4pClient client) throws IOException { - super(client); - } - - @SuppressWarnings("rawtypes") - @Override - protected J4pResponse unwrapException(J4pException e) throws IOException, InstanceNotFoundException { - // recognize signs of disconnect and signal to the application for better - // handling - if (isKnownDisconnectException(e)) { - throw new ConnectionException(e.getMessage()); - } else { - return super.unwrapException(e); - } - } - - private boolean isKnownDisconnectException(J4pException e) { - if (!(e instanceof J4pRemoteException)) { - return false; - } - if (!"io.fabric8.kubernetes.client.KubernetesClientException".equals(((J4pRemoteException) e).getErrorType())) { //$NON-NLS-1$ - return false; - } - return DISCONNECT_SIGNS.stream().anyMatch(pattern -> pattern.matcher(e.getMessage()).matches()); - } - -} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnectionProvider.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnectionProvider.java deleted file mode 100644 index b74b999f68..0000000000 --- a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnectionProvider.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2024, Kantega AS. All rights reserved. - * - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * The contents of this file are subject to the terms of either the Universal Permissive License - * v 1.0 as shown at http://oss.oracle.com/licenses/upl - * - * or the following license: - * - * Redistribution and use in source and binary forms, with or without modification, are permitted - * provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list of - * conditions and the following disclaimer in the documentation and/or other materials provided with - * the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors may be used to - * endorse or promote products derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY - * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.openjdk.jmc.kubernetes; - -import java.io.IOException; -import java.net.MalformedURLException; -import java.util.Map; - -import javax.management.remote.JMXConnector; -import javax.management.remote.JMXConnectorProvider; -import javax.management.remote.JMXServiceURL; - -/** - * This {@code JMXConnectionProvider} handles the "kubernetes" protocol. - */ -public class JmcKubernetesJmxConnectionProvider implements JMXConnectorProvider { - @Override - public JMXConnector newJMXConnector(JMXServiceURL serviceURL, Map environment) throws IOException { - if (!"kubernetes".equals(serviceURL.getProtocol())) { //$NON-NLS-1$ - throw new MalformedURLException("I only serve Kubernetes connections"); //$NON-NLS-1$ - } - return new JmcKubernetesJmxConnector(serviceURL, environment); - } -} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnector.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnector.java deleted file mode 100644 index 0d1d98264a..0000000000 --- a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnector.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2024, Kantega AS. All rights reserved. - * - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * The contents of this file are subject to the terms of either the Universal Permissive License - * v 1.0 as shown at http://oss.oracle.com/licenses/upl - * - * or the following license: - * - * Redistribution and use in source and binary forms, with or without modification, are permitted - * provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list of - * conditions and the following disclaimer in the documentation and/or other materials provided with - * the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors may be used to - * endorse or promote products derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY - * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.openjdk.jmc.kubernetes; - -import java.io.IOException; -import java.util.Map; - -import javax.management.remote.JMXServiceURL; - -import org.jolokia.client.J4pClient; -import org.jolokia.client.jmxadapter.RemoteJmxAdapter; -import org.jolokia.kubernetes.client.KubernetesJmxConnector; - -public class JmcKubernetesJmxConnector extends KubernetesJmxConnector { - - public JmcKubernetesJmxConnector(JMXServiceURL serviceURL, Map environment) { - super(serviceURL, environment); - } - - @Override - protected RemoteJmxAdapter createAdapter(J4pClient client) throws IOException { - return new JmcKubernetesJmxConnection(client); - } -} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesPlugin.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesPlugin.java deleted file mode 100644 index a0cd408f67..0000000000 --- a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesPlugin.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2024, Kantega AS. All rights reserved. - * - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * The contents of this file are subject to the terms of either the Universal Permissive License - * v 1.0 as shown at http://oss.oracle.com/licenses/upl - * - * or the following license: - * - * Redistribution and use in source and binary forms, with or without modification, are permitted - * provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list of - * conditions and the following disclaimer in the documentation and/or other materials provided with - * the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors may be used to - * endorse or promote products derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY - * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.openjdk.jmc.kubernetes; - -import org.eclipse.core.runtime.Platform; -import org.openjdk.jmc.kubernetes.preferences.KubernetesScanningParameters; -import org.openjdk.jmc.kubernetes.preferences.PreferenceConstants; -import org.openjdk.jmc.ui.MCAbstractUIPlugin; -import org.openjdk.jmc.common.security.ICredentials; -import org.openjdk.jmc.common.security.PersistentCredentials; -import org.openjdk.jmc.common.security.SecurityException; -import org.openjdk.jmc.common.security.SecurityManagerFactory; -import org.openjdk.jmc.ui.misc.DisplayToolkit; -import org.osgi.framework.BundleContext; -import org.osgi.framework.FrameworkUtil; - -public class JmcKubernetesPlugin extends MCAbstractUIPlugin - implements KubernetesScanningParameters, PreferenceConstants { - - public final static String PLUGIN_ID = "org.openjdk.jmc.kubernetes"; //$NON-NLS-1$ - - // The shared instance. - private static JmcKubernetesPlugin plugin; - - /** - * The constructor. - */ - public JmcKubernetesPlugin() { - super(PLUGIN_ID); - plugin = this; - } - - /** - * @return the shared instance. - */ - public static JmcKubernetesPlugin getDefault() { - return plugin; - } - - private void ensureNeededCredentialsAreUnlocked() { - if (getScanningCredentials() != null && SecurityManagerFactory.getSecurityManager().isLocked()) { - DisplayToolkit.safeAsyncExec(() -> { - try { - SecurityManagerFactory.getSecurityManager().unlock(); - } catch (SecurityException e) { - logError("Error unlocking credentials needed for kubernetes scanning", e);//$NON-NLS-1$ - } - }); - } - } - - @Override - public boolean scanForInstances() { - // If credentials are locked and credentials are required, the scanner thread - // will get hung - // therefore await credentials store to be unlocked before proceeding to scan - return getPreferenceStore().getBoolean(P_SCAN_FOR_INSTANCES) - && (getScanningCredentials() == null || !SecurityManagerFactory.getSecurityManager().isLocked()); - - } - - @Override - public boolean scanAllContexts() { - return getPreferenceStore().getBoolean(P_SCAN_ALL_CONTEXTS); - } - - @Override - public String jolokiaPort() { - return getPreferenceStore().getString(P_JOLOKIA_PORT); - } - - private PersistentCredentials getScanningCredentials() { - String key = getPreferenceStore().getString(P_CREDENTIALS_KEY); - return key == null ? null : new PersistentCredentials(key); - } - - public ICredentials storeCredentials(String username, String password) throws SecurityException { - PersistentCredentials credentials = new PersistentCredentials(username, password, "kubernetes");//$NON-NLS-1$ - getPreferenceStore().setValue(P_CREDENTIALS_KEY, credentials.getExportedId()); - return credentials; - } - - @Override - public String username() throws SecurityException { - final PersistentCredentials cred = getScanningCredentials(); - if (cred == null) { - return "";//$NON-NLS-1$ - } else { - return cred.getUsername(); - } - } - - @Override - public String password() throws SecurityException { - final PersistentCredentials cred = getScanningCredentials(); - if (cred == null) { - return "";//$NON-NLS-1$ - } else { - return cred.getPassword(); - } - } - - @Override - public String jolokiaPath() { - return getPreferenceStore().getString(P_JOLOKIA_PATH); - } - - @Override - public String requireLabel() { - return getPreferenceStore().getString(P_REQUIRE_LABEL); - } - - @Override - public String jolokiaProtocol() { - return getPreferenceStore().getString(P_JOLOKIA_PROTOCOL); - } - - @Override - public void logError(String message, Throwable error) { - Platform.getLog(FrameworkUtil.getBundle(getClass())).error(message, error); - } - - @Override - public void start(BundleContext context) throws Exception { - super.start(context); - this.ensureNeededCredentialsAreUnlocked(); - } - -} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/KubernetesDiscoveryListener.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/KubernetesDiscoveryListener.java deleted file mode 100644 index bcb2420ac3..0000000000 --- a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/KubernetesDiscoveryListener.java +++ /dev/null @@ -1,340 +0,0 @@ -/* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2024, Kantega AS. All rights reserved. - * - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * The contents of this file are subject to the terms of either the Universal Permissive License - * v 1.0 as shown at http://oss.oracle.com/licenses/upl - * - * or the following license: - * - * Redistribution and use in source and binary forms, with or without modification, are permitted - * provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list of - * conditions and the following disclaimer in the documentation and/or other materials provided with - * the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors may be used to - * endorse or promote products derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY - * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.openjdk.jmc.kubernetes; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.IOException; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.NoSuchElementException; -import java.util.Properties; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -import javax.management.remote.JMXConnector; -import javax.management.remote.JMXServiceURL; - -import org.apache.commons.codec.binary.Base64; -import org.jolokia.client.J4pClient; -import org.jolokia.kubernetes.client.KubernetesJmxConnector; -import org.jolokia.server.core.osgi.security.AuthorizationHeaderParser; -import org.jolokia.server.core.util.Base64Util; -import org.openjdk.jmc.jolokia.AbstractCachedDescriptorProvider; -import org.openjdk.jmc.jolokia.JolokiaAgentDescriptor; -import org.openjdk.jmc.jolokia.ServerConnectionDescriptor; -import org.openjdk.jmc.kubernetes.preferences.KubernetesScanningParameters; -import org.openjdk.jmc.common.jvm.JVMDescriptor; -import org.openjdk.jmc.common.security.SecurityException; - -import io.fabric8.kubernetes.api.model.NamedContext; -import io.fabric8.kubernetes.api.model.ObjectMeta; -import io.fabric8.kubernetes.api.model.Pod; -import io.fabric8.kubernetes.api.model.PodList; -import io.fabric8.kubernetes.api.model.Secret; -import io.fabric8.kubernetes.client.Config; -import io.fabric8.kubernetes.client.KubernetesClient; -import io.fabric8.kubernetes.client.internal.KubeConfigUtils; -import io.fabric8.kubernetes.client.utils.Utils; -import io.fabric8.kubernetes.client.dsl.AnyNamespaceOperation; -import io.fabric8.kubernetes.client.dsl.PodResource; - -/** - * This class attempts to connect to JVMs in pods running in kubernetes in a background thread. - * Enablement and parameters for the scanning is given by user preferences. - */ -public class KubernetesDiscoveryListener extends AbstractCachedDescriptorProvider { - - private final static Pattern SECRET_PATTERN = Pattern - .compile("\\$\\{kubernetes/secret/(?[^/]+)/(?[^\\}]+)}"); //$NON-NLS-1$ - private final static Pattern ATTRIBUTE_PATTERN = Pattern - .compile("\\$\\{kubernetes/annotation/(?[^/]+)}"); //$NON-NLS-1$ - private final static Set VALID_JOLOKIA_PROTOCOLS = new HashSet<>(Arrays.asList("http", "https")); //$NON-NLS-1$ //$NON-NLS-2$ - - KubernetesScanningParameters settings; - - public KubernetesDiscoveryListener() { - this(JmcKubernetesPlugin.getDefault()); - } - - //Public constructor in order for test plugin to be able to rig tests in an easier manner - public KubernetesDiscoveryListener(KubernetesScanningParameters parameters) { - this.settings = parameters; - } - - public final String getDescription() { - return Messages.KubernetesDiscoveryListener_Description; - } - - @Override - public String getName() { - return "kubernetes"; //$NON-NLS-1$ - } - - boolean notEmpty(String value) { - return value != null && value.length() > 0; - } - - private List contexts; - private long contextsCached = 0L; - - private List allContexts() throws IOException { - final String path = Utils.getSystemPropertyOrEnvVar(Config.KUBERNETES_KUBECONFIG_FILE, - new File(System.getProperty("user.home"), ".kube" + File.separator + "config").toString()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - File configPath = new File(path); - if (contexts != null && contextsCached > configPath.lastModified()) {// the YAML parsing is soo incredibly - // sloow, hence cache context names for - // later - // runs - return contexts; - } - // reload config if kubeconfig has been modified since we cached the config - io.fabric8.kubernetes.api.model.Config config = KubeConfigUtils.parseConfig(configPath); - this.contextsCached = System.currentTimeMillis(); - KubernetesJmxConnector.resetKubernetesConfig(); - return contexts = config.getContexts().stream().map(NamedContext::getName).collect(Collectors.toList()); - } - - @Override - protected Map discoverJvms() { - return scanForJvms(); - } - - Map scanForJvms() { - Map found = new HashMap<>(); - if (!isEnabled()) { - return found; - } - boolean hasScanned = false; - - if (settings.scanAllContexts()) { - try { - for (final String context : allContexts()) { - hasScanned = true; - scanContext(found, settings, context); - } - } catch (IOException e) { - settings.logError(Messages.KubernetesDiscoveryListener_UnableToFindContexts, e); - } - } - if (!hasScanned) {// scan default context - return scanContext(found, settings, null); - } - return found; - } - - private Map scanContext( - Map found, KubernetesScanningParameters parameters, String context) { - try { - scanContextUnsafe(found, parameters, context); - } catch (Exception e) { - parameters.logError(Messages.KubernetesDiscoveryListener_UnableToScan + context, e); - } - return found; - } - - private Map scanContextUnsafe( - Map found, KubernetesScanningParameters parameters, String context) { - String pathLabel = parameters.requireLabel(); - KubernetesClient client = KubernetesJmxConnector.getApiClient(context); - - AnyNamespaceOperation query = client.pods().inAnyNamespace(); - List podList; - boolean hasPathLabel = notEmpty(pathLabel); - if (hasPathLabel) { - podList = query.withLabel(pathLabel).list().getItems(); - } else { - podList = query.list().getItems(); - } - // can consider parallelization for big contexts, however since it is the - // background await the situation a bit - podList.stream().forEach(pod -> scanPod(found, parameters, context, client, pod)); - return found; - } - - private void scanPod( - Map found, KubernetesScanningParameters parameters, String context, - KubernetesClient client, Pod pod) { - - final ObjectMeta metadata = pod.getMetadata(); - HashMap headers = new HashMap<>(); - Map env = new HashMap<>(); - try { - if (notEmpty(parameters.username())) { - if (!notEmpty(parameters.password())) { - throw new IllegalArgumentException(Messages.KubernetesDiscoveryListener_MustProvidePassword); - } - authorize(headers, client, parameters.username(), parameters.password(), metadata.getNamespace(), env); - } - } catch (SecurityException e) { - // skipping authorization if anything fails - } - final StringBuilder url = new StringBuilder("/api/").append(pod.getApiVersion()).append("/namespaces/") - .append(metadata.getNamespace()).append("/pods/"); - // JMX url must be reverse constructed, so that we can connect from the - // resulting node in the JVM browser - final StringBuilder jmxUrl = new StringBuilder("service:jmx:kubernetes:///").append(metadata.getNamespace()) //$NON-NLS-1$ - .append('/'); - - final String protocol = getValueOrAttribute(parameters.jolokiaProtocol(), metadata); - final String podName = metadata.getName(); - if (notEmpty(protocol)) { - if (!VALID_JOLOKIA_PROTOCOLS.contains(protocol)) { - throw new IllegalArgumentException(Messages.KubernetesDiscoveryListener_JolokiaProtocol + protocol - + Messages.KubernetesDiscoveryListener_HttpOrHttps); - } - // a bit clumsy, need to inject protocol _before_ podname in selflink - url.append(protocol).append(':'); - jmxUrl.append(protocol).append(':'); - } - - jmxUrl.append(podName); - url.append(podName); - - final String port = getValueOrAttribute(parameters.jolokiaPort(), metadata); - if (port != null) { - url.append(":").append(port); //$NON-NLS-1$ - jmxUrl.append(':').append(port); - } - - url.append("/proxy"); //$NON-NLS-1$ - - final String path = getValueOrAttribute(parameters.jolokiaPath(), metadata); - - if (!path.startsWith("/")) { //$NON-NLS-1$ - url.append('/'); - jmxUrl.append('/'); - } - url.append(path); - jmxUrl.append(path); - - if (context != null) { - env.put(KubernetesJmxConnector.KUBERNETES_CLIENT_CONTEXT, context); - } - J4pClient jvmClient = KubernetesJmxConnector.probeProxyPath(env, client, url, headers); - if (jvmClient != null) { - JmcKubernetesJmxConnection connection; - try { - connection = new JmcKubernetesJmxConnection(jvmClient); - JVMDescriptor jvmDescriptor = JolokiaAgentDescriptor.attemptToGetJvmInfo(connection); - JMXServiceURL jmxServiceURL = new JMXServiceURL(jmxUrl.toString()); - KubernetesJvmDescriptor descriptor = new KubernetesJvmDescriptor(metadata, jvmDescriptor, jmxServiceURL, - env); - found.put(descriptor.getGUID(), descriptor); - } catch (IOException e) { - parameters.logError(Messages.KubernetesDiscoveryListener_ErrConnectingToJvm, e); - - } - } - } - - private String getValueOrAttribute(String configValue, ObjectMeta metadata) { - if (notEmpty(configValue)) { - Matcher pattern = ATTRIBUTE_PATTERN.matcher(configValue); - if (pattern.find()) { - return metadata.getAnnotations().get(pattern.group("annotationName")); //$NON-NLS-1$ - } else { - return configValue;// the default is to use config value as is - } - } - return null; - } - - private void authorize( - HashMap headers, KubernetesClient client, String username, String password, String namespace, - Map jmxEnv) { - - final Matcher userNameMatcher = SECRET_PATTERN.matcher(username); - String secretName = null; - Map secretValues = null; - if (userNameMatcher.find()) { - secretName = userNameMatcher.group("secretName"); //$NON-NLS-1$ - secretValues = findSecret(client, namespace, secretName); - username = secretValues.get(userNameMatcher.group("itemName")); //$NON-NLS-1$ - } - - final Matcher passwordMatcher = SECRET_PATTERN.matcher(password); - if (passwordMatcher.find()) { - if (!secretName.equals(passwordMatcher.group("secretName"))) { //$NON-NLS-1$ - secretValues = findSecret(client, namespace, passwordMatcher.group("secretName")); //$NON-NLS-1$ - } - password = secretValues.get(passwordMatcher.group("itemName")); //$NON-NLS-1$ - } - - headers.put(AuthorizationHeaderParser.JOLOKIA_ALTERNATE_AUTHORIZATION_HEADER, - "Basic " + Base64Util.encode((username + ":" + password).getBytes())); //$NON-NLS-1$ //$NON-NLS-2$ - jmxEnv.put(JMXConnector.CREDENTIALS, new String[] {username, password}); - - } - - @SuppressWarnings({"unchecked", "rawtypes"}) - private Map findSecret(KubernetesClient client, String namespace, String secretName) { - - for (Secret secret : client.secrets().inNamespace(namespace).list().getItems()) { - if (secret.getMetadata().getName().equals(secretName)) { - if ("kubernetes.io/basic-auth".equals(secret.getType())) { //$NON-NLS-1$ - Map data = secret.getData(); - data.replaceAll((key, value) -> new String(Base64.decodeBase64(value))); - return data; - } else if ("Opaque".equals(secret.getType())) { //$NON-NLS-1$ - for (Entry entry : secret.getData().entrySet()) { - if (entry.getKey().endsWith(".properties")) { //$NON-NLS-1$ - try { - Properties properties = new Properties(); - properties.load(new ByteArrayInputStream(Base64.decodeBase64(entry.getValue()))); - return (Map) properties; - } catch (IOException ignore) { - } - } - } - } - } - - } - throw new NoSuchElementException(Messages.KubernetesDiscoveryListener_CouldNotFindSecret + secretName - + Messages.KubernetesDiscoveryListener_InNamespace + namespace); - - } - - @Override - protected boolean isEnabled() { - return this.settings.scanForInstances(); - } -} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/KubernetesJvmDescriptor.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/KubernetesJvmDescriptor.java deleted file mode 100644 index a646a0fcc7..0000000000 --- a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/KubernetesJvmDescriptor.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2024, Kantega AS. All rights reserved. - * - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * The contents of this file are subject to the terms of either the Universal Permissive License - * v 1.0 as shown at http://oss.oracle.com/licenses/upl - * - * or the following license: - * - * Redistribution and use in source and binary forms, with or without modification, are permitted - * provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list of - * conditions and the following disclaimer in the documentation and/or other materials provided with - * the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors may be used to - * endorse or promote products derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY - * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.openjdk.jmc.kubernetes; - -import java.io.IOException; -import java.util.Map; - -import javax.management.remote.JMXServiceURL; - -import org.jolokia.kubernetes.client.KubernetesJmxConnector; -import org.openjdk.jmc.jolokia.ServerConnectionDescriptor; -import org.openjdk.jmc.common.jvm.JVMDescriptor; - -import io.fabric8.kubernetes.api.model.ObjectMeta; - -public class KubernetesJvmDescriptor implements ServerConnectionDescriptor { - - private final JVMDescriptor jvmDescriptor; - private final ObjectMeta metadata; - private final Map env; - private final JMXServiceURL connectUrl; - - public KubernetesJvmDescriptor(ObjectMeta metadata, JVMDescriptor jvmDescriptor, JMXServiceURL connectUrl, - Map env) { - this.jvmDescriptor = jvmDescriptor; - this.metadata = metadata; - this.env = env; - this.connectUrl = connectUrl; - } - - @Override - public String getGUID() { - return this.metadata.getName(); - } - - @Override - public String getDisplayName() { - return this.metadata.getName(); - } - - @Override - public JVMDescriptor getJvmInfo() { - return this.jvmDescriptor; - } - - public String getPath() { - String namespace = metadata.getNamespace(); - final Object context = this.env.get(KubernetesJmxConnector.KUBERNETES_CLIENT_CONTEXT); - if (context != null) { - return context + "/" + namespace; //$NON-NLS-1$ - } - return namespace; - } - - @Override - public JMXServiceURL createJMXServiceURL() throws IOException { - return this.connectUrl; - } - - @Override - public Map getEnvironment() { - return this.env; - } - - @Override - public JMXServiceURL serviceUrl() { - return this.connectUrl; - } - -} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/Messages.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/Messages.java deleted file mode 100644 index 9dcff25a26..0000000000 --- a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/Messages.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2024, Kantega AS. All rights reserved. - * - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * The contents of this file are subject to the terms of either the Universal Permissive License - * v 1.0 as shown at http://oss.oracle.com/licenses/upl - * - * or the following license: - * - * Redistribution and use in source and binary forms, with or without modification, are permitted - * provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list of - * conditions and the following disclaimer in the documentation and/or other materials provided with - * the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors may be used to - * endorse or promote products derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY - * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.openjdk.jmc.kubernetes; - -import org.eclipse.osgi.util.NLS; - -public class Messages extends NLS { - private static final String BUNDLE_NAME = "org.openjdk.jmc.kubernetes.messages"; //$NON-NLS-1$ - public static String KubernetesDiscoveryListener_CouldNotFindSecret; - public static String KubernetesDiscoveryListener_Description; - public static String KubernetesDiscoveryListener_ErrConnectingToJvm; - public static String KubernetesDiscoveryListener_HttpOrHttps; - public static String KubernetesDiscoveryListener_InNamespace; - public static String KubernetesDiscoveryListener_JolokiaProtocol; - public static String KubernetesDiscoveryListener_MustProvidePassword; - public static String KubernetesDiscoveryListener_UnableToFindContexts; - public static String KubernetesDiscoveryListener_UnableToScan; - static { - // initialize resource bundle - NLS.initializeMessages(BUNDLE_NAME, Messages.class); - } - - private Messages() { - } -} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/messages.properties b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/messages.properties deleted file mode 100644 index ea0166360a..0000000000 --- a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/messages.properties +++ /dev/null @@ -1,9 +0,0 @@ -KubernetesDiscoveryListener_CouldNotFindSecret=Could not find secret named -KubernetesDiscoveryListener_Description=List JVM in kubernetes cluster -KubernetesDiscoveryListener_ErrConnectingToJvm=Error connecting to JVM in pod -KubernetesDiscoveryListener_HttpOrHttps=' must be either 'http' or 'https' -KubernetesDiscoveryListener_InNamespace=\ in namespace -KubernetesDiscoveryListener_JolokiaProtocol=Jolokia protocol ' -KubernetesDiscoveryListener_MustProvidePassword=Password must be specified when username is specified -KubernetesDiscoveryListener_UnableToFindContexts=Unable to find all kubernetes contexts -KubernetesDiscoveryListener_UnableToScan=Unable to scan kubernetes context diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/JmcKubernetesPreferenceForm.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/JmcKubernetesPreferenceForm.java deleted file mode 100644 index 5b2198933a..0000000000 --- a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/JmcKubernetesPreferenceForm.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2024, Kantega AS. All rights reserved. - * - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * The contents of this file are subject to the terms of either the Universal Permissive License - * v 1.0 as shown at http://oss.oracle.com/licenses/upl - * - * or the following license: - * - * Redistribution and use in source and binary forms, with or without modification, are permitted - * provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list of - * conditions and the following disclaimer in the documentation and/or other materials provided with - * the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors may be used to - * endorse or promote products derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY - * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.openjdk.jmc.kubernetes.preferences; - -import java.util.Map; -import java.util.WeakHashMap; -import java.util.logging.Level; - -import org.eclipse.jface.preference.BooleanFieldEditor; -import org.eclipse.jface.preference.FieldEditor; -import org.eclipse.jface.preference.FieldEditorPreferencePage; -import org.eclipse.jface.preference.StringFieldEditor; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.ModifyListener; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Text; -import org.eclipse.ui.IWorkbench; -import org.eclipse.ui.IWorkbenchPreferencePage; -import org.openjdk.jmc.common.security.CredentialsNotAvailableException; -import org.openjdk.jmc.common.security.SecurityException; -import org.openjdk.jmc.kubernetes.JmcKubernetesPlugin; - -/** - * This class represents a preference page that is contributed to the Preferences dialog. By - * subclassing FieldEditorPreferencePage, we can use the field support built into JFace - * that allows us to create a page that is small and knows how to save, restore and apply itself. - *

- * This page is used to modify preferences only. They are stored in the preference store that - * belongs to the main plug-in class. That way, preferences can be accessed directly via the - * preference store. - */ -public class JmcKubernetesPreferenceForm extends FieldEditorPreferencePage - implements IWorkbenchPreferencePage, PreferenceConstants { - - private Map dependantControls = new WeakHashMap<>(); - private Text userField; - private Text passwordField; - private boolean credentialsDirty; - - public JmcKubernetesPreferenceForm() { - super(GRID); - setPreferenceStore(JmcKubernetesPlugin.getDefault().getPreferenceStore()); - setDescription(Messages.JmcKubernetesPreferenceForm_FormDescription); - } - - /** - * Creates the field editors. Field editors are abstractions of the common GUI blocks needed to - * manipulate various types of preferences. Each field editor knows how to save and restore - * itself. - */ - public void createFieldEditors() { - BooleanFieldEditor mainEnabler = new BooleanFieldEditor(P_SCAN_FOR_INSTANCES, - Messages.JmcKubernetesPreferenceForm_ScanForPods, getFieldEditorParent()) { - @Override - protected void valueChanged(boolean oldValue, boolean newValue) { - super.valueChanged(oldValue, newValue); - enableDependantFields(newValue); - } - }; - addField(mainEnabler); - - BooleanFieldEditor scanContextsEditor = new BooleanFieldEditor(P_SCAN_ALL_CONTEXTS, - Messages.JmcKubernetesPreferenceForm_AllContexts, getFieldEditorParent()); - this.addDependantField(scanContextsEditor, scanContextsEditor.getDescriptionControl(getFieldEditorParent())); - this.addTextField(new StringFieldEditor(P_REQUIRE_LABEL, Messages.JmcKubernetesPreferenceForm_RequireLabel, - getFieldEditorParent()), Messages.JmcKubernetesPreferenceForm_LabelToolTip); - this.addTextField(new StringFieldEditor(P_JOLOKIA_PATH, Messages.JmcKubernetesPreferenceForm_PathLabel, - getFieldEditorParent()), Messages.JmcKubernetesPreferenceForm_PathTooltip); - this.addTextField(new StringFieldEditor(P_JOLOKIA_PORT, Messages.JmcKubernetesPreferenceForm_PortLabel, - getFieldEditorParent()), Messages.JmcKubernetesPreferenceForm_PortTooltip); - this.addTextField(new StringFieldEditor(P_JOLOKIA_PROTOCOL, Messages.JmcKubernetesPreferenceForm_ProtocolLabel, - getFieldEditorParent()), Messages.JmcKubernetesPreferenceForm_ProtocolTooltip); - createCredentialFields(); - // set initial enablement - enableDependantFields(JmcKubernetesPlugin.getDefault().scanForInstances()); - - } - - private void createCredentialFields() { - Label userLabel = new Label(getFieldEditorParent(), SWT.NONE); - userLabel.setText(Messages.JmcKubernetesPreferenceForm_UsernameLabel); - userLabel.setLayoutData(new GridData()); - this.userField = new Text(getFieldEditorParent(), SWT.SINGLE | SWT.BORDER); - userField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); - userField.setToolTipText(Messages.JmcKubernetesPreferenceForm_UsernameTooltip); - this.dependantControls.put(userField, null); - - Label passLabel = new Label(getFieldEditorParent(), SWT.NONE); - passLabel.setText(Messages.JmcKubernetesPreferenceForm_PasswordLabel); - passLabel.setLayoutData(new GridData()); - this.passwordField = new Text(getFieldEditorParent(), SWT.PASSWORD | SWT.SINGLE | SWT.BORDER); - passwordField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); - - JmcKubernetesPlugin plugin = JmcKubernetesPlugin.getDefault(); - - try { - userField.setText(plugin.username()); - passwordField.setText(plugin.password()); - } catch (CredentialsNotAvailableException ignore) { - // ignore if credentials are not stored - } catch (SecurityException e) { - plugin.getLogger().log(Level.WARNING, "Could not load kubernetes credentials", e); //$NON-NLS-1$ - } - - ModifyListener markCredentials = e -> credentialsDirty = true; - this.userField.addModifyListener(markCredentials); - this.passwordField.addModifyListener(markCredentials); - } - - private void addTextField(StringFieldEditor field, String tooltip) { - Text textControl = field.getTextControl(getFieldEditorParent()); - this.addDependantField(field, textControl); - textControl.setToolTipText(tooltip); - field.getLabelControl(getFieldEditorParent()).setToolTipText(tooltip); - - } - - private void addDependantField(FieldEditor field, Control control) { - this.dependantControls.put(control, null); - addField(field); - } - - private void enableDependantFields(boolean enabled) { - for (Control field : this.dependantControls.keySet()) { - field.setEnabled(enabled); - } - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.ui.IWorkbenchPreferencePage#init(org.eclipse.ui.IWorkbench) - */ - public void init(IWorkbench workbench) { - } - - @Override - public boolean performOk() { - updateCredentialsIfApplicable(); - return super.performOk(); - } - - private void updateCredentialsIfApplicable() { - if (this.credentialsDirty) { - try { - JmcKubernetesPlugin.getDefault().storeCredentials(userField.getText(), passwordField.getText()); - this.credentialsDirty = false; - } catch (SecurityException ex) { - JmcKubernetesPlugin.getDefault().getLogger().log(Level.WARNING, - "Could not store kubernetes credentials", ex); //$NON-NLS-1$ - } - } - } - -} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/KubernetesScanningParameters.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/KubernetesScanningParameters.java deleted file mode 100644 index a67697d42b..0000000000 --- a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/KubernetesScanningParameters.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2024, Kantega AS. All rights reserved. - * - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * The contents of this file are subject to the terms of either the Universal Permissive License - * v 1.0 as shown at http://oss.oracle.com/licenses/upl - * - * or the following license: - * - * Redistribution and use in source and binary forms, with or without modification, are permitted - * provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list of - * conditions and the following disclaimer in the documentation and/or other materials provided with - * the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors may be used to - * endorse or promote products derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY - * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.openjdk.jmc.kubernetes.preferences; - -import org.openjdk.jmc.common.security.ICredentials; -import org.openjdk.jmc.common.security.SecurityException; - -public interface KubernetesScanningParameters { - boolean scanForInstances(); - - boolean scanAllContexts(); - - String jolokiaPort(); - - String username() throws SecurityException; - - String password() throws SecurityException; - - String jolokiaPath(); - - String jolokiaProtocol(); - - String requireLabel(); - - ICredentials storeCredentials(String username, String password) throws SecurityException; - - void logError(String message, Throwable error); -} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/Messages.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/Messages.java deleted file mode 100644 index a718015331..0000000000 --- a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/Messages.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2024, Kantega AS. All rights reserved. - * - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * The contents of this file are subject to the terms of either the Universal Permissive License - * v 1.0 as shown at http://oss.oracle.com/licenses/upl - * - * or the following license: - * - * Redistribution and use in source and binary forms, with or without modification, are permitted - * provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list of - * conditions and the following disclaimer in the documentation and/or other materials provided with - * the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors may be used to - * endorse or promote products derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY - * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.openjdk.jmc.kubernetes.preferences; - -import org.eclipse.osgi.util.NLS; - -public class Messages extends NLS { - private static final String BUNDLE_NAME = "org.openjdk.jmc.kubernetes.preferences.messages"; //$NON-NLS-1$ - public static String JmcKubernetesPreferenceForm_AllContexts; - public static String JmcKubernetesPreferenceForm_FormDescription; - public static String JmcKubernetesPreferenceForm_LabelToolTip; - public static String JmcKubernetesPreferenceForm_PasswordLabel; - public static String JmcKubernetesPreferenceForm_PasswordTooltip; - public static String JmcKubernetesPreferenceForm_PathLabel; - public static String JmcKubernetesPreferenceForm_PathTooltip; - public static String JmcKubernetesPreferenceForm_PortLabel; - public static String JmcKubernetesPreferenceForm_PortTooltip; - public static String JmcKubernetesPreferenceForm_ProtocolLabel; - public static String JmcKubernetesPreferenceForm_ProtocolTooltip; - public static String JmcKubernetesPreferenceForm_RequireLabel; - public static String JmcKubernetesPreferenceForm_ScanForPods; - public static String JmcKubernetesPreferenceForm_UsernameTooltip; - public static String JmcKubernetesPreferenceForm_UsernameLabel; - static { - // initialize resource bundle - NLS.initializeMessages(BUNDLE_NAME, Messages.class); - } - - private Messages() { - } -} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/PreferenceConstants.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/PreferenceConstants.java deleted file mode 100644 index 876d65a1b9..0000000000 --- a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/PreferenceConstants.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2024, Kantega AS. All rights reserved. - * - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * The contents of this file are subject to the terms of either the Universal Permissive License - * v 1.0 as shown at http://oss.oracle.com/licenses/upl - * - * or the following license: - * - * Redistribution and use in source and binary forms, with or without modification, are permitted - * provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list of - * conditions and the following disclaimer in the documentation and/or other materials provided with - * the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors may be used to - * endorse or promote products derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY - * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.openjdk.jmc.kubernetes.preferences; - -/** - * Constant definitions for plug-in preferences - */ -public interface PreferenceConstants { - - public static final String P_SCAN_FOR_INSTANCES = "scanForInstances"; //$NON-NLS-1$ - public static final String P_SCAN_ALL_CONTEXTS = "scanAllContexts"; //$NON-NLS-1$ - public static final String P_REQUIRE_LABEL = "requireLabel"; //$NON-NLS-1$ - public static final String P_JOLOKIA_PATH = "jolokiaPath"; //$NON-NLS-1$ - public static final String P_JOLOKIA_PORT = "jolokiaPort"; //$NON-NLS-1$ - public static final String P_JOLOKIA_PROTOCOL = "jolokiaProtocol"; //$NON-NLS-1$ - public static final String P_CREDENTIALS_KEY = "kubernetes.scanning.credentials"; //$NON-NLS-1$ -} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/PreferenceInitializer.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/PreferenceInitializer.java deleted file mode 100644 index 3aac64c2c2..0000000000 --- a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/PreferenceInitializer.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2024, Kantega AS. All rights reserved. - * - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * The contents of this file are subject to the terms of either the Universal Permissive License - * v 1.0 as shown at http://oss.oracle.com/licenses/upl - * - * or the following license: - * - * Redistribution and use in source and binary forms, with or without modification, are permitted - * provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list of - * conditions and the following disclaimer in the documentation and/or other materials provided with - * the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors may be used to - * endorse or promote products derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY - * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.openjdk.jmc.kubernetes.preferences; - -import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer; -import org.eclipse.jface.preference.IPreferenceStore; -import org.openjdk.jmc.kubernetes.JmcKubernetesPlugin; - -/** - * Class used to initialize default preference values. - */ -public class PreferenceInitializer extends AbstractPreferenceInitializer implements PreferenceConstants { - - /* - * (non-Javadoc) - * - * @see org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer# - * initializeDefaultPreferences() - */ - public void initializeDefaultPreferences() { - IPreferenceStore store = JmcKubernetesPlugin.getDefault().getPreferenceStore(); - store.setDefault(P_SCAN_FOR_INSTANCES, false); - store.setDefault(P_SCAN_ALL_CONTEXTS, false); - store.setDefault(P_REQUIRE_LABEL, "jolokia"); //$NON-NLS-1$ - store.setDefault(P_JOLOKIA_PATH, "/jolokia/"); //$NON-NLS-1$ - store.setDefault(P_JOLOKIA_PORT, "8778"); //$NON-NLS-1$ - } - -} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/messages.properties b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/messages.properties deleted file mode 100644 index 44b18279a0..0000000000 --- a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/messages.properties +++ /dev/null @@ -1,17 +0,0 @@ -JmcKubernetesPreferenceForm_AllContexts=Scan pods from all locally configured &contexts, if false: only scan the current contexts -JmcKubernetesPreferenceForm_FormDescription=Options that allows you to scan kubernetes for JVMs running Jolokia\ -\n\nThe Kubernetes Proxy API will be used to attempt to access Jolokia like this (parenthesis denote optional components): \ -\n\n$kubernetesBaseUrl/api/v1/namespaces/podNamespace/pods/(jolokiaProtocol:)podname(:jolokiaPort)/proxy/jolokiaPath\n\n -JmcKubernetesPreferenceForm_LabelToolTip=Only attempt to connect to pods with this label set, leave empty to try to scan all pods -JmcKubernetesPreferenceForm_PasswordLabel=Require pass&word -JmcKubernetesPreferenceForm_PasswordTooltip=Password , alternatively use ${kubernetes/secret/secretName/secretItem} where the secret is in the same namespace as the pod and the type is either kubernetes.io/basic-auth or Opaque with java.util.Properties compatible values -JmcKubernetesPreferenceForm_PathLabel=Jolokia &path in pods -JmcKubernetesPreferenceForm_PathTooltip=Use this path for jolokia, or specify ${kubernetes/annotation/annotationName} to be able to to get port name from pod metadata annotation of your choice -JmcKubernetesPreferenceForm_PortLabel=Jolokia p&ort in pods -JmcKubernetesPreferenceForm_PortTooltip=Port to use, leave empty to use default port of Kubernetes proxy, hardcode a port value, or alternatively ${kubernetes/annotation/annotationName} to get port name from pod metadata annotation of your choice -JmcKubernetesPreferenceForm_ProtocolLabel=Jolokia pro&tocol in pods -JmcKubernetesPreferenceForm_ProtocolTooltip=Protocol to use (optional), will infer http if not set, or hardcode to https, or alternatively ${kubernetes/annotation/annotationName} to get port name from pod metadata annotation of your choice -JmcKubernetesPreferenceForm_RequireLabel=Require &label to scan pod -JmcKubernetesPreferenceForm_ScanForPods=&Scan for kubernetes pods with Jolokia support -JmcKubernetesPreferenceForm_UsernameTooltip=Username , alternatively use ${kubernetes/secret/secretName/secretItem} where the secret is in the same namespace as the pod and the type is either kubernetes.io/basic-auth or Opaque with java.util.Properties compatible values -JmcKubernetesPreferenceForm_UsernameLabel=Require &username diff --git a/application/pom.xml b/application/pom.xml index e40f01a4a3..90dc2c96f2 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -112,7 +112,6 @@ org.openjdk.jmc.updatesite.ide org.openjdk.jmc.updatesite.rcp org.openjdk.jmc.jolokia - org.openjdk.jmc.kubernetes l10n tests diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/.classpath b/application/uitests/org.openjdk.jmc.kubernetes.test/.classpath deleted file mode 100644 index cb04fe10dc..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/.classpath +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/META-INF/MANIFEST.MF b/application/uitests/org.openjdk.jmc.kubernetes.test/META-INF/MANIFEST.MF deleted file mode 100644 index 46a9f1b9fc..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/META-INF/MANIFEST.MF +++ /dev/null @@ -1,20 +0,0 @@ -Manifest-Version: 1.0 -Bundle-ManifestVersion: 2 -Bundle-Name: RJMX Test -Bundle-SymbolicName: org.openjdk.jmc.kubernetes.test;singleton:=true -Bundle-Version: 9.1.0.qualifier -Import-Package: io.fabric8.kubernetes.api.model -Bundle-Vendor: Oracle Corporation -Bundle-RequiredExecutionEnvironment: JavaSE-17 -Bundle-ActivationPolicy: lazy -Require-Bundle: org.junit, - org.openjdk.jmc.jolokia, - org.eclipse.osgi;bundle-version="3.16.200", - org.eclipse.ui.workbench, - org.openjdk.jmc.rjmx;bundle-version="9.0.0", - org.eclipse.ui, - org.hamcrest;bundle-version="2.1", - org.openjdk.jmc.kubernetes;bundle-version="9.0.0", - com.github.tomakehurst.wiremock-standalone;bundle-version="2.27.2", - org.awaitility;bundle-version="4.0.0" -Automatic-Module-Name: org.openjdk.jmc.jolokia.test diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/build.properties b/application/uitests/org.openjdk.jmc.kubernetes.test/build.properties deleted file mode 100644 index b4608c8522..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/build.properties +++ /dev/null @@ -1,36 +0,0 @@ -# -# Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. -# Copyright (c) 2024, Kantega AS. All rights reserved. -# -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# The contents of this file are subject to the terms of either the Universal Permissive License -# v 1.0 as shown at http://oss.oracle.com/licenses/upl -# -# or the following license: -# -# Redistribution and use in source and binary forms, with or without modification, are permitted -# provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, this list of conditions -# and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright notice, this list of -# conditions and the following disclaimer in the documentation and/or other materials provided with -# the distribution. -# -# 3. Neither the name of the copyright holder nor the names of its contributors may be used to -# endorse or promote products derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR -# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY -# WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -source.. = src/test/java/,\ - src/test/resources/ -output.. = target/test-classes/ -bin.includes = META-INF/,. diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/pom.xml b/application/uitests/org.openjdk.jmc.kubernetes.test/pom.xml deleted file mode 100644 index 652ddbc5a6..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/pom.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - - 4.0.0 - - org.openjdk.jmc - missioncontrol.application.uitests - ${revision}${changelist} - - org.openjdk.jmc.kubernetes.test - eclipse-test-plugin - - - ${project.basedir}/../../../configuration - - - - - org.awaitility - awaitility - 4.0.0 - - - diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/java/org/openjdk/jmc/kubernetes/JmcKubernetesTest.java b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/java/org/openjdk/jmc/kubernetes/JmcKubernetesTest.java deleted file mode 100644 index 46d3c5ea8b..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/java/org/openjdk/jmc/kubernetes/JmcKubernetesTest.java +++ /dev/null @@ -1,313 +0,0 @@ -/* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2024, Kantega AS. All rights reserved. - * - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * The contents of this file are subject to the terms of either the Universal Permissive License - * v 1.0 as shown at http://oss.oracle.com/licenses/upl - * - * or the following license: - * - * Redistribution and use in source and binary forms, with or without modification, are permitted - * provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list of - * conditions and the following disclaimer in the documentation and/or other materials provided with - * the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors may be used to - * endorse or promote products derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY - * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.openjdk.jmc.kubernetes; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.net.MalformedURLException; -import java.time.Duration; -import java.util.Base64; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import javax.management.AttributeNotFoundException; -import javax.management.InstanceNotFoundException; -import javax.management.InvalidAttributeValueException; -import javax.management.MBeanException; -import javax.management.MBeanServerConnection; -import javax.management.MalformedObjectNameException; -import javax.management.ObjectName; -import javax.management.ReflectionException; -import javax.management.remote.JMXConnector; -import javax.management.remote.JMXServiceURL; - -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.HttpClients; -import org.awaitility.Awaitility; -import org.junit.Assert; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.ClassRule; -import org.junit.Test; -import org.openjdk.jmc.common.IDescribable; -import org.openjdk.jmc.common.jvm.JVMType; -import org.openjdk.jmc.common.security.ICredentials; -import org.openjdk.jmc.common.security.InMemoryCredentials; -import org.openjdk.jmc.common.security.SecurityException; -import org.openjdk.jmc.kubernetes.preferences.KubernetesScanningParameters; -import org.openjdk.jmc.rjmx.common.IConnectionDescriptor; -import org.openjdk.jmc.rjmx.common.IServerDescriptor; -import org.openjdk.jmc.rjmx.descriptorprovider.IDescriptorListener; - -import com.github.tomakehurst.wiremock.client.WireMock; -import com.github.tomakehurst.wiremock.core.WireMockConfiguration; -import com.github.tomakehurst.wiremock.extension.responsetemplating.ResponseTemplateTransformer; -import com.github.tomakehurst.wiremock.junit.WireMockRule; - -/** - * Test that JMX connections done with JmcKubernetesJmxConnectionProvider are functional. In order - * to be able to test this in a contained environment, the kubernetes API is mocked with wiremock. - */ -@SuppressWarnings("restriction") -public class JmcKubernetesTest { - - static class TestParameters implements KubernetesScanningParameters { - public boolean scanForInstances, scanAllContexts; - public String jolokiaPort, jolokiaPath = "/jolokia/", jolokiaProtocol, requireLabel; - public InMemoryCredentials credentials; - - @Override - public boolean scanForInstances() { - return this.scanForInstances; - } - - @Override - public boolean scanAllContexts() { - return this.scanAllContexts; - } - - @Override - public String jolokiaPort() { - return this.jolokiaPort; - } - - @Override - public String username() throws SecurityException { - return this.credentials == null ? null : this.credentials.getUsername(); - } - - @Override - public String password() throws SecurityException { - return this.credentials == null ? null : this.credentials.getPassword(); - } - - @Override - public String jolokiaPath() { - return this.jolokiaPath; - } - - @Override - public String jolokiaProtocol() { - return this.jolokiaProtocol; - } - - @Override - public String requireLabel() { - return this.requireLabel; - } - - @Override - public ICredentials storeCredentials(String username, String password) throws SecurityException { - return this.credentials = new InMemoryCredentials(username, password); - } - - @Override - public void logError(String message, Throwable error) { - System.out.println(message); - error.printStackTrace(System.out); - } - } - - @ClassRule - public static WireMockRule wiremock = new WireMockRule( - WireMockConfiguration.options().extensions(new ResponseTemplateTransformer(false)).port(0)); - - static final String jolokiaUrl = "service:jmx:kubernetes:///ns1/pod-abcdef/jolokia"; - - private static MBeanServerConnection jolokiaConnection; - - @BeforeClass - public static void connect() throws Exception { - CloseableHttpResponse configResponse = HttpClients.createDefault() - .execute(new HttpGet(wiremock.baseUrl() + "/mock-kube-config.yml")); - Assert.assertEquals(configResponse.getStatusLine().getStatusCode(), 200); - File configFile = File.createTempFile("mock-kube-config", ".yml"); - configResponse.getEntity().writeTo(new FileOutputStream(configFile)); - // we set this so the KubernetesDiscoveryListener will work - //Setting taken from: https://github.com/fabric8io/kubernetes-client/blob/77a65f7d40f31a5dc37492cd9de3c317c2702fb4/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/Config.java#L120, unlikely to change - System.setProperty("kubeconfig", configFile.getAbsolutePath()); - jolokiaConnection = getKubernetesMBeanConnector(); - } - - @Test - public void testExecuteOperation() throws InstanceNotFoundException, MalformedObjectNameException, MBeanException, - ReflectionException, MalformedURLException, IOException { - jolokiaConnection.invoke(new ObjectName("java.lang:type=Memory"), "gc", new Object[0], new String[0]); - } - - @Test - public void testReadAttribute() - throws InstanceNotFoundException, AttributeNotFoundException, InvalidAttributeValueException, - MalformedObjectNameException, MBeanException, ReflectionException, MalformedURLException, IOException { - MBeanServerConnection jmxConnection = jolokiaConnection; - assertOneSingleAttribute(jmxConnection); - - } - - private void assertOneSingleAttribute(MBeanServerConnection jmxConnection) throws MalformedObjectNameException, - MBeanException, AttributeNotFoundException, InstanceNotFoundException, ReflectionException, IOException { - ObjectName objectName = new ObjectName("java.lang:type=Memory"); - String attribute = "Verbose"; - Assert.assertEquals(false, jmxConnection.getAttribute(objectName, attribute)); - } - - @Before - public void reset() { - wiremock.resetAll(); - wiremock.resetRequests(); - } - - private static MBeanServerConnection getKubernetesMBeanConnector() throws IOException, MalformedURLException { - JMXConnector connector = new JmcKubernetesJmxConnectionProvider().newJMXConnector(new JMXServiceURL(jolokiaUrl), - Collections.emptyMap()); - connector.connect(); - MBeanServerConnection connection = connector.getMBeanServerConnection(); - return connection; - } - - @Test - public void testDiscoverWithMostlyDefaultSettings() throws Exception { - - TestParameters parameters = new TestParameters(); - // Set config so that scanning takes place - parameters.scanForInstances = true; - testThatJvmIsFound(parameters); - } - - @Test - public void testDiscoverWithPathFromAnnotation() throws Exception { - TestParameters parameters = new TestParameters(); - parameters.scanForInstances = true; - parameters.jolokiaPath = "${kubernetes/annotation/jolokiaPath}"; - testThatJvmIsFound(parameters); - } - - @Test - public void testDiscoverWithPortFromAnnotation() throws Exception { - TestParameters parameters = new TestParameters(); - parameters.scanForInstances = true; - parameters.jolokiaPort = "${kubernetes/annotation/jolokiaPort}"; - - testThatJvmIsFound(parameters); - } - - @Test - public void testDiscoverWithBasicAuthFromSecret() throws Exception { - TestParameters parameters = new TestParameters(); - parameters.scanForInstances = true; - parameters.credentials = new InMemoryCredentials("${kubernetes/secret/jolokia-auth/username}", - "${kubernetes/secret/jolokia-auth/password}"); - - testThatJvmIsFound(parameters); - // Verify that the expected authorization was picked up - WireMock.verify(WireMock - .postRequestedFor(WireMock.urlPathMatching("/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*")) - .withHeader("X-jolokia-authorization", - WireMock.equalTo("Basic " + Base64.getEncoder().encodeToString("admin:admin".getBytes())))); - } - - @Test - public void testDiscoverWithAuthFromProperties() throws Exception { - - TestParameters parameters = new TestParameters(); - parameters.scanForInstances = true; - parameters.credentials = new InMemoryCredentials("${kubernetes/secret/jolokia-properties/user}", - "${kubernetes/secret/jolokia-properties/password}"); - - testThatJvmIsFound(parameters); - // Verify that the expected authorization was picked up - WireMock.verify(WireMock - .postRequestedFor(WireMock.urlPathMatching("/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*")) - .withHeader("X-jolokia-authorization", - WireMock.equalTo("Basic " + Base64.getEncoder().encodeToString("admin:secret".getBytes())))); - } - - @Test - public void testDiscoverWithAuthDirectlyFromSettings() throws Exception { - - TestParameters parameters = new TestParameters(); - parameters.scanForInstances = true; - parameters.credentials = new InMemoryCredentials("user", "***"); - testThatJvmIsFound(parameters); - // Verify that the expected authorization was picked up - WireMock.verify(WireMock - .postRequestedFor(WireMock.urlPathMatching("/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*")) - .withHeader("X-jolokia-authorization", - WireMock.equalTo("Basic " + Base64.getEncoder().encodeToString("user:***".getBytes())))); - } - - private void testThatJvmIsFound(TestParameters parameters) throws Exception { - - final KubernetesDiscoveryListener scanner = new KubernetesDiscoveryListener(parameters); - final Map foundVms = new HashMap<>(); - IDescriptorListener descriptorListener = new IDescriptorListener() { - public void onDescriptorDetected( - IServerDescriptor serverDescriptor, String path, JMXServiceURL url, - IConnectionDescriptor connectionDescriptor, IDescribable provider) { - foundVms.put(serverDescriptor.getGUID(), serverDescriptor); - } - - public void onDescriptorRemoved(String descriptorId) { - foundVms.remove(descriptorId); - } - }; - scanner.addDescriptorListener(descriptorListener); - - try { - // Test that at least one VM (the one running the test was discovered) - - Awaitility.await().atMost(Duration.ofSeconds(5)).until(() -> !foundVms.isEmpty()); - IServerDescriptor descriptor = foundVms.get("pod-abcdef"); - Assert.assertNotNull(descriptor); - Assert.assertEquals( - "[JVMDescriptor] Java command: /Users/marska/Downloads/hawtio-app-2.9.1.jar --port 9090 PID: 88774", - descriptor.getJvmInfo().toString()); - Assert.assertEquals(JVMType.HOTSPOT, descriptor.getJvmInfo().getJvmType()); - Assert.assertEquals("18.0.1", descriptor.getJvmInfo().getJavaVersion()); - Assert.assertTrue(descriptor instanceof IConnectionDescriptor); - IConnectionDescriptor connectDescriptor = (IConnectionDescriptor) descriptor; - JMXConnector connector = new JmcKubernetesJmxConnectionProvider() - .newJMXConnector(connectDescriptor.createJMXServiceURL(), connectDescriptor.getEnvironment()); - connector.connect(); - assertOneSingleAttribute(connector.getMBeanServerConnection()); - - } finally { - // Tell scanner thread to exit - scanner.shutdown(); - } - } -} diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-attribute.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-attribute.json deleted file mode 100644 index 4230121a7e..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-attribute.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "request" : { - "attribute" : "Verbose", - "mbean" : "java.lang:type=Memory", - "type" : "read" - }, - "status" : 200, - "timestamp" : 1658563869, - "value" : false -} diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-basic-auth-secret.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-basic-auth-secret.json deleted file mode 100644 index b5bb351b7e..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-basic-auth-secret.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "apiVersion": "v1", - "data": { - "password": "YWRtaW4=", - "username": "YWRtaW4=" - }, - "kind": "Secret", - "metadata": { - "annotations": { - "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Secret\",\"metadata\":{\"annotations\":{},\"name\":\"jolokia-auth\",\"namespace\":\"jfr\"},\"stringData\":{\"password\":\"admin\",\"username\":\"admin\"},\"type\":\"kubernetes.io/basic-auth\"}\n" - }, - "creationTimestamp": "2022-05-20T13:59:12Z", - "name": "jolokia-auth", - "namespace": "jfr", - "resourceVersion": "130", - "uid": "e1563217-ef08-481d-b2e3-233fa3040b56" - }, - "type": "kubernetes.io/basic-auth" -} diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-exec.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-exec.json deleted file mode 100644 index 5bd35a69f2..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-exec.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "request" : { - "mbean" : "java.lang:type=Memory", - "operation" : "gc()", - "type" : "exec" - }, - "status" : 200, - "timestamp" : 1658567003, - "value" : null -} diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-properties-secret.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-properties-secret.json deleted file mode 100644 index e12bbd16c1..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-properties-secret.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "apiVersion": "v1", - "data": { - "jolokia.properties": "aG9zdD0wLjAuMC4wCnBhc3N3b3JkPXNlY3JldAp1c2VyPWFkbWluCnBvcnQ9ODc3OApkaXNjb3ZlcnlFbmFibGVkPXRydWUKZGlzY292ZXJ5QWdlbnRVcmw9aHR0cDovLyR7aG9zdH06ODc3OC9qb2xva2lhLwpwb2xpY3lMb2NhdGlvbj1jbGFzc3BhdGg6L0JPT1QtSU5GL2NsYXNzZXMvam9sb2tpYS1hY2Nlc3MueG1s" - }, - "kind": "Secret", - "metadata": { - "creationTimestamp": "2022-05-20T13:59:12Z", - "name": "jolokia-properties", - "namespace": "jfr", - "resourceVersion": "147", - "uid": "025bf17a-cff5-46e6-8499-dd9d6de43fa9" - }, - "type": "Opaque" -} diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-version.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-version.json deleted file mode 100644 index 6299d0407a..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-version.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "request": { - "type": "version" - }, - "value": { - "agent": "1.7.1", - "protocol": "7.2", - "config": { - "listenForHttpService": "true", - "authIgnoreCerts": "false", - "agentId": "192.168.1.104-28660-5bd82fed-servlet", - "debug": "false", - "agentType": "servlet", - "policyLocation": "classpath:\/jolokia-access.xml", - "agentContext": "\/jolokia", - "serializeException": "false", - "mimeType": "text\/plain", - "dispatcherClasses": "org.jolokia.http.Jsr160ProxyNotEnabledByDefaultAnymoreDispatcher", - "authMode": "basic", - "authMatch": "any", - "streaming": "true", - "canonicalNaming": "true", - "historyMaxEntries": "10", - "allowErrorDetails": "false", - "allowDnsReverseLookup": "true", - "realm": "jolokia", - "includeStackTrace": "false", - "restrictorClass": "io.hawt.system.RBACRestrictor", - "mbeanQualifier": "qualifier=hawtio", - "useRestrictorService": "false", - "debugMaxEntries": "100" - }, - "info": { - "product": "jetty", - "vendor": "Eclipse", - "version": "9.4.z-SNAPSHOT" - } - }, - "timestamp": 1658556959, - "status": 200 -} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/memory-list.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/memory-list.json deleted file mode 100644 index 46d117f2e4..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/memory-list.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "request" : { - "path" : "java.lang/type=Memory", - "type" : "list" - }, - "status" : 200, - "timestamp" : 1658566168, - "value" : { - "attr" : { - "HeapMemoryUsage" : { - "desc" : "HeapMemoryUsage", - "rw" : false, - "type" : "javax.management.openmbean.CompositeData" - }, - "NonHeapMemoryUsage" : { - "desc" : "NonHeapMemoryUsage", - "rw" : false, - "type" : "javax.management.openmbean.CompositeData" - }, - "ObjectName" : { - "desc" : "ObjectName", - "rw" : false, - "type" : "javax.management.ObjectName" - }, - "ObjectPendingFinalizationCount" : { - "desc" : "ObjectPendingFinalizationCount", - "rw" : false, - "type" : "int" - }, - "Verbose" : { - "desc" : "Verbose", - "rw" : true, - "type" : "boolean" - } - }, - "class" : "sun.management.MemoryImpl", - "desc" : "Information on the management interface of the MBean", - "op" : { - "gc" : { - "args" : [], - "desc" : "gc", - "ret" : "void" - } - } - } -} diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/mock-kube-config.yml b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/mock-kube-config.yml deleted file mode 100644 index f222f471a7..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/mock-kube-config.yml +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: v1 -clusters: -- cluster: - server: {{request.baseUrl}}/ - name: test -contexts: -- context: - cluster: test - user: test - name: test -current-context: test -kind: Config -preferences: {} -users: -- name: test - user: - token: foobar diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/pod.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/pod.json deleted file mode 100644 index 77a61a3e32..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/pod.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "metadata": { - "clusterName": "test", - "labels": { - "jolokia": true - }, - "name": "pod-abcdef", - "namespace": "ns1", - "selfLink": "/api/v1/namespaces/ns1/pods/pod-abcdef" - } -} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/pods.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/pods.json deleted file mode 100644 index 114edd3640..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/pods.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "items": [ - { - "metadata": { - "clusterName": "test", - "labels": { - "jolokia": true - }, - "annotations": { - "jolokiaPort": 8778, - "jolokiaPath": "jolokia" - }, - "name": "pod-abcdef", - "namespace": "ns1", - "selfLink": "/api/v1/namespaces/ns1/pods/pod-abcdef" - } - } - ] -} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/runtime-list.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/runtime-list.json deleted file mode 100644 index c096d78c52..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/runtime-list.json +++ /dev/null @@ -1,104 +0,0 @@ -{ - "request" : { - "path" : "java.lang/type=Runtime", - "type" : "list" - }, - "status" : 200, - "timestamp" : 1658564737, - "value" : { - "attr" : { - "BootClassPath" : { - "desc" : "BootClassPath", - "rw" : false, - "type" : "java.lang.String" - }, - "BootClassPathSupported" : { - "desc" : "BootClassPathSupported", - "rw" : false, - "type" : "boolean" - }, - "ClassPath" : { - "desc" : "ClassPath", - "rw" : false, - "type" : "java.lang.String" - }, - "InputArguments" : { - "desc" : "InputArguments", - "rw" : false, - "type" : "[Ljava.lang.String;" - }, - "LibraryPath" : { - "desc" : "LibraryPath", - "rw" : false, - "type" : "java.lang.String" - }, - "ManagementSpecVersion" : { - "desc" : "ManagementSpecVersion", - "rw" : false, - "type" : "java.lang.String" - }, - "Name" : { - "desc" : "Name", - "rw" : false, - "type" : "java.lang.String" - }, - "ObjectName" : { - "desc" : "ObjectName", - "rw" : false, - "type" : "javax.management.ObjectName" - }, - "Pid" : { - "desc" : "Pid", - "rw" : false, - "type" : "long" - }, - "SpecName" : { - "desc" : "SpecName", - "rw" : false, - "type" : "java.lang.String" - }, - "SpecVendor" : { - "desc" : "SpecVendor", - "rw" : false, - "type" : "java.lang.String" - }, - "SpecVersion" : { - "desc" : "SpecVersion", - "rw" : false, - "type" : "java.lang.String" - }, - "StartTime" : { - "desc" : "StartTime", - "rw" : false, - "type" : "long" - }, - "SystemProperties" : { - "desc" : "SystemProperties", - "rw" : false, - "type" : "javax.management.openmbean.TabularData" - }, - "Uptime" : { - "desc" : "Uptime", - "rw" : false, - "type" : "long" - }, - "VmName" : { - "desc" : "VmName", - "rw" : false, - "type" : "java.lang.String" - }, - "VmVendor" : { - "desc" : "VmVendor", - "rw" : false, - "type" : "java.lang.String" - }, - "VmVersion" : { - "desc" : "VmVersion", - "rw" : false, - "type" : "java.lang.String" - } - }, - "class" : "sun.management.RuntimeImpl", - "desc" : "Information on the management interface of the MBean" - } -} diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/secrets.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/secrets.json deleted file mode 100644 index ac68f94b51..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/secrets.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "apiVersion": "v1", - "items": [ - { - "apiVersion": "v1", - "data": { - "password": "YWRtaW4=", - "username": "YWRtaW4=" - }, - "kind": "Secret", - "metadata": { - "annotations": { - "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Secret\",\"metadata\":{\"annotations\":{},\"name\":\"jolokia-auth\",\"namespace\":\"ns1\"},\"stringData\":{\"password\":\"admin\",\"username\":\"admin\"},\"type\":\"kubernetes.io/basic-auth\"}\n" - }, - "creationTimestamp": "2022-05-20T13:59:12Z", - "name": "jolokia-auth", - "namespace": "ns1", - "resourceVersion": "130", - "uid": "e1563217-ef08-481d-b2e3-233fa3040b56" - }, - "type": "kubernetes.io/basic-auth" - }, - { - "apiVersion": "v1", - "data": { - "jolokia.properties": "aG9zdD0wLjAuMC4wCnBhc3N3b3JkPXNlY3JldAp1c2VyPWFkbWluCnBvcnQ9ODc3OApkaXNjb3ZlcnlFbmFibGVkPXRydWUKZGlzY292ZXJ5QWdlbnRVcmw9aHR0cDovLyR7aG9zdH06ODc3OC9qb2xva2lhLwpwb2xpY3lMb2NhdGlvbj1jbGFzc3BhdGg6L0JPT1QtSU5GL2NsYXNzZXMvam9sb2tpYS1hY2Nlc3MueG1s" - }, - "kind": "Secret", - "metadata": { - "creationTimestamp": "2022-05-20T13:59:12Z", - "name": "jolokia-properties", - "namespace": "ns1", - "resourceVersion": "147", - "uid": "025bf17a-cff5-46e6-8499-dd9d6de43fa9" - }, - "type": "Opaque" - } - ], - "kind": "List", - "metadata": { - "resourceVersion": "" - } -} diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/system-attributes.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/system-attributes.json deleted file mode 100644 index dc2ff55433..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/system-attributes.json +++ /dev/null @@ -1,101 +0,0 @@ -[ - { - "request" : { - "attribute" : "Pid", - "mbean" : "java.lang:type=Runtime", - "type" : "read" - }, - "status" : 200, - "timestamp" : 1658586915, - "value" : 88774 - }, - { - "request" : { - "attribute" : "Name", - "mbean" : "java.lang:type=Runtime", - "type" : "read" - }, - "status" : 200, - "timestamp" : 1658586915, - "value" : "88774@Martins-MacBook-Pro-2.local" - }, - { - "request" : { - "attribute" : "InputArguments", - "mbean" : "java.lang:type=Runtime", - "type" : "read" - }, - "status" : 200, - "timestamp" : 1658586915, - "value" : [] - }, - { - "request" : { - "attribute" : "SystemProperties", - "mbean" : "java.lang:type=Runtime", - "type" : "read" - }, - "status" : 200, - "timestamp" : 1658586915, - "value" : { - "apple.awt.application.name" : "App", - "file.encoding" : "UTF-8", - "file.separator" : "/", - "ftp.nonProxyHosts" : "local|*.local|169.254/16|*.169.254/16", - "hawtio.authenticationEnabled" : "false", - "hawtio.redirect.scheme" : "http", - "hawtio.url" : "http://localhost:9090/hawtio", - "http.nonProxyHosts" : "local|*.local|169.254/16|*.169.254/16", - "java.class.path" : "/Users/marska/Downloads/hawtio-app-2.9.1.jar", - "java.class.version" : "62.0", - "java.home" : "/Library/Java/JavaVirtualMachines/temurin-18.jdk/Contents/Home", - "java.io.tmpdir" : "/var/folders/1f/cdm0073x1mj1swnhtw181_4m0000gn/T/", - "java.library.path" : "/Users/marska/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:.", - "java.runtime.name" : "OpenJDK Runtime Environment", - "java.runtime.version" : "18.0.1+10", - "java.specification.name" : "Java Platform API Specification", - "java.specification.vendor" : "Oracle Corporation", - "java.specification.version" : "18", - "java.vendor" : "Eclipse Adoptium", - "java.vendor.url" : "https://adoptium.net/", - "java.vendor.url.bug" : "https://github.com/adoptium/adoptium-support/issues", - "java.vendor.version" : "Temurin-18.0.1+10", - "java.version" : "18.0.1", - "java.version.date" : "2022-04-19", - "java.vm.compressedOopsMode" : "Zero based", - "java.vm.info" : "mixed mode, sharing", - "java.vm.name" : "OpenJDK 64-Bit Server VM", - "java.vm.specification.name" : "Java Virtual Machine Specification", - "java.vm.specification.vendor" : "Oracle Corporation", - "java.vm.specification.version" : "18", - "java.vm.vendor" : "Eclipse Adoptium", - "java.vm.version" : "18.0.1+10", - "jdk.debug" : "release", - "line.separator" : "\n", - "native.encoding" : "UTF-8", - "org.eclipse.jetty.util.log.class" : "org.eclipse.jetty.util.log.Slf4jLog", - "os.arch" : "x86_64", - "os.name" : "Mac OS X", - "os.version" : "11.6.6", - "path.separator" : ":", - "socksNonProxyHosts" : "local|*.local|169.254/16|*.169.254/16", - "sun.arch.data.model" : "64", - "sun.awt.enableExtraMouseButtons" : "true", - "sun.boot.library.path" : "/Library/Java/JavaVirtualMachines/temurin-18.jdk/Contents/Home/lib", - "sun.cpu.endian" : "little", - "sun.io.unicode.encoding" : "UnicodeBig", - "sun.java.command" : "/Users/marska/Downloads/hawtio-app-2.9.1.jar --port 9090", - "sun.java.launcher" : "SUN_STANDARD", - "sun.jnu.encoding" : "UTF-8", - "sun.management.compiler" : "HotSpot 64-Bit Tiered Compilers", - "sun.stderr.encoding" : "UTF-8", - "sun.stdout.encoding" : "UTF-8", - "user.country" : "NO", - "user.dir" : "/Users/marska/Documents/dev/jmc/jmc8/releng/third-party", - "user.home" : "/Users/marska", - "user.language" : "nb", - "user.name" : "marska", - "user.timezone" : "Asia/Taipei" - } - } -] diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-exec.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-exec.json deleted file mode 100644 index 6bb03ececf..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-exec.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "request": { - "method": "POST", - "urlPath": "/api/v1/namespaces/ns1/pods/pod-abcdef/proxy/jolokia", - "bodyPatterns": [ - { - "equalToJson": "{\"operation\":\"gc()\",\"mbean\":\"java.lang:type=Memory\",\"type\":\"EXEC\"}" - } - ] - }, - "response": { - "status": 200, - "bodyFileName": "jolokia-exec.json" - } -} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-list-memory.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-list-memory.json deleted file mode 100644 index e4170948cb..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-list-memory.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "request": { - "method": "POST", - "urlPathPattern": "/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*", - "bodyPatterns": [ - { - "equalToJson": "{\"path\":\"java.lang/type=Memory\",\"type\":\"LIST\"}" - } - ] - }, - "response": { - "status": 200, - "bodyFileName": "memory-list.json" - } -} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-list-runtime.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-list-runtime.json deleted file mode 100644 index f936e7eb0f..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-list-runtime.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "request": { - "method": "POST", - "urlPathPattern": "/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*", - "bodyPatterns": [ - { - "equalToJson": "{\"path\":\"java.lang/type=Runtime\",\"type\":\"LIST\"}" - } - ] - }, - "response": { - "status": 200, - "bodyFileName": "runtime-list.json" - } -} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-probe.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-probe.json deleted file mode 100644 index 5b26da423d..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-probe.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "request": { - "method": "POST", - "urlPathPattern": "/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*", - "bodyPatterns": [ - { - "equalToJson": "{\"type\":\"version\"}" - } - ] - }, - "response": { - "status": 200, - "bodyFileName": "jolokia-version.json" - } -} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-probe2.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-probe2.json deleted file mode 100644 index 98670016fd..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-probe2.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "request": { - "method": "POST", - "urlPathPattern": "/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*", - "bodyPatterns": [ - { - "equalToJson": "{\"type\":\"VERSION\"}" - } - ] - }, - "response": { - "status": 200, - "bodyFileName": "jolokia-version.json" - } -} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-read-attribute.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-read-attribute.json deleted file mode 100644 index 1584e3b4a3..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-read-attribute.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "request": { - "method": "POST", - "urlPathPattern": "/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*", - "bodyPatterns": [ - { - "equalToJson": "{\"attribute\":\"Verbose\",\"mbean\":\"java.lang:type=Memory\",\"type\":\"READ\"}" - } - ] - }, - "response": { - "status": 200, - "bodyFileName": "jolokia-attribute.json" - } -} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jvm-properties.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jvm-properties.json deleted file mode 100644 index 5b26da423d..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jvm-properties.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "request": { - "method": "POST", - "urlPathPattern": "/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*", - "bodyPatterns": [ - { - "equalToJson": "{\"type\":\"version\"}" - } - ] - }, - "response": { - "status": 200, - "bodyFileName": "jolokia-version.json" - } -} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/kube-config.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/kube-config.json deleted file mode 100644 index 418a887637..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/kube-config.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "request": { - "method": "GET", - "url": "/mock-kube-config.yml" - }, - "response": { - "status": 200, - "bodyFileName": "mock-kube-config.yml", - "transformers": [ - "response-template" - ] - } -} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/pod.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/pod.json deleted file mode 100644 index ebcbd7c5e3..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/pod.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "request": { - "method": "GET", - "url": "/api/v1/namespaces/ns1/pods/pod-abcdef" - }, - "response": { - "status": 200, - "bodyFileName": "pod.json" - } -} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/pods.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/pods.json deleted file mode 100644 index 061fb0f53b..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/pods.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "request": { - "method": "GET", - "urlPath": "/api/v1/pods" - }, - "response": { - "status": 200, - "bodyFileName": "pods.json" - } -} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/secrets.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/secrets.json deleted file mode 100644 index 113bb91e1f..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/secrets.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "request": { - "method": "GET", - "url": "/api/v1/namespaces/ns1/secrets" - }, - "response": { - "status": 200, - "bodyFileName": "secrets.json" - } -} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/system-attributes.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/system-attributes.json deleted file mode 100644 index e3d1147b2f..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/system-attributes.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "request": { - "method": "POST", - "urlPathPattern": "/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*", - "bodyPatterns": [ - { - "equalToJson": "[{\"attribute\":\"Pid\",\"mbean\":\"java.lang:type=Runtime\",\"type\":\"READ\"},{\"attribute\":\"Name\",\"mbean\":\"java.lang:type=Runtime\",\"type\":\"READ\"},{\"attribute\":\"InputArguments\",\"mbean\":\"java.lang:type=Runtime\",\"type\":\"READ\"},{\"attribute\":\"SystemProperties\",\"mbean\":\"java.lang:type=Runtime\",\"type\":\"READ\"}]" - } - ] - }, - "response": { - "status": 200, - "bodyFileName": "system-attributes.json" - } -} \ No newline at end of file diff --git a/application/uitests/pom.xml b/application/uitests/pom.xml index f6496c214d..98327fd9a8 100644 --- a/application/uitests/pom.xml +++ b/application/uitests/pom.xml @@ -1,6 +1,6 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/application/org.openjdk.jmc.kubernetes/pom.xml b/application/org.openjdk.jmc.kubernetes/pom.xml new file mode 100644 index 0000000000..8593216ed0 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/pom.xml @@ -0,0 +1,49 @@ + + + + 4.0.0 + + org.openjdk.jmc + missioncontrol.application + ${revision}${changelist} + + org.openjdk.jmc.kubernetes + eclipse-plugin + + ${project.basedir}/../../configuration + + diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnection.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnection.java new file mode 100644 index 0000000000..6aae451116 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnection.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Kantega AS. All rights reserved. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The contents of this file are subject to the terms of either the Universal Permissive License + * v 1.0 as shown at http://oss.oracle.com/licenses/upl + * + * or the following license: + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.openjdk.jmc.kubernetes; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; +import java.util.regex.Pattern; + +import javax.management.InstanceNotFoundException; + +import org.jolokia.client.J4pClient; +import org.jolokia.client.exception.J4pException; +import org.jolokia.client.exception.J4pRemoteException; +import org.jolokia.client.request.J4pResponse; +import org.openjdk.jmc.jolokia.JmcJolokiaJmxConnection; +import org.openjdk.jmc.rjmx.common.ConnectionException; + +/** + * Jolokia based MBeanServerConnector tailored for JMC needs + */ +public class JmcKubernetesJmxConnection extends JmcJolokiaJmxConnection { + + static final Collection DISCONNECT_SIGNS = Arrays.asList(Pattern.compile("Error: pods \".+\" not found")); //$NON-NLS-1$ + + public JmcKubernetesJmxConnection(J4pClient client) throws IOException { + super(client); + } + + @SuppressWarnings("rawtypes") + @Override + protected J4pResponse unwrapException(J4pException e) throws IOException, InstanceNotFoundException { + // recognize signs of disconnect and signal to the application for better + // handling + if (isKnownDisconnectException(e)) { + throw new ConnectionException(e.getMessage()); + } else { + return super.unwrapException(e); + } + } + + private boolean isKnownDisconnectException(J4pException e) { + if (!(e instanceof J4pRemoteException)) { + return false; + } + if (!"io.fabric8.kubernetes.client.KubernetesClientException".equals(((J4pRemoteException) e).getErrorType())) { //$NON-NLS-1$ + return false; + } + return DISCONNECT_SIGNS.stream().anyMatch(pattern -> pattern.matcher(e.getMessage()).matches()); + } + +} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnectionProvider.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnectionProvider.java new file mode 100644 index 0000000000..b74b999f68 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnectionProvider.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Kantega AS. All rights reserved. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The contents of this file are subject to the terms of either the Universal Permissive License + * v 1.0 as shown at http://oss.oracle.com/licenses/upl + * + * or the following license: + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.openjdk.jmc.kubernetes; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.util.Map; + +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorProvider; +import javax.management.remote.JMXServiceURL; + +/** + * This {@code JMXConnectionProvider} handles the "kubernetes" protocol. + */ +public class JmcKubernetesJmxConnectionProvider implements JMXConnectorProvider { + @Override + public JMXConnector newJMXConnector(JMXServiceURL serviceURL, Map environment) throws IOException { + if (!"kubernetes".equals(serviceURL.getProtocol())) { //$NON-NLS-1$ + throw new MalformedURLException("I only serve Kubernetes connections"); //$NON-NLS-1$ + } + return new JmcKubernetesJmxConnector(serviceURL, environment); + } +} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnector.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnector.java new file mode 100644 index 0000000000..0d1d98264a --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnector.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Kantega AS. All rights reserved. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The contents of this file are subject to the terms of either the Universal Permissive License + * v 1.0 as shown at http://oss.oracle.com/licenses/upl + * + * or the following license: + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.openjdk.jmc.kubernetes; + +import java.io.IOException; +import java.util.Map; + +import javax.management.remote.JMXServiceURL; + +import org.jolokia.client.J4pClient; +import org.jolokia.client.jmxadapter.RemoteJmxAdapter; +import org.jolokia.kubernetes.client.KubernetesJmxConnector; + +public class JmcKubernetesJmxConnector extends KubernetesJmxConnector { + + public JmcKubernetesJmxConnector(JMXServiceURL serviceURL, Map environment) { + super(serviceURL, environment); + } + + @Override + protected RemoteJmxAdapter createAdapter(J4pClient client) throws IOException { + return new JmcKubernetesJmxConnection(client); + } +} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesPlugin.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesPlugin.java new file mode 100644 index 0000000000..a0cd408f67 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesPlugin.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Kantega AS. All rights reserved. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The contents of this file are subject to the terms of either the Universal Permissive License + * v 1.0 as shown at http://oss.oracle.com/licenses/upl + * + * or the following license: + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.openjdk.jmc.kubernetes; + +import org.eclipse.core.runtime.Platform; +import org.openjdk.jmc.kubernetes.preferences.KubernetesScanningParameters; +import org.openjdk.jmc.kubernetes.preferences.PreferenceConstants; +import org.openjdk.jmc.ui.MCAbstractUIPlugin; +import org.openjdk.jmc.common.security.ICredentials; +import org.openjdk.jmc.common.security.PersistentCredentials; +import org.openjdk.jmc.common.security.SecurityException; +import org.openjdk.jmc.common.security.SecurityManagerFactory; +import org.openjdk.jmc.ui.misc.DisplayToolkit; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; + +public class JmcKubernetesPlugin extends MCAbstractUIPlugin + implements KubernetesScanningParameters, PreferenceConstants { + + public final static String PLUGIN_ID = "org.openjdk.jmc.kubernetes"; //$NON-NLS-1$ + + // The shared instance. + private static JmcKubernetesPlugin plugin; + + /** + * The constructor. + */ + public JmcKubernetesPlugin() { + super(PLUGIN_ID); + plugin = this; + } + + /** + * @return the shared instance. + */ + public static JmcKubernetesPlugin getDefault() { + return plugin; + } + + private void ensureNeededCredentialsAreUnlocked() { + if (getScanningCredentials() != null && SecurityManagerFactory.getSecurityManager().isLocked()) { + DisplayToolkit.safeAsyncExec(() -> { + try { + SecurityManagerFactory.getSecurityManager().unlock(); + } catch (SecurityException e) { + logError("Error unlocking credentials needed for kubernetes scanning", e);//$NON-NLS-1$ + } + }); + } + } + + @Override + public boolean scanForInstances() { + // If credentials are locked and credentials are required, the scanner thread + // will get hung + // therefore await credentials store to be unlocked before proceeding to scan + return getPreferenceStore().getBoolean(P_SCAN_FOR_INSTANCES) + && (getScanningCredentials() == null || !SecurityManagerFactory.getSecurityManager().isLocked()); + + } + + @Override + public boolean scanAllContexts() { + return getPreferenceStore().getBoolean(P_SCAN_ALL_CONTEXTS); + } + + @Override + public String jolokiaPort() { + return getPreferenceStore().getString(P_JOLOKIA_PORT); + } + + private PersistentCredentials getScanningCredentials() { + String key = getPreferenceStore().getString(P_CREDENTIALS_KEY); + return key == null ? null : new PersistentCredentials(key); + } + + public ICredentials storeCredentials(String username, String password) throws SecurityException { + PersistentCredentials credentials = new PersistentCredentials(username, password, "kubernetes");//$NON-NLS-1$ + getPreferenceStore().setValue(P_CREDENTIALS_KEY, credentials.getExportedId()); + return credentials; + } + + @Override + public String username() throws SecurityException { + final PersistentCredentials cred = getScanningCredentials(); + if (cred == null) { + return "";//$NON-NLS-1$ + } else { + return cred.getUsername(); + } + } + + @Override + public String password() throws SecurityException { + final PersistentCredentials cred = getScanningCredentials(); + if (cred == null) { + return "";//$NON-NLS-1$ + } else { + return cred.getPassword(); + } + } + + @Override + public String jolokiaPath() { + return getPreferenceStore().getString(P_JOLOKIA_PATH); + } + + @Override + public String requireLabel() { + return getPreferenceStore().getString(P_REQUIRE_LABEL); + } + + @Override + public String jolokiaProtocol() { + return getPreferenceStore().getString(P_JOLOKIA_PROTOCOL); + } + + @Override + public void logError(String message, Throwable error) { + Platform.getLog(FrameworkUtil.getBundle(getClass())).error(message, error); + } + + @Override + public void start(BundleContext context) throws Exception { + super.start(context); + this.ensureNeededCredentialsAreUnlocked(); + } + +} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/KubernetesDiscoveryListener.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/KubernetesDiscoveryListener.java new file mode 100644 index 0000000000..bcb2420ac3 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/KubernetesDiscoveryListener.java @@ -0,0 +1,340 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Kantega AS. All rights reserved. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The contents of this file are subject to the terms of either the Universal Permissive License + * v 1.0 as shown at http://oss.oracle.com/licenses/upl + * + * or the following license: + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.openjdk.jmc.kubernetes; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.NoSuchElementException; +import java.util.Properties; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXServiceURL; + +import org.apache.commons.codec.binary.Base64; +import org.jolokia.client.J4pClient; +import org.jolokia.kubernetes.client.KubernetesJmxConnector; +import org.jolokia.server.core.osgi.security.AuthorizationHeaderParser; +import org.jolokia.server.core.util.Base64Util; +import org.openjdk.jmc.jolokia.AbstractCachedDescriptorProvider; +import org.openjdk.jmc.jolokia.JolokiaAgentDescriptor; +import org.openjdk.jmc.jolokia.ServerConnectionDescriptor; +import org.openjdk.jmc.kubernetes.preferences.KubernetesScanningParameters; +import org.openjdk.jmc.common.jvm.JVMDescriptor; +import org.openjdk.jmc.common.security.SecurityException; + +import io.fabric8.kubernetes.api.model.NamedContext; +import io.fabric8.kubernetes.api.model.ObjectMeta; +import io.fabric8.kubernetes.api.model.Pod; +import io.fabric8.kubernetes.api.model.PodList; +import io.fabric8.kubernetes.api.model.Secret; +import io.fabric8.kubernetes.client.Config; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.internal.KubeConfigUtils; +import io.fabric8.kubernetes.client.utils.Utils; +import io.fabric8.kubernetes.client.dsl.AnyNamespaceOperation; +import io.fabric8.kubernetes.client.dsl.PodResource; + +/** + * This class attempts to connect to JVMs in pods running in kubernetes in a background thread. + * Enablement and parameters for the scanning is given by user preferences. + */ +public class KubernetesDiscoveryListener extends AbstractCachedDescriptorProvider { + + private final static Pattern SECRET_PATTERN = Pattern + .compile("\\$\\{kubernetes/secret/(?[^/]+)/(?[^\\}]+)}"); //$NON-NLS-1$ + private final static Pattern ATTRIBUTE_PATTERN = Pattern + .compile("\\$\\{kubernetes/annotation/(?[^/]+)}"); //$NON-NLS-1$ + private final static Set VALID_JOLOKIA_PROTOCOLS = new HashSet<>(Arrays.asList("http", "https")); //$NON-NLS-1$ //$NON-NLS-2$ + + KubernetesScanningParameters settings; + + public KubernetesDiscoveryListener() { + this(JmcKubernetesPlugin.getDefault()); + } + + //Public constructor in order for test plugin to be able to rig tests in an easier manner + public KubernetesDiscoveryListener(KubernetesScanningParameters parameters) { + this.settings = parameters; + } + + public final String getDescription() { + return Messages.KubernetesDiscoveryListener_Description; + } + + @Override + public String getName() { + return "kubernetes"; //$NON-NLS-1$ + } + + boolean notEmpty(String value) { + return value != null && value.length() > 0; + } + + private List contexts; + private long contextsCached = 0L; + + private List allContexts() throws IOException { + final String path = Utils.getSystemPropertyOrEnvVar(Config.KUBERNETES_KUBECONFIG_FILE, + new File(System.getProperty("user.home"), ".kube" + File.separator + "config").toString()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + File configPath = new File(path); + if (contexts != null && contextsCached > configPath.lastModified()) {// the YAML parsing is soo incredibly + // sloow, hence cache context names for + // later + // runs + return contexts; + } + // reload config if kubeconfig has been modified since we cached the config + io.fabric8.kubernetes.api.model.Config config = KubeConfigUtils.parseConfig(configPath); + this.contextsCached = System.currentTimeMillis(); + KubernetesJmxConnector.resetKubernetesConfig(); + return contexts = config.getContexts().stream().map(NamedContext::getName).collect(Collectors.toList()); + } + + @Override + protected Map discoverJvms() { + return scanForJvms(); + } + + Map scanForJvms() { + Map found = new HashMap<>(); + if (!isEnabled()) { + return found; + } + boolean hasScanned = false; + + if (settings.scanAllContexts()) { + try { + for (final String context : allContexts()) { + hasScanned = true; + scanContext(found, settings, context); + } + } catch (IOException e) { + settings.logError(Messages.KubernetesDiscoveryListener_UnableToFindContexts, e); + } + } + if (!hasScanned) {// scan default context + return scanContext(found, settings, null); + } + return found; + } + + private Map scanContext( + Map found, KubernetesScanningParameters parameters, String context) { + try { + scanContextUnsafe(found, parameters, context); + } catch (Exception e) { + parameters.logError(Messages.KubernetesDiscoveryListener_UnableToScan + context, e); + } + return found; + } + + private Map scanContextUnsafe( + Map found, KubernetesScanningParameters parameters, String context) { + String pathLabel = parameters.requireLabel(); + KubernetesClient client = KubernetesJmxConnector.getApiClient(context); + + AnyNamespaceOperation query = client.pods().inAnyNamespace(); + List podList; + boolean hasPathLabel = notEmpty(pathLabel); + if (hasPathLabel) { + podList = query.withLabel(pathLabel).list().getItems(); + } else { + podList = query.list().getItems(); + } + // can consider parallelization for big contexts, however since it is the + // background await the situation a bit + podList.stream().forEach(pod -> scanPod(found, parameters, context, client, pod)); + return found; + } + + private void scanPod( + Map found, KubernetesScanningParameters parameters, String context, + KubernetesClient client, Pod pod) { + + final ObjectMeta metadata = pod.getMetadata(); + HashMap headers = new HashMap<>(); + Map env = new HashMap<>(); + try { + if (notEmpty(parameters.username())) { + if (!notEmpty(parameters.password())) { + throw new IllegalArgumentException(Messages.KubernetesDiscoveryListener_MustProvidePassword); + } + authorize(headers, client, parameters.username(), parameters.password(), metadata.getNamespace(), env); + } + } catch (SecurityException e) { + // skipping authorization if anything fails + } + final StringBuilder url = new StringBuilder("/api/").append(pod.getApiVersion()).append("/namespaces/") + .append(metadata.getNamespace()).append("/pods/"); + // JMX url must be reverse constructed, so that we can connect from the + // resulting node in the JVM browser + final StringBuilder jmxUrl = new StringBuilder("service:jmx:kubernetes:///").append(metadata.getNamespace()) //$NON-NLS-1$ + .append('/'); + + final String protocol = getValueOrAttribute(parameters.jolokiaProtocol(), metadata); + final String podName = metadata.getName(); + if (notEmpty(protocol)) { + if (!VALID_JOLOKIA_PROTOCOLS.contains(protocol)) { + throw new IllegalArgumentException(Messages.KubernetesDiscoveryListener_JolokiaProtocol + protocol + + Messages.KubernetesDiscoveryListener_HttpOrHttps); + } + // a bit clumsy, need to inject protocol _before_ podname in selflink + url.append(protocol).append(':'); + jmxUrl.append(protocol).append(':'); + } + + jmxUrl.append(podName); + url.append(podName); + + final String port = getValueOrAttribute(parameters.jolokiaPort(), metadata); + if (port != null) { + url.append(":").append(port); //$NON-NLS-1$ + jmxUrl.append(':').append(port); + } + + url.append("/proxy"); //$NON-NLS-1$ + + final String path = getValueOrAttribute(parameters.jolokiaPath(), metadata); + + if (!path.startsWith("/")) { //$NON-NLS-1$ + url.append('/'); + jmxUrl.append('/'); + } + url.append(path); + jmxUrl.append(path); + + if (context != null) { + env.put(KubernetesJmxConnector.KUBERNETES_CLIENT_CONTEXT, context); + } + J4pClient jvmClient = KubernetesJmxConnector.probeProxyPath(env, client, url, headers); + if (jvmClient != null) { + JmcKubernetesJmxConnection connection; + try { + connection = new JmcKubernetesJmxConnection(jvmClient); + JVMDescriptor jvmDescriptor = JolokiaAgentDescriptor.attemptToGetJvmInfo(connection); + JMXServiceURL jmxServiceURL = new JMXServiceURL(jmxUrl.toString()); + KubernetesJvmDescriptor descriptor = new KubernetesJvmDescriptor(metadata, jvmDescriptor, jmxServiceURL, + env); + found.put(descriptor.getGUID(), descriptor); + } catch (IOException e) { + parameters.logError(Messages.KubernetesDiscoveryListener_ErrConnectingToJvm, e); + + } + } + } + + private String getValueOrAttribute(String configValue, ObjectMeta metadata) { + if (notEmpty(configValue)) { + Matcher pattern = ATTRIBUTE_PATTERN.matcher(configValue); + if (pattern.find()) { + return metadata.getAnnotations().get(pattern.group("annotationName")); //$NON-NLS-1$ + } else { + return configValue;// the default is to use config value as is + } + } + return null; + } + + private void authorize( + HashMap headers, KubernetesClient client, String username, String password, String namespace, + Map jmxEnv) { + + final Matcher userNameMatcher = SECRET_PATTERN.matcher(username); + String secretName = null; + Map secretValues = null; + if (userNameMatcher.find()) { + secretName = userNameMatcher.group("secretName"); //$NON-NLS-1$ + secretValues = findSecret(client, namespace, secretName); + username = secretValues.get(userNameMatcher.group("itemName")); //$NON-NLS-1$ + } + + final Matcher passwordMatcher = SECRET_PATTERN.matcher(password); + if (passwordMatcher.find()) { + if (!secretName.equals(passwordMatcher.group("secretName"))) { //$NON-NLS-1$ + secretValues = findSecret(client, namespace, passwordMatcher.group("secretName")); //$NON-NLS-1$ + } + password = secretValues.get(passwordMatcher.group("itemName")); //$NON-NLS-1$ + } + + headers.put(AuthorizationHeaderParser.JOLOKIA_ALTERNATE_AUTHORIZATION_HEADER, + "Basic " + Base64Util.encode((username + ":" + password).getBytes())); //$NON-NLS-1$ //$NON-NLS-2$ + jmxEnv.put(JMXConnector.CREDENTIALS, new String[] {username, password}); + + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + private Map findSecret(KubernetesClient client, String namespace, String secretName) { + + for (Secret secret : client.secrets().inNamespace(namespace).list().getItems()) { + if (secret.getMetadata().getName().equals(secretName)) { + if ("kubernetes.io/basic-auth".equals(secret.getType())) { //$NON-NLS-1$ + Map data = secret.getData(); + data.replaceAll((key, value) -> new String(Base64.decodeBase64(value))); + return data; + } else if ("Opaque".equals(secret.getType())) { //$NON-NLS-1$ + for (Entry entry : secret.getData().entrySet()) { + if (entry.getKey().endsWith(".properties")) { //$NON-NLS-1$ + try { + Properties properties = new Properties(); + properties.load(new ByteArrayInputStream(Base64.decodeBase64(entry.getValue()))); + return (Map) properties; + } catch (IOException ignore) { + } + } + } + } + } + + } + throw new NoSuchElementException(Messages.KubernetesDiscoveryListener_CouldNotFindSecret + secretName + + Messages.KubernetesDiscoveryListener_InNamespace + namespace); + + } + + @Override + protected boolean isEnabled() { + return this.settings.scanForInstances(); + } +} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/KubernetesJvmDescriptor.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/KubernetesJvmDescriptor.java new file mode 100644 index 0000000000..a646a0fcc7 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/KubernetesJvmDescriptor.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Kantega AS. All rights reserved. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The contents of this file are subject to the terms of either the Universal Permissive License + * v 1.0 as shown at http://oss.oracle.com/licenses/upl + * + * or the following license: + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.openjdk.jmc.kubernetes; + +import java.io.IOException; +import java.util.Map; + +import javax.management.remote.JMXServiceURL; + +import org.jolokia.kubernetes.client.KubernetesJmxConnector; +import org.openjdk.jmc.jolokia.ServerConnectionDescriptor; +import org.openjdk.jmc.common.jvm.JVMDescriptor; + +import io.fabric8.kubernetes.api.model.ObjectMeta; + +public class KubernetesJvmDescriptor implements ServerConnectionDescriptor { + + private final JVMDescriptor jvmDescriptor; + private final ObjectMeta metadata; + private final Map env; + private final JMXServiceURL connectUrl; + + public KubernetesJvmDescriptor(ObjectMeta metadata, JVMDescriptor jvmDescriptor, JMXServiceURL connectUrl, + Map env) { + this.jvmDescriptor = jvmDescriptor; + this.metadata = metadata; + this.env = env; + this.connectUrl = connectUrl; + } + + @Override + public String getGUID() { + return this.metadata.getName(); + } + + @Override + public String getDisplayName() { + return this.metadata.getName(); + } + + @Override + public JVMDescriptor getJvmInfo() { + return this.jvmDescriptor; + } + + public String getPath() { + String namespace = metadata.getNamespace(); + final Object context = this.env.get(KubernetesJmxConnector.KUBERNETES_CLIENT_CONTEXT); + if (context != null) { + return context + "/" + namespace; //$NON-NLS-1$ + } + return namespace; + } + + @Override + public JMXServiceURL createJMXServiceURL() throws IOException { + return this.connectUrl; + } + + @Override + public Map getEnvironment() { + return this.env; + } + + @Override + public JMXServiceURL serviceUrl() { + return this.connectUrl; + } + +} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/Messages.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/Messages.java new file mode 100644 index 0000000000..9dcff25a26 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/Messages.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Kantega AS. All rights reserved. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The contents of this file are subject to the terms of either the Universal Permissive License + * v 1.0 as shown at http://oss.oracle.com/licenses/upl + * + * or the following license: + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.openjdk.jmc.kubernetes; + +import org.eclipse.osgi.util.NLS; + +public class Messages extends NLS { + private static final String BUNDLE_NAME = "org.openjdk.jmc.kubernetes.messages"; //$NON-NLS-1$ + public static String KubernetesDiscoveryListener_CouldNotFindSecret; + public static String KubernetesDiscoveryListener_Description; + public static String KubernetesDiscoveryListener_ErrConnectingToJvm; + public static String KubernetesDiscoveryListener_HttpOrHttps; + public static String KubernetesDiscoveryListener_InNamespace; + public static String KubernetesDiscoveryListener_JolokiaProtocol; + public static String KubernetesDiscoveryListener_MustProvidePassword; + public static String KubernetesDiscoveryListener_UnableToFindContexts; + public static String KubernetesDiscoveryListener_UnableToScan; + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + private Messages() { + } +} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/messages.properties b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/messages.properties new file mode 100644 index 0000000000..ea0166360a --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/messages.properties @@ -0,0 +1,9 @@ +KubernetesDiscoveryListener_CouldNotFindSecret=Could not find secret named +KubernetesDiscoveryListener_Description=List JVM in kubernetes cluster +KubernetesDiscoveryListener_ErrConnectingToJvm=Error connecting to JVM in pod +KubernetesDiscoveryListener_HttpOrHttps=' must be either 'http' or 'https' +KubernetesDiscoveryListener_InNamespace=\ in namespace +KubernetesDiscoveryListener_JolokiaProtocol=Jolokia protocol ' +KubernetesDiscoveryListener_MustProvidePassword=Password must be specified when username is specified +KubernetesDiscoveryListener_UnableToFindContexts=Unable to find all kubernetes contexts +KubernetesDiscoveryListener_UnableToScan=Unable to scan kubernetes context diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/JmcKubernetesPreferenceForm.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/JmcKubernetesPreferenceForm.java new file mode 100644 index 0000000000..5b2198933a --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/JmcKubernetesPreferenceForm.java @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Kantega AS. All rights reserved. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The contents of this file are subject to the terms of either the Universal Permissive License + * v 1.0 as shown at http://oss.oracle.com/licenses/upl + * + * or the following license: + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.openjdk.jmc.kubernetes.preferences; + +import java.util.Map; +import java.util.WeakHashMap; +import java.util.logging.Level; + +import org.eclipse.jface.preference.BooleanFieldEditor; +import org.eclipse.jface.preference.FieldEditor; +import org.eclipse.jface.preference.FieldEditorPreferencePage; +import org.eclipse.jface.preference.StringFieldEditor; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPreferencePage; +import org.openjdk.jmc.common.security.CredentialsNotAvailableException; +import org.openjdk.jmc.common.security.SecurityException; +import org.openjdk.jmc.kubernetes.JmcKubernetesPlugin; + +/** + * This class represents a preference page that is contributed to the Preferences dialog. By + * subclassing FieldEditorPreferencePage, we can use the field support built into JFace + * that allows us to create a page that is small and knows how to save, restore and apply itself. + *

+ * This page is used to modify preferences only. They are stored in the preference store that + * belongs to the main plug-in class. That way, preferences can be accessed directly via the + * preference store. + */ +public class JmcKubernetesPreferenceForm extends FieldEditorPreferencePage + implements IWorkbenchPreferencePage, PreferenceConstants { + + private Map dependantControls = new WeakHashMap<>(); + private Text userField; + private Text passwordField; + private boolean credentialsDirty; + + public JmcKubernetesPreferenceForm() { + super(GRID); + setPreferenceStore(JmcKubernetesPlugin.getDefault().getPreferenceStore()); + setDescription(Messages.JmcKubernetesPreferenceForm_FormDescription); + } + + /** + * Creates the field editors. Field editors are abstractions of the common GUI blocks needed to + * manipulate various types of preferences. Each field editor knows how to save and restore + * itself. + */ + public void createFieldEditors() { + BooleanFieldEditor mainEnabler = new BooleanFieldEditor(P_SCAN_FOR_INSTANCES, + Messages.JmcKubernetesPreferenceForm_ScanForPods, getFieldEditorParent()) { + @Override + protected void valueChanged(boolean oldValue, boolean newValue) { + super.valueChanged(oldValue, newValue); + enableDependantFields(newValue); + } + }; + addField(mainEnabler); + + BooleanFieldEditor scanContextsEditor = new BooleanFieldEditor(P_SCAN_ALL_CONTEXTS, + Messages.JmcKubernetesPreferenceForm_AllContexts, getFieldEditorParent()); + this.addDependantField(scanContextsEditor, scanContextsEditor.getDescriptionControl(getFieldEditorParent())); + this.addTextField(new StringFieldEditor(P_REQUIRE_LABEL, Messages.JmcKubernetesPreferenceForm_RequireLabel, + getFieldEditorParent()), Messages.JmcKubernetesPreferenceForm_LabelToolTip); + this.addTextField(new StringFieldEditor(P_JOLOKIA_PATH, Messages.JmcKubernetesPreferenceForm_PathLabel, + getFieldEditorParent()), Messages.JmcKubernetesPreferenceForm_PathTooltip); + this.addTextField(new StringFieldEditor(P_JOLOKIA_PORT, Messages.JmcKubernetesPreferenceForm_PortLabel, + getFieldEditorParent()), Messages.JmcKubernetesPreferenceForm_PortTooltip); + this.addTextField(new StringFieldEditor(P_JOLOKIA_PROTOCOL, Messages.JmcKubernetesPreferenceForm_ProtocolLabel, + getFieldEditorParent()), Messages.JmcKubernetesPreferenceForm_ProtocolTooltip); + createCredentialFields(); + // set initial enablement + enableDependantFields(JmcKubernetesPlugin.getDefault().scanForInstances()); + + } + + private void createCredentialFields() { + Label userLabel = new Label(getFieldEditorParent(), SWT.NONE); + userLabel.setText(Messages.JmcKubernetesPreferenceForm_UsernameLabel); + userLabel.setLayoutData(new GridData()); + this.userField = new Text(getFieldEditorParent(), SWT.SINGLE | SWT.BORDER); + userField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + userField.setToolTipText(Messages.JmcKubernetesPreferenceForm_UsernameTooltip); + this.dependantControls.put(userField, null); + + Label passLabel = new Label(getFieldEditorParent(), SWT.NONE); + passLabel.setText(Messages.JmcKubernetesPreferenceForm_PasswordLabel); + passLabel.setLayoutData(new GridData()); + this.passwordField = new Text(getFieldEditorParent(), SWT.PASSWORD | SWT.SINGLE | SWT.BORDER); + passwordField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + + JmcKubernetesPlugin plugin = JmcKubernetesPlugin.getDefault(); + + try { + userField.setText(plugin.username()); + passwordField.setText(plugin.password()); + } catch (CredentialsNotAvailableException ignore) { + // ignore if credentials are not stored + } catch (SecurityException e) { + plugin.getLogger().log(Level.WARNING, "Could not load kubernetes credentials", e); //$NON-NLS-1$ + } + + ModifyListener markCredentials = e -> credentialsDirty = true; + this.userField.addModifyListener(markCredentials); + this.passwordField.addModifyListener(markCredentials); + } + + private void addTextField(StringFieldEditor field, String tooltip) { + Text textControl = field.getTextControl(getFieldEditorParent()); + this.addDependantField(field, textControl); + textControl.setToolTipText(tooltip); + field.getLabelControl(getFieldEditorParent()).setToolTipText(tooltip); + + } + + private void addDependantField(FieldEditor field, Control control) { + this.dependantControls.put(control, null); + addField(field); + } + + private void enableDependantFields(boolean enabled) { + for (Control field : this.dependantControls.keySet()) { + field.setEnabled(enabled); + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.IWorkbenchPreferencePage#init(org.eclipse.ui.IWorkbench) + */ + public void init(IWorkbench workbench) { + } + + @Override + public boolean performOk() { + updateCredentialsIfApplicable(); + return super.performOk(); + } + + private void updateCredentialsIfApplicable() { + if (this.credentialsDirty) { + try { + JmcKubernetesPlugin.getDefault().storeCredentials(userField.getText(), passwordField.getText()); + this.credentialsDirty = false; + } catch (SecurityException ex) { + JmcKubernetesPlugin.getDefault().getLogger().log(Level.WARNING, + "Could not store kubernetes credentials", ex); //$NON-NLS-1$ + } + } + } + +} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/KubernetesScanningParameters.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/KubernetesScanningParameters.java new file mode 100644 index 0000000000..a67697d42b --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/KubernetesScanningParameters.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Kantega AS. All rights reserved. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The contents of this file are subject to the terms of either the Universal Permissive License + * v 1.0 as shown at http://oss.oracle.com/licenses/upl + * + * or the following license: + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.openjdk.jmc.kubernetes.preferences; + +import org.openjdk.jmc.common.security.ICredentials; +import org.openjdk.jmc.common.security.SecurityException; + +public interface KubernetesScanningParameters { + boolean scanForInstances(); + + boolean scanAllContexts(); + + String jolokiaPort(); + + String username() throws SecurityException; + + String password() throws SecurityException; + + String jolokiaPath(); + + String jolokiaProtocol(); + + String requireLabel(); + + ICredentials storeCredentials(String username, String password) throws SecurityException; + + void logError(String message, Throwable error); +} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/Messages.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/Messages.java new file mode 100644 index 0000000000..a718015331 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/Messages.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Kantega AS. All rights reserved. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The contents of this file are subject to the terms of either the Universal Permissive License + * v 1.0 as shown at http://oss.oracle.com/licenses/upl + * + * or the following license: + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.openjdk.jmc.kubernetes.preferences; + +import org.eclipse.osgi.util.NLS; + +public class Messages extends NLS { + private static final String BUNDLE_NAME = "org.openjdk.jmc.kubernetes.preferences.messages"; //$NON-NLS-1$ + public static String JmcKubernetesPreferenceForm_AllContexts; + public static String JmcKubernetesPreferenceForm_FormDescription; + public static String JmcKubernetesPreferenceForm_LabelToolTip; + public static String JmcKubernetesPreferenceForm_PasswordLabel; + public static String JmcKubernetesPreferenceForm_PasswordTooltip; + public static String JmcKubernetesPreferenceForm_PathLabel; + public static String JmcKubernetesPreferenceForm_PathTooltip; + public static String JmcKubernetesPreferenceForm_PortLabel; + public static String JmcKubernetesPreferenceForm_PortTooltip; + public static String JmcKubernetesPreferenceForm_ProtocolLabel; + public static String JmcKubernetesPreferenceForm_ProtocolTooltip; + public static String JmcKubernetesPreferenceForm_RequireLabel; + public static String JmcKubernetesPreferenceForm_ScanForPods; + public static String JmcKubernetesPreferenceForm_UsernameTooltip; + public static String JmcKubernetesPreferenceForm_UsernameLabel; + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + private Messages() { + } +} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/PreferenceConstants.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/PreferenceConstants.java new file mode 100644 index 0000000000..876d65a1b9 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/PreferenceConstants.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Kantega AS. All rights reserved. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The contents of this file are subject to the terms of either the Universal Permissive License + * v 1.0 as shown at http://oss.oracle.com/licenses/upl + * + * or the following license: + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.openjdk.jmc.kubernetes.preferences; + +/** + * Constant definitions for plug-in preferences + */ +public interface PreferenceConstants { + + public static final String P_SCAN_FOR_INSTANCES = "scanForInstances"; //$NON-NLS-1$ + public static final String P_SCAN_ALL_CONTEXTS = "scanAllContexts"; //$NON-NLS-1$ + public static final String P_REQUIRE_LABEL = "requireLabel"; //$NON-NLS-1$ + public static final String P_JOLOKIA_PATH = "jolokiaPath"; //$NON-NLS-1$ + public static final String P_JOLOKIA_PORT = "jolokiaPort"; //$NON-NLS-1$ + public static final String P_JOLOKIA_PROTOCOL = "jolokiaProtocol"; //$NON-NLS-1$ + public static final String P_CREDENTIALS_KEY = "kubernetes.scanning.credentials"; //$NON-NLS-1$ +} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/PreferenceInitializer.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/PreferenceInitializer.java new file mode 100644 index 0000000000..3aac64c2c2 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/PreferenceInitializer.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Kantega AS. All rights reserved. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The contents of this file are subject to the terms of either the Universal Permissive License + * v 1.0 as shown at http://oss.oracle.com/licenses/upl + * + * or the following license: + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.openjdk.jmc.kubernetes.preferences; + +import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer; +import org.eclipse.jface.preference.IPreferenceStore; +import org.openjdk.jmc.kubernetes.JmcKubernetesPlugin; + +/** + * Class used to initialize default preference values. + */ +public class PreferenceInitializer extends AbstractPreferenceInitializer implements PreferenceConstants { + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer# + * initializeDefaultPreferences() + */ + public void initializeDefaultPreferences() { + IPreferenceStore store = JmcKubernetesPlugin.getDefault().getPreferenceStore(); + store.setDefault(P_SCAN_FOR_INSTANCES, false); + store.setDefault(P_SCAN_ALL_CONTEXTS, false); + store.setDefault(P_REQUIRE_LABEL, "jolokia"); //$NON-NLS-1$ + store.setDefault(P_JOLOKIA_PATH, "/jolokia/"); //$NON-NLS-1$ + store.setDefault(P_JOLOKIA_PORT, "8778"); //$NON-NLS-1$ + } + +} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/messages.properties b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/messages.properties new file mode 100644 index 0000000000..44b18279a0 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/messages.properties @@ -0,0 +1,17 @@ +JmcKubernetesPreferenceForm_AllContexts=Scan pods from all locally configured &contexts, if false: only scan the current contexts +JmcKubernetesPreferenceForm_FormDescription=Options that allows you to scan kubernetes for JVMs running Jolokia\ +\n\nThe Kubernetes Proxy API will be used to attempt to access Jolokia like this (parenthesis denote optional components): \ +\n\n$kubernetesBaseUrl/api/v1/namespaces/podNamespace/pods/(jolokiaProtocol:)podname(:jolokiaPort)/proxy/jolokiaPath\n\n +JmcKubernetesPreferenceForm_LabelToolTip=Only attempt to connect to pods with this label set, leave empty to try to scan all pods +JmcKubernetesPreferenceForm_PasswordLabel=Require pass&word +JmcKubernetesPreferenceForm_PasswordTooltip=Password , alternatively use ${kubernetes/secret/secretName/secretItem} where the secret is in the same namespace as the pod and the type is either kubernetes.io/basic-auth or Opaque with java.util.Properties compatible values +JmcKubernetesPreferenceForm_PathLabel=Jolokia &path in pods +JmcKubernetesPreferenceForm_PathTooltip=Use this path for jolokia, or specify ${kubernetes/annotation/annotationName} to be able to to get port name from pod metadata annotation of your choice +JmcKubernetesPreferenceForm_PortLabel=Jolokia p&ort in pods +JmcKubernetesPreferenceForm_PortTooltip=Port to use, leave empty to use default port of Kubernetes proxy, hardcode a port value, or alternatively ${kubernetes/annotation/annotationName} to get port name from pod metadata annotation of your choice +JmcKubernetesPreferenceForm_ProtocolLabel=Jolokia pro&tocol in pods +JmcKubernetesPreferenceForm_ProtocolTooltip=Protocol to use (optional), will infer http if not set, or hardcode to https, or alternatively ${kubernetes/annotation/annotationName} to get port name from pod metadata annotation of your choice +JmcKubernetesPreferenceForm_RequireLabel=Require &label to scan pod +JmcKubernetesPreferenceForm_ScanForPods=&Scan for kubernetes pods with Jolokia support +JmcKubernetesPreferenceForm_UsernameTooltip=Username , alternatively use ${kubernetes/secret/secretName/secretItem} where the secret is in the same namespace as the pod and the type is either kubernetes.io/basic-auth or Opaque with java.util.Properties compatible values +JmcKubernetesPreferenceForm_UsernameLabel=Require &username diff --git a/application/pom.xml b/application/pom.xml index 90dc2c96f2..e40f01a4a3 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -112,6 +112,7 @@ org.openjdk.jmc.updatesite.ide org.openjdk.jmc.updatesite.rcp org.openjdk.jmc.jolokia + org.openjdk.jmc.kubernetes l10n tests diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/META-INF/MANIFEST.MF b/application/uitests/org.openjdk.jmc.kubernetes.test/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..46a9f1b9fc --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/META-INF/MANIFEST.MF @@ -0,0 +1,20 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: RJMX Test +Bundle-SymbolicName: org.openjdk.jmc.kubernetes.test;singleton:=true +Bundle-Version: 9.1.0.qualifier +Import-Package: io.fabric8.kubernetes.api.model +Bundle-Vendor: Oracle Corporation +Bundle-RequiredExecutionEnvironment: JavaSE-17 +Bundle-ActivationPolicy: lazy +Require-Bundle: org.junit, + org.openjdk.jmc.jolokia, + org.eclipse.osgi;bundle-version="3.16.200", + org.eclipse.ui.workbench, + org.openjdk.jmc.rjmx;bundle-version="9.0.0", + org.eclipse.ui, + org.hamcrest;bundle-version="2.1", + org.openjdk.jmc.kubernetes;bundle-version="9.0.0", + com.github.tomakehurst.wiremock-standalone;bundle-version="2.27.2", + org.awaitility;bundle-version="4.0.0" +Automatic-Module-Name: org.openjdk.jmc.jolokia.test diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/build.properties b/application/uitests/org.openjdk.jmc.kubernetes.test/build.properties new file mode 100644 index 0000000000..b4608c8522 --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/build.properties @@ -0,0 +1,36 @@ +# +# Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2024, Kantega AS. All rights reserved. +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# The contents of this file are subject to the terms of either the Universal Permissive License +# v 1.0 as shown at http://oss.oracle.com/licenses/upl +# +# or the following license: +# +# Redistribution and use in source and binary forms, with or without modification, are permitted +# provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions +# and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of +# conditions and the following disclaimer in the documentation and/or other materials provided with +# the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to +# endorse or promote products derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY +# WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +source.. = src/test/java/,\ + src/test/resources/ +output.. = target/test-classes/ +bin.includes = META-INF/,. diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/pom.xml b/application/uitests/org.openjdk.jmc.kubernetes.test/pom.xml new file mode 100644 index 0000000000..652ddbc5a6 --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/pom.xml @@ -0,0 +1,58 @@ + + + + 4.0.0 + + org.openjdk.jmc + missioncontrol.application.uitests + ${revision}${changelist} + + org.openjdk.jmc.kubernetes.test + eclipse-test-plugin + + + ${project.basedir}/../../../configuration + + + + + org.awaitility + awaitility + 4.0.0 + + + diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/java/org/openjdk/jmc/kubernetes/JmcKubernetesTest.java b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/java/org/openjdk/jmc/kubernetes/JmcKubernetesTest.java new file mode 100644 index 0000000000..46d3c5ea8b --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/java/org/openjdk/jmc/kubernetes/JmcKubernetesTest.java @@ -0,0 +1,313 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Kantega AS. All rights reserved. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The contents of this file are subject to the terms of either the Universal Permissive License + * v 1.0 as shown at http://oss.oracle.com/licenses/upl + * + * or the following license: + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.openjdk.jmc.kubernetes; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.MalformedURLException; +import java.time.Duration; +import java.util.Base64; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import javax.management.AttributeNotFoundException; +import javax.management.InstanceNotFoundException; +import javax.management.InvalidAttributeValueException; +import javax.management.MBeanException; +import javax.management.MBeanServerConnection; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; +import javax.management.ReflectionException; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXServiceURL; + +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.HttpClients; +import org.awaitility.Awaitility; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; +import org.openjdk.jmc.common.IDescribable; +import org.openjdk.jmc.common.jvm.JVMType; +import org.openjdk.jmc.common.security.ICredentials; +import org.openjdk.jmc.common.security.InMemoryCredentials; +import org.openjdk.jmc.common.security.SecurityException; +import org.openjdk.jmc.kubernetes.preferences.KubernetesScanningParameters; +import org.openjdk.jmc.rjmx.common.IConnectionDescriptor; +import org.openjdk.jmc.rjmx.common.IServerDescriptor; +import org.openjdk.jmc.rjmx.descriptorprovider.IDescriptorListener; + +import com.github.tomakehurst.wiremock.client.WireMock; +import com.github.tomakehurst.wiremock.core.WireMockConfiguration; +import com.github.tomakehurst.wiremock.extension.responsetemplating.ResponseTemplateTransformer; +import com.github.tomakehurst.wiremock.junit.WireMockRule; + +/** + * Test that JMX connections done with JmcKubernetesJmxConnectionProvider are functional. In order + * to be able to test this in a contained environment, the kubernetes API is mocked with wiremock. + */ +@SuppressWarnings("restriction") +public class JmcKubernetesTest { + + static class TestParameters implements KubernetesScanningParameters { + public boolean scanForInstances, scanAllContexts; + public String jolokiaPort, jolokiaPath = "/jolokia/", jolokiaProtocol, requireLabel; + public InMemoryCredentials credentials; + + @Override + public boolean scanForInstances() { + return this.scanForInstances; + } + + @Override + public boolean scanAllContexts() { + return this.scanAllContexts; + } + + @Override + public String jolokiaPort() { + return this.jolokiaPort; + } + + @Override + public String username() throws SecurityException { + return this.credentials == null ? null : this.credentials.getUsername(); + } + + @Override + public String password() throws SecurityException { + return this.credentials == null ? null : this.credentials.getPassword(); + } + + @Override + public String jolokiaPath() { + return this.jolokiaPath; + } + + @Override + public String jolokiaProtocol() { + return this.jolokiaProtocol; + } + + @Override + public String requireLabel() { + return this.requireLabel; + } + + @Override + public ICredentials storeCredentials(String username, String password) throws SecurityException { + return this.credentials = new InMemoryCredentials(username, password); + } + + @Override + public void logError(String message, Throwable error) { + System.out.println(message); + error.printStackTrace(System.out); + } + } + + @ClassRule + public static WireMockRule wiremock = new WireMockRule( + WireMockConfiguration.options().extensions(new ResponseTemplateTransformer(false)).port(0)); + + static final String jolokiaUrl = "service:jmx:kubernetes:///ns1/pod-abcdef/jolokia"; + + private static MBeanServerConnection jolokiaConnection; + + @BeforeClass + public static void connect() throws Exception { + CloseableHttpResponse configResponse = HttpClients.createDefault() + .execute(new HttpGet(wiremock.baseUrl() + "/mock-kube-config.yml")); + Assert.assertEquals(configResponse.getStatusLine().getStatusCode(), 200); + File configFile = File.createTempFile("mock-kube-config", ".yml"); + configResponse.getEntity().writeTo(new FileOutputStream(configFile)); + // we set this so the KubernetesDiscoveryListener will work + //Setting taken from: https://github.com/fabric8io/kubernetes-client/blob/77a65f7d40f31a5dc37492cd9de3c317c2702fb4/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/Config.java#L120, unlikely to change + System.setProperty("kubeconfig", configFile.getAbsolutePath()); + jolokiaConnection = getKubernetesMBeanConnector(); + } + + @Test + public void testExecuteOperation() throws InstanceNotFoundException, MalformedObjectNameException, MBeanException, + ReflectionException, MalformedURLException, IOException { + jolokiaConnection.invoke(new ObjectName("java.lang:type=Memory"), "gc", new Object[0], new String[0]); + } + + @Test + public void testReadAttribute() + throws InstanceNotFoundException, AttributeNotFoundException, InvalidAttributeValueException, + MalformedObjectNameException, MBeanException, ReflectionException, MalformedURLException, IOException { + MBeanServerConnection jmxConnection = jolokiaConnection; + assertOneSingleAttribute(jmxConnection); + + } + + private void assertOneSingleAttribute(MBeanServerConnection jmxConnection) throws MalformedObjectNameException, + MBeanException, AttributeNotFoundException, InstanceNotFoundException, ReflectionException, IOException { + ObjectName objectName = new ObjectName("java.lang:type=Memory"); + String attribute = "Verbose"; + Assert.assertEquals(false, jmxConnection.getAttribute(objectName, attribute)); + } + + @Before + public void reset() { + wiremock.resetAll(); + wiremock.resetRequests(); + } + + private static MBeanServerConnection getKubernetesMBeanConnector() throws IOException, MalformedURLException { + JMXConnector connector = new JmcKubernetesJmxConnectionProvider().newJMXConnector(new JMXServiceURL(jolokiaUrl), + Collections.emptyMap()); + connector.connect(); + MBeanServerConnection connection = connector.getMBeanServerConnection(); + return connection; + } + + @Test + public void testDiscoverWithMostlyDefaultSettings() throws Exception { + + TestParameters parameters = new TestParameters(); + // Set config so that scanning takes place + parameters.scanForInstances = true; + testThatJvmIsFound(parameters); + } + + @Test + public void testDiscoverWithPathFromAnnotation() throws Exception { + TestParameters parameters = new TestParameters(); + parameters.scanForInstances = true; + parameters.jolokiaPath = "${kubernetes/annotation/jolokiaPath}"; + testThatJvmIsFound(parameters); + } + + @Test + public void testDiscoverWithPortFromAnnotation() throws Exception { + TestParameters parameters = new TestParameters(); + parameters.scanForInstances = true; + parameters.jolokiaPort = "${kubernetes/annotation/jolokiaPort}"; + + testThatJvmIsFound(parameters); + } + + @Test + public void testDiscoverWithBasicAuthFromSecret() throws Exception { + TestParameters parameters = new TestParameters(); + parameters.scanForInstances = true; + parameters.credentials = new InMemoryCredentials("${kubernetes/secret/jolokia-auth/username}", + "${kubernetes/secret/jolokia-auth/password}"); + + testThatJvmIsFound(parameters); + // Verify that the expected authorization was picked up + WireMock.verify(WireMock + .postRequestedFor(WireMock.urlPathMatching("/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*")) + .withHeader("X-jolokia-authorization", + WireMock.equalTo("Basic " + Base64.getEncoder().encodeToString("admin:admin".getBytes())))); + } + + @Test + public void testDiscoverWithAuthFromProperties() throws Exception { + + TestParameters parameters = new TestParameters(); + parameters.scanForInstances = true; + parameters.credentials = new InMemoryCredentials("${kubernetes/secret/jolokia-properties/user}", + "${kubernetes/secret/jolokia-properties/password}"); + + testThatJvmIsFound(parameters); + // Verify that the expected authorization was picked up + WireMock.verify(WireMock + .postRequestedFor(WireMock.urlPathMatching("/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*")) + .withHeader("X-jolokia-authorization", + WireMock.equalTo("Basic " + Base64.getEncoder().encodeToString("admin:secret".getBytes())))); + } + + @Test + public void testDiscoverWithAuthDirectlyFromSettings() throws Exception { + + TestParameters parameters = new TestParameters(); + parameters.scanForInstances = true; + parameters.credentials = new InMemoryCredentials("user", "***"); + testThatJvmIsFound(parameters); + // Verify that the expected authorization was picked up + WireMock.verify(WireMock + .postRequestedFor(WireMock.urlPathMatching("/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*")) + .withHeader("X-jolokia-authorization", + WireMock.equalTo("Basic " + Base64.getEncoder().encodeToString("user:***".getBytes())))); + } + + private void testThatJvmIsFound(TestParameters parameters) throws Exception { + + final KubernetesDiscoveryListener scanner = new KubernetesDiscoveryListener(parameters); + final Map foundVms = new HashMap<>(); + IDescriptorListener descriptorListener = new IDescriptorListener() { + public void onDescriptorDetected( + IServerDescriptor serverDescriptor, String path, JMXServiceURL url, + IConnectionDescriptor connectionDescriptor, IDescribable provider) { + foundVms.put(serverDescriptor.getGUID(), serverDescriptor); + } + + public void onDescriptorRemoved(String descriptorId) { + foundVms.remove(descriptorId); + } + }; + scanner.addDescriptorListener(descriptorListener); + + try { + // Test that at least one VM (the one running the test was discovered) + + Awaitility.await().atMost(Duration.ofSeconds(5)).until(() -> !foundVms.isEmpty()); + IServerDescriptor descriptor = foundVms.get("pod-abcdef"); + Assert.assertNotNull(descriptor); + Assert.assertEquals( + "[JVMDescriptor] Java command: /Users/marska/Downloads/hawtio-app-2.9.1.jar --port 9090 PID: 88774", + descriptor.getJvmInfo().toString()); + Assert.assertEquals(JVMType.HOTSPOT, descriptor.getJvmInfo().getJvmType()); + Assert.assertEquals("18.0.1", descriptor.getJvmInfo().getJavaVersion()); + Assert.assertTrue(descriptor instanceof IConnectionDescriptor); + IConnectionDescriptor connectDescriptor = (IConnectionDescriptor) descriptor; + JMXConnector connector = new JmcKubernetesJmxConnectionProvider() + .newJMXConnector(connectDescriptor.createJMXServiceURL(), connectDescriptor.getEnvironment()); + connector.connect(); + assertOneSingleAttribute(connector.getMBeanServerConnection()); + + } finally { + // Tell scanner thread to exit + scanner.shutdown(); + } + } +} diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-attribute.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-attribute.json new file mode 100644 index 0000000000..4230121a7e --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-attribute.json @@ -0,0 +1,10 @@ +{ + "request" : { + "attribute" : "Verbose", + "mbean" : "java.lang:type=Memory", + "type" : "read" + }, + "status" : 200, + "timestamp" : 1658563869, + "value" : false +} diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-basic-auth-secret.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-basic-auth-secret.json new file mode 100644 index 0000000000..b5bb351b7e --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-basic-auth-secret.json @@ -0,0 +1,19 @@ +{ + "apiVersion": "v1", + "data": { + "password": "YWRtaW4=", + "username": "YWRtaW4=" + }, + "kind": "Secret", + "metadata": { + "annotations": { + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Secret\",\"metadata\":{\"annotations\":{},\"name\":\"jolokia-auth\",\"namespace\":\"jfr\"},\"stringData\":{\"password\":\"admin\",\"username\":\"admin\"},\"type\":\"kubernetes.io/basic-auth\"}\n" + }, + "creationTimestamp": "2022-05-20T13:59:12Z", + "name": "jolokia-auth", + "namespace": "jfr", + "resourceVersion": "130", + "uid": "e1563217-ef08-481d-b2e3-233fa3040b56" + }, + "type": "kubernetes.io/basic-auth" +} diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-exec.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-exec.json new file mode 100644 index 0000000000..5bd35a69f2 --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-exec.json @@ -0,0 +1,10 @@ +{ + "request" : { + "mbean" : "java.lang:type=Memory", + "operation" : "gc()", + "type" : "exec" + }, + "status" : 200, + "timestamp" : 1658567003, + "value" : null +} diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-properties-secret.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-properties-secret.json new file mode 100644 index 0000000000..e12bbd16c1 --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-properties-secret.json @@ -0,0 +1,15 @@ +{ + "apiVersion": "v1", + "data": { + "jolokia.properties": "aG9zdD0wLjAuMC4wCnBhc3N3b3JkPXNlY3JldAp1c2VyPWFkbWluCnBvcnQ9ODc3OApkaXNjb3ZlcnlFbmFibGVkPXRydWUKZGlzY292ZXJ5QWdlbnRVcmw9aHR0cDovLyR7aG9zdH06ODc3OC9qb2xva2lhLwpwb2xpY3lMb2NhdGlvbj1jbGFzc3BhdGg6L0JPT1QtSU5GL2NsYXNzZXMvam9sb2tpYS1hY2Nlc3MueG1s" + }, + "kind": "Secret", + "metadata": { + "creationTimestamp": "2022-05-20T13:59:12Z", + "name": "jolokia-properties", + "namespace": "jfr", + "resourceVersion": "147", + "uid": "025bf17a-cff5-46e6-8499-dd9d6de43fa9" + }, + "type": "Opaque" +} diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-version.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-version.json new file mode 100644 index 0000000000..6299d0407a --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-version.json @@ -0,0 +1,41 @@ +{ + "request": { + "type": "version" + }, + "value": { + "agent": "1.7.1", + "protocol": "7.2", + "config": { + "listenForHttpService": "true", + "authIgnoreCerts": "false", + "agentId": "192.168.1.104-28660-5bd82fed-servlet", + "debug": "false", + "agentType": "servlet", + "policyLocation": "classpath:\/jolokia-access.xml", + "agentContext": "\/jolokia", + "serializeException": "false", + "mimeType": "text\/plain", + "dispatcherClasses": "org.jolokia.http.Jsr160ProxyNotEnabledByDefaultAnymoreDispatcher", + "authMode": "basic", + "authMatch": "any", + "streaming": "true", + "canonicalNaming": "true", + "historyMaxEntries": "10", + "allowErrorDetails": "false", + "allowDnsReverseLookup": "true", + "realm": "jolokia", + "includeStackTrace": "false", + "restrictorClass": "io.hawt.system.RBACRestrictor", + "mbeanQualifier": "qualifier=hawtio", + "useRestrictorService": "false", + "debugMaxEntries": "100" + }, + "info": { + "product": "jetty", + "vendor": "Eclipse", + "version": "9.4.z-SNAPSHOT" + } + }, + "timestamp": 1658556959, + "status": 200 +} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/memory-list.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/memory-list.json new file mode 100644 index 0000000000..46d117f2e4 --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/memory-list.json @@ -0,0 +1,46 @@ +{ + "request" : { + "path" : "java.lang/type=Memory", + "type" : "list" + }, + "status" : 200, + "timestamp" : 1658566168, + "value" : { + "attr" : { + "HeapMemoryUsage" : { + "desc" : "HeapMemoryUsage", + "rw" : false, + "type" : "javax.management.openmbean.CompositeData" + }, + "NonHeapMemoryUsage" : { + "desc" : "NonHeapMemoryUsage", + "rw" : false, + "type" : "javax.management.openmbean.CompositeData" + }, + "ObjectName" : { + "desc" : "ObjectName", + "rw" : false, + "type" : "javax.management.ObjectName" + }, + "ObjectPendingFinalizationCount" : { + "desc" : "ObjectPendingFinalizationCount", + "rw" : false, + "type" : "int" + }, + "Verbose" : { + "desc" : "Verbose", + "rw" : true, + "type" : "boolean" + } + }, + "class" : "sun.management.MemoryImpl", + "desc" : "Information on the management interface of the MBean", + "op" : { + "gc" : { + "args" : [], + "desc" : "gc", + "ret" : "void" + } + } + } +} diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/mock-kube-config.yml b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/mock-kube-config.yml new file mode 100644 index 0000000000..f222f471a7 --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/mock-kube-config.yml @@ -0,0 +1,17 @@ +apiVersion: v1 +clusters: +- cluster: + server: {{request.baseUrl}}/ + name: test +contexts: +- context: + cluster: test + user: test + name: test +current-context: test +kind: Config +preferences: {} +users: +- name: test + user: + token: foobar diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/pod.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/pod.json new file mode 100644 index 0000000000..77a61a3e32 --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/pod.json @@ -0,0 +1,11 @@ +{ + "metadata": { + "clusterName": "test", + "labels": { + "jolokia": true + }, + "name": "pod-abcdef", + "namespace": "ns1", + "selfLink": "/api/v1/namespaces/ns1/pods/pod-abcdef" + } +} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/pods.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/pods.json new file mode 100644 index 0000000000..114edd3640 --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/pods.json @@ -0,0 +1,19 @@ +{ + "items": [ + { + "metadata": { + "clusterName": "test", + "labels": { + "jolokia": true + }, + "annotations": { + "jolokiaPort": 8778, + "jolokiaPath": "jolokia" + }, + "name": "pod-abcdef", + "namespace": "ns1", + "selfLink": "/api/v1/namespaces/ns1/pods/pod-abcdef" + } + } + ] +} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/runtime-list.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/runtime-list.json new file mode 100644 index 0000000000..c096d78c52 --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/runtime-list.json @@ -0,0 +1,104 @@ +{ + "request" : { + "path" : "java.lang/type=Runtime", + "type" : "list" + }, + "status" : 200, + "timestamp" : 1658564737, + "value" : { + "attr" : { + "BootClassPath" : { + "desc" : "BootClassPath", + "rw" : false, + "type" : "java.lang.String" + }, + "BootClassPathSupported" : { + "desc" : "BootClassPathSupported", + "rw" : false, + "type" : "boolean" + }, + "ClassPath" : { + "desc" : "ClassPath", + "rw" : false, + "type" : "java.lang.String" + }, + "InputArguments" : { + "desc" : "InputArguments", + "rw" : false, + "type" : "[Ljava.lang.String;" + }, + "LibraryPath" : { + "desc" : "LibraryPath", + "rw" : false, + "type" : "java.lang.String" + }, + "ManagementSpecVersion" : { + "desc" : "ManagementSpecVersion", + "rw" : false, + "type" : "java.lang.String" + }, + "Name" : { + "desc" : "Name", + "rw" : false, + "type" : "java.lang.String" + }, + "ObjectName" : { + "desc" : "ObjectName", + "rw" : false, + "type" : "javax.management.ObjectName" + }, + "Pid" : { + "desc" : "Pid", + "rw" : false, + "type" : "long" + }, + "SpecName" : { + "desc" : "SpecName", + "rw" : false, + "type" : "java.lang.String" + }, + "SpecVendor" : { + "desc" : "SpecVendor", + "rw" : false, + "type" : "java.lang.String" + }, + "SpecVersion" : { + "desc" : "SpecVersion", + "rw" : false, + "type" : "java.lang.String" + }, + "StartTime" : { + "desc" : "StartTime", + "rw" : false, + "type" : "long" + }, + "SystemProperties" : { + "desc" : "SystemProperties", + "rw" : false, + "type" : "javax.management.openmbean.TabularData" + }, + "Uptime" : { + "desc" : "Uptime", + "rw" : false, + "type" : "long" + }, + "VmName" : { + "desc" : "VmName", + "rw" : false, + "type" : "java.lang.String" + }, + "VmVendor" : { + "desc" : "VmVendor", + "rw" : false, + "type" : "java.lang.String" + }, + "VmVersion" : { + "desc" : "VmVersion", + "rw" : false, + "type" : "java.lang.String" + } + }, + "class" : "sun.management.RuntimeImpl", + "desc" : "Information on the management interface of the MBean" + } +} diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/secrets.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/secrets.json new file mode 100644 index 0000000000..ac68f94b51 --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/secrets.json @@ -0,0 +1,43 @@ +{ + "apiVersion": "v1", + "items": [ + { + "apiVersion": "v1", + "data": { + "password": "YWRtaW4=", + "username": "YWRtaW4=" + }, + "kind": "Secret", + "metadata": { + "annotations": { + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Secret\",\"metadata\":{\"annotations\":{},\"name\":\"jolokia-auth\",\"namespace\":\"ns1\"},\"stringData\":{\"password\":\"admin\",\"username\":\"admin\"},\"type\":\"kubernetes.io/basic-auth\"}\n" + }, + "creationTimestamp": "2022-05-20T13:59:12Z", + "name": "jolokia-auth", + "namespace": "ns1", + "resourceVersion": "130", + "uid": "e1563217-ef08-481d-b2e3-233fa3040b56" + }, + "type": "kubernetes.io/basic-auth" + }, + { + "apiVersion": "v1", + "data": { + "jolokia.properties": "aG9zdD0wLjAuMC4wCnBhc3N3b3JkPXNlY3JldAp1c2VyPWFkbWluCnBvcnQ9ODc3OApkaXNjb3ZlcnlFbmFibGVkPXRydWUKZGlzY292ZXJ5QWdlbnRVcmw9aHR0cDovLyR7aG9zdH06ODc3OC9qb2xva2lhLwpwb2xpY3lMb2NhdGlvbj1jbGFzc3BhdGg6L0JPT1QtSU5GL2NsYXNzZXMvam9sb2tpYS1hY2Nlc3MueG1s" + }, + "kind": "Secret", + "metadata": { + "creationTimestamp": "2022-05-20T13:59:12Z", + "name": "jolokia-properties", + "namespace": "ns1", + "resourceVersion": "147", + "uid": "025bf17a-cff5-46e6-8499-dd9d6de43fa9" + }, + "type": "Opaque" + } + ], + "kind": "List", + "metadata": { + "resourceVersion": "" + } +} diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/system-attributes.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/system-attributes.json new file mode 100644 index 0000000000..dc2ff55433 --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/system-attributes.json @@ -0,0 +1,101 @@ +[ + { + "request" : { + "attribute" : "Pid", + "mbean" : "java.lang:type=Runtime", + "type" : "read" + }, + "status" : 200, + "timestamp" : 1658586915, + "value" : 88774 + }, + { + "request" : { + "attribute" : "Name", + "mbean" : "java.lang:type=Runtime", + "type" : "read" + }, + "status" : 200, + "timestamp" : 1658586915, + "value" : "88774@Martins-MacBook-Pro-2.local" + }, + { + "request" : { + "attribute" : "InputArguments", + "mbean" : "java.lang:type=Runtime", + "type" : "read" + }, + "status" : 200, + "timestamp" : 1658586915, + "value" : [] + }, + { + "request" : { + "attribute" : "SystemProperties", + "mbean" : "java.lang:type=Runtime", + "type" : "read" + }, + "status" : 200, + "timestamp" : 1658586915, + "value" : { + "apple.awt.application.name" : "App", + "file.encoding" : "UTF-8", + "file.separator" : "/", + "ftp.nonProxyHosts" : "local|*.local|169.254/16|*.169.254/16", + "hawtio.authenticationEnabled" : "false", + "hawtio.redirect.scheme" : "http", + "hawtio.url" : "http://localhost:9090/hawtio", + "http.nonProxyHosts" : "local|*.local|169.254/16|*.169.254/16", + "java.class.path" : "/Users/marska/Downloads/hawtio-app-2.9.1.jar", + "java.class.version" : "62.0", + "java.home" : "/Library/Java/JavaVirtualMachines/temurin-18.jdk/Contents/Home", + "java.io.tmpdir" : "/var/folders/1f/cdm0073x1mj1swnhtw181_4m0000gn/T/", + "java.library.path" : "/Users/marska/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:.", + "java.runtime.name" : "OpenJDK Runtime Environment", + "java.runtime.version" : "18.0.1+10", + "java.specification.name" : "Java Platform API Specification", + "java.specification.vendor" : "Oracle Corporation", + "java.specification.version" : "18", + "java.vendor" : "Eclipse Adoptium", + "java.vendor.url" : "https://adoptium.net/", + "java.vendor.url.bug" : "https://github.com/adoptium/adoptium-support/issues", + "java.vendor.version" : "Temurin-18.0.1+10", + "java.version" : "18.0.1", + "java.version.date" : "2022-04-19", + "java.vm.compressedOopsMode" : "Zero based", + "java.vm.info" : "mixed mode, sharing", + "java.vm.name" : "OpenJDK 64-Bit Server VM", + "java.vm.specification.name" : "Java Virtual Machine Specification", + "java.vm.specification.vendor" : "Oracle Corporation", + "java.vm.specification.version" : "18", + "java.vm.vendor" : "Eclipse Adoptium", + "java.vm.version" : "18.0.1+10", + "jdk.debug" : "release", + "line.separator" : "\n", + "native.encoding" : "UTF-8", + "org.eclipse.jetty.util.log.class" : "org.eclipse.jetty.util.log.Slf4jLog", + "os.arch" : "x86_64", + "os.name" : "Mac OS X", + "os.version" : "11.6.6", + "path.separator" : ":", + "socksNonProxyHosts" : "local|*.local|169.254/16|*.169.254/16", + "sun.arch.data.model" : "64", + "sun.awt.enableExtraMouseButtons" : "true", + "sun.boot.library.path" : "/Library/Java/JavaVirtualMachines/temurin-18.jdk/Contents/Home/lib", + "sun.cpu.endian" : "little", + "sun.io.unicode.encoding" : "UnicodeBig", + "sun.java.command" : "/Users/marska/Downloads/hawtio-app-2.9.1.jar --port 9090", + "sun.java.launcher" : "SUN_STANDARD", + "sun.jnu.encoding" : "UTF-8", + "sun.management.compiler" : "HotSpot 64-Bit Tiered Compilers", + "sun.stderr.encoding" : "UTF-8", + "sun.stdout.encoding" : "UTF-8", + "user.country" : "NO", + "user.dir" : "/Users/marska/Documents/dev/jmc/jmc8/releng/third-party", + "user.home" : "/Users/marska", + "user.language" : "nb", + "user.name" : "marska", + "user.timezone" : "Asia/Taipei" + } + } +] diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-exec.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-exec.json new file mode 100644 index 0000000000..6bb03ececf --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-exec.json @@ -0,0 +1,15 @@ +{ + "request": { + "method": "POST", + "urlPath": "/api/v1/namespaces/ns1/pods/pod-abcdef/proxy/jolokia", + "bodyPatterns": [ + { + "equalToJson": "{\"operation\":\"gc()\",\"mbean\":\"java.lang:type=Memory\",\"type\":\"EXEC\"}" + } + ] + }, + "response": { + "status": 200, + "bodyFileName": "jolokia-exec.json" + } +} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-list-memory.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-list-memory.json new file mode 100644 index 0000000000..e4170948cb --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-list-memory.json @@ -0,0 +1,15 @@ +{ + "request": { + "method": "POST", + "urlPathPattern": "/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*", + "bodyPatterns": [ + { + "equalToJson": "{\"path\":\"java.lang/type=Memory\",\"type\":\"LIST\"}" + } + ] + }, + "response": { + "status": 200, + "bodyFileName": "memory-list.json" + } +} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-list-runtime.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-list-runtime.json new file mode 100644 index 0000000000..f936e7eb0f --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-list-runtime.json @@ -0,0 +1,15 @@ +{ + "request": { + "method": "POST", + "urlPathPattern": "/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*", + "bodyPatterns": [ + { + "equalToJson": "{\"path\":\"java.lang/type=Runtime\",\"type\":\"LIST\"}" + } + ] + }, + "response": { + "status": 200, + "bodyFileName": "runtime-list.json" + } +} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-probe.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-probe.json new file mode 100644 index 0000000000..5b26da423d --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-probe.json @@ -0,0 +1,15 @@ +{ + "request": { + "method": "POST", + "urlPathPattern": "/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*", + "bodyPatterns": [ + { + "equalToJson": "{\"type\":\"version\"}" + } + ] + }, + "response": { + "status": 200, + "bodyFileName": "jolokia-version.json" + } +} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-probe2.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-probe2.json new file mode 100644 index 0000000000..98670016fd --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-probe2.json @@ -0,0 +1,15 @@ +{ + "request": { + "method": "POST", + "urlPathPattern": "/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*", + "bodyPatterns": [ + { + "equalToJson": "{\"type\":\"VERSION\"}" + } + ] + }, + "response": { + "status": 200, + "bodyFileName": "jolokia-version.json" + } +} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-read-attribute.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-read-attribute.json new file mode 100644 index 0000000000..1584e3b4a3 --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-read-attribute.json @@ -0,0 +1,15 @@ +{ + "request": { + "method": "POST", + "urlPathPattern": "/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*", + "bodyPatterns": [ + { + "equalToJson": "{\"attribute\":\"Verbose\",\"mbean\":\"java.lang:type=Memory\",\"type\":\"READ\"}" + } + ] + }, + "response": { + "status": 200, + "bodyFileName": "jolokia-attribute.json" + } +} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jvm-properties.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jvm-properties.json new file mode 100644 index 0000000000..5b26da423d --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jvm-properties.json @@ -0,0 +1,15 @@ +{ + "request": { + "method": "POST", + "urlPathPattern": "/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*", + "bodyPatterns": [ + { + "equalToJson": "{\"type\":\"version\"}" + } + ] + }, + "response": { + "status": 200, + "bodyFileName": "jolokia-version.json" + } +} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/kube-config.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/kube-config.json new file mode 100644 index 0000000000..418a887637 --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/kube-config.json @@ -0,0 +1,13 @@ +{ + "request": { + "method": "GET", + "url": "/mock-kube-config.yml" + }, + "response": { + "status": 200, + "bodyFileName": "mock-kube-config.yml", + "transformers": [ + "response-template" + ] + } +} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/pod.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/pod.json new file mode 100644 index 0000000000..ebcbd7c5e3 --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/pod.json @@ -0,0 +1,10 @@ +{ + "request": { + "method": "GET", + "url": "/api/v1/namespaces/ns1/pods/pod-abcdef" + }, + "response": { + "status": 200, + "bodyFileName": "pod.json" + } +} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/pods.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/pods.json new file mode 100644 index 0000000000..061fb0f53b --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/pods.json @@ -0,0 +1,10 @@ +{ + "request": { + "method": "GET", + "urlPath": "/api/v1/pods" + }, + "response": { + "status": 200, + "bodyFileName": "pods.json" + } +} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/secrets.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/secrets.json new file mode 100644 index 0000000000..113bb91e1f --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/secrets.json @@ -0,0 +1,10 @@ +{ + "request": { + "method": "GET", + "url": "/api/v1/namespaces/ns1/secrets" + }, + "response": { + "status": 200, + "bodyFileName": "secrets.json" + } +} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/system-attributes.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/system-attributes.json new file mode 100644 index 0000000000..e3d1147b2f --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/system-attributes.json @@ -0,0 +1,15 @@ +{ + "request": { + "method": "POST", + "urlPathPattern": "/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*", + "bodyPatterns": [ + { + "equalToJson": "[{\"attribute\":\"Pid\",\"mbean\":\"java.lang:type=Runtime\",\"type\":\"READ\"},{\"attribute\":\"Name\",\"mbean\":\"java.lang:type=Runtime\",\"type\":\"READ\"},{\"attribute\":\"InputArguments\",\"mbean\":\"java.lang:type=Runtime\",\"type\":\"READ\"},{\"attribute\":\"SystemProperties\",\"mbean\":\"java.lang:type=Runtime\",\"type\":\"READ\"}]" + } + ] + }, + "response": { + "status": 200, + "bodyFileName": "system-attributes.json" + } +} \ No newline at end of file diff --git a/application/uitests/pom.xml b/application/uitests/pom.xml index 98327fd9a8..f6496c214d 100644 --- a/application/uitests/pom.xml +++ b/application/uitests/pom.xml @@ -1,6 +1,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/application/org.openjdk.jmc.kubernetes/pom.xml b/application/org.openjdk.jmc.kubernetes/pom.xml deleted file mode 100644 index 8593216ed0..0000000000 --- a/application/org.openjdk.jmc.kubernetes/pom.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - - 4.0.0 - - org.openjdk.jmc - missioncontrol.application - ${revision}${changelist} - - org.openjdk.jmc.kubernetes - eclipse-plugin - - ${project.basedir}/../../configuration - - diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnection.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnection.java deleted file mode 100644 index 6aae451116..0000000000 --- a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnection.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2024, Kantega AS. All rights reserved. - * - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * The contents of this file are subject to the terms of either the Universal Permissive License - * v 1.0 as shown at http://oss.oracle.com/licenses/upl - * - * or the following license: - * - * Redistribution and use in source and binary forms, with or without modification, are permitted - * provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list of - * conditions and the following disclaimer in the documentation and/or other materials provided with - * the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors may be used to - * endorse or promote products derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY - * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.openjdk.jmc.kubernetes; - -import java.io.IOException; -import java.util.Arrays; -import java.util.Collection; -import java.util.regex.Pattern; - -import javax.management.InstanceNotFoundException; - -import org.jolokia.client.J4pClient; -import org.jolokia.client.exception.J4pException; -import org.jolokia.client.exception.J4pRemoteException; -import org.jolokia.client.request.J4pResponse; -import org.openjdk.jmc.jolokia.JmcJolokiaJmxConnection; -import org.openjdk.jmc.rjmx.common.ConnectionException; - -/** - * Jolokia based MBeanServerConnector tailored for JMC needs - */ -public class JmcKubernetesJmxConnection extends JmcJolokiaJmxConnection { - - static final Collection DISCONNECT_SIGNS = Arrays.asList(Pattern.compile("Error: pods \".+\" not found")); //$NON-NLS-1$ - - public JmcKubernetesJmxConnection(J4pClient client) throws IOException { - super(client); - } - - @SuppressWarnings("rawtypes") - @Override - protected J4pResponse unwrapException(J4pException e) throws IOException, InstanceNotFoundException { - // recognize signs of disconnect and signal to the application for better - // handling - if (isKnownDisconnectException(e)) { - throw new ConnectionException(e.getMessage()); - } else { - return super.unwrapException(e); - } - } - - private boolean isKnownDisconnectException(J4pException e) { - if (!(e instanceof J4pRemoteException)) { - return false; - } - if (!"io.fabric8.kubernetes.client.KubernetesClientException".equals(((J4pRemoteException) e).getErrorType())) { //$NON-NLS-1$ - return false; - } - return DISCONNECT_SIGNS.stream().anyMatch(pattern -> pattern.matcher(e.getMessage()).matches()); - } - -} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnectionProvider.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnectionProvider.java deleted file mode 100644 index b74b999f68..0000000000 --- a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnectionProvider.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2024, Kantega AS. All rights reserved. - * - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * The contents of this file are subject to the terms of either the Universal Permissive License - * v 1.0 as shown at http://oss.oracle.com/licenses/upl - * - * or the following license: - * - * Redistribution and use in source and binary forms, with or without modification, are permitted - * provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list of - * conditions and the following disclaimer in the documentation and/or other materials provided with - * the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors may be used to - * endorse or promote products derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY - * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.openjdk.jmc.kubernetes; - -import java.io.IOException; -import java.net.MalformedURLException; -import java.util.Map; - -import javax.management.remote.JMXConnector; -import javax.management.remote.JMXConnectorProvider; -import javax.management.remote.JMXServiceURL; - -/** - * This {@code JMXConnectionProvider} handles the "kubernetes" protocol. - */ -public class JmcKubernetesJmxConnectionProvider implements JMXConnectorProvider { - @Override - public JMXConnector newJMXConnector(JMXServiceURL serviceURL, Map environment) throws IOException { - if (!"kubernetes".equals(serviceURL.getProtocol())) { //$NON-NLS-1$ - throw new MalformedURLException("I only serve Kubernetes connections"); //$NON-NLS-1$ - } - return new JmcKubernetesJmxConnector(serviceURL, environment); - } -} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnector.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnector.java deleted file mode 100644 index 0d1d98264a..0000000000 --- a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnector.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2024, Kantega AS. All rights reserved. - * - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * The contents of this file are subject to the terms of either the Universal Permissive License - * v 1.0 as shown at http://oss.oracle.com/licenses/upl - * - * or the following license: - * - * Redistribution and use in source and binary forms, with or without modification, are permitted - * provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list of - * conditions and the following disclaimer in the documentation and/or other materials provided with - * the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors may be used to - * endorse or promote products derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY - * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.openjdk.jmc.kubernetes; - -import java.io.IOException; -import java.util.Map; - -import javax.management.remote.JMXServiceURL; - -import org.jolokia.client.J4pClient; -import org.jolokia.client.jmxadapter.RemoteJmxAdapter; -import org.jolokia.kubernetes.client.KubernetesJmxConnector; - -public class JmcKubernetesJmxConnector extends KubernetesJmxConnector { - - public JmcKubernetesJmxConnector(JMXServiceURL serviceURL, Map environment) { - super(serviceURL, environment); - } - - @Override - protected RemoteJmxAdapter createAdapter(J4pClient client) throws IOException { - return new JmcKubernetesJmxConnection(client); - } -} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesPlugin.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesPlugin.java deleted file mode 100644 index a0cd408f67..0000000000 --- a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesPlugin.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2024, Kantega AS. All rights reserved. - * - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * The contents of this file are subject to the terms of either the Universal Permissive License - * v 1.0 as shown at http://oss.oracle.com/licenses/upl - * - * or the following license: - * - * Redistribution and use in source and binary forms, with or without modification, are permitted - * provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list of - * conditions and the following disclaimer in the documentation and/or other materials provided with - * the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors may be used to - * endorse or promote products derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY - * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.openjdk.jmc.kubernetes; - -import org.eclipse.core.runtime.Platform; -import org.openjdk.jmc.kubernetes.preferences.KubernetesScanningParameters; -import org.openjdk.jmc.kubernetes.preferences.PreferenceConstants; -import org.openjdk.jmc.ui.MCAbstractUIPlugin; -import org.openjdk.jmc.common.security.ICredentials; -import org.openjdk.jmc.common.security.PersistentCredentials; -import org.openjdk.jmc.common.security.SecurityException; -import org.openjdk.jmc.common.security.SecurityManagerFactory; -import org.openjdk.jmc.ui.misc.DisplayToolkit; -import org.osgi.framework.BundleContext; -import org.osgi.framework.FrameworkUtil; - -public class JmcKubernetesPlugin extends MCAbstractUIPlugin - implements KubernetesScanningParameters, PreferenceConstants { - - public final static String PLUGIN_ID = "org.openjdk.jmc.kubernetes"; //$NON-NLS-1$ - - // The shared instance. - private static JmcKubernetesPlugin plugin; - - /** - * The constructor. - */ - public JmcKubernetesPlugin() { - super(PLUGIN_ID); - plugin = this; - } - - /** - * @return the shared instance. - */ - public static JmcKubernetesPlugin getDefault() { - return plugin; - } - - private void ensureNeededCredentialsAreUnlocked() { - if (getScanningCredentials() != null && SecurityManagerFactory.getSecurityManager().isLocked()) { - DisplayToolkit.safeAsyncExec(() -> { - try { - SecurityManagerFactory.getSecurityManager().unlock(); - } catch (SecurityException e) { - logError("Error unlocking credentials needed for kubernetes scanning", e);//$NON-NLS-1$ - } - }); - } - } - - @Override - public boolean scanForInstances() { - // If credentials are locked and credentials are required, the scanner thread - // will get hung - // therefore await credentials store to be unlocked before proceeding to scan - return getPreferenceStore().getBoolean(P_SCAN_FOR_INSTANCES) - && (getScanningCredentials() == null || !SecurityManagerFactory.getSecurityManager().isLocked()); - - } - - @Override - public boolean scanAllContexts() { - return getPreferenceStore().getBoolean(P_SCAN_ALL_CONTEXTS); - } - - @Override - public String jolokiaPort() { - return getPreferenceStore().getString(P_JOLOKIA_PORT); - } - - private PersistentCredentials getScanningCredentials() { - String key = getPreferenceStore().getString(P_CREDENTIALS_KEY); - return key == null ? null : new PersistentCredentials(key); - } - - public ICredentials storeCredentials(String username, String password) throws SecurityException { - PersistentCredentials credentials = new PersistentCredentials(username, password, "kubernetes");//$NON-NLS-1$ - getPreferenceStore().setValue(P_CREDENTIALS_KEY, credentials.getExportedId()); - return credentials; - } - - @Override - public String username() throws SecurityException { - final PersistentCredentials cred = getScanningCredentials(); - if (cred == null) { - return "";//$NON-NLS-1$ - } else { - return cred.getUsername(); - } - } - - @Override - public String password() throws SecurityException { - final PersistentCredentials cred = getScanningCredentials(); - if (cred == null) { - return "";//$NON-NLS-1$ - } else { - return cred.getPassword(); - } - } - - @Override - public String jolokiaPath() { - return getPreferenceStore().getString(P_JOLOKIA_PATH); - } - - @Override - public String requireLabel() { - return getPreferenceStore().getString(P_REQUIRE_LABEL); - } - - @Override - public String jolokiaProtocol() { - return getPreferenceStore().getString(P_JOLOKIA_PROTOCOL); - } - - @Override - public void logError(String message, Throwable error) { - Platform.getLog(FrameworkUtil.getBundle(getClass())).error(message, error); - } - - @Override - public void start(BundleContext context) throws Exception { - super.start(context); - this.ensureNeededCredentialsAreUnlocked(); - } - -} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/KubernetesDiscoveryListener.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/KubernetesDiscoveryListener.java deleted file mode 100644 index bcb2420ac3..0000000000 --- a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/KubernetesDiscoveryListener.java +++ /dev/null @@ -1,340 +0,0 @@ -/* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2024, Kantega AS. All rights reserved. - * - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * The contents of this file are subject to the terms of either the Universal Permissive License - * v 1.0 as shown at http://oss.oracle.com/licenses/upl - * - * or the following license: - * - * Redistribution and use in source and binary forms, with or without modification, are permitted - * provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list of - * conditions and the following disclaimer in the documentation and/or other materials provided with - * the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors may be used to - * endorse or promote products derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY - * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.openjdk.jmc.kubernetes; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.IOException; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.NoSuchElementException; -import java.util.Properties; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -import javax.management.remote.JMXConnector; -import javax.management.remote.JMXServiceURL; - -import org.apache.commons.codec.binary.Base64; -import org.jolokia.client.J4pClient; -import org.jolokia.kubernetes.client.KubernetesJmxConnector; -import org.jolokia.server.core.osgi.security.AuthorizationHeaderParser; -import org.jolokia.server.core.util.Base64Util; -import org.openjdk.jmc.jolokia.AbstractCachedDescriptorProvider; -import org.openjdk.jmc.jolokia.JolokiaAgentDescriptor; -import org.openjdk.jmc.jolokia.ServerConnectionDescriptor; -import org.openjdk.jmc.kubernetes.preferences.KubernetesScanningParameters; -import org.openjdk.jmc.common.jvm.JVMDescriptor; -import org.openjdk.jmc.common.security.SecurityException; - -import io.fabric8.kubernetes.api.model.NamedContext; -import io.fabric8.kubernetes.api.model.ObjectMeta; -import io.fabric8.kubernetes.api.model.Pod; -import io.fabric8.kubernetes.api.model.PodList; -import io.fabric8.kubernetes.api.model.Secret; -import io.fabric8.kubernetes.client.Config; -import io.fabric8.kubernetes.client.KubernetesClient; -import io.fabric8.kubernetes.client.internal.KubeConfigUtils; -import io.fabric8.kubernetes.client.utils.Utils; -import io.fabric8.kubernetes.client.dsl.AnyNamespaceOperation; -import io.fabric8.kubernetes.client.dsl.PodResource; - -/** - * This class attempts to connect to JVMs in pods running in kubernetes in a background thread. - * Enablement and parameters for the scanning is given by user preferences. - */ -public class KubernetesDiscoveryListener extends AbstractCachedDescriptorProvider { - - private final static Pattern SECRET_PATTERN = Pattern - .compile("\\$\\{kubernetes/secret/(?[^/]+)/(?[^\\}]+)}"); //$NON-NLS-1$ - private final static Pattern ATTRIBUTE_PATTERN = Pattern - .compile("\\$\\{kubernetes/annotation/(?[^/]+)}"); //$NON-NLS-1$ - private final static Set VALID_JOLOKIA_PROTOCOLS = new HashSet<>(Arrays.asList("http", "https")); //$NON-NLS-1$ //$NON-NLS-2$ - - KubernetesScanningParameters settings; - - public KubernetesDiscoveryListener() { - this(JmcKubernetesPlugin.getDefault()); - } - - //Public constructor in order for test plugin to be able to rig tests in an easier manner - public KubernetesDiscoveryListener(KubernetesScanningParameters parameters) { - this.settings = parameters; - } - - public final String getDescription() { - return Messages.KubernetesDiscoveryListener_Description; - } - - @Override - public String getName() { - return "kubernetes"; //$NON-NLS-1$ - } - - boolean notEmpty(String value) { - return value != null && value.length() > 0; - } - - private List contexts; - private long contextsCached = 0L; - - private List allContexts() throws IOException { - final String path = Utils.getSystemPropertyOrEnvVar(Config.KUBERNETES_KUBECONFIG_FILE, - new File(System.getProperty("user.home"), ".kube" + File.separator + "config").toString()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - File configPath = new File(path); - if (contexts != null && contextsCached > configPath.lastModified()) {// the YAML parsing is soo incredibly - // sloow, hence cache context names for - // later - // runs - return contexts; - } - // reload config if kubeconfig has been modified since we cached the config - io.fabric8.kubernetes.api.model.Config config = KubeConfigUtils.parseConfig(configPath); - this.contextsCached = System.currentTimeMillis(); - KubernetesJmxConnector.resetKubernetesConfig(); - return contexts = config.getContexts().stream().map(NamedContext::getName).collect(Collectors.toList()); - } - - @Override - protected Map discoverJvms() { - return scanForJvms(); - } - - Map scanForJvms() { - Map found = new HashMap<>(); - if (!isEnabled()) { - return found; - } - boolean hasScanned = false; - - if (settings.scanAllContexts()) { - try { - for (final String context : allContexts()) { - hasScanned = true; - scanContext(found, settings, context); - } - } catch (IOException e) { - settings.logError(Messages.KubernetesDiscoveryListener_UnableToFindContexts, e); - } - } - if (!hasScanned) {// scan default context - return scanContext(found, settings, null); - } - return found; - } - - private Map scanContext( - Map found, KubernetesScanningParameters parameters, String context) { - try { - scanContextUnsafe(found, parameters, context); - } catch (Exception e) { - parameters.logError(Messages.KubernetesDiscoveryListener_UnableToScan + context, e); - } - return found; - } - - private Map scanContextUnsafe( - Map found, KubernetesScanningParameters parameters, String context) { - String pathLabel = parameters.requireLabel(); - KubernetesClient client = KubernetesJmxConnector.getApiClient(context); - - AnyNamespaceOperation query = client.pods().inAnyNamespace(); - List podList; - boolean hasPathLabel = notEmpty(pathLabel); - if (hasPathLabel) { - podList = query.withLabel(pathLabel).list().getItems(); - } else { - podList = query.list().getItems(); - } - // can consider parallelization for big contexts, however since it is the - // background await the situation a bit - podList.stream().forEach(pod -> scanPod(found, parameters, context, client, pod)); - return found; - } - - private void scanPod( - Map found, KubernetesScanningParameters parameters, String context, - KubernetesClient client, Pod pod) { - - final ObjectMeta metadata = pod.getMetadata(); - HashMap headers = new HashMap<>(); - Map env = new HashMap<>(); - try { - if (notEmpty(parameters.username())) { - if (!notEmpty(parameters.password())) { - throw new IllegalArgumentException(Messages.KubernetesDiscoveryListener_MustProvidePassword); - } - authorize(headers, client, parameters.username(), parameters.password(), metadata.getNamespace(), env); - } - } catch (SecurityException e) { - // skipping authorization if anything fails - } - final StringBuilder url = new StringBuilder("/api/").append(pod.getApiVersion()).append("/namespaces/") - .append(metadata.getNamespace()).append("/pods/"); - // JMX url must be reverse constructed, so that we can connect from the - // resulting node in the JVM browser - final StringBuilder jmxUrl = new StringBuilder("service:jmx:kubernetes:///").append(metadata.getNamespace()) //$NON-NLS-1$ - .append('/'); - - final String protocol = getValueOrAttribute(parameters.jolokiaProtocol(), metadata); - final String podName = metadata.getName(); - if (notEmpty(protocol)) { - if (!VALID_JOLOKIA_PROTOCOLS.contains(protocol)) { - throw new IllegalArgumentException(Messages.KubernetesDiscoveryListener_JolokiaProtocol + protocol - + Messages.KubernetesDiscoveryListener_HttpOrHttps); - } - // a bit clumsy, need to inject protocol _before_ podname in selflink - url.append(protocol).append(':'); - jmxUrl.append(protocol).append(':'); - } - - jmxUrl.append(podName); - url.append(podName); - - final String port = getValueOrAttribute(parameters.jolokiaPort(), metadata); - if (port != null) { - url.append(":").append(port); //$NON-NLS-1$ - jmxUrl.append(':').append(port); - } - - url.append("/proxy"); //$NON-NLS-1$ - - final String path = getValueOrAttribute(parameters.jolokiaPath(), metadata); - - if (!path.startsWith("/")) { //$NON-NLS-1$ - url.append('/'); - jmxUrl.append('/'); - } - url.append(path); - jmxUrl.append(path); - - if (context != null) { - env.put(KubernetesJmxConnector.KUBERNETES_CLIENT_CONTEXT, context); - } - J4pClient jvmClient = KubernetesJmxConnector.probeProxyPath(env, client, url, headers); - if (jvmClient != null) { - JmcKubernetesJmxConnection connection; - try { - connection = new JmcKubernetesJmxConnection(jvmClient); - JVMDescriptor jvmDescriptor = JolokiaAgentDescriptor.attemptToGetJvmInfo(connection); - JMXServiceURL jmxServiceURL = new JMXServiceURL(jmxUrl.toString()); - KubernetesJvmDescriptor descriptor = new KubernetesJvmDescriptor(metadata, jvmDescriptor, jmxServiceURL, - env); - found.put(descriptor.getGUID(), descriptor); - } catch (IOException e) { - parameters.logError(Messages.KubernetesDiscoveryListener_ErrConnectingToJvm, e); - - } - } - } - - private String getValueOrAttribute(String configValue, ObjectMeta metadata) { - if (notEmpty(configValue)) { - Matcher pattern = ATTRIBUTE_PATTERN.matcher(configValue); - if (pattern.find()) { - return metadata.getAnnotations().get(pattern.group("annotationName")); //$NON-NLS-1$ - } else { - return configValue;// the default is to use config value as is - } - } - return null; - } - - private void authorize( - HashMap headers, KubernetesClient client, String username, String password, String namespace, - Map jmxEnv) { - - final Matcher userNameMatcher = SECRET_PATTERN.matcher(username); - String secretName = null; - Map secretValues = null; - if (userNameMatcher.find()) { - secretName = userNameMatcher.group("secretName"); //$NON-NLS-1$ - secretValues = findSecret(client, namespace, secretName); - username = secretValues.get(userNameMatcher.group("itemName")); //$NON-NLS-1$ - } - - final Matcher passwordMatcher = SECRET_PATTERN.matcher(password); - if (passwordMatcher.find()) { - if (!secretName.equals(passwordMatcher.group("secretName"))) { //$NON-NLS-1$ - secretValues = findSecret(client, namespace, passwordMatcher.group("secretName")); //$NON-NLS-1$ - } - password = secretValues.get(passwordMatcher.group("itemName")); //$NON-NLS-1$ - } - - headers.put(AuthorizationHeaderParser.JOLOKIA_ALTERNATE_AUTHORIZATION_HEADER, - "Basic " + Base64Util.encode((username + ":" + password).getBytes())); //$NON-NLS-1$ //$NON-NLS-2$ - jmxEnv.put(JMXConnector.CREDENTIALS, new String[] {username, password}); - - } - - @SuppressWarnings({"unchecked", "rawtypes"}) - private Map findSecret(KubernetesClient client, String namespace, String secretName) { - - for (Secret secret : client.secrets().inNamespace(namespace).list().getItems()) { - if (secret.getMetadata().getName().equals(secretName)) { - if ("kubernetes.io/basic-auth".equals(secret.getType())) { //$NON-NLS-1$ - Map data = secret.getData(); - data.replaceAll((key, value) -> new String(Base64.decodeBase64(value))); - return data; - } else if ("Opaque".equals(secret.getType())) { //$NON-NLS-1$ - for (Entry entry : secret.getData().entrySet()) { - if (entry.getKey().endsWith(".properties")) { //$NON-NLS-1$ - try { - Properties properties = new Properties(); - properties.load(new ByteArrayInputStream(Base64.decodeBase64(entry.getValue()))); - return (Map) properties; - } catch (IOException ignore) { - } - } - } - } - } - - } - throw new NoSuchElementException(Messages.KubernetesDiscoveryListener_CouldNotFindSecret + secretName - + Messages.KubernetesDiscoveryListener_InNamespace + namespace); - - } - - @Override - protected boolean isEnabled() { - return this.settings.scanForInstances(); - } -} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/KubernetesJvmDescriptor.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/KubernetesJvmDescriptor.java deleted file mode 100644 index a646a0fcc7..0000000000 --- a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/KubernetesJvmDescriptor.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2024, Kantega AS. All rights reserved. - * - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * The contents of this file are subject to the terms of either the Universal Permissive License - * v 1.0 as shown at http://oss.oracle.com/licenses/upl - * - * or the following license: - * - * Redistribution and use in source and binary forms, with or without modification, are permitted - * provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list of - * conditions and the following disclaimer in the documentation and/or other materials provided with - * the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors may be used to - * endorse or promote products derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY - * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.openjdk.jmc.kubernetes; - -import java.io.IOException; -import java.util.Map; - -import javax.management.remote.JMXServiceURL; - -import org.jolokia.kubernetes.client.KubernetesJmxConnector; -import org.openjdk.jmc.jolokia.ServerConnectionDescriptor; -import org.openjdk.jmc.common.jvm.JVMDescriptor; - -import io.fabric8.kubernetes.api.model.ObjectMeta; - -public class KubernetesJvmDescriptor implements ServerConnectionDescriptor { - - private final JVMDescriptor jvmDescriptor; - private final ObjectMeta metadata; - private final Map env; - private final JMXServiceURL connectUrl; - - public KubernetesJvmDescriptor(ObjectMeta metadata, JVMDescriptor jvmDescriptor, JMXServiceURL connectUrl, - Map env) { - this.jvmDescriptor = jvmDescriptor; - this.metadata = metadata; - this.env = env; - this.connectUrl = connectUrl; - } - - @Override - public String getGUID() { - return this.metadata.getName(); - } - - @Override - public String getDisplayName() { - return this.metadata.getName(); - } - - @Override - public JVMDescriptor getJvmInfo() { - return this.jvmDescriptor; - } - - public String getPath() { - String namespace = metadata.getNamespace(); - final Object context = this.env.get(KubernetesJmxConnector.KUBERNETES_CLIENT_CONTEXT); - if (context != null) { - return context + "/" + namespace; //$NON-NLS-1$ - } - return namespace; - } - - @Override - public JMXServiceURL createJMXServiceURL() throws IOException { - return this.connectUrl; - } - - @Override - public Map getEnvironment() { - return this.env; - } - - @Override - public JMXServiceURL serviceUrl() { - return this.connectUrl; - } - -} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/Messages.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/Messages.java deleted file mode 100644 index 9dcff25a26..0000000000 --- a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/Messages.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2024, Kantega AS. All rights reserved. - * - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * The contents of this file are subject to the terms of either the Universal Permissive License - * v 1.0 as shown at http://oss.oracle.com/licenses/upl - * - * or the following license: - * - * Redistribution and use in source and binary forms, with or without modification, are permitted - * provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list of - * conditions and the following disclaimer in the documentation and/or other materials provided with - * the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors may be used to - * endorse or promote products derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY - * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.openjdk.jmc.kubernetes; - -import org.eclipse.osgi.util.NLS; - -public class Messages extends NLS { - private static final String BUNDLE_NAME = "org.openjdk.jmc.kubernetes.messages"; //$NON-NLS-1$ - public static String KubernetesDiscoveryListener_CouldNotFindSecret; - public static String KubernetesDiscoveryListener_Description; - public static String KubernetesDiscoveryListener_ErrConnectingToJvm; - public static String KubernetesDiscoveryListener_HttpOrHttps; - public static String KubernetesDiscoveryListener_InNamespace; - public static String KubernetesDiscoveryListener_JolokiaProtocol; - public static String KubernetesDiscoveryListener_MustProvidePassword; - public static String KubernetesDiscoveryListener_UnableToFindContexts; - public static String KubernetesDiscoveryListener_UnableToScan; - static { - // initialize resource bundle - NLS.initializeMessages(BUNDLE_NAME, Messages.class); - } - - private Messages() { - } -} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/messages.properties b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/messages.properties deleted file mode 100644 index ea0166360a..0000000000 --- a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/messages.properties +++ /dev/null @@ -1,9 +0,0 @@ -KubernetesDiscoveryListener_CouldNotFindSecret=Could not find secret named -KubernetesDiscoveryListener_Description=List JVM in kubernetes cluster -KubernetesDiscoveryListener_ErrConnectingToJvm=Error connecting to JVM in pod -KubernetesDiscoveryListener_HttpOrHttps=' must be either 'http' or 'https' -KubernetesDiscoveryListener_InNamespace=\ in namespace -KubernetesDiscoveryListener_JolokiaProtocol=Jolokia protocol ' -KubernetesDiscoveryListener_MustProvidePassword=Password must be specified when username is specified -KubernetesDiscoveryListener_UnableToFindContexts=Unable to find all kubernetes contexts -KubernetesDiscoveryListener_UnableToScan=Unable to scan kubernetes context diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/JmcKubernetesPreferenceForm.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/JmcKubernetesPreferenceForm.java deleted file mode 100644 index 5b2198933a..0000000000 --- a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/JmcKubernetesPreferenceForm.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2024, Kantega AS. All rights reserved. - * - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * The contents of this file are subject to the terms of either the Universal Permissive License - * v 1.0 as shown at http://oss.oracle.com/licenses/upl - * - * or the following license: - * - * Redistribution and use in source and binary forms, with or without modification, are permitted - * provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list of - * conditions and the following disclaimer in the documentation and/or other materials provided with - * the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors may be used to - * endorse or promote products derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY - * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.openjdk.jmc.kubernetes.preferences; - -import java.util.Map; -import java.util.WeakHashMap; -import java.util.logging.Level; - -import org.eclipse.jface.preference.BooleanFieldEditor; -import org.eclipse.jface.preference.FieldEditor; -import org.eclipse.jface.preference.FieldEditorPreferencePage; -import org.eclipse.jface.preference.StringFieldEditor; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.ModifyListener; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Text; -import org.eclipse.ui.IWorkbench; -import org.eclipse.ui.IWorkbenchPreferencePage; -import org.openjdk.jmc.common.security.CredentialsNotAvailableException; -import org.openjdk.jmc.common.security.SecurityException; -import org.openjdk.jmc.kubernetes.JmcKubernetesPlugin; - -/** - * This class represents a preference page that is contributed to the Preferences dialog. By - * subclassing FieldEditorPreferencePage, we can use the field support built into JFace - * that allows us to create a page that is small and knows how to save, restore and apply itself. - *

- * This page is used to modify preferences only. They are stored in the preference store that - * belongs to the main plug-in class. That way, preferences can be accessed directly via the - * preference store. - */ -public class JmcKubernetesPreferenceForm extends FieldEditorPreferencePage - implements IWorkbenchPreferencePage, PreferenceConstants { - - private Map dependantControls = new WeakHashMap<>(); - private Text userField; - private Text passwordField; - private boolean credentialsDirty; - - public JmcKubernetesPreferenceForm() { - super(GRID); - setPreferenceStore(JmcKubernetesPlugin.getDefault().getPreferenceStore()); - setDescription(Messages.JmcKubernetesPreferenceForm_FormDescription); - } - - /** - * Creates the field editors. Field editors are abstractions of the common GUI blocks needed to - * manipulate various types of preferences. Each field editor knows how to save and restore - * itself. - */ - public void createFieldEditors() { - BooleanFieldEditor mainEnabler = new BooleanFieldEditor(P_SCAN_FOR_INSTANCES, - Messages.JmcKubernetesPreferenceForm_ScanForPods, getFieldEditorParent()) { - @Override - protected void valueChanged(boolean oldValue, boolean newValue) { - super.valueChanged(oldValue, newValue); - enableDependantFields(newValue); - } - }; - addField(mainEnabler); - - BooleanFieldEditor scanContextsEditor = new BooleanFieldEditor(P_SCAN_ALL_CONTEXTS, - Messages.JmcKubernetesPreferenceForm_AllContexts, getFieldEditorParent()); - this.addDependantField(scanContextsEditor, scanContextsEditor.getDescriptionControl(getFieldEditorParent())); - this.addTextField(new StringFieldEditor(P_REQUIRE_LABEL, Messages.JmcKubernetesPreferenceForm_RequireLabel, - getFieldEditorParent()), Messages.JmcKubernetesPreferenceForm_LabelToolTip); - this.addTextField(new StringFieldEditor(P_JOLOKIA_PATH, Messages.JmcKubernetesPreferenceForm_PathLabel, - getFieldEditorParent()), Messages.JmcKubernetesPreferenceForm_PathTooltip); - this.addTextField(new StringFieldEditor(P_JOLOKIA_PORT, Messages.JmcKubernetesPreferenceForm_PortLabel, - getFieldEditorParent()), Messages.JmcKubernetesPreferenceForm_PortTooltip); - this.addTextField(new StringFieldEditor(P_JOLOKIA_PROTOCOL, Messages.JmcKubernetesPreferenceForm_ProtocolLabel, - getFieldEditorParent()), Messages.JmcKubernetesPreferenceForm_ProtocolTooltip); - createCredentialFields(); - // set initial enablement - enableDependantFields(JmcKubernetesPlugin.getDefault().scanForInstances()); - - } - - private void createCredentialFields() { - Label userLabel = new Label(getFieldEditorParent(), SWT.NONE); - userLabel.setText(Messages.JmcKubernetesPreferenceForm_UsernameLabel); - userLabel.setLayoutData(new GridData()); - this.userField = new Text(getFieldEditorParent(), SWT.SINGLE | SWT.BORDER); - userField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); - userField.setToolTipText(Messages.JmcKubernetesPreferenceForm_UsernameTooltip); - this.dependantControls.put(userField, null); - - Label passLabel = new Label(getFieldEditorParent(), SWT.NONE); - passLabel.setText(Messages.JmcKubernetesPreferenceForm_PasswordLabel); - passLabel.setLayoutData(new GridData()); - this.passwordField = new Text(getFieldEditorParent(), SWT.PASSWORD | SWT.SINGLE | SWT.BORDER); - passwordField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); - - JmcKubernetesPlugin plugin = JmcKubernetesPlugin.getDefault(); - - try { - userField.setText(plugin.username()); - passwordField.setText(plugin.password()); - } catch (CredentialsNotAvailableException ignore) { - // ignore if credentials are not stored - } catch (SecurityException e) { - plugin.getLogger().log(Level.WARNING, "Could not load kubernetes credentials", e); //$NON-NLS-1$ - } - - ModifyListener markCredentials = e -> credentialsDirty = true; - this.userField.addModifyListener(markCredentials); - this.passwordField.addModifyListener(markCredentials); - } - - private void addTextField(StringFieldEditor field, String tooltip) { - Text textControl = field.getTextControl(getFieldEditorParent()); - this.addDependantField(field, textControl); - textControl.setToolTipText(tooltip); - field.getLabelControl(getFieldEditorParent()).setToolTipText(tooltip); - - } - - private void addDependantField(FieldEditor field, Control control) { - this.dependantControls.put(control, null); - addField(field); - } - - private void enableDependantFields(boolean enabled) { - for (Control field : this.dependantControls.keySet()) { - field.setEnabled(enabled); - } - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.ui.IWorkbenchPreferencePage#init(org.eclipse.ui.IWorkbench) - */ - public void init(IWorkbench workbench) { - } - - @Override - public boolean performOk() { - updateCredentialsIfApplicable(); - return super.performOk(); - } - - private void updateCredentialsIfApplicable() { - if (this.credentialsDirty) { - try { - JmcKubernetesPlugin.getDefault().storeCredentials(userField.getText(), passwordField.getText()); - this.credentialsDirty = false; - } catch (SecurityException ex) { - JmcKubernetesPlugin.getDefault().getLogger().log(Level.WARNING, - "Could not store kubernetes credentials", ex); //$NON-NLS-1$ - } - } - } - -} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/KubernetesScanningParameters.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/KubernetesScanningParameters.java deleted file mode 100644 index a67697d42b..0000000000 --- a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/KubernetesScanningParameters.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2024, Kantega AS. All rights reserved. - * - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * The contents of this file are subject to the terms of either the Universal Permissive License - * v 1.0 as shown at http://oss.oracle.com/licenses/upl - * - * or the following license: - * - * Redistribution and use in source and binary forms, with or without modification, are permitted - * provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list of - * conditions and the following disclaimer in the documentation and/or other materials provided with - * the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors may be used to - * endorse or promote products derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY - * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.openjdk.jmc.kubernetes.preferences; - -import org.openjdk.jmc.common.security.ICredentials; -import org.openjdk.jmc.common.security.SecurityException; - -public interface KubernetesScanningParameters { - boolean scanForInstances(); - - boolean scanAllContexts(); - - String jolokiaPort(); - - String username() throws SecurityException; - - String password() throws SecurityException; - - String jolokiaPath(); - - String jolokiaProtocol(); - - String requireLabel(); - - ICredentials storeCredentials(String username, String password) throws SecurityException; - - void logError(String message, Throwable error); -} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/Messages.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/Messages.java deleted file mode 100644 index a718015331..0000000000 --- a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/Messages.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2024, Kantega AS. All rights reserved. - * - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * The contents of this file are subject to the terms of either the Universal Permissive License - * v 1.0 as shown at http://oss.oracle.com/licenses/upl - * - * or the following license: - * - * Redistribution and use in source and binary forms, with or without modification, are permitted - * provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list of - * conditions and the following disclaimer in the documentation and/or other materials provided with - * the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors may be used to - * endorse or promote products derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY - * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.openjdk.jmc.kubernetes.preferences; - -import org.eclipse.osgi.util.NLS; - -public class Messages extends NLS { - private static final String BUNDLE_NAME = "org.openjdk.jmc.kubernetes.preferences.messages"; //$NON-NLS-1$ - public static String JmcKubernetesPreferenceForm_AllContexts; - public static String JmcKubernetesPreferenceForm_FormDescription; - public static String JmcKubernetesPreferenceForm_LabelToolTip; - public static String JmcKubernetesPreferenceForm_PasswordLabel; - public static String JmcKubernetesPreferenceForm_PasswordTooltip; - public static String JmcKubernetesPreferenceForm_PathLabel; - public static String JmcKubernetesPreferenceForm_PathTooltip; - public static String JmcKubernetesPreferenceForm_PortLabel; - public static String JmcKubernetesPreferenceForm_PortTooltip; - public static String JmcKubernetesPreferenceForm_ProtocolLabel; - public static String JmcKubernetesPreferenceForm_ProtocolTooltip; - public static String JmcKubernetesPreferenceForm_RequireLabel; - public static String JmcKubernetesPreferenceForm_ScanForPods; - public static String JmcKubernetesPreferenceForm_UsernameTooltip; - public static String JmcKubernetesPreferenceForm_UsernameLabel; - static { - // initialize resource bundle - NLS.initializeMessages(BUNDLE_NAME, Messages.class); - } - - private Messages() { - } -} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/PreferenceConstants.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/PreferenceConstants.java deleted file mode 100644 index 876d65a1b9..0000000000 --- a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/PreferenceConstants.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2024, Kantega AS. All rights reserved. - * - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * The contents of this file are subject to the terms of either the Universal Permissive License - * v 1.0 as shown at http://oss.oracle.com/licenses/upl - * - * or the following license: - * - * Redistribution and use in source and binary forms, with or without modification, are permitted - * provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list of - * conditions and the following disclaimer in the documentation and/or other materials provided with - * the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors may be used to - * endorse or promote products derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY - * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.openjdk.jmc.kubernetes.preferences; - -/** - * Constant definitions for plug-in preferences - */ -public interface PreferenceConstants { - - public static final String P_SCAN_FOR_INSTANCES = "scanForInstances"; //$NON-NLS-1$ - public static final String P_SCAN_ALL_CONTEXTS = "scanAllContexts"; //$NON-NLS-1$ - public static final String P_REQUIRE_LABEL = "requireLabel"; //$NON-NLS-1$ - public static final String P_JOLOKIA_PATH = "jolokiaPath"; //$NON-NLS-1$ - public static final String P_JOLOKIA_PORT = "jolokiaPort"; //$NON-NLS-1$ - public static final String P_JOLOKIA_PROTOCOL = "jolokiaProtocol"; //$NON-NLS-1$ - public static final String P_CREDENTIALS_KEY = "kubernetes.scanning.credentials"; //$NON-NLS-1$ -} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/PreferenceInitializer.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/PreferenceInitializer.java deleted file mode 100644 index 3aac64c2c2..0000000000 --- a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/PreferenceInitializer.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2024, Kantega AS. All rights reserved. - * - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * The contents of this file are subject to the terms of either the Universal Permissive License - * v 1.0 as shown at http://oss.oracle.com/licenses/upl - * - * or the following license: - * - * Redistribution and use in source and binary forms, with or without modification, are permitted - * provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list of - * conditions and the following disclaimer in the documentation and/or other materials provided with - * the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors may be used to - * endorse or promote products derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY - * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.openjdk.jmc.kubernetes.preferences; - -import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer; -import org.eclipse.jface.preference.IPreferenceStore; -import org.openjdk.jmc.kubernetes.JmcKubernetesPlugin; - -/** - * Class used to initialize default preference values. - */ -public class PreferenceInitializer extends AbstractPreferenceInitializer implements PreferenceConstants { - - /* - * (non-Javadoc) - * - * @see org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer# - * initializeDefaultPreferences() - */ - public void initializeDefaultPreferences() { - IPreferenceStore store = JmcKubernetesPlugin.getDefault().getPreferenceStore(); - store.setDefault(P_SCAN_FOR_INSTANCES, false); - store.setDefault(P_SCAN_ALL_CONTEXTS, false); - store.setDefault(P_REQUIRE_LABEL, "jolokia"); //$NON-NLS-1$ - store.setDefault(P_JOLOKIA_PATH, "/jolokia/"); //$NON-NLS-1$ - store.setDefault(P_JOLOKIA_PORT, "8778"); //$NON-NLS-1$ - } - -} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/messages.properties b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/messages.properties deleted file mode 100644 index 44b18279a0..0000000000 --- a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/messages.properties +++ /dev/null @@ -1,17 +0,0 @@ -JmcKubernetesPreferenceForm_AllContexts=Scan pods from all locally configured &contexts, if false: only scan the current contexts -JmcKubernetesPreferenceForm_FormDescription=Options that allows you to scan kubernetes for JVMs running Jolokia\ -\n\nThe Kubernetes Proxy API will be used to attempt to access Jolokia like this (parenthesis denote optional components): \ -\n\n$kubernetesBaseUrl/api/v1/namespaces/podNamespace/pods/(jolokiaProtocol:)podname(:jolokiaPort)/proxy/jolokiaPath\n\n -JmcKubernetesPreferenceForm_LabelToolTip=Only attempt to connect to pods with this label set, leave empty to try to scan all pods -JmcKubernetesPreferenceForm_PasswordLabel=Require pass&word -JmcKubernetesPreferenceForm_PasswordTooltip=Password , alternatively use ${kubernetes/secret/secretName/secretItem} where the secret is in the same namespace as the pod and the type is either kubernetes.io/basic-auth or Opaque with java.util.Properties compatible values -JmcKubernetesPreferenceForm_PathLabel=Jolokia &path in pods -JmcKubernetesPreferenceForm_PathTooltip=Use this path for jolokia, or specify ${kubernetes/annotation/annotationName} to be able to to get port name from pod metadata annotation of your choice -JmcKubernetesPreferenceForm_PortLabel=Jolokia p&ort in pods -JmcKubernetesPreferenceForm_PortTooltip=Port to use, leave empty to use default port of Kubernetes proxy, hardcode a port value, or alternatively ${kubernetes/annotation/annotationName} to get port name from pod metadata annotation of your choice -JmcKubernetesPreferenceForm_ProtocolLabel=Jolokia pro&tocol in pods -JmcKubernetesPreferenceForm_ProtocolTooltip=Protocol to use (optional), will infer http if not set, or hardcode to https, or alternatively ${kubernetes/annotation/annotationName} to get port name from pod metadata annotation of your choice -JmcKubernetesPreferenceForm_RequireLabel=Require &label to scan pod -JmcKubernetesPreferenceForm_ScanForPods=&Scan for kubernetes pods with Jolokia support -JmcKubernetesPreferenceForm_UsernameTooltip=Username , alternatively use ${kubernetes/secret/secretName/secretItem} where the secret is in the same namespace as the pod and the type is either kubernetes.io/basic-auth or Opaque with java.util.Properties compatible values -JmcKubernetesPreferenceForm_UsernameLabel=Require &username diff --git a/application/pom.xml b/application/pom.xml index e40f01a4a3..90dc2c96f2 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -112,7 +112,6 @@ org.openjdk.jmc.updatesite.ide org.openjdk.jmc.updatesite.rcp org.openjdk.jmc.jolokia - org.openjdk.jmc.kubernetes l10n tests diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/META-INF/MANIFEST.MF b/application/uitests/org.openjdk.jmc.kubernetes.test/META-INF/MANIFEST.MF deleted file mode 100644 index 46a9f1b9fc..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/META-INF/MANIFEST.MF +++ /dev/null @@ -1,20 +0,0 @@ -Manifest-Version: 1.0 -Bundle-ManifestVersion: 2 -Bundle-Name: RJMX Test -Bundle-SymbolicName: org.openjdk.jmc.kubernetes.test;singleton:=true -Bundle-Version: 9.1.0.qualifier -Import-Package: io.fabric8.kubernetes.api.model -Bundle-Vendor: Oracle Corporation -Bundle-RequiredExecutionEnvironment: JavaSE-17 -Bundle-ActivationPolicy: lazy -Require-Bundle: org.junit, - org.openjdk.jmc.jolokia, - org.eclipse.osgi;bundle-version="3.16.200", - org.eclipse.ui.workbench, - org.openjdk.jmc.rjmx;bundle-version="9.0.0", - org.eclipse.ui, - org.hamcrest;bundle-version="2.1", - org.openjdk.jmc.kubernetes;bundle-version="9.0.0", - com.github.tomakehurst.wiremock-standalone;bundle-version="2.27.2", - org.awaitility;bundle-version="4.0.0" -Automatic-Module-Name: org.openjdk.jmc.jolokia.test diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/build.properties b/application/uitests/org.openjdk.jmc.kubernetes.test/build.properties deleted file mode 100644 index b4608c8522..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/build.properties +++ /dev/null @@ -1,36 +0,0 @@ -# -# Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. -# Copyright (c) 2024, Kantega AS. All rights reserved. -# -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# The contents of this file are subject to the terms of either the Universal Permissive License -# v 1.0 as shown at http://oss.oracle.com/licenses/upl -# -# or the following license: -# -# Redistribution and use in source and binary forms, with or without modification, are permitted -# provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, this list of conditions -# and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright notice, this list of -# conditions and the following disclaimer in the documentation and/or other materials provided with -# the distribution. -# -# 3. Neither the name of the copyright holder nor the names of its contributors may be used to -# endorse or promote products derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR -# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY -# WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -source.. = src/test/java/,\ - src/test/resources/ -output.. = target/test-classes/ -bin.includes = META-INF/,. diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/pom.xml b/application/uitests/org.openjdk.jmc.kubernetes.test/pom.xml deleted file mode 100644 index 652ddbc5a6..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/pom.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - - 4.0.0 - - org.openjdk.jmc - missioncontrol.application.uitests - ${revision}${changelist} - - org.openjdk.jmc.kubernetes.test - eclipse-test-plugin - - - ${project.basedir}/../../../configuration - - - - - org.awaitility - awaitility - 4.0.0 - - - diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/java/org/openjdk/jmc/kubernetes/JmcKubernetesTest.java b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/java/org/openjdk/jmc/kubernetes/JmcKubernetesTest.java deleted file mode 100644 index 46d3c5ea8b..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/java/org/openjdk/jmc/kubernetes/JmcKubernetesTest.java +++ /dev/null @@ -1,313 +0,0 @@ -/* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2024, Kantega AS. All rights reserved. - * - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * The contents of this file are subject to the terms of either the Universal Permissive License - * v 1.0 as shown at http://oss.oracle.com/licenses/upl - * - * or the following license: - * - * Redistribution and use in source and binary forms, with or without modification, are permitted - * provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list of - * conditions and the following disclaimer in the documentation and/or other materials provided with - * the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors may be used to - * endorse or promote products derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY - * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.openjdk.jmc.kubernetes; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.net.MalformedURLException; -import java.time.Duration; -import java.util.Base64; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import javax.management.AttributeNotFoundException; -import javax.management.InstanceNotFoundException; -import javax.management.InvalidAttributeValueException; -import javax.management.MBeanException; -import javax.management.MBeanServerConnection; -import javax.management.MalformedObjectNameException; -import javax.management.ObjectName; -import javax.management.ReflectionException; -import javax.management.remote.JMXConnector; -import javax.management.remote.JMXServiceURL; - -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.HttpClients; -import org.awaitility.Awaitility; -import org.junit.Assert; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.ClassRule; -import org.junit.Test; -import org.openjdk.jmc.common.IDescribable; -import org.openjdk.jmc.common.jvm.JVMType; -import org.openjdk.jmc.common.security.ICredentials; -import org.openjdk.jmc.common.security.InMemoryCredentials; -import org.openjdk.jmc.common.security.SecurityException; -import org.openjdk.jmc.kubernetes.preferences.KubernetesScanningParameters; -import org.openjdk.jmc.rjmx.common.IConnectionDescriptor; -import org.openjdk.jmc.rjmx.common.IServerDescriptor; -import org.openjdk.jmc.rjmx.descriptorprovider.IDescriptorListener; - -import com.github.tomakehurst.wiremock.client.WireMock; -import com.github.tomakehurst.wiremock.core.WireMockConfiguration; -import com.github.tomakehurst.wiremock.extension.responsetemplating.ResponseTemplateTransformer; -import com.github.tomakehurst.wiremock.junit.WireMockRule; - -/** - * Test that JMX connections done with JmcKubernetesJmxConnectionProvider are functional. In order - * to be able to test this in a contained environment, the kubernetes API is mocked with wiremock. - */ -@SuppressWarnings("restriction") -public class JmcKubernetesTest { - - static class TestParameters implements KubernetesScanningParameters { - public boolean scanForInstances, scanAllContexts; - public String jolokiaPort, jolokiaPath = "/jolokia/", jolokiaProtocol, requireLabel; - public InMemoryCredentials credentials; - - @Override - public boolean scanForInstances() { - return this.scanForInstances; - } - - @Override - public boolean scanAllContexts() { - return this.scanAllContexts; - } - - @Override - public String jolokiaPort() { - return this.jolokiaPort; - } - - @Override - public String username() throws SecurityException { - return this.credentials == null ? null : this.credentials.getUsername(); - } - - @Override - public String password() throws SecurityException { - return this.credentials == null ? null : this.credentials.getPassword(); - } - - @Override - public String jolokiaPath() { - return this.jolokiaPath; - } - - @Override - public String jolokiaProtocol() { - return this.jolokiaProtocol; - } - - @Override - public String requireLabel() { - return this.requireLabel; - } - - @Override - public ICredentials storeCredentials(String username, String password) throws SecurityException { - return this.credentials = new InMemoryCredentials(username, password); - } - - @Override - public void logError(String message, Throwable error) { - System.out.println(message); - error.printStackTrace(System.out); - } - } - - @ClassRule - public static WireMockRule wiremock = new WireMockRule( - WireMockConfiguration.options().extensions(new ResponseTemplateTransformer(false)).port(0)); - - static final String jolokiaUrl = "service:jmx:kubernetes:///ns1/pod-abcdef/jolokia"; - - private static MBeanServerConnection jolokiaConnection; - - @BeforeClass - public static void connect() throws Exception { - CloseableHttpResponse configResponse = HttpClients.createDefault() - .execute(new HttpGet(wiremock.baseUrl() + "/mock-kube-config.yml")); - Assert.assertEquals(configResponse.getStatusLine().getStatusCode(), 200); - File configFile = File.createTempFile("mock-kube-config", ".yml"); - configResponse.getEntity().writeTo(new FileOutputStream(configFile)); - // we set this so the KubernetesDiscoveryListener will work - //Setting taken from: https://github.com/fabric8io/kubernetes-client/blob/77a65f7d40f31a5dc37492cd9de3c317c2702fb4/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/Config.java#L120, unlikely to change - System.setProperty("kubeconfig", configFile.getAbsolutePath()); - jolokiaConnection = getKubernetesMBeanConnector(); - } - - @Test - public void testExecuteOperation() throws InstanceNotFoundException, MalformedObjectNameException, MBeanException, - ReflectionException, MalformedURLException, IOException { - jolokiaConnection.invoke(new ObjectName("java.lang:type=Memory"), "gc", new Object[0], new String[0]); - } - - @Test - public void testReadAttribute() - throws InstanceNotFoundException, AttributeNotFoundException, InvalidAttributeValueException, - MalformedObjectNameException, MBeanException, ReflectionException, MalformedURLException, IOException { - MBeanServerConnection jmxConnection = jolokiaConnection; - assertOneSingleAttribute(jmxConnection); - - } - - private void assertOneSingleAttribute(MBeanServerConnection jmxConnection) throws MalformedObjectNameException, - MBeanException, AttributeNotFoundException, InstanceNotFoundException, ReflectionException, IOException { - ObjectName objectName = new ObjectName("java.lang:type=Memory"); - String attribute = "Verbose"; - Assert.assertEquals(false, jmxConnection.getAttribute(objectName, attribute)); - } - - @Before - public void reset() { - wiremock.resetAll(); - wiremock.resetRequests(); - } - - private static MBeanServerConnection getKubernetesMBeanConnector() throws IOException, MalformedURLException { - JMXConnector connector = new JmcKubernetesJmxConnectionProvider().newJMXConnector(new JMXServiceURL(jolokiaUrl), - Collections.emptyMap()); - connector.connect(); - MBeanServerConnection connection = connector.getMBeanServerConnection(); - return connection; - } - - @Test - public void testDiscoverWithMostlyDefaultSettings() throws Exception { - - TestParameters parameters = new TestParameters(); - // Set config so that scanning takes place - parameters.scanForInstances = true; - testThatJvmIsFound(parameters); - } - - @Test - public void testDiscoverWithPathFromAnnotation() throws Exception { - TestParameters parameters = new TestParameters(); - parameters.scanForInstances = true; - parameters.jolokiaPath = "${kubernetes/annotation/jolokiaPath}"; - testThatJvmIsFound(parameters); - } - - @Test - public void testDiscoverWithPortFromAnnotation() throws Exception { - TestParameters parameters = new TestParameters(); - parameters.scanForInstances = true; - parameters.jolokiaPort = "${kubernetes/annotation/jolokiaPort}"; - - testThatJvmIsFound(parameters); - } - - @Test - public void testDiscoverWithBasicAuthFromSecret() throws Exception { - TestParameters parameters = new TestParameters(); - parameters.scanForInstances = true; - parameters.credentials = new InMemoryCredentials("${kubernetes/secret/jolokia-auth/username}", - "${kubernetes/secret/jolokia-auth/password}"); - - testThatJvmIsFound(parameters); - // Verify that the expected authorization was picked up - WireMock.verify(WireMock - .postRequestedFor(WireMock.urlPathMatching("/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*")) - .withHeader("X-jolokia-authorization", - WireMock.equalTo("Basic " + Base64.getEncoder().encodeToString("admin:admin".getBytes())))); - } - - @Test - public void testDiscoverWithAuthFromProperties() throws Exception { - - TestParameters parameters = new TestParameters(); - parameters.scanForInstances = true; - parameters.credentials = new InMemoryCredentials("${kubernetes/secret/jolokia-properties/user}", - "${kubernetes/secret/jolokia-properties/password}"); - - testThatJvmIsFound(parameters); - // Verify that the expected authorization was picked up - WireMock.verify(WireMock - .postRequestedFor(WireMock.urlPathMatching("/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*")) - .withHeader("X-jolokia-authorization", - WireMock.equalTo("Basic " + Base64.getEncoder().encodeToString("admin:secret".getBytes())))); - } - - @Test - public void testDiscoverWithAuthDirectlyFromSettings() throws Exception { - - TestParameters parameters = new TestParameters(); - parameters.scanForInstances = true; - parameters.credentials = new InMemoryCredentials("user", "***"); - testThatJvmIsFound(parameters); - // Verify that the expected authorization was picked up - WireMock.verify(WireMock - .postRequestedFor(WireMock.urlPathMatching("/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*")) - .withHeader("X-jolokia-authorization", - WireMock.equalTo("Basic " + Base64.getEncoder().encodeToString("user:***".getBytes())))); - } - - private void testThatJvmIsFound(TestParameters parameters) throws Exception { - - final KubernetesDiscoveryListener scanner = new KubernetesDiscoveryListener(parameters); - final Map foundVms = new HashMap<>(); - IDescriptorListener descriptorListener = new IDescriptorListener() { - public void onDescriptorDetected( - IServerDescriptor serverDescriptor, String path, JMXServiceURL url, - IConnectionDescriptor connectionDescriptor, IDescribable provider) { - foundVms.put(serverDescriptor.getGUID(), serverDescriptor); - } - - public void onDescriptorRemoved(String descriptorId) { - foundVms.remove(descriptorId); - } - }; - scanner.addDescriptorListener(descriptorListener); - - try { - // Test that at least one VM (the one running the test was discovered) - - Awaitility.await().atMost(Duration.ofSeconds(5)).until(() -> !foundVms.isEmpty()); - IServerDescriptor descriptor = foundVms.get("pod-abcdef"); - Assert.assertNotNull(descriptor); - Assert.assertEquals( - "[JVMDescriptor] Java command: /Users/marska/Downloads/hawtio-app-2.9.1.jar --port 9090 PID: 88774", - descriptor.getJvmInfo().toString()); - Assert.assertEquals(JVMType.HOTSPOT, descriptor.getJvmInfo().getJvmType()); - Assert.assertEquals("18.0.1", descriptor.getJvmInfo().getJavaVersion()); - Assert.assertTrue(descriptor instanceof IConnectionDescriptor); - IConnectionDescriptor connectDescriptor = (IConnectionDescriptor) descriptor; - JMXConnector connector = new JmcKubernetesJmxConnectionProvider() - .newJMXConnector(connectDescriptor.createJMXServiceURL(), connectDescriptor.getEnvironment()); - connector.connect(); - assertOneSingleAttribute(connector.getMBeanServerConnection()); - - } finally { - // Tell scanner thread to exit - scanner.shutdown(); - } - } -} diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-attribute.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-attribute.json deleted file mode 100644 index 4230121a7e..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-attribute.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "request" : { - "attribute" : "Verbose", - "mbean" : "java.lang:type=Memory", - "type" : "read" - }, - "status" : 200, - "timestamp" : 1658563869, - "value" : false -} diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-basic-auth-secret.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-basic-auth-secret.json deleted file mode 100644 index b5bb351b7e..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-basic-auth-secret.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "apiVersion": "v1", - "data": { - "password": "YWRtaW4=", - "username": "YWRtaW4=" - }, - "kind": "Secret", - "metadata": { - "annotations": { - "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Secret\",\"metadata\":{\"annotations\":{},\"name\":\"jolokia-auth\",\"namespace\":\"jfr\"},\"stringData\":{\"password\":\"admin\",\"username\":\"admin\"},\"type\":\"kubernetes.io/basic-auth\"}\n" - }, - "creationTimestamp": "2022-05-20T13:59:12Z", - "name": "jolokia-auth", - "namespace": "jfr", - "resourceVersion": "130", - "uid": "e1563217-ef08-481d-b2e3-233fa3040b56" - }, - "type": "kubernetes.io/basic-auth" -} diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-exec.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-exec.json deleted file mode 100644 index 5bd35a69f2..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-exec.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "request" : { - "mbean" : "java.lang:type=Memory", - "operation" : "gc()", - "type" : "exec" - }, - "status" : 200, - "timestamp" : 1658567003, - "value" : null -} diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-properties-secret.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-properties-secret.json deleted file mode 100644 index e12bbd16c1..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-properties-secret.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "apiVersion": "v1", - "data": { - "jolokia.properties": "aG9zdD0wLjAuMC4wCnBhc3N3b3JkPXNlY3JldAp1c2VyPWFkbWluCnBvcnQ9ODc3OApkaXNjb3ZlcnlFbmFibGVkPXRydWUKZGlzY292ZXJ5QWdlbnRVcmw9aHR0cDovLyR7aG9zdH06ODc3OC9qb2xva2lhLwpwb2xpY3lMb2NhdGlvbj1jbGFzc3BhdGg6L0JPT1QtSU5GL2NsYXNzZXMvam9sb2tpYS1hY2Nlc3MueG1s" - }, - "kind": "Secret", - "metadata": { - "creationTimestamp": "2022-05-20T13:59:12Z", - "name": "jolokia-properties", - "namespace": "jfr", - "resourceVersion": "147", - "uid": "025bf17a-cff5-46e6-8499-dd9d6de43fa9" - }, - "type": "Opaque" -} diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-version.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-version.json deleted file mode 100644 index 6299d0407a..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-version.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "request": { - "type": "version" - }, - "value": { - "agent": "1.7.1", - "protocol": "7.2", - "config": { - "listenForHttpService": "true", - "authIgnoreCerts": "false", - "agentId": "192.168.1.104-28660-5bd82fed-servlet", - "debug": "false", - "agentType": "servlet", - "policyLocation": "classpath:\/jolokia-access.xml", - "agentContext": "\/jolokia", - "serializeException": "false", - "mimeType": "text\/plain", - "dispatcherClasses": "org.jolokia.http.Jsr160ProxyNotEnabledByDefaultAnymoreDispatcher", - "authMode": "basic", - "authMatch": "any", - "streaming": "true", - "canonicalNaming": "true", - "historyMaxEntries": "10", - "allowErrorDetails": "false", - "allowDnsReverseLookup": "true", - "realm": "jolokia", - "includeStackTrace": "false", - "restrictorClass": "io.hawt.system.RBACRestrictor", - "mbeanQualifier": "qualifier=hawtio", - "useRestrictorService": "false", - "debugMaxEntries": "100" - }, - "info": { - "product": "jetty", - "vendor": "Eclipse", - "version": "9.4.z-SNAPSHOT" - } - }, - "timestamp": 1658556959, - "status": 200 -} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/memory-list.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/memory-list.json deleted file mode 100644 index 46d117f2e4..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/memory-list.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "request" : { - "path" : "java.lang/type=Memory", - "type" : "list" - }, - "status" : 200, - "timestamp" : 1658566168, - "value" : { - "attr" : { - "HeapMemoryUsage" : { - "desc" : "HeapMemoryUsage", - "rw" : false, - "type" : "javax.management.openmbean.CompositeData" - }, - "NonHeapMemoryUsage" : { - "desc" : "NonHeapMemoryUsage", - "rw" : false, - "type" : "javax.management.openmbean.CompositeData" - }, - "ObjectName" : { - "desc" : "ObjectName", - "rw" : false, - "type" : "javax.management.ObjectName" - }, - "ObjectPendingFinalizationCount" : { - "desc" : "ObjectPendingFinalizationCount", - "rw" : false, - "type" : "int" - }, - "Verbose" : { - "desc" : "Verbose", - "rw" : true, - "type" : "boolean" - } - }, - "class" : "sun.management.MemoryImpl", - "desc" : "Information on the management interface of the MBean", - "op" : { - "gc" : { - "args" : [], - "desc" : "gc", - "ret" : "void" - } - } - } -} diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/mock-kube-config.yml b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/mock-kube-config.yml deleted file mode 100644 index f222f471a7..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/mock-kube-config.yml +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: v1 -clusters: -- cluster: - server: {{request.baseUrl}}/ - name: test -contexts: -- context: - cluster: test - user: test - name: test -current-context: test -kind: Config -preferences: {} -users: -- name: test - user: - token: foobar diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/pod.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/pod.json deleted file mode 100644 index 77a61a3e32..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/pod.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "metadata": { - "clusterName": "test", - "labels": { - "jolokia": true - }, - "name": "pod-abcdef", - "namespace": "ns1", - "selfLink": "/api/v1/namespaces/ns1/pods/pod-abcdef" - } -} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/pods.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/pods.json deleted file mode 100644 index 114edd3640..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/pods.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "items": [ - { - "metadata": { - "clusterName": "test", - "labels": { - "jolokia": true - }, - "annotations": { - "jolokiaPort": 8778, - "jolokiaPath": "jolokia" - }, - "name": "pod-abcdef", - "namespace": "ns1", - "selfLink": "/api/v1/namespaces/ns1/pods/pod-abcdef" - } - } - ] -} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/runtime-list.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/runtime-list.json deleted file mode 100644 index c096d78c52..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/runtime-list.json +++ /dev/null @@ -1,104 +0,0 @@ -{ - "request" : { - "path" : "java.lang/type=Runtime", - "type" : "list" - }, - "status" : 200, - "timestamp" : 1658564737, - "value" : { - "attr" : { - "BootClassPath" : { - "desc" : "BootClassPath", - "rw" : false, - "type" : "java.lang.String" - }, - "BootClassPathSupported" : { - "desc" : "BootClassPathSupported", - "rw" : false, - "type" : "boolean" - }, - "ClassPath" : { - "desc" : "ClassPath", - "rw" : false, - "type" : "java.lang.String" - }, - "InputArguments" : { - "desc" : "InputArguments", - "rw" : false, - "type" : "[Ljava.lang.String;" - }, - "LibraryPath" : { - "desc" : "LibraryPath", - "rw" : false, - "type" : "java.lang.String" - }, - "ManagementSpecVersion" : { - "desc" : "ManagementSpecVersion", - "rw" : false, - "type" : "java.lang.String" - }, - "Name" : { - "desc" : "Name", - "rw" : false, - "type" : "java.lang.String" - }, - "ObjectName" : { - "desc" : "ObjectName", - "rw" : false, - "type" : "javax.management.ObjectName" - }, - "Pid" : { - "desc" : "Pid", - "rw" : false, - "type" : "long" - }, - "SpecName" : { - "desc" : "SpecName", - "rw" : false, - "type" : "java.lang.String" - }, - "SpecVendor" : { - "desc" : "SpecVendor", - "rw" : false, - "type" : "java.lang.String" - }, - "SpecVersion" : { - "desc" : "SpecVersion", - "rw" : false, - "type" : "java.lang.String" - }, - "StartTime" : { - "desc" : "StartTime", - "rw" : false, - "type" : "long" - }, - "SystemProperties" : { - "desc" : "SystemProperties", - "rw" : false, - "type" : "javax.management.openmbean.TabularData" - }, - "Uptime" : { - "desc" : "Uptime", - "rw" : false, - "type" : "long" - }, - "VmName" : { - "desc" : "VmName", - "rw" : false, - "type" : "java.lang.String" - }, - "VmVendor" : { - "desc" : "VmVendor", - "rw" : false, - "type" : "java.lang.String" - }, - "VmVersion" : { - "desc" : "VmVersion", - "rw" : false, - "type" : "java.lang.String" - } - }, - "class" : "sun.management.RuntimeImpl", - "desc" : "Information on the management interface of the MBean" - } -} diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/secrets.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/secrets.json deleted file mode 100644 index ac68f94b51..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/secrets.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "apiVersion": "v1", - "items": [ - { - "apiVersion": "v1", - "data": { - "password": "YWRtaW4=", - "username": "YWRtaW4=" - }, - "kind": "Secret", - "metadata": { - "annotations": { - "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Secret\",\"metadata\":{\"annotations\":{},\"name\":\"jolokia-auth\",\"namespace\":\"ns1\"},\"stringData\":{\"password\":\"admin\",\"username\":\"admin\"},\"type\":\"kubernetes.io/basic-auth\"}\n" - }, - "creationTimestamp": "2022-05-20T13:59:12Z", - "name": "jolokia-auth", - "namespace": "ns1", - "resourceVersion": "130", - "uid": "e1563217-ef08-481d-b2e3-233fa3040b56" - }, - "type": "kubernetes.io/basic-auth" - }, - { - "apiVersion": "v1", - "data": { - "jolokia.properties": "aG9zdD0wLjAuMC4wCnBhc3N3b3JkPXNlY3JldAp1c2VyPWFkbWluCnBvcnQ9ODc3OApkaXNjb3ZlcnlFbmFibGVkPXRydWUKZGlzY292ZXJ5QWdlbnRVcmw9aHR0cDovLyR7aG9zdH06ODc3OC9qb2xva2lhLwpwb2xpY3lMb2NhdGlvbj1jbGFzc3BhdGg6L0JPT1QtSU5GL2NsYXNzZXMvam9sb2tpYS1hY2Nlc3MueG1s" - }, - "kind": "Secret", - "metadata": { - "creationTimestamp": "2022-05-20T13:59:12Z", - "name": "jolokia-properties", - "namespace": "ns1", - "resourceVersion": "147", - "uid": "025bf17a-cff5-46e6-8499-dd9d6de43fa9" - }, - "type": "Opaque" - } - ], - "kind": "List", - "metadata": { - "resourceVersion": "" - } -} diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/system-attributes.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/system-attributes.json deleted file mode 100644 index dc2ff55433..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/system-attributes.json +++ /dev/null @@ -1,101 +0,0 @@ -[ - { - "request" : { - "attribute" : "Pid", - "mbean" : "java.lang:type=Runtime", - "type" : "read" - }, - "status" : 200, - "timestamp" : 1658586915, - "value" : 88774 - }, - { - "request" : { - "attribute" : "Name", - "mbean" : "java.lang:type=Runtime", - "type" : "read" - }, - "status" : 200, - "timestamp" : 1658586915, - "value" : "88774@Martins-MacBook-Pro-2.local" - }, - { - "request" : { - "attribute" : "InputArguments", - "mbean" : "java.lang:type=Runtime", - "type" : "read" - }, - "status" : 200, - "timestamp" : 1658586915, - "value" : [] - }, - { - "request" : { - "attribute" : "SystemProperties", - "mbean" : "java.lang:type=Runtime", - "type" : "read" - }, - "status" : 200, - "timestamp" : 1658586915, - "value" : { - "apple.awt.application.name" : "App", - "file.encoding" : "UTF-8", - "file.separator" : "/", - "ftp.nonProxyHosts" : "local|*.local|169.254/16|*.169.254/16", - "hawtio.authenticationEnabled" : "false", - "hawtio.redirect.scheme" : "http", - "hawtio.url" : "http://localhost:9090/hawtio", - "http.nonProxyHosts" : "local|*.local|169.254/16|*.169.254/16", - "java.class.path" : "/Users/marska/Downloads/hawtio-app-2.9.1.jar", - "java.class.version" : "62.0", - "java.home" : "/Library/Java/JavaVirtualMachines/temurin-18.jdk/Contents/Home", - "java.io.tmpdir" : "/var/folders/1f/cdm0073x1mj1swnhtw181_4m0000gn/T/", - "java.library.path" : "/Users/marska/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:.", - "java.runtime.name" : "OpenJDK Runtime Environment", - "java.runtime.version" : "18.0.1+10", - "java.specification.name" : "Java Platform API Specification", - "java.specification.vendor" : "Oracle Corporation", - "java.specification.version" : "18", - "java.vendor" : "Eclipse Adoptium", - "java.vendor.url" : "https://adoptium.net/", - "java.vendor.url.bug" : "https://github.com/adoptium/adoptium-support/issues", - "java.vendor.version" : "Temurin-18.0.1+10", - "java.version" : "18.0.1", - "java.version.date" : "2022-04-19", - "java.vm.compressedOopsMode" : "Zero based", - "java.vm.info" : "mixed mode, sharing", - "java.vm.name" : "OpenJDK 64-Bit Server VM", - "java.vm.specification.name" : "Java Virtual Machine Specification", - "java.vm.specification.vendor" : "Oracle Corporation", - "java.vm.specification.version" : "18", - "java.vm.vendor" : "Eclipse Adoptium", - "java.vm.version" : "18.0.1+10", - "jdk.debug" : "release", - "line.separator" : "\n", - "native.encoding" : "UTF-8", - "org.eclipse.jetty.util.log.class" : "org.eclipse.jetty.util.log.Slf4jLog", - "os.arch" : "x86_64", - "os.name" : "Mac OS X", - "os.version" : "11.6.6", - "path.separator" : ":", - "socksNonProxyHosts" : "local|*.local|169.254/16|*.169.254/16", - "sun.arch.data.model" : "64", - "sun.awt.enableExtraMouseButtons" : "true", - "sun.boot.library.path" : "/Library/Java/JavaVirtualMachines/temurin-18.jdk/Contents/Home/lib", - "sun.cpu.endian" : "little", - "sun.io.unicode.encoding" : "UnicodeBig", - "sun.java.command" : "/Users/marska/Downloads/hawtio-app-2.9.1.jar --port 9090", - "sun.java.launcher" : "SUN_STANDARD", - "sun.jnu.encoding" : "UTF-8", - "sun.management.compiler" : "HotSpot 64-Bit Tiered Compilers", - "sun.stderr.encoding" : "UTF-8", - "sun.stdout.encoding" : "UTF-8", - "user.country" : "NO", - "user.dir" : "/Users/marska/Documents/dev/jmc/jmc8/releng/third-party", - "user.home" : "/Users/marska", - "user.language" : "nb", - "user.name" : "marska", - "user.timezone" : "Asia/Taipei" - } - } -] diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-exec.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-exec.json deleted file mode 100644 index 6bb03ececf..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-exec.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "request": { - "method": "POST", - "urlPath": "/api/v1/namespaces/ns1/pods/pod-abcdef/proxy/jolokia", - "bodyPatterns": [ - { - "equalToJson": "{\"operation\":\"gc()\",\"mbean\":\"java.lang:type=Memory\",\"type\":\"EXEC\"}" - } - ] - }, - "response": { - "status": 200, - "bodyFileName": "jolokia-exec.json" - } -} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-list-memory.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-list-memory.json deleted file mode 100644 index e4170948cb..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-list-memory.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "request": { - "method": "POST", - "urlPathPattern": "/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*", - "bodyPatterns": [ - { - "equalToJson": "{\"path\":\"java.lang/type=Memory\",\"type\":\"LIST\"}" - } - ] - }, - "response": { - "status": 200, - "bodyFileName": "memory-list.json" - } -} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-list-runtime.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-list-runtime.json deleted file mode 100644 index f936e7eb0f..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-list-runtime.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "request": { - "method": "POST", - "urlPathPattern": "/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*", - "bodyPatterns": [ - { - "equalToJson": "{\"path\":\"java.lang/type=Runtime\",\"type\":\"LIST\"}" - } - ] - }, - "response": { - "status": 200, - "bodyFileName": "runtime-list.json" - } -} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-probe.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-probe.json deleted file mode 100644 index 5b26da423d..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-probe.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "request": { - "method": "POST", - "urlPathPattern": "/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*", - "bodyPatterns": [ - { - "equalToJson": "{\"type\":\"version\"}" - } - ] - }, - "response": { - "status": 200, - "bodyFileName": "jolokia-version.json" - } -} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-probe2.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-probe2.json deleted file mode 100644 index 98670016fd..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-probe2.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "request": { - "method": "POST", - "urlPathPattern": "/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*", - "bodyPatterns": [ - { - "equalToJson": "{\"type\":\"VERSION\"}" - } - ] - }, - "response": { - "status": 200, - "bodyFileName": "jolokia-version.json" - } -} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-read-attribute.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-read-attribute.json deleted file mode 100644 index 1584e3b4a3..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-read-attribute.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "request": { - "method": "POST", - "urlPathPattern": "/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*", - "bodyPatterns": [ - { - "equalToJson": "{\"attribute\":\"Verbose\",\"mbean\":\"java.lang:type=Memory\",\"type\":\"READ\"}" - } - ] - }, - "response": { - "status": 200, - "bodyFileName": "jolokia-attribute.json" - } -} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jvm-properties.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jvm-properties.json deleted file mode 100644 index 5b26da423d..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jvm-properties.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "request": { - "method": "POST", - "urlPathPattern": "/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*", - "bodyPatterns": [ - { - "equalToJson": "{\"type\":\"version\"}" - } - ] - }, - "response": { - "status": 200, - "bodyFileName": "jolokia-version.json" - } -} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/kube-config.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/kube-config.json deleted file mode 100644 index 418a887637..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/kube-config.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "request": { - "method": "GET", - "url": "/mock-kube-config.yml" - }, - "response": { - "status": 200, - "bodyFileName": "mock-kube-config.yml", - "transformers": [ - "response-template" - ] - } -} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/pod.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/pod.json deleted file mode 100644 index ebcbd7c5e3..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/pod.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "request": { - "method": "GET", - "url": "/api/v1/namespaces/ns1/pods/pod-abcdef" - }, - "response": { - "status": 200, - "bodyFileName": "pod.json" - } -} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/pods.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/pods.json deleted file mode 100644 index 061fb0f53b..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/pods.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "request": { - "method": "GET", - "urlPath": "/api/v1/pods" - }, - "response": { - "status": 200, - "bodyFileName": "pods.json" - } -} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/secrets.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/secrets.json deleted file mode 100644 index 113bb91e1f..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/secrets.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "request": { - "method": "GET", - "url": "/api/v1/namespaces/ns1/secrets" - }, - "response": { - "status": 200, - "bodyFileName": "secrets.json" - } -} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/system-attributes.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/system-attributes.json deleted file mode 100644 index e3d1147b2f..0000000000 --- a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/system-attributes.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "request": { - "method": "POST", - "urlPathPattern": "/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*", - "bodyPatterns": [ - { - "equalToJson": "[{\"attribute\":\"Pid\",\"mbean\":\"java.lang:type=Runtime\",\"type\":\"READ\"},{\"attribute\":\"Name\",\"mbean\":\"java.lang:type=Runtime\",\"type\":\"READ\"},{\"attribute\":\"InputArguments\",\"mbean\":\"java.lang:type=Runtime\",\"type\":\"READ\"},{\"attribute\":\"SystemProperties\",\"mbean\":\"java.lang:type=Runtime\",\"type\":\"READ\"}]" - } - ] - }, - "response": { - "status": 200, - "bodyFileName": "system-attributes.json" - } -} \ No newline at end of file diff --git a/application/uitests/pom.xml b/application/uitests/pom.xml index f6496c214d..98327fd9a8 100644 --- a/application/uitests/pom.xml +++ b/application/uitests/pom.xml @@ -1,6 +1,6 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/application/org.openjdk.jmc.kubernetes/pom.xml b/application/org.openjdk.jmc.kubernetes/pom.xml new file mode 100644 index 0000000000..8593216ed0 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/pom.xml @@ -0,0 +1,49 @@ + + + + 4.0.0 + + org.openjdk.jmc + missioncontrol.application + ${revision}${changelist} + + org.openjdk.jmc.kubernetes + eclipse-plugin + + ${project.basedir}/../../configuration + + diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnection.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnection.java new file mode 100644 index 0000000000..6aae451116 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnection.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Kantega AS. All rights reserved. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The contents of this file are subject to the terms of either the Universal Permissive License + * v 1.0 as shown at http://oss.oracle.com/licenses/upl + * + * or the following license: + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.openjdk.jmc.kubernetes; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; +import java.util.regex.Pattern; + +import javax.management.InstanceNotFoundException; + +import org.jolokia.client.J4pClient; +import org.jolokia.client.exception.J4pException; +import org.jolokia.client.exception.J4pRemoteException; +import org.jolokia.client.request.J4pResponse; +import org.openjdk.jmc.jolokia.JmcJolokiaJmxConnection; +import org.openjdk.jmc.rjmx.common.ConnectionException; + +/** + * Jolokia based MBeanServerConnector tailored for JMC needs + */ +public class JmcKubernetesJmxConnection extends JmcJolokiaJmxConnection { + + static final Collection DISCONNECT_SIGNS = Arrays.asList(Pattern.compile("Error: pods \".+\" not found")); //$NON-NLS-1$ + + public JmcKubernetesJmxConnection(J4pClient client) throws IOException { + super(client); + } + + @SuppressWarnings("rawtypes") + @Override + protected J4pResponse unwrapException(J4pException e) throws IOException, InstanceNotFoundException { + // recognize signs of disconnect and signal to the application for better + // handling + if (isKnownDisconnectException(e)) { + throw new ConnectionException(e.getMessage()); + } else { + return super.unwrapException(e); + } + } + + private boolean isKnownDisconnectException(J4pException e) { + if (!(e instanceof J4pRemoteException)) { + return false; + } + if (!"io.fabric8.kubernetes.client.KubernetesClientException".equals(((J4pRemoteException) e).getErrorType())) { //$NON-NLS-1$ + return false; + } + return DISCONNECT_SIGNS.stream().anyMatch(pattern -> pattern.matcher(e.getMessage()).matches()); + } + +} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnectionProvider.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnectionProvider.java new file mode 100644 index 0000000000..b74b999f68 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnectionProvider.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Kantega AS. All rights reserved. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The contents of this file are subject to the terms of either the Universal Permissive License + * v 1.0 as shown at http://oss.oracle.com/licenses/upl + * + * or the following license: + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.openjdk.jmc.kubernetes; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.util.Map; + +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorProvider; +import javax.management.remote.JMXServiceURL; + +/** + * This {@code JMXConnectionProvider} handles the "kubernetes" protocol. + */ +public class JmcKubernetesJmxConnectionProvider implements JMXConnectorProvider { + @Override + public JMXConnector newJMXConnector(JMXServiceURL serviceURL, Map environment) throws IOException { + if (!"kubernetes".equals(serviceURL.getProtocol())) { //$NON-NLS-1$ + throw new MalformedURLException("I only serve Kubernetes connections"); //$NON-NLS-1$ + } + return new JmcKubernetesJmxConnector(serviceURL, environment); + } +} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnector.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnector.java new file mode 100644 index 0000000000..0d1d98264a --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnector.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Kantega AS. All rights reserved. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The contents of this file are subject to the terms of either the Universal Permissive License + * v 1.0 as shown at http://oss.oracle.com/licenses/upl + * + * or the following license: + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.openjdk.jmc.kubernetes; + +import java.io.IOException; +import java.util.Map; + +import javax.management.remote.JMXServiceURL; + +import org.jolokia.client.J4pClient; +import org.jolokia.client.jmxadapter.RemoteJmxAdapter; +import org.jolokia.kubernetes.client.KubernetesJmxConnector; + +public class JmcKubernetesJmxConnector extends KubernetesJmxConnector { + + public JmcKubernetesJmxConnector(JMXServiceURL serviceURL, Map environment) { + super(serviceURL, environment); + } + + @Override + protected RemoteJmxAdapter createAdapter(J4pClient client) throws IOException { + return new JmcKubernetesJmxConnection(client); + } +} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesPlugin.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesPlugin.java new file mode 100644 index 0000000000..e7ff7e21b0 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesPlugin.java @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Kantega AS. All rights reserved. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The contents of this file are subject to the terms of either the Universal Permissive License + * v 1.0 as shown at http://oss.oracle.com/licenses/upl + * + * or the following license: + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.openjdk.jmc.kubernetes; + +import org.eclipse.core.runtime.Platform; +import org.openjdk.jmc.kubernetes.preferences.KubernetesScanningParameters; +import org.openjdk.jmc.kubernetes.preferences.PreferenceConstants; +import org.openjdk.jmc.ui.MCAbstractUIPlugin; +import org.openjdk.jmc.common.security.ICredentials; +import org.openjdk.jmc.common.security.PersistentCredentials; +import org.openjdk.jmc.common.security.SecurityException; +import org.openjdk.jmc.common.security.SecurityManagerFactory; +import org.openjdk.jmc.ui.misc.DisplayToolkit; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; + +public class JmcKubernetesPlugin extends MCAbstractUIPlugin + implements KubernetesScanningParameters, PreferenceConstants { + + public final static String PLUGIN_ID = "org.openjdk.jmc.kubernetes"; //$NON-NLS-1$ + + // The shared instance. + private static JmcKubernetesPlugin plugin; + + /** + * The constructor. + */ + public JmcKubernetesPlugin() { + super(PLUGIN_ID); + plugin = this; + } + + /** + * @return the shared instance. + */ + public static JmcKubernetesPlugin getDefault() { + return plugin; + } + + private void ensureNeededCredentialsAreUnlocked() { + if (getScanningCredentials() != null && SecurityManagerFactory.getSecurityManager().isLocked()) { + DisplayToolkit.safeAsyncExec(() -> { + try { + SecurityManagerFactory.getSecurityManager().unlock(); + } catch (SecurityException e) { + logError("Error unlocking credentials needed for kubernetes scanning", e);//$NON-NLS-1$ + } + }); + } + } + + @Override + public boolean scanForInstances() { + // If credentials are locked and credentials are required, the scanner thread + // will get hung + // therefore await credentials store to be unlocked before proceeding to scan + return getPreferenceStore().getBoolean(P_SCAN_FOR_INSTANCES) + && (getScanningCredentials() == null || !SecurityManagerFactory.getSecurityManager().isLocked()); + + } + + @Override + public boolean scanAllContexts() { + return getPreferenceStore().getBoolean(P_SCAN_ALL_CONTEXTS); + } + + @Override + public String jolokiaPort() { + return getPreferenceStore().getString(P_JOLOKIA_PORT); + } + + private PersistentCredentials getScanningCredentials() { + String key = getPreferenceStore().getString(P_CREDENTIALS_KEY); + return key == null ? null : new PersistentCredentials(key); + } + + public ICredentials storeCredentials(String username, String password) throws SecurityException { + PersistentCredentials credentials = new PersistentCredentials(username, password, "kubernetes");//$NON-NLS-1$ + getPreferenceStore().setValue(P_CREDENTIALS_KEY, credentials.getExportedId()); + return credentials; + } + + @Override + public String username() throws SecurityException { + final PersistentCredentials cred = getScanningCredentials(); + if (cred == null) { + return "";//$NON-NLS-1$ + } else { + return cred.getUsername(); + } + } + + @Override + public String password() throws SecurityException { + final PersistentCredentials cred = getScanningCredentials(); + if (cred == null) { + return "";//$NON-NLS-1$ + } else { + return cred.getPassword(); + } + } + + @Override + public String jolokiaPath() { + return getPreferenceStore().getString(P_JOLOKIA_PATH); + } + + @Override + public String requireLabel() { + return getPreferenceStore().getString(P_REQUIRE_LABEL); + } + + @Override + public String jolokiaProtocol() { + return getPreferenceStore().getString(P_JOLOKIA_PROTOCOL); + } + + @Override + public void logError(String message, Throwable error) { + if (getPreferenceStore().getBoolean(P_LOG_ERRORS)) { + Platform.getLog(FrameworkUtil.getBundle(getClass())).error(message, error); + } + } + + @Override + public void start(BundleContext context) throws Exception { + super.start(context); + this.ensureNeededCredentialsAreUnlocked(); + } + +} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/KubernetesDiscoveryListener.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/KubernetesDiscoveryListener.java new file mode 100644 index 0000000000..bcb2420ac3 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/KubernetesDiscoveryListener.java @@ -0,0 +1,340 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Kantega AS. All rights reserved. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The contents of this file are subject to the terms of either the Universal Permissive License + * v 1.0 as shown at http://oss.oracle.com/licenses/upl + * + * or the following license: + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.openjdk.jmc.kubernetes; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.NoSuchElementException; +import java.util.Properties; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXServiceURL; + +import org.apache.commons.codec.binary.Base64; +import org.jolokia.client.J4pClient; +import org.jolokia.kubernetes.client.KubernetesJmxConnector; +import org.jolokia.server.core.osgi.security.AuthorizationHeaderParser; +import org.jolokia.server.core.util.Base64Util; +import org.openjdk.jmc.jolokia.AbstractCachedDescriptorProvider; +import org.openjdk.jmc.jolokia.JolokiaAgentDescriptor; +import org.openjdk.jmc.jolokia.ServerConnectionDescriptor; +import org.openjdk.jmc.kubernetes.preferences.KubernetesScanningParameters; +import org.openjdk.jmc.common.jvm.JVMDescriptor; +import org.openjdk.jmc.common.security.SecurityException; + +import io.fabric8.kubernetes.api.model.NamedContext; +import io.fabric8.kubernetes.api.model.ObjectMeta; +import io.fabric8.kubernetes.api.model.Pod; +import io.fabric8.kubernetes.api.model.PodList; +import io.fabric8.kubernetes.api.model.Secret; +import io.fabric8.kubernetes.client.Config; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.internal.KubeConfigUtils; +import io.fabric8.kubernetes.client.utils.Utils; +import io.fabric8.kubernetes.client.dsl.AnyNamespaceOperation; +import io.fabric8.kubernetes.client.dsl.PodResource; + +/** + * This class attempts to connect to JVMs in pods running in kubernetes in a background thread. + * Enablement and parameters for the scanning is given by user preferences. + */ +public class KubernetesDiscoveryListener extends AbstractCachedDescriptorProvider { + + private final static Pattern SECRET_PATTERN = Pattern + .compile("\\$\\{kubernetes/secret/(?[^/]+)/(?[^\\}]+)}"); //$NON-NLS-1$ + private final static Pattern ATTRIBUTE_PATTERN = Pattern + .compile("\\$\\{kubernetes/annotation/(?[^/]+)}"); //$NON-NLS-1$ + private final static Set VALID_JOLOKIA_PROTOCOLS = new HashSet<>(Arrays.asList("http", "https")); //$NON-NLS-1$ //$NON-NLS-2$ + + KubernetesScanningParameters settings; + + public KubernetesDiscoveryListener() { + this(JmcKubernetesPlugin.getDefault()); + } + + //Public constructor in order for test plugin to be able to rig tests in an easier manner + public KubernetesDiscoveryListener(KubernetesScanningParameters parameters) { + this.settings = parameters; + } + + public final String getDescription() { + return Messages.KubernetesDiscoveryListener_Description; + } + + @Override + public String getName() { + return "kubernetes"; //$NON-NLS-1$ + } + + boolean notEmpty(String value) { + return value != null && value.length() > 0; + } + + private List contexts; + private long contextsCached = 0L; + + private List allContexts() throws IOException { + final String path = Utils.getSystemPropertyOrEnvVar(Config.KUBERNETES_KUBECONFIG_FILE, + new File(System.getProperty("user.home"), ".kube" + File.separator + "config").toString()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + File configPath = new File(path); + if (contexts != null && contextsCached > configPath.lastModified()) {// the YAML parsing is soo incredibly + // sloow, hence cache context names for + // later + // runs + return contexts; + } + // reload config if kubeconfig has been modified since we cached the config + io.fabric8.kubernetes.api.model.Config config = KubeConfigUtils.parseConfig(configPath); + this.contextsCached = System.currentTimeMillis(); + KubernetesJmxConnector.resetKubernetesConfig(); + return contexts = config.getContexts().stream().map(NamedContext::getName).collect(Collectors.toList()); + } + + @Override + protected Map discoverJvms() { + return scanForJvms(); + } + + Map scanForJvms() { + Map found = new HashMap<>(); + if (!isEnabled()) { + return found; + } + boolean hasScanned = false; + + if (settings.scanAllContexts()) { + try { + for (final String context : allContexts()) { + hasScanned = true; + scanContext(found, settings, context); + } + } catch (IOException e) { + settings.logError(Messages.KubernetesDiscoveryListener_UnableToFindContexts, e); + } + } + if (!hasScanned) {// scan default context + return scanContext(found, settings, null); + } + return found; + } + + private Map scanContext( + Map found, KubernetesScanningParameters parameters, String context) { + try { + scanContextUnsafe(found, parameters, context); + } catch (Exception e) { + parameters.logError(Messages.KubernetesDiscoveryListener_UnableToScan + context, e); + } + return found; + } + + private Map scanContextUnsafe( + Map found, KubernetesScanningParameters parameters, String context) { + String pathLabel = parameters.requireLabel(); + KubernetesClient client = KubernetesJmxConnector.getApiClient(context); + + AnyNamespaceOperation query = client.pods().inAnyNamespace(); + List podList; + boolean hasPathLabel = notEmpty(pathLabel); + if (hasPathLabel) { + podList = query.withLabel(pathLabel).list().getItems(); + } else { + podList = query.list().getItems(); + } + // can consider parallelization for big contexts, however since it is the + // background await the situation a bit + podList.stream().forEach(pod -> scanPod(found, parameters, context, client, pod)); + return found; + } + + private void scanPod( + Map found, KubernetesScanningParameters parameters, String context, + KubernetesClient client, Pod pod) { + + final ObjectMeta metadata = pod.getMetadata(); + HashMap headers = new HashMap<>(); + Map env = new HashMap<>(); + try { + if (notEmpty(parameters.username())) { + if (!notEmpty(parameters.password())) { + throw new IllegalArgumentException(Messages.KubernetesDiscoveryListener_MustProvidePassword); + } + authorize(headers, client, parameters.username(), parameters.password(), metadata.getNamespace(), env); + } + } catch (SecurityException e) { + // skipping authorization if anything fails + } + final StringBuilder url = new StringBuilder("/api/").append(pod.getApiVersion()).append("/namespaces/") + .append(metadata.getNamespace()).append("/pods/"); + // JMX url must be reverse constructed, so that we can connect from the + // resulting node in the JVM browser + final StringBuilder jmxUrl = new StringBuilder("service:jmx:kubernetes:///").append(metadata.getNamespace()) //$NON-NLS-1$ + .append('/'); + + final String protocol = getValueOrAttribute(parameters.jolokiaProtocol(), metadata); + final String podName = metadata.getName(); + if (notEmpty(protocol)) { + if (!VALID_JOLOKIA_PROTOCOLS.contains(protocol)) { + throw new IllegalArgumentException(Messages.KubernetesDiscoveryListener_JolokiaProtocol + protocol + + Messages.KubernetesDiscoveryListener_HttpOrHttps); + } + // a bit clumsy, need to inject protocol _before_ podname in selflink + url.append(protocol).append(':'); + jmxUrl.append(protocol).append(':'); + } + + jmxUrl.append(podName); + url.append(podName); + + final String port = getValueOrAttribute(parameters.jolokiaPort(), metadata); + if (port != null) { + url.append(":").append(port); //$NON-NLS-1$ + jmxUrl.append(':').append(port); + } + + url.append("/proxy"); //$NON-NLS-1$ + + final String path = getValueOrAttribute(parameters.jolokiaPath(), metadata); + + if (!path.startsWith("/")) { //$NON-NLS-1$ + url.append('/'); + jmxUrl.append('/'); + } + url.append(path); + jmxUrl.append(path); + + if (context != null) { + env.put(KubernetesJmxConnector.KUBERNETES_CLIENT_CONTEXT, context); + } + J4pClient jvmClient = KubernetesJmxConnector.probeProxyPath(env, client, url, headers); + if (jvmClient != null) { + JmcKubernetesJmxConnection connection; + try { + connection = new JmcKubernetesJmxConnection(jvmClient); + JVMDescriptor jvmDescriptor = JolokiaAgentDescriptor.attemptToGetJvmInfo(connection); + JMXServiceURL jmxServiceURL = new JMXServiceURL(jmxUrl.toString()); + KubernetesJvmDescriptor descriptor = new KubernetesJvmDescriptor(metadata, jvmDescriptor, jmxServiceURL, + env); + found.put(descriptor.getGUID(), descriptor); + } catch (IOException e) { + parameters.logError(Messages.KubernetesDiscoveryListener_ErrConnectingToJvm, e); + + } + } + } + + private String getValueOrAttribute(String configValue, ObjectMeta metadata) { + if (notEmpty(configValue)) { + Matcher pattern = ATTRIBUTE_PATTERN.matcher(configValue); + if (pattern.find()) { + return metadata.getAnnotations().get(pattern.group("annotationName")); //$NON-NLS-1$ + } else { + return configValue;// the default is to use config value as is + } + } + return null; + } + + private void authorize( + HashMap headers, KubernetesClient client, String username, String password, String namespace, + Map jmxEnv) { + + final Matcher userNameMatcher = SECRET_PATTERN.matcher(username); + String secretName = null; + Map secretValues = null; + if (userNameMatcher.find()) { + secretName = userNameMatcher.group("secretName"); //$NON-NLS-1$ + secretValues = findSecret(client, namespace, secretName); + username = secretValues.get(userNameMatcher.group("itemName")); //$NON-NLS-1$ + } + + final Matcher passwordMatcher = SECRET_PATTERN.matcher(password); + if (passwordMatcher.find()) { + if (!secretName.equals(passwordMatcher.group("secretName"))) { //$NON-NLS-1$ + secretValues = findSecret(client, namespace, passwordMatcher.group("secretName")); //$NON-NLS-1$ + } + password = secretValues.get(passwordMatcher.group("itemName")); //$NON-NLS-1$ + } + + headers.put(AuthorizationHeaderParser.JOLOKIA_ALTERNATE_AUTHORIZATION_HEADER, + "Basic " + Base64Util.encode((username + ":" + password).getBytes())); //$NON-NLS-1$ //$NON-NLS-2$ + jmxEnv.put(JMXConnector.CREDENTIALS, new String[] {username, password}); + + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + private Map findSecret(KubernetesClient client, String namespace, String secretName) { + + for (Secret secret : client.secrets().inNamespace(namespace).list().getItems()) { + if (secret.getMetadata().getName().equals(secretName)) { + if ("kubernetes.io/basic-auth".equals(secret.getType())) { //$NON-NLS-1$ + Map data = secret.getData(); + data.replaceAll((key, value) -> new String(Base64.decodeBase64(value))); + return data; + } else if ("Opaque".equals(secret.getType())) { //$NON-NLS-1$ + for (Entry entry : secret.getData().entrySet()) { + if (entry.getKey().endsWith(".properties")) { //$NON-NLS-1$ + try { + Properties properties = new Properties(); + properties.load(new ByteArrayInputStream(Base64.decodeBase64(entry.getValue()))); + return (Map) properties; + } catch (IOException ignore) { + } + } + } + } + } + + } + throw new NoSuchElementException(Messages.KubernetesDiscoveryListener_CouldNotFindSecret + secretName + + Messages.KubernetesDiscoveryListener_InNamespace + namespace); + + } + + @Override + protected boolean isEnabled() { + return this.settings.scanForInstances(); + } +} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/KubernetesJvmDescriptor.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/KubernetesJvmDescriptor.java new file mode 100644 index 0000000000..a646a0fcc7 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/KubernetesJvmDescriptor.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Kantega AS. All rights reserved. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The contents of this file are subject to the terms of either the Universal Permissive License + * v 1.0 as shown at http://oss.oracle.com/licenses/upl + * + * or the following license: + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.openjdk.jmc.kubernetes; + +import java.io.IOException; +import java.util.Map; + +import javax.management.remote.JMXServiceURL; + +import org.jolokia.kubernetes.client.KubernetesJmxConnector; +import org.openjdk.jmc.jolokia.ServerConnectionDescriptor; +import org.openjdk.jmc.common.jvm.JVMDescriptor; + +import io.fabric8.kubernetes.api.model.ObjectMeta; + +public class KubernetesJvmDescriptor implements ServerConnectionDescriptor { + + private final JVMDescriptor jvmDescriptor; + private final ObjectMeta metadata; + private final Map env; + private final JMXServiceURL connectUrl; + + public KubernetesJvmDescriptor(ObjectMeta metadata, JVMDescriptor jvmDescriptor, JMXServiceURL connectUrl, + Map env) { + this.jvmDescriptor = jvmDescriptor; + this.metadata = metadata; + this.env = env; + this.connectUrl = connectUrl; + } + + @Override + public String getGUID() { + return this.metadata.getName(); + } + + @Override + public String getDisplayName() { + return this.metadata.getName(); + } + + @Override + public JVMDescriptor getJvmInfo() { + return this.jvmDescriptor; + } + + public String getPath() { + String namespace = metadata.getNamespace(); + final Object context = this.env.get(KubernetesJmxConnector.KUBERNETES_CLIENT_CONTEXT); + if (context != null) { + return context + "/" + namespace; //$NON-NLS-1$ + } + return namespace; + } + + @Override + public JMXServiceURL createJMXServiceURL() throws IOException { + return this.connectUrl; + } + + @Override + public Map getEnvironment() { + return this.env; + } + + @Override + public JMXServiceURL serviceUrl() { + return this.connectUrl; + } + +} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/Messages.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/Messages.java new file mode 100644 index 0000000000..9dcff25a26 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/Messages.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Kantega AS. All rights reserved. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The contents of this file are subject to the terms of either the Universal Permissive License + * v 1.0 as shown at http://oss.oracle.com/licenses/upl + * + * or the following license: + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.openjdk.jmc.kubernetes; + +import org.eclipse.osgi.util.NLS; + +public class Messages extends NLS { + private static final String BUNDLE_NAME = "org.openjdk.jmc.kubernetes.messages"; //$NON-NLS-1$ + public static String KubernetesDiscoveryListener_CouldNotFindSecret; + public static String KubernetesDiscoveryListener_Description; + public static String KubernetesDiscoveryListener_ErrConnectingToJvm; + public static String KubernetesDiscoveryListener_HttpOrHttps; + public static String KubernetesDiscoveryListener_InNamespace; + public static String KubernetesDiscoveryListener_JolokiaProtocol; + public static String KubernetesDiscoveryListener_MustProvidePassword; + public static String KubernetesDiscoveryListener_UnableToFindContexts; + public static String KubernetesDiscoveryListener_UnableToScan; + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + private Messages() { + } +} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/messages.properties b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/messages.properties new file mode 100644 index 0000000000..ea0166360a --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/messages.properties @@ -0,0 +1,9 @@ +KubernetesDiscoveryListener_CouldNotFindSecret=Could not find secret named +KubernetesDiscoveryListener_Description=List JVM in kubernetes cluster +KubernetesDiscoveryListener_ErrConnectingToJvm=Error connecting to JVM in pod +KubernetesDiscoveryListener_HttpOrHttps=' must be either 'http' or 'https' +KubernetesDiscoveryListener_InNamespace=\ in namespace +KubernetesDiscoveryListener_JolokiaProtocol=Jolokia protocol ' +KubernetesDiscoveryListener_MustProvidePassword=Password must be specified when username is specified +KubernetesDiscoveryListener_UnableToFindContexts=Unable to find all kubernetes contexts +KubernetesDiscoveryListener_UnableToScan=Unable to scan kubernetes context diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/JmcKubernetesPreferenceForm.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/JmcKubernetesPreferenceForm.java new file mode 100644 index 0000000000..876a71d128 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/JmcKubernetesPreferenceForm.java @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Kantega AS. All rights reserved. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The contents of this file are subject to the terms of either the Universal Permissive License + * v 1.0 as shown at http://oss.oracle.com/licenses/upl + * + * or the following license: + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.openjdk.jmc.kubernetes.preferences; + +import java.util.Map; +import java.util.WeakHashMap; +import java.util.logging.Level; + +import org.eclipse.jface.preference.BooleanFieldEditor; +import org.eclipse.jface.preference.FieldEditor; +import org.eclipse.jface.preference.FieldEditorPreferencePage; +import org.eclipse.jface.preference.StringFieldEditor; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPreferencePage; +import org.openjdk.jmc.common.security.CredentialsNotAvailableException; +import org.openjdk.jmc.common.security.SecurityException; +import org.openjdk.jmc.kubernetes.JmcKubernetesPlugin; + +/** + * This class represents a preference page that is contributed to the Preferences dialog. By + * subclassing FieldEditorPreferencePage, we can use the field support built into JFace + * that allows us to create a page that is small and knows how to save, restore and apply itself. + *

+ * This page is used to modify preferences only. They are stored in the preference store that + * belongs to the main plug-in class. That way, preferences can be accessed directly via the + * preference store. + */ +public class JmcKubernetesPreferenceForm extends FieldEditorPreferencePage + implements IWorkbenchPreferencePage, PreferenceConstants { + + private Map dependantControls = new WeakHashMap<>(); + private Text userField; + private Text passwordField; + private boolean credentialsDirty; + + public JmcKubernetesPreferenceForm() { + super(GRID); + setPreferenceStore(JmcKubernetesPlugin.getDefault().getPreferenceStore()); + setDescription(Messages.JmcKubernetesPreferenceForm_FormDescription); + } + + /** + * Creates the field editors. Field editors are abstractions of the common GUI blocks needed to + * manipulate various types of preferences. Each field editor knows how to save and restore + * itself. + */ + public void createFieldEditors() { + BooleanFieldEditor mainEnabler = new BooleanFieldEditor(P_SCAN_FOR_INSTANCES, + Messages.JmcKubernetesPreferenceForm_ScanForPods, getFieldEditorParent()) { + @Override + protected void valueChanged(boolean oldValue, boolean newValue) { + super.valueChanged(oldValue, newValue); + enableDependantFields(newValue); + } + }; + addField(mainEnabler); + + final BooleanFieldEditor scanContextsEditor = new BooleanFieldEditor(P_SCAN_ALL_CONTEXTS, + Messages.JmcKubernetesPreferenceForm_AllContexts, getFieldEditorParent()); + this.addDependantField(scanContextsEditor, scanContextsEditor.getDescriptionControl(getFieldEditorParent())); + final BooleanFieldEditor logErrors = new BooleanFieldEditor(P_LOG_ERRORS, + Messages.JmcKubernetesPreferenceForm_LogErrorsLabel, getFieldEditorParent()); + this.addDependantField(logErrors, logErrors.getDescriptionControl(getFieldEditorParent())); + this.addTextField(new StringFieldEditor(P_REQUIRE_LABEL, Messages.JmcKubernetesPreferenceForm_RequireLabel, + getFieldEditorParent()), Messages.JmcKubernetesPreferenceForm_LabelToolTip); + this.addTextField(new StringFieldEditor(P_JOLOKIA_PATH, Messages.JmcKubernetesPreferenceForm_PathLabel, + getFieldEditorParent()), Messages.JmcKubernetesPreferenceForm_PathTooltip); + this.addTextField(new StringFieldEditor(P_JOLOKIA_PORT, Messages.JmcKubernetesPreferenceForm_PortLabel, + getFieldEditorParent()), Messages.JmcKubernetesPreferenceForm_PortTooltip); + this.addTextField(new StringFieldEditor(P_JOLOKIA_PROTOCOL, Messages.JmcKubernetesPreferenceForm_ProtocolLabel, + getFieldEditorParent()), Messages.JmcKubernetesPreferenceForm_ProtocolTooltip); + createCredentialFields(); + // set initial enablement + enableDependantFields(JmcKubernetesPlugin.getDefault().scanForInstances()); + + } + + private void createCredentialFields() { + Label userLabel = new Label(getFieldEditorParent(), SWT.NONE); + userLabel.setText(Messages.JmcKubernetesPreferenceForm_UsernameLabel); + userLabel.setLayoutData(new GridData()); + this.userField = new Text(getFieldEditorParent(), SWT.SINGLE | SWT.BORDER); + userField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + userField.setToolTipText(Messages.JmcKubernetesPreferenceForm_UsernameTooltip); + this.dependantControls.put(userField, null); + + Label passLabel = new Label(getFieldEditorParent(), SWT.NONE); + passLabel.setText(Messages.JmcKubernetesPreferenceForm_PasswordLabel); + passLabel.setLayoutData(new GridData()); + this.passwordField = new Text(getFieldEditorParent(), SWT.PASSWORD | SWT.SINGLE | SWT.BORDER); + passwordField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + this.dependantControls.put(passwordField, null); + + JmcKubernetesPlugin plugin = JmcKubernetesPlugin.getDefault(); + + try { + userField.setText(plugin.username()); + passwordField.setText(plugin.password()); + } catch (CredentialsNotAvailableException ignore) { + // ignore if credentials are not stored + } catch (SecurityException e) { + plugin.getLogger().log(Level.WARNING, "Could not load kubernetes credentials", e); //$NON-NLS-1$ + } + + ModifyListener markCredentials = e -> credentialsDirty = true; + this.userField.addModifyListener(markCredentials); + this.passwordField.addModifyListener(markCredentials); + } + + private void addTextField(StringFieldEditor field, String tooltip) { + Text textControl = field.getTextControl(getFieldEditorParent()); + this.addDependantField(field, textControl); + textControl.setToolTipText(tooltip); + field.getLabelControl(getFieldEditorParent()).setToolTipText(tooltip); + + } + + private void addDependantField(FieldEditor field, Control control) { + this.dependantControls.put(control, null); + addField(field); + } + + private void enableDependantFields(boolean enabled) { + for (Control field : this.dependantControls.keySet()) { + field.setEnabled(enabled); + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.IWorkbenchPreferencePage#init(org.eclipse.ui.IWorkbench) + */ + public void init(IWorkbench workbench) { + } + + @Override + public boolean performOk() { + updateCredentialsIfApplicable(); + return super.performOk(); + } + + private void updateCredentialsIfApplicable() { + if (this.credentialsDirty) { + try { + JmcKubernetesPlugin.getDefault().storeCredentials(userField.getText(), passwordField.getText()); + this.credentialsDirty = false; + } catch (SecurityException ex) { + JmcKubernetesPlugin.getDefault().getLogger().log(Level.WARNING, + "Could not store kubernetes credentials", ex); //$NON-NLS-1$ + } + } + } + +} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/KubernetesScanningParameters.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/KubernetesScanningParameters.java new file mode 100644 index 0000000000..a67697d42b --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/KubernetesScanningParameters.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Kantega AS. All rights reserved. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The contents of this file are subject to the terms of either the Universal Permissive License + * v 1.0 as shown at http://oss.oracle.com/licenses/upl + * + * or the following license: + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.openjdk.jmc.kubernetes.preferences; + +import org.openjdk.jmc.common.security.ICredentials; +import org.openjdk.jmc.common.security.SecurityException; + +public interface KubernetesScanningParameters { + boolean scanForInstances(); + + boolean scanAllContexts(); + + String jolokiaPort(); + + String username() throws SecurityException; + + String password() throws SecurityException; + + String jolokiaPath(); + + String jolokiaProtocol(); + + String requireLabel(); + + ICredentials storeCredentials(String username, String password) throws SecurityException; + + void logError(String message, Throwable error); +} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/Messages.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/Messages.java new file mode 100644 index 0000000000..63bfc50efa --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/Messages.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Kantega AS. All rights reserved. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The contents of this file are subject to the terms of either the Universal Permissive License + * v 1.0 as shown at http://oss.oracle.com/licenses/upl + * + * or the following license: + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.openjdk.jmc.kubernetes.preferences; + +import org.eclipse.osgi.util.NLS; + +public class Messages extends NLS { + private static final String BUNDLE_NAME = "org.openjdk.jmc.kubernetes.preferences.messages"; //$NON-NLS-1$ + public static String JmcKubernetesPreferenceForm_AllContexts; + public static String JmcKubernetesPreferenceForm_FormDescription; + public static String JmcKubernetesPreferenceForm_LabelToolTip; + public static String JmcKubernetesPreferenceForm_LogErrorsLabel; + public static String JmcKubernetesPreferenceForm_LogErrorsTooltip; + public static String JmcKubernetesPreferenceForm_PasswordLabel; + public static String JmcKubernetesPreferenceForm_PasswordTooltip; + public static String JmcKubernetesPreferenceForm_PathLabel; + public static String JmcKubernetesPreferenceForm_PathTooltip; + public static String JmcKubernetesPreferenceForm_PortLabel; + public static String JmcKubernetesPreferenceForm_PortTooltip; + public static String JmcKubernetesPreferenceForm_ProtocolLabel; + public static String JmcKubernetesPreferenceForm_ProtocolTooltip; + public static String JmcKubernetesPreferenceForm_RequireLabel; + public static String JmcKubernetesPreferenceForm_ScanForPods; + public static String JmcKubernetesPreferenceForm_UsernameTooltip; + public static String JmcKubernetesPreferenceForm_UsernameLabel; + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + private Messages() { + } +} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/PreferenceConstants.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/PreferenceConstants.java new file mode 100644 index 0000000000..da4f52269f --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/PreferenceConstants.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Kantega AS. All rights reserved. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The contents of this file are subject to the terms of either the Universal Permissive License + * v 1.0 as shown at http://oss.oracle.com/licenses/upl + * + * or the following license: + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.openjdk.jmc.kubernetes.preferences; + +/** + * Constant definitions for plug-in preferences + */ +public interface PreferenceConstants { + + public static final String P_SCAN_FOR_INSTANCES = "scanForInstances"; //$NON-NLS-1$ + public static final String P_SCAN_ALL_CONTEXTS = "scanAllContexts"; //$NON-NLS-1$ + public static final String P_LOG_ERRORS = "logErrors"; //$NON-NLS-1$ + public static final String P_REQUIRE_LABEL = "requireLabel"; //$NON-NLS-1$ + public static final String P_JOLOKIA_PATH = "jolokiaPath"; //$NON-NLS-1$ + public static final String P_JOLOKIA_PORT = "jolokiaPort"; //$NON-NLS-1$ + public static final String P_JOLOKIA_PROTOCOL = "jolokiaProtocol"; //$NON-NLS-1$ + public static final String P_CREDENTIALS_KEY = "kubernetes.scanning.credentials"; //$NON-NLS-1$ +} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/PreferenceInitializer.java b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/PreferenceInitializer.java new file mode 100644 index 0000000000..3aac64c2c2 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/PreferenceInitializer.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Kantega AS. All rights reserved. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The contents of this file are subject to the terms of either the Universal Permissive License + * v 1.0 as shown at http://oss.oracle.com/licenses/upl + * + * or the following license: + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.openjdk.jmc.kubernetes.preferences; + +import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer; +import org.eclipse.jface.preference.IPreferenceStore; +import org.openjdk.jmc.kubernetes.JmcKubernetesPlugin; + +/** + * Class used to initialize default preference values. + */ +public class PreferenceInitializer extends AbstractPreferenceInitializer implements PreferenceConstants { + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer# + * initializeDefaultPreferences() + */ + public void initializeDefaultPreferences() { + IPreferenceStore store = JmcKubernetesPlugin.getDefault().getPreferenceStore(); + store.setDefault(P_SCAN_FOR_INSTANCES, false); + store.setDefault(P_SCAN_ALL_CONTEXTS, false); + store.setDefault(P_REQUIRE_LABEL, "jolokia"); //$NON-NLS-1$ + store.setDefault(P_JOLOKIA_PATH, "/jolokia/"); //$NON-NLS-1$ + store.setDefault(P_JOLOKIA_PORT, "8778"); //$NON-NLS-1$ + } + +} diff --git a/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/messages.properties b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/messages.properties new file mode 100644 index 0000000000..cb743096d3 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/messages.properties @@ -0,0 +1,18 @@ +JmcKubernetesPreferenceForm_AllContexts=Scan pods from all locally configured &contexts, if false: only scan the current contexts +JmcKubernetesPreferenceForm_FormDescription=Options that allows you to scan kubernetes for JVMs running Jolokia\ +\n\nThe Kubernetes Proxy API will be used to attempt to access Jolokia like this (parenthesis denote optional components): \ +\n\n$kubernetesBaseUrl/api/v1/namespaces/podNamespace/pods/(jolokiaProtocol:)podname(:jolokiaPort)/proxy/jolokiaPath\n\n +JmcKubernetesPreferenceForm_LabelToolTip=Only attempt to connect to pods with this label set, leave empty to try to scan all pods +JmcKubernetesPreferenceForm_LogErrorsLabel=Log errors during scanning. Aids in troubleshooting but may generate some background noise. +JmcKubernetesPreferenceForm_PasswordLabel=Require pass&word +JmcKubernetesPreferenceForm_PasswordTooltip=Password , alternatively use ${kubernetes/secret/secretName/secretItem} where the secret is in the same namespace as the pod and the type is either kubernetes.io/basic-auth or Opaque with java.util.Properties compatible values +JmcKubernetesPreferenceForm_PathLabel=Jolokia &path in pods +JmcKubernetesPreferenceForm_PathTooltip=Use this path for jolokia, or specify ${kubernetes/annotation/annotationName} to be able to to get port name from pod metadata annotation of your choice +JmcKubernetesPreferenceForm_PortLabel=Jolokia p&ort in pods +JmcKubernetesPreferenceForm_PortTooltip=Port to use, leave empty to use default port of Kubernetes proxy, hardcode a port value, or alternatively ${kubernetes/annotation/annotationName} to get port name from pod metadata annotation of your choice +JmcKubernetesPreferenceForm_ProtocolLabel=Jolokia pro&tocol in pods +JmcKubernetesPreferenceForm_ProtocolTooltip=Protocol to use (optional), will infer http if not set, or hardcode to https, or alternatively ${kubernetes/annotation/annotationName} to get port name from pod metadata annotation of your choice +JmcKubernetesPreferenceForm_RequireLabel=Require &label to scan pod +JmcKubernetesPreferenceForm_ScanForPods=&Scan for kubernetes pods with Jolokia support +JmcKubernetesPreferenceForm_UsernameTooltip=Username , alternatively use ${kubernetes/secret/secretName/secretItem} where the secret is in the same namespace as the pod and the type is either kubernetes.io/basic-auth or Opaque with java.util.Properties compatible values +JmcKubernetesPreferenceForm_UsernameLabel=Require &username diff --git a/application/pom.xml b/application/pom.xml index 90dc2c96f2..e40f01a4a3 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -112,6 +112,7 @@ org.openjdk.jmc.updatesite.ide org.openjdk.jmc.updatesite.rcp org.openjdk.jmc.jolokia + org.openjdk.jmc.kubernetes l10n tests diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/META-INF/MANIFEST.MF b/application/uitests/org.openjdk.jmc.kubernetes.test/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..46a9f1b9fc --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/META-INF/MANIFEST.MF @@ -0,0 +1,20 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: RJMX Test +Bundle-SymbolicName: org.openjdk.jmc.kubernetes.test;singleton:=true +Bundle-Version: 9.1.0.qualifier +Import-Package: io.fabric8.kubernetes.api.model +Bundle-Vendor: Oracle Corporation +Bundle-RequiredExecutionEnvironment: JavaSE-17 +Bundle-ActivationPolicy: lazy +Require-Bundle: org.junit, + org.openjdk.jmc.jolokia, + org.eclipse.osgi;bundle-version="3.16.200", + org.eclipse.ui.workbench, + org.openjdk.jmc.rjmx;bundle-version="9.0.0", + org.eclipse.ui, + org.hamcrest;bundle-version="2.1", + org.openjdk.jmc.kubernetes;bundle-version="9.0.0", + com.github.tomakehurst.wiremock-standalone;bundle-version="2.27.2", + org.awaitility;bundle-version="4.0.0" +Automatic-Module-Name: org.openjdk.jmc.jolokia.test diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/build.properties b/application/uitests/org.openjdk.jmc.kubernetes.test/build.properties new file mode 100644 index 0000000000..b4608c8522 --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/build.properties @@ -0,0 +1,36 @@ +# +# Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2024, Kantega AS. All rights reserved. +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# The contents of this file are subject to the terms of either the Universal Permissive License +# v 1.0 as shown at http://oss.oracle.com/licenses/upl +# +# or the following license: +# +# Redistribution and use in source and binary forms, with or without modification, are permitted +# provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions +# and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of +# conditions and the following disclaimer in the documentation and/or other materials provided with +# the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to +# endorse or promote products derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY +# WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +source.. = src/test/java/,\ + src/test/resources/ +output.. = target/test-classes/ +bin.includes = META-INF/,. diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/pom.xml b/application/uitests/org.openjdk.jmc.kubernetes.test/pom.xml new file mode 100644 index 0000000000..652ddbc5a6 --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/pom.xml @@ -0,0 +1,58 @@ + + + + 4.0.0 + + org.openjdk.jmc + missioncontrol.application.uitests + ${revision}${changelist} + + org.openjdk.jmc.kubernetes.test + eclipse-test-plugin + + + ${project.basedir}/../../../configuration + + + + + org.awaitility + awaitility + 4.0.0 + + + diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/java/org/openjdk/jmc/kubernetes/JmcKubernetesTest.java b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/java/org/openjdk/jmc/kubernetes/JmcKubernetesTest.java new file mode 100644 index 0000000000..46d3c5ea8b --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/java/org/openjdk/jmc/kubernetes/JmcKubernetesTest.java @@ -0,0 +1,313 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Kantega AS. All rights reserved. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The contents of this file are subject to the terms of either the Universal Permissive License + * v 1.0 as shown at http://oss.oracle.com/licenses/upl + * + * or the following license: + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.openjdk.jmc.kubernetes; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.MalformedURLException; +import java.time.Duration; +import java.util.Base64; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import javax.management.AttributeNotFoundException; +import javax.management.InstanceNotFoundException; +import javax.management.InvalidAttributeValueException; +import javax.management.MBeanException; +import javax.management.MBeanServerConnection; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; +import javax.management.ReflectionException; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXServiceURL; + +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.HttpClients; +import org.awaitility.Awaitility; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; +import org.openjdk.jmc.common.IDescribable; +import org.openjdk.jmc.common.jvm.JVMType; +import org.openjdk.jmc.common.security.ICredentials; +import org.openjdk.jmc.common.security.InMemoryCredentials; +import org.openjdk.jmc.common.security.SecurityException; +import org.openjdk.jmc.kubernetes.preferences.KubernetesScanningParameters; +import org.openjdk.jmc.rjmx.common.IConnectionDescriptor; +import org.openjdk.jmc.rjmx.common.IServerDescriptor; +import org.openjdk.jmc.rjmx.descriptorprovider.IDescriptorListener; + +import com.github.tomakehurst.wiremock.client.WireMock; +import com.github.tomakehurst.wiremock.core.WireMockConfiguration; +import com.github.tomakehurst.wiremock.extension.responsetemplating.ResponseTemplateTransformer; +import com.github.tomakehurst.wiremock.junit.WireMockRule; + +/** + * Test that JMX connections done with JmcKubernetesJmxConnectionProvider are functional. In order + * to be able to test this in a contained environment, the kubernetes API is mocked with wiremock. + */ +@SuppressWarnings("restriction") +public class JmcKubernetesTest { + + static class TestParameters implements KubernetesScanningParameters { + public boolean scanForInstances, scanAllContexts; + public String jolokiaPort, jolokiaPath = "/jolokia/", jolokiaProtocol, requireLabel; + public InMemoryCredentials credentials; + + @Override + public boolean scanForInstances() { + return this.scanForInstances; + } + + @Override + public boolean scanAllContexts() { + return this.scanAllContexts; + } + + @Override + public String jolokiaPort() { + return this.jolokiaPort; + } + + @Override + public String username() throws SecurityException { + return this.credentials == null ? null : this.credentials.getUsername(); + } + + @Override + public String password() throws SecurityException { + return this.credentials == null ? null : this.credentials.getPassword(); + } + + @Override + public String jolokiaPath() { + return this.jolokiaPath; + } + + @Override + public String jolokiaProtocol() { + return this.jolokiaProtocol; + } + + @Override + public String requireLabel() { + return this.requireLabel; + } + + @Override + public ICredentials storeCredentials(String username, String password) throws SecurityException { + return this.credentials = new InMemoryCredentials(username, password); + } + + @Override + public void logError(String message, Throwable error) { + System.out.println(message); + error.printStackTrace(System.out); + } + } + + @ClassRule + public static WireMockRule wiremock = new WireMockRule( + WireMockConfiguration.options().extensions(new ResponseTemplateTransformer(false)).port(0)); + + static final String jolokiaUrl = "service:jmx:kubernetes:///ns1/pod-abcdef/jolokia"; + + private static MBeanServerConnection jolokiaConnection; + + @BeforeClass + public static void connect() throws Exception { + CloseableHttpResponse configResponse = HttpClients.createDefault() + .execute(new HttpGet(wiremock.baseUrl() + "/mock-kube-config.yml")); + Assert.assertEquals(configResponse.getStatusLine().getStatusCode(), 200); + File configFile = File.createTempFile("mock-kube-config", ".yml"); + configResponse.getEntity().writeTo(new FileOutputStream(configFile)); + // we set this so the KubernetesDiscoveryListener will work + //Setting taken from: https://github.com/fabric8io/kubernetes-client/blob/77a65f7d40f31a5dc37492cd9de3c317c2702fb4/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/Config.java#L120, unlikely to change + System.setProperty("kubeconfig", configFile.getAbsolutePath()); + jolokiaConnection = getKubernetesMBeanConnector(); + } + + @Test + public void testExecuteOperation() throws InstanceNotFoundException, MalformedObjectNameException, MBeanException, + ReflectionException, MalformedURLException, IOException { + jolokiaConnection.invoke(new ObjectName("java.lang:type=Memory"), "gc", new Object[0], new String[0]); + } + + @Test + public void testReadAttribute() + throws InstanceNotFoundException, AttributeNotFoundException, InvalidAttributeValueException, + MalformedObjectNameException, MBeanException, ReflectionException, MalformedURLException, IOException { + MBeanServerConnection jmxConnection = jolokiaConnection; + assertOneSingleAttribute(jmxConnection); + + } + + private void assertOneSingleAttribute(MBeanServerConnection jmxConnection) throws MalformedObjectNameException, + MBeanException, AttributeNotFoundException, InstanceNotFoundException, ReflectionException, IOException { + ObjectName objectName = new ObjectName("java.lang:type=Memory"); + String attribute = "Verbose"; + Assert.assertEquals(false, jmxConnection.getAttribute(objectName, attribute)); + } + + @Before + public void reset() { + wiremock.resetAll(); + wiremock.resetRequests(); + } + + private static MBeanServerConnection getKubernetesMBeanConnector() throws IOException, MalformedURLException { + JMXConnector connector = new JmcKubernetesJmxConnectionProvider().newJMXConnector(new JMXServiceURL(jolokiaUrl), + Collections.emptyMap()); + connector.connect(); + MBeanServerConnection connection = connector.getMBeanServerConnection(); + return connection; + } + + @Test + public void testDiscoverWithMostlyDefaultSettings() throws Exception { + + TestParameters parameters = new TestParameters(); + // Set config so that scanning takes place + parameters.scanForInstances = true; + testThatJvmIsFound(parameters); + } + + @Test + public void testDiscoverWithPathFromAnnotation() throws Exception { + TestParameters parameters = new TestParameters(); + parameters.scanForInstances = true; + parameters.jolokiaPath = "${kubernetes/annotation/jolokiaPath}"; + testThatJvmIsFound(parameters); + } + + @Test + public void testDiscoverWithPortFromAnnotation() throws Exception { + TestParameters parameters = new TestParameters(); + parameters.scanForInstances = true; + parameters.jolokiaPort = "${kubernetes/annotation/jolokiaPort}"; + + testThatJvmIsFound(parameters); + } + + @Test + public void testDiscoverWithBasicAuthFromSecret() throws Exception { + TestParameters parameters = new TestParameters(); + parameters.scanForInstances = true; + parameters.credentials = new InMemoryCredentials("${kubernetes/secret/jolokia-auth/username}", + "${kubernetes/secret/jolokia-auth/password}"); + + testThatJvmIsFound(parameters); + // Verify that the expected authorization was picked up + WireMock.verify(WireMock + .postRequestedFor(WireMock.urlPathMatching("/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*")) + .withHeader("X-jolokia-authorization", + WireMock.equalTo("Basic " + Base64.getEncoder().encodeToString("admin:admin".getBytes())))); + } + + @Test + public void testDiscoverWithAuthFromProperties() throws Exception { + + TestParameters parameters = new TestParameters(); + parameters.scanForInstances = true; + parameters.credentials = new InMemoryCredentials("${kubernetes/secret/jolokia-properties/user}", + "${kubernetes/secret/jolokia-properties/password}"); + + testThatJvmIsFound(parameters); + // Verify that the expected authorization was picked up + WireMock.verify(WireMock + .postRequestedFor(WireMock.urlPathMatching("/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*")) + .withHeader("X-jolokia-authorization", + WireMock.equalTo("Basic " + Base64.getEncoder().encodeToString("admin:secret".getBytes())))); + } + + @Test + public void testDiscoverWithAuthDirectlyFromSettings() throws Exception { + + TestParameters parameters = new TestParameters(); + parameters.scanForInstances = true; + parameters.credentials = new InMemoryCredentials("user", "***"); + testThatJvmIsFound(parameters); + // Verify that the expected authorization was picked up + WireMock.verify(WireMock + .postRequestedFor(WireMock.urlPathMatching("/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*")) + .withHeader("X-jolokia-authorization", + WireMock.equalTo("Basic " + Base64.getEncoder().encodeToString("user:***".getBytes())))); + } + + private void testThatJvmIsFound(TestParameters parameters) throws Exception { + + final KubernetesDiscoveryListener scanner = new KubernetesDiscoveryListener(parameters); + final Map foundVms = new HashMap<>(); + IDescriptorListener descriptorListener = new IDescriptorListener() { + public void onDescriptorDetected( + IServerDescriptor serverDescriptor, String path, JMXServiceURL url, + IConnectionDescriptor connectionDescriptor, IDescribable provider) { + foundVms.put(serverDescriptor.getGUID(), serverDescriptor); + } + + public void onDescriptorRemoved(String descriptorId) { + foundVms.remove(descriptorId); + } + }; + scanner.addDescriptorListener(descriptorListener); + + try { + // Test that at least one VM (the one running the test was discovered) + + Awaitility.await().atMost(Duration.ofSeconds(5)).until(() -> !foundVms.isEmpty()); + IServerDescriptor descriptor = foundVms.get("pod-abcdef"); + Assert.assertNotNull(descriptor); + Assert.assertEquals( + "[JVMDescriptor] Java command: /Users/marska/Downloads/hawtio-app-2.9.1.jar --port 9090 PID: 88774", + descriptor.getJvmInfo().toString()); + Assert.assertEquals(JVMType.HOTSPOT, descriptor.getJvmInfo().getJvmType()); + Assert.assertEquals("18.0.1", descriptor.getJvmInfo().getJavaVersion()); + Assert.assertTrue(descriptor instanceof IConnectionDescriptor); + IConnectionDescriptor connectDescriptor = (IConnectionDescriptor) descriptor; + JMXConnector connector = new JmcKubernetesJmxConnectionProvider() + .newJMXConnector(connectDescriptor.createJMXServiceURL(), connectDescriptor.getEnvironment()); + connector.connect(); + assertOneSingleAttribute(connector.getMBeanServerConnection()); + + } finally { + // Tell scanner thread to exit + scanner.shutdown(); + } + } +} diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-attribute.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-attribute.json new file mode 100644 index 0000000000..4230121a7e --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-attribute.json @@ -0,0 +1,10 @@ +{ + "request" : { + "attribute" : "Verbose", + "mbean" : "java.lang:type=Memory", + "type" : "read" + }, + "status" : 200, + "timestamp" : 1658563869, + "value" : false +} diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-basic-auth-secret.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-basic-auth-secret.json new file mode 100644 index 0000000000..b5bb351b7e --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-basic-auth-secret.json @@ -0,0 +1,19 @@ +{ + "apiVersion": "v1", + "data": { + "password": "YWRtaW4=", + "username": "YWRtaW4=" + }, + "kind": "Secret", + "metadata": { + "annotations": { + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Secret\",\"metadata\":{\"annotations\":{},\"name\":\"jolokia-auth\",\"namespace\":\"jfr\"},\"stringData\":{\"password\":\"admin\",\"username\":\"admin\"},\"type\":\"kubernetes.io/basic-auth\"}\n" + }, + "creationTimestamp": "2022-05-20T13:59:12Z", + "name": "jolokia-auth", + "namespace": "jfr", + "resourceVersion": "130", + "uid": "e1563217-ef08-481d-b2e3-233fa3040b56" + }, + "type": "kubernetes.io/basic-auth" +} diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-exec.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-exec.json new file mode 100644 index 0000000000..5bd35a69f2 --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-exec.json @@ -0,0 +1,10 @@ +{ + "request" : { + "mbean" : "java.lang:type=Memory", + "operation" : "gc()", + "type" : "exec" + }, + "status" : 200, + "timestamp" : 1658567003, + "value" : null +} diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-properties-secret.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-properties-secret.json new file mode 100644 index 0000000000..e12bbd16c1 --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-properties-secret.json @@ -0,0 +1,15 @@ +{ + "apiVersion": "v1", + "data": { + "jolokia.properties": "aG9zdD0wLjAuMC4wCnBhc3N3b3JkPXNlY3JldAp1c2VyPWFkbWluCnBvcnQ9ODc3OApkaXNjb3ZlcnlFbmFibGVkPXRydWUKZGlzY292ZXJ5QWdlbnRVcmw9aHR0cDovLyR7aG9zdH06ODc3OC9qb2xva2lhLwpwb2xpY3lMb2NhdGlvbj1jbGFzc3BhdGg6L0JPT1QtSU5GL2NsYXNzZXMvam9sb2tpYS1hY2Nlc3MueG1s" + }, + "kind": "Secret", + "metadata": { + "creationTimestamp": "2022-05-20T13:59:12Z", + "name": "jolokia-properties", + "namespace": "jfr", + "resourceVersion": "147", + "uid": "025bf17a-cff5-46e6-8499-dd9d6de43fa9" + }, + "type": "Opaque" +} diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-version.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-version.json new file mode 100644 index 0000000000..6299d0407a --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-version.json @@ -0,0 +1,41 @@ +{ + "request": { + "type": "version" + }, + "value": { + "agent": "1.7.1", + "protocol": "7.2", + "config": { + "listenForHttpService": "true", + "authIgnoreCerts": "false", + "agentId": "192.168.1.104-28660-5bd82fed-servlet", + "debug": "false", + "agentType": "servlet", + "policyLocation": "classpath:\/jolokia-access.xml", + "agentContext": "\/jolokia", + "serializeException": "false", + "mimeType": "text\/plain", + "dispatcherClasses": "org.jolokia.http.Jsr160ProxyNotEnabledByDefaultAnymoreDispatcher", + "authMode": "basic", + "authMatch": "any", + "streaming": "true", + "canonicalNaming": "true", + "historyMaxEntries": "10", + "allowErrorDetails": "false", + "allowDnsReverseLookup": "true", + "realm": "jolokia", + "includeStackTrace": "false", + "restrictorClass": "io.hawt.system.RBACRestrictor", + "mbeanQualifier": "qualifier=hawtio", + "useRestrictorService": "false", + "debugMaxEntries": "100" + }, + "info": { + "product": "jetty", + "vendor": "Eclipse", + "version": "9.4.z-SNAPSHOT" + } + }, + "timestamp": 1658556959, + "status": 200 +} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/memory-list.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/memory-list.json new file mode 100644 index 0000000000..46d117f2e4 --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/memory-list.json @@ -0,0 +1,46 @@ +{ + "request" : { + "path" : "java.lang/type=Memory", + "type" : "list" + }, + "status" : 200, + "timestamp" : 1658566168, + "value" : { + "attr" : { + "HeapMemoryUsage" : { + "desc" : "HeapMemoryUsage", + "rw" : false, + "type" : "javax.management.openmbean.CompositeData" + }, + "NonHeapMemoryUsage" : { + "desc" : "NonHeapMemoryUsage", + "rw" : false, + "type" : "javax.management.openmbean.CompositeData" + }, + "ObjectName" : { + "desc" : "ObjectName", + "rw" : false, + "type" : "javax.management.ObjectName" + }, + "ObjectPendingFinalizationCount" : { + "desc" : "ObjectPendingFinalizationCount", + "rw" : false, + "type" : "int" + }, + "Verbose" : { + "desc" : "Verbose", + "rw" : true, + "type" : "boolean" + } + }, + "class" : "sun.management.MemoryImpl", + "desc" : "Information on the management interface of the MBean", + "op" : { + "gc" : { + "args" : [], + "desc" : "gc", + "ret" : "void" + } + } + } +} diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/mock-kube-config.yml b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/mock-kube-config.yml new file mode 100644 index 0000000000..f222f471a7 --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/mock-kube-config.yml @@ -0,0 +1,17 @@ +apiVersion: v1 +clusters: +- cluster: + server: {{request.baseUrl}}/ + name: test +contexts: +- context: + cluster: test + user: test + name: test +current-context: test +kind: Config +preferences: {} +users: +- name: test + user: + token: foobar diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/pod.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/pod.json new file mode 100644 index 0000000000..77a61a3e32 --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/pod.json @@ -0,0 +1,11 @@ +{ + "metadata": { + "clusterName": "test", + "labels": { + "jolokia": true + }, + "name": "pod-abcdef", + "namespace": "ns1", + "selfLink": "/api/v1/namespaces/ns1/pods/pod-abcdef" + } +} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/pods.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/pods.json new file mode 100644 index 0000000000..114edd3640 --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/pods.json @@ -0,0 +1,19 @@ +{ + "items": [ + { + "metadata": { + "clusterName": "test", + "labels": { + "jolokia": true + }, + "annotations": { + "jolokiaPort": 8778, + "jolokiaPath": "jolokia" + }, + "name": "pod-abcdef", + "namespace": "ns1", + "selfLink": "/api/v1/namespaces/ns1/pods/pod-abcdef" + } + } + ] +} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/runtime-list.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/runtime-list.json new file mode 100644 index 0000000000..c096d78c52 --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/runtime-list.json @@ -0,0 +1,104 @@ +{ + "request" : { + "path" : "java.lang/type=Runtime", + "type" : "list" + }, + "status" : 200, + "timestamp" : 1658564737, + "value" : { + "attr" : { + "BootClassPath" : { + "desc" : "BootClassPath", + "rw" : false, + "type" : "java.lang.String" + }, + "BootClassPathSupported" : { + "desc" : "BootClassPathSupported", + "rw" : false, + "type" : "boolean" + }, + "ClassPath" : { + "desc" : "ClassPath", + "rw" : false, + "type" : "java.lang.String" + }, + "InputArguments" : { + "desc" : "InputArguments", + "rw" : false, + "type" : "[Ljava.lang.String;" + }, + "LibraryPath" : { + "desc" : "LibraryPath", + "rw" : false, + "type" : "java.lang.String" + }, + "ManagementSpecVersion" : { + "desc" : "ManagementSpecVersion", + "rw" : false, + "type" : "java.lang.String" + }, + "Name" : { + "desc" : "Name", + "rw" : false, + "type" : "java.lang.String" + }, + "ObjectName" : { + "desc" : "ObjectName", + "rw" : false, + "type" : "javax.management.ObjectName" + }, + "Pid" : { + "desc" : "Pid", + "rw" : false, + "type" : "long" + }, + "SpecName" : { + "desc" : "SpecName", + "rw" : false, + "type" : "java.lang.String" + }, + "SpecVendor" : { + "desc" : "SpecVendor", + "rw" : false, + "type" : "java.lang.String" + }, + "SpecVersion" : { + "desc" : "SpecVersion", + "rw" : false, + "type" : "java.lang.String" + }, + "StartTime" : { + "desc" : "StartTime", + "rw" : false, + "type" : "long" + }, + "SystemProperties" : { + "desc" : "SystemProperties", + "rw" : false, + "type" : "javax.management.openmbean.TabularData" + }, + "Uptime" : { + "desc" : "Uptime", + "rw" : false, + "type" : "long" + }, + "VmName" : { + "desc" : "VmName", + "rw" : false, + "type" : "java.lang.String" + }, + "VmVendor" : { + "desc" : "VmVendor", + "rw" : false, + "type" : "java.lang.String" + }, + "VmVersion" : { + "desc" : "VmVersion", + "rw" : false, + "type" : "java.lang.String" + } + }, + "class" : "sun.management.RuntimeImpl", + "desc" : "Information on the management interface of the MBean" + } +} diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/secrets.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/secrets.json new file mode 100644 index 0000000000..ac68f94b51 --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/secrets.json @@ -0,0 +1,43 @@ +{ + "apiVersion": "v1", + "items": [ + { + "apiVersion": "v1", + "data": { + "password": "YWRtaW4=", + "username": "YWRtaW4=" + }, + "kind": "Secret", + "metadata": { + "annotations": { + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Secret\",\"metadata\":{\"annotations\":{},\"name\":\"jolokia-auth\",\"namespace\":\"ns1\"},\"stringData\":{\"password\":\"admin\",\"username\":\"admin\"},\"type\":\"kubernetes.io/basic-auth\"}\n" + }, + "creationTimestamp": "2022-05-20T13:59:12Z", + "name": "jolokia-auth", + "namespace": "ns1", + "resourceVersion": "130", + "uid": "e1563217-ef08-481d-b2e3-233fa3040b56" + }, + "type": "kubernetes.io/basic-auth" + }, + { + "apiVersion": "v1", + "data": { + "jolokia.properties": "aG9zdD0wLjAuMC4wCnBhc3N3b3JkPXNlY3JldAp1c2VyPWFkbWluCnBvcnQ9ODc3OApkaXNjb3ZlcnlFbmFibGVkPXRydWUKZGlzY292ZXJ5QWdlbnRVcmw9aHR0cDovLyR7aG9zdH06ODc3OC9qb2xva2lhLwpwb2xpY3lMb2NhdGlvbj1jbGFzc3BhdGg6L0JPT1QtSU5GL2NsYXNzZXMvam9sb2tpYS1hY2Nlc3MueG1s" + }, + "kind": "Secret", + "metadata": { + "creationTimestamp": "2022-05-20T13:59:12Z", + "name": "jolokia-properties", + "namespace": "ns1", + "resourceVersion": "147", + "uid": "025bf17a-cff5-46e6-8499-dd9d6de43fa9" + }, + "type": "Opaque" + } + ], + "kind": "List", + "metadata": { + "resourceVersion": "" + } +} diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/system-attributes.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/system-attributes.json new file mode 100644 index 0000000000..dc2ff55433 --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/system-attributes.json @@ -0,0 +1,101 @@ +[ + { + "request" : { + "attribute" : "Pid", + "mbean" : "java.lang:type=Runtime", + "type" : "read" + }, + "status" : 200, + "timestamp" : 1658586915, + "value" : 88774 + }, + { + "request" : { + "attribute" : "Name", + "mbean" : "java.lang:type=Runtime", + "type" : "read" + }, + "status" : 200, + "timestamp" : 1658586915, + "value" : "88774@Martins-MacBook-Pro-2.local" + }, + { + "request" : { + "attribute" : "InputArguments", + "mbean" : "java.lang:type=Runtime", + "type" : "read" + }, + "status" : 200, + "timestamp" : 1658586915, + "value" : [] + }, + { + "request" : { + "attribute" : "SystemProperties", + "mbean" : "java.lang:type=Runtime", + "type" : "read" + }, + "status" : 200, + "timestamp" : 1658586915, + "value" : { + "apple.awt.application.name" : "App", + "file.encoding" : "UTF-8", + "file.separator" : "/", + "ftp.nonProxyHosts" : "local|*.local|169.254/16|*.169.254/16", + "hawtio.authenticationEnabled" : "false", + "hawtio.redirect.scheme" : "http", + "hawtio.url" : "http://localhost:9090/hawtio", + "http.nonProxyHosts" : "local|*.local|169.254/16|*.169.254/16", + "java.class.path" : "/Users/marska/Downloads/hawtio-app-2.9.1.jar", + "java.class.version" : "62.0", + "java.home" : "/Library/Java/JavaVirtualMachines/temurin-18.jdk/Contents/Home", + "java.io.tmpdir" : "/var/folders/1f/cdm0073x1mj1swnhtw181_4m0000gn/T/", + "java.library.path" : "/Users/marska/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:.", + "java.runtime.name" : "OpenJDK Runtime Environment", + "java.runtime.version" : "18.0.1+10", + "java.specification.name" : "Java Platform API Specification", + "java.specification.vendor" : "Oracle Corporation", + "java.specification.version" : "18", + "java.vendor" : "Eclipse Adoptium", + "java.vendor.url" : "https://adoptium.net/", + "java.vendor.url.bug" : "https://github.com/adoptium/adoptium-support/issues", + "java.vendor.version" : "Temurin-18.0.1+10", + "java.version" : "18.0.1", + "java.version.date" : "2022-04-19", + "java.vm.compressedOopsMode" : "Zero based", + "java.vm.info" : "mixed mode, sharing", + "java.vm.name" : "OpenJDK 64-Bit Server VM", + "java.vm.specification.name" : "Java Virtual Machine Specification", + "java.vm.specification.vendor" : "Oracle Corporation", + "java.vm.specification.version" : "18", + "java.vm.vendor" : "Eclipse Adoptium", + "java.vm.version" : "18.0.1+10", + "jdk.debug" : "release", + "line.separator" : "\n", + "native.encoding" : "UTF-8", + "org.eclipse.jetty.util.log.class" : "org.eclipse.jetty.util.log.Slf4jLog", + "os.arch" : "x86_64", + "os.name" : "Mac OS X", + "os.version" : "11.6.6", + "path.separator" : ":", + "socksNonProxyHosts" : "local|*.local|169.254/16|*.169.254/16", + "sun.arch.data.model" : "64", + "sun.awt.enableExtraMouseButtons" : "true", + "sun.boot.library.path" : "/Library/Java/JavaVirtualMachines/temurin-18.jdk/Contents/Home/lib", + "sun.cpu.endian" : "little", + "sun.io.unicode.encoding" : "UnicodeBig", + "sun.java.command" : "/Users/marska/Downloads/hawtio-app-2.9.1.jar --port 9090", + "sun.java.launcher" : "SUN_STANDARD", + "sun.jnu.encoding" : "UTF-8", + "sun.management.compiler" : "HotSpot 64-Bit Tiered Compilers", + "sun.stderr.encoding" : "UTF-8", + "sun.stdout.encoding" : "UTF-8", + "user.country" : "NO", + "user.dir" : "/Users/marska/Documents/dev/jmc/jmc8/releng/third-party", + "user.home" : "/Users/marska", + "user.language" : "nb", + "user.name" : "marska", + "user.timezone" : "Asia/Taipei" + } + } +] diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-exec.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-exec.json new file mode 100644 index 0000000000..6bb03ececf --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-exec.json @@ -0,0 +1,15 @@ +{ + "request": { + "method": "POST", + "urlPath": "/api/v1/namespaces/ns1/pods/pod-abcdef/proxy/jolokia", + "bodyPatterns": [ + { + "equalToJson": "{\"operation\":\"gc()\",\"mbean\":\"java.lang:type=Memory\",\"type\":\"EXEC\"}" + } + ] + }, + "response": { + "status": 200, + "bodyFileName": "jolokia-exec.json" + } +} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-list-memory.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-list-memory.json new file mode 100644 index 0000000000..e4170948cb --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-list-memory.json @@ -0,0 +1,15 @@ +{ + "request": { + "method": "POST", + "urlPathPattern": "/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*", + "bodyPatterns": [ + { + "equalToJson": "{\"path\":\"java.lang/type=Memory\",\"type\":\"LIST\"}" + } + ] + }, + "response": { + "status": 200, + "bodyFileName": "memory-list.json" + } +} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-list-runtime.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-list-runtime.json new file mode 100644 index 0000000000..f936e7eb0f --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-list-runtime.json @@ -0,0 +1,15 @@ +{ + "request": { + "method": "POST", + "urlPathPattern": "/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*", + "bodyPatterns": [ + { + "equalToJson": "{\"path\":\"java.lang/type=Runtime\",\"type\":\"LIST\"}" + } + ] + }, + "response": { + "status": 200, + "bodyFileName": "runtime-list.json" + } +} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-probe.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-probe.json new file mode 100644 index 0000000000..5b26da423d --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-probe.json @@ -0,0 +1,15 @@ +{ + "request": { + "method": "POST", + "urlPathPattern": "/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*", + "bodyPatterns": [ + { + "equalToJson": "{\"type\":\"version\"}" + } + ] + }, + "response": { + "status": 200, + "bodyFileName": "jolokia-version.json" + } +} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-probe2.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-probe2.json new file mode 100644 index 0000000000..98670016fd --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-probe2.json @@ -0,0 +1,15 @@ +{ + "request": { + "method": "POST", + "urlPathPattern": "/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*", + "bodyPatterns": [ + { + "equalToJson": "{\"type\":\"VERSION\"}" + } + ] + }, + "response": { + "status": 200, + "bodyFileName": "jolokia-version.json" + } +} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-read-attribute.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-read-attribute.json new file mode 100644 index 0000000000..1584e3b4a3 --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-read-attribute.json @@ -0,0 +1,15 @@ +{ + "request": { + "method": "POST", + "urlPathPattern": "/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*", + "bodyPatterns": [ + { + "equalToJson": "{\"attribute\":\"Verbose\",\"mbean\":\"java.lang:type=Memory\",\"type\":\"READ\"}" + } + ] + }, + "response": { + "status": 200, + "bodyFileName": "jolokia-attribute.json" + } +} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jvm-properties.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jvm-properties.json new file mode 100644 index 0000000000..5b26da423d --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jvm-properties.json @@ -0,0 +1,15 @@ +{ + "request": { + "method": "POST", + "urlPathPattern": "/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*", + "bodyPatterns": [ + { + "equalToJson": "{\"type\":\"version\"}" + } + ] + }, + "response": { + "status": 200, + "bodyFileName": "jolokia-version.json" + } +} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/kube-config.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/kube-config.json new file mode 100644 index 0000000000..418a887637 --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/kube-config.json @@ -0,0 +1,13 @@ +{ + "request": { + "method": "GET", + "url": "/mock-kube-config.yml" + }, + "response": { + "status": 200, + "bodyFileName": "mock-kube-config.yml", + "transformers": [ + "response-template" + ] + } +} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/pod.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/pod.json new file mode 100644 index 0000000000..ebcbd7c5e3 --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/pod.json @@ -0,0 +1,10 @@ +{ + "request": { + "method": "GET", + "url": "/api/v1/namespaces/ns1/pods/pod-abcdef" + }, + "response": { + "status": 200, + "bodyFileName": "pod.json" + } +} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/pods.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/pods.json new file mode 100644 index 0000000000..061fb0f53b --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/pods.json @@ -0,0 +1,10 @@ +{ + "request": { + "method": "GET", + "urlPath": "/api/v1/pods" + }, + "response": { + "status": 200, + "bodyFileName": "pods.json" + } +} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/secrets.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/secrets.json new file mode 100644 index 0000000000..113bb91e1f --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/secrets.json @@ -0,0 +1,10 @@ +{ + "request": { + "method": "GET", + "url": "/api/v1/namespaces/ns1/secrets" + }, + "response": { + "status": 200, + "bodyFileName": "secrets.json" + } +} \ No newline at end of file diff --git a/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/system-attributes.json b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/system-attributes.json new file mode 100644 index 0000000000..e3d1147b2f --- /dev/null +++ b/application/uitests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/system-attributes.json @@ -0,0 +1,15 @@ +{ + "request": { + "method": "POST", + "urlPathPattern": "/api/v1/namespaces/ns1/pods/pod-abcdef.*/proxy/jolokia.*", + "bodyPatterns": [ + { + "equalToJson": "[{\"attribute\":\"Pid\",\"mbean\":\"java.lang:type=Runtime\",\"type\":\"READ\"},{\"attribute\":\"Name\",\"mbean\":\"java.lang:type=Runtime\",\"type\":\"READ\"},{\"attribute\":\"InputArguments\",\"mbean\":\"java.lang:type=Runtime\",\"type\":\"READ\"},{\"attribute\":\"SystemProperties\",\"mbean\":\"java.lang:type=Runtime\",\"type\":\"READ\"}]" + } + ] + }, + "response": { + "status": 200, + "bodyFileName": "system-attributes.json" + } +} \ No newline at end of file diff --git a/application/uitests/pom.xml b/application/uitests/pom.xml index 98327fd9a8..f6496c214d 100644 --- a/application/uitests/pom.xml +++ b/application/uitests/pom.xml @@ -1,6 +1,6 @@