|  | 
|  | 1 | +/* | 
|  | 2 | + * Copyright The OpenTelemetry Authors | 
|  | 3 | + * SPDX-License-Identifier: Apache-2.0 | 
|  | 4 | + */ | 
|  | 5 | + | 
|  | 6 | +package io.opentelemetry.contrib.jmxscraper; | 
|  | 7 | + | 
|  | 8 | +import static org.assertj.core.api.Assertions.assertThat; | 
|  | 9 | + | 
|  | 10 | +import java.util.function.Function; | 
|  | 11 | +import org.junit.jupiter.api.AfterAll; | 
|  | 12 | +import org.junit.jupiter.api.BeforeAll; | 
|  | 13 | +import org.junit.jupiter.api.Test; | 
|  | 14 | +import org.slf4j.Logger; | 
|  | 15 | +import org.slf4j.LoggerFactory; | 
|  | 16 | +import org.testcontainers.containers.GenericContainer; | 
|  | 17 | +import org.testcontainers.containers.Network; | 
|  | 18 | +import org.testcontainers.containers.output.Slf4jLogConsumer; | 
|  | 19 | + | 
|  | 20 | +/** | 
|  | 21 | + * Tests all supported ways to connect to remote JMX interface. This indirectly tests | 
|  | 22 | + * JmxConnectionBuilder and relies on containers to minimize the JMX/RMI network complications which | 
|  | 23 | + * are not NAT-friendly. | 
|  | 24 | + */ | 
|  | 25 | +public class JmxConnectionTest { | 
|  | 26 | + | 
|  | 27 | +  // OTLP endpoint is not used in test mode, but still has to be provided | 
|  | 28 | +  private static final String DUMMY_OTLP_ENDPOINT = "http://dummy-otlp-endpoint:8080/"; | 
|  | 29 | +  private static final String SCRAPER_BASE_IMAGE = "openjdk:8u342-jre-slim"; | 
|  | 30 | + | 
|  | 31 | +  private static final int JMX_PORT = 9999; | 
|  | 32 | +  private static final String APP_HOST = "app"; | 
|  | 33 | + | 
|  | 34 | +  private static final Logger jmxScraperLogger = LoggerFactory.getLogger("JmxScraperContainer"); | 
|  | 35 | +  private static final Logger appLogger = LoggerFactory.getLogger("TestAppContainer"); | 
|  | 36 | + | 
|  | 37 | +  private static Network network; | 
|  | 38 | + | 
|  | 39 | +  @BeforeAll | 
|  | 40 | +  static void beforeAll() { | 
|  | 41 | +    network = Network.newNetwork(); | 
|  | 42 | +  } | 
|  | 43 | + | 
|  | 44 | +  @AfterAll | 
|  | 45 | +  static void afterAll() { | 
|  | 46 | +    network.close(); | 
|  | 47 | +  } | 
|  | 48 | + | 
|  | 49 | +  @Test | 
|  | 50 | +  void connectionError() { | 
|  | 51 | +    try (JmxScraperContainer scraper = scraperContainer().withRmiServiceUrl("unknown_host", 1234)) { | 
|  | 52 | +      scraper.start(); | 
|  | 53 | +      waitTerminated(scraper); | 
|  | 54 | +      checkConnectionLogs(scraper, /* expectedOk= */ false); | 
|  | 55 | +    } | 
|  | 56 | +  } | 
|  | 57 | + | 
|  | 58 | +  @Test | 
|  | 59 | +  void connectNoAuth() { | 
|  | 60 | +    connectionTest( | 
|  | 61 | +        app -> app.withJmxPort(JMX_PORT), scraper -> scraper.withRmiServiceUrl(APP_HOST, JMX_PORT)); | 
|  | 62 | +  } | 
|  | 63 | + | 
|  | 64 | +  @Test | 
|  | 65 | +  void userPassword() { | 
|  | 66 | +    String login = "user"; | 
|  | 67 | +    String pwd = "t0p!Secret"; | 
|  | 68 | +    connectionTest( | 
|  | 69 | +        app -> app.withJmxPort(JMX_PORT).withUserAuth(login, pwd), | 
|  | 70 | +        scraper -> scraper.withRmiServiceUrl(APP_HOST, JMX_PORT).withUser(login).withPassword(pwd)); | 
|  | 71 | +  } | 
|  | 72 | + | 
|  | 73 | +  private static void connectionTest( | 
|  | 74 | +      Function<TestAppContainer, TestAppContainer> customizeApp, | 
|  | 75 | +      Function<JmxScraperContainer, JmxScraperContainer> customizeScraper) { | 
|  | 76 | +    try (TestAppContainer app = customizeApp.apply(appContainer())) { | 
|  | 77 | +      app.start(); | 
|  | 78 | +      try (JmxScraperContainer scraper = customizeScraper.apply(scraperContainer())) { | 
|  | 79 | +        scraper.start(); | 
|  | 80 | +        waitTerminated(scraper); | 
|  | 81 | +        checkConnectionLogs(scraper, /* expectedOk= */ true); | 
|  | 82 | +      } | 
|  | 83 | +    } | 
|  | 84 | +  } | 
|  | 85 | + | 
|  | 86 | +  private static void checkConnectionLogs(JmxScraperContainer scraper, boolean expectedOk) { | 
|  | 87 | + | 
|  | 88 | +    String[] logLines = scraper.getLogs().split("\n"); | 
|  | 89 | +    String lastLine = logLines[logLines.length - 1]; | 
|  | 90 | + | 
|  | 91 | +    if (expectedOk) { | 
|  | 92 | +      assertThat(lastLine) | 
|  | 93 | +          .describedAs("should log connection success") | 
|  | 94 | +          .endsWith("JMX connection test OK"); | 
|  | 95 | +    } else { | 
|  | 96 | +      assertThat(lastLine) | 
|  | 97 | +          .describedAs("should log connection failure") | 
|  | 98 | +          .endsWith("JMX connection test ERROR"); | 
|  | 99 | +    } | 
|  | 100 | +  } | 
|  | 101 | + | 
|  | 102 | +  private static void waitTerminated(GenericContainer<?> container) { | 
|  | 103 | +    int retries = 10; | 
|  | 104 | +    while (retries > 0 && container.isRunning()) { | 
|  | 105 | +      retries--; | 
|  | 106 | +      try { | 
|  | 107 | +        Thread.sleep(100); | 
|  | 108 | +      } catch (InterruptedException e) { | 
|  | 109 | +        throw new RuntimeException(e); | 
|  | 110 | +      } | 
|  | 111 | +    } | 
|  | 112 | +    assertThat(retries) | 
|  | 113 | +        .describedAs("container should stop when testing connection") | 
|  | 114 | +        .isNotEqualTo(0); | 
|  | 115 | +  } | 
|  | 116 | + | 
|  | 117 | +  private static JmxScraperContainer scraperContainer() { | 
|  | 118 | +    return new JmxScraperContainer(DUMMY_OTLP_ENDPOINT, SCRAPER_BASE_IMAGE) | 
|  | 119 | +        .withLogConsumer(new Slf4jLogConsumer(jmxScraperLogger)) | 
|  | 120 | +        .withNetwork(network) | 
|  | 121 | +        // mandatory to have a target system even if we don't collect metrics | 
|  | 122 | +        .withTargetSystem("jvm") | 
|  | 123 | +        // we are only testing JMX connection here | 
|  | 124 | +        .withTestJmx(); | 
|  | 125 | +  } | 
|  | 126 | + | 
|  | 127 | +  private static TestAppContainer appContainer() { | 
|  | 128 | +    return new TestAppContainer() | 
|  | 129 | +        .withLogConsumer(new Slf4jLogConsumer(appLogger)) | 
|  | 130 | +        .withNetwork(network) | 
|  | 131 | +        .withNetworkAliases(APP_HOST); | 
|  | 132 | +  } | 
|  | 133 | +} | 
0 commit comments