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