From 5cfd2fcf18171618e42d96c413da4377bea1a867 Mon Sep 17 00:00:00 2001 From: Andrew Azores Date: Thu, 9 Nov 2023 11:27:24 -0500 Subject: [PATCH] feat(plugin): send additional env payload to plugins on registration (#1520) --- .../api/v2/DiscoveryRegistrationHandler.java | 25 +++++-- .../internal/PlatformDetectionStrategy.java | 12 ++++ .../v2/DiscoveryRegistrationHandlerTest.java | 72 ++++++++++++++++++- src/test/java/itest/DiscoveryPluginIT.java | 6 ++ 4 files changed, 108 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/cryostat/net/web/http/api/v2/DiscoveryRegistrationHandler.java b/src/main/java/io/cryostat/net/web/http/api/v2/DiscoveryRegistrationHandler.java index 4cfc9b0e08..896c771ef5 100644 --- a/src/main/java/io/cryostat/net/web/http/api/v2/DiscoveryRegistrationHandler.java +++ b/src/main/java/io/cryostat/net/web/http/api/v2/DiscoveryRegistrationHandler.java @@ -20,6 +20,7 @@ import java.net.URISyntaxException; import java.net.URL; import java.util.EnumSet; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -34,6 +35,7 @@ import io.cryostat.MainModule; import io.cryostat.configuration.CredentialsManager; import io.cryostat.core.log.Logger; +import io.cryostat.core.sys.Environment; import io.cryostat.discovery.DiscoveryStorage; import io.cryostat.discovery.PluginInfo; import io.cryostat.discovery.RegistrationException; @@ -43,6 +45,8 @@ import io.cryostat.net.web.WebServer; import io.cryostat.net.web.http.HttpMimeType; import io.cryostat.net.web.http.api.ApiVersion; +import io.cryostat.platform.PlatformModule; +import io.cryostat.platform.internal.PlatformDetectionStrategy; import io.cryostat.util.StringUtil; import com.google.gson.Gson; @@ -55,11 +59,13 @@ import io.vertx.core.json.JsonObject; import org.apache.commons.lang3.StringUtils; -class DiscoveryRegistrationHandler extends AbstractV2RequestHandler> { +class DiscoveryRegistrationHandler extends AbstractV2RequestHandler> { static final String PATH = "discovery"; + private final Environment env; private final DiscoveryStorage storage; private final Lazy webServer; + private final Set> selectedStrategies; private final DiscoveryJwtHelper jwtFactory; private final Function uuidFromString; private final Logger logger; @@ -68,15 +74,20 @@ class DiscoveryRegistrationHandler extends AbstractV2RequestHandler webServer, + @Named(PlatformModule.SELECTED_PLATFORMS) + Set> selectedStrategies, DiscoveryJwtHelper jwt, @Named(MainModule.UUID_FROM_STRING) Function uuidFromString, Gson gson, Logger logger) { super(auth, credentialsManager, gson); + this.env = env; this.storage = storage; this.webServer = webServer; + this.selectedStrategies = selectedStrategies; this.jwtFactory = jwt; this.uuidFromString = uuidFromString; this.logger = logger; @@ -118,7 +129,7 @@ public boolean isAsync() { } @Override - public IntermediateResponse> handle(RequestParameters params) + public IntermediateResponse> handle(RequestParameters params) throws Exception { String pluginId, realm, priorToken; URI callbackUri; @@ -175,9 +186,15 @@ public IntermediateResponse> handle(RequestParameters params realm, address, AbstractDiscoveryJwtConsumingHandler.getResourceUri(hostUrl, pluginId)); - return new IntermediateResponse>() + Map mergedEnv = new HashMap<>(); + // FIXME currently only the OpenShiftPlatformStrategy provides any entries for the env map, + // but in the future if any more strategies also provide entries then the order here may be + // undefined and the map entries may collide and be overwritten. There should be some + // prefixing scheme to prevent collisions. + selectedStrategies.forEach(s -> mergedEnv.putAll(s.environment(env))); + return new IntermediateResponse>() .statusCode(201) .addHeader(HttpHeaders.LOCATION, String.format("%s/%s", path(), pluginId)) - .body(Map.of("id", pluginId, "token", token)); + .body(Map.of("id", pluginId, "token", token, "env", mergedEnv)); } } diff --git a/src/main/java/io/cryostat/platform/internal/PlatformDetectionStrategy.java b/src/main/java/io/cryostat/platform/internal/PlatformDetectionStrategy.java index 7bd1c6f2dd..edb73edde7 100644 --- a/src/main/java/io/cryostat/platform/internal/PlatformDetectionStrategy.java +++ b/src/main/java/io/cryostat/platform/internal/PlatformDetectionStrategy.java @@ -15,6 +15,10 @@ */ package io.cryostat.platform.internal; +import java.util.HashMap; +import java.util.Map; + +import io.cryostat.core.sys.Environment; import io.cryostat.net.AuthManager; import io.cryostat.platform.PlatformClient; @@ -24,4 +28,12 @@ public interface PlatformDetectionStrategy { T getPlatformClient(); AuthManager getAuthManager(); + + default Map environment(Environment env) { + Map map = new HashMap<>(); + if (env.hasEnv("INSIGHTS_PROXY")) { + map.put("INSIGHTS_SVC", env.getEnv("INSIGHTS_PROXY")); + } + return map; + } } diff --git a/src/test/java/io/cryostat/net/web/http/api/v2/DiscoveryRegistrationHandlerTest.java b/src/test/java/io/cryostat/net/web/http/api/v2/DiscoveryRegistrationHandlerTest.java index c1ccb29915..a477ab268b 100644 --- a/src/test/java/io/cryostat/net/web/http/api/v2/DiscoveryRegistrationHandlerTest.java +++ b/src/test/java/io/cryostat/net/web/http/api/v2/DiscoveryRegistrationHandlerTest.java @@ -27,6 +27,7 @@ import io.cryostat.MainModule; import io.cryostat.configuration.CredentialsManager; 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.net.security.ResourceAction; @@ -34,6 +35,8 @@ import io.cryostat.net.web.WebServer; import io.cryostat.net.web.http.HttpMimeType; import io.cryostat.net.web.http.api.ApiVersion; +import io.cryostat.platform.PlatformClient; +import io.cryostat.platform.internal.PlatformDetectionStrategy; import com.google.gson.Gson; import io.vertx.core.MultiMap; @@ -55,9 +58,10 @@ @ExtendWith(MockitoExtension.class) class DiscoveryRegistrationHandlerTest { - AbstractV2RequestHandler> handler; + AbstractV2RequestHandler> handler; @Mock AuthManager auth; @Mock CredentialsManager credentialsManager; + @Mock Environment env; @Mock DiscoveryStorage storage; @Mock WebServer webServer; @Mock DiscoveryJwtHelper jwt; @@ -70,8 +74,10 @@ void setup() { new DiscoveryRegistrationHandler( auth, credentialsManager, + env, storage, () -> webServer, + Set.of(new AllEnvPlatformStrategy(), new FakePlatformStrategy()), jwt, UUID::fromString, gson, @@ -161,6 +167,8 @@ void shouldRegisterWithStorageAndSendResponse() throws Exception { Mockito.when(storage.register(Mockito.anyString(), Mockito.any(URI.class))) .thenReturn(id); + Mockito.when(env.getEnv()).thenReturn(Map.of("CRYOSTAT", "hello", "TEST", "true")); + Mockito.when(requestParams.getBody()) .thenReturn( gson.toJson( @@ -185,14 +193,28 @@ void shouldRegisterWithStorageAndSendResponse() throws Exception { Mockito.any(URI.class))) .thenReturn(token); - IntermediateResponse> resp = handler.handle(requestParams); + IntermediateResponse> resp = handler.handle(requestParams); MatcherAssert.assertThat(resp.getStatusCode(), Matchers.equalTo(201)); MatcherAssert.assertThat( resp.getHeaders(), Matchers.equalTo(Map.of(HttpHeaders.LOCATION, "/api/v2.2/discovery/" + id))); MatcherAssert.assertThat( - resp.getBody(), Matchers.equalTo(Map.of("token", token, "id", id.toString()))); + resp.getBody(), + Matchers.equalTo( + Map.of( + "token", + token, + "id", + id.toString(), + "env", + Map.of( + "CRYOSTAT", + "hello", + "TEST", + "true", + "HELLO", + "WORLD")))); Mockito.verify(storage) .register( @@ -200,4 +222,48 @@ void shouldRegisterWithStorageAndSendResponse() throws Exception { Mockito.eq(URI.create("http://example.com/callback"))); } } + + static class AllEnvPlatformStrategy implements PlatformDetectionStrategy { + @Override + public Map environment(Environment env) { + return env.getEnv(); + } + + @Override + public boolean isAvailable() { + return true; + } + + @Override + public PlatformClient getPlatformClient() { + throw new UnsupportedOperationException("Unimplemented method 'getPlatformClient'"); + } + + @Override + public AuthManager getAuthManager() { + throw new UnsupportedOperationException("Unimplemented method 'getAuthManager'"); + } + } + + static class FakePlatformStrategy implements PlatformDetectionStrategy { + @Override + public Map environment(Environment env) { + return Map.of("HELLO", "WORLD"); + } + + @Override + public boolean isAvailable() { + return true; + } + + @Override + public PlatformClient getPlatformClient() { + throw new UnsupportedOperationException("Unimplemented method 'getPlatformClient'"); + } + + @Override + public AuthManager getAuthManager() { + throw new UnsupportedOperationException("Unimplemented method 'getAuthManager'"); + } + } } diff --git a/src/test/java/itest/DiscoveryPluginIT.java b/src/test/java/itest/DiscoveryPluginIT.java index 7bfcf2cf60..60870b7fc8 100644 --- a/src/test/java/itest/DiscoveryPluginIT.java +++ b/src/test/java/itest/DiscoveryPluginIT.java @@ -21,6 +21,7 @@ import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; +import java.util.stream.Collectors; import io.vertx.core.buffer.Buffer; import io.vertx.core.json.JsonArray; @@ -40,6 +41,7 @@ class DiscoveryPluginIT extends StandardSelfTest { final URI callback = URI.create("http://localhost:8181/"); private static volatile String id; private static volatile String token; + private static volatile Map env; @Test @Order(1) @@ -60,8 +62,12 @@ void shouldBeAbleToRegister() throws InterruptedException, ExecutionException { JsonObject info = resp.getJsonObject("data").getJsonObject("result"); DiscoveryPluginIT.id = info.getString("id"); DiscoveryPluginIT.token = info.getString("token"); + DiscoveryPluginIT.env = + info.getJsonObject("env").getMap().entrySet().stream() + .collect(Collectors.toMap(k -> k.toString(), v -> v.toString())); MatcherAssert.assertThat(id, Matchers.not(Matchers.emptyOrNullString())); MatcherAssert.assertThat(token, Matchers.not(Matchers.emptyOrNullString())); + MatcherAssert.assertThat(env, Matchers.is(Matchers.equalTo(Map.of()))); } @Test