Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Additional fields for ServiceRefs #523

Merged
merged 10 commits into from
Jul 6, 2021
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

import javax.inject.Inject;
Expand All @@ -49,6 +51,7 @@
import io.cryostat.net.web.http.api.ApiVersion;
import io.cryostat.platform.PlatformClient;
import io.cryostat.platform.ServiceRef;
import io.cryostat.platform.ServiceRef.AnnotationKey;
import io.cryostat.platform.internal.CustomTargetPlatformClient;
import io.cryostat.util.URIUtil;

Expand Down Expand Up @@ -128,7 +131,18 @@ public IntermediateResponse<ServiceRef> handle(RequestParameters params) throws
throw new ApiException(400, "Duplicate connectUrl");
}
}
Map<AnnotationKey, String> cryostatAnnotations = new HashMap<>();
ServiceRef serviceRef = new ServiceRef(uri, alias);
for (AnnotationKey ak : AnnotationKey.values()) {
// TODO is there a good way to determine this prefix from the structure of the
// ServiceRef's serialized form?
String formKey = "annotations.cryostat." + ak.name();
if (attrs.contains(formKey)) {
cryostatAnnotations.put(ak, attrs.get(formKey));
}
}
serviceRef.setCryostatAnnotations(cryostatAnnotations);

boolean v = customTargetPlatformClient.addTarget(serviceRef);
if (!v) {
throw new ApiException(400, "Duplicate connectUrl");
Expand Down
105 changes: 102 additions & 3 deletions src/main/java/io/cryostat/platform/ServiceRef.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@
package io.cryostat.platform;

import java.net.URI;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

import com.google.gson.annotations.SerializedName;
Expand All @@ -49,6 +53,8 @@ public class ServiceRef {

private final @SerializedName("connectUrl") URI serviceUri;
private final String alias;
private final Map<String, String> labels = new HashMap<>();
private final Annotations annotations = new Annotations();

public ServiceRef(URI uri, String alias) {
this.serviceUri = uri;
Expand All @@ -63,18 +69,111 @@ public Optional<String> getAlias() {
return Optional.ofNullable(alias);
}

public void setLabels(Map<String, String> labels) {
this.labels.clear();
this.labels.putAll(labels);
}

public Map<String, String> getLabels() {
return Collections.unmodifiableMap(labels);
}

public void setPlatformAnnotations(Map<String, String> annotations) {
this.annotations.platform.clear();
this.annotations.platform.putAll(annotations);
}
andrewazores marked this conversation as resolved.
Show resolved Hide resolved

public Map<String, String> getPlatformAnnotations() {
return new HashMap<>(annotations.platform);
}

public void setCryostatAnnotations(Map<AnnotationKey, String> annotations) {
this.annotations.cryostat.clear();
this.annotations.cryostat.putAll(annotations);
}

public Map<AnnotationKey, String> getCryostatAnnotations() {
return new HashMap<>(annotations.cryostat);
}

@Override
public boolean equals(Object o) {
return EqualsBuilder.reflectionEquals(this, o);
public boolean equals(Object other) {
if (other == null) {
return false;
}
if (other == this) {
return true;
}
if (!(other instanceof ServiceRef)) {
return false;
}
ServiceRef sr = (ServiceRef) other;
return new EqualsBuilder()
.append(serviceUri, sr.serviceUri)
.append(alias, sr.alias)
.append(labels, sr.labels)
.append(annotations, sr.annotations)
.build();
}

@Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this);
return new HashCodeBuilder()
.append(serviceUri)
.append(alias)
.append(labels)
.append(annotations)
.toHashCode();
}

@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}

private static class Annotations {
private final Map<String, String> platform = new HashMap<>();
private final Map<AnnotationKey, String> cryostat = new EnumMap<>(AnnotationKey.class);

@Override
public boolean equals(Object other) {
if (other == null) {
return false;
}
if (other == this) {
return true;
}
if (!(other instanceof Annotations)) {
return false;
}
Annotations o = (Annotations) other;
return new EqualsBuilder()
.append(platform, o.platform)
.append(cryostat, o.cryostat)
.build();
}

@Override
public int hashCode() {
return new HashCodeBuilder().append(platform).append(cryostat).toHashCode();
}

@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}

public enum AnnotationKey {
HOST,
PORT,
JAVA_MAIN,
PID,
START_TIME,
NAMESPACE,
SERVICE_NAME,
CONTAINER_NAME,
POD_NAME,
;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,21 @@

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.stream.Collectors;

import javax.management.remote.JMXServiceURL;

import io.cryostat.core.log.Logger;
import io.cryostat.core.net.discovery.DiscoveredJvmDescriptor;
import io.cryostat.core.net.discovery.JvmDiscoveryClient;
import io.cryostat.core.net.discovery.JvmDiscoveryClient.JvmDiscoveryEvent;
import io.cryostat.platform.ServiceRef;
import io.cryostat.platform.ServiceRef.AnnotationKey;
import io.cryostat.util.URIUtil;

class DefaultPlatformClient extends AbstractPlatformClient implements Consumer<JvmDiscoveryEvent> {
Expand All @@ -69,11 +75,7 @@ public void start() throws IOException {
@Override
public void accept(JvmDiscoveryEvent evt) {
try {
ServiceRef serviceRef =
new ServiceRef(
URIUtil.convert(evt.getJvmDescriptor().getJmxServiceUrl()),
evt.getJvmDescriptor().getMainClass());
notifyAsyncTargetDiscovery(evt.getEventKind(), serviceRef);
notifyAsyncTargetDiscovery(evt.getEventKind(), convert(evt.getJvmDescriptor()));
} catch (MalformedURLException | URISyntaxException e) {
logger.warn(e);
}
Expand All @@ -83,10 +85,9 @@ public void accept(JvmDiscoveryEvent evt) {
public List<ServiceRef> listDiscoverableServices() {
return discoveryClient.getDiscoveredJvmDescriptors().stream()
.map(
u -> {
desc -> {
try {
return new ServiceRef(
URIUtil.convert(u.getJmxServiceUrl()), u.getMainClass());
return convert(desc);
} catch (MalformedURLException | URISyntaxException e) {
logger.warn(e);
return null;
Expand All @@ -95,4 +96,17 @@ public List<ServiceRef> listDiscoverableServices() {
.filter(s -> s != null)
.collect(Collectors.toList());
}

private static ServiceRef convert(DiscoveredJvmDescriptor desc)
throws MalformedURLException, URISyntaxException {
JMXServiceURL serviceUrl = desc.getJmxServiceUrl();
ServiceRef serviceRef = new ServiceRef(URIUtil.convert(serviceUrl), desc.getMainClass());
URI rmiTarget = URIUtil.getRmiTarget(serviceUrl);
serviceRef.setCryostatAnnotations(
Map.of(
AnnotationKey.JAVA_MAIN, desc.getMainClass(),
AnnotationKey.HOST, rmiTarget.getHost(),
AnnotationKey.PORT, Integer.toString(rmiTarget.getPort())));
return serviceRef;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,15 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import io.cryostat.core.log.Logger;
import io.cryostat.core.net.JFRConnectionToolkit;
import io.cryostat.core.net.discovery.JvmDiscoveryClient.EventKind;
import io.cryostat.platform.ServiceRef;
import io.cryostat.platform.ServiceRef.AnnotationKey;
import io.cryostat.util.URIUtil;

import dagger.Lazy;
Expand Down Expand Up @@ -171,13 +173,25 @@ private List<ServiceRef> createServiceRefs(EndpointSubset subset, EndpointPort p
.map(
addr -> {
try {
return new ServiceRef(
URIUtil.convert(
connectionToolkit
.get()
.createServiceURL(
addr.getIp(), port.getPort())),
addr.getTargetRef().getName());
ServiceRef serviceRef =
new ServiceRef(
URIUtil.convert(
connectionToolkit
.get()
.createServiceURL(
addr.getIp(),
port.getPort())),
addr.getTargetRef().getName());
serviceRef.setCryostatAnnotations(
Map.of(
AnnotationKey.HOST, addr.getIp(),
AnnotationKey.PORT,
Integer.toString(port.getPort()),
AnnotationKey.NAMESPACE,
addr.getTargetRef().getNamespace(),
AnnotationKey.POD_NAME,
addr.getTargetRef().getName()));
return serviceRef;
} catch (Exception e) {
logger.warn(e);
return null;
Expand Down
9 changes: 9 additions & 0 deletions src/main/java/io/cryostat/util/URIUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,13 @@ public static URI createAbsolute(String uri) throws URISyntaxException, Relative
public static URI convert(JMXServiceURL serviceUrl) throws URISyntaxException {
return new URI(serviceUrl.toString());
}

public static URI getRmiTarget(JMXServiceURL serviceUrl) throws URISyntaxException {
String rmiPart = "/jndi/rmi://";
String pathPart = serviceUrl.getURLPath();
if (!pathPart.startsWith(rmiPart)) {
throw new IllegalArgumentException(serviceUrl.getURLPath());
}
return new URI(pathPart.substring("/jndi/".length(), pathPart.length()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import java.io.IOException;
import java.net.URI;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import io.cryostat.MainModule;
Expand All @@ -49,6 +50,7 @@
import io.cryostat.net.web.http.api.ApiVersion;
import io.cryostat.platform.PlatformClient;
import io.cryostat.platform.ServiceRef;
import io.cryostat.platform.ServiceRef.AnnotationKey;
import io.cryostat.platform.internal.CustomTargetPlatformClient;

import com.google.gson.Gson;
Expand Down Expand Up @@ -140,6 +142,8 @@ void testSuccessfulRequest() throws Exception {
ServiceRef captured = refCaptor.getValue();
MatcherAssert.assertThat(captured.getServiceUri(), Matchers.equalTo(new URI(connectUrl)));
MatcherAssert.assertThat(captured.getAlias(), Matchers.equalTo(Optional.of(alias)));
MatcherAssert.assertThat(captured.getPlatformAnnotations(), Matchers.equalTo(Map.of()));
MatcherAssert.assertThat(captured.getCryostatAnnotations(), Matchers.equalTo(Map.of()));
MatcherAssert.assertThat(response.getBody(), Matchers.equalTo(captured));
}

Expand Down Expand Up @@ -186,6 +190,38 @@ void testRequestWithDuplicateTarget() throws IOException {
MatcherAssert.assertThat(ex.getStatusCode(), Matchers.equalTo(400));
}

@Test
void testRequestWithAdditionalAnnotations() throws Exception {
MultiMap attrs = MultiMap.caseInsensitiveMultiMap();
RequestParameters params = Mockito.mock(RequestParameters.class);
Mockito.when(params.getFormAttributes()).thenReturn(attrs);
String connectUrl = "service:jmx:rmi:///jndi/rmi://cryostat:9099/jmxrmi";
String alias = "TestTarget";

attrs.set("connectUrl", connectUrl);
attrs.set("alias", alias);
attrs.set("annotations.cryostat.HOST", "app.example.com");
attrs.set("annotations.cryostat.PID", "1234");
attrs.set("annotations.cryostat.MADEUPKEY", "should not appear");

Mockito.when(customTargetPlatformClient.addTarget(Mockito.any())).thenReturn(true);

IntermediateResponse<ServiceRef> response = handler.handle(params);
MatcherAssert.assertThat(response.getStatusCode(), Matchers.equalTo(200));

ArgumentCaptor<ServiceRef> refCaptor = ArgumentCaptor.forClass(ServiceRef.class);
Mockito.verify(customTargetPlatformClient).addTarget(refCaptor.capture());
ServiceRef captured = refCaptor.getValue();
MatcherAssert.assertThat(captured.getServiceUri(), Matchers.equalTo(new URI(connectUrl)));
MatcherAssert.assertThat(captured.getAlias(), Matchers.equalTo(Optional.of(alias)));
MatcherAssert.assertThat(captured.getPlatformAnnotations(), Matchers.equalTo(Map.of()));
MatcherAssert.assertThat(
captured.getCryostatAnnotations(),
Matchers.equalTo(
Map.of(AnnotationKey.HOST, "app.example.com", AnnotationKey.PID, "1234")));
MatcherAssert.assertThat(response.getBody(), Matchers.equalTo(captured));
}

@Test
void testRequestWithIOException() throws IOException {
MultiMap attrs = MultiMap.caseInsensitiveMultiMap();
Expand Down
Loading