This is an automated email from the ASF dual-hosted git repository.
ycai pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/cassandra-sidecar.git
The following commit(s) were added to refs/heads/trunk by this push:
new 07deb07 CASSANDRASC-40 Fix search in list snapshot endpoint
07deb07 is described below
commit 07deb07d2e57a32c67643385403980a9a3c3fc95
Author: Francisco Guerrero <[email protected]>
AuthorDate: Wed Jul 13 10:39:06 2022 -0700
CASSANDRASC-40 Fix search in list snapshot endpoint
This commit fixes test setup in SnapshotUtils. Because of the incorrect
test setup
the execution is providing incorrect results. For example, assume the
following path
/cassandra-test/data/ks/tbl/snapshots/test-snapshot
The test was configuring data directories as ["/cassandra-test/data"], but
in a real
execution data directories is provided as ["/cassandra-test"]. This is
causing the
endpoint to return incorrect values in the JSON payload.
Additionally, the response was providing the port for Cassandra and not the
Sidecar
port.
---
.../common/data/ListSnapshotFilesResponse.java | 6 +-
gradle.properties | 2 +-
spotbugs-exclude.xml | 6 +
.../sidecar/routes/ListSnapshotFilesHandler.java | 159 +++++++++++----------
.../sidecar/snapshots/SnapshotDirectory.java | 54 +++++++
.../sidecar/snapshots/SnapshotPathBuilder.java | 37 +++--
.../org/apache/cassandra/sidecar/TestModule.java | 8 +-
.../apache/cassandra/sidecar/TestSslModule.java | 6 +-
.../routes/ListSnapshotFilesHandlerTest.java | 67 +++++++--
.../sidecar/snapshots/SnapshotDirectoryTest.java | 74 ++++++++++
.../sidecar/snapshots/SnapshotSearchTest.java | 6 +-
.../cassandra/sidecar/snapshots/SnapshotUtils.java | 4 +-
12 files changed, 316 insertions(+), 113 deletions(-)
diff --git
a/common/src/main/java/org/apache/cassandra/sidecar/common/data/ListSnapshotFilesResponse.java
b/common/src/main/java/org/apache/cassandra/sidecar/common/data/ListSnapshotFilesResponse.java
index 5ccd609..bc57050 100644
---
a/common/src/main/java/org/apache/cassandra/sidecar/common/data/ListSnapshotFilesResponse.java
+++
b/common/src/main/java/org/apache/cassandra/sidecar/common/data/ListSnapshotFilesResponse.java
@@ -23,11 +23,15 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
+import com.google.common.annotations.Beta;
+
import com.fasterxml.jackson.annotation.JsonProperty;
/**
- * A class representing a response for the {@link ListSnapshotFilesRequest}
+ * A class representing a response for the {@link ListSnapshotFilesRequest}.
+ * This class is expected to evolve and has been mark with the {@link Beta}
annotation.
*/
+@Beta
public class ListSnapshotFilesResponse
{
private final List<FileInfo> snapshotFilesInfo;
diff --git a/gradle.properties b/gradle.properties
index 7a32a30..fa34b46 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,5 +1,5 @@
version=1.0-SNAPSHOT
junitVersion=5.4.2
kubernetesClientVersion=9.0.0
-cassandra40Version=4.0.4
+cassandra40Version=4.0.5
vertxVersion=4.2.1
diff --git a/spotbugs-exclude.xml b/spotbugs-exclude.xml
index 12c771b..ee76f3c 100644
--- a/spotbugs-exclude.xml
+++ b/spotbugs-exclude.xml
@@ -17,4 +17,10 @@
<Bug pattern="RV_RETURN_VALUE_IGNORED_BAD_PRACTICE" />
</Match>
+ <!-- Ignore DMI_HARDCODED_ABSOLUTE_FILENAME for testing
SnapshotDirectory.of with strings that are paths -->
+ <Match>
+ <Class
name="org.apache.cassandra.sidecar.snapshots.SnapshotDirectoryTest" />
+ <Bug pattern="DMI_HARDCODED_ABSOLUTE_FILENAME" />
+ </Match>
+
</FindBugsFilter>
\ No newline at end of file
diff --git
a/src/main/java/org/apache/cassandra/sidecar/routes/ListSnapshotFilesHandler.java
b/src/main/java/org/apache/cassandra/sidecar/routes/ListSnapshotFilesHandler.java
index 42166c9..5fe8502 100644
---
a/src/main/java/org/apache/cassandra/sidecar/routes/ListSnapshotFilesHandler.java
+++
b/src/main/java/org/apache/cassandra/sidecar/routes/ListSnapshotFilesHandler.java
@@ -20,25 +20,22 @@ package org.apache.cassandra.sidecar.routes;
import java.io.FileNotFoundException;
import java.nio.file.NoSuchFileException;
-import java.nio.file.Path;
-import java.nio.file.Paths;
import java.util.List;
-import org.apache.commons.lang3.tuple.Pair;
+import com.google.common.base.Preconditions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.inject.Inject;
import io.netty.handler.codec.http.HttpResponseStatus;
-import io.vertx.core.file.FileProps;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.net.SocketAddress;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.HttpException;
-import org.apache.cassandra.sidecar.cluster.InstancesConfig;
-import org.apache.cassandra.sidecar.cluster.instance.InstanceMetadata;
+import org.apache.cassandra.sidecar.Configuration;
import org.apache.cassandra.sidecar.common.data.ListSnapshotFilesRequest;
import org.apache.cassandra.sidecar.common.data.ListSnapshotFilesResponse;
+import org.apache.cassandra.sidecar.snapshots.SnapshotDirectory;
import org.apache.cassandra.sidecar.snapshots.SnapshotPathBuilder;
/**
@@ -47,37 +44,26 @@ import
org.apache.cassandra.sidecar.snapshots.SnapshotPathBuilder;
* For example:
*
* <p>
- * /api/v1/snapshots/testSnapshot lists all
SSTable component files for all the
- * "testSnapshot" snapshots
- * <p>
- * /api/v1/snapshots/testSnapshot?includeSecondaryIndexFiles=true lists all
SSTable component files including
- * secondary index files for all the "testSnapshot"
- * snapshots
- * <p>
- * /api/v1/keyspace/ks/table/tbl/snapshots/testSnapshot lists all
SSTable component files for the
- * "testSnapshot" snapshot for the "ks" keyspace
- * and the "tbl" table
+ * /api/v1/keyspace/ks/table/tbl/snapshots/testSnapshot
+ * lists all SSTable component files for the "testSnapshot" snapshot for the
"ks" keyspace and the "tbl" table
* <p>
*
/api/v1/keyspace/ks/table/tbl/snapshots/testSnapshot?includeSecondaryIndexFiles=true
- * lists all SSTable component files including
- * secondary index files for the "testSnapshot"
- * snapshot for the "ks" keyspace and the "tbl"
- * table
+ * lists all SSTable component files including secondary index files for the
"testSnapshot" snapshot for the "ks"
+ * keyspace and the "tbl" table
*/
public class ListSnapshotFilesHandler extends AbstractHandler
{
private static final Logger logger =
LoggerFactory.getLogger(ListSnapshotFilesHandler.class);
private static final String INCLUDE_SECONDARY_INDEX_FILES =
"includeSecondaryIndexFiles";
- private static final int DATA_DIR_INDEX = 0;
- private static final int TABLE_NAME_SUBPATH_INDEX = 1;
- private static final int FILE_NAME_SUBPATH_INDEX = 4;
private final SnapshotPathBuilder builder;
+ private final Configuration configuration;
@Inject
- public ListSnapshotFilesHandler(SnapshotPathBuilder builder,
InstancesConfig instancesConfig)
+ public ListSnapshotFilesHandler(SnapshotPathBuilder builder, Configuration
configuration)
{
- super(instancesConfig);
+ super(configuration.getInstancesConfig());
this.builder = builder;
+ this.configuration = configuration;
}
@Override
@@ -93,70 +79,87 @@ public class ListSnapshotFilesHandler extends
AbstractHandler
boolean secondaryIndexFiles =
requestParams.includeSecondaryIndexFiles();
builder.build(host, requestParams)
- .compose(directory -> builder.listSnapshotDirectory(directory,
secondaryIndexFiles))
- .onSuccess(fileList ->
- {
- if (fileList.isEmpty())
- {
- String payload = "Snapshot '" +
requestParams.getSnapshotName() + "' not found";
- context.fail(new
HttpException(HttpResponseStatus.NOT_FOUND.code(), payload));
- }
- else
- {
- logger.debug("ListSnapshotFilesHandler
handled {} for {}. Instance: {}",
- requestParams, remoteAddress,
host);
- context.json(buildResponse(host,
requestParams, fileList));
- }
- })
- .onFailure(cause ->
- {
- logger.error("ListSnapshotFilesHandler failed
for request: {} from: {}. Instance: {}",
- requestParams, remoteAddress, host);
- if (cause instanceof FileNotFoundException ||
- cause instanceof NoSuchFileException)
- {
- context.fail(new
HttpException(HttpResponseStatus.NOT_FOUND.code(),
-
cause.getMessage()));
- }
- else
- {
- context.fail(new
HttpException(HttpResponseStatus.BAD_REQUEST.code(),
- "Invalid
request for " + requestParams));
- }
- });
+ .onSuccess(snapshotDirectory ->
+ builder.listSnapshotDirectory(snapshotDirectory,
secondaryIndexFiles)
+ .onSuccess(fileList ->
+ {
+ if (fileList.isEmpty())
+ {
+ String payload = "Snapshot '" +
requestParams.getSnapshotName() +
+ "' not found";
+ context.fail(new
HttpException(HttpResponseStatus.NOT_FOUND.code(), payload));
+ }
+ else
+ {
+
logger.debug("ListSnapshotFilesHandler handled {} for {}. Instance: {}",
+ requestParams,
remoteAddress, host);
+ context.json(buildResponse(host,
snapshotDirectory, fileList));
+ }
+ })
+ .onFailure(cause -> processFailure(cause,
context, requestParams, remoteAddress, host))
+ )
+ .onFailure(cause -> processFailure(cause, context,
requestParams, remoteAddress, host));
+ }
+
+ private void processFailure(Throwable cause, RoutingContext context,
ListSnapshotFilesRequest requestParams,
+ SocketAddress remoteAddress, String host)
+ {
+ logger.error("ListSnapshotFilesHandler failed for request: {} from:
{}. Instance: {}",
+ requestParams, remoteAddress, host);
+ if (cause instanceof FileNotFoundException ||
+ cause instanceof NoSuchFileException)
+ {
+ context.fail(new HttpException(HttpResponseStatus.NOT_FOUND.code(),
+ cause.getMessage()));
+ }
+ else
+ {
+ context.fail(new
HttpException(HttpResponseStatus.BAD_REQUEST.code(),
+ "Invalid request for " +
requestParams));
+ }
}
private ListSnapshotFilesResponse buildResponse(String host,
- ListSnapshotFilesRequest
request,
- List<Pair<String,
FileProps>> fileList)
+ String snapshotDirectory,
+
List<SnapshotPathBuilder.SnapshotFile> fileList)
{
- InstanceMetadata instanceMetadata =
instancesConfig.instanceFromHost(host);
- int sidecarPort = instanceMetadata.port();
- Path dataDirPath =
Paths.get(instanceMetadata.dataDirs().get(DATA_DIR_INDEX));
ListSnapshotFilesResponse response = new ListSnapshotFilesResponse();
- String snapshotName = request.getSnapshotName();
+ int sidecarPort = configuration.getPort();
+ SnapshotDirectory directory = SnapshotDirectory.of(snapshotDirectory);
+ int dataDirectoryIndex = dataDirectoryIndex(host,
directory.dataDirectory);
+ int offset = snapshotDirectory.length() + 1;
- for (Pair<String, FileProps> file : fileList)
+ for (SnapshotPathBuilder.SnapshotFile snapshotFile : fileList)
{
- Path pathFromDataDir =
dataDirPath.relativize(Paths.get(file.getLeft()));
-
- String keyspace = request.getKeyspace();
- // table name might include a dash (-) with the table UUID so we
always use it as part of the response
- String tableName =
pathFromDataDir.getName(TABLE_NAME_SUBPATH_INDEX).toString();
- String fileName =
pathFromDataDir.getName(FILE_NAME_SUBPATH_INDEX).toString();
-
- response.addSnapshotFile(new
ListSnapshotFilesResponse.FileInfo(file.getRight().size(),
-
host,
-
sidecarPort,
-
DATA_DIR_INDEX,
-
snapshotName,
-
keyspace,
-
tableName,
-
fileName));
+ int fileNameIndex = snapshotFile.path.indexOf(snapshotDirectory) +
offset;
+ Preconditions.checkArgument(fileNameIndex <
snapshotFile.path.length(),
+ "Invalid snapshot file '" +
snapshotFile.path + "'");
+ response.addSnapshotFile(
+ new ListSnapshotFilesResponse.FileInfo(snapshotFile.size,
+ host,
+ sidecarPort,
+ dataDirectoryIndex,
+ directory.snapshotName,
+ directory.keyspace,
+ directory.tableName,
+
snapshotFile.path.substring(fileNameIndex)));
}
return response;
}
+ private int dataDirectoryIndex(String host, String dataDirectory)
+ {
+ List<String> dataDirs =
instancesConfig.instanceFromHost(host).dataDirs();
+ for (int index = 0; index < dataDirs.size(); index++)
+ {
+ if (dataDirectory.startsWith(dataDirs.get(index)))
+ {
+ return index;
+ }
+ }
+ return -1;
+ }
+
private ListSnapshotFilesRequest extractParamsOrThrow(final RoutingContext
context)
{
boolean includeSecondaryIndexFiles =
diff --git
a/src/main/java/org/apache/cassandra/sidecar/snapshots/SnapshotDirectory.java
b/src/main/java/org/apache/cassandra/sidecar/snapshots/SnapshotDirectory.java
new file mode 100644
index 0000000..215aabf
--- /dev/null
+++
b/src/main/java/org/apache/cassandra/sidecar/snapshots/SnapshotDirectory.java
@@ -0,0 +1,54 @@
+package org.apache.cassandra.sidecar.snapshots;
+
+import java.io.File;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import com.google.common.base.Preconditions;
+
+import static
org.apache.cassandra.sidecar.snapshots.SnapshotPathBuilder.SNAPSHOTS_DIR_NAME;
+
+/**
+ * An object that encapsulates the parts of a snapshot directory
+ */
+public class SnapshotDirectory
+{
+ public final String dataDirectory;
+ public final String keyspace;
+ public final String tableName;
+ public final String snapshotName;
+
+ SnapshotDirectory(String dataDirectory, String keyspace, String tableName,
String snapshotName)
+ {
+ this.dataDirectory = dataDirectory;
+ this.keyspace = keyspace;
+ this.tableName = tableName;
+ this.snapshotName = snapshotName;
+ }
+
+ /**
+ * Parses a snapshot directory string into a {@link SnapshotDirectory}
object. The snapshot directory
+ * has the following structure {@code
/<data_dir>/<ks>/<table>/snapshots/<snapshot_name>}.
+ *
+ * @param snapshotDirectory the absolute path to the snapshot directory
+ * @return the {@link SnapshotDirectory} object representing the provided
{@code snapshotDirectory}
+ */
+ public static SnapshotDirectory of(String snapshotDirectory)
+ {
+ Path snapshotDirectoryPath = Paths.get(snapshotDirectory);
+ int nameCount = snapshotDirectoryPath.getNameCount();
+ Preconditions.checkArgument(nameCount >= 5, "Invalid
snapshotDirectory. " +
+ "Expected at least 5 parts
but found " + nameCount);
+ String snapshotName = snapshotDirectoryPath.getName(nameCount -
1).toString();
+ String snapshotDirName = snapshotDirectoryPath.getName(nameCount -
2).toString();
+ String tableName = snapshotDirectoryPath.getName(nameCount -
3).toString();
+ String keyspace = snapshotDirectoryPath.getName(nameCount -
4).toString();
+ String dataDirectory = File.separator +
snapshotDirectoryPath.subpath(0, nameCount - 4);
+
+
Preconditions.checkArgument(SNAPSHOTS_DIR_NAME.equalsIgnoreCase(snapshotDirName),
+ "Invalid snapshotDirectory. The expected
directory structure is " +
+
"'/<data_dir>/<ks>/<table>/snapshots/<snapshot_name>'");
+
+ return new SnapshotDirectory(dataDirectory, keyspace, tableName,
snapshotName);
+ }
+}
diff --git
a/src/main/java/org/apache/cassandra/sidecar/snapshots/SnapshotPathBuilder.java
b/src/main/java/org/apache/cassandra/sidecar/snapshots/SnapshotPathBuilder.java
index 54736ca..528a1a3 100644
---
a/src/main/java/org/apache/cassandra/sidecar/snapshots/SnapshotPathBuilder.java
+++
b/src/main/java/org/apache/cassandra/sidecar/snapshots/SnapshotPathBuilder.java
@@ -60,7 +60,7 @@ public class SnapshotPathBuilder
{
private static final Logger logger =
LoggerFactory.getLogger(SnapshotPathBuilder.class);
private static final String DATA_SUB_DIR = "/data";
- public static final int SNAPSHOTS_MAX_DEPTH = 4;
+ public static final int SNAPSHOTS_MAX_DEPTH = 5;
public static final String SNAPSHOTS_DIR_NAME = "snapshots";
protected final Vertx vertx;
protected final FileSystem fs;
@@ -129,10 +129,10 @@ public class SnapshotPathBuilder
* @param includeSecondaryIndexFiles whether to include secondary index
files
* @return a future with a list of files inside the snapshot directory
*/
- public Future<List<Pair<String, FileProps>>> listSnapshotDirectory(String
snapshotDirectory,
- boolean
includeSecondaryIndexFiles)
+ public Future<List<SnapshotFile>> listSnapshotDirectory(String
snapshotDirectory,
+ boolean
includeSecondaryIndexFiles)
{
- Promise<List<Pair<String, FileProps>>> promise = Promise.promise();
+ Promise<List<SnapshotFile>> promise = Promise.promise();
// List the snapshot directory
fs.readDir(snapshotDirectory)
@@ -158,10 +158,15 @@ public class SnapshotPathBuilder
{
// Create a pair of path/fileProps for every
regular file
- List<Pair<String, FileProps>> snapshotList =
+ List<SnapshotFile> snapshotList =
IntStream.range(0, list.size())
.filter(i ->
ar.<FileProps>resultAt(i).isRegularFile())
- .mapToObj(i -> Pair.of(list.get(i),
ar.<FileProps>resultAt(i)))
+ .mapToObj(i ->
+ {
+ long size =
ar.<FileProps>resultAt(i).size();
+ return new
SnapshotFile(list.get(i),
+ size);
+ })
.collect(Collectors.toList());
@@ -205,10 +210,10 @@ public class SnapshotPathBuilder
.onSuccess(idx ->
{
//noinspection unchecked
- List<Pair<String,
FileProps>> idxPropList =
+ List<SnapshotFile>
idxPropList =
idx.list()
.stream()
- .flatMap(l ->
((List<Pair<String, FileProps>>) l).stream())
+ .flatMap(l ->
((List<SnapshotFile>) l).stream())
.collect(Collectors.toList());
// aggregate the results
and return the full list
@@ -249,7 +254,6 @@ public class SnapshotPathBuilder
return vertx.executeBlocking(promise ->
{
-
// a filter to keep directories ending in
"/snapshots/<snapshotName>"
BiPredicate<Path, BasicFileAttributes> filter = (path,
basicFileAttributes) ->
{
@@ -512,4 +516,19 @@ public class SnapshotPathBuilder
});
return promise.future();
}
+
+ /**
+ * Class representing a snapshot component file
+ */
+ public static class SnapshotFile
+ {
+ public final String path;
+ public final long size;
+
+ SnapshotFile(String path, long size)
+ {
+ this.path = path;
+ this.size = size;
+ }
+ }
}
diff --git a/src/test/java/org/apache/cassandra/sidecar/TestModule.java
b/src/test/java/org/apache/cassandra/sidecar/TestModule.java
index bd83707..5006909 100644
--- a/src/test/java/org/apache/cassandra/sidecar/TestModule.java
+++ b/src/test/java/org/apache/cassandra/sidecar/TestModule.java
@@ -59,15 +59,15 @@ public class TestModule extends AbstractModule
@Provides
@Singleton
- public Configuration configuration()
+ public Configuration configuration(InstancesConfig instancesConfig)
{
- return abstractConfig();
+ return abstractConfig(instancesConfig);
}
- protected Configuration abstractConfig()
+ protected Configuration abstractConfig(InstancesConfig instancesConfig)
{
return new Configuration.Builder()
- .setInstancesConfig(getInstancesConfig())
+ .setInstancesConfig(instancesConfig)
.setHost("127.0.0.1")
.setPort(6475)
.setHealthCheckFrequency(1000)
diff --git a/src/test/java/org/apache/cassandra/sidecar/TestSslModule.java
b/src/test/java/org/apache/cassandra/sidecar/TestSslModule.java
index a76ce13..a9def61 100644
--- a/src/test/java/org/apache/cassandra/sidecar/TestSslModule.java
+++ b/src/test/java/org/apache/cassandra/sidecar/TestSslModule.java
@@ -23,6 +23,8 @@ import java.io.File;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.apache.cassandra.sidecar.cluster.InstancesConfig;
+
/**
* Changes to the TestModule to define SSL dependencies
*/
@@ -32,7 +34,7 @@ public class TestSslModule extends TestModule
@Override
- public Configuration abstractConfig()
+ public Configuration abstractConfig(InstancesConfig instancesConfig)
{
final String keyStorePath =
TestSslModule.class.getClassLoader().getResource("certs/test.p12").getPath();
final String keyStorePassword = "password";
@@ -50,7 +52,7 @@ public class TestSslModule extends TestModule
}
return new Configuration.Builder()
- .setInstancesConfig(getInstancesConfig())
+ .setInstancesConfig(instancesConfig)
.setHost("127.0.0.1")
.setPort(6475)
.setHealthCheckFrequency(1000)
diff --git
a/src/test/java/org/apache/cassandra/sidecar/routes/ListSnapshotFilesHandlerTest.java
b/src/test/java/org/apache/cassandra/sidecar/routes/ListSnapshotFilesHandlerTest.java
index 7c959c9..9f9765e 100644
---
a/src/test/java/org/apache/cassandra/sidecar/routes/ListSnapshotFilesHandlerTest.java
+++
b/src/test/java/org/apache/cassandra/sidecar/routes/ListSnapshotFilesHandlerTest.java
@@ -20,6 +20,8 @@ package org.apache.cassandra.sidecar.routes;
import java.io.File;
import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -34,6 +36,8 @@ import org.slf4j.LoggerFactory;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
+import com.google.inject.Provides;
+import com.google.inject.Singleton;
import com.google.inject.util.Modules;
import io.vertx.core.Vertx;
import io.vertx.core.http.HttpServer;
@@ -103,7 +107,7 @@ public class ListSnapshotFilesHandlerTest
ListSnapshotFilesResponse.FileInfo fileInfoExpected =
new ListSnapshotFilesResponse.FileInfo(11,
"localhost",
- 9043,
+ 6475,
0,
"snapshot1",
"keyspace1",
@@ -112,7 +116,7 @@ public class ListSnapshotFilesHandlerTest
ListSnapshotFilesResponse.FileInfo fileInfoNotExpected =
new ListSnapshotFilesResponse.FileInfo(11,
"localhost",
- 9043,
+ 6475,
0,
"snapshot1",
"keyspace1",
@@ -131,6 +135,51 @@ public class ListSnapshotFilesHandlerTest
})));
}
+ @Test
+ public void testRouteSucceedsIncludeSecondaryIndexes(VertxTestContext
context)
+ {
+ WebClient client = WebClient.create(vertx);
+ String testRoute = "/api/v1/keyspace/keyspace1/table/table1-1234" +
+
"/snapshots/snapshot1?includeSecondaryIndexFiles=true";
+ List<ListSnapshotFilesResponse.FileInfo> fileInfoExpected =
Arrays.asList(
+ new ListSnapshotFilesResponse.FileInfo(11,
+ "localhost",
+ 6475,
+ 0,
+ "snapshot1",
+ "keyspace1",
+ "table1-1234",
+ "1.db"),
+ new ListSnapshotFilesResponse.FileInfo(0,
+ "localhost",
+ 6475,
+ 0,
+ "snapshot1",
+ "keyspace1",
+ "table1-1234",
+ ".index/secondary.db")
+ );
+ ListSnapshotFilesResponse.FileInfo fileInfoNotExpected =
+ new ListSnapshotFilesResponse.FileInfo(11,
+ "localhost",
+ 6475,
+ 0,
+ "snapshot1",
+ "keyspace1",
+ "table1-1234",
+ "2.db");
+
+ client.get(config.getPort(), "localhost", testRoute)
+ .send(context.succeeding(response -> context.verify(() ->
+ {
+ assertThat(response.statusCode()).isEqualTo(OK.code());
+ ListSnapshotFilesResponse resp =
response.bodyAsJson(ListSnapshotFilesResponse.class);
+
assertThat(resp.getSnapshotFilesInfo()).containsAll(fileInfoExpected);
+
assertThat(resp.getSnapshotFilesInfo()).doesNotContain(fileInfoNotExpected);
+ context.completeNow();
+ })));
+ }
+
@Test
public void testRouteInvalidSnapshot(VertxTestContext context)
{
@@ -147,17 +196,11 @@ public class ListSnapshotFilesHandlerTest
class ListSnapshotTestModule extends AbstractModule
{
- @Override
- protected void configure()
+ @Provides
+ @Singleton
+ public InstancesConfig getInstancesConfig() throws IOException
{
- try
- {
-
bind(InstancesConfig.class).toInstance(mockInstancesConfig(temporaryFolder.getCanonicalPath()));
- }
- catch (IOException e)
- {
- throw new RuntimeException(e);
- }
+ return mockInstancesConfig(temporaryFolder.getCanonicalPath());
}
}
}
diff --git
a/src/test/java/org/apache/cassandra/sidecar/snapshots/SnapshotDirectoryTest.java
b/src/test/java/org/apache/cassandra/sidecar/snapshots/SnapshotDirectoryTest.java
new file mode 100644
index 0000000..dfc0aa1
--- /dev/null
+++
b/src/test/java/org/apache/cassandra/sidecar/snapshots/SnapshotDirectoryTest.java
@@ -0,0 +1,74 @@
+package org.apache.cassandra.sidecar.snapshots;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static
org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
+
+class SnapshotDirectoryTest
+{
+
+ @ParameterizedTest
+ @ValueSource(strings = { "not-valid", "/two-levels/not-valid",
"three/levels/not-valid", "four/levels/not/valid" })
+ void failsOnInvalidLengthDirectory()
+ {
+ assertThatIllegalArgumentException()
+ .isThrownBy(() -> SnapshotDirectory.of("not-valid"))
+ .withMessageContaining("Invalid snapshotDirectory. Expected at least 5
parts but found");
+ }
+
+ @Test
+ void failsOnInvalidDirectory()
+ {
+ assertThatIllegalArgumentException()
+ .isThrownBy(() ->
SnapshotDirectory.of("/cassandra/data/ks1/tbl2/sneaky/test-snapshot"))
+ .withMessage("Invalid snapshotDirectory. The expected directory
structure is " +
+ "'/<data_dir>/<ks>/<table>/snapshots/<snapshot_name>'");
+ }
+
+ @Test
+ void testValidDirectory1()
+ {
+ String snapshotDirectory =
"/cassandra/data/ks1/tbl2/snapshots/test-snapshot";
+ SnapshotDirectory directory = SnapshotDirectory.of(snapshotDirectory);
+ assertThat(directory.dataDirectory).isEqualTo("/cassandra/data");
+ assertThat(directory.keyspace).isEqualTo("ks1");
+ assertThat(directory.tableName).isEqualTo("tbl2");
+ assertThat(directory.snapshotName).isEqualTo("test-snapshot");
+ }
+
+ @Test
+ void testValidDirectory2()
+ {
+ String snapshotDirectory =
"/cassandra/data/ks1/tbl2/SNAPSHOTS/test-snapshot";
+ SnapshotDirectory directory = SnapshotDirectory.of(snapshotDirectory);
+ assertThat(directory.dataDirectory).isEqualTo("/cassandra/data");
+ assertThat(directory.keyspace).isEqualTo("ks1");
+ assertThat(directory.tableName).isEqualTo("tbl2");
+ assertThat(directory.snapshotName).isEqualTo("test-snapshot");
+ }
+
+ @Test
+ void testValidDirectory3()
+ {
+ String snapshotDirectory =
"/datadir/inventory/shipping/snapshots/2022-07-23";
+ SnapshotDirectory directory = SnapshotDirectory.of(snapshotDirectory);
+ assertThat(directory.dataDirectory).isEqualTo("/datadir");
+ assertThat(directory.keyspace).isEqualTo("inventory");
+ assertThat(directory.tableName).isEqualTo("shipping");
+ assertThat(directory.snapshotName).isEqualTo("2022-07-23");
+ }
+
+ @Test
+ void testValidDirectory4()
+ {
+ String snapshotDirectory =
"/cassandra/disk1/data/inventory/shipping/snapshots/2022-07-23/";
+ SnapshotDirectory directory = SnapshotDirectory.of(snapshotDirectory);
+ assertThat(directory.dataDirectory).isEqualTo("/cassandra/disk1/data");
+ assertThat(directory.keyspace).isEqualTo("inventory");
+ assertThat(directory.tableName).isEqualTo("shipping");
+ assertThat(directory.snapshotName).isEqualTo("2022-07-23");
+ }
+}
diff --git
a/src/test/java/org/apache/cassandra/sidecar/snapshots/SnapshotSearchTest.java
b/src/test/java/org/apache/cassandra/sidecar/snapshots/SnapshotSearchTest.java
index 2649c32..7d10c8d 100644
---
a/src/test/java/org/apache/cassandra/sidecar/snapshots/SnapshotSearchTest.java
+++
b/src/test/java/org/apache/cassandra/sidecar/snapshots/SnapshotSearchTest.java
@@ -26,7 +26,6 @@ import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
-import org.apache.commons.lang3.tuple.Pair;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -35,7 +34,6 @@ import org.junit.jupiter.api.io.TempDir;
import io.vertx.core.CompositeFuture;
import io.vertx.core.Future;
import io.vertx.core.Vertx;
-import io.vertx.core.file.FileProps;
import io.vertx.junit5.VertxExtension;
import io.vertx.junit5.VertxTestContext;
import org.apache.cassandra.sidecar.cluster.InstancesConfig;
@@ -145,8 +143,8 @@ public class SnapshotSearchTest
//noinspection unchecked
List<String> snapshotFiles = ar.list()
.stream()
- .flatMap(l -> ((List<Pair<String,
FileProps>>) l).stream())
- .map(Pair::getLeft)
+ .flatMap(l ->
((List<SnapshotPathBuilder.SnapshotFile>) l).stream())
+ .map(snapshotFile -> snapshotFile.path)
.sorted()
.collect(Collectors.toList());
diff --git
a/src/test/java/org/apache/cassandra/sidecar/snapshots/SnapshotUtils.java
b/src/test/java/org/apache/cassandra/sidecar/snapshots/SnapshotUtils.java
index f16605d..d482fbd 100644
--- a/src/test/java/org/apache/cassandra/sidecar/snapshots/SnapshotUtils.java
+++ b/src/test/java/org/apache/cassandra/sidecar/snapshots/SnapshotUtils.java
@@ -81,14 +81,14 @@ public class SnapshotUtils
InstanceMetadataImpl localhost = new InstanceMetadataImpl(1,
"localhost",
9043,
-
Collections.singletonList(rootPath + "/d1/data"),
+
Collections.singletonList(rootPath + "/d1"),
null,
versionProvider,
1000);
InstanceMetadataImpl localhost2 = new InstanceMetadataImpl(2,
"localhost2",
9043,
-
Collections.singletonList(rootPath + "/d2/data"),
+
Collections.singletonList(rootPath + "/d2"),
null,
versionProvider,
1000);
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]