Avoid having sidecar's health response code depend on Cassandra's health information
patch by Saranya Krishnakumar; reviewed by Yifan Cai, Dinesh Joshi for CASSANDRASC-29
diff --git a/src/main/java/org/apache/cassandra/sidecar/MainModule.java b/src/main/java/org/apache/cassandra/sidecar/MainModule.java
index 223ab97..701900c 100644
--- a/src/main/java/org/apache/cassandra/sidecar/MainModule.java
+++ b/src/main/java/org/apache/cassandra/sidecar/MainModule.java
@@ -45,6 +45,7 @@
import org.apache.cassandra.sidecar.common.CQLSession;
import org.apache.cassandra.sidecar.common.CassandraAdapterDelegate;
import org.apache.cassandra.sidecar.common.CassandraVersionProvider;
+import org.apache.cassandra.sidecar.routes.CassandraHealthService;
import org.apache.cassandra.sidecar.routes.HealthService;
import org.apache.cassandra.sidecar.routes.StreamSSTableComponent;
import org.apache.cassandra.sidecar.routes.SwaggerOpenApiResource;
@@ -102,7 +103,8 @@
@Singleton
private VertxRequestHandler configureServices(Vertx vertx,
HealthService healthService,
- StreamSSTableComponent ssTableComponent)
+ StreamSSTableComponent ssTableComponent,
+ CassandraHealthService cassandraHealthService)
{
VertxResteasyDeployment deployment = new VertxResteasyDeployment();
deployment.start();
@@ -111,6 +113,7 @@
r.addPerInstanceResource(SwaggerOpenApiResource.class);
r.addSingletonResource(healthService);
r.addSingletonResource(ssTableComponent);
+ r.addSingletonResource(cassandraHealthService);
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
new file mode 100644
index 0000000..cc17ef8
--- /dev/null
+++ b/src/main/java/org/apache/cassandra/sidecar/routes/CassandraHealthService.java
@@ -0,0 +1,70 @@
+/*
+ * 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.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import com.google.common.collect.ImmutableMap;
+
+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.json.Json;
+import io.vertx.core.logging.Logger;
+import io.vertx.core.logging.LoggerFactory;
+import org.apache.cassandra.sidecar.common.CassandraAdapterDelegate;
+
+/**
+ * Provides a simple REST endpoint to determine if a node is available
+ */
+@Singleton
+@Path("/api/v1/cassandra/__health")
+public class CassandraHealthService
+{
+ private static final Logger logger = LoggerFactory.getLogger(HealthService.class);
+ private final CassandraAdapterDelegate cassandra;
+
+ @Inject
+ public CassandraHealthService(CassandraAdapterDelegate cassandra)
+ {
+ this.cassandra = cassandra;
+ }
+
+ @Operation(summary = "Health Check for Cassandra's status",
+ description = "Returns HTTP 200 if Cassandra is available, 503 otherwise",
+ responses = {
+ @ApiResponse(responseCode = "200", description = "Cassandra is available"),
+ @ApiResponse(responseCode = "503", description = "Cassandra is not available")
+ })
+ @Produces(MediaType.APPLICATION_JSON)
+ @GET
+ public Response getCassandraHealth()
+ {
+ Boolean up = cassandra.isUp();
+ int status = up ? HttpResponseStatus.OK.code() : HttpResponseStatus.SERVICE_UNAVAILABLE.code();
+ return Response.status(status).entity(Json.encode(ImmutableMap.of("status", up ?
+ "OK" : "NOT_OK"))).build();
+ }
+}
diff --git a/src/main/java/org/apache/cassandra/sidecar/routes/HealthService.java b/src/main/java/org/apache/cassandra/sidecar/routes/HealthService.java
index bd15fde..dd0a46a 100644
--- a/src/main/java/org/apache/cassandra/sidecar/routes/HealthService.java
+++ b/src/main/java/org/apache/cassandra/sidecar/routes/HealthService.java
@@ -26,45 +26,29 @@
import com.google.common.collect.ImmutableMap;
-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.json.Json;
import io.vertx.core.logging.Logger;
import io.vertx.core.logging.LoggerFactory;
-import org.apache.cassandra.sidecar.common.CassandraAdapterDelegate;
/**
- * Provides a simple REST endpoint to determine if a node is available
+ * Provides a simple REST endpoint to determine if Sidecar is available
*/
@Singleton
@Path("/api/v1/__health")
public class HealthService
{
private static final Logger logger = LoggerFactory.getLogger(HealthService.class);
- private final CassandraAdapterDelegate cassandra;
- @Inject
- public HealthService(CassandraAdapterDelegate cassandra)
- {
- this.cassandra = cassandra;
- }
-
- @Operation(summary = "Health Check for Cassandra's status",
- description = "Returns HTTP 200 if Cassandra is available, 503 otherwise",
- responses = {
- @ApiResponse(responseCode = "200", description = "Cassandra is available"),
- @ApiResponse(responseCode = "503", description = "Cassandra is not available")
- })
+ @Operation(summary = "Health Check for Sidecar's status",
+ description = "Returns HTTP 200 if Sidecar is available")
@Produces(MediaType.APPLICATION_JSON)
@GET
- public Response doGet()
+ public Response getSidecarHealth()
{
- Boolean up = cassandra.isUp();
- int status = up ? HttpResponseStatus.OK.code() : HttpResponseStatus.SERVICE_UNAVAILABLE.code();
- return Response.status(status).entity(Json.encode(ImmutableMap.of("status", up ?
- "OK" : "NOT_OK"))).build();
+ return Response.status(HttpResponseStatus.OK.code()).entity(Json.encode(ImmutableMap.of("status", "OK")))
+ .build();
}
}
diff --git a/src/test/java/org/apache/cassandra/sidecar/AbstractHealthServiceTest.java b/src/test/java/org/apache/cassandra/sidecar/AbstractHealthServiceTest.java
index 946be3c..9b19b8f 100644
--- a/src/test/java/org/apache/cassandra/sidecar/AbstractHealthServiceTest.java
+++ b/src/test/java/org/apache/cassandra/sidecar/AbstractHealthServiceTest.java
@@ -96,11 +96,10 @@
logger.error("Close event timed out.");
}
- @DisplayName("Should return HTTP 200 OK when check=True")
+ @DisplayName("Should return HTTP 200 OK if sidecar server is running")
@Test
- public void testHealthCheckReturns200OK(VertxTestContext testContext)
+ public void testSidecarHealthCheckReturnsOK(VertxTestContext testContext)
{
- when(cassandra.isUp()).thenReturn(true);
WebClient client = getClient();
client.get(config.getPort(), "localhost", "/api/v1/__health")
@@ -109,6 +108,7 @@
.send(testContext.succeeding(response -> testContext.verify(() ->
{
Assert.assertEquals(200, response.statusCode());
+ Assert.assertEquals("{\"status\":\"OK\"}", response.body());
testContext.completeNow();
})));
}
@@ -129,20 +129,38 @@
return options;
}
+ @DisplayName("Should return HTTP 200 OK when check=True")
+ @Test
+ public void testHealthCheckReturns200OK(VertxTestContext testContext)
+ {
+ when(cassandra.isUp()).thenReturn(true);
+ WebClient client = getClient();
+
+ client.get(config.getPort(), "localhost", "/api/v1/cassandra/__health")
+ .as(BodyCodec.string())
+ .ssl(isSslEnabled())
+ .send(testContext.succeeding(response -> testContext.verify(() ->
+ {
+ Assert.assertEquals(200, response.statusCode());
+ Assert.assertEquals("{\"status\":\"OK\"}", response.body());
+ testContext.completeNow();
+ })));
+ }
+
@DisplayName("Should return HTTP 503 Failure when check=False")
@Test
public void testHealthCheckReturns503Failure(VertxTestContext testContext)
{
-
when(cassandra.isUp()).thenReturn(false);
WebClient client = getClient();
- client.get(config.getPort(), "localhost", "/api/v1/__health")
+ client.get(config.getPort(), "localhost", "/api/v1/cassandra/__health")
.as(BodyCodec.string())
.ssl(isSslEnabled())
.send(testContext.succeeding(response -> testContext.verify(() ->
{
Assert.assertEquals(503, response.statusCode());
+ Assert.assertEquals("{\"status\":\"NOT_OK\"}", response.body());
testContext.completeNow();
})));
}
diff --git a/src/test/java/org/apache/cassandra/sidecar/FilePathBuilderTest.java b/src/test/java/org/apache/cassandra/sidecar/FilePathBuilderTest.java
index 734ca22..2f37749 100644
--- a/src/test/java/org/apache/cassandra/sidecar/FilePathBuilderTest.java
+++ b/src/test/java/org/apache/cassandra/sidecar/FilePathBuilderTest.java
@@ -18,7 +18,6 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* FilePathBuilderTest
diff --git a/src/test/java/org/apache/cassandra/sidecar/TestModule.java b/src/test/java/org/apache/cassandra/sidecar/TestModule.java
index 755ad4a..4cf7d9f 100644
--- a/src/test/java/org/apache/cassandra/sidecar/TestModule.java
+++ b/src/test/java/org/apache/cassandra/sidecar/TestModule.java
@@ -29,7 +29,6 @@
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.routes.HealthService;
import static org.mockito.Mockito.mock;
@@ -47,14 +46,6 @@
return mock(CassandraAdapterDelegate.class);
}
- @Singleton
- @Provides
- public HealthService healthService(CassandraAdapterDelegate delegate)
- {
- return new HealthService(delegate);
- }
-
-
@Provides
@Singleton
public Configuration configuration()