Skip to content

Commit

Permalink
feat(graphQL): add missing labels/archives subqueries/mutations (#319)
Browse files Browse the repository at this point in the history
  • Loading branch information
aali309 authored and andrewazores committed Apr 13, 2024
1 parent 82319cf commit 4c50721
Show file tree
Hide file tree
Showing 6 changed files with 178 additions and 5 deletions.
34 changes: 34 additions & 0 deletions src/main/java/io/cryostat/graphql/ActiveRecordings.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import java.time.Duration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
Expand Down Expand Up @@ -322,6 +323,39 @@ public RecordingOptions asOptions() {
}
}

@Blocking
@Transactional
@Description("Updates the metadata labels for an existing Flight Recording.")
public Uni<ActiveRecording> doPutMetadata(
@Source ActiveRecording recording, MetadataLabels metadataInput) {
return Uni.createFrom()
.item(
() -> {
return recordingHelper.updateRecordingMetadata(
recording.id, metadataInput.getLabels());
});
}

@SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
public static class MetadataLabels {

private Map<String, String> labels;

public MetadataLabels() {}

public MetadataLabels(Map<String, String> labels) {
this.labels = new HashMap<>(labels);
}

public Map<String, String> getLabels() {
return new HashMap<>(labels);
}

public void setLabels(Map<String, String> labels) {
this.labels = new HashMap<>(labels);
}
}

@SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
public static class RecordingMetadata {
public @Nullable Map<String, String> labels;
Expand Down
28 changes: 28 additions & 0 deletions src/main/java/io/cryostat/graphql/ArchivedRecordings.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,20 @@
import java.util.Objects;
import java.util.function.Predicate;

import io.cryostat.graphql.ActiveRecordings.MetadataLabels;
import io.cryostat.graphql.TargetNodes.AggregateInfo;
import io.cryostat.graphql.TargetNodes.Recordings;
import io.cryostat.graphql.matchers.LabelSelectorMatcher;
import io.cryostat.recordings.RecordingHelper;
import io.cryostat.recordings.Recordings.ArchivedRecording;
import io.cryostat.recordings.Recordings.Metadata;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import io.smallrye.common.annotation.Blocking;
import io.smallrye.graphql.api.Nullable;
import jakarta.inject.Inject;
import org.eclipse.microprofile.graphql.GraphQLApi;
import org.eclipse.microprofile.graphql.NonNull;
import org.eclipse.microprofile.graphql.Query;
import org.eclipse.microprofile.graphql.Source;

Expand Down Expand Up @@ -69,6 +72,31 @@ public TargetNodes.ArchivedRecordings archived(
return out;
}

@NonNull
public ArchivedRecording doDelete(@Source ArchivedRecording recording) {
recordingHelper.deleteArchivedRecording(recording.jvmId(), recording.name());
return recording;
}

@NonNull
public ArchivedRecording doPutMetadata(
@Source ArchivedRecording recording, MetadataLabels metadataInput) {
recordingHelper.updateArchivedRecordingMetadata(
recording.jvmId(), recording.name(), metadataInput.getLabels());

String downloadUrl = recordingHelper.downloadUrl(recording.jvmId(), recording.name());
String reportUrl = recordingHelper.reportUrl(recording.jvmId(), recording.name());

return new ArchivedRecording(
recording.jvmId(),
recording.name(),
downloadUrl,
reportUrl,
new Metadata(metadataInput.getLabels()),
recording.size(),
recording.archivedTime());
}

@SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
public static class ArchivedRecordingsFilter implements Predicate<ArchivedRecording> {
public @Nullable String name;
Expand Down
11 changes: 9 additions & 2 deletions src/main/java/io/cryostat/recordings/ActiveRecording.java
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,10 @@ public static ActiveRecording getByName(String name) {
return find("name", name).singleResult();
}

public void setMetadata(Metadata metadata) {
this.metadata = metadata;
}

@Transactional
public static boolean deleteFromTarget(Target target, String recordingName) {
Optional<ActiveRecording> recording =
Expand Down Expand Up @@ -282,16 +286,19 @@ public record ActiveRecordingEvent(
Objects.requireNonNull(payload);
}

public record Payload(String target, LinkedRecordingDescriptor recording) {
public record Payload(
String target, LinkedRecordingDescriptor recording, String jvmId) {
public Payload {
Objects.requireNonNull(target);
Objects.requireNonNull(recording);
Objects.requireNonNull(jvmId);
}

public static Payload of(RecordingHelper helper, ActiveRecording recording) {
return new Payload(
recording.target.connectUrl.toString(),
helper.toExternalForm(recording));
helper.toExternalForm(recording),
recording.target.jvmId);
}
}
}
Expand Down
88 changes: 88 additions & 0 deletions src/main/java/io/cryostat/recordings/RecordingHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
import jakarta.inject.Named;
import jakarta.transaction.Transactional;
import jakarta.ws.rs.BadRequestException;
import jakarta.ws.rs.NotFoundException;
import jakarta.ws.rs.ServerErrorException;
import jdk.jfr.RecordingState;
import org.apache.commons.codec.binary.Base64;
Expand All @@ -108,8 +109,10 @@
import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
import software.amazon.awssdk.services.s3.model.GetObjectTaggingRequest;
import software.amazon.awssdk.services.s3.model.HeadObjectRequest;
import software.amazon.awssdk.services.s3.model.ListObjectsV2Request;
import software.amazon.awssdk.services.s3.model.NoSuchKeyException;
import software.amazon.awssdk.services.s3.model.PutObjectTaggingRequest;
import software.amazon.awssdk.services.s3.model.S3Object;
import software.amazon.awssdk.services.s3.model.Tag;
import software.amazon.awssdk.services.s3.model.Tagging;
Expand All @@ -135,6 +138,7 @@ public class RecordingHelper {
@Inject EventOptionsBuilder.Factory eventOptionsBuilderFactory;
@Inject TargetTemplateService.Factory targetTemplateServiceFactory;
@Inject S3TemplateService customTemplateService;
@Inject RecordingHelper recordingHelper;

@Inject
@Named(Producers.BASE64_URL)
Expand Down Expand Up @@ -548,6 +552,7 @@ public List<ArchivedRecording> listArchivedRecordings() {
getArchivedRecordingMetadata(jvmId, filename)
.orElseGet(Metadata::empty);
return new ArchivedRecording(
jvmId,
filename,
downloadUrl(jvmId, filename),
reportUrl(jvmId, filename),
Expand Down Expand Up @@ -584,6 +589,7 @@ public List<ArchivedRecording> listArchivedRecordings(String jvmId) {
getArchivedRecordingMetadata(jvmId, filename)
.orElseGet(Metadata::empty);
return new ArchivedRecording(
jvmId,
filename,
downloadUrl(jvmId, filename),
reportUrl(jvmId, filename),
Expand Down Expand Up @@ -721,6 +727,7 @@ public ArchivedRecording archiveRecording(
new Notification(event.category().category(), event.payload()));
}
return new ArchivedRecording(
activeRecording.target.jvmId,
filename,
downloadUrl(activeRecording.target.jvmId, filename),
reportUrl(activeRecording.target.jvmId, filename),
Expand Down Expand Up @@ -853,6 +860,7 @@ public void deleteArchivedRecording(String jvmId, String filename) {
ArchivedRecordingEvent.Payload.of(
target.map(t -> t.connectUrl).orElse(null),
new ArchivedRecording(
jvmId,
filename,
downloadUrl(jvmId, filename),
reportUrl(jvmId, filename),
Expand Down Expand Up @@ -929,6 +937,86 @@ private Metadata taggingToMetadata(List<Tag> tagSet) {
return new Metadata(labels, expiry);
}

public ActiveRecording updateRecordingMetadata(
long recordingId, Map<String, String> newLabels) {
ActiveRecording recording = ActiveRecording.findById(recordingId);

if (recording == null) {
throw new NotFoundException("Recording not found for ID: " + recordingId);
}

if (!recording.metadata.labels().equals(newLabels)) {
Metadata updatedMetadata = new Metadata(newLabels);
recording.setMetadata(updatedMetadata);
recording.persist();

notify(
new ActiveRecordingEvent(
Recordings.RecordingEventCategory.METADATA_UPDATED,
ActiveRecordingEvent.Payload.of(recordingHelper, recording)));
}
return recording;
}

private void notify(ActiveRecordingEvent event) {
bus.publish(
MessagingServer.class.getName(),
new Notification(event.category().category(), event.payload()));
}

public ArchivedRecording updateArchivedRecordingMetadata(
String jvmId, String filename, Map<String, String> updatedLabels) {
String key = archivedRecordingKey(jvmId, filename);
Optional<Metadata> existingMetadataOpt = getArchivedRecordingMetadata(key);

if (existingMetadataOpt.isEmpty()) {
throw new NotFoundException(
"Could not find metadata for archived recording with key: " + key);
}

Metadata updatedMetadata = new Metadata(updatedLabels);

Tagging tagging = createMetadataTagging(updatedMetadata);
storage.putObjectTagging(
PutObjectTaggingRequest.builder()
.bucket(archiveBucket)
.key(key)
.tagging(tagging)
.build());

var response =
storage.headObject(
HeadObjectRequest.builder().bucket(archiveBucket).key(key).build());
long size = response.contentLength();
Instant lastModified = response.lastModified();

ArchivedRecording updatedRecording =
new ArchivedRecording(
jvmId,
filename,
downloadUrl(jvmId, filename),
reportUrl(jvmId, filename),
updatedMetadata,
size,
lastModified.getEpochSecond());

notifyArchiveMetadataUpdate(updatedRecording);
return updatedRecording;
}

private void notifyArchiveMetadataUpdate(ArchivedRecording updatedRecording) {

var event =
new ArchivedRecordingEvent(
Recordings.RecordingEventCategory.METADATA_UPDATED,
new ArchivedRecordingEvent.Payload(
updatedRecording.downloadUrl(), updatedRecording));
bus.publish(event.category().category(), event.payload().recording());
bus.publish(
MessagingServer.class.getName(),
new Notification(event.category().category(), event.payload()));
}

public Uni<String> uploadToJFRDatasource(long targetEntityId, long remoteId) throws Exception {
Target target = Target.getTargetById(targetEntityId);
Objects.requireNonNull(target, "Target from targetId not found");
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/io/cryostat/recordings/Recordings.java
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ public void agentPush(
ArchivedRecordingEvent.Payload.of(
target.map(t -> t.connectUrl).orElse(null),
new ArchivedRecording(
jvmId,
recording.fileName(),
recordingHelper.downloadUrl(jvmId, recording.fileName()),
recordingHelper.reportUrl(jvmId, recording.fileName()),
Expand Down Expand Up @@ -275,6 +276,7 @@ public List<ArchivedRecording> agentGet(@RestPath String jvmId) {
.orElseGet(Metadata::empty);
result.add(
new ArchivedRecording(
jvmId,
filename,
recordingHelper.downloadUrl(jvmId, filename),
recordingHelper.reportUrl(jvmId, filename),
Expand Down Expand Up @@ -338,6 +340,7 @@ Map<String, Object> doUpload(FileUpload recording, Metadata metadata, String jvm
ArchivedRecordingEvent.Payload.of(
target.map(t -> t.connectUrl).orElse(null),
new ArchivedRecording(
jvmId,
filename,
recordingHelper.downloadUrl(jvmId, filename),
recordingHelper.reportUrl(jvmId, filename),
Expand Down Expand Up @@ -395,6 +398,7 @@ public Collection<ArchivedRecordingDirectory> listFsArchives() {
connectUrl, id, new ArrayList<>()));
dir.recordings.add(
new ArchivedRecording(
jvmId,
filename,
recordingHelper.downloadUrl(jvmId, filename),
recordingHelper.reportUrl(jvmId, filename),
Expand Down Expand Up @@ -432,6 +436,7 @@ public Collection<ArchivedRecordingDirectory> listFsArchives(@RestPath String jv
connectUrl, id, new ArrayList<>()));
dir.recordings.add(
new ArchivedRecording(
jvmId,
filename,
recordingHelper.downloadUrl(jvmId, filename),
recordingHelper.reportUrl(jvmId, filename),
Expand Down Expand Up @@ -768,6 +773,7 @@ public void deleteArchivedRecording(@RestPath String jvmId, @RestPath String fil
ArchivedRecordingEvent.Payload.of(
URI.create(connectUrl),
new ArchivedRecording(
jvmId,
filename,
recordingHelper.downloadUrl(jvmId, filename),
recordingHelper.reportUrl(jvmId, filename),
Expand Down Expand Up @@ -1127,13 +1133,15 @@ public record LinkedRecordingDescriptor(

// TODO include jvmId and filename
public record ArchivedRecording(
String jvmId,
String name,
String downloadUrl,
String reportUrl,
Metadata metadata,
long size,
long archivedTime) {
public ArchivedRecording {
Objects.requireNonNull(jvmId);
Objects.requireNonNull(name);
Objects.requireNonNull(downloadUrl);
Objects.requireNonNull(reportUrl);
Expand Down Expand Up @@ -1183,6 +1191,7 @@ public static Metadata empty() {
public static final String ACTIVE_RECORDING_DELETED = "ActiveRecordingDeleted";
public static final String ACTIVE_RECORDING_SAVED = "ActiveRecordingSaved";
public static final String SNAPSHOT_RECORDING_CREATED = "SnapshotCreated";
public static final String RECORDING_METADATA_UPDATED = "RecordingMetadataUpdated";

public enum RecordingEventCategory {
ACTIVE_CREATED(ACTIVE_RECORDING_CREATED),
Expand All @@ -1192,6 +1201,7 @@ public enum RecordingEventCategory {
ARCHIVED_CREATED(ARCHIVED_RECORDING_CREATED),
ARCHIVED_DELETED(ARCHIVED_RECORDING_DELETED),
SNAPSHOT_CREATED(SNAPSHOT_RECORDING_CREATED),
METADATA_UPDATED(RECORDING_METADATA_UPDATED),
;

private final String category;
Expand Down
12 changes: 9 additions & 3 deletions src/main/java/io/cryostat/targets/Target.java
Original file line number Diff line number Diff line change
Expand Up @@ -188,10 +188,11 @@ public enum EventKind {
}

@SuppressFBWarnings(value = {"EI_EXPOSE_REP", "EI_EXPOSE_REP2"})
public record TargetDiscovery(EventKind kind, Target serviceRef) {
public record TargetDiscovery(EventKind kind, Target serviceRef, String jvmId) {
public TargetDiscovery {
Objects.requireNonNull(kind);
Objects.requireNonNull(serviceRef);
Objects.requireNonNull(jvmId);
}
}

Expand Down Expand Up @@ -275,14 +276,19 @@ private void notify(EventKind eventKind, Target target) {
MessagingServer.class.getName(),
new Notification(
TARGET_JVM_DISCOVERY,
new TargetDiscoveryEvent(new TargetDiscovery(eventKind, target))));
bus.publish(TARGET_JVM_DISCOVERY, new TargetDiscovery(eventKind, target));
new TargetDiscoveryEvent(
new TargetDiscovery(eventKind, target, target.jvmId))));
bus.publish(TARGET_JVM_DISCOVERY, new TargetDiscovery(eventKind, target, target.jvmId));
}

public record TargetDiscoveryEvent(TargetDiscovery event) {
public TargetDiscoveryEvent {
Objects.requireNonNull(event);
}

public String jvmId() {
return event.serviceRef().jvmId;
}
}
}
}

0 comments on commit 4c50721

Please sign in to comment.