diff --git a/application/org.openjdk.jmc.feature.core/feature.xml b/application/org.openjdk.jmc.feature.core/feature.xml index b1eff711d..5f332d436 100644 --- a/application/org.openjdk.jmc.feature.core/feature.xml +++ b/application/org.openjdk.jmc.feature.core/feature.xml @@ -196,4 +196,18 @@ version="0.0.0" unpack="false"/> + + + + diff --git a/application/org.openjdk.jmc.jolokia/.classpath b/application/org.openjdk.jmc.jolokia/.classpath new file mode 100644 index 000000000..f15cbe421 --- /dev/null +++ b/application/org.openjdk.jmc.jolokia/.classpath @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/application/org.openjdk.jmc.jolokia/META-INF/MANIFEST.MF b/application/org.openjdk.jmc.jolokia/META-INF/MANIFEST.MF new file mode 100644 index 000000000..14dd1a5ef --- /dev/null +++ b/application/org.openjdk.jmc.jolokia/META-INF/MANIFEST.MF @@ -0,0 +1,20 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: %Bundle-Name +Bundle-SymbolicName: org.openjdk.jmc.jolokia;singleton:=true +Bundle-Version: 9.0.0.qualifier +Automatic-Module-Name: org.openjdk.jmc.jolokia +Bundle-RequiredExecutionEnvironment: JavaSE-17 +Require-Bundle: org.eclipse.core.runtime, + org.eclipse.ui, + org.jolokia.jmx-adapter.standalone;bundle-version="1.7.2";visibility:=reexport, + org.jolokia.jvm;bundle-version="1.7.2", + org.openjdk.jmc.common, + org.openjdk.jmc.rjmx, + org.openjdk.jmc.ui +Export-Package: org.openjdk.jmc.jolokia, + org.openjdk.jmc.jolokia.preferences +Import-Package: org.jolokia.converter, + org.json.simple +Bundle-Activator: org.openjdk.jmc.jolokia.JmcJolokiaPlugin +Bundle-ActivationPolicy: lazy diff --git a/application/org.openjdk.jmc.jolokia/OSGI-INF/l10n/bundle.properties b/application/org.openjdk.jmc.jolokia/OSGI-INF/l10n/bundle.properties new file mode 100644 index 000000000..11d361ea1 --- /dev/null +++ b/application/org.openjdk.jmc.jolokia/OSGI-INF/l10n/bundle.properties @@ -0,0 +1,3 @@ +#Properties file for org.openjdk.jmc.jolokia +page.name = Jolokia +Bundle-Name = Jolokia JMC Connection \ No newline at end of file diff --git a/application/org.openjdk.jmc.jolokia/build.properties b/application/org.openjdk.jmc.jolokia/build.properties new file mode 100644 index 000000000..49f8b881c --- /dev/null +++ b/application/org.openjdk.jmc.jolokia/build.properties @@ -0,0 +1,39 @@ +# +# Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2021, 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/main/java/ +output.. = target/classes/ +bin.includes = META-INF/,\ + OSGI-INF/,\ + .,\ + plugin.xml,\ + OSGI-INF/l10n/bundle.properties diff --git a/application/org.openjdk.jmc.jolokia/plugin.xml b/application/org.openjdk.jmc.jolokia/plugin.xml new file mode 100644 index 000000000..a0a08b420 --- /dev/null +++ b/application/org.openjdk.jmc.jolokia/plugin.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/application/org.openjdk.jmc.jolokia/pom.xml b/application/org.openjdk.jmc.jolokia/pom.xml new file mode 100644 index 000000000..2d4541b69 --- /dev/null +++ b/application/org.openjdk.jmc.jolokia/pom.xml @@ -0,0 +1,49 @@ + + + + 4.0.0 + + org.openjdk.jmc + missioncontrol.application + ${revision}${changelist} + + org.openjdk.jmc.jolokia + eclipse-plugin + + ${project.basedir}/../../configuration + + diff --git a/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/AbstractCachedDescriptorProvider.java b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/AbstractCachedDescriptorProvider.java new file mode 100644 index 000000000..b1eb561eb --- /dev/null +++ b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/AbstractCachedDescriptorProvider.java @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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.jolokia; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; + +import org.openjdk.jmc.rjmx.descriptorprovider.AbstractDescriptorProvider; +import org.openjdk.jmc.rjmx.descriptorprovider.IDescriptorListener; + +/** + * The {@code AbstractCachedDescriptorProvider} keeps a list of identified JVMs that can be + * refreshed in the background by some means of discovering JVMs. Listeners will be notified of any + * changes. + */ +@SuppressWarnings("restriction") +public abstract class AbstractCachedDescriptorProvider extends AbstractDescriptorProvider { + + private static final long LOCAL_REFRESH_INTERVAL = 20000; + private Scanner scanner; + private Thread scannerThread; + /** + * Map + */ + private final Map knownDescriptors = new HashMap<>(); + + /** + * This is where we periodically scan and report deltas to the listeners. + */ + private class Scanner implements Runnable { + boolean isRunning; + + @Override + public void run() { + isRunning = true; + while (isRunning) { + try { + scan(); + Thread.sleep(LOCAL_REFRESH_INTERVAL); + } catch (InterruptedException ignore) { + // Don't mind being interrupted. + } + } + } + + /** + * Marks this scanner as terminated. + */ + public void shutdown() { + isRunning = false; + } + + protected void scan() { + Map newOnes = discoverJvms(); + + synchronized (knownDescriptors) { + // Remove stale ones... + for (Iterator> entryIterator = knownDescriptors.entrySet() + .iterator(); entryIterator.hasNext();) { + Entry entry = entryIterator.next(); + if (newOnes.containsKey(entry.getKey())) { + continue; + } + entryIterator.remove(); + onDescriptorRemoved(entry.getKey()); + } + + // Add new ones... + for (Entry entry : newOnes.entrySet()) { + if (knownDescriptors.containsKey(entry.getKey())) { + continue; + } + onDescriptorDetected(entry.getValue(), entry.getValue().getPath(), entry.getValue().serviceUrl(), + entry.getValue()); + } + knownDescriptors.clear(); + knownDescriptors.putAll(newOnes); + } + } + } + + /** + * Sets up the thread. + */ + private void initialize() { + + scanner = new Scanner(); + scannerThread = new Thread(scanner, getName()); // $NON-NLS-1$ + scannerThread.start(); + } + + protected abstract boolean isEnabled(); + + protected abstract Map discoverJvms(); + + @Override + public void addDescriptorListener(IDescriptorListener l) { + synchronized (m_descriptorListeners) { + if (m_descriptorListeners.size() == 0) { + super.addDescriptorListener(l); + initialize(); + } else { + super.addDescriptorListener(l); + } + } + } + + @Override + public void removeDescriptorListener(IDescriptorListener l) { + synchronized (m_descriptorListeners) { + super.removeDescriptorListener(l); + if (m_descriptorListeners.size() == 0 && scanner != null) { + scanner.shutdown(); + scannerThread.interrupt(); + } + } + } + + /** + * Shuts down the scanner thread. + */ + public void shutdown() { + if (scanner != null) { + scanner.shutdown(); + } + } + +} diff --git a/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/JmcJolokiaJmxConnection.java b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/JmcJolokiaJmxConnection.java new file mode 100644 index 000000000..bd8cdfec9 --- /dev/null +++ b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/JmcJolokiaJmxConnection.java @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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.jolokia; + +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.util.LinkedList; +import java.util.Optional; + +import javax.management.AttributeNotFoundException; +import javax.management.Descriptor; +import javax.management.ImmutableDescriptor; +import javax.management.InstanceNotFoundException; +import javax.management.MBeanException; +import javax.management.MBeanInfo; +import javax.management.MBeanOperationInfo; +import javax.management.MBeanParameterInfo; +import javax.management.ObjectName; +import javax.management.modelmbean.DescriptorSupport; +import javax.management.openmbean.TabularData; + +import org.jolokia.client.J4pClient; +import org.jolokia.client.jmxadapter.RemoteJmxAdapter; +import org.jolokia.converter.Converters; +import org.jolokia.converter.json.JsonConvertOptions; + +/** + * Make JMC specific adjustments to Jolokia JMX connection. May consider to use the decorator + * pattern if differences are big, but for now subclass + */ +public class JmcJolokiaJmxConnection extends RemoteJmxAdapter { + + private static final String UNKNOWN = "Unknown"; //$NON-NLS-1$ + private static final String DIAGNOSTIC_OPTIONS = "com.sun.management:type=DiagnosticCommand"; //$NON-NLS-1$ + private static final String PREFIX = "dcmd."; //$NON-NLS-1$ + private static final String IMPACT = PREFIX + "vmImpact"; //$NON-NLS-1$ + private static final String NAME = PREFIX + "name"; //$NON-NLS-1$ + private static final String DESCRIPTION = PREFIX + "description"; //$NON-NLS-1$ + private static final String ARGUMENTS = PREFIX + "arguments"; //$NON-NLS-1$ + private static final String ARGUMENT_NAME = PREFIX + "arg.name"; //$NON-NLS-1$ + private static final String ARGUMENT_DESCRIPTION = PREFIX + "arg.description"; //$NON-NLS-1$ + private static final String ARGUMENT_MANDATORY = PREFIX + "arg.isMandatory"; //$NON-NLS-1$ + private static final String ARGUMENT_TYPE = PREFIX + "arg.type"; //$NON-NLS-1$ + private static final String ARGUMENT_OPTION = PREFIX + "arg.isOption"; //$NON-NLS-1$ + private static final String ARGUMENT_MULITPLE = PREFIX + "arg.isMultiple"; //$NON-NLS-1$ + + public JmcJolokiaJmxConnection(J4pClient client) throws IOException { + super(client); + } + + @Override + public MBeanInfo getMBeanInfo(ObjectName name) throws InstanceNotFoundException, IOException { + MBeanInfo mBeanInfo = super.getMBeanInfo(name); + // the diagnostic options tab and memory relies on descriptor info in MBeanInfo, + // modify descriptors the first time + if (DIAGNOSTIC_OPTIONS.equals(name.getCanonicalName()) + && mBeanInfo.getOperations()[0].getDescriptor() == ImmutableDescriptor.EMPTY_DESCRIPTOR) { + + MBeanOperationInfo[] modifiedOperations = new MBeanOperationInfo[mBeanInfo.getOperations().length]; + + for (int i = 0; i < mBeanInfo.getOperations().length; i++) { + modifiedOperations[i] = stealOrBuildOperationInfo(mBeanInfo.getOperations()[i], + checkForLocalOperationInfo(name)); + } + //create a copy with modified operations in place of the original MBeanInfo in the cache + final MBeanInfo modifiedMBeanInfo = new MBeanInfo(mBeanInfo.getClassName(), mBeanInfo.getDescription(), + mBeanInfo.getAttributes(), mBeanInfo.getConstructors(), modifiedOperations, + mBeanInfo.getNotifications()); + this.mbeanInfoCache.put(name, modifiedMBeanInfo); + return modifiedMBeanInfo; + } + return mBeanInfo; + } + + private Optional checkForLocalOperationInfo(ObjectName name) { + MBeanInfo localInfo; + try { + localInfo = ManagementFactory.getPlatformMBeanServer().getMBeanInfo(name); + } catch (Exception ignore) { + localInfo = null; + } + return Optional.ofNullable(localInfo); + } + + @Override + public Object invoke(ObjectName name, String operationName, Object[] params, String[] signature) + throws InstanceNotFoundException, MBeanException, IOException { + for (int i = 0; i < params.length; i++) { + Object object = params[i]; + if (object instanceof TabularData) { + try { + params[i] = new Converters().getToJsonConverter().convertToJson(object, new LinkedList(), + JsonConvertOptions.DEFAULT); + } catch (AttributeNotFoundException ignore) { + } + } + + } + return super.invoke(name, operationName, params, signature); + } + + /** + * Build MBeanOperationInfo by taking information from the corresponding MBean in the local JVM + * for a more precise signature. If it is not available locally, attempt to construct it from + * the metadata from Jolokia. + * + * @param original + * MBeanInfo from Jolokia list. + * @param localInfo + * MBeanInfo from this JVM to use for getting descriptor. + * @return Descriptor + */ + private MBeanOperationInfo stealOrBuildOperationInfo(MBeanOperationInfo original, Optional localInfo) { + return localInfo.map(info -> checkForMatchingLocalOperation(original, info))// first attempt to get descriptor from local copy + .orElseGet(() -> reverseEngineerOperationInfo(original));// if not, reverse engineer descriptor from operation info + } + + private MBeanOperationInfo checkForMatchingLocalOperation(MBeanOperationInfo original, MBeanInfo info) { + for (MBeanOperationInfo localOperation : info.getOperations()) { + if (localOperation.getName().equals(original.getName())) { + if (localOperation.getSignature().length == original.getSignature().length) { + for (int i = 0; i < original.getSignature().length; i++) { + MBeanParameterInfo param = original.getSignature()[i]; + if (!param.getType().equals(localOperation.getSignature()[i].getType())) { + break; + } else if (i == original.getSignature().length - 1) { + // whole signature matches, use as replacement + return localOperation; + } + } + } + } + } + return null; + } + + private MBeanOperationInfo reverseEngineerOperationInfo(MBeanOperationInfo original) { + DescriptorSupport result = new DescriptorSupport(); + result.setField(NAME, original.getName()); + result.setField(DESCRIPTION, original.getDescription()); + result.setField(IMPACT, UNKNOWN); + result.setField(ARGUMENTS, buildArguments(original.getSignature())); + return new MBeanOperationInfo(original.getName(), original.getDescription(), original.getSignature(), + original.getReturnType(), MBeanOperationInfo.UNKNOWN, result); + } + + private Descriptor buildArguments(MBeanParameterInfo[] signature) { + DescriptorSupport parameters = new DescriptorSupport(); + for (MBeanParameterInfo parameter : signature) { + parameters.setField(parameter.getName(), buildArgument(parameter)); + } + return parameters; + } + + private Descriptor buildArgument(MBeanParameterInfo parameter) { + DescriptorSupport result = new DescriptorSupport(); + result.setField(ARGUMENT_NAME, parameter.getName()); + boolean isMultiple = parameter.getType().startsWith("["); //$NON-NLS-1$ + result.setField(ARGUMENT_MULITPLE, String.valueOf(isMultiple)); + String type = parameter.getType(); + if (isMultiple) { + if (type.startsWith("[L")) { //$NON-NLS-1$ + type = type.substring(2); + } else { + type = type.substring(1); + } + + } + // probably more reverse mapping of types should be done here, but we hope it is + // sufficient + result.setField(ARGUMENT_TYPE, type); + result.setField(ARGUMENT_DESCRIPTION, parameter.getDescription()); + result.setField(ARGUMENT_MANDATORY, "false"); //$NON-NLS-1$ + result.setField(ARGUMENT_OPTION, "false"); //$NON-NLS-1$ + return result; + } + +} diff --git a/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/JmcJolokiaJmxConnectionProvider.java b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/JmcJolokiaJmxConnectionProvider.java new file mode 100644 index 000000000..d1f0ca841 --- /dev/null +++ b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/JmcJolokiaJmxConnectionProvider.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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.jolokia; + +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; + +public class JmcJolokiaJmxConnectionProvider implements JMXConnectorProvider { + @Override + public JMXConnector newJMXConnector(JMXServiceURL serviceURL, Map environment) throws IOException { + if (!"jolokia".equals(serviceURL.getProtocol())) { //$NON-NLS-1$ + throw new MalformedURLException("I only serve Jolokia connections"); //$NON-NLS-1$ + } + return new JmcJolokiaJmxConnector(serviceURL, environment); + } +} diff --git a/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/JmcJolokiaJmxConnector.java b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/JmcJolokiaJmxConnector.java new file mode 100644 index 000000000..2a0835ed1 --- /dev/null +++ b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/JmcJolokiaJmxConnector.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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.jolokia; + +import java.io.IOException; +import java.util.Map; + +import javax.management.remote.JMXServiceURL; + +import org.jolokia.client.J4pClientBuilder; +import org.jolokia.client.jmxadapter.JolokiaJmxConnector; +import org.jolokia.client.jmxadapter.RemoteJmxAdapter; + +public class JmcJolokiaJmxConnector extends JolokiaJmxConnector { + + public JmcJolokiaJmxConnector(JMXServiceURL serviceURL, Map environment) { + super(serviceURL, environment); + } + + @Override + protected RemoteJmxAdapter instantiateAdapter(J4pClientBuilder clientBuilder, Map mergedEnv) + throws IOException { + return new JmcJolokiaJmxConnection(clientBuilder.build()); + } +} diff --git a/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/JmcJolokiaPlugin.java b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/JmcJolokiaPlugin.java new file mode 100644 index 000000000..94816d42f --- /dev/null +++ b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/JmcJolokiaPlugin.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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.jolokia; + +import org.openjdk.jmc.ui.MCAbstractUIPlugin; + +public class JmcJolokiaPlugin extends MCAbstractUIPlugin { + + public final static String PLUGIN_ID = "org.openjdk.jmc.jolokia"; //$NON-NLS-1$ + private static JmcJolokiaPlugin plugin; + + public JmcJolokiaPlugin() { + super(PLUGIN_ID); + plugin = this; + } + + public static JmcJolokiaPlugin getDefault() { + return plugin; + } +} diff --git a/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/JolokiaAgentDescriptor.java b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/JolokiaAgentDescriptor.java new file mode 100644 index 000000000..b872c28c4 --- /dev/null +++ b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/JolokiaAgentDescriptor.java @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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.jolokia; + +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Arrays; +import java.util.Map; + +import javax.management.Attribute; +import javax.management.AttributeList; +import javax.management.InstanceNotFoundException; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; +import javax.management.openmbean.CompositeDataSupport; +import javax.management.openmbean.TabularDataSupport; +import javax.management.remote.JMXServiceURL; + +import org.jolokia.client.jmxadapter.RemoteJmxAdapter; +import org.openjdk.jmc.common.jvm.Connectable; +import org.openjdk.jmc.common.jvm.JVMArch; +import org.openjdk.jmc.common.jvm.JVMDescriptor; +import org.openjdk.jmc.common.jvm.JVMType; + +/** + * Provide data about JVMs accessed over Jolokia for the JVM browser + */ +public class JolokiaAgentDescriptor implements ServerConnectionDescriptor { + + public static final JVMDescriptor NULL_DESCRIPTOR = new JVMDescriptor(null, null, null, null, null, null, null, + null, false, Connectable.UNKNOWN); + private final JMXServiceURL serviceUrl; + private final Map agentData; + private final JVMDescriptor jvmDescriptor; + + public JolokiaAgentDescriptor(Map agentData, JVMDescriptor jvmDescriptor) + throws URISyntaxException, MalformedURLException { + super(); + URI uri = new URI((String) agentData.get("url")); //$NON-NLS-1$ + this.serviceUrl = new JMXServiceURL( + String.format("service:jmx:jolokia://%s:%s%s", uri.getHost(), uri.getPort(), uri.getPath())); //$NON-NLS-1$ + this.agentData = agentData; + this.jvmDescriptor = jvmDescriptor; + } + + JMXServiceURL getServiceUrl() { + return serviceUrl; + } + + @Override + public String getGUID() { + return String.valueOf(agentData.get("agent_id")); //$NON-NLS-1$ + } + + @Override + public String getDisplayName() { + return String.valueOf(agentData.get("agent_id")); //$NON-NLS-1$ + } + + @Override + public JVMDescriptor getJvmInfo() { + return this.jvmDescriptor; + } + + /** + * Best effort to extract JVM information from a connection if everything works. Can be adjusted + * to support different flavors of JVM. + */ + public static JVMDescriptor attemptToGetJvmInfo(RemoteJmxAdapter adapter) { + + try { + AttributeList attributes = adapter.getAttributes(new ObjectName(ManagementFactory.RUNTIME_MXBEAN_NAME), + new String[] {"Pid", "Name", "InputArguments", "SystemProperties"}); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + Integer pid = null; + String arguments = null, javaCommand = null, javaVersion = null, vmName = null, vmVendor = null; + boolean isDebug = false; + JVMType type = JVMType.UNKNOWN; + JVMArch arch = JVMArch.UNKNOWN; + for (Attribute attribute : attributes.asList()) { + // newer JVM have pid as separate attribute, older have to parse from name + if (attribute.getName().equalsIgnoreCase("Pid")) { //$NON-NLS-1$ + try { + pid = Integer.valueOf(String.valueOf(attribute.getValue())); + } catch (NumberFormatException ignore) { + } + } else if (attribute.getName().equalsIgnoreCase("Name") && pid == null) { //$NON-NLS-1$ + String pidAndHost = String.valueOf(attribute.getValue()); + int separator = pidAndHost.indexOf('@'); + if (separator > 0) { + try { + pid = Integer.valueOf(pidAndHost.substring(0, separator)); + } catch (NumberFormatException e) { + } + } + } else if (attribute.getName().equalsIgnoreCase("InputArguments")) { //$NON-NLS-1$ + + if (attribute.getValue() instanceof String[]) { + arguments = Arrays.toString((String[]) attribute.getValue()); + } else { + arguments = String.valueOf(attribute.getValue()); + } + if (arguments.contains("-agentlib:jdwp")) { //$NON-NLS-1$ + isDebug = true; + } + } else if (attribute.getName().equalsIgnoreCase("SystemProperties") //$NON-NLS-1$ + && attribute.getValue() instanceof TabularDataSupport) { + TabularDataSupport systemProperties = (TabularDataSupport) attribute.getValue(); + + // quite clumsy: iterate over properties as we need to use the exact key, which is non trivial + // to reproduce + for (Object entry : systemProperties.values()) { + String key = ((CompositeDataSupport) entry).get("key").toString(); //$NON-NLS-1$ + String value = ((CompositeDataSupport) entry).get("value").toString(); //$NON-NLS-1$ + if (key.equalsIgnoreCase("sun.management.compiler")) { //$NON-NLS-1$ + if (value.toLowerCase().contains("hotspot")) { //$NON-NLS-1$ + type = JVMType.HOTSPOT; + } + } else if (key.equalsIgnoreCase("sun.arch.data.model")) { //$NON-NLS-1$ + String archIndicator = value; + if (archIndicator.contains("64")) { //$NON-NLS-1$ + arch = JVMArch.BIT64; + } else if (archIndicator.contains("32")) { //$NON-NLS-1$ + arch = JVMArch.BIT32; + } + } else if (key.equalsIgnoreCase("sun.java.command")) { //$NON-NLS-1$ + javaCommand = value; + } else if (key.equalsIgnoreCase("java.version")) { //$NON-NLS-1$ + javaVersion = value; + } else if (key.equalsIgnoreCase("java.vm.name")) { //$NON-NLS-1$ + vmName = value; + } else if (key.equalsIgnoreCase("java.vm.vendor")) { //$NON-NLS-1$ + vmVendor = value; + } + } + + } + + } + return new JVMDescriptor(javaVersion, type, arch, javaCommand, arguments, vmName, vmVendor, pid, isDebug, + Connectable.UNKNOWN); + + } catch (RuntimeException | IOException | InstanceNotFoundException | MalformedObjectNameException ignore) { + return NULL_DESCRIPTOR; + } + + } + + @Override + public JMXServiceURL createJMXServiceURL() throws IOException { + return serviceUrl; + } + + @Override + public Map getEnvironment() { + return null; + } + + @Override + public String getPath() { + return null; + } + + @Override + public JMXServiceURL serviceUrl() { + return this.serviceUrl; + } + +} diff --git a/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/JolokiaDiscoveryListener.java b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/JolokiaDiscoveryListener.java new file mode 100644 index 000000000..dd5b36458 --- /dev/null +++ b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/JolokiaDiscoveryListener.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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.jolokia; + +import java.io.IOException; + +import java.net.URISyntaxException; +import java.util.HashMap; +import java.util.Map; + +import org.jolokia.client.jmxadapter.RemoteJmxAdapter; +import org.jolokia.discovery.JolokiaDiscovery; +import org.jolokia.util.JulLogHandler; +import org.openjdk.jmc.common.jvm.JVMDescriptor; +import org.openjdk.jmc.jolokia.preferences.PreferenceConstants; + +/** + * Add found Jolokia instances to the JVM browser using the Jolokia discovery mechanism. + * https://jolokia.org/reference/html/protocol.html#discovery + */ +public class JolokiaDiscoveryListener extends AbstractCachedDescriptorProvider implements PreferenceConstants { + + @Override + protected Map discoverJvms() { + Map found = new HashMap<>(); + if (!JmcJolokiaPlugin.getDefault().getPreferenceStore().getBoolean(P_SCAN)) { + return found; + } + try { + for (Object object : new JolokiaDiscovery("jmc", new JulLogHandler()).lookupAgents()) { //$NON-NLS-1$ + try { + + @SuppressWarnings("unchecked") + Map response = (Map) object; + JVMDescriptor jvmInfo; + try {// if it is connectable, see if we can get info from connection + jvmInfo = JolokiaAgentDescriptor + .attemptToGetJvmInfo(new RemoteJmxAdapter(String.valueOf(response.get("url")))); //$NON-NLS-1$ + } catch (Exception ignore) { + jvmInfo = JolokiaAgentDescriptor.NULL_DESCRIPTOR; + } + JolokiaAgentDescriptor agentDescriptor = new JolokiaAgentDescriptor(response, jvmInfo); + found.put(agentDescriptor.getGUID(), agentDescriptor); + + } catch (URISyntaxException ignore) { + } + } + } catch (IOException ignore) { + } + return found; + } + + @Override + public String getDescription() { + return Messages.JolokiaDiscoveryListener_Description; + } + + @Override + public String getName() { + return "jolokia"; //$NON-NLS-1$ + } + + @Override + protected boolean isEnabled() { + return true; + } + +} diff --git a/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/Messages.java b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/Messages.java new file mode 100644 index 000000000..7f9321d39 --- /dev/null +++ b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/Messages.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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.jolokia; + +import org.eclipse.osgi.util.NLS; + +public class Messages extends NLS { + private static final String BUNDLE_NAME = "org.openjdk.jmc.jolokia.messages"; //$NON-NLS-1$ + public static String JolokiaDiscoveryListener_Description; + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + private Messages() { + } +} diff --git a/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/ServerConnectionDescriptor.java b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/ServerConnectionDescriptor.java new file mode 100644 index 000000000..01e9fd1bf --- /dev/null +++ b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/ServerConnectionDescriptor.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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.jolokia; + +import javax.management.remote.JMXServiceURL; + +import org.openjdk.jmc.rjmx.IConnectionDescriptor; +import org.openjdk.jmc.rjmx.IServerDescriptor; + +/** + * Describes the JVM and how to connect to it. + */ +public interface ServerConnectionDescriptor extends IServerDescriptor, IConnectionDescriptor { + String getPath(); + + JMXServiceURL serviceUrl(); +} diff --git a/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/messages.properties b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/messages.properties new file mode 100644 index 000000000..37881ad5b --- /dev/null +++ b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/messages.properties @@ -0,0 +1 @@ +JolokiaDiscoveryListener_Description=Uses Jolokia Discovery to report any active JVMs with Jolokia broadcasting diff --git a/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/preferences/JolokiaPreferencePage.java b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/preferences/JolokiaPreferencePage.java new file mode 100644 index 000000000..1f606eaf3 --- /dev/null +++ b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/preferences/JolokiaPreferencePage.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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.jolokia.preferences; + +import org.eclipse.jface.preference.BooleanFieldEditor; +import org.eclipse.jface.preference.FieldEditorPreferencePage; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPreferencePage; +import org.openjdk.jmc.jolokia.JmcJolokiaPlugin; + +public class JolokiaPreferencePage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage { + + public JolokiaPreferencePage() { + super(GRID); + setPreferenceStore(JmcJolokiaPlugin.getDefault().getPreferenceStore()); + setDescription(Messages.JolokiaPreferencePage_Description); + } + + public void createFieldEditors() { + addField(new BooleanFieldEditor(PreferenceConstants.P_SCAN, Messages.JolokiaPreferencePage_Label, + getFieldEditorParent())); + + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.IWorkbenchPreferencePage#init(org.eclipse.ui.IWorkbench) + */ + public void init(IWorkbench workbench) { + } + +} diff --git a/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/preferences/Messages.java b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/preferences/Messages.java new file mode 100644 index 000000000..bb526be23 --- /dev/null +++ b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/preferences/Messages.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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.jolokia.preferences; + +import org.eclipse.osgi.util.NLS; + +public class Messages extends NLS { + private static final String BUNDLE_NAME = "org.openjdk.jmc.jolokia.preferences.messages"; //$NON-NLS-1$ + public static String JolokiaPreferencePage_Description; + public static String JolokiaPreferencePage_Label; + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + private Messages() { + } +} diff --git a/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/preferences/PreferenceConstants.java b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/preferences/PreferenceConstants.java new file mode 100644 index 000000000..1d14211b6 --- /dev/null +++ b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/preferences/PreferenceConstants.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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.jolokia.preferences; + +/** + * Constant definitions for plug-in preferences. + */ +public interface PreferenceConstants { + + public static final String P_SCAN = "discoverJolokia"; //$NON-NLS-1$ + +} diff --git a/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/preferences/PreferenceInitializer.java b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/preferences/PreferenceInitializer.java new file mode 100644 index 000000000..f3c41d45a --- /dev/null +++ b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/preferences/PreferenceInitializer.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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.jolokia.preferences; + +import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer; +import org.eclipse.jface.preference.IPreferenceStore; +import org.openjdk.jmc.jolokia.JmcJolokiaPlugin; + +/** + * Class used to initialize default preference values. + */ +public class PreferenceInitializer extends AbstractPreferenceInitializer { + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer# + * initializeDefaultPreferences() + */ + public void initializeDefaultPreferences() { + IPreferenceStore store = JmcJolokiaPlugin.getDefault().getPreferenceStore(); + store.setDefault(PreferenceConstants.P_SCAN, false); + } + +} diff --git a/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/preferences/messages.properties b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/preferences/messages.properties new file mode 100644 index 000000000..d81f04ddf --- /dev/null +++ b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/preferences/messages.properties @@ -0,0 +1,2 @@ +JolokiaPreferencePage_Description=Discover Jolokia Agents\n\nhttps://jolokia.org/reference/html/protocol.html\#discovery\n\n +JolokiaPreferencePage_Label=&Discover Jolokia agents diff --git a/application/org.openjdk.jmc.kubernetes/.classpath b/application/org.openjdk.jmc.kubernetes/.classpath new file mode 100644 index 000000000..29127e464 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/.classpath @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/application/org.openjdk.jmc.kubernetes/.gitignore b/application/org.openjdk.jmc.kubernetes/.gitignore new file mode 100644 index 000000000..ae3c17260 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/.gitignore @@ -0,0 +1 @@ +/bin/ 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 000000000..68a0132f9 --- /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.0.0.qualifier +Automatic-Module-Name: org.openjdk.jmc.kubernetes +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Require-Bundle: org.openjdk.jmc.jolokia, + org.eclipse.core.runtime, + org.eclipse.ui, + org.openjdk.jmc.ui, + org.eclipse.swt, + org.jolokia.kubernetes.standalone;bundle-version="1.7.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 000000000..83ca6439d --- /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 000000000..25bd92d04 --- /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 000000000..3f2eaf104 --- /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 000000000..2d10e6cd9 --- /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 000000000..4c9a58904 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnection.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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.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 000000000..2ce8c4392 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnectionProvider.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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 000000000..b15f1e462 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesJmxConnector.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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 000000000..bef09cbf7 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/JmcKubernetesPlugin.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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 000000000..92dedd036 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/KubernetesDiscoveryListener.java @@ -0,0 +1,342 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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.util.AuthorizationHeaderParser; +import org.jolokia.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.Watch; +import io.fabric8.kubernetes.client.Watcher; +import io.fabric8.kubernetes.client.dsl.FilterWatchListMultiDeletable; +import io.fabric8.kubernetes.client.internal.KubeConfigUtils; +import io.fabric8.kubernetes.client.utils.Utils; + +/** + * 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); + + FilterWatchListMultiDeletable> 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 000000000..e57624274 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/KubernetesJvmDescriptor.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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 000000000..0f3a3b3aa --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/Messages.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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 000000000..ea0166360 --- /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 000000000..30e0b16c5 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/JmcKubernetesPreferenceForm.java @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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 000000000..ea41c79d8 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/KubernetesScanningParameters.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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 000000000..f4b2a4ff9 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/Messages.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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 000000000..1770dcf89 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/PreferenceConstants.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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 000000000..203c567b9 --- /dev/null +++ b/application/org.openjdk.jmc.kubernetes/src/main/java/org/openjdk/jmc/kubernetes/preferences/PreferenceInitializer.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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 000000000..44b18279a --- /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 7029bf0db..101786fcb 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -44,6 +44,7 @@ -XX:+FlightRecorder ${project.basedir}/../configuration + 1.7.2 org.openjdk.jmc.alert @@ -113,6 +114,8 @@ org.openjdk.jmc.ui.celleditors org.openjdk.jmc.updatesite.ide org.openjdk.jmc.updatesite.rcp + org.openjdk.jmc.jolokia + org.openjdk.jmc.kubernetes l10n tests diff --git a/application/tests/org.openjdk.jmc.jolokia.test/.classpath b/application/tests/org.openjdk.jmc.jolokia.test/.classpath new file mode 100644 index 000000000..4432907f4 --- /dev/null +++ b/application/tests/org.openjdk.jmc.jolokia.test/.classpath @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/application/tests/org.openjdk.jmc.jolokia.test/META-INF/MANIFEST.MF b/application/tests/org.openjdk.jmc.jolokia.test/META-INF/MANIFEST.MF new file mode 100644 index 000000000..043e657d1 --- /dev/null +++ b/application/tests/org.openjdk.jmc.jolokia.test/META-INF/MANIFEST.MF @@ -0,0 +1,17 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: RJMX Test +Bundle-SymbolicName: org.openjdk.jmc.jolokia.test;singleton:=true +Bundle-Version: 9.0.0.qualifier +Bundle-Vendor: Oracle Corporation +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +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, + org.eclipse.ui, + org.awaitility, + org.hamcrest;bundle-version="2.1.0" +Automatic-Module-Name: org.openjdk.jmc.jolokia.test diff --git a/application/tests/org.openjdk.jmc.jolokia.test/build.properties b/application/tests/org.openjdk.jmc.jolokia.test/build.properties new file mode 100644 index 000000000..a8bc8160a --- /dev/null +++ b/application/tests/org.openjdk.jmc.jolokia.test/build.properties @@ -0,0 +1,36 @@ +# +# Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2021, 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/tests/org.openjdk.jmc.jolokia.test/pom.xml b/application/tests/org.openjdk.jmc.jolokia.test/pom.xml new file mode 100644 index 000000000..6b35d110a --- /dev/null +++ b/application/tests/org.openjdk.jmc.jolokia.test/pom.xml @@ -0,0 +1,77 @@ + + + + 4.0.0 + + org.openjdk.jmc + missioncontrol.application.tests + ${revision}${changelist} + + org.openjdk.jmc.jolokia.test + eclipse-test-plugin + + + ${project.basedir}/../../../configuration + + + + + org.awaitility + awaitility + 4.0.0 + + + + + + + org.eclipse.tycho + tycho-surefire-plugin + ${tycho.version} + + ${test.includes} + + + -javaagent:${settings.localRepository}/org/jolokia/jolokia-jvm/${jolokia.version}/jolokia-jvm-${jolokia.version}.jar=port=0,discover=true + + + + + + diff --git a/application/tests/org.openjdk.jmc.jolokia.test/src/test/java/org/openjdk/jmc/jolokia/JolokiaTest.java b/application/tests/org.openjdk.jmc.jolokia.test/src/test/java/org/openjdk/jmc/jolokia/JolokiaTest.java new file mode 100644 index 000000000..d51bc9cc7 --- /dev/null +++ b/application/tests/org.openjdk.jmc.jolokia.test/src/test/java/org/openjdk/jmc/jolokia/JolokiaTest.java @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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.jolokia; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.time.Duration; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; + +import javax.management.Attribute; +import javax.management.AttributeNotFoundException; +import javax.management.InstanceNotFoundException; +import javax.management.IntrospectionException; +import javax.management.InvalidAttributeValueException; +import javax.management.MBeanAttributeInfo; +import javax.management.MBeanException; +import javax.management.MBeanServerConnection; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; +import javax.management.OperationsException; +import javax.management.ReflectionException; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXServiceURL; + +import org.awaitility.Awaitility; +import org.eclipse.core.runtime.preferences.InstanceScope; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.openjdk.jmc.common.IDescribable; +import org.openjdk.jmc.jolokia.preferences.PreferenceConstants; +import org.openjdk.jmc.rjmx.IConnectionDescriptor; +import org.openjdk.jmc.rjmx.IServerDescriptor; +import org.openjdk.jmc.rjmx.descriptorprovider.IDescriptorListener; + +/** + * I test that JMX connections done with JmcJolokiaJmxConnectionProvider are functional + */ +@SuppressWarnings("restriction") +public class JolokiaTest { + + static String jolokiaUrl; + + private static Set unsafeAttributes = new HashSet<>( + Arrays.asList("BootClassPath", "UsageThreshold", "UsageThresholdExceeded", "UsageThresholdCount", + "CollectionUsageThreshold", "CollectionUsageThresholdExceeded", "CollectionUsageThresholdCount")); + + private static JolokiaDiscoveryListener discoveryListener; + + private static MBeanServerConnection jolokiaConnection; + + @BeforeClass + public static void startServer() throws Exception { + // wait for Jolokia to be ready before commencing tests + Awaitility.await().atMost(Duration.ofSeconds(15))//Note: hard code property to avoid module dependency on agent + .until(() -> (jolokiaUrl = System.getProperty("jolokia.agent")) != null); + discoveryListener = new JolokiaDiscoveryListener(); + jolokiaConnection = getJolokiaMBeanConnector(); + + } + + @Test + public void testReadAttributesOverJolokia() throws MalformedURLException, IOException, OperationsException, + IntrospectionException, AttributeNotFoundException, ReflectionException, MBeanException { + for (ObjectName objectName : jolokiaConnection.queryNames(null, null)) { + for (MBeanAttributeInfo attributeInfo : getJolokiaMBeanConnector().getMBeanInfo(objectName) + .getAttributes()) { + if (!unsafeAttributes.contains(attributeInfo.getName())) { + System.out.println("Getting attribute " + objectName + "/" + attributeInfo.getName()); + getJolokiaMBeanConnector().getAttribute(objectName, attributeInfo.getName()); + } + } + } + + } + + @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 testWriteAttribute() + throws InstanceNotFoundException, AttributeNotFoundException, InvalidAttributeValueException, + MalformedObjectNameException, MBeanException, ReflectionException, MalformedURLException, IOException { + ObjectName objectName = new ObjectName("jolokia:type=Config"); + String attribute = "Debug"; + jolokiaConnection.setAttribute(objectName, new Attribute(attribute, true)); + Assert.assertEquals(true, jolokiaConnection.getAttribute(objectName, attribute)); + + } + + private static MBeanServerConnection getJolokiaMBeanConnector() throws IOException, MalformedURLException { + JMXConnector connector = new JmcJolokiaJmxConnectionProvider().newJMXConnector( + new JMXServiceURL(jolokiaUrl.replace("http", "service:jmx:jolokia")), Collections.emptyMap()); + connector.connect(); + MBeanServerConnection connection = connector.getMBeanServerConnection(); + return connection; + } + + @Test + public void testDiscover() { + + final AtomicInteger foundVms = new AtomicInteger(0); + + //Set config so that scanning takes place + InstanceScope.INSTANCE.getNode(JmcJolokiaPlugin.PLUGIN_ID).put(PreferenceConstants.P_SCAN, "true"); + + discoveryListener.addDescriptorListener(new IDescriptorListener() { + public void onDescriptorDetected( + IServerDescriptor serverDescriptor, String path, JMXServiceURL url, + IConnectionDescriptor connectionDescriptor, IDescribable provider) { + foundVms.getAndIncrement(); + } + + public void onDescriptorRemoved(String descriptorId) { + foundVms.getAndDecrement(); + } + + }); + // Test that at least one VM (the one running the test was discovered) + Awaitility.await().atMost(Duration.ofSeconds(5)).until(() -> foundVms.get() > 0); + } + + @AfterClass + public static void stopServer() throws Exception { + discoveryListener.shutdown(); + } + +} diff --git a/application/tests/org.openjdk.jmc.kubernetes.test/.classpath b/application/tests/org.openjdk.jmc.kubernetes.test/.classpath new file mode 100644 index 000000000..cb04fe10d --- /dev/null +++ b/application/tests/org.openjdk.jmc.kubernetes.test/.classpath @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/application/tests/org.openjdk.jmc.kubernetes.test/META-INF/MANIFEST.MF b/application/tests/org.openjdk.jmc.kubernetes.test/META-INF/MANIFEST.MF new file mode 100644 index 000000000..5913e76e3 --- /dev/null +++ b/application/tests/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.0.0.qualifier +Import-Package: io.fabric8.kubernetes.api.model +Bundle-Vendor: Oracle Corporation +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +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/tests/org.openjdk.jmc.kubernetes.test/build.properties b/application/tests/org.openjdk.jmc.kubernetes.test/build.properties new file mode 100644 index 000000000..a8bc8160a --- /dev/null +++ b/application/tests/org.openjdk.jmc.kubernetes.test/build.properties @@ -0,0 +1,36 @@ +# +# Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2021, 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/tests/org.openjdk.jmc.kubernetes.test/pom.xml b/application/tests/org.openjdk.jmc.kubernetes.test/pom.xml new file mode 100644 index 000000000..b3e832633 --- /dev/null +++ b/application/tests/org.openjdk.jmc.kubernetes.test/pom.xml @@ -0,0 +1,58 @@ + + + + 4.0.0 + + org.openjdk.jmc + missioncontrol.application.tests + ${revision}${changelist} + + org.openjdk.jmc.kubernetes.test + eclipse-test-plugin + + + ${project.basedir}/../../../configuration + + + + + org.awaitility + awaitility + 4.0.0 + + + diff --git a/application/tests/org.openjdk.jmc.kubernetes.test/src/test/java/org/openjdk/jmc/kubernetes/JmcKubernetesTest.java b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/java/org/openjdk/jmc/kubernetes/JmcKubernetesTest.java new file mode 100644 index 000000000..fa346077f --- /dev/null +++ b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/java/org/openjdk/jmc/kubernetes/JmcKubernetesTest.java @@ -0,0 +1,313 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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.IConnectionDescriptor; +import org.openjdk.jmc.rjmx.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/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-attribute.json b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-attribute.json new file mode 100644 index 000000000..4230121a7 --- /dev/null +++ b/application/tests/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/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-basic-auth-secret.json b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-basic-auth-secret.json new file mode 100644 index 000000000..b5bb351b7 --- /dev/null +++ b/application/tests/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/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-exec.json b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-exec.json new file mode 100644 index 000000000..5bd35a69f --- /dev/null +++ b/application/tests/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/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-properties-secret.json b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-properties-secret.json new file mode 100644 index 000000000..e12bbd16c --- /dev/null +++ b/application/tests/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/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-version.json b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/jolokia-version.json new file mode 100644 index 000000000..6299d0407 --- /dev/null +++ b/application/tests/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/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/memory-list.json b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/memory-list.json new file mode 100644 index 000000000..46d117f2e --- /dev/null +++ b/application/tests/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/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/mock-kube-config.yml b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/mock-kube-config.yml new file mode 100644 index 000000000..f222f471a --- /dev/null +++ b/application/tests/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/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/pod.json b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/pod.json new file mode 100644 index 000000000..77a61a3e3 --- /dev/null +++ b/application/tests/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/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/pods.json b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/pods.json new file mode 100644 index 000000000..114edd364 --- /dev/null +++ b/application/tests/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/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/runtime-list.json b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/runtime-list.json new file mode 100644 index 000000000..c096d78c5 --- /dev/null +++ b/application/tests/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/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/secrets.json b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/secrets.json new file mode 100644 index 000000000..ac68f94b5 --- /dev/null +++ b/application/tests/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/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/system-attributes.json b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/__files/system-attributes.json new file mode 100644 index 000000000..dc2ff5543 --- /dev/null +++ b/application/tests/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/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-exec.json b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-exec.json new file mode 100644 index 000000000..6bb03ecec --- /dev/null +++ b/application/tests/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/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-list-memory.json b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-list-memory.json new file mode 100644 index 000000000..e4170948c --- /dev/null +++ b/application/tests/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/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-list-runtime.json b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-list-runtime.json new file mode 100644 index 000000000..f936e7eb0 --- /dev/null +++ b/application/tests/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/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-probe.json b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-probe.json new file mode 100644 index 000000000..5b26da423 --- /dev/null +++ b/application/tests/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/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-probe2.json b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-probe2.json new file mode 100644 index 000000000..98670016f --- /dev/null +++ b/application/tests/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/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-read-attribute.json b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jolokia-read-attribute.json new file mode 100644 index 000000000..1584e3b4a --- /dev/null +++ b/application/tests/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/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jvm-properties.json b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/jvm-properties.json new file mode 100644 index 000000000..5b26da423 --- /dev/null +++ b/application/tests/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/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/kube-config.json b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/kube-config.json new file mode 100644 index 000000000..418a88763 --- /dev/null +++ b/application/tests/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/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/pod.json b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/pod.json new file mode 100644 index 000000000..ebcbd7c5e --- /dev/null +++ b/application/tests/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/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/pods.json b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/pods.json new file mode 100644 index 000000000..061fb0f53 --- /dev/null +++ b/application/tests/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/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/secrets.json b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/secrets.json new file mode 100644 index 000000000..113bb91e1 --- /dev/null +++ b/application/tests/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/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/system-attributes.json b/application/tests/org.openjdk.jmc.kubernetes.test/src/test/resources/mappings/system-attributes.json new file mode 100644 index 000000000..e3d1147b2 --- /dev/null +++ b/application/tests/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/tests/pom.xml b/application/tests/pom.xml index b17b9ceea..da5f8cb80 100644 --- a/application/tests/pom.xml +++ b/application/tests/pom.xml @@ -54,6 +54,8 @@ org.openjdk.jmc.rjmx.test org.openjdk.jmc.ui.common.test org.openjdk.jmc.ui.test + org.openjdk.jmc.jolokia.test + org.openjdk.jmc.kubernetes.test **/Test*.java,**/*Test.java,**/*TestCase.java diff --git a/releng/platform-definitions/platform-definition-2022-09/platform-definition-2022-09.target b/releng/platform-definitions/platform-definition-2022-09/platform-definition-2022-09.target index 7e62ff50c..12ebe1c5f 100644 --- a/releng/platform-definitions/platform-definition-2022-09/platform-definition-2022-09.target +++ b/releng/platform-definitions/platform-definition-2022-09/platform-definition-2022-09.target @@ -50,6 +50,10 @@ + + + + diff --git a/releng/platform-definitions/platform-definition-2022-12/platform-definition-2022-12.target b/releng/platform-definitions/platform-definition-2022-12/platform-definition-2022-12.target index 0508bf318..752b82c64 100644 --- a/releng/platform-definitions/platform-definition-2022-12/platform-definition-2022-12.target +++ b/releng/platform-definitions/platform-definition-2022-12/platform-definition-2022-12.target @@ -50,6 +50,10 @@ + + + + diff --git a/releng/platform-definitions/platform-definition-2023-03/platform-definition-2023-03.target b/releng/platform-definitions/platform-definition-2023-03/platform-definition-2023-03.target index e323304f4..209732d96 100644 --- a/releng/platform-definitions/platform-definition-2023-03/platform-definition-2023-03.target +++ b/releng/platform-definitions/platform-definition-2023-03/platform-definition-2023-03.target @@ -45,6 +45,10 @@ + + + + diff --git a/releng/platform-definitions/platform-definition-2023-09/platform-definition-2023-09.target b/releng/platform-definitions/platform-definition-2023-09/platform-definition-2023-09.target index fc382b0b9..ba2187584 100644 --- a/releng/platform-definitions/platform-definition-2023-09/platform-definition-2023-09.target +++ b/releng/platform-definitions/platform-definition-2023-09/platform-definition-2023-09.target @@ -45,6 +45,10 @@ + + + + diff --git a/releng/third-party/pom.xml b/releng/third-party/pom.xml index d20fa15fc..80b8e61d1 100644 --- a/releng/third-party/pom.xml +++ b/releng/third-party/pom.xml @@ -59,6 +59,8 @@ 2.0.0 10.0.12 1.3.6 + 1.7.2 + 2.27.2 0.0.1-rc3 6.0.0 @@ -135,6 +137,22 @@ org.apache.aries.spifly:org.apache.aries.spifly.dynamic.bundle:${spifly.version} + + org.jolokia:jolokia-jmx-adapter:jar:standalone:${jolokia.version} + false + + + org.jolokia:jolokia-kubernetes:jar:standalone:${jolokia.version} + false + + + org.jolokia:jolokia-jvm:${jolokia.version} + false + + + com.github.tomakehurst:wiremock-standalone:${wiremock.version} + false + io.github.bric3.fireplace:fireplace-swing:${fireplace.version} true