diff --git a/src/main/java/io/cryostat/discovery/BuiltInDiscovery.java b/src/main/java/io/cryostat/discovery/BuiltInDiscovery.java index 628b297b73..aa1e709ff2 100644 --- a/src/main/java/io/cryostat/discovery/BuiltInDiscovery.java +++ b/src/main/java/io/cryostat/discovery/BuiltInDiscovery.java @@ -41,20 +41,16 @@ import java.util.Iterator; import java.util.Map; import java.util.Set; -import java.util.SortedSet; import java.util.UUID; import java.util.function.Consumer; -import java.util.stream.Stream; import io.cryostat.core.log.Logger; import io.cryostat.messaging.notifications.NotificationFactory; import io.cryostat.platform.PlatformClient; import io.cryostat.platform.TargetDiscoveryEvent; import io.cryostat.platform.discovery.EnvironmentNode; -import io.cryostat.platform.internal.CustomTargetPlatformClient; import io.cryostat.platform.internal.PlatformDetectionStrategy; -import dagger.Lazy; import io.vertx.core.AbstractVerticle; import io.vertx.core.Promise; @@ -65,22 +61,19 @@ public class BuiltInDiscovery extends AbstractVerticle implements Consumer> selectedStrategies; private final Set> unselectedStrategies; - private final Lazy customTargets; private final Set enabledClients = new HashSet<>(); private final NotificationFactory notificationFactory; private final Logger logger; BuiltInDiscovery( DiscoveryStorage storage, - SortedSet> selectedStrategies, - SortedSet> unselectedStrategies, - Lazy customTargets, + Set> selectedStrategies, + Set> unselectedStrategies, NotificationFactory notificationFactory, Logger logger) { this.storage = storage; this.selectedStrategies = selectedStrategies; this.unselectedStrategies = unselectedStrategies; - this.customTargets = customTargets; this.notificationFactory = notificationFactory; this.logger = logger; } @@ -98,12 +91,8 @@ public void start(Promise start) { .map(PluginInfo::getId) .ifPresent(storage::deregister)); - Stream.concat( - // ensure custom targets is always available regardless of other - // configurations - Stream.of(customTargets.get()), - selectedStrategies.stream() - .map(PlatformDetectionStrategy::getPlatformClient)) + selectedStrategies.stream() + .map(PlatformDetectionStrategy::getPlatformClient) .distinct() .forEach( platform -> { diff --git a/src/main/java/io/cryostat/discovery/DiscoveryModule.java b/src/main/java/io/cryostat/discovery/DiscoveryModule.java index 8947ec6edc..0f979ac1b9 100644 --- a/src/main/java/io/cryostat/discovery/DiscoveryModule.java +++ b/src/main/java/io/cryostat/discovery/DiscoveryModule.java @@ -39,7 +39,6 @@ import java.time.Duration; import java.util.Set; -import java.util.SortedSet; import javax.inject.Named; import javax.inject.Singleton; @@ -53,7 +52,6 @@ import io.cryostat.messaging.notifications.NotificationFactory; import io.cryostat.platform.PlatformModule; import io.cryostat.platform.discovery.AbstractNode; -import io.cryostat.platform.internal.CustomTargetPlatformClient; import io.cryostat.platform.internal.PlatformDetectionStrategy; import io.cryostat.recordings.JvmIdHelper; import io.cryostat.rules.MatchExpressionEvaluator; @@ -119,19 +117,13 @@ static DiscoveryStorage provideDiscoveryStorage( static BuiltInDiscovery provideBuiltInDiscovery( DiscoveryStorage storage, @Named(PlatformModule.SELECTED_PLATFORMS) - SortedSet> selectedStrategies, + Set> selectedStrategies, @Named(PlatformModule.UNSELECTED_PLATFORMS) - SortedSet> unselectedStrategies, - Lazy customTargets, + Set> unselectedStrategies, NotificationFactory notificationFactory, Logger logger) { return new BuiltInDiscovery( - storage, - selectedStrategies, - unselectedStrategies, - customTargets, - notificationFactory, - logger); + storage, selectedStrategies, unselectedStrategies, notificationFactory, logger); } @Provides diff --git a/src/main/java/io/cryostat/platform/PlatformModule.java b/src/main/java/io/cryostat/platform/PlatformModule.java index 841300a0d7..9b6fae68bc 100644 --- a/src/main/java/io/cryostat/platform/PlatformModule.java +++ b/src/main/java/io/cryostat/platform/PlatformModule.java @@ -38,13 +38,14 @@ package io.cryostat.platform; import java.util.Arrays; +import java.util.HashSet; import java.util.List; import java.util.NoSuchElementException; import java.util.Objects; import java.util.Set; -import java.util.SortedSet; -import java.util.TreeSet; import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.inject.Named; import javax.inject.Singleton; @@ -52,14 +53,12 @@ import io.cryostat.configuration.Variables; import io.cryostat.core.log.Logger; import io.cryostat.core.sys.Environment; -import io.cryostat.discovery.DiscoveryStorage; import io.cryostat.net.AuthManager; import io.cryostat.platform.discovery.PlatformDiscoveryModule; -import io.cryostat.platform.internal.CustomTargetPlatformClient; +import io.cryostat.platform.internal.CustomTargetPlatformStrategy; import io.cryostat.platform.internal.PlatformDetectionStrategy; import io.cryostat.platform.internal.PlatformStrategyModule; -import dagger.Lazy; import dagger.Module; import dagger.Provides; @@ -69,13 +68,6 @@ public abstract class PlatformModule { public static final String SELECTED_PLATFORMS = "SELECTED_PLATFORMS"; public static final String UNSELECTED_PLATFORMS = "UNSELECTED_PLATFORMS"; - @Provides - @Singleton - static CustomTargetPlatformClient provideCustomTargetPlatformClient( - Lazy storage) { - return new CustomTargetPlatformClient(storage); - } - @Provides @Singleton static AuthManager provideAuthManager( @@ -111,11 +103,10 @@ static AuthManager provideAuthManager( @Provides @Singleton @Named(SELECTED_PLATFORMS) - static SortedSet> provideSelectedPlatformStrategies( - Set> platformStrategies, Environment env) { - // reverse sort, higher priorities should be earlier in the stream - SortedSet> selectedStrategies = - new TreeSet<>((a, b) -> Integer.compare(b.getPriority(), a.getPriority())); + static Set> provideSelectedPlatformStrategies( + CustomTargetPlatformStrategy customTargets, + Set> platformStrategies, + Environment env) { Predicate> fn; if (env.hasEnv(Variables.PLATFORM_STRATEGY_ENV_VAR)) { List platforms = @@ -126,22 +117,17 @@ static SortedSet> provideSelectedPlatformStrategies } else { fn = PlatformDetectionStrategy::isAvailable; } - for (PlatformDetectionStrategy s : platformStrategies) { - if (fn.test(s)) { - selectedStrategies.add(s); - } - } - return selectedStrategies; + return Stream.concat(Stream.of(customTargets), platformStrategies.stream().filter(fn)) + .collect(Collectors.toSet()); } @Provides @Singleton @Named(UNSELECTED_PLATFORMS) - static SortedSet> provideUnselectedPlatformStrategies( - @Named(SELECTED_PLATFORMS) SortedSet> selectedStrategies, + static Set> provideUnselectedPlatformStrategies( + @Named(SELECTED_PLATFORMS) Set> selectedStrategies, Set> platformStrategies) { - SortedSet> unselected = - new TreeSet<>((a, b) -> Integer.compare(b.getPriority(), a.getPriority())); + Set> unselected = new HashSet<>(); unselected.addAll(platformStrategies); unselected.removeAll(selectedStrategies); return unselected; @@ -150,7 +136,7 @@ static SortedSet> provideUnselectedPlatformStrategi @Provides @Singleton static PlatformDetectionStrategy providePlatformStrategy( - @Named(SELECTED_PLATFORMS) SortedSet> selectedStrategies, + @Named(SELECTED_PLATFORMS) Set> selectedStrategies, Set> strategies) { return selectedStrategies.stream() .findFirst() @@ -161,11 +147,6 @@ static PlatformDetectionStrategy providePlatformStrategy( "No selected platforms found. Available platforms:" + " %s", strategies.stream() - .sorted( - (a, b) -> - Integer.compare( - b.getPriority(), - a.getPriority())) .map(s -> s.getClass().getCanonicalName()) .toList()))); } diff --git a/src/main/java/io/cryostat/platform/internal/CustomTargetPlatformStrategy.java b/src/main/java/io/cryostat/platform/internal/CustomTargetPlatformStrategy.java new file mode 100644 index 0000000000..65ed3264c7 --- /dev/null +++ b/src/main/java/io/cryostat/platform/internal/CustomTargetPlatformStrategy.java @@ -0,0 +1,76 @@ +/* + * Copyright The Cryostat Authors + * + * 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. + */ +package io.cryostat.platform.internal; + +import io.cryostat.core.log.Logger; +import io.cryostat.net.AuthManager; + +import dagger.Lazy; + +public class CustomTargetPlatformStrategy + implements PlatformDetectionStrategy { + + private final Logger logger; + private final Lazy authMgr; + private final Lazy client; + + CustomTargetPlatformStrategy( + Logger logger, + Lazy authMgr, + Lazy client) { + this.logger = logger; + this.authMgr = authMgr; + this.client = client; + } + + @Override + public boolean isAvailable() { + return true; + } + + @Override + public CustomTargetPlatformClient getPlatformClient() { + logger.info("Selected Default Platform Strategy"); + return client.get(); + } + + @Override + public AuthManager getAuthManager() { + return authMgr.get(); + } +} diff --git a/src/main/java/io/cryostat/platform/internal/DefaultPlatformStrategy.java b/src/main/java/io/cryostat/platform/internal/DefaultPlatformStrategy.java index ddb2ed2f63..49b37b3354 100644 --- a/src/main/java/io/cryostat/platform/internal/DefaultPlatformStrategy.java +++ b/src/main/java/io/cryostat/platform/internal/DefaultPlatformStrategy.java @@ -58,11 +58,6 @@ class DefaultPlatformStrategy implements PlatformDetectionStrategy { - int PRIORITY_DEFAULT = 0; - int PRIORITY_PLATFORM = 50; - - int getPriority(); - boolean isAvailable(); T getPlatformClient(); diff --git a/src/main/java/io/cryostat/platform/internal/PlatformStrategyModule.java b/src/main/java/io/cryostat/platform/internal/PlatformStrategyModule.java index 1271116d20..fcb9781bcd 100644 --- a/src/main/java/io/cryostat/platform/internal/PlatformStrategyModule.java +++ b/src/main/java/io/cryostat/platform/internal/PlatformStrategyModule.java @@ -39,12 +39,14 @@ import java.util.Set; +import javax.inject.Singleton; + import io.cryostat.core.log.Logger; import io.cryostat.core.net.JFRConnectionToolkit; import io.cryostat.core.net.discovery.JvmDiscoveryClient; import io.cryostat.core.sys.Environment; import io.cryostat.core.sys.FileSystem; -import io.cryostat.net.NetworkResolver; +import io.cryostat.discovery.DiscoveryStorage; import io.cryostat.net.NoopAuthManager; import io.cryostat.net.openshift.OpenShiftAuthManager; @@ -59,23 +61,70 @@ public abstract class PlatformStrategyModule { @Provides - @ElementsIntoSet - static Set> providePlatformDetectionStrategies( + @Singleton + static CustomTargetPlatformClient provideCustomTargetPlatformClient( + Lazy storage) { + return new CustomTargetPlatformClient(storage); + } + + @Provides + @Singleton + static CustomTargetPlatformStrategy provideCustomTargetPlatformStrategy( + Logger logger, + Lazy noopAuthManager, + Lazy client) { + return new CustomTargetPlatformStrategy(logger, noopAuthManager, client); + } + + @Provides + @Singleton + static OpenShiftPlatformStrategy provideOpenShiftPlatformStrategy( + Logger logger, + Lazy authManager, + Lazy connectionToolkit, + Environment env, + FileSystem fs) { + return new OpenShiftPlatformStrategy(logger, authManager, connectionToolkit, env, fs); + } + + @Provides + @Singleton + static KubeApiPlatformStrategy provideKubeApiPlatformStrategy( Logger logger, - Lazy openShiftAuthManager, Lazy noopAuthManager, Lazy connectionToolkit, - Vertx vertx, - Gson gson, - NetworkResolver resolver, Environment env, FileSystem fs) { - return Set.of( - new OpenShiftPlatformStrategy( - logger, openShiftAuthManager, connectionToolkit, env, fs), - new KubeApiPlatformStrategy(logger, noopAuthManager, connectionToolkit, env, fs), - new PodmanPlatformStrategy(logger, noopAuthManager, vertx, gson, fs), - new DefaultPlatformStrategy( - logger, noopAuthManager, () -> new JvmDiscoveryClient(logger))); + return new KubeApiPlatformStrategy(logger, noopAuthManager, connectionToolkit, env, fs); + } + + @Provides + @Singleton + static PodmanPlatformStrategy providePodmanPlatformStrategy( + Logger logger, + Lazy noopAuthManager, + Lazy vertx, + Gson gson, + FileSystem fs) { + return new PodmanPlatformStrategy(logger, noopAuthManager, vertx, gson, fs); + } + + @Provides + @Singleton + static DefaultPlatformStrategy provideDefaultPlatformStrategy( + Logger logger, Lazy noopAuthManager) { + return new DefaultPlatformStrategy( + logger, noopAuthManager, () -> new JvmDiscoveryClient(logger)); + } + + @Provides + @ElementsIntoSet + static Set> providePlatformDetectionStrategies( + CustomTargetPlatformStrategy customTargets, + OpenShiftPlatformStrategy openShift, + KubeApiPlatformStrategy kubeApi, + PodmanPlatformStrategy podman, + DefaultPlatformStrategy jdp) { + return Set.of(customTargets, openShift, kubeApi, podman, jdp); } } diff --git a/src/main/java/io/cryostat/platform/internal/PodmanPlatformClient.java b/src/main/java/io/cryostat/platform/internal/PodmanPlatformClient.java index be325ae42f..63c2e71f02 100644 --- a/src/main/java/io/cryostat/platform/internal/PodmanPlatformClient.java +++ b/src/main/java/io/cryostat/platform/internal/PodmanPlatformClient.java @@ -69,6 +69,7 @@ import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; +import dagger.Lazy; import io.vertx.core.Vertx; import io.vertx.core.http.HttpMethod; import io.vertx.core.net.SocketAddress; @@ -82,8 +83,8 @@ public class PodmanPlatformClient extends AbstractPlatformClient { public static final String REALM = "Podman"; public static final String CRYOSTAT_LABEL = "io.cryostat.connectUrl"; - private final Vertx vertx; - private final WebClient webClient; + private final Lazy vertx; + private WebClient webClient; private final Gson gson; private final SocketAddress podmanSocket; private final Logger logger; @@ -91,9 +92,8 @@ public class PodmanPlatformClient extends AbstractPlatformClient { private final CopyOnWriteArrayList containers = new CopyOnWriteArrayList<>(); - PodmanPlatformClient(Vertx vertx, SocketAddress podmanSocket, Gson gson, Logger logger) { + PodmanPlatformClient(Lazy vertx, SocketAddress podmanSocket, Gson gson, Logger logger) { this.vertx = vertx; - this.webClient = WebClient.create(vertx); this.podmanSocket = podmanSocket; this.gson = gson; this.logger = logger; @@ -103,16 +103,18 @@ public class PodmanPlatformClient extends AbstractPlatformClient { public void start() throws Exception { super.start(); queryContainers(); + logger.info("native transport? {}", vertx.get().isNativeTransportEnabled()); this.timerId = - vertx.setPeriodic( - // TODO make this configurable - 10_000, unused -> queryContainers()); + vertx.get() + .setPeriodic( + // TODO make this configurable + 10_000, unused -> queryContainers()); } @Override public void stop() throws Exception { super.stop(); - vertx.cancelTimer(timerId); + vertx.get().cancelTimer(timerId); } @Override @@ -156,37 +158,41 @@ private void queryContainers() { private void doPodmanRequest(Consumer> successHandler) { URI requestPath = URI.create("http://d/v3.0.0/libpod/containers/json"); - vertx.executeBlocking( - promise -> - webClient - .request( - HttpMethod.GET, - podmanSocket, - 80, - "localhost", - requestPath.toString()) - .addQueryParam( - "filters", - gson.toJson(Map.of("label", List.of(CRYOSTAT_LABEL)))) - // TODO make this configurable? - .timeout(5_000L) - .as(BodyCodec.string()) - .send( - ar -> { - if (ar.failed()) { - Throwable t = ar.cause(); - logger.error("Podman API request failed", t); - promise.fail(t); - return; - } - HttpResponse response = ar.result(); - successHandler.accept( - gson.fromJson( - response.body(), - new TypeToken< - List>() {})); - promise.complete(); - })); + vertx.get() + .executeBlocking( + promise -> + webClient() + .request( + HttpMethod.GET, + podmanSocket, + 80, + "localhost", + requestPath.toString()) + .addQueryParam( + "filters", + gson.toJson( + Map.of("label", List.of(CRYOSTAT_LABEL)))) + // TODO make this configurable? + .timeout(5_000L) + .as(BodyCodec.string()) + .send( + ar -> { + if (ar.failed()) { + Throwable t = ar.cause(); + logger.error( + "Podman API request failed", t); + promise.fail(t); + return; + } + HttpResponse response = ar.result(); + successHandler.accept( + gson.fromJson( + response.body(), + new TypeToken< + List< + ContainerSpec>>() {})); + promise.complete(); + })); } private ServiceRef convert(ContainerSpec desc) { @@ -262,6 +268,13 @@ public EnvironmentNode getDiscoveryTree() { return new EnvironmentNode(REALM, BaseNodeType.REALM, Collections.emptyMap(), children); } + private WebClient webClient() { + if (this.webClient == null) { + this.webClient = WebClient.create(this.vertx.get()); + } + return webClient; + } + static record PortSpec( long container_port, String host_ip, long host_port, String protocol, long range) {} diff --git a/src/main/java/io/cryostat/platform/internal/PodmanPlatformStrategy.java b/src/main/java/io/cryostat/platform/internal/PodmanPlatformStrategy.java index c5603a0a48..4571f5f2df 100644 --- a/src/main/java/io/cryostat/platform/internal/PodmanPlatformStrategy.java +++ b/src/main/java/io/cryostat/platform/internal/PodmanPlatformStrategy.java @@ -51,14 +51,14 @@ class PodmanPlatformStrategy implements PlatformDetectionStrategy authMgr; - private final Vertx vertx; + private final Lazy vertx; private final Gson gson; private final FileSystem fs; PodmanPlatformStrategy( Logger logger, Lazy authMgr, - Vertx vertx, + Lazy vertx, Gson gson, FileSystem fs) { this.logger = logger; @@ -68,11 +68,6 @@ class PodmanPlatformStrategy implements PlatformDetectionStrategy