From f74498f20bcab5a65c9cadaf022bcd440f1ab93a Mon Sep 17 00:00:00 2001 From: Marco Boi Date: Thu, 7 Mar 2019 10:37:49 +0100 Subject: [PATCH] fix for colon split for windows paths Signed-off-by: Marco Boi --- .../io/prometheus/jmx/AgentParameters.java | 26 ++ .../java/io/prometheus/jmx/JavaAgent.java | 104 ++++--- .../java/io/prometheus/jmx/JavaAgentIT.java | 254 +++++++++++------- 3 files changed, 246 insertions(+), 138 deletions(-) create mode 100644 jmx_prometheus_javaagent/src/main/java/io/prometheus/jmx/AgentParameters.java diff --git a/jmx_prometheus_javaagent/src/main/java/io/prometheus/jmx/AgentParameters.java b/jmx_prometheus_javaagent/src/main/java/io/prometheus/jmx/AgentParameters.java new file mode 100644 index 00000000..78c337e0 --- /dev/null +++ b/jmx_prometheus_javaagent/src/main/java/io/prometheus/jmx/AgentParameters.java @@ -0,0 +1,26 @@ +package io.prometheus.jmx; + +class AgentParameters { + + private final String hostName; + private final int port; + private final String settingsFilePath; + + AgentParameters(String hostName, int port, String settingsFilePath) { + this.hostName = hostName; + this.port = port; + this.settingsFilePath = settingsFilePath; + } + + String getHost() { + return hostName; + } + + int getPort() { + return port; + } + + String getSettingsFilePath() { + return settingsFilePath; + } +} diff --git a/jmx_prometheus_javaagent/src/main/java/io/prometheus/jmx/JavaAgent.java b/jmx_prometheus_javaagent/src/main/java/io/prometheus/jmx/JavaAgent.java index 6356e61a..b62e359f 100644 --- a/jmx_prometheus_javaagent/src/main/java/io/prometheus/jmx/JavaAgent.java +++ b/jmx_prometheus_javaagent/src/main/java/io/prometheus/jmx/JavaAgent.java @@ -3,6 +3,8 @@ import java.io.File; import java.lang.instrument.Instrumentation; import java.net.InetSocketAddress; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import io.prometheus.client.CollectorRegistry; import io.prometheus.client.exporter.HTTPServer; @@ -10,47 +12,63 @@ public class JavaAgent { - static HTTPServer server; - - public static void agentmain(String agentArgument, Instrumentation instrumentation) throws Exception { - premain(agentArgument, instrumentation); - } - - public static void premain(String agentArgument, Instrumentation instrumentation) throws Exception { - // Bind to all interfaces by default (this includes IPv6). - String host = "0.0.0.0"; - - // If we have IPv6 address in square brackets, extract it first and then - // remove it from arguments to prevent confusion from too many colons. - Integer indexOfClosingSquareBracket = agentArgument.indexOf("]:"); - if (indexOfClosingSquareBracket >= 0) { - host = agentArgument.substring(0, indexOfClosingSquareBracket + 1); - agentArgument = agentArgument.substring(indexOfClosingSquareBracket + 2); - } - - String[] args = agentArgument.split(":"); - if (args.length < 2 || args.length > 3) { - System.err.println("Usage: -javaagent:/path/to/JavaAgent.jar=[host:]:"); - System.exit(1); - } - - int port; - String file; - InetSocketAddress socket; - - if (args.length == 3) { - port = Integer.parseInt(args[1]); - socket = new InetSocketAddress(args[0], port); - file = args[2]; - } else { - port = Integer.parseInt(args[0]); - socket = new InetSocketAddress(host, port); - file = args[1]; - } - - new BuildInfoCollector().register(); - new JmxCollector(new File(file)).register(); - DefaultExports.initialize(); - server = new HTTPServer(socket, CollectorRegistry.defaultRegistry, true); - } + static final String DEFAULT_HOST = "0.0.0.0"; + + private static final String IPV6_REGEX = "((\\[.*\\]+):)"; + private static final String DNS_IPV4_REGEX = "(((\\w+\\.)+\\w+):)"; + private static final String PORT_REGEX = "(([0-9]+):)"; + private static final String PATH_REGEX = "(.*)"; + + private static final Pattern IPV6_PATTERN = Pattern.compile("^" + IPV6_REGEX + PORT_REGEX + PATH_REGEX); + private static final Pattern PORT_PATTERN = Pattern.compile("^" + PORT_REGEX + PATH_REGEX); + private static final Pattern DNS_IPV4_PATTERN = Pattern.compile("^" + DNS_IPV4_REGEX + PORT_REGEX + PATH_REGEX); + + static HTTPServer server; + + public static void agentmain(String agentArgument, Instrumentation instrumentation) throws Exception { + premain(agentArgument, instrumentation); + } + + public static void premain(String agentArgument, Instrumentation instrumentation) throws Exception { + + AgentParameters params = parseParameters(agentArgument); + InetSocketAddress socket = new InetSocketAddress(params.getHost(), params.getPort()); + new BuildInfoCollector().register(); + new JmxCollector(new File(params.getSettingsFilePath())).register(); + DefaultExports.initialize(); + server = new HTTPServer(socket, CollectorRegistry.defaultRegistry, true); + } + + static AgentParameters parseParameters(String agentArguments) { + + Matcher ipv6Matcher = IPV6_PATTERN.matcher(agentArguments); + Matcher portMatcher = PORT_PATTERN.matcher(agentArguments); + Matcher dnsMatcher = DNS_IPV4_PATTERN.matcher(agentArguments); + + try { + if (ipv6Matcher.find()) { + + String host = ipv6Matcher.group(2); + int port = Integer.parseInt(ipv6Matcher.group(4)); + String settingsFile = ipv6Matcher.group(5); + return new AgentParameters(host, port, settingsFile); + } else if (portMatcher.find()) { + + int port = Integer.parseInt(portMatcher.group(2)); + String settingsFile = portMatcher.group(3); + return new AgentParameters(DEFAULT_HOST, port, settingsFile); + } else if (dnsMatcher.find()) { + + String host = dnsMatcher.group(2); + int port = Integer.parseInt(dnsMatcher.group(5)); + String settingsFile = dnsMatcher.group(6); + return new AgentParameters(host, port, settingsFile); + } else { + throw new IllegalArgumentException("Could not parse arguments: " + agentArguments); + } + } catch (Exception e) { + throw new IllegalArgumentException( + "Usage: -javaagent:/path/to/JavaAgent.jar=[host]::", e); + } + } } diff --git a/jmx_prometheus_javaagent/src/test/java/io/prometheus/jmx/JavaAgentIT.java b/jmx_prometheus_javaagent/src/test/java/io/prometheus/jmx/JavaAgentIT.java index 17052c07..f58d9e76 100644 --- a/jmx_prometheus_javaagent/src/test/java/io/prometheus/jmx/JavaAgentIT.java +++ b/jmx_prometheus_javaagent/src/test/java/io/prometheus/jmx/JavaAgentIT.java @@ -14,102 +14,166 @@ import java.util.Collections; import java.util.List; +import org.junit.Assert; import org.junit.Test; public class JavaAgentIT { - private List getClassloaderUrls() { - return getClassloaderUrls(getClass().getClassLoader()); - } - - private static List getClassloaderUrls(ClassLoader classLoader) { - if (classLoader == null) { - return Collections.emptyList(); - } - if (!(classLoader instanceof URLClassLoader)) { - return getClassloaderUrls(classLoader.getParent()); - } - URLClassLoader u = (URLClassLoader) classLoader; - List result = new ArrayList(Arrays.asList(u.getURLs())); - result.addAll(getClassloaderUrls(u.getParent())); - return result; - } - - private String buildClasspath() { - StringBuilder sb = new StringBuilder(); - for (URL url : getClassloaderUrls()) { - if (!url.getProtocol().equals("file")) { - continue; - } - if (sb.length() != 0) { - sb.append(java.io.File.pathSeparatorChar); - } - sb.append(url.getPath()); - } - return sb.toString(); - } - - @Test - public void agentLoads() throws IOException, InterruptedException { - // If not starting the testcase via Maven, set the buildDirectory and finalName system properties manually. - final String buildDirectory = (String) System.getProperties().get("buildDirectory"); - final String finalName = (String) System.getProperties().get("finalName"); - final int port = Integer.parseInt((String) System.getProperties().get("it.port")); - final String config = resolveRelativePathToResource("test.yml"); - final String javaagent = "-javaagent:" + buildDirectory + "/" + finalName + ".jar=" + port + ":" + config; - - final String javaHome = System.getenv("JAVA_HOME"); - final String java; - if (javaHome != null && javaHome.equals("")) { - java = javaHome + "/bin/java"; - } else { - java = "java"; - } - - final Process app = new ProcessBuilder() - .command(java, javaagent, "-cp", buildClasspath(), "io.prometheus.jmx.TestApplication") - .start(); - try { - // Wait for application to start - app.getInputStream().read(); - - InputStream stream = new URL("http://localhost:" + port + "/metrics").openStream(); - BufferedReader contents = new BufferedReader(new InputStreamReader(stream)); - boolean found = false; - while (!found) { - String line = contents.readLine(); - if (line == null) { - break; - } - if (line.contains("jmx_scrape_duration_seconds")) { - found = true; - } - } - - assertThat("Expected metric not found", found); - - // Tell application to stop - app.getOutputStream().write('\n'); - try { - app.getOutputStream().flush(); - } catch (IOException ignored) { - } - } finally { - final int exitcode = app.waitFor(); - // Log any errors printed - int len; - byte[] buffer = new byte[100]; - while ((len = app.getErrorStream().read(buffer)) != -1) { - System.out.write(buffer, 0, len); - } - - assertThat("Application did not exit cleanly", exitcode == 0); - } - } - - //trying to avoid the occurrence of any : in the windows path - private String resolveRelativePathToResource(String resource) { - final String configwk = new File(getClass().getClassLoader().getResource(resource).getFile()).getAbsolutePath(); - final File workingDir = new File(new File(".").getAbsolutePath()); - return "." + configwk.replace(workingDir.getParentFile().getAbsolutePath(), ""); - } + private List getClassloaderUrls() { + return getClassloaderUrls(getClass().getClassLoader()); + } + + private static List getClassloaderUrls(ClassLoader classLoader) { + if (classLoader == null) { + return Collections.emptyList(); + } + if (!(classLoader instanceof URLClassLoader)) { + return getClassloaderUrls(classLoader.getParent()); + } + URLClassLoader u = (URLClassLoader) classLoader; + List result = new ArrayList(Arrays.asList(u.getURLs())); + result.addAll(getClassloaderUrls(u.getParent())); + return result; + } + + private String buildClasspath() { + StringBuilder sb = new StringBuilder(); + for (URL url : getClassloaderUrls()) { + if (!url.getProtocol().equals("file")) { + continue; + } + if (sb.length() != 0) { + sb.append(java.io.File.pathSeparatorChar); + } + sb.append(url.getPath()); + } + return sb.toString(); + } + + @Test + public void agentLoads() throws IOException, InterruptedException { + // If not starting the testcase via Maven, set the buildDirectory and finalName system properties manually. + final String buildDirectory = (String) System.getProperties().get("buildDirectory"); + final String finalName = (String) System.getProperties().get("finalName"); + final int port = Integer.parseInt((String) System.getProperties().get("it.port")); + final String config = resolveRelativePathToResource("test.yml"); + final String javaagent = "-javaagent:" + buildDirectory + "/" + finalName + ".jar=" + port + ":" + config; + + final String javaHome = System.getenv("JAVA_HOME"); + final String java; + if (javaHome != null && javaHome.equals("")) { + java = javaHome + "/bin/java"; + } else { + java = "java"; + } + + final Process app = new ProcessBuilder() + .command(java, javaagent, "-cp", buildClasspath(), "io.prometheus.jmx.TestApplication") + .start(); + try { + // Wait for application to start + app.getInputStream().read(); + + InputStream stream = new URL("http://localhost:" + port + "/metrics").openStream(); + BufferedReader contents = new BufferedReader(new InputStreamReader(stream)); + boolean found = false; + while (!found) { + String line = contents.readLine(); + if (line == null) { + break; + } + if (line.contains("jmx_scrape_duration_seconds")) { + found = true; + } + } + + assertThat("Expected metric not found", found); + + // Tell application to stop + app.getOutputStream().write('\n'); + try { + app.getOutputStream().flush(); + } catch (IOException ignored) { + } + } finally { + final int exitcode = app.waitFor(); + // Log any errors printed + int len; + byte[] buffer = new byte[100]; + while ((len = app.getErrorStream().read(buffer)) != -1) { + System.out.write(buffer, 0, len); + } + + assertThat("Application did not exit cleanly", exitcode == 0); + } + } + + @Test + public void shouldParseWhenIPv6() { + String host = "[2001:0db8:0000:0000:0000:ff00:0042:8329]"; + int port = 1234; + String settingsFile = "/home/user/data/monitoring.yml"; + + AgentParameters params = JavaAgent.parseParameters(host + ":" + port + ":" + settingsFile); + + Assert.assertEquals(host, params.getHost()); + Assert.assertEquals(port, params.getPort()); + Assert.assertEquals(settingsFile, params.getSettingsFilePath()); + } + + @Test + public void shouldParseWhenDns() { + String host = "my.service.com"; + int port = 1234; + String settingsFile = "/home/user/data/monitoring.yml"; + + AgentParameters params = JavaAgent.parseParameters(host + ":" + port + ":" + settingsFile); + + Assert.assertEquals(host, params.getHost()); + Assert.assertEquals(port, params.getPort()); + Assert.assertEquals(settingsFile, params.getSettingsFilePath()); + } + + @Test + public void shouldParseWhenIp4() { + String host = "127.0.0.1"; + int port = 1234; + String settingsFile = "/home/user/data/monitoring.yml"; + + AgentParameters params = JavaAgent.parseParameters(host + ":" + port + ":" + settingsFile); + + Assert.assertEquals(host, params.getHost()); + Assert.assertEquals(port, params.getPort()); + Assert.assertEquals(settingsFile, params.getSettingsFilePath()); + } + + @Test + public void shouldParseWhenNoAddress() { + int port = 1234; + String settingsFile = "/home/user/data/monitoring.yml"; + + AgentParameters params = JavaAgent.parseParameters(port + ":" + settingsFile); + + Assert.assertEquals(JavaAgent.DEFAULT_HOST, params.getHost()); + Assert.assertEquals(port, params.getPort()); + Assert.assertEquals(settingsFile, params.getSettingsFilePath()); + } + + @Test + public void shouldParseWhenWindowsPath() { + int port = 1234; + String settingsFile = "C:\\Program Files\\monitoring.yml"; + + AgentParameters params = JavaAgent.parseParameters(port + ":" + settingsFile); + + Assert.assertEquals(JavaAgent.DEFAULT_HOST, params.getHost()); + Assert.assertEquals(port, params.getPort()); + Assert.assertEquals(settingsFile, params.getSettingsFilePath()); + } + + //trying to avoid the occurrence of any : in the windows path + private String resolveRelativePathToResource(String resource) { + final String configwk = new File(getClass().getClassLoader().getResource(resource).getFile()).getAbsolutePath(); + final File workingDir = new File(new File(".").getAbsolutePath()); + return "." + configwk.replace(workingDir.getParentFile().getAbsolutePath(), ""); + } }