This is an automated email from the ASF dual-hosted git repository.

frankgh 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 995d1683 CASSSIDECAR-122: yaml configuration defaults to a file that 
doesn't e… (#153)
995d1683 is described below

commit 995d1683eb688a75d53cbaef4b1507905dcf24e0
Author: Francisco Guerrero <fran...@apache.org>
AuthorDate: Fri Dec 6 13:02:34 2024 -0800

    CASSSIDECAR-122: yaml configuration defaults to a file that doesn't e… 
(#153)
    
    * CASSSIDECAR-122: yaml configuration defaults to a file that doesn't exist
    
    Patch by Francisco Guerrero; Reviewed by Jon Haddad, Yifan Cai for 
CASSSIDECAR-122
---
 CHANGES.txt                                        |   1 +
 build.gradle                                       |  32 ++--
 {server/src/main/dist/conf => conf}/logback.xml    |   0
 {server/src/main/dist/conf => conf}/sidecar.yaml   |   0
 .../cassandra/sidecar/CassandraSidecarDaemon.java  |  53 +++++--
 .../sidecar/CassandraSidecarDaemonTest.java        | 173 +++++++++++++++++++++
 .../cassandra/sidecar/server/ServerTest.java       |   2 +-
 ... => sidecar_single_instance_non_zero_port.yaml} |   2 +-
 8 files changed, 233 insertions(+), 30 deletions(-)

diff --git a/CHANGES.txt b/CHANGES.txt
index d790c1b5..9ae24873 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,5 +1,6 @@
 1.0.0
 -----
+ * yaml configuration defaults to a file that doesn't exist (CASSSIDECAR-122)
  * Add advanced driver settings to allow taking in password or certificates 
for Cassandra connection (CASSSIDECAR-159)
  * Add metric to report consistency check duration (CASSSIDECAR-165)
  * Add mTLS Authentication in Sidecar (CASSANDRASC-156)
diff --git a/build.gradle b/build.gradle
index 52e53116..12ac1b8c 100644
--- a/build.gradle
+++ b/build.gradle
@@ -131,7 +131,14 @@ distributions {
     main {
         contents {
             from 'LICENSE.txt'
-            from 'server/src/main/dist'
+            // Include the "agents" directory in the distribution
+            from('agents') {
+                into('agents')
+            }
+            // Include the "conf" directory in the distribution
+            from('conf') {
+                into('conf')
+            }
         }
     }
 }
@@ -171,26 +178,22 @@ tasks.named('idea').configure {
     dependsOn copyIdeaSettings
 }
 
-// Lets copy the distributions from build/install directory to /bin and /lib
-// directories to be aligned with C* distribution format
-tasks.register('copyDist', Copy) {
-    from "$buildDir/install/$applicationName"
+tasks.register('copyJolokia', Copy) {
     into "$projectDir"
-}
 
-tasks.register('copyJolokia', Copy) {
-    from configurations.jolokia
-    into "$projectDir/src/main/dist/agents"
+    into("agents") {
+        from configurations.jolokia
+    }
 }
 
 // Lets clean distribution directories along with default build directories.
 clean.doLast {
-    ["agents", "bin", "conf", "lib"].each {
+    ["agents"].each {
         println "Deleting directory $projectDir/$it"
         delete "$projectDir/$it"
     }
-    println "Deleting generated docs $projectDir/src/main/resources/docs"
-    delete "$projectDir/src/main/resources/docs"
+    println "Deleting generated docs 
$projectDir/server/src/main/resources/docs"
+    delete "$projectDir/server/src/main/resources/docs"
 }
 
 subprojects {
@@ -287,10 +290,9 @@ rat {
     reportDir.set(file("build/reports/rat"))
 }
 
-// copyDist gets called on every build
-copyDist.dependsOn installDist, copyJolokia
+installDist.dependsOn copyJolokia
 check.dependsOn codeCheckTasks
-build.dependsOn copyDist, copyJolokia, copyDocs
+build.dependsOn copyJolokia, copyDocs
 run.dependsOn build
 
 tasks.named('rat').configure {
diff --git a/server/src/main/dist/conf/logback.xml b/conf/logback.xml
similarity index 100%
rename from server/src/main/dist/conf/logback.xml
rename to conf/logback.xml
diff --git a/server/src/main/dist/conf/sidecar.yaml b/conf/sidecar.yaml
similarity index 100%
rename from server/src/main/dist/conf/sidecar.yaml
rename to conf/sidecar.yaml
diff --git 
a/server/src/main/java/org/apache/cassandra/sidecar/CassandraSidecarDaemon.java 
b/server/src/main/java/org/apache/cassandra/sidecar/CassandraSidecarDaemon.java
index fd020a22..276282f3 100644
--- 
a/server/src/main/java/org/apache/cassandra/sidecar/CassandraSidecarDaemon.java
+++ 
b/server/src/main/java/org/apache/cassandra/sidecar/CassandraSidecarDaemon.java
@@ -19,6 +19,7 @@
 package org.apache.cassandra.sidecar;
 
 import java.net.URI;
+import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.concurrent.TimeUnit;
@@ -29,6 +30,7 @@ import org.slf4j.LoggerFactory;
 import com.google.inject.Guice;
 import org.apache.cassandra.sidecar.server.MainModule;
 import org.apache.cassandra.sidecar.server.Server;
+import org.jetbrains.annotations.VisibleForTesting;
 
 /**
  * Main class for initiating the Cassandra sidecar
@@ -37,23 +39,15 @@ import org.apache.cassandra.sidecar.server.Server;
 public class CassandraSidecarDaemon
 {
     private static final Logger LOGGER = 
LoggerFactory.getLogger(CassandraSidecarDaemon.class);
+    @VisibleForTesting
+    static Server runningApplication;
 
     public static void main(String[] args)
     {
-        String yamlConfigurationPath = System.getProperty("sidecar.config", 
"file://./conf/config.yaml");
-
-        Path confPath;
-        try
-        {
-            confPath = Paths.get(new URI(yamlConfigurationPath));
-        }
-        catch (Throwable e)
-        {
-            throw new RuntimeException("Invalid URI: " + 
yamlConfigurationPath, e);
-        }
+        Path confPath = determineConfigPath();
 
         Server app = Guice.createInjector(new 
MainModule(confPath)).getInstance(Server.class);
-
+        runningApplication = app;
         app.start().onSuccess(deploymentId -> 
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
             if (close(app))
             {
@@ -72,7 +66,8 @@ public class CassandraSidecarDaemon
      * @param app the server
      * @return {@code true} if the server shutdown successfully, {@code false} 
otherwise
      */
-    private static boolean close(Server app)
+    @VisibleForTesting
+    static boolean close(Server app)
     {
         try
         {
@@ -88,5 +83,37 @@ public class CassandraSidecarDaemon
         }
         return false;
     }
+
+    private static Path determineConfigPath()
+    {
+        Path confPath;
+        String yamlConfigurationPath = System.getProperty("sidecar.config");
+        if (yamlConfigurationPath != null)
+        {
+            try
+            {
+                confPath = Paths.get(new URI(yamlConfigurationPath));
+            }
+            catch (Throwable e)
+            {
+                throw new IllegalArgumentException("Invalid URI: " + 
yamlConfigurationPath, e);
+            }
+        }
+        else
+        {
+            confPath = Paths.get("conf/sidecar.yaml");
+        }
+        return ensurePathExists(confPath);
+    }
+
+    private static Path ensurePathExists(Path confPath)
+    {
+        if (!Files.exists(confPath))
+        {
+            throw new IllegalArgumentException(String.format("Sidecar 
configuration file '%s' does not exist",
+                                                             
confPath.toAbsolutePath()));
+        }
+        return confPath;
+    }
 }
 
diff --git 
a/server/src/test/java/org/apache/cassandra/sidecar/CassandraSidecarDaemonTest.java
 
b/server/src/test/java/org/apache/cassandra/sidecar/CassandraSidecarDaemonTest.java
new file mode 100644
index 00000000..bad370ae
--- /dev/null
+++ 
b/server/src/test/java/org/apache/cassandra/sidecar/CassandraSidecarDaemonTest.java
@@ -0,0 +1,173 @@
+/*
+ * 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;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import io.vertx.core.Vertx;
+import io.vertx.ext.web.client.HttpResponse;
+import io.vertx.ext.web.client.WebClient;
+import io.vertx.ext.web.codec.BodyCodec;
+import org.apache.cassandra.sidecar.server.Server;
+
+import static io.netty.handler.codec.http.HttpResponseStatus.OK;
+import static org.assertj.core.api.Assertions.assertThat;
+import static 
org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
+
+/**
+ * Unit tests for the {@link CassandraSidecarDaemon}
+ */
+class CassandraSidecarDaemonTest
+{
+    static final String[] NO_ARGS = {};
+
+    @BeforeEach
+    void setup()
+    {
+        System.clearProperty("sidecar.config");
+    }
+
+    @Test
+    void testStartFailsWithInvalidURI()
+    {
+        System.setProperty("sidecar.config", "file://./invalid/URI");
+
+        assertThatIllegalArgumentException().isThrownBy(() -> 
CassandraSidecarDaemon.main(NO_ARGS))
+                                            .withMessage("Invalid URI: 
file://./invalid/URI");
+    }
+
+    @Test
+    void testStartFailsWithNonExistentFile()
+    {
+        System.setProperty("sidecar.config", 
"file:///tmp/file/does/not/exist.yaml");
+
+        assertThatIllegalArgumentException().isThrownBy(() -> 
CassandraSidecarDaemon.main(NO_ARGS))
+                                            .withMessage("Sidecar 
configuration file '/tmp/file/does/not/exist.yaml' does not exist");
+    }
+
+    @Test
+    void testSuccessfulStartup() throws Exception
+    {
+        Path path = Paths.get("../conf/sidecar.yaml");
+        assertThat(path).exists();
+
+        System.setProperty("sidecar.config", path.toUri().toString());
+        try
+        {
+            CassandraSidecarDaemon.main(NO_ARGS);
+
+            WebClient client = WebClient.create(Vertx.vertx());
+            HttpResponse<String> response = client.get(9043, "localhost", 
"/api/v1/__health")
+                                                  .as(BodyCodec.string())
+                                                  .send()
+                                                  .toCompletionStage()
+                                                  .toCompletableFuture()
+                                                  .get(10, TimeUnit.SECONDS);
+            assertThat(response.statusCode()).isEqualTo(OK.code());
+            assertThat(response.body()).isEqualTo("{\"status\":\"OK\"}");
+        }
+        finally
+        {
+            maybeStopCassandraSidecar();
+        }
+    }
+
+    @Test
+    void testSuccessfulStartupWithDefaultPath() throws Exception
+    {
+        Path path = Paths.get("../conf/sidecar.yaml");
+        assertThat(path).exists();
+
+        // First ensure startup fails because the conf file does not exist
+        assertThatIllegalArgumentException().isThrownBy(() -> 
CassandraSidecarDaemon.main(NO_ARGS))
+                                            .withMessageMatching("Sidecar 
configuration file '.*/conf/sidecar.yaml' does not exist");
+
+        // Now let's copy the file to the expected location
+        Path targetFile = Paths.get("conf/sidecar.yaml");
+        List<Path> createdParents = null;
+
+        try
+        {
+            createdParents = createParents(targetFile.toAbsolutePath());
+            Files.copy(path.toAbsolutePath(), targetFile.toAbsolutePath());
+
+            CassandraSidecarDaemon.main(NO_ARGS);
+
+            WebClient client = WebClient.create(Vertx.vertx());
+            HttpResponse<String> response = client.get(9043, "localhost", 
"/api/v1/__health")
+                                                  .as(BodyCodec.string())
+                                                  .send()
+                                                  .toCompletionStage()
+                                                  .toCompletableFuture()
+                                                  .get(10, TimeUnit.SECONDS);
+            assertThat(response.statusCode()).isEqualTo(OK.code());
+            assertThat(response.body()).isEqualTo("{\"status\":\"OK\"}");
+        }
+        finally
+        {
+            maybeStopCassandraSidecar();
+            Files.deleteIfExists(targetFile);
+
+            if (createdParents != null)
+            {
+                for (Path createdParent : createdParents)
+                {
+                    Files.deleteIfExists(createdParent);
+                }
+            }
+        }
+    }
+
+    static void maybeStopCassandraSidecar()
+    {
+        Server runningApplication = CassandraSidecarDaemon.runningApplication;
+        if (runningApplication != null)
+        {
+            CassandraSidecarDaemon.close(runningApplication);
+        }
+    }
+
+    static List<Path> createParents(Path file) throws IOException
+    {
+        List<Path> createdParents = new ArrayList<>();
+        Path parentDirectory = file.getParent();
+        if (parentDirectory == null)
+        {
+            return createdParents;
+        }
+        Path directory = parentDirectory;
+
+        while (directory != null && !Files.exists(directory))
+        {
+            createdParents.add(directory);
+            directory = directory.getParent();
+        }
+        Files.createDirectories(parentDirectory);
+        return createdParents;
+    }
+}
diff --git 
a/server/src/test/java/org/apache/cassandra/sidecar/server/ServerTest.java 
b/server/src/test/java/org/apache/cassandra/sidecar/server/ServerTest.java
index 53dfd2e3..e294cd50 100644
--- a/server/src/test/java/org/apache/cassandra/sidecar/server/ServerTest.java
+++ b/server/src/test/java/org/apache/cassandra/sidecar/server/ServerTest.java
@@ -200,7 +200,7 @@ class ServerTest
     @DisplayName("Updating traffic shaping options with non-zero listen port 
should succeed")
     void updateTrafficShapingOptionsWithNonZeroListenPort()
     {
-        configureServer("config/sidecar_single_instance_default_port.yaml");
+        configureServer("config/sidecar_single_instance_non_zero_port.yaml");
 
         assertThatNoException().isThrownBy(() -> {
             server.start().toCompletionStage().toCompletableFuture().get(30, 
TimeUnit.SECONDS);
diff --git 
a/server/src/test/resources/config/sidecar_single_instance_default_port.yaml 
b/server/src/test/resources/config/sidecar_single_instance_non_zero_port.yaml
similarity index 99%
rename from 
server/src/test/resources/config/sidecar_single_instance_default_port.yaml
rename to 
server/src/test/resources/config/sidecar_single_instance_non_zero_port.yaml
index 5e3d5b2d..de40d57c 100644
--- a/server/src/test/resources/config/sidecar_single_instance_default_port.yaml
+++ 
b/server/src/test/resources/config/sidecar_single_instance_non_zero_port.yaml
@@ -16,7 +16,7 @@ cassandra:
 
 sidecar:
   host: 0.0.0.0
-  port: 9043
+  port: 9046
   request_idle_timeout_millis: 300000 # this field expects integer value
   request_timeout_millis: 300000
   tcp_keep_alive: false


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@cassandra.apache.org
For additional commands, e-mail: commits-h...@cassandra.apache.org

Reply via email to