diff --git a/src/main/java/io/cryostat/recordings/ArchivedRecordings.java b/src/main/java/io/cryostat/recordings/ArchivedRecordings.java index b3feb4334..8d7302828 100644 --- a/src/main/java/io/cryostat/recordings/ArchivedRecordings.java +++ b/src/main/java/io/cryostat/recordings/ArchivedRecordings.java @@ -435,12 +435,12 @@ public void deleteArchivedRecording(@RestPath String jvmId, @RestPath String fil logger.tracev( "Sending S3 deletion request for {0} {1}", bucket, recordingHelper.archivedRecordingKey(jvmId, filename)); + + var key = recordingHelper.archivedRecordingKey(jvmId, filename); + storage.headObject(HeadObjectRequest.builder().bucket(bucket).key(key).build()) + .sdkHttpResponse(); var resp = - storage.deleteObject( - DeleteObjectRequest.builder() - .bucket(bucket) - .key(recordingHelper.archivedRecordingKey(jvmId, filename)) - .build()); + storage.deleteObject(DeleteObjectRequest.builder().bucket(bucket).key(key).build()); logger.tracev( "Got SDK response {0} {1}", resp.sdkHttpResponse().statusCode(), resp.sdkHttpResponse().statusText()); @@ -474,18 +474,20 @@ public void deleteArchivedRecording(@RestPath String jvmId, @RestPath String fil @Path("/api/v4/grafana/{encodedKey}") @RolesAllowed("write") public Uni uploadArchivedToGrafana(@RestPath String encodedKey) throws Exception { - var key = recordingHelper.decodedKey(encodedKey); + var pair = recordingHelper.decodedKey(encodedKey); + var key = recordingHelper.archivedRecordingKey(pair); + storage.headObject(HeadObjectRequest.builder().bucket(bucket).key(key).build()) + .sdkHttpResponse(); var found = recordingHelper.listArchivedRecordingObjects().stream() .anyMatch( - o -> - Objects.equals( - o.key(), - recordingHelper.archivedRecordingKey(key))); + o -> { + return Objects.equals(o.key(), key); + }); if (!found) { throw new NotFoundException(); } - return recordingHelper.uploadToJFRDatasource(key); + return recordingHelper.uploadToJFRDatasource(pair); } @GET @@ -495,6 +497,10 @@ public Uni uploadArchivedToGrafana(@RestPath String encodedKey) throws E public RestResponse handleStorageDownload( @RestPath String encodedKey, @RestQuery String f) throws URISyntaxException { Pair pair = recordingHelper.decodedKey(encodedKey); + String key = recordingHelper.archivedRecordingKey(pair); + + storage.headObject(HeadObjectRequest.builder().bucket(bucket).key(key).build()) + .sdkHttpResponse(); if (!presignedDownloadsEnabled) { return ResponseBuilder.ok() @@ -507,11 +513,7 @@ public RestResponse handleStorageDownload( } logger.tracev("Handling presigned download request for {0}", pair); - GetObjectRequest getRequest = - GetObjectRequest.builder() - .bucket(bucket) - .key(recordingHelper.archivedRecordingKey(pair)) - .build(); + GetObjectRequest getRequest = GetObjectRequest.builder().bucket(bucket).key(key).build(); GetObjectPresignRequest presignRequest = GetObjectPresignRequest.builder() .signatureDuration(Duration.ofMinutes(1)) diff --git a/src/main/java/io/cryostat/recordings/RecordingHelper.java b/src/main/java/io/cryostat/recordings/RecordingHelper.java index 1eb9c2400..d40850e48 100644 --- a/src/main/java/io/cryostat/recordings/RecordingHelper.java +++ b/src/main/java/io/cryostat/recordings/RecordingHelper.java @@ -967,6 +967,9 @@ public String encodedKey(String jvmId, String filename) { public Pair decodedKey(String encodedKey) { String key = new String(base64Url.decode(encodedKey), StandardCharsets.UTF_8); String[] parts = key.split("/"); + if (parts.length != 2) { + throw new IllegalArgumentException(); + } return Pair.of(parts[0], parts[1]); } diff --git a/src/test/java/io/cryostat/recordings/ArchivedRecordingsTest.java b/src/test/java/io/cryostat/recordings/ArchivedRecordingsTest.java new file mode 100644 index 000000000..2fbcbad6b --- /dev/null +++ b/src/test/java/io/cryostat/recordings/ArchivedRecordingsTest.java @@ -0,0 +1,136 @@ +/* + * Copyright The Cryostat Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.cryostat.recordings; + +import static io.restassured.RestAssured.given; + +import io.cryostat.AbstractTransactionalTestBase; + +import io.quarkus.test.common.http.TestHTTPEndpoint; +import io.quarkus.test.junit.QuarkusTest; +import io.restassured.http.ContentType; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.Test; + +@QuarkusTest +@TestHTTPEndpoint(ArchivedRecordings.class) +// TODO add tests storing new archived recordings, listing, downloading, and deleting them - already +// largely handled by itest.RecordingWorkflowTest +public class ArchivedRecordingsTest extends AbstractTransactionalTestBase { + + @Test + void testListNone() { + given().log() + .all() + .when() + .get("/api/v4/recordings") + .then() + .assertThat() + .contentType(ContentType.JSON) + .statusCode(200); + } + + @Test + void testListFsNone() { + given().log() + .all() + .when() + .get("/api/beta/fs/recordings") + .then() + .assertThat() + .contentType(ContentType.JSON) + .statusCode(200); + } + + @Test + void testListFsInvalid() { + given().log() + .all() + .when() + .get("/api/beta/fs/recordings/abcd1234") + .then() + .assertThat() + .contentType(ContentType.JSON) + .statusCode(200) + .body("size()", Matchers.equalTo(0)); + } + + @Test + void testDeleteNone() { + given().log() + .all() + .when() + .delete("/api/v4/recordings/nothing") + .then() + .assertThat() + .statusCode(404); + } + + @Test + void testDeleteFsInvalid() { + given().log() + .all() + .when() + .delete("/api/beta/fs/recordings/abcd1234/nothing") + .then() + .assertThat() + .statusCode(404); + } + + @Test + void testUploadGrafanaInvalid() { + given().log() + .all() + .when() + .post("/api/v4/grafana/abcd1234") + .then() + .assertThat() + .statusCode(400); + } + + @Test + void testUploadGrafanaNotFound() { + given().log() + .all() + .when() + .post("/api/v4/grafana/Zm9vL2Jhcg==") + .then() + .assertThat() + .statusCode(404); + } + + @Test + void testDownloadInvalid() { + given().log() + .all() + .when() + .get("/api/v4/download/abcd1234") + .then() + .assertThat() + .statusCode(400); + } + + @Test + void testDownloadNotFound() { + given().log() + .all() + .when() + .get("/api/v4/download/Zm9vL2Jhcg==") + .then() + .assertThat() + .statusCode(404); + } +}