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 252d983 CASSANDRASC-48: Adding new endpoint that gives information
about the release version & partitioner name of a node
252d983 is described below
commit 252d983cc11af3dee3588a1c328eac7a080bd462
Author: jkonisa <[email protected]>
AuthorDate: Thu Nov 10 12:06:09 2022 -0800
CASSANDRASC-48: Adding new endpoint that gives information about the
release version & partitioner name of a node
Adds new APIs
GET /api/v1/cassandra/status
- Returns the NodeStatus with release version & partitioner info of the
node receiving the request
GET /api/v1/cassandra/status?instanceId={id}
- Returs the NodeStatus with release version & partitioner info of a
given instanceId
patch by Jyothsna Konisa; reviewed by Dinesh Joshi, Francisco Guerrero,
Yifan Cai for CASSANDRASC-48
---
CHANGES.txt | 1 +
build.gradle | 1 +
.../sidecar/cassandra40/Cassandra40Factory.java | 7 +-
.../sidecar/common/CassandraAdapterDelegate.java | 55 +++++----
.../sidecar/common/ICassandraAdapter.java | 4 +-
.../cassandra/sidecar/common/NodeSettings.java | 57 +++++++++
.../cassandra/sidecar/common/NodeStatus.java | 28 -----
.../org/apache/cassandra/sidecar/MainModule.java | 5 +-
.../sidecar/routes/CassandraHealthService.java | 10 +-
.../sidecar/routes/CassandraSettingsService.java | 84 +++++++++++++
.../sidecar/utils/InstanceMetadataFetcher.java | 16 +++
.../sidecar/AbstractHealthServiceTest.java | 5 +-
.../org/apache/cassandra/sidecar/TestModule.java | 5 +
.../routes/CassandraSettingsServiceTest.java | 135 +++++++++++++++++++++
14 files changed, 342 insertions(+), 71 deletions(-)
diff --git a/CHANGES.txt b/CHANGES.txt
index dd7f1e1..aec5dc7 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,5 +1,6 @@
1.0.0
-----
+ * Add an endpoint that gives information about the release version &
partitioner name of a node (CASSANDRASC-48)
* Introduce JMX foundation in Sidecar (CASSANDRASC-47)
* Delegate methods to the RateLimiter (CASSANDRASC-45)
* Add Schema API (CASSANDRASC-43)
diff --git a/build.gradle b/build.gradle
index c81c4c4..d959335 100644
--- a/build.gradle
+++ b/build.gradle
@@ -108,6 +108,7 @@ dependencies {
implementation('com.google.inject:guice:4.2.2')
implementation('io.swagger.core.v3:swagger-jaxrs2:2.1.0')
implementation("org.jboss.resteasy:resteasy-vertx:4.7.4.Final")
+ implementation('org.jboss.resteasy:resteasy-jackson2-provider:4.7.4.Final')
implementation(group: 'org.jboss.spec.javax.servlet', name:
'jboss-servlet-api_4.0_spec', version: '2.0.0.Final')
implementation('org.jetbrains:annotations:23.0.0')
diff --git
a/cassandra40/src/main/java/org/apache/cassandra/sidecar/cassandra40/Cassandra40Factory.java
b/cassandra40/src/main/java/org/apache/cassandra/sidecar/cassandra40/Cassandra40Factory.java
index be98d32..b62b80a 100644
---
a/cassandra40/src/main/java/org/apache/cassandra/sidecar/cassandra40/Cassandra40Factory.java
+++
b/cassandra40/src/main/java/org/apache/cassandra/sidecar/cassandra40/Cassandra40Factory.java
@@ -18,14 +18,12 @@
package org.apache.cassandra.sidecar.cassandra40;
-import java.util.List;
-
import org.apache.cassandra.sidecar.common.CQLSession;
import org.apache.cassandra.sidecar.common.ICassandraAdapter;
import org.apache.cassandra.sidecar.common.ICassandraFactory;
import org.apache.cassandra.sidecar.common.JmxClient;
import org.apache.cassandra.sidecar.common.MinimumVersion;
-import org.apache.cassandra.sidecar.common.NodeStatus;
+import org.apache.cassandra.sidecar.common.NodeSettings;
import org.apache.cassandra.sidecar.common.StorageOperations;
/**
@@ -47,8 +45,9 @@ public class Cassandra40Factory implements ICassandraFactory
{
return new ICassandraAdapter()
{
+
@Override
- public List<NodeStatus> getStatus()
+ public NodeSettings getSettings()
{
return null;
}
diff --git
a/common/src/main/java/org/apache/cassandra/sidecar/common/CassandraAdapterDelegate.java
b/common/src/main/java/org/apache/cassandra/sidecar/common/CassandraAdapterDelegate.java
index 0407a4d..03d0049 100644
---
a/common/src/main/java/org/apache/cassandra/sidecar/common/CassandraAdapterDelegate.java
+++
b/common/src/main/java/org/apache/cassandra/sidecar/common/CassandraAdapterDelegate.java
@@ -18,17 +18,19 @@
package org.apache.cassandra.sidecar.common;
-import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.datastax.driver.core.Cluster;
import com.datastax.driver.core.Host;
+import com.datastax.driver.core.Row;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.exceptions.NoHostAvailableException;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
/**
@@ -48,10 +50,10 @@ public class CassandraAdapterDelegate implements
ICassandraAdapter, Host.StateLi
private final CQLSession cqlSession;
private final JmxClient jmxClient;
private final CassandraVersionProvider versionProvider;
- private volatile Session session;
+ private final AtomicReference<Session> session = new
AtomicReference<>(null);
private SimpleCassandraVersion currentVersion;
private ICassandraAdapter adapter;
- private volatile boolean isUp = false;
+ private volatile NodeSettings nodeSettings = null;
private static final Logger logger =
LoggerFactory.getLogger(CassandraAdapterDelegate.class);
private final AtomicBoolean registered = new AtomicBoolean(false);
@@ -88,17 +90,14 @@ public class CassandraAdapterDelegate implements
ICassandraAdapter, Host.StateLi
*/
public void checkSession()
{
- if (session != null)
+ if (session.get() != null)
{
return;
}
synchronized (this)
{
- if (session == null)
- {
- session = cqlSession.getLocalCql();
- }
+ session.compareAndSet(null, cqlSession.getLocalCql());
}
}
@@ -131,11 +130,11 @@ public class CassandraAdapterDelegate implements
ICassandraAdapter, Host.StateLi
{
checkSession();
- Session activeSession = session;
+ Session activeSession = session.get();
if (activeSession == null)
{
logger.info("No local CQL session is available. Cassandra is down
presumably.");
- isUp = false;
+ nodeSettings = null;
return;
}
@@ -143,37 +142,44 @@ public class CassandraAdapterDelegate implements
ICassandraAdapter, Host.StateLi
try
{
- String version = activeSession.execute("select release_version
from system.local")
- .one()
- .getString("release_version");
- isUp = true;
+ Row oneResult = activeSession.execute("select release_version,
partitioner from system.local")
+ .one();
+
+ String releaseVersion = oneResult.getString("release_version");
+ // update the nodeSettings cache.
+ // Note that within the scope of this method, we should keep on
using the local releaseVersion
+ nodeSettings = new NodeSettings(releaseVersion,
oneResult.getString("partitioner"));
// this might swap the adapter out
- SimpleCassandraVersion newVersion =
SimpleCassandraVersion.create(version);
+ SimpleCassandraVersion newVersion =
SimpleCassandraVersion.create(releaseVersion);
if (!newVersion.equals(currentVersion))
{
currentVersion = newVersion;
- adapter =
versionProvider.getCassandra(version).create(cqlSession, jmxClient);
+ adapter =
versionProvider.getCassandra(nodeSettings.releaseVersion()).create(cqlSession,
jmxClient);
logger.info("Cassandra version change detected. New adapter
loaded: {}", adapter);
}
- logger.debug("Cassandra version {}", version);
+ logger.debug("Cassandra version {}", releaseVersion);
}
catch (NoHostAvailableException e)
{
logger.error("Unexpected error connecting to Cassandra instance.",
e);
// The cassandra node is down.
// Unregister the host listener and nullify the session in order
to get a new object.
- isUp = false;
+ nodeSettings = null;
maybeUnregisterHostListener(activeSession);
- session = null;
+ activeSession.closeAsync();
+ session.compareAndSet(activeSession, null);
}
}
-
+ /**
+ * @return a cached {@link NodeSettings}. The returned value could be null
when no CQL connection is established
+ */
+ @Nullable
@Override
- public List<NodeStatus> getStatus()
+ public NodeSettings getSettings()
{
checkSession();
- return adapter.getStatus();
+ return nodeSettings;
}
@Override
@@ -192,13 +198,12 @@ public class CassandraAdapterDelegate implements
ICassandraAdapter, Host.StateLi
public void onUp(Host host)
{
healthCheck();
- isUp = true;
}
@Override
public void onDown(Host host)
{
- isUp = false;
+ nodeSettings = null;
}
@Override
@@ -219,7 +224,7 @@ public class CassandraAdapterDelegate implements
ICassandraAdapter, Host.StateLi
public boolean isUp()
{
- return isUp;
+ return nodeSettings != null;
}
public SimpleCassandraVersion getVersion()
diff --git
a/common/src/main/java/org/apache/cassandra/sidecar/common/ICassandraAdapter.java
b/common/src/main/java/org/apache/cassandra/sidecar/common/ICassandraAdapter.java
index cf6c7bb..6a4efc6 100644
---
a/common/src/main/java/org/apache/cassandra/sidecar/common/ICassandraAdapter.java
+++
b/common/src/main/java/org/apache/cassandra/sidecar/common/ICassandraAdapter.java
@@ -18,8 +18,6 @@
package org.apache.cassandra.sidecar.common;
-import java.util.List;
-
/**
* Core Cassandra Adapter interface
* For now, this is just a placeholder. We will most likely want to define
the interface to returns bits such as
@@ -28,7 +26,7 @@ import java.util.List;
*/
public interface ICassandraAdapter
{
- List<NodeStatus> getStatus();
+ NodeSettings getSettings();
/**
* @return the {@link StorageOperations} implementation for the Cassandra
cluster
diff --git
a/common/src/main/java/org/apache/cassandra/sidecar/common/NodeSettings.java
b/common/src/main/java/org/apache/cassandra/sidecar/common/NodeSettings.java
new file mode 100644
index 0000000..3d822a4
--- /dev/null
+++ b/common/src/main/java/org/apache/cassandra/sidecar/common/NodeSettings.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.cassandra.sidecar.common;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * Holds information about the specific node settings
+ */
+public class NodeSettings
+{
+ private final String releaseVersion;
+ private final String partitioner;
+
+ /**
+ * Constructs a new {@link NodeSettings} object with the Cassandra node's
release version and partitioner
+ * information.
+ *
+ * @param releaseVersion the release version of the Cassandra node
+ * @param partitioner the partitioner used by the Cassandra node
+ */
+ public NodeSettings(@JsonProperty("releaseVersion") String releaseVersion,
+ @JsonProperty("partitioner") String partitioner)
+ {
+ this.releaseVersion = releaseVersion;
+ this.partitioner = partitioner;
+ }
+
+ @JsonProperty("releaseVersion")
+ public String releaseVersion()
+ {
+ return releaseVersion;
+ }
+
+ @JsonProperty("partitioner")
+ public String partitioner()
+ {
+ return partitioner;
+ }
+}
diff --git
a/common/src/main/java/org/apache/cassandra/sidecar/common/NodeStatus.java
b/common/src/main/java/org/apache/cassandra/sidecar/common/NodeStatus.java
deleted file mode 100644
index d50a30f..0000000
--- a/common/src/main/java/org/apache/cassandra/sidecar/common/NodeStatus.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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 org.apache.cassandra.sidecar.common;
-
-/**
- * Placeholder
- */
-public class NodeStatus
-{
-
-}
diff --git a/src/main/java/org/apache/cassandra/sidecar/MainModule.java
b/src/main/java/org/apache/cassandra/sidecar/MainModule.java
index 86132f3..b74fe66 100644
--- a/src/main/java/org/apache/cassandra/sidecar/MainModule.java
+++ b/src/main/java/org/apache/cassandra/sidecar/MainModule.java
@@ -41,6 +41,7 @@ import
org.apache.cassandra.sidecar.common.CassandraVersionProvider;
import org.apache.cassandra.sidecar.common.data.QualifiedTableName;
import org.apache.cassandra.sidecar.common.utils.ValidationConfiguration;
import org.apache.cassandra.sidecar.routes.CassandraHealthService;
+import org.apache.cassandra.sidecar.routes.CassandraSettingsService;
import org.apache.cassandra.sidecar.routes.FileStreamHandler;
import org.apache.cassandra.sidecar.routes.HealthService;
import org.apache.cassandra.sidecar.routes.ListSnapshotFilesHandler;
@@ -105,7 +106,8 @@ public class MainModule extends AbstractModule
@Singleton
private VertxRequestHandler configureServices(Vertx vertx,
HealthService healthService,
- CassandraHealthService
cassandraHealthService)
+ CassandraHealthService
cassandraHealthService,
+ CassandraSettingsService
cassandraSettingsService)
{
VertxResteasyDeployment deployment = new VertxResteasyDeployment();
deployment.start();
@@ -114,6 +116,7 @@ public class MainModule extends AbstractModule
r.addPerInstanceResource(SwaggerOpenApiResource.class);
r.addSingletonResource(healthService);
r.addSingletonResource(cassandraHealthService);
+ r.addSingletonResource(cassandraSettingsService);
return new VertxRequestHandler(vertx, deployment);
}
diff --git
a/src/main/java/org/apache/cassandra/sidecar/routes/CassandraHealthService.java
b/src/main/java/org/apache/cassandra/sidecar/routes/CassandraHealthService.java
index 6f1cd31..1eea3b6 100644
---
a/src/main/java/org/apache/cassandra/sidecar/routes/CassandraHealthService.java
+++
b/src/main/java/org/apache/cassandra/sidecar/routes/CassandraHealthService.java
@@ -39,8 +39,6 @@ import io.vertx.core.json.Json;
import org.apache.cassandra.sidecar.common.CassandraAdapterDelegate;
import org.apache.cassandra.sidecar.utils.InstanceMetadataFetcher;
-import static
org.apache.cassandra.sidecar.utils.RequestUtils.extractHostAddressWithoutPort;
-
/**
* Provides a simple REST endpoint to determine if a node is available
*/
@@ -69,13 +67,13 @@ public class CassandraHealthService
@QueryParam(AbstractHandler.INSTANCE_ID) Integer instanceId)
{
CassandraAdapterDelegate cassandra;
- if (instanceId != null)
+ try
{
- cassandra = metadataFetcher.getDelegate(instanceId);
+ cassandra = metadataFetcher.getDelegate(req.host(), instanceId);
}
- else
+ catch (IllegalArgumentException e)
{
- cassandra =
metadataFetcher.getDelegate(extractHostAddressWithoutPort(req.host()));
+ return
Response.status(HttpResponseStatus.BAD_REQUEST.code()).entity(e.getMessage()).build();
}
return getHealthResponse(cassandra);
}
diff --git
a/src/main/java/org/apache/cassandra/sidecar/routes/CassandraSettingsService.java
b/src/main/java/org/apache/cassandra/sidecar/routes/CassandraSettingsService.java
new file mode 100644
index 0000000..8483452
--- /dev/null
+++
b/src/main/java/org/apache/cassandra/sidecar/routes/CassandraSettingsService.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.cassandra.sidecar.routes;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import io.netty.handler.codec.http.HttpResponseStatus;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.vertx.core.http.HttpServerRequest;
+import org.apache.cassandra.sidecar.common.CassandraAdapterDelegate;
+import org.apache.cassandra.sidecar.common.NodeSettings;
+import org.apache.cassandra.sidecar.utils.InstanceMetadataFetcher;
+
+
+/**
+ * Provides REST endpoint to get the configured settings of a cassandra node
+ */
+@Singleton
+@Path("/api")
+@Produces(MediaType.APPLICATION_JSON)
+public class CassandraSettingsService
+{
+ private final InstanceMetadataFetcher metadataFetcher;
+
+ @Inject
+ public CassandraSettingsService(InstanceMetadataFetcher metadataFetcher)
+ {
+ this.metadataFetcher = metadataFetcher;
+ }
+
+ @Operation(summary = "Cassandra's node settings",
+ description = "Returns HTTP 200 if Cassandra is available,400 on
incorrect instanceId, 503 otherwise",
+ responses = {
+ @ApiResponse(responseCode = "200", description = "Cassandra status
is available"),
+ @ApiResponse(responseCode = "400", description = "Incorrect
instanceId"),
+ @ApiResponse(responseCode = "503", description = "Cassandra status
is not available")
+ })
+ @GET
+ @Path("/v1/cassandra/settings")
+ public Response settings(@Context HttpServerRequest req,
+ @QueryParam(AbstractHandler.INSTANCE_ID) Integer
instanceId)
+ {
+ NodeSettings nodeSettings;
+ try
+ {
+ CassandraAdapterDelegate cassandra =
metadataFetcher.getDelegate(req.host(), instanceId);
+ nodeSettings = cassandra.getSettings();
+ }
+ catch (IllegalArgumentException e)
+ {
+ return
Response.status(HttpResponseStatus.BAD_REQUEST.code()).entity(e.getMessage()).build();
+ }
+ int statusCode = nodeSettings != null
+ ? HttpResponseStatus.OK.code()
+ : HttpResponseStatus.SERVICE_UNAVAILABLE.code();
+ return Response.status(statusCode).entity(nodeSettings).build();
+ }
+
+}
diff --git
a/src/main/java/org/apache/cassandra/sidecar/utils/InstanceMetadataFetcher.java
b/src/main/java/org/apache/cassandra/sidecar/utils/InstanceMetadataFetcher.java
index 3eecbc9..3a557ec 100644
---
a/src/main/java/org/apache/cassandra/sidecar/utils/InstanceMetadataFetcher.java
+++
b/src/main/java/org/apache/cassandra/sidecar/utils/InstanceMetadataFetcher.java
@@ -24,6 +24,8 @@ import org.apache.cassandra.sidecar.cluster.InstancesConfig;
import org.apache.cassandra.sidecar.cluster.instance.InstanceMetadata;
import org.apache.cassandra.sidecar.common.CassandraAdapterDelegate;
+import static
org.apache.cassandra.sidecar.utils.RequestUtils.extractHostAddressWithoutPort;
+
/**
* To fetch instance information according to instance id provided.
* By default returns 1st instance's information
@@ -53,6 +55,20 @@ public class InstanceMetadataFetcher
: instancesConfig.instanceFromId(instanceId).delegate();
}
+ public CassandraAdapterDelegate getDelegate(String host, Integer
instanceId)
+ {
+ CassandraAdapterDelegate cassandra;
+ if (instanceId != null)
+ {
+ cassandra = getDelegate(instanceId);
+ }
+ else
+ {
+ cassandra = getDelegate(extractHostAddressWithoutPort(host));
+ }
+ return cassandra;
+ }
+
private InstanceMetadata getFirstInstance()
{
if (instancesConfig.instances().isEmpty())
diff --git
a/src/test/java/org/apache/cassandra/sidecar/AbstractHealthServiceTest.java
b/src/test/java/org/apache/cassandra/sidecar/AbstractHealthServiceTest.java
index 0938a9d..6526563 100644
--- a/src/test/java/org/apache/cassandra/sidecar/AbstractHealthServiceTest.java
+++ b/src/test/java/org/apache/cassandra/sidecar/AbstractHealthServiceTest.java
@@ -39,7 +39,6 @@ import io.vertx.ext.web.client.WebClient;
import io.vertx.ext.web.client.WebClientOptions;
import io.vertx.ext.web.codec.BodyCodec;
import io.vertx.junit5.VertxTestContext;
-import org.apache.cassandra.sidecar.routes.HealthService;
import static io.netty.handler.codec.http.HttpResponseStatus.OK;
import static
io.netty.handler.codec.http.HttpResponseStatus.SERVICE_UNAVAILABLE;
@@ -51,7 +50,6 @@ import static org.assertj.core.api.Assertions.assertThat;
public abstract class AbstractHealthServiceTest
{
private static final Logger logger =
LoggerFactory.getLogger(AbstractHealthServiceTest.class);
- private HealthService service;
private Vertx vertx;
private Configuration config;
private HttpServer server;
@@ -71,12 +69,11 @@ public abstract class AbstractHealthServiceTest
{
Injector injector = Guice.createInjector(Modules.override(new
MainModule()).with(getTestModule()));
server = injector.getInstance(HttpServer.class);
- service = injector.getInstance(HealthService.class);
vertx = injector.getInstance(Vertx.class);
config = injector.getInstance(Configuration.class);
VertxTestContext context = new VertxTestContext();
- server.listen(config.getPort(), context.succeedingThenComplete());
+ server.listen(config.getPort(), config.getHost(),
context.succeedingThenComplete());
context.awaitCompletion(5, TimeUnit.SECONDS);
}
diff --git a/src/test/java/org/apache/cassandra/sidecar/TestModule.java
b/src/test/java/org/apache/cassandra/sidecar/TestModule.java
index 1a6393a..f8a679f 100644
--- a/src/test/java/org/apache/cassandra/sidecar/TestModule.java
+++ b/src/test/java/org/apache/cassandra/sidecar/TestModule.java
@@ -36,6 +36,7 @@ import
org.apache.cassandra.sidecar.cluster.instance.InstanceMetadata;
import org.apache.cassandra.sidecar.common.CassandraAdapterDelegate;
import org.apache.cassandra.sidecar.common.CassandraVersionProvider;
import org.apache.cassandra.sidecar.common.MockCassandraFactory;
+import org.apache.cassandra.sidecar.common.NodeSettings;
import org.apache.cassandra.sidecar.common.TestValidationConfiguration;
import org.apache.cassandra.sidecar.common.utils.ValidationConfiguration;
@@ -105,6 +106,10 @@ public class TestModule extends AbstractModule
when(instanceMeta.dataDirs()).thenReturn(Collections.singletonList(dataDir));
CassandraAdapterDelegate delegate =
mock(CassandraAdapterDelegate.class);
+ if (isUp)
+ {
+ when(delegate.getSettings()).thenReturn(new
NodeSettings("testVersion", "testPartitioner"));
+ }
when(delegate.isUp()).thenReturn(isUp);
when(instanceMeta.delegate()).thenReturn(delegate);
return instanceMeta;
diff --git
a/src/test/java/org/apache/cassandra/sidecar/routes/CassandraSettingsServiceTest.java
b/src/test/java/org/apache/cassandra/sidecar/routes/CassandraSettingsServiceTest.java
new file mode 100644
index 0000000..629a242
--- /dev/null
+++
b/src/test/java/org/apache/cassandra/sidecar/routes/CassandraSettingsServiceTest.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.cassandra.sidecar.routes;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.util.Modules;
+import io.vertx.core.Vertx;
+import io.vertx.core.http.HttpServer;
+import io.vertx.ext.web.client.WebClient;
+import io.vertx.ext.web.codec.BodyCodec;
+import io.vertx.junit5.VertxExtension;
+import io.vertx.junit5.VertxTestContext;
+
+import org.apache.cassandra.sidecar.Configuration;
+import org.apache.cassandra.sidecar.MainModule;
+import org.apache.cassandra.sidecar.TestModule;
+import org.apache.cassandra.sidecar.common.NodeSettings;
+import org.apache.http.HttpStatus;
+
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@ExtendWith(VertxExtension.class)
+class CassandraSettingsServiceTest
+{
+ private static final Logger logger =
LoggerFactory.getLogger(CassandraSettingsService.class);
+ private static final String URI = "/api/v1/cassandra/settings";
+ private static final String URI_WITH_INSTANCE_ID = URI + "?instanceId=%s";
+
+ private Vertx vertx;
+ private Configuration config;
+ private HttpServer server;
+
+ @BeforeEach
+ void setUp() throws InterruptedException
+ {
+ Injector injector = Guice.createInjector(Modules.override(new
MainModule()).with(new TestModule()));
+ server = injector.getInstance(HttpServer.class);
+ vertx = injector.getInstance(Vertx.class);
+ config = injector.getInstance(Configuration.class);
+
+ VertxTestContext context = new VertxTestContext();
+ server.listen(config.getPort(), config.getHost(),
context.succeedingThenComplete());
+
+ context.awaitCompletion(5, TimeUnit.SECONDS);
+ }
+
+ @AfterEach
+ void tearDown() throws InterruptedException
+ {
+ final CountDownLatch closeLatch = new CountDownLatch(1);
+ server.close(res -> closeLatch.countDown());
+ vertx.close();
+ if (closeLatch.await(60, TimeUnit.SECONDS))
+ logger.info("Close event received before timeout.");
+ else
+ logger.error("Close event timed out.");
+ }
+
+
+ @Test
+ public void validRequestWithoutInstanceId(VertxTestContext context) throws
InterruptedException
+ {
+ WebClient client = WebClient.create(vertx);
+ client.get(config.getPort(), "localhost", URI)
+ .as(BodyCodec.buffer())
+ .send(resp -> {
+
assertThat(resp.result().statusCode()).isEqualTo(HttpStatus.SC_OK);
+ NodeSettings status =
resp.result().bodyAsJson(NodeSettings.class);
+
assertThat(status.partitioner()).isEqualTo("testPartitioner");
+
assertThat(status.releaseVersion()).isEqualTo("testVersion");
+ context.completeNow();
+ });
+ context.awaitCompletion(1, TimeUnit.MINUTES);
+ client.close();
+ }
+
+ @Test
+ public void validRequestWithInstanceId(VertxTestContext context) throws
InterruptedException
+ {
+ WebClient client = WebClient.create(vertx);
+ client.get(config.getPort(), "localhost",
String.format(URI_WITH_INSTANCE_ID, "1"))
+ .as(BodyCodec.buffer())
+ .send(resp -> {
+
assertThat(resp.result().statusCode()).isEqualTo(HttpStatus.SC_OK);
+ NodeSettings status =
resp.result().bodyAsJson(NodeSettings.class);
+
assertThat(status.partitioner()).isEqualTo("testPartitioner");
+
assertThat(status.releaseVersion()).isEqualTo("testVersion");
+ context.completeNow();
+ });
+ context.awaitCompletion(1, TimeUnit.MINUTES);
+ client.close();
+ }
+
+ @Test
+ public void validRequestWithInvalidInstanceId(VertxTestContext context)
throws InterruptedException
+ {
+ WebClient client = WebClient.create(vertx);
+ client.get(config.getPort(), "localhost",
String.format(URI_WITH_INSTANCE_ID, "10"))
+ .as(BodyCodec.buffer())
+ .send(resp -> {
+
assertThat(resp.result().statusCode()).isEqualTo(HttpStatus.SC_BAD_REQUEST);
+
assertThat(resp.result().bodyAsString()).isEqualTo("Instance id 10 not found");
+ context.completeNow();
+ });
+ context.awaitCompletion(1, TimeUnit.MINUTES);
+ client.close();
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]