diff --git a/README.md b/README.md index 4236c3559c..d1761391aa 100644 --- a/README.md +++ b/README.md @@ -194,7 +194,14 @@ service endpoints and expose all discovered services as potential targets. This is runtime dynamic, allowing `cryostat` to discover new services which come online after `cryostat`, or to detect when known services disappear later. This requires the `cryostat` pod to have authorization to list services -within its own namespace. +within its own namespace. By default this will look for `Endpoints` objects +with ports named `jfr-jmx` or numbered `9091`. This behaviour can be overridden +using the environment variables `CRYOSTAT_DISCOVERY_K8S_PORT_NAMES` and +`CRYOSTAT_DISCOVERY_K8S_PORT_NUMBERS` respectively. Both of these accept +comma-separated lists as values. Any observed `Endpoints` object with a name +in the given list or a number in the given list will be taken as a connectable +target application. To set the names list to the empty list use `-`. To set the +numbers list to the empty list use `0`. The second discovery mechanism is JDP (Java Discovery Protocol). This relies on target JVMs being configured with the JVM flags to enable JDP and requires the diff --git a/src/main/java/io/cryostat/configuration/Variables.java b/src/main/java/io/cryostat/configuration/Variables.java index 49c056642c..b269a60344 100644 --- a/src/main/java/io/cryostat/configuration/Variables.java +++ b/src/main/java/io/cryostat/configuration/Variables.java @@ -47,6 +47,8 @@ private Variables() {} public static final String DISABLE_BUILTIN_DISCOVERY = "CRYOSTAT_DISABLE_BUILTIN_DISCOVERY"; public static final String DISCOVERY_PING_PERIOD_MS = "CRYOSTAT_DISCOVERY_PING_PERIOD"; public static final String K8S_NAMESPACES = "CRYOSTAT_K8S_NAMESPACES"; + public static final String K8S_PORT_NAMES = "CRYOSTAT_DISCOVERY_K8S_PORT_NAMES"; + public static final String K8S_PORT_NUMBERS = "CRYOSTAT_DISCOVERY_K8S_PORT_NUMBERS"; public static final String VERTX_POOL_SIZE = "CRYOSTAT_VERTX_POOL_SIZE"; // webserver configuration diff --git a/src/main/java/io/cryostat/platform/internal/KubeApiPlatformClient.java b/src/main/java/io/cryostat/platform/internal/KubeApiPlatformClient.java index 348bff56d5..ed9dba2fac 100644 --- a/src/main/java/io/cryostat/platform/internal/KubeApiPlatformClient.java +++ b/src/main/java/io/cryostat/platform/internal/KubeApiPlatformClient.java @@ -33,7 +33,6 @@ import javax.management.remote.JMXServiceURL; import io.cryostat.core.log.Logger; -import io.cryostat.core.net.JFRConnectionToolkit; import io.cryostat.core.net.discovery.JvmDiscoveryClient.EventKind; import io.cryostat.core.sys.Environment; import io.cryostat.platform.AbstractPlatformClient; @@ -44,7 +43,6 @@ import io.cryostat.platform.discovery.NodeType; import io.cryostat.platform.discovery.TargetNode; -import dagger.Lazy; import io.fabric8.kubernetes.api.model.EndpointAddress; import io.fabric8.kubernetes.api.model.EndpointPort; import io.fabric8.kubernetes.api.model.EndpointSubset; @@ -68,6 +66,8 @@ public class KubeApiPlatformClient extends AbstractPlatformClient { private final KubernetesClient k8sClient; private final Set namespaces; + private final Set portNames; + private final Set portNumbers; private final LazyInitializer>> nsInformers = new LazyInitializer>>() { @Override @@ -98,7 +98,6 @@ protected HashMap> initialize() }; private Integer memoHash; private EnvironmentNode memoTree; - private final Lazy connectionToolkit; private final Logger logger; private final Map, Pair> discoveryNodeCache = new ConcurrentHashMap<>(); @@ -108,13 +107,15 @@ protected HashMap> initialize() KubeApiPlatformClient( Environment environment, Collection namespaces, + Collection portNames, + Collection portNumbers, KubernetesClient k8sClient, - Lazy connectionToolkit, Logger logger) { super(environment); this.namespaces = new HashSet<>(namespaces); + this.portNames = new HashSet<>(portNames); + this.portNumbers = new HashSet<>(portNumbers); this.k8sClient = k8sClient; - this.connectionToolkit = connectionToolkit; this.logger = logger; } @@ -289,7 +290,7 @@ private Pair queryForNode( } private boolean isCompatiblePort(EndpointPort port) { - return "jfr-jmx".equals(port.getName()) || 9091 == port.getPort(); + return portNames.contains(port.getName()) || portNumbers.contains(port.getPort()); } private List getAllServiceRefs() { diff --git a/src/main/java/io/cryostat/platform/internal/KubeApiPlatformStrategy.java b/src/main/java/io/cryostat/platform/internal/KubeApiPlatformStrategy.java index 14257e3c39..f0dd9cb824 100644 --- a/src/main/java/io/cryostat/platform/internal/KubeApiPlatformStrategy.java +++ b/src/main/java/io/cryostat/platform/internal/KubeApiPlatformStrategy.java @@ -25,7 +25,6 @@ import io.cryostat.configuration.Variables; import io.cryostat.core.log.Logger; -import io.cryostat.core.net.JFRConnectionToolkit; import io.cryostat.core.sys.Environment; import io.cryostat.core.sys.FileSystem; import io.cryostat.net.AuthManager; @@ -39,21 +38,18 @@ class KubeApiPlatformStrategy implements PlatformDetectionStrategy { + public static final String NO_PORT_NAME = "-"; + public static final Integer NO_PORT_NUMBER = 0; + protected final Lazy authMgr; protected final Environment env; protected final FileSystem fs; - protected final Lazy connectionToolkit; protected final Logger logger; KubeApiPlatformStrategy( - Lazy authMgr, - Lazy connectionToolkit, - Environment env, - FileSystem fs, - Logger logger) { + Lazy authMgr, Environment env, FileSystem fs, Logger logger) { this.logger = logger; this.authMgr = authMgr; - this.connectionToolkit = connectionToolkit; this.env = env; this.fs = fs; } @@ -72,8 +68,19 @@ public boolean isAvailable() { @Override public KubeApiPlatformClient getPlatformClient() { logger.info("Selected {} Strategy", getClass().getSimpleName()); + List portNames = + Arrays.asList(env.getEnv(Variables.K8S_PORT_NAMES, "jfr-jmx").split(",")).stream() + .map(String::strip) + .filter(n -> !NO_PORT_NAME.equals(n)) + .toList(); + List portNumbers = + Arrays.asList(env.getEnv(Variables.K8S_PORT_NUMBERS, "9091").split(",")).stream() + .map(String::strip) + .map(Integer::parseInt) + .filter(n -> !NO_PORT_NUMBER.equals(n)) + .toList(); return new KubeApiPlatformClient( - env, getNamespaces(), createClient(), connectionToolkit, logger); + env, getNamespaces(), portNames, portNumbers, createClient(), logger); } @Override diff --git a/src/main/java/io/cryostat/platform/internal/OpenShiftPlatformStrategy.java b/src/main/java/io/cryostat/platform/internal/OpenShiftPlatformStrategy.java index 441c8c3626..2848c569bc 100644 --- a/src/main/java/io/cryostat/platform/internal/OpenShiftPlatformStrategy.java +++ b/src/main/java/io/cryostat/platform/internal/OpenShiftPlatformStrategy.java @@ -16,7 +16,6 @@ package io.cryostat.platform.internal; import io.cryostat.core.log.Logger; -import io.cryostat.core.net.JFRConnectionToolkit; import io.cryostat.core.sys.Environment; import io.cryostat.core.sys.FileSystem; import io.cryostat.net.AuthManager; @@ -28,12 +27,8 @@ class OpenShiftPlatformStrategy extends KubeApiPlatformStrategy { OpenShiftPlatformStrategy( - Lazy authMgr, - Lazy connectionToolkit, - Environment env, - FileSystem fs, - Logger logger) { - super(authMgr, connectionToolkit, env, fs, logger); + Lazy authMgr, Environment env, FileSystem fs, Logger logger) { + super(authMgr, env, fs, logger); } @Override diff --git a/src/main/java/io/cryostat/platform/internal/PlatformStrategyModule.java b/src/main/java/io/cryostat/platform/internal/PlatformStrategyModule.java index e7ca3bbc58..2e375bc72d 100644 --- a/src/main/java/io/cryostat/platform/internal/PlatformStrategyModule.java +++ b/src/main/java/io/cryostat/platform/internal/PlatformStrategyModule.java @@ -68,23 +68,15 @@ static CustomTargetPlatformStrategy provideCustomTargetPlatformStrategy( @Provides @Singleton static OpenShiftPlatformStrategy provideOpenShiftPlatformStrategy( - Logger logger, - Lazy authManager, - Lazy connectionToolkit, - Environment env, - FileSystem fs) { - return new OpenShiftPlatformStrategy(authManager, connectionToolkit, env, fs, logger); + Lazy authManager, Environment env, FileSystem fs, Logger logger) { + return new OpenShiftPlatformStrategy(authManager, env, fs, logger); } @Provides @Singleton static KubeApiPlatformStrategy provideKubeApiPlatformStrategy( - Lazy noopAuthManager, - Lazy connectionToolkit, - Environment env, - FileSystem fs, - Logger logger) { - return new KubeApiPlatformStrategy(noopAuthManager, connectionToolkit, env, fs, logger); + Lazy noopAuthManager, Environment env, FileSystem fs, Logger logger) { + return new KubeApiPlatformStrategy(noopAuthManager, env, fs, logger); } @Provides diff --git a/src/test/java/io/cryostat/platform/internal/KubeApiPlatformClientTest.java b/src/test/java/io/cryostat/platform/internal/KubeApiPlatformClientTest.java index c786581d8d..da3fea2c56 100644 --- a/src/test/java/io/cryostat/platform/internal/KubeApiPlatformClientTest.java +++ b/src/test/java/io/cryostat/platform/internal/KubeApiPlatformClientTest.java @@ -22,12 +22,12 @@ import java.util.List; import java.util.Map; import java.util.Queue; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import io.cryostat.core.log.Logger; -import io.cryostat.core.net.JFRConnectionToolkit; import io.cryostat.core.net.discovery.JvmDiscoveryClient.EventKind; import io.cryostat.core.sys.Environment; import io.cryostat.platform.ServiceRef; @@ -51,764 +51,1324 @@ import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) -@EnableKubernetesMockClient(https = false, crud = true) class KubeApiPlatformClientTest { static final String NAMESPACE = "foo-namespace"; - KubeApiPlatformClient platformClient; - KubernetesClient k8sClient; - KubernetesMockServer server; - @Mock JFRConnectionToolkit connectionToolkit; - @Mock Environment env; - @Mock Logger logger; - - @BeforeEach - void setup() throws Exception { - this.platformClient = - new KubeApiPlatformClient( - env, List.of(NAMESPACE), k8sClient, () -> connectionToolkit, logger); - } + @Nested + @EnableKubernetesMockClient(https = false, crud = true) + class WithDefaultPortNameAndNumber { - @Test - void shouldReturnEmptyListIfNoEndpointsFound() throws Exception { - platformClient.start(); - List result = platformClient.listDiscoverableServices(); - MatcherAssert.assertThat(result, Matchers.equalTo(Collections.emptyList())); - } + KubeApiPlatformClient platformClient; + KubernetesClient k8sClient; + KubernetesMockServer server; + @Mock Environment env; + @Mock Logger logger; - @Test - void shouldReturnListOfMatchingEndpointRefs() throws Exception { - Pod targetA = - new PodBuilder() - .withNewMetadata() - .withName("targetA") - .withNamespace(NAMESPACE) - .endMetadata() - .build(); - String ipA = "127.0.0.2"; - String transformedIpA = ipA.replaceAll("\\.", "-"); - int portA = 80; - k8sClient.pods().inNamespace(NAMESPACE).resource(targetA).create(); - - Pod targetB = - new PodBuilder() - .withNewMetadata() - .withName("targetB") - .withNamespace(NAMESPACE) - .endMetadata() - .build(); - String ipB = "127.0.0.3"; - String transformedIpB = ipB.replaceAll("\\.", "-"); - int portB = 1234; - k8sClient.pods().inNamespace(NAMESPACE).resource(targetB).create(); - - Pod targetC = - new PodBuilder() - .withNewMetadata() - .withName("targetC") - .withNamespace(NAMESPACE) - .endMetadata() - .build(); - String ipC = "127.0.0.4"; - String transformedIpC = ipC.replaceAll("\\.", "-"); - int portC = 9091; - k8sClient.pods().inNamespace(NAMESPACE).resource(targetC).create(); - - Pod targetD = - new PodBuilder() - .withNewMetadata() - .withName("targetD") - .withNamespace(NAMESPACE) - .endMetadata() - .build(); - String ipD = "127.0.0.5"; - String transformedIpD = ipD.replaceAll("\\.", "-"); - int portD = 9091; - k8sClient.pods().inNamespace(NAMESPACE).resource(targetD).create(); - - Pod targetE = - new PodBuilder() - .withNewMetadata() - .withName("targetE") - .withNamespace(NAMESPACE) - .endMetadata() - .build(); - String ipE = "127.0.0.6"; - String transformedIpE = ipE.replaceAll("\\.", "-"); - int portE = 5678; - k8sClient.pods().inNamespace(NAMESPACE).resource(targetE).create(); - - Endpoints endpoints = - new EndpointsBuilder() - .withNewMetadata() - .withName("endpoints1") - .withNamespace(NAMESPACE) - .endMetadata() - .addNewSubset() - .withAddresses( - new EndpointAddressBuilder() - .withIp(ipA) - .withHostname(targetA.getMetadata().getName()) - .withNewTargetRef() - .withName(targetA.getMetadata().getName()) - .withKind(targetA.getKind()) - .withNamespace(NAMESPACE) - .endTargetRef() - .build()) - .withPorts( - new EndpointPortBuilder() - .withName("tcp-80") - .withPort(portA) - .withProtocol("tcp") - .build()) - .endSubset() - .addNewSubset() - .withAddresses( - new EndpointAddressBuilder() - .withIp(ipB) - .withHostname(targetB.getMetadata().getName()) - .withNewTargetRef() - .withName(targetB.getMetadata().getName()) - .withKind(targetB.getKind()) - .withNamespace(NAMESPACE) - .endTargetRef() - .build()) - .withPorts( - new EndpointPortBuilder() - .withName("jfr-jmx") - .withPort(portB) - .withProtocol("tcp") - .build()) - .endSubset() - .addNewSubset() - .withAddresses( - new EndpointAddressBuilder() - .withIp(ipC) - .withHostname(targetC.getMetadata().getName()) - .withNewTargetRef() - .withName(targetC.getMetadata().getName()) - .withKind(targetC.getKind()) - .withNamespace(NAMESPACE) - .endTargetRef() - .build()) - .withPorts( - new EndpointPortBuilder() - .withName("tcp-9091") - .withPort(portC) - .withProtocol("tcp") - .build()) - .endSubset() - .addNewSubset() - .withAddresses( - new EndpointAddressBuilder() - .withIp(ipD) - .withHostname(targetD.getMetadata().getName()) - .withNewTargetRef() - .withName(targetD.getMetadata().getName()) - .withKind(targetD.getKind()) - .withNamespace(NAMESPACE) - .endTargetRef() - .build()) - .withPorts( - new EndpointPortBuilder() - .withName("tcp-9091") - .withPort(portD) - .withProtocol("tcp") - .build()) - .endSubset() - .addNewSubset() - .withAddresses( - new EndpointAddressBuilder() - .withIp(ipE) - .withHostname(targetE.getMetadata().getName()) - .withNewTargetRef() - .withName(targetE.getMetadata().getName()) - .withKind(targetE.getKind()) - .withNamespace(NAMESPACE) - .endTargetRef() - .build()) - .withPorts( - new EndpointPortBuilder() - .withName("jfr-jmx") - .withPort(portE) - .withProtocol("tcp") - .build()) - .endSubset() - .build(); - - k8sClient.endpoints().inNamespace(NAMESPACE).resource(endpoints).create(); - - platformClient.start(); - List result = platformClient.listDiscoverableServices(); - - // targetA is intentionally not a matching service - ServiceRef serv1 = - new ServiceRef( - null, - URI.create( - String.format( - "service:jmx:rmi:///jndi/rmi://%s.%s.pod:%d/jmxrmi", - transformedIpB, NAMESPACE, portB)), - targetB.getMetadata().getName()); - serv1.setCryostatAnnotations( - Map.of( - AnnotationKey.REALM, - "KubernetesApi", - AnnotationKey.HOST, - ipB, - AnnotationKey.PORT, - Integer.toString(portB), - AnnotationKey.NAMESPACE, - NAMESPACE, - AnnotationKey.POD_NAME, - targetB.getMetadata().getName())); - ServiceRef serv2 = - new ServiceRef( - null, - URI.create( - String.format( - "service:jmx:rmi:///jndi/rmi://%s.%s.pod:%d/jmxrmi", - transformedIpC, NAMESPACE, portC)), - targetC.getMetadata().getName()); - serv2.setCryostatAnnotations( - Map.of( - AnnotationKey.REALM, - "KubernetesApi", - AnnotationKey.HOST, - ipC, - AnnotationKey.PORT, - Integer.toString(portC), - AnnotationKey.NAMESPACE, - NAMESPACE, - AnnotationKey.POD_NAME, - targetC.getMetadata().getName())); - ServiceRef serv3 = - new ServiceRef( - null, - URI.create( - String.format( - "service:jmx:rmi:///jndi/rmi://%s.%s.pod:%d/jmxrmi", - transformedIpD, NAMESPACE, portD)), - targetD.getMetadata().getName()); - serv3.setCryostatAnnotations( - Map.of( - AnnotationKey.REALM, - "KubernetesApi", - AnnotationKey.HOST, - ipD, - AnnotationKey.PORT, - Integer.toString(portD), - AnnotationKey.NAMESPACE, - NAMESPACE, - AnnotationKey.POD_NAME, - targetD.getMetadata().getName())); - ServiceRef serv4 = - new ServiceRef( - null, - URI.create( - String.format( - "service:jmx:rmi:///jndi/rmi://%s.%s.pod:%d/jmxrmi", - transformedIpE, NAMESPACE, portE)), - targetE.getMetadata().getName()); - serv4.setCryostatAnnotations( - Map.of( - AnnotationKey.REALM, - "KubernetesApi", - AnnotationKey.HOST, - ipE, - AnnotationKey.PORT, - Integer.toString(portE), - AnnotationKey.NAMESPACE, - NAMESPACE, - AnnotationKey.POD_NAME, - targetE.getMetadata().getName())); - MatcherAssert.assertThat( - result, Matchers.equalTo(Arrays.asList(serv1, serv2, serv3, serv4))); - } + @BeforeEach + void setup() throws Exception { + platformClient = + new KubeApiPlatformClient( + env, + List.of(NAMESPACE), + Set.of("jfr-jmx"), + Set.of(9091), + k8sClient, + logger); + } - @Test - void shouldReturnDiscoveryTree() throws Exception { - Pod targetA = - new PodBuilder() - .withNewMetadata() - .withName("targetA") - .withNamespace(NAMESPACE) - .endMetadata() - .build(); - k8sClient.pods().inNamespace(NAMESPACE).resource(targetA).create(); - Pod targetB = - new PodBuilder() - .withNewMetadata() - .withName("targetB") - .withNamespace(NAMESPACE) - .endMetadata() - .build(); - k8sClient.pods().inNamespace(NAMESPACE).resource(targetB).create(); - - String ipA = "127.0.0.2"; - String transformedIpA = ipA.replaceAll("\\.", "-"); - int portA = 9091; - String ipB = "127.0.0.3"; - String transformedIpB = ipB.replaceAll("\\.", "-"); - int portB = 1234; - Endpoints endpoints = - new EndpointsBuilder() - .withNewMetadata() - .withName("endpoints1") - .withNamespace(NAMESPACE) - .endMetadata() - .addNewSubset() - .withAddresses( - new EndpointAddressBuilder() - .withIp(ipA) - .withHostname(targetA.getMetadata().getName()) - .withNewTargetRef() - .withName(targetA.getMetadata().getName()) - .withKind(targetA.getKind()) - .withNamespace(NAMESPACE) - .endTargetRef() - .build()) - .withPorts( - new EndpointPortBuilder() - .withName("tcp-9091") - .withPort(portA) - .withProtocol("tcp") - .build()) - .endSubset() - .addNewSubset() - .withAddresses( - new EndpointAddressBuilder() - .withIp(ipB) - .withHostname(targetB.getMetadata().getName()) - .withNewTargetRef() - .withName(targetB.getMetadata().getName()) - .withKind(targetB.getKind()) - .withNamespace(NAMESPACE) - .endTargetRef() - .build()) - .withPorts( - new EndpointPortBuilder() - .withName("jfr-jmx") - .withPort(portB) - .withProtocol("tcp") - .build()) - .endSubset() - .build(); - k8sClient.endpoints().inNamespace(NAMESPACE).resource(endpoints).create(); - - platformClient.start(); - EnvironmentNode realmNode = platformClient.getDiscoveryTree(); - ServiceRef serv1 = - new ServiceRef( - null, - URI.create( - String.format( - "service:jmx:rmi:///jndi/rmi://%s.%s.pod:%d/jmxrmi", - transformedIpA, NAMESPACE, portA)), - targetA.getMetadata().getName()); - serv1.setCryostatAnnotations( - Map.of( - AnnotationKey.REALM, - "KubernetesApi", - AnnotationKey.HOST, - ipA, - AnnotationKey.PORT, - Integer.toString(portA), - AnnotationKey.NAMESPACE, - NAMESPACE, - AnnotationKey.POD_NAME, - targetA.getMetadata().getName())); - ServiceRef serv2 = - new ServiceRef( - null, - URI.create( - String.format( - "service:jmx:rmi:///jndi/rmi://%s.%s.pod:%d/jmxrmi", - transformedIpB, NAMESPACE, portB)), - targetB.getMetadata().getName()); - serv2.setCryostatAnnotations( - Map.of( - AnnotationKey.REALM, - "KubernetesApi", - AnnotationKey.HOST, - ipB, - AnnotationKey.PORT, - Integer.toString(portB), - AnnotationKey.NAMESPACE, - NAMESPACE, - AnnotationKey.POD_NAME, - targetB.getMetadata().getName())); - - MatcherAssert.assertThat(realmNode.getName(), Matchers.equalTo("KubernetesApi")); - MatcherAssert.assertThat(realmNode.getNodeType(), Matchers.equalTo(BaseNodeType.REALM)); - MatcherAssert.assertThat(realmNode.getLabels().size(), Matchers.equalTo(0)); - MatcherAssert.assertThat(realmNode.getChildren(), Matchers.hasSize(1)); - - AbstractNode realmChild = realmNode.getChildren().get(0); - MatcherAssert.assertThat(realmChild, Matchers.instanceOf(EnvironmentNode.class)); - EnvironmentNode namespaceNode = (EnvironmentNode) realmChild; - MatcherAssert.assertThat(namespaceNode.getName(), Matchers.equalTo(NAMESPACE)); - MatcherAssert.assertThat( - namespaceNode.getNodeType(), Matchers.equalTo(KubernetesNodeType.NAMESPACE)); - MatcherAssert.assertThat(namespaceNode.getLabels().size(), Matchers.equalTo(0)); - MatcherAssert.assertThat(namespaceNode.getChildren(), Matchers.hasSize(2)); - - MatcherAssert.assertThat( - namespaceNode.getChildren(), - Matchers.everyItem( - Matchers.hasProperty( - "nodeType", Matchers.equalTo(KubernetesNodeType.POD)))); - MatcherAssert.assertThat( - namespaceNode.getChildren(), - Matchers.allOf( - Matchers.hasItem( - Matchers.hasProperty( - "name", Matchers.equalTo(targetA.getMetadata().getName()))), - Matchers.hasItem( - Matchers.hasProperty( - "name", - Matchers.equalTo(targetB.getMetadata().getName()))))); - - EnvironmentNode podA = - (EnvironmentNode) - namespaceNode.getChildren().stream() - .filter(c -> c.getName().equals(targetA.getMetadata().getName())) - .findFirst() - .get(); - EnvironmentNode podB = - (EnvironmentNode) - namespaceNode.getChildren().stream() - .filter(c -> c.getName().equals(targetB.getMetadata().getName())) - .findFirst() - .get(); - - // FIXME fill in more intermediate nodes, ie. ReplicationController, DeploymentConfig - Matcher sr1Matcher = - Matchers.allOf( - Matchers.hasProperty( - "name", Matchers.equalTo(serv1.getServiceUri().toString())), - Matchers.hasProperty( - "nodeType", Matchers.equalTo(KubernetesNodeType.ENDPOINT)), - Matchers.hasProperty("target", Matchers.equalTo(serv1))); - MatcherAssert.assertThat(podA.getChildren(), Matchers.contains(sr1Matcher)); - Matcher sr2Matcher = - Matchers.allOf( - Matchers.hasProperty( - "name", Matchers.equalTo(serv2.getServiceUri().toString())), - Matchers.hasProperty( - "nodeType", Matchers.equalTo(KubernetesNodeType.ENDPOINT)), - Matchers.hasProperty("target", Matchers.equalTo(serv2))); - MatcherAssert.assertThat(podB.getChildren(), Matchers.contains(sr2Matcher)); - } + @Test + void shouldReturnEmptyListIfNoEndpointsFound() throws Exception { + platformClient.start(); + List result = platformClient.listDiscoverableServices(); + MatcherAssert.assertThat(result, Matchers.equalTo(Collections.emptyList())); + } + + @Test + void shouldReturnListOfMatchingEndpointRefs() throws Exception { + Pod targetA = + new PodBuilder() + .withNewMetadata() + .withName("targetA") + .withNamespace(NAMESPACE) + .endMetadata() + .build(); + String ipA = "127.0.0.2"; + String transformedIpA = ipA.replaceAll("\\.", "-"); + int portA = 80; + k8sClient.pods().inNamespace(NAMESPACE).resource(targetA).create(); + + Pod targetB = + new PodBuilder() + .withNewMetadata() + .withName("targetB") + .withNamespace(NAMESPACE) + .endMetadata() + .build(); + String ipB = "127.0.0.3"; + String transformedIpB = ipB.replaceAll("\\.", "-"); + int portB = 1234; + k8sClient.pods().inNamespace(NAMESPACE).resource(targetB).create(); + + Pod targetC = + new PodBuilder() + .withNewMetadata() + .withName("targetC") + .withNamespace(NAMESPACE) + .endMetadata() + .build(); + String ipC = "127.0.0.4"; + String transformedIpC = ipC.replaceAll("\\.", "-"); + int portC = 9091; + k8sClient.pods().inNamespace(NAMESPACE).resource(targetC).create(); + + Pod targetD = + new PodBuilder() + .withNewMetadata() + .withName("targetD") + .withNamespace(NAMESPACE) + .endMetadata() + .build(); + String ipD = "127.0.0.5"; + String transformedIpD = ipD.replaceAll("\\.", "-"); + int portD = 9091; + k8sClient.pods().inNamespace(NAMESPACE).resource(targetD).create(); + + Pod targetE = + new PodBuilder() + .withNewMetadata() + .withName("targetE") + .withNamespace(NAMESPACE) + .endMetadata() + .build(); + String ipE = "127.0.0.6"; + String transformedIpE = ipE.replaceAll("\\.", "-"); + int portE = 5678; + k8sClient.pods().inNamespace(NAMESPACE).resource(targetE).create(); + + Endpoints endpoints = + new EndpointsBuilder() + .withNewMetadata() + .withName("endpoints1") + .withNamespace(NAMESPACE) + .endMetadata() + .addNewSubset() + .withAddresses( + new EndpointAddressBuilder() + .withIp(ipA) + .withHostname(targetA.getMetadata().getName()) + .withNewTargetRef() + .withName(targetA.getMetadata().getName()) + .withKind(targetA.getKind()) + .withNamespace(NAMESPACE) + .endTargetRef() + .build()) + .withPorts( + new EndpointPortBuilder() + .withName("tcp-80") + .withPort(portA) + .withProtocol("tcp") + .build()) + .endSubset() + .addNewSubset() + .withAddresses( + new EndpointAddressBuilder() + .withIp(ipB) + .withHostname(targetB.getMetadata().getName()) + .withNewTargetRef() + .withName(targetB.getMetadata().getName()) + .withKind(targetB.getKind()) + .withNamespace(NAMESPACE) + .endTargetRef() + .build()) + .withPorts( + new EndpointPortBuilder() + .withName("jfr-jmx") + .withPort(portB) + .withProtocol("tcp") + .build()) + .endSubset() + .addNewSubset() + .withAddresses( + new EndpointAddressBuilder() + .withIp(ipC) + .withHostname(targetC.getMetadata().getName()) + .withNewTargetRef() + .withName(targetC.getMetadata().getName()) + .withKind(targetC.getKind()) + .withNamespace(NAMESPACE) + .endTargetRef() + .build()) + .withPorts( + new EndpointPortBuilder() + .withName("tcp-9091") + .withPort(portC) + .withProtocol("tcp") + .build()) + .endSubset() + .addNewSubset() + .withAddresses( + new EndpointAddressBuilder() + .withIp(ipD) + .withHostname(targetD.getMetadata().getName()) + .withNewTargetRef() + .withName(targetD.getMetadata().getName()) + .withKind(targetD.getKind()) + .withNamespace(NAMESPACE) + .endTargetRef() + .build()) + .withPorts( + new EndpointPortBuilder() + .withName("tcp-9091") + .withPort(portD) + .withProtocol("tcp") + .build()) + .endSubset() + .addNewSubset() + .withAddresses( + new EndpointAddressBuilder() + .withIp(ipE) + .withHostname(targetE.getMetadata().getName()) + .withNewTargetRef() + .withName(targetE.getMetadata().getName()) + .withKind(targetE.getKind()) + .withNamespace(NAMESPACE) + .endTargetRef() + .build()) + .withPorts( + new EndpointPortBuilder() + .withName("jfr-jmx") + .withPort(portE) + .withProtocol("tcp") + .build()) + .endSubset() + .build(); + + k8sClient.endpoints().inNamespace(NAMESPACE).resource(endpoints).create(); + + platformClient.start(); + List result = platformClient.listDiscoverableServices(); + + // targetA is intentionally not a matching service + ServiceRef serv1 = + new ServiceRef( + null, + URI.create( + String.format( + "service:jmx:rmi:///jndi/rmi://%s.%s.pod:%d/jmxrmi", + transformedIpB, NAMESPACE, portB)), + targetB.getMetadata().getName()); + serv1.setCryostatAnnotations( + Map.of( + AnnotationKey.REALM, + "KubernetesApi", + AnnotationKey.HOST, + ipB, + AnnotationKey.PORT, + Integer.toString(portB), + AnnotationKey.NAMESPACE, + NAMESPACE, + AnnotationKey.POD_NAME, + targetB.getMetadata().getName())); + ServiceRef serv2 = + new ServiceRef( + null, + URI.create( + String.format( + "service:jmx:rmi:///jndi/rmi://%s.%s.pod:%d/jmxrmi", + transformedIpC, NAMESPACE, portC)), + targetC.getMetadata().getName()); + serv2.setCryostatAnnotations( + Map.of( + AnnotationKey.REALM, + "KubernetesApi", + AnnotationKey.HOST, + ipC, + AnnotationKey.PORT, + Integer.toString(portC), + AnnotationKey.NAMESPACE, + NAMESPACE, + AnnotationKey.POD_NAME, + targetC.getMetadata().getName())); + ServiceRef serv3 = + new ServiceRef( + null, + URI.create( + String.format( + "service:jmx:rmi:///jndi/rmi://%s.%s.pod:%d/jmxrmi", + transformedIpD, NAMESPACE, portD)), + targetD.getMetadata().getName()); + serv3.setCryostatAnnotations( + Map.of( + AnnotationKey.REALM, + "KubernetesApi", + AnnotationKey.HOST, + ipD, + AnnotationKey.PORT, + Integer.toString(portD), + AnnotationKey.NAMESPACE, + NAMESPACE, + AnnotationKey.POD_NAME, + targetD.getMetadata().getName())); + ServiceRef serv4 = + new ServiceRef( + null, + URI.create( + String.format( + "service:jmx:rmi:///jndi/rmi://%s.%s.pod:%d/jmxrmi", + transformedIpE, NAMESPACE, portE)), + targetE.getMetadata().getName()); + serv4.setCryostatAnnotations( + Map.of( + AnnotationKey.REALM, + "KubernetesApi", + AnnotationKey.HOST, + ipE, + AnnotationKey.PORT, + Integer.toString(portE), + AnnotationKey.NAMESPACE, + NAMESPACE, + AnnotationKey.POD_NAME, + targetE.getMetadata().getName())); + MatcherAssert.assertThat( + result, Matchers.equalTo(Arrays.asList(serv1, serv2, serv3, serv4))); + } + + @Test + void shouldReturnDiscoveryTree() throws Exception { + Pod targetA = + new PodBuilder() + .withNewMetadata() + .withName("targetA") + .withNamespace(NAMESPACE) + .endMetadata() + .build(); + k8sClient.pods().inNamespace(NAMESPACE).resource(targetA).create(); + Pod targetB = + new PodBuilder() + .withNewMetadata() + .withName("targetB") + .withNamespace(NAMESPACE) + .endMetadata() + .build(); + k8sClient.pods().inNamespace(NAMESPACE).resource(targetB).create(); + + String ipA = "127.0.0.2"; + String transformedIpA = ipA.replaceAll("\\.", "-"); + int portA = 9091; + String ipB = "127.0.0.3"; + String transformedIpB = ipB.replaceAll("\\.", "-"); + int portB = 1234; + Endpoints endpoints = + new EndpointsBuilder() + .withNewMetadata() + .withName("endpoints1") + .withNamespace(NAMESPACE) + .endMetadata() + .addNewSubset() + .withAddresses( + new EndpointAddressBuilder() + .withIp(ipA) + .withHostname(targetA.getMetadata().getName()) + .withNewTargetRef() + .withName(targetA.getMetadata().getName()) + .withKind(targetA.getKind()) + .withNamespace(NAMESPACE) + .endTargetRef() + .build()) + .withPorts( + new EndpointPortBuilder() + .withName("tcp-9091") + .withPort(portA) + .withProtocol("tcp") + .build()) + .endSubset() + .addNewSubset() + .withAddresses( + new EndpointAddressBuilder() + .withIp(ipB) + .withHostname(targetB.getMetadata().getName()) + .withNewTargetRef() + .withName(targetB.getMetadata().getName()) + .withKind(targetB.getKind()) + .withNamespace(NAMESPACE) + .endTargetRef() + .build()) + .withPorts( + new EndpointPortBuilder() + .withName("jfr-jmx") + .withPort(portB) + .withProtocol("tcp") + .build()) + .endSubset() + .build(); + k8sClient.endpoints().inNamespace(NAMESPACE).resource(endpoints).create(); + + platformClient.start(); + EnvironmentNode realmNode = platformClient.getDiscoveryTree(); + ServiceRef serv1 = + new ServiceRef( + null, + URI.create( + String.format( + "service:jmx:rmi:///jndi/rmi://%s.%s.pod:%d/jmxrmi", + transformedIpA, NAMESPACE, portA)), + targetA.getMetadata().getName()); + serv1.setCryostatAnnotations( + Map.of( + AnnotationKey.REALM, + "KubernetesApi", + AnnotationKey.HOST, + ipA, + AnnotationKey.PORT, + Integer.toString(portA), + AnnotationKey.NAMESPACE, + NAMESPACE, + AnnotationKey.POD_NAME, + targetA.getMetadata().getName())); + ServiceRef serv2 = + new ServiceRef( + null, + URI.create( + String.format( + "service:jmx:rmi:///jndi/rmi://%s.%s.pod:%d/jmxrmi", + transformedIpB, NAMESPACE, portB)), + targetB.getMetadata().getName()); + serv2.setCryostatAnnotations( + Map.of( + AnnotationKey.REALM, + "KubernetesApi", + AnnotationKey.HOST, + ipB, + AnnotationKey.PORT, + Integer.toString(portB), + AnnotationKey.NAMESPACE, + NAMESPACE, + AnnotationKey.POD_NAME, + targetB.getMetadata().getName())); + + MatcherAssert.assertThat(realmNode.getName(), Matchers.equalTo("KubernetesApi")); + MatcherAssert.assertThat(realmNode.getNodeType(), Matchers.equalTo(BaseNodeType.REALM)); + MatcherAssert.assertThat(realmNode.getLabels().size(), Matchers.equalTo(0)); + MatcherAssert.assertThat(realmNode.getChildren(), Matchers.hasSize(1)); + + AbstractNode realmChild = realmNode.getChildren().get(0); + MatcherAssert.assertThat(realmChild, Matchers.instanceOf(EnvironmentNode.class)); + EnvironmentNode namespaceNode = (EnvironmentNode) realmChild; + MatcherAssert.assertThat(namespaceNode.getName(), Matchers.equalTo(NAMESPACE)); + MatcherAssert.assertThat( + namespaceNode.getNodeType(), Matchers.equalTo(KubernetesNodeType.NAMESPACE)); + MatcherAssert.assertThat(namespaceNode.getLabels().size(), Matchers.equalTo(0)); + MatcherAssert.assertThat(namespaceNode.getChildren(), Matchers.hasSize(2)); + + MatcherAssert.assertThat( + namespaceNode.getChildren(), + Matchers.everyItem( + Matchers.hasProperty( + "nodeType", Matchers.equalTo(KubernetesNodeType.POD)))); + MatcherAssert.assertThat( + namespaceNode.getChildren(), + Matchers.allOf( + Matchers.hasItem( + Matchers.hasProperty( + "name", + Matchers.equalTo(targetA.getMetadata().getName()))), + Matchers.hasItem( + Matchers.hasProperty( + "name", + Matchers.equalTo(targetB.getMetadata().getName()))))); + + EnvironmentNode podA = + (EnvironmentNode) + namespaceNode.getChildren().stream() + .filter( + c -> + c.getName() + .equals( + targetA.getMetadata() + .getName())) + .findFirst() + .get(); + EnvironmentNode podB = + (EnvironmentNode) + namespaceNode.getChildren().stream() + .filter( + c -> + c.getName() + .equals( + targetB.getMetadata() + .getName())) + .findFirst() + .get(); + + // FIXME fill in more intermediate nodes, ie. ReplicationController, DeploymentConfig + Matcher sr1Matcher = + Matchers.allOf( + Matchers.hasProperty( + "name", Matchers.equalTo(serv1.getServiceUri().toString())), + Matchers.hasProperty( + "nodeType", Matchers.equalTo(KubernetesNodeType.ENDPOINT)), + Matchers.hasProperty("target", Matchers.equalTo(serv1))); + MatcherAssert.assertThat(podA.getChildren(), Matchers.contains(sr1Matcher)); + Matcher sr2Matcher = + Matchers.allOf( + Matchers.hasProperty( + "name", Matchers.equalTo(serv2.getServiceUri().toString())), + Matchers.hasProperty( + "nodeType", Matchers.equalTo(KubernetesNodeType.ENDPOINT)), + Matchers.hasProperty("target", Matchers.equalTo(serv2))); + MatcherAssert.assertThat(podB.getChildren(), Matchers.contains(sr2Matcher)); + } + + @Test + public void shouldNotifyOnAsyncAdded() throws Exception { + CompletableFuture eventFuture = new CompletableFuture<>(); + platformClient.addTargetDiscoveryListener(eventFuture::complete); + + Pod watchedTarget = + new PodBuilder() + .withNewMetadata() + .withName("watchedTarget") + .withNamespace(NAMESPACE) + .endMetadata() + .build(); + k8sClient.pods().inNamespace(NAMESPACE).resource(watchedTarget).create(); + + platformClient.start(); + + String ip = "192.168.1.10"; + String transformedIp = ip.replaceAll("\\.", "-"); + int port = 9876; + Endpoints endpoints = + new EndpointsBuilder() + .withNewMetadata() + .withName("endpoints1") + .withNamespace(NAMESPACE) + .endMetadata() + .addNewSubset() + .withAddresses( + new EndpointAddressBuilder() + .withIp(ip) + .withHostname(watchedTarget.getMetadata().getName()) + .withNewTargetRef() + .withName(watchedTarget.getMetadata().getName()) + .withKind(watchedTarget.getKind()) + .withNamespace(NAMESPACE) + .endTargetRef() + .build()) + .withPorts( + new EndpointPortBuilder() + .withName("jfr-jmx") + .withPort(port) + .withProtocol("tcp") + .build()) + .endSubset() + .build(); + k8sClient.endpoints().inNamespace(NAMESPACE).resource(endpoints).create(); + + TargetDiscoveryEvent evt = eventFuture.get(1, TimeUnit.SECONDS); + MatcherAssert.assertThat(evt.getEventKind(), Matchers.equalTo(EventKind.FOUND)); + ServiceRef serv = + new ServiceRef( + null, + URI.create( + String.format( + "service:jmx:rmi:///jndi/rmi://%s.%s.pod:%d/jmxrmi", + transformedIp, NAMESPACE, port)), + watchedTarget.getMetadata().getName()); + serv.setCryostatAnnotations( + Map.of( + AnnotationKey.REALM, + "KubernetesApi", + AnnotationKey.HOST, + ip, + AnnotationKey.PORT, + Integer.toString(port), + AnnotationKey.NAMESPACE, + NAMESPACE, + AnnotationKey.POD_NAME, + watchedTarget.getMetadata().getName())); + MatcherAssert.assertThat(evt.getServiceRef(), Matchers.equalTo(serv)); + } + + @Test + public void shouldNotifyOnAsyncDeleted() throws Exception { + Pod watchedTarget = + new PodBuilder() + .withNewMetadata() + .withName("watchedTarget") + .withNamespace(NAMESPACE) + .endMetadata() + .build(); + k8sClient.pods().inNamespace(NAMESPACE).resource(watchedTarget).create(); + + String ip = "192.168.1.10"; + String transformedIp = ip.replaceAll("\\.", "-"); + int port = 9876; + Endpoints endpoints = + new EndpointsBuilder() + .withNewMetadata() + .withName("endpoints1") + .withNamespace(NAMESPACE) + .endMetadata() + .addNewSubset() + .withAddresses( + new EndpointAddressBuilder() + .withIp(ip) + .withHostname(watchedTarget.getMetadata().getName()) + .withNewTargetRef() + .withName(watchedTarget.getMetadata().getName()) + .withKind(watchedTarget.getKind()) + .withNamespace(NAMESPACE) + .endTargetRef() + .build()) + .withPorts( + new EndpointPortBuilder() + .withName("jfr-jmx") + .withPort(port) + .withProtocol("tcp") + .build()) + .endSubset() + .build(); + + CountDownLatch latch = new CountDownLatch(2); + Queue events = new ArrayDeque<>(2); + platformClient.addTargetDiscoveryListener( + tde -> { + events.add(tde); + latch.countDown(); + }); + + platformClient.start(); + + k8sClient.endpoints().inNamespace(NAMESPACE).resource(endpoints).create(); + k8sClient.endpoints().inNamespace(NAMESPACE).resource(endpoints).delete(); + + latch.await(); + Thread.sleep(100); // to ensure no more events are coming + + MatcherAssert.assertThat(events, Matchers.hasSize(2)); + + ServiceRef serv = + new ServiceRef( + null, + URI.create( + String.format( + "service:jmx:rmi:///jndi/rmi://%s.%s.pod:%d/jmxrmi", + transformedIp, NAMESPACE, port)), + "watchedTarget"); + serv.setCryostatAnnotations( + Map.of( + AnnotationKey.REALM, + "KubernetesApi", + AnnotationKey.HOST, + ip, + AnnotationKey.PORT, + Integer.toString(port), + AnnotationKey.NAMESPACE, + NAMESPACE, + AnnotationKey.POD_NAME, + watchedTarget.getMetadata().getName())); + + TargetDiscoveryEvent found = events.remove(); + MatcherAssert.assertThat(found.getEventKind(), Matchers.equalTo(EventKind.FOUND)); + MatcherAssert.assertThat(found.getServiceRef(), Matchers.equalTo(serv)); - @Test - public void shouldNotifyOnAsyncAdded() throws Exception { - CompletableFuture eventFuture = new CompletableFuture<>(); - platformClient.addTargetDiscoveryListener(eventFuture::complete); - - Pod watchedTarget = - new PodBuilder() - .withNewMetadata() - .withName("watchedTarget") - .withNamespace(NAMESPACE) - .endMetadata() - .build(); - k8sClient.pods().inNamespace(NAMESPACE).resource(watchedTarget).create(); - - platformClient.start(); - - String ip = "192.168.1.10"; - String transformedIp = ip.replaceAll("\\.", "-"); - int port = 9876; - Endpoints endpoints = - new EndpointsBuilder() - .withNewMetadata() - .withName("endpoints1") - .withNamespace(NAMESPACE) - .endMetadata() - .addNewSubset() - .withAddresses( - new EndpointAddressBuilder() - .withIp(ip) - .withHostname(watchedTarget.getMetadata().getName()) - .withNewTargetRef() - .withName(watchedTarget.getMetadata().getName()) - .withKind(watchedTarget.getKind()) - .withNamespace(NAMESPACE) - .endTargetRef() - .build()) - .withPorts( - new EndpointPortBuilder() - .withName("jfr-jmx") - .withPort(port) - .withProtocol("tcp") - .build()) - .endSubset() - .build(); - k8sClient.endpoints().inNamespace(NAMESPACE).resource(endpoints).create(); - - TargetDiscoveryEvent evt = eventFuture.get(1, TimeUnit.SECONDS); - MatcherAssert.assertThat(evt.getEventKind(), Matchers.equalTo(EventKind.FOUND)); - ServiceRef serv = - new ServiceRef( - null, - URI.create( - String.format( - "service:jmx:rmi:///jndi/rmi://%s.%s.pod:%d/jmxrmi", - transformedIp, NAMESPACE, port)), - watchedTarget.getMetadata().getName()); - serv.setCryostatAnnotations( - Map.of( - AnnotationKey.REALM, - "KubernetesApi", - AnnotationKey.HOST, - ip, - AnnotationKey.PORT, - Integer.toString(port), - AnnotationKey.NAMESPACE, - NAMESPACE, - AnnotationKey.POD_NAME, - watchedTarget.getMetadata().getName())); - MatcherAssert.assertThat(evt.getServiceRef(), Matchers.equalTo(serv)); + TargetDiscoveryEvent lost = events.remove(); + MatcherAssert.assertThat(lost.getEventKind(), Matchers.equalTo(EventKind.LOST)); + MatcherAssert.assertThat(lost.getServiceRef(), Matchers.equalTo(serv)); + } + + @Test + public void shouldNotifyOnAsyncModified() throws Exception { + Pod watchedTarget = + new PodBuilder() + .withNewMetadata() + .withName("watchedTarget") + .withNamespace(NAMESPACE) + .endMetadata() + .build(); + k8sClient.pods().inNamespace(NAMESPACE).resource(watchedTarget).create(); + Pod modifiedTarget = + new PodBuilder() + .withNewMetadata() + .withName("modifiedTarget") + .withNamespace(NAMESPACE) + .endMetadata() + .build(); + k8sClient.pods().inNamespace(NAMESPACE).resource(modifiedTarget).create(); + + String ip = "192.168.1.10"; + String transformedIp = ip.replaceAll("\\.", "-"); + int port = 9876; + + Endpoints endpoints = + new EndpointsBuilder() + .withNewMetadata() + .withName("endpoints1") + .withNamespace(NAMESPACE) + .endMetadata() + .addNewSubset() + .withAddresses( + new EndpointAddressBuilder() + .withIp(ip) + .withHostname(watchedTarget.getMetadata().getName()) + .withNewTargetRef() + .withName(watchedTarget.getMetadata().getName()) + .withKind(watchedTarget.getKind()) + .withNamespace(NAMESPACE) + .endTargetRef() + .build()) + .withPorts( + new EndpointPortBuilder() + .withName("jfr-jmx") + .withPort(port) + .withProtocol("tcp") + .build()) + .endSubset() + .build(); + + CountDownLatch latch = new CountDownLatch(2); + Queue events = new ArrayDeque<>(2); + platformClient.addTargetDiscoveryListener( + tde -> { + events.add(tde); + latch.countDown(); + }); + + platformClient.start(); + + k8sClient.endpoints().inNamespace(NAMESPACE).resource(endpoints).create(); + + endpoints = + new EndpointsBuilder() + .withNewMetadata() + .withName("endpoints1") + .withNamespace(NAMESPACE) + .endMetadata() + .addNewSubset() + .withAddresses( + new EndpointAddressBuilder() + .withIp(ip) + .withHostname(modifiedTarget.getMetadata().getName()) + .withNewTargetRef() + .withName(modifiedTarget.getMetadata().getName()) + .withKind(modifiedTarget.getKind()) + .withNamespace(NAMESPACE) + .endTargetRef() + .build()) + .withPorts( + new EndpointPortBuilder() + .withName("jfr-jmx") + .withPort(port) + .withProtocol("tcp") + .build()) + .endSubset() + .build(); + k8sClient.endpoints().inNamespace(NAMESPACE).resource(endpoints).replace(); + + latch.await(); + Thread.sleep(100); // to ensure no more events are coming + + MatcherAssert.assertThat(events, Matchers.hasSize(2)); + + ServiceRef original = + new ServiceRef( + null, + URI.create( + String.format( + "service:jmx:rmi:///jndi/rmi://%s.%s.pod:%d/jmxrmi", + transformedIp, NAMESPACE, port)), + watchedTarget.getMetadata().getName()); + original.setCryostatAnnotations( + Map.of( + AnnotationKey.REALM, + "KubernetesApi", + AnnotationKey.HOST, + ip, + AnnotationKey.PORT, + Integer.toString(port), + AnnotationKey.NAMESPACE, + NAMESPACE, + AnnotationKey.POD_NAME, + watchedTarget.getMetadata().getName())); + + ServiceRef modified = + new ServiceRef( + null, + URI.create( + String.format( + "service:jmx:rmi:///jndi/rmi://%s.%s.pod:%d/jmxrmi", + transformedIp, NAMESPACE, port)), + modifiedTarget.getMetadata().getName()); + modified.setCryostatAnnotations( + Map.of( + AnnotationKey.REALM, + "KubernetesApi", + AnnotationKey.HOST, + ip, + AnnotationKey.PORT, + Integer.toString(port), + AnnotationKey.NAMESPACE, + NAMESPACE, + AnnotationKey.POD_NAME, + modifiedTarget.getMetadata().getName())); + + TargetDiscoveryEvent foundEvent = events.remove(); + MatcherAssert.assertThat(foundEvent.getEventKind(), Matchers.equalTo(EventKind.FOUND)); + MatcherAssert.assertThat(foundEvent.getServiceRef(), Matchers.equalTo(original)); + + TargetDiscoveryEvent modifiedEvent = events.remove(); + MatcherAssert.assertThat( + modifiedEvent.getEventKind(), Matchers.equalTo(EventKind.MODIFIED)); + MatcherAssert.assertThat(modifiedEvent.getServiceRef(), Matchers.equalTo(modified)); + } } - @Test - public void shouldNotifyOnAsyncDeleted() throws Exception { - Pod watchedTarget = - new PodBuilder() - .withNewMetadata() - .withName("watchedTarget") - .withNamespace(NAMESPACE) - .endMetadata() - .build(); - k8sClient.pods().inNamespace(NAMESPACE).resource(watchedTarget).create(); - - String ip = "192.168.1.10"; - String transformedIp = ip.replaceAll("\\.", "-"); - int port = 9876; - Endpoints endpoints = - new EndpointsBuilder() - .withNewMetadata() - .withName("endpoints1") - .withNamespace(NAMESPACE) - .endMetadata() - .addNewSubset() - .withAddresses( - new EndpointAddressBuilder() - .withIp(ip) - .withHostname(watchedTarget.getMetadata().getName()) - .withNewTargetRef() - .withName(watchedTarget.getMetadata().getName()) - .withKind(watchedTarget.getKind()) - .withNamespace(NAMESPACE) - .endTargetRef() - .build()) - .withPorts( - new EndpointPortBuilder() - .withName("jfr-jmx") - .withPort(port) - .withProtocol("tcp") - .build()) - .endSubset() - .build(); - - CountDownLatch latch = new CountDownLatch(2); - Queue events = new ArrayDeque<>(2); - platformClient.addTargetDiscoveryListener( - tde -> { - events.add(tde); - latch.countDown(); - }); - - platformClient.start(); - - k8sClient.endpoints().inNamespace(NAMESPACE).resource(endpoints).create(); - k8sClient.endpoints().inNamespace(NAMESPACE).resource(endpoints).delete(); - - latch.await(); - Thread.sleep(100); // to ensure no more events are coming - - MatcherAssert.assertThat(events, Matchers.hasSize(2)); - - ServiceRef serv = - new ServiceRef( - null, - URI.create( - String.format( - "service:jmx:rmi:///jndi/rmi://%s.%s.pod:%d/jmxrmi", - transformedIp, NAMESPACE, port)), - "watchedTarget"); - serv.setCryostatAnnotations( - Map.of( - AnnotationKey.REALM, - "KubernetesApi", - AnnotationKey.HOST, - ip, - AnnotationKey.PORT, - Integer.toString(port), - AnnotationKey.NAMESPACE, - NAMESPACE, - AnnotationKey.POD_NAME, - watchedTarget.getMetadata().getName())); - - TargetDiscoveryEvent found = events.remove(); - MatcherAssert.assertThat(found.getEventKind(), Matchers.equalTo(EventKind.FOUND)); - MatcherAssert.assertThat(found.getServiceRef(), Matchers.equalTo(serv)); - - TargetDiscoveryEvent lost = events.remove(); - MatcherAssert.assertThat(lost.getEventKind(), Matchers.equalTo(EventKind.LOST)); - MatcherAssert.assertThat(lost.getServiceRef(), Matchers.equalTo(serv)); + @Nested + @EnableKubernetesMockClient(https = false, crud = true) + class WithNonDefaultPortNameAndNumber { + + KubeApiPlatformClient platformClient; + KubernetesClient k8sClient; + KubernetesMockServer server; + @Mock Environment env; + @Mock Logger logger; + + @BeforeEach + void setup() throws Exception { + platformClient = + new KubeApiPlatformClient( + env, + List.of(NAMESPACE), + Set.of("cryostat-jmx", "cryostat-jfr"), + Set.of(9999, 4545), + k8sClient, + logger); + } + + @Test + void shouldReturnListOfMatchingEndpointRefs() throws Exception { + Pod targetA = + new PodBuilder() + .withNewMetadata() + .withName("targetA") + .withNamespace(NAMESPACE) + .endMetadata() + .build(); + String ipA = "127.0.0.2"; + String transformedIpA = ipA.replaceAll("\\.", "-"); + int portA = 80; + k8sClient.pods().inNamespace(NAMESPACE).resource(targetA).create(); + + Pod targetB = + new PodBuilder() + .withNewMetadata() + .withName("targetB") + .withNamespace(NAMESPACE) + .endMetadata() + .build(); + String ipB = "127.0.0.3"; + String transformedIpB = ipB.replaceAll("\\.", "-"); + int portB = 1234; + k8sClient.pods().inNamespace(NAMESPACE).resource(targetB).create(); + + Pod targetC = + new PodBuilder() + .withNewMetadata() + .withName("targetC") + .withNamespace(NAMESPACE) + .endMetadata() + .build(); + String ipC = "127.0.0.4"; + String transformedIpC = ipC.replaceAll("\\.", "-"); + int portC = 9999; + k8sClient.pods().inNamespace(NAMESPACE).resource(targetC).create(); + + Pod targetD = + new PodBuilder() + .withNewMetadata() + .withName("targetD") + .withNamespace(NAMESPACE) + .endMetadata() + .build(); + String ipD = "127.0.0.5"; + String transformedIpD = ipD.replaceAll("\\.", "-"); + int portD = 4545; + k8sClient.pods().inNamespace(NAMESPACE).resource(targetD).create(); + + Pod targetE = + new PodBuilder() + .withNewMetadata() + .withName("targetE") + .withNamespace(NAMESPACE) + .endMetadata() + .build(); + String ipE = "127.0.0.6"; + String transformedIpE = ipE.replaceAll("\\.", "-"); + int portE = 5678; + k8sClient.pods().inNamespace(NAMESPACE).resource(targetE).create(); + + Endpoints endpoints = + new EndpointsBuilder() + .withNewMetadata() + .withName("endpoints1") + .withNamespace(NAMESPACE) + .endMetadata() + .addNewSubset() + .withAddresses( + new EndpointAddressBuilder() + .withIp(ipA) + .withHostname(targetA.getMetadata().getName()) + .withNewTargetRef() + .withName(targetA.getMetadata().getName()) + .withKind(targetA.getKind()) + .withNamespace(NAMESPACE) + .endTargetRef() + .build()) + .withPorts( + new EndpointPortBuilder() + .withName("tcp-80") + .withPort(portA) + .withProtocol("tcp") + .build()) + .endSubset() + .addNewSubset() + .withAddresses( + new EndpointAddressBuilder() + .withIp(ipB) + .withHostname(targetB.getMetadata().getName()) + .withNewTargetRef() + .withName(targetB.getMetadata().getName()) + .withKind(targetB.getKind()) + .withNamespace(NAMESPACE) + .endTargetRef() + .build()) + .withPorts( + new EndpointPortBuilder() + .withName("cryostat-jmx") + .withPort(portB) + .withProtocol("tcp") + .build()) + .endSubset() + .addNewSubset() + .withAddresses( + new EndpointAddressBuilder() + .withIp(ipC) + .withHostname(targetC.getMetadata().getName()) + .withNewTargetRef() + .withName(targetC.getMetadata().getName()) + .withKind(targetC.getKind()) + .withNamespace(NAMESPACE) + .endTargetRef() + .build()) + .withPorts( + new EndpointPortBuilder() + .withName("tcp-9999") + .withPort(portC) + .withProtocol("tcp") + .build()) + .endSubset() + .addNewSubset() + .withAddresses( + new EndpointAddressBuilder() + .withIp(ipD) + .withHostname(targetD.getMetadata().getName()) + .withNewTargetRef() + .withName(targetD.getMetadata().getName()) + .withKind(targetD.getKind()) + .withNamespace(NAMESPACE) + .endTargetRef() + .build()) + .withPorts( + new EndpointPortBuilder() + .withName("tcp-9999") + .withPort(portD) + .withProtocol("tcp") + .build()) + .endSubset() + .addNewSubset() + .withAddresses( + new EndpointAddressBuilder() + .withIp(ipE) + .withHostname(targetE.getMetadata().getName()) + .withNewTargetRef() + .withName(targetE.getMetadata().getName()) + .withKind(targetE.getKind()) + .withNamespace(NAMESPACE) + .endTargetRef() + .build()) + .withPorts( + new EndpointPortBuilder() + .withName("cryostat-jfr") + .withPort(portE) + .withProtocol("tcp") + .build()) + .endSubset() + .build(); + + k8sClient.endpoints().inNamespace(NAMESPACE).resource(endpoints).create(); + + platformClient.start(); + List result = platformClient.listDiscoverableServices(); + + // targetA is intentionally not a matching service + ServiceRef serv1 = + new ServiceRef( + null, + URI.create( + String.format( + "service:jmx:rmi:///jndi/rmi://%s.%s.pod:%d/jmxrmi", + transformedIpB, NAMESPACE, portB)), + targetB.getMetadata().getName()); + serv1.setCryostatAnnotations( + Map.of( + AnnotationKey.REALM, + "KubernetesApi", + AnnotationKey.HOST, + ipB, + AnnotationKey.PORT, + Integer.toString(portB), + AnnotationKey.NAMESPACE, + NAMESPACE, + AnnotationKey.POD_NAME, + targetB.getMetadata().getName())); + ServiceRef serv2 = + new ServiceRef( + null, + URI.create( + String.format( + "service:jmx:rmi:///jndi/rmi://%s.%s.pod:%d/jmxrmi", + transformedIpC, NAMESPACE, portC)), + targetC.getMetadata().getName()); + serv2.setCryostatAnnotations( + Map.of( + AnnotationKey.REALM, + "KubernetesApi", + AnnotationKey.HOST, + ipC, + AnnotationKey.PORT, + Integer.toString(portC), + AnnotationKey.NAMESPACE, + NAMESPACE, + AnnotationKey.POD_NAME, + targetC.getMetadata().getName())); + ServiceRef serv3 = + new ServiceRef( + null, + URI.create( + String.format( + "service:jmx:rmi:///jndi/rmi://%s.%s.pod:%d/jmxrmi", + transformedIpD, NAMESPACE, portD)), + targetD.getMetadata().getName()); + serv3.setCryostatAnnotations( + Map.of( + AnnotationKey.REALM, + "KubernetesApi", + AnnotationKey.HOST, + ipD, + AnnotationKey.PORT, + Integer.toString(portD), + AnnotationKey.NAMESPACE, + NAMESPACE, + AnnotationKey.POD_NAME, + targetD.getMetadata().getName())); + ServiceRef serv4 = + new ServiceRef( + null, + URI.create( + String.format( + "service:jmx:rmi:///jndi/rmi://%s.%s.pod:%d/jmxrmi", + transformedIpE, NAMESPACE, portE)), + targetE.getMetadata().getName()); + serv4.setCryostatAnnotations( + Map.of( + AnnotationKey.REALM, + "KubernetesApi", + AnnotationKey.HOST, + ipE, + AnnotationKey.PORT, + Integer.toString(portE), + AnnotationKey.NAMESPACE, + NAMESPACE, + AnnotationKey.POD_NAME, + targetE.getMetadata().getName())); + MatcherAssert.assertThat( + result, Matchers.equalTo(Arrays.asList(serv1, serv2, serv3, serv4))); + } } - @Test - public void shouldNotifyOnAsyncModified() throws Exception { - Pod watchedTarget = - new PodBuilder() - .withNewMetadata() - .withName("watchedTarget") - .withNamespace(NAMESPACE) - .endMetadata() - .build(); - k8sClient.pods().inNamespace(NAMESPACE).resource(watchedTarget).create(); - Pod modifiedTarget = - new PodBuilder() - .withNewMetadata() - .withName("modifiedTarget") - .withNamespace(NAMESPACE) - .endMetadata() - .build(); - k8sClient.pods().inNamespace(NAMESPACE).resource(modifiedTarget).create(); - - String ip = "192.168.1.10"; - String transformedIp = ip.replaceAll("\\.", "-"); - int port = 9876; - - Endpoints endpoints = - new EndpointsBuilder() - .withNewMetadata() - .withName("endpoints1") - .withNamespace(NAMESPACE) - .endMetadata() - .addNewSubset() - .withAddresses( - new EndpointAddressBuilder() - .withIp(ip) - .withHostname(watchedTarget.getMetadata().getName()) - .withNewTargetRef() - .withName(watchedTarget.getMetadata().getName()) - .withKind(watchedTarget.getKind()) - .withNamespace(NAMESPACE) - .endTargetRef() - .build()) - .withPorts( - new EndpointPortBuilder() - .withName("jfr-jmx") - .withPort(port) - .withProtocol("tcp") - .build()) - .endSubset() - .build(); - - CountDownLatch latch = new CountDownLatch(2); - Queue events = new ArrayDeque<>(2); - platformClient.addTargetDiscoveryListener( - tde -> { - events.add(tde); - latch.countDown(); - }); - - platformClient.start(); - - k8sClient.endpoints().inNamespace(NAMESPACE).resource(endpoints).create(); - - endpoints = - new EndpointsBuilder() - .withNewMetadata() - .withName("endpoints1") - .withNamespace(NAMESPACE) - .endMetadata() - .addNewSubset() - .withAddresses( - new EndpointAddressBuilder() - .withIp(ip) - .withHostname(modifiedTarget.getMetadata().getName()) - .withNewTargetRef() - .withName(modifiedTarget.getMetadata().getName()) - .withKind(modifiedTarget.getKind()) - .withNamespace(NAMESPACE) - .endTargetRef() - .build()) - .withPorts( - new EndpointPortBuilder() - .withName("jfr-jmx") - .withPort(port) - .withProtocol("tcp") - .build()) - .endSubset() - .build(); - k8sClient.endpoints().inNamespace(NAMESPACE).resource(endpoints).replace(); - - latch.await(); - Thread.sleep(100); // to ensure no more events are coming - - MatcherAssert.assertThat(events, Matchers.hasSize(2)); - - ServiceRef original = - new ServiceRef( - null, - URI.create( - String.format( - "service:jmx:rmi:///jndi/rmi://%s.%s.pod:%d/jmxrmi", - transformedIp, NAMESPACE, port)), - watchedTarget.getMetadata().getName()); - original.setCryostatAnnotations( - Map.of( - AnnotationKey.REALM, - "KubernetesApi", - AnnotationKey.HOST, - ip, - AnnotationKey.PORT, - Integer.toString(port), - AnnotationKey.NAMESPACE, - NAMESPACE, - AnnotationKey.POD_NAME, - watchedTarget.getMetadata().getName())); - - ServiceRef modified = - new ServiceRef( - null, - URI.create( - String.format( - "service:jmx:rmi:///jndi/rmi://%s.%s.pod:%d/jmxrmi", - transformedIp, NAMESPACE, port)), - modifiedTarget.getMetadata().getName()); - modified.setCryostatAnnotations( - Map.of( - AnnotationKey.REALM, - "KubernetesApi", - AnnotationKey.HOST, - ip, - AnnotationKey.PORT, - Integer.toString(port), - AnnotationKey.NAMESPACE, - NAMESPACE, - AnnotationKey.POD_NAME, - modifiedTarget.getMetadata().getName())); - - TargetDiscoveryEvent foundEvent = events.remove(); - MatcherAssert.assertThat(foundEvent.getEventKind(), Matchers.equalTo(EventKind.FOUND)); - MatcherAssert.assertThat(foundEvent.getServiceRef(), Matchers.equalTo(original)); - - TargetDiscoveryEvent modifiedEvent = events.remove(); - MatcherAssert.assertThat( - modifiedEvent.getEventKind(), Matchers.equalTo(EventKind.MODIFIED)); - MatcherAssert.assertThat(modifiedEvent.getServiceRef(), Matchers.equalTo(modified)); + @Nested + @EnableKubernetesMockClient(https = false, crud = true) + class WithEmptyPortNameAndNumber { + + KubeApiPlatformClient platformClient; + KubernetesClient k8sClient; + KubernetesMockServer server; + @Mock Environment env; + @Mock Logger logger; + + @BeforeEach + void setup() throws Exception { + platformClient = + new KubeApiPlatformClient( + env, List.of(NAMESPACE), Set.of(), Set.of(), k8sClient, logger); + } + + @Test + void shouldReturnListOfMatchingEndpointRefs() throws Exception { + Pod targetA = + new PodBuilder() + .withNewMetadata() + .withName("targetA") + .withNamespace(NAMESPACE) + .endMetadata() + .build(); + String ipA = "127.0.0.2"; + String transformedIpA = ipA.replaceAll("\\.", "-"); + int portA = 80; + k8sClient.pods().inNamespace(NAMESPACE).resource(targetA).create(); + + Pod targetB = + new PodBuilder() + .withNewMetadata() + .withName("targetB") + .withNamespace(NAMESPACE) + .endMetadata() + .build(); + String ipB = "127.0.0.3"; + String transformedIpB = ipB.replaceAll("\\.", "-"); + int portB = 1234; + k8sClient.pods().inNamespace(NAMESPACE).resource(targetB).create(); + + Pod targetC = + new PodBuilder() + .withNewMetadata() + .withName("targetC") + .withNamespace(NAMESPACE) + .endMetadata() + .build(); + String ipC = "127.0.0.4"; + String transformedIpC = ipC.replaceAll("\\.", "-"); + int portC = 9091; + k8sClient.pods().inNamespace(NAMESPACE).resource(targetC).create(); + + Pod targetD = + new PodBuilder() + .withNewMetadata() + .withName("targetD") + .withNamespace(NAMESPACE) + .endMetadata() + .build(); + String ipD = "127.0.0.5"; + String transformedIpD = ipD.replaceAll("\\.", "-"); + int portD = 9091; + k8sClient.pods().inNamespace(NAMESPACE).resource(targetD).create(); + + Pod targetE = + new PodBuilder() + .withNewMetadata() + .withName("targetE") + .withNamespace(NAMESPACE) + .endMetadata() + .build(); + String ipE = "127.0.0.6"; + String transformedIpE = ipE.replaceAll("\\.", "-"); + int portE = 5678; + k8sClient.pods().inNamespace(NAMESPACE).resource(targetE).create(); + + Endpoints endpoints = + new EndpointsBuilder() + .withNewMetadata() + .withName("endpoints1") + .withNamespace(NAMESPACE) + .endMetadata() + .addNewSubset() + .withAddresses( + new EndpointAddressBuilder() + .withIp(ipA) + .withHostname(targetA.getMetadata().getName()) + .withNewTargetRef() + .withName(targetA.getMetadata().getName()) + .withKind(targetA.getKind()) + .withNamespace(NAMESPACE) + .endTargetRef() + .build()) + .withPorts( + new EndpointPortBuilder() + .withName("tcp-80") + .withPort(portA) + .withProtocol("tcp") + .build()) + .endSubset() + .addNewSubset() + .withAddresses( + new EndpointAddressBuilder() + .withIp(ipB) + .withHostname(targetB.getMetadata().getName()) + .withNewTargetRef() + .withName(targetB.getMetadata().getName()) + .withKind(targetB.getKind()) + .withNamespace(NAMESPACE) + .endTargetRef() + .build()) + .withPorts( + new EndpointPortBuilder() + .withName("jfr-jmx") + .withPort(portB) + .withProtocol("tcp") + .build()) + .endSubset() + .addNewSubset() + .withAddresses( + new EndpointAddressBuilder() + .withIp(ipC) + .withHostname(targetC.getMetadata().getName()) + .withNewTargetRef() + .withName(targetC.getMetadata().getName()) + .withKind(targetC.getKind()) + .withNamespace(NAMESPACE) + .endTargetRef() + .build()) + .withPorts( + new EndpointPortBuilder() + .withName("tcp-9091") + .withPort(portC) + .withProtocol("tcp") + .build()) + .endSubset() + .addNewSubset() + .withAddresses( + new EndpointAddressBuilder() + .withIp(ipD) + .withHostname(targetD.getMetadata().getName()) + .withNewTargetRef() + .withName(targetD.getMetadata().getName()) + .withKind(targetD.getKind()) + .withNamespace(NAMESPACE) + .endTargetRef() + .build()) + .withPorts( + new EndpointPortBuilder() + .withName("tcp-9091") + .withPort(portD) + .withProtocol("tcp") + .build()) + .endSubset() + .addNewSubset() + .withAddresses( + new EndpointAddressBuilder() + .withIp(ipE) + .withHostname(targetE.getMetadata().getName()) + .withNewTargetRef() + .withName(targetE.getMetadata().getName()) + .withKind(targetE.getKind()) + .withNamespace(NAMESPACE) + .endTargetRef() + .build()) + .withPorts( + new EndpointPortBuilder() + .withName("jfr-jmx") + .withPort(portE) + .withProtocol("tcp") + .build()) + .endSubset() + .build(); + + k8sClient.endpoints().inNamespace(NAMESPACE).resource(endpoints).create(); + + platformClient.start(); + List result = platformClient.listDiscoverableServices(); + + // targetA is intentionally not a matching service + ServiceRef serv1 = + new ServiceRef( + null, + URI.create( + String.format( + "service:jmx:rmi:///jndi/rmi://%s.%s.pod:%d/jmxrmi", + transformedIpB, NAMESPACE, portB)), + targetB.getMetadata().getName()); + serv1.setCryostatAnnotations( + Map.of( + AnnotationKey.REALM, + "KubernetesApi", + AnnotationKey.HOST, + ipB, + AnnotationKey.PORT, + Integer.toString(portB), + AnnotationKey.NAMESPACE, + NAMESPACE, + AnnotationKey.POD_NAME, + targetB.getMetadata().getName())); + ServiceRef serv2 = + new ServiceRef( + null, + URI.create( + String.format( + "service:jmx:rmi:///jndi/rmi://%s.%s.pod:%d/jmxrmi", + transformedIpC, NAMESPACE, portC)), + targetC.getMetadata().getName()); + serv2.setCryostatAnnotations( + Map.of( + AnnotationKey.REALM, + "KubernetesApi", + AnnotationKey.HOST, + ipC, + AnnotationKey.PORT, + Integer.toString(portC), + AnnotationKey.NAMESPACE, + NAMESPACE, + AnnotationKey.POD_NAME, + targetC.getMetadata().getName())); + ServiceRef serv3 = + new ServiceRef( + null, + URI.create( + String.format( + "service:jmx:rmi:///jndi/rmi://%s.%s.pod:%d/jmxrmi", + transformedIpD, NAMESPACE, portD)), + targetD.getMetadata().getName()); + serv3.setCryostatAnnotations( + Map.of( + AnnotationKey.REALM, + "KubernetesApi", + AnnotationKey.HOST, + ipD, + AnnotationKey.PORT, + Integer.toString(portD), + AnnotationKey.NAMESPACE, + NAMESPACE, + AnnotationKey.POD_NAME, + targetD.getMetadata().getName())); + ServiceRef serv4 = + new ServiceRef( + null, + URI.create( + String.format( + "service:jmx:rmi:///jndi/rmi://%s.%s.pod:%d/jmxrmi", + transformedIpE, NAMESPACE, portE)), + targetE.getMetadata().getName()); + serv4.setCryostatAnnotations( + Map.of( + AnnotationKey.REALM, + "KubernetesApi", + AnnotationKey.HOST, + ipE, + AnnotationKey.PORT, + Integer.toString(portE), + AnnotationKey.NAMESPACE, + NAMESPACE, + AnnotationKey.POD_NAME, + targetE.getMetadata().getName())); + MatcherAssert.assertThat(result, Matchers.equalTo(Arrays.asList())); + } } }