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]

Reply via email to