From c14b12f891456e92c9e8d9f602741350c4807049 Mon Sep 17 00:00:00 2001 From: Andrew Azores Date: Wed, 23 Dec 2020 13:31:27 -0500 Subject: [PATCH] Add basic external container discovery test Add test and utils to run an external container within the test pod, check that ContainerJFR discovers it, and then tear down --- .../itest/ConnectToExternalTargetsIT.java | 116 ++++++++++++ src/test/java/itest/PodmanUtils.java | 171 ++++++++++++++++++ 2 files changed, 287 insertions(+) create mode 100644 src/test/java/itest/ConnectToExternalTargetsIT.java create mode 100644 src/test/java/itest/PodmanUtils.java diff --git a/src/test/java/itest/ConnectToExternalTargetsIT.java b/src/test/java/itest/ConnectToExternalTargetsIT.java new file mode 100644 index 0000000000..e05ae4d8c1 --- /dev/null +++ b/src/test/java/itest/ConnectToExternalTargetsIT.java @@ -0,0 +1,116 @@ +/*- + * #%L + * Container JFR + * %% + * Copyright (C) 2020 Red Hat, Inc. + * %% + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or data + * (collectively the "Software"), free of charge and under any and all copyright + * rights in the Software, and any and all patent rights owned or freely + * licensable by each licensor hereunder covering either (i) the unmodified + * Software as contributed to or provided by such licensor, or (ii) the Larger + * Works (as defined below), to deal in both + * + * (a) the Software, and + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software (each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * The above copyright notice and either this complete permission notice or at + * a minimum a reference to the UPL must be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * #L% + */ +package itest; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledIf; +import io.vertx.core.json.JsonArray; + +@EnabledIf( + value = "itest.PodmanUtils#podmanAvailable", + disabledReason = "Podman not available on test host") +class ConnectToExternalTargetsIT extends ITestBase { + + static List CONTAINERS = new ArrayList<>(); + + @BeforeAll + static void setup() throws Exception { + Set specs = Set.of("quay.io/andrewazores/vertx-fib-demo:0.3.0"); + for (String spec : specs) { + CONTAINERS.add(PodmanUtils.run(spec)); + } + for (String id : CONTAINERS) { + PodmanUtils.waitForContainerState(id, "running"); + } + Thread.sleep(7_500L); // wait for JDP to discover new containers + } + + @AfterAll + static void cleanup() throws Exception { + for (String id : CONTAINERS) { + PodmanUtils.rm(id); + } + } + + @Test + void testOtherContainerFound() throws Exception { + CompletableFuture resp = new CompletableFuture<>(); + webClient + .get("/api/v1/targets") + .send( + ar -> { + if (assertRequestStatus(ar, resp)) { + resp.complete(ar.result().bodyAsJsonArray()); + } + }); + JsonArray listResp = resp.get(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS); + Set> actual = new HashSet<>(listResp.getList()); + // ordering may not be guaranteed so use a Set, but there should be no duplicates and so + // size should not change + MatcherAssert.assertThat(actual.size(), Matchers.equalTo(listResp.size())); + Set> expected = + Set.of( + Map.of( + "connectUrl", + "service:jmx:rmi:///jndi/rmi://container-jfr-itests:9091/jmxrmi", + "alias", + "com.redhat.rhjmc.containerjfr.ContainerJfr"), + Map.of( + "connectUrl", + "service:jmx:rmi:///jndi/rmi://container-jfr-itests:9093/jmxrmi", + "alias", + "es.andrewazor.demo.Main")); + MatcherAssert.assertThat(actual, Matchers.equalTo(expected)); + } +} diff --git a/src/test/java/itest/PodmanUtils.java b/src/test/java/itest/PodmanUtils.java new file mode 100644 index 0000000000..cdb1bf5d0c --- /dev/null +++ b/src/test/java/itest/PodmanUtils.java @@ -0,0 +1,171 @@ +/*- + * #%L + * Container JFR + * %% + * Copyright (C) 2020 Red Hat, Inc. + * %% + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or data + * (collectively the "Software"), free of charge and under any and all copyright + * rights in the Software, and any and all patent rights owned or freely + * licensable by each licensor hereunder covering either (i) the unmodified + * Software as contributed to or provided by such licensor, or (ii) the Larger + * Works (as defined below), to deal in both + * + * (a) the Software, and + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software (each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * The above copyright notice and either this complete permission notice or at + * a minimum a reference to the UPL must be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * #L% + */ +package itest; + +import java.io.IOException; +import java.time.Duration; +import java.util.Objects; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.io.IOUtils; + +abstract class PodmanUtils { + + // this can take some time if an image needs to be pulled + static final Duration POD_STARTUP_TIMEOUT = Duration.ofSeconds(60); + + static boolean podmanAvailable() { + Process proc = null; + try { + proc = new ProcessBuilder().command("podman", "--help").start(); + proc.waitFor(POD_STARTUP_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS); + return proc.exitValue() == 0; + } catch (Exception e) { + e.printStackTrace(); + return false; + } finally { + if (proc != null) { + proc.destroyForcibly(); + } + } + } + + static String run(String imageSpec) throws Exception { + Process proc = null; + try { + String[] args = { + "podman", + "run", + "--quiet", + "--pod=container-jfr-itests", + "--detach", + "--rm", + imageSpec + }; + System.out.println(String.join(" ", args)); + proc = new ProcessBuilder().command(args).start(); + proc.waitFor(POD_STARTUP_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS); + String out = IOUtils.toString(proc.getInputStream(), "UTF-8").trim(); + String err = IOUtils.toString(proc.getErrorStream(), "UTF-8").trim(); + System.out.println(out); + System.out.println(err); + if (proc.exitValue() != 0) { + throw new PodmanException(proc.exitValue(), out, err); + } + return out; + } finally { + if (proc != null) { + proc.destroyForcibly(); + } + } + } + + static void waitForContainerState(String id, String state) throws Exception { + long start = System.currentTimeMillis(); + long elapsed = 0; + state = String.format("\"%s\"", Objects.requireNonNull(state)); + while (elapsed < POD_STARTUP_TIMEOUT.toMillis()) { + Process proc = null; + try { + String[] args = { + "podman", "container", "inspect", "--format=\"{{.State.Status}}\"", id + }; + System.out.println(String.join(" ", args)); + proc = new ProcessBuilder().command(args).start(); + proc.waitFor(POD_STARTUP_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS); + String out = IOUtils.toString(proc.getInputStream(), "UTF-8").trim(); + String err = IOUtils.toString(proc.getErrorStream(), "UTF-8").trim(); + if (proc.exitValue() != 0) { + throw new PodmanException(proc.exitValue(), out, err); + } + if (state.trim().equalsIgnoreCase(out)) { + break; + } + System.out.println(out); + long now = System.currentTimeMillis(); + long delta = now - start; + elapsed += delta; + Thread.sleep(5_000L); + } finally { + if (proc != null) { + proc.destroyForcibly(); + } + } + } + if (elapsed >= POD_STARTUP_TIMEOUT.toMillis()) { + throw new PodmanException( + String.format( + "Container %s did not reach %s state in %ds", + id, state, POD_STARTUP_TIMEOUT.toSeconds())); + } + } + + static String rm(String id) throws Exception { + Process proc = null; + try { + String[] args = {"podman", "rm", "--force", id}; + System.out.println(String.join(" ", args)); + proc = new ProcessBuilder().command(args).start(); + proc.waitFor(POD_STARTUP_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS); + String out = IOUtils.toString(proc.getInputStream(), "UTF-8").trim(); + String err = IOUtils.toString(proc.getErrorStream(), "UTF-8").trim(); + if (proc.exitValue() != 0) { + throw new PodmanException(proc.exitValue(), out, err); + } + return out; + } finally { + if (proc != null) { + proc.destroyForcibly(); + } + } + } + + static class PodmanException extends IOException { + PodmanException(int status, String out, String err) { + super(String.format("Exit status %d: out: %s - err: %s", status, out, err)); + } + + PodmanException(String reason) { + super(reason); + } + } +}