From d5621a869ee34b7b92a753b4412842067a09afcb Mon Sep 17 00:00:00 2001 From: Mike Gager Date: Tue, 18 Nov 2025 20:18:47 -0500 Subject: [PATCH 1/3] Upgrade several dependencies --- pom.xml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 3e6f132..e9fb9c2 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ com.fizzed maven-parent - 2.7.0 + 3.4.0 @@ -26,6 +26,7 @@ 2.14.1 1.7.21 1.6.0 + 2.0.17 1.0.48 @@ -116,7 +117,7 @@ joda-time joda-time - 2.9.4 + 2.14.0 @@ -124,7 +125,7 @@ org.apache.commons commons-compress - 1.21 + 1.28.0 @@ -150,7 +151,7 @@ org.hibernate.validator hibernate-validator - 6.0.20.Final + 6.2.5.Final @@ -194,7 +195,7 @@ commons-io commons-io - 2.7 + 2.19.0 @@ -220,7 +221,7 @@ ch.qos.logback logback-classic - 1.2.3 + 1.5.18 From e6ab90280d560e6e360809a662632eab42fd9f31 Mon Sep 17 00:00:00 2001 From: Mike Gager Date: Tue, 18 Nov 2025 21:04:37 -0500 Subject: [PATCH 2/3] Upgrade blaze to 2.6.0 --- pom.xml | 1 + .../java/com/fizzed/stork/deploy/Targets.java | 4 +- .../com/fizzed/stork/deploy/UnixTarget.java | 6 +- .../fizzed/stork/launcher/LauncherTest.java | 1156 ++++++++--------- 4 files changed, 584 insertions(+), 583 deletions(-) diff --git a/pom.xml b/pom.xml index e9fb9c2..cdd4e8e 100644 --- a/pom.xml +++ b/pom.xml @@ -27,6 +27,7 @@ 1.7.21 1.6.0 2.0.17 + 2.6.0 1.0.48 diff --git a/stork-deploy/src/main/java/com/fizzed/stork/deploy/Targets.java b/stork-deploy/src/main/java/com/fizzed/stork/deploy/Targets.java index 9e72469..e48c0e1 100644 --- a/stork-deploy/src/main/java/com/fizzed/stork/deploy/Targets.java +++ b/stork-deploy/src/main/java/com/fizzed/stork/deploy/Targets.java @@ -87,8 +87,8 @@ static private InitType initType(SshSession ssh) { String initTypeString = sshExec(ssh) .command("sh").args("-c", - "if $(/sbin/init --version | egrep -q 'upstart'); then echo upstart; " + - "elif $(systemctl | egrep -q '.mount'); then echo systemd; " + + "if \\$(/sbin/init --version 2>/dev/null | egrep -q upstart); then echo upstart; " + + "elif \\$(systemctl 2>/dev/null| egrep -q .mount); then echo systemd; " + "elif [ -f /etc/init.d/cron ] || [ -f /etc/init.d/crond ]; then echo sysv; " + "else echo unknown; fi") .pipeOutput(Streamables.captureOutput()) diff --git a/stork-deploy/src/main/java/com/fizzed/stork/deploy/UnixTarget.java b/stork-deploy/src/main/java/com/fizzed/stork/deploy/UnixTarget.java index 2bbd3fe..94b19b1 100644 --- a/stork-deploy/src/main/java/com/fizzed/stork/deploy/UnixTarget.java +++ b/stork-deploy/src/main/java/com/fizzed/stork/deploy/UnixTarget.java @@ -307,7 +307,7 @@ private void installDaemonDefaults(Deployment install, Daemon daemon) { String defaultsFile = "/etc/" + dir + "/" + daemon.getName(); String cmd - = "if [ -d /etc/" + dir + " ]; then " + = "'if [ -d /etc/" + dir + " ]; then " + " if [ ! -f " + defaultsFile + " ]; then " + " echo \"APP_HOME=\\\"" + install.getCurrentDir() + "\\\"\" > " + defaultsFile + "; " + " echo \"APP_USER=\\\"" + install.getUser().orElse("") + "\\\"\" >> " + defaultsFile + "; " @@ -318,7 +318,7 @@ private void installDaemonDefaults(Deployment install, Daemon daemon) { + " fi " + "else " + " exit 10; " - + "fi"; + + "fi'"; Integer exitValue = sshExec(true, true, cmd) @@ -388,4 +388,4 @@ private void installSystemdDaemon(Deployment install, Daemon daemon, boolean onB installDaemonDefaults(install, daemon); } -} \ No newline at end of file +} diff --git a/stork-launcher/src/test/java/com/fizzed/stork/launcher/LauncherTest.java b/stork-launcher/src/test/java/com/fizzed/stork/launcher/LauncherTest.java index 332c508..28774f6 100644 --- a/stork-launcher/src/test/java/com/fizzed/stork/launcher/LauncherTest.java +++ b/stork-launcher/src/test/java/com/fizzed/stork/launcher/LauncherTest.java @@ -1,578 +1,578 @@ -/* - * Copyright 2016 Fizzed, Inc. - * - * Licensed 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 com.fizzed.stork.launcher; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fizzed.blaze.SecureShells; -import com.fizzed.blaze.Systems; -import com.fizzed.blaze.ssh.SshExec; -import com.fizzed.blaze.ssh.SshSession; -import com.fizzed.blaze.system.Exec; -import com.fizzed.blaze.util.CaptureOutput; -import com.fizzed.blaze.util.Streamables; -import com.fizzed.stork.test.LaunchData; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import static org.hamcrest.CoreMatchers.containsString; -import static org.hamcrest.CoreMatchers.hasItem; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.not; -import static org.hamcrest.Matchers.hasEntry; -import static org.hamcrest.Matchers.hasKey; -import static org.hamcrest.Matchers.hasSize; -import org.junit.Test; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.fail; -import org.junit.Assume; -import static org.junit.Assume.assumeFalse; -import static org.junit.Assume.assumeTrue; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameters; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -@RunWith(Parameterized.class) -public class LauncherTest { - static private final Logger log = LoggerFactory.getLogger(LauncherTest.class); - - @Parameters(name = "{index}: host={0}") - public static Collection data() { - return TestHelper.hosts(); - } - - static private final Map sshs = new HashMap<>(); - private final ObjectMapper objectMapper = new ObjectMapper(); - private final String host; - private final Path exeCat; - private final Path exeEchoConsole1; - private final Path exeEchoConsole2; - private final Path exeEchoConsole3; - private final Path exeEchoDaemon1; - private final SshSession ssh; - static private boolean vagrantRsynced; - - public LauncherTest(String host) { - this.host = host; - this.exeCat = resolveExe("cat"); - this.exeEchoConsole1 = resolveExe("echo-console1"); - this.exeEchoConsole2 = resolveExe("echo-console2"); - this.exeEchoConsole3 = resolveExe("echo-console3"); - this.exeEchoDaemon1 = resolveExe("echo-daemon1"); - //this.symlinkJavaExe = resolveExe("symlink-java"); - if (!sshs.containsKey(host)) { - sshs.put(host, sshConnect()); - } - this.ssh = sshs.get(host); - } - - private boolean isHostRunning() { - return host.equals("local") - || TestHelper.VAGRANT_CLIENT.machinesRunning().contains(host); - } - - private SshSession sshConnect() { - switch (this.host) { - case "local": - return null; - default: - if (isHostRunning()) { - Path sshConfig = TestHelper.VAGRANT_CLIENT.sshConfig(host); - return SecureShells.sshConnect("ssh://" + host).configFile(sshConfig).run(); - } else { - return null; - } - } - } - - @BeforeClass - static public void vagrantRsynced() throws Exception { - if (!vagrantRsynced) { - try { - Systems.exec("vagrant", "rsync").exitValues(0, 1).run(); - } catch (Exception e) { - // ignore - } - vagrantRsynced = true; - } - } - - @Before - public void onlyIfHostIsRunning() throws Exception { - assumeTrue("Is host running?", isHostRunning()); - -// if (symlinkJava == null) { -// // do not try to symlink java on a local windows client -// if (isWindows()) { -// return; // skip -// } -// -// String output = execute(0, symlinkJavaExe); -// -// // 3 lines -// // /usr/lib/jvm/jdk1.8.0_77/jre -// ///tmp/java-linked -// ///tmp/java-linked with spaces -// String[] lines = output.trim().split("\n"); -// symlinkJava = Paths.get(lines[1].trim()); -// symlinkJavaWithSpaces = Paths.get(lines[2].trim()); -// } - } - - private boolean isLocal() { - return host.equals("local"); - } - - private boolean isWindows() { - return host.startsWith("windows") - || (host.equals("local") && TestHelper.isWindows()); - } - - private Path resolveExe(String exeName) { - if (isWindows()) { - exeName += ".bat"; - } - - switch (this.host) { - case "local": - return Paths.get("target/stork/bin").resolve(exeName); - default: - return Paths.get("/vagrant/stork-launcher/target/stork/bin").resolve(exeName); - } - } - - public String execute(int exitValue, Path exe, String... args) throws Exception { - return execute(exitValue, exe, null, args); - } - - public String execute(int exitValue, Path exe, Map environment, String... args) throws Exception { - CaptureOutput captureOutput = Streamables.captureOutput(); - - try { - if (this.host.equals("local")) { - Exec exec - = Systems.exec(exe) - .args((Object[]) args) - .exitValue(exitValue) - .pipeOutput(captureOutput) - .pipeError(captureOutput) - .pipeInput(Streamables.nullInput()); - - if (environment != null) { - for (Map.Entry entry : environment.entrySet()) { - exec.env(entry.getKey(), entry.getValue()); - } - } - - exec.run(); - } else { - SshExec exec - = SecureShells.sshExec(ssh) - .command(exe) - .args((Object[]) args) - .exitValue(exitValue) - .pipeOutput(captureOutput) - .pipeError(captureOutput); - - if (environment != null) { - for (Map.Entry entry : environment.entrySet()) { - exec.env(entry.getKey(), entry.getValue()); - } - } - - exec.run(); - } - } catch (Exception e) { - log.error("Unable to cleanly capture output: {}", captureOutput.asString()); - throw e; - } - - return captureOutput.asString(); - } - - public String findJson(String stdout) { - int start = stdout.indexOf("{"); - if (start < 0) { - throw new IllegalArgumentException("Unable to find starting json {"); - } - int end = stdout.lastIndexOf("}"); - if (end < 0) { - throw new IllegalArgumentException("Unable to find ending json {"); - } - return stdout.substring(start, end+1); - } - - public T readValue(Path file, Class type) throws IOException { - try { - return objectMapper.readValue(file.toFile(), type); - } catch (IOException e) { - byte[] bytes = Files.readAllBytes(file); - log.error("Unable to parse {} into json. File contained\n{}", - file, - new String(bytes, StandardCharsets.UTF_8)); - fail(e.getMessage()); - return null; - } - } - - public T readValue(String json, Class type) { - try { - return objectMapper.readValue(json, type); - } catch (IOException e) { - log.error("Unable to parse into json. json was\n{}", json); - fail(e.getMessage()); - return null; - } - } - - @Test - public void console() throws Exception { - String stdout = execute(0, exeEchoConsole1); - String json = findJson(stdout); - - LaunchData output = this.readValue(json, LaunchData.class); - - assertThat(output.getConfirm(), is("Hello World!")); - assertThat(output.getArguments(), hasSize(0)); - - // stork always sets launcher.name, launcher.type, launcher.app.dir - assertThat(output.getSystemProperties(), hasEntry("launcher.name", "echo-console1")); - assertThat(output.getSystemProperties(), hasEntry("launcher.type", "CONSOLE")); - assertThat(output.getSystemProperties(), hasKey("launcher.app.dir")); - - // hard to determine real working dir so only do this on "local" - if (isLocal()) { - // verify working directory was retained - Path ourWorkingDir = Paths.get((String)System.getProperty("user.dir")); - Path appWorkingDir = Paths.get((String)output.getSystemProperties().get("user.dir")); - Path appHomeDir = Paths.get((String)output.getSystemProperties().get("launcher.app.dir")); - - assertThat(appWorkingDir, is(ourWorkingDir)); - assertThat(appHomeDir, is(not(ourWorkingDir))); - assertThat(appHomeDir, is(not(appWorkingDir))); - } - } - - @Test - public void consoleWithArguments() throws Exception { - String arg0 = "128401"; - String arg1 = "ahs3h1"; - - String stdout = execute(0, exeEchoConsole1, arg0, arg1); - String json = findJson(stdout); - - LaunchData output = this.readValue(json, LaunchData.class); - - assertThat(output.getConfirm(), is("Hello World!")); - assertThat(output.getArguments(), hasSize(2)); - assertThat(output.getArguments().get(0), is(arg0)); - assertThat(output.getArguments().get(1), is(arg1)); - } - - @Test - public void consoleWithArgumentsThatHaveSpaces() throws Exception { - // skip on windows since its just too complicated to escape them - // correctly using a batch file - Assume.assumeFalse(this.isWindows()); - - String arg0 = "128 401"; - String arg1 = "ahs 3h1"; - - String stdout = execute(0, exeEchoConsole1, arg0, arg1); - String json = findJson(stdout); - - LaunchData output = this.readValue(json, LaunchData.class); - - assertThat(output.getConfirm(), is("Hello World!")); - assertThat(output.getArguments(), hasSize(2)); - assertThat(output.getArguments().get(0), is(arg0)); - assertThat(output.getArguments().get(1), is(arg1)); - } - - @Test - public void consoleWithExtraArguments() throws Exception { - // windows sshd does NOT allow env vars - Assume.assumeFalse(this.host.startsWith("windows")); - - Map environment = new HashMap<>(); - environment.put("EXTRA_APP_ARGS", "a b"); - - String stdout = execute(0, exeEchoConsole1, environment); - String json = findJson(stdout); - - LaunchData output = this.readValue(json, LaunchData.class); - - assertThat(output.getConfirm(), is("Hello World!")); - assertThat(output.getArguments(), hasSize(2)); - assertThat(output.getArguments().get(0), is("a")); - assertThat(output.getArguments().get(1), is("b")); - } - - @Test - public void consoleWithExtraArgumentsThenCommandLineArguments() throws Exception { - // windows sshd does NOT allow env vars - Assume.assumeFalse(this.host.startsWith("windows")); - - Map environment = new HashMap<>(); - environment.put("EXTRA_APP_ARGS", "a b"); - - String stdout = execute(0, exeEchoConsole1, environment, "c", "d"); - String json = findJson(stdout); - - LaunchData output = this.readValue(json, LaunchData.class); - - assertThat(output.getConfirm(), is("Hello World!")); - assertThat(output.getArguments(), hasSize(4)); - assertThat(output.getArguments().get(0), is("a")); - assertThat(output.getArguments().get(1), is("b")); - assertThat(output.getArguments().get(2), is("c")); - assertThat(output.getArguments().get(3), is("d")); - } - - @Test - public void consoleWithSystemProperties() throws Exception { - String stdout = execute(0, exeEchoConsole1, "-Da=1", "-Db=2"); - String json = findJson(stdout); - - LaunchData output = this.readValue(json, LaunchData.class); - - assertThat(output.getConfirm(), is("Hello World!")); - assertThat(output.getArguments(), hasSize(0)); - assertThat(output.getSystemProperties(), hasEntry("a", "1")); - assertThat(output.getSystemProperties(), hasEntry("b", "2")); - } - - @Test - public void consoleWithArgumentsAndSystemProperties() throws Exception { - String stdout = execute(0, exeEchoConsole1, "a", "-Da=1", "b", "-Db=2", "c"); - String json = findJson(stdout); - - LaunchData output = this.readValue(json, LaunchData.class); - - assertThat(output.getConfirm(), is("Hello World!")); - assertThat(output.getArguments(), hasSize(3)); - assertThat(output.getArguments().get(0), is("a")); - assertThat(output.getArguments().get(1), is("b")); - assertThat(output.getArguments().get(2), is("c")); - assertThat(output.getSystemProperties(), hasEntry("a", "1")); - assertThat(output.getSystemProperties(), hasEntry("b", "2")); - } - - @Test - public void consoleWithExtraJavaArgs() throws Exception { - // windows sshd does NOT allow env vars - Assume.assumeFalse(this.host.startsWith("windows")); - - Map environment = new HashMap<>(); - environment.put("EXTRA_JAVA_ARGS", "-Da=1 -Db=2"); - - String stdout = execute(0, exeEchoConsole1, environment); - String json = findJson(stdout); - - LaunchData output = this.readValue(json, LaunchData.class); - - assertThat(output.getConfirm(), is("Hello World!")); - assertThat(output.getArguments(), hasSize(0)); - assertThat(output.getSystemProperties(), hasEntry("a", "1")); - assertThat(output.getSystemProperties(), hasEntry("b", "2")); - } - - @Test - public void consoleWithExtraJavaArgsAndCommandLineSystemProperties() throws Exception { - // windows sshd does NOT allow env vars - Assume.assumeFalse(this.host.startsWith("windows")); - - Map environment = new HashMap<>(); - environment.put("EXTRA_JAVA_ARGS", "-Da=1 -Db=2"); - - // this should override the "extra" one since it will be appended - // after the extra_java_args would be - String stdout = execute(0, exeEchoConsole1, environment, "-Da=2"); - String json = findJson(stdout); - - LaunchData output = this.readValue(json, LaunchData.class); - - assertThat(output.getConfirm(), is("Hello World!")); - assertThat(output.getArguments(), hasSize(0)); - assertThat(output.getSystemProperties(), hasEntry("a", "2")); - assertThat(output.getSystemProperties(), hasEntry("b", "2")); - } - - @Test - public void consoleMainClassNotFound() throws Exception { - String stdout = execute(1, exeEchoConsole2); - - assertThat(stdout, containsString("Could not find or load main class com.fizzed.stork.test.ClassNotFoundMain")); - } - - @Test - public void consoleMinJavaVersionNotFound() throws Exception { - String stdout = execute(1, exeEchoConsole3); - - // should this be printed out to error stream rather than stdout? - assertThat(stdout, containsString("Unable to find Java runtime on system with version >= 1.99")); - } - -// @Test -// public void consoleJavaHomeWithSpaces() throws Exception { -// assumeTrue("java symlink worked", symlinkJava != null); -// -// Map environment = new HashMap<>(); -// //environment.put("PATH", "/bin:/usr/bin"); -// environment.put("JAVA_HOME", symlinkJavaWithSpaces.toString()); -// -// String json = execute(0, exeEchoConsole1, environment); -// -// LaunchData output = this.readValue(json, LaunchData.class); -// -// assertThat(output.getConfirm(), is("Hello World!")); -// assertThat(output.getArguments(), hasSize(0)); -// } - - @Test - public void consoleJavaArgsWithSpaces() throws Exception { - // windows sshd does NOT allow env vars - Assume.assumeFalse(this.host.startsWith("windows")); - - Map environment = new HashMap<>(); - environment.put("JAVA_ARGS", "-Da=1 -Db=2 -Dc=3"); - - String stdout = execute(0, exeEchoConsole1, environment); - String json = findJson(stdout); - - LaunchData output = this.readValue(json, LaunchData.class); - - assertThat(output.getConfirm(), is("Hello World!")); - assertThat(output.getSystemProperties(), hasEntry("a", "1")); - assertThat(output.getSystemProperties(), hasEntry("b", "2")); - assertThat(output.getSystemProperties(), hasEntry("c", "3")); - } - - @Test - public void daemonRun() throws Exception { - String stdout = execute(0, exeEchoDaemon1, "--run"); - String json = findJson(stdout); - - LaunchData output = this.readValue(json, LaunchData.class); - - assertThat(output.getConfirm(), is("Hello World!")); - - // -Xrs flag should have been set - assertThat(output.getJvmArguments(), hasItem("-Xrs")); - - // stork always sets launcher.name, launcher.type, launcher.app.dir - // daemons that use the --run command are considered a CONSOLE app by stork - assertThat(output.getSystemProperties(), hasEntry("launcher.name", "echo-daemon1")); - assertThat(output.getSystemProperties(), hasEntry("launcher.type", "CONSOLE")); - assertThat(output.getSystemProperties(), hasKey("launcher.app.dir")); - - // only do these on local since its hard to get correct path via ssh - if (isLocal()) { - // verify working directory was retained - Path ourWorkingDir = Paths.get((String)System.getProperty("user.dir")); - Path appWorkingDir = Paths.get((String)output.getSystemProperties().get("user.dir")); - Path appHomeDir = Paths.get((String)output.getSystemProperties().get("launcher.app.dir")); - - assertThat(appWorkingDir, is(not((ourWorkingDir)))); - assertThat(appWorkingDir, is(appHomeDir)); - } - } - - @Test - public void daemonRunWithArguments() throws Exception { - String stdout = execute(0, exeEchoDaemon1, "--run", "a", "b"); - String json = findJson(stdout); - - LaunchData output = this.readValue(json, LaunchData.class); - - assertThat(output.getConfirm(), is("Hello World!")); - assertThat(output.getArguments(), hasSize(2)); - assertThat(output.getArguments().get(0), is("a")); - assertThat(output.getArguments().get(1), is("b")); - } - - @Test - public void daemonRunWithSystemProperties() throws Exception { - String stdout = execute(0, exeEchoDaemon1, "--run", "-Da=1", "-Db=2"); - String json = findJson(stdout); - - LaunchData output = this.readValue(json, LaunchData.class); - - assertThat(output.getConfirm(), is("Hello World!")); - assertThat(output.getArguments(), hasSize(0)); - assertThat(output.getSystemProperties(), hasEntry("a", "1")); - assertThat(output.getSystemProperties(), hasEntry("b", "2")); - } - - @Test - public void daemonExec() throws Exception { - // do not run this test on windows - assumeFalse(isWindows()); - - // make sure the file does not exist between tests - Path path = Paths.get("run/test-launch-data.json"); - Files.deleteIfExists(path); - - // by setting working dir mode to APP_HOME - this will be relative - // in case we are running on windows fix the path - execute(0, exeEchoDaemon1, "--exec", "--data-file", path.toString().replace('\\', '/')); - - // give daemon just a little time to write its data file - Thread.sleep(1000L); - - String stdout = execute(0, exeCat, path.toString().replace('\\', '/')); - String json = this.findJson(stdout); - - LaunchData output = this.readValue(json, LaunchData.class); - - assertThat(output.getConfirm(), is("Hello World!")); - - // -Xrs flag should have been set ONCE - assertThat(output.getJvmArguments(), hasItem("-Xrs")); - long count = 0; - for (String s : output.getJvmArguments()) { - if (s.equals("-Xrs")) { - count++; - } - } - assertThat("-Xrs only occurs once", count, is(1L)); - - // stork always sets launcher.name, launcher.type, launcher.app.dir - // daemons that use the --run command are considered a CONSOLE app by stork - assertThat(output.getSystemProperties(), hasEntry("launcher.name", "echo-daemon1")); - assertThat(output.getSystemProperties(), hasEntry("launcher.type", "DAEMON")); - assertThat(output.getSystemProperties(), hasKey("launcher.app.dir")); - - // only do these on local since its hard to get correct path via ssh - if (isLocal()) { - // verify working directory was retained - Path ourWorkingDir = Paths.get((String)System.getProperty("user.dir")); - Path appWorkingDir = Paths.get((String)output.getSystemProperties().get("user.dir")); - Path appHomeDir = Paths.get((String)output.getSystemProperties().get("launcher.app.dir")); - - assertThat(appWorkingDir, is(not((ourWorkingDir)))); - assertThat(appWorkingDir, is(appHomeDir)); - } - } -} +/* + * Copyright 2016 Fizzed, Inc. + * + * Licensed 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 com.fizzed.stork.launcher; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fizzed.blaze.SecureShells; +import com.fizzed.blaze.Systems; +import com.fizzed.blaze.ssh.SshExec; +import com.fizzed.blaze.ssh.SshSession; +import com.fizzed.blaze.system.Exec; +import com.fizzed.blaze.util.CaptureOutput; +import com.fizzed.blaze.util.Streamables; +import com.fizzed.stork.test.LaunchData; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.hasItem; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.Matchers.hasEntry; +import static org.hamcrest.Matchers.hasKey; +import static org.hamcrest.Matchers.hasSize; +import org.junit.Test; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; +import org.junit.Assume; +import static org.junit.Assume.assumeFalse; +import static org.junit.Assume.assumeTrue; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@RunWith(Parameterized.class) +public class LauncherTest { + static private final Logger log = LoggerFactory.getLogger(LauncherTest.class); + + @Parameters(name = "{index}: host={0}") + public static Collection data() { + return TestHelper.hosts(); + } + + static private final Map sshs = new HashMap<>(); + private final ObjectMapper objectMapper = new ObjectMapper(); + private final String host; + private final Path exeCat; + private final Path exeEchoConsole1; + private final Path exeEchoConsole2; + private final Path exeEchoConsole3; + private final Path exeEchoDaemon1; + private final SshSession ssh; + static private boolean vagrantRsynced; + + public LauncherTest(String host) { + this.host = host; + this.exeCat = resolveExe("cat"); + this.exeEchoConsole1 = resolveExe("echo-console1"); + this.exeEchoConsole2 = resolveExe("echo-console2"); + this.exeEchoConsole3 = resolveExe("echo-console3"); + this.exeEchoDaemon1 = resolveExe("echo-daemon1"); + //this.symlinkJavaExe = resolveExe("symlink-java"); + if (!sshs.containsKey(host)) { + sshs.put(host, sshConnect()); + } + this.ssh = sshs.get(host); + } + + private boolean isHostRunning() { + return host.equals("local") + || TestHelper.VAGRANT_CLIENT.machinesRunning().contains(host); + } + + private SshSession sshConnect() { + switch (this.host) { + case "local": + return null; + default: + if (isHostRunning()) { + Path sshConfig = TestHelper.VAGRANT_CLIENT.sshConfig(host); + return SecureShells.sshConnect("ssh://" + host).configFile(sshConfig).run(); + } else { + return null; + } + } + } + + @BeforeClass + static public void vagrantRsynced() throws Exception { + if (!vagrantRsynced) { + try { + Systems.exec("vagrant", "rsync").exitValues(0, 1).run(); + } catch (Exception e) { + // ignore + } + vagrantRsynced = true; + } + } + + @Before + public void onlyIfHostIsRunning() throws Exception { + assumeTrue("Is host running?", isHostRunning()); + +// if (symlinkJava == null) { +// // do not try to symlink java on a local windows client +// if (isWindows()) { +// return; // skip +// } +// +// String output = execute(0, symlinkJavaExe); +// +// // 3 lines +// // /usr/lib/jvm/jdk1.8.0_77/jre +// ///tmp/java-linked +// ///tmp/java-linked with spaces +// String[] lines = output.trim().split("\n"); +// symlinkJava = Paths.get(lines[1].trim()); +// symlinkJavaWithSpaces = Paths.get(lines[2].trim()); +// } + } + + private boolean isLocal() { + return host.equals("local"); + } + + private boolean isWindows() { + return host.startsWith("windows") + || (host.equals("local") && TestHelper.isWindows()); + } + + private Path resolveExe(String exeName) { + if (isWindows()) { + exeName += ".bat"; + } + + switch (this.host) { + case "local": + return Paths.get("target/stork/bin").resolve(exeName); + default: + return Paths.get("/vagrant/stork-launcher/target/stork/bin").resolve(exeName); + } + } + + public String execute(int exitValue, Path exe, String... args) throws Exception { + return execute(exitValue, exe, null, args); + } + + public String execute(int exitValue, Path exe, Map environment, String... args) throws Exception { + CaptureOutput captureOutput = Streamables.captureOutput(); + + try { + if (this.host.equals("local")) { + Exec exec + = Systems.exec(exe) + .args((Object[]) args) + .exitValue(exitValue) + .pipeOutput(captureOutput) + .pipeError(captureOutput) + .pipeInput(Streamables.nullInput()); + + if (environment != null) { + for (Map.Entry entry : environment.entrySet()) { + exec.env(entry.getKey(), entry.getValue()); + } + } + + exec.run(); + } else { + Exec exec + = SecureShells.sshExec(ssh) + .command(exe) + .args((Object[]) args) + .exitValue(exitValue) + .pipeOutput(captureOutput) + .pipeError(captureOutput); + + if (environment != null) { + for (Map.Entry entry : environment.entrySet()) { + exec.env(entry.getKey(), entry.getValue()); + } + } + + exec.run(); + } + } catch (Exception e) { + log.error("Unable to cleanly capture output: {}", captureOutput.asString()); + throw e; + } + + return captureOutput.asString(); + } + + public String findJson(String stdout) { + int start = stdout.indexOf("{"); + if (start < 0) { + throw new IllegalArgumentException("Unable to find starting json {"); + } + int end = stdout.lastIndexOf("}"); + if (end < 0) { + throw new IllegalArgumentException("Unable to find ending json {"); + } + return stdout.substring(start, end+1); + } + + public T readValue(Path file, Class type) throws IOException { + try { + return objectMapper.readValue(file.toFile(), type); + } catch (IOException e) { + byte[] bytes = Files.readAllBytes(file); + log.error("Unable to parse {} into json. File contained\n{}", + file, + new String(bytes, StandardCharsets.UTF_8)); + fail(e.getMessage()); + return null; + } + } + + public T readValue(String json, Class type) { + try { + return objectMapper.readValue(json, type); + } catch (IOException e) { + log.error("Unable to parse into json. json was\n{}", json); + fail(e.getMessage()); + return null; + } + } + + @Test + public void console() throws Exception { + String stdout = execute(0, exeEchoConsole1); + String json = findJson(stdout); + + LaunchData output = this.readValue(json, LaunchData.class); + + assertThat(output.getConfirm(), is("Hello World!")); + assertThat(output.getArguments(), hasSize(0)); + + // stork always sets launcher.name, launcher.type, launcher.app.dir + assertThat(output.getSystemProperties(), hasEntry("launcher.name", "echo-console1")); + assertThat(output.getSystemProperties(), hasEntry("launcher.type", "CONSOLE")); + assertThat(output.getSystemProperties(), hasKey("launcher.app.dir")); + + // hard to determine real working dir so only do this on "local" + if (isLocal()) { + // verify working directory was retained + Path ourWorkingDir = Paths.get((String)System.getProperty("user.dir")); + Path appWorkingDir = Paths.get((String)output.getSystemProperties().get("user.dir")); + Path appHomeDir = Paths.get((String)output.getSystemProperties().get("launcher.app.dir")); + + assertThat(appWorkingDir, is(ourWorkingDir)); + assertThat(appHomeDir, is(not(ourWorkingDir))); + assertThat(appHomeDir, is(not(appWorkingDir))); + } + } + + @Test + public void consoleWithArguments() throws Exception { + String arg0 = "128401"; + String arg1 = "ahs3h1"; + + String stdout = execute(0, exeEchoConsole1, arg0, arg1); + String json = findJson(stdout); + + LaunchData output = this.readValue(json, LaunchData.class); + + assertThat(output.getConfirm(), is("Hello World!")); + assertThat(output.getArguments(), hasSize(2)); + assertThat(output.getArguments().get(0), is(arg0)); + assertThat(output.getArguments().get(1), is(arg1)); + } + + @Test + public void consoleWithArgumentsThatHaveSpaces() throws Exception { + // skip on windows since its just too complicated to escape them + // correctly using a batch file + Assume.assumeFalse(this.isWindows()); + + String arg0 = "128 401"; + String arg1 = "ahs 3h1"; + + String stdout = execute(0, exeEchoConsole1, arg0, arg1); + String json = findJson(stdout); + + LaunchData output = this.readValue(json, LaunchData.class); + + assertThat(output.getConfirm(), is("Hello World!")); + assertThat(output.getArguments(), hasSize(2)); + assertThat(output.getArguments().get(0), is(arg0)); + assertThat(output.getArguments().get(1), is(arg1)); + } + + @Test + public void consoleWithExtraArguments() throws Exception { + // windows sshd does NOT allow env vars + Assume.assumeFalse(this.host.startsWith("windows")); + + Map environment = new HashMap<>(); + environment.put("EXTRA_APP_ARGS", "a b"); + + String stdout = execute(0, exeEchoConsole1, environment); + String json = findJson(stdout); + + LaunchData output = this.readValue(json, LaunchData.class); + + assertThat(output.getConfirm(), is("Hello World!")); + assertThat(output.getArguments(), hasSize(2)); + assertThat(output.getArguments().get(0), is("a")); + assertThat(output.getArguments().get(1), is("b")); + } + + @Test + public void consoleWithExtraArgumentsThenCommandLineArguments() throws Exception { + // windows sshd does NOT allow env vars + Assume.assumeFalse(this.host.startsWith("windows")); + + Map environment = new HashMap<>(); + environment.put("EXTRA_APP_ARGS", "a b"); + + String stdout = execute(0, exeEchoConsole1, environment, "c", "d"); + String json = findJson(stdout); + + LaunchData output = this.readValue(json, LaunchData.class); + + assertThat(output.getConfirm(), is("Hello World!")); + assertThat(output.getArguments(), hasSize(4)); + assertThat(output.getArguments().get(0), is("a")); + assertThat(output.getArguments().get(1), is("b")); + assertThat(output.getArguments().get(2), is("c")); + assertThat(output.getArguments().get(3), is("d")); + } + + @Test + public void consoleWithSystemProperties() throws Exception { + String stdout = execute(0, exeEchoConsole1, "-Da=1", "-Db=2"); + String json = findJson(stdout); + + LaunchData output = this.readValue(json, LaunchData.class); + + assertThat(output.getConfirm(), is("Hello World!")); + assertThat(output.getArguments(), hasSize(0)); + assertThat(output.getSystemProperties(), hasEntry("a", "1")); + assertThat(output.getSystemProperties(), hasEntry("b", "2")); + } + + @Test + public void consoleWithArgumentsAndSystemProperties() throws Exception { + String stdout = execute(0, exeEchoConsole1, "a", "-Da=1", "b", "-Db=2", "c"); + String json = findJson(stdout); + + LaunchData output = this.readValue(json, LaunchData.class); + + assertThat(output.getConfirm(), is("Hello World!")); + assertThat(output.getArguments(), hasSize(3)); + assertThat(output.getArguments().get(0), is("a")); + assertThat(output.getArguments().get(1), is("b")); + assertThat(output.getArguments().get(2), is("c")); + assertThat(output.getSystemProperties(), hasEntry("a", "1")); + assertThat(output.getSystemProperties(), hasEntry("b", "2")); + } + + @Test + public void consoleWithExtraJavaArgs() throws Exception { + // windows sshd does NOT allow env vars + Assume.assumeFalse(this.host.startsWith("windows")); + + Map environment = new HashMap<>(); + environment.put("EXTRA_JAVA_ARGS", "-Da=1 -Db=2"); + + String stdout = execute(0, exeEchoConsole1, environment); + String json = findJson(stdout); + + LaunchData output = this.readValue(json, LaunchData.class); + + assertThat(output.getConfirm(), is("Hello World!")); + assertThat(output.getArguments(), hasSize(0)); + assertThat(output.getSystemProperties(), hasEntry("a", "1")); + assertThat(output.getSystemProperties(), hasEntry("b", "2")); + } + + @Test + public void consoleWithExtraJavaArgsAndCommandLineSystemProperties() throws Exception { + // windows sshd does NOT allow env vars + Assume.assumeFalse(this.host.startsWith("windows")); + + Map environment = new HashMap<>(); + environment.put("EXTRA_JAVA_ARGS", "-Da=1 -Db=2"); + + // this should override the "extra" one since it will be appended + // after the extra_java_args would be + String stdout = execute(0, exeEchoConsole1, environment, "-Da=2"); + String json = findJson(stdout); + + LaunchData output = this.readValue(json, LaunchData.class); + + assertThat(output.getConfirm(), is("Hello World!")); + assertThat(output.getArguments(), hasSize(0)); + assertThat(output.getSystemProperties(), hasEntry("a", "2")); + assertThat(output.getSystemProperties(), hasEntry("b", "2")); + } + + @Test + public void consoleMainClassNotFound() throws Exception { + String stdout = execute(1, exeEchoConsole2); + + assertThat(stdout, containsString("Could not find or load main class com.fizzed.stork.test.ClassNotFoundMain")); + } + + @Test + public void consoleMinJavaVersionNotFound() throws Exception { + String stdout = execute(1, exeEchoConsole3); + + // should this be printed out to error stream rather than stdout? + assertThat(stdout, containsString("Unable to find Java runtime on system with version >= 1.99")); + } + +// @Test +// public void consoleJavaHomeWithSpaces() throws Exception { +// assumeTrue("java symlink worked", symlinkJava != null); +// +// Map environment = new HashMap<>(); +// //environment.put("PATH", "/bin:/usr/bin"); +// environment.put("JAVA_HOME", symlinkJavaWithSpaces.toString()); +// +// String json = execute(0, exeEchoConsole1, environment); +// +// LaunchData output = this.readValue(json, LaunchData.class); +// +// assertThat(output.getConfirm(), is("Hello World!")); +// assertThat(output.getArguments(), hasSize(0)); +// } + + @Test + public void consoleJavaArgsWithSpaces() throws Exception { + // windows sshd does NOT allow env vars + Assume.assumeFalse(this.host.startsWith("windows")); + + Map environment = new HashMap<>(); + environment.put("JAVA_ARGS", "-Da=1 -Db=2 -Dc=3"); + + String stdout = execute(0, exeEchoConsole1, environment); + String json = findJson(stdout); + + LaunchData output = this.readValue(json, LaunchData.class); + + assertThat(output.getConfirm(), is("Hello World!")); + assertThat(output.getSystemProperties(), hasEntry("a", "1")); + assertThat(output.getSystemProperties(), hasEntry("b", "2")); + assertThat(output.getSystemProperties(), hasEntry("c", "3")); + } + + @Test + public void daemonRun() throws Exception { + String stdout = execute(0, exeEchoDaemon1, "--run"); + String json = findJson(stdout); + + LaunchData output = this.readValue(json, LaunchData.class); + + assertThat(output.getConfirm(), is("Hello World!")); + + // -Xrs flag should have been set + assertThat(output.getJvmArguments(), hasItem("-Xrs")); + + // stork always sets launcher.name, launcher.type, launcher.app.dir + // daemons that use the --run command are considered a CONSOLE app by stork + assertThat(output.getSystemProperties(), hasEntry("launcher.name", "echo-daemon1")); + assertThat(output.getSystemProperties(), hasEntry("launcher.type", "CONSOLE")); + assertThat(output.getSystemProperties(), hasKey("launcher.app.dir")); + + // only do these on local since its hard to get correct path via ssh + if (isLocal()) { + // verify working directory was retained + Path ourWorkingDir = Paths.get((String)System.getProperty("user.dir")); + Path appWorkingDir = Paths.get((String)output.getSystemProperties().get("user.dir")); + Path appHomeDir = Paths.get((String)output.getSystemProperties().get("launcher.app.dir")); + + assertThat(appWorkingDir, is(not((ourWorkingDir)))); + assertThat(appWorkingDir, is(appHomeDir)); + } + } + + @Test + public void daemonRunWithArguments() throws Exception { + String stdout = execute(0, exeEchoDaemon1, "--run", "a", "b"); + String json = findJson(stdout); + + LaunchData output = this.readValue(json, LaunchData.class); + + assertThat(output.getConfirm(), is("Hello World!")); + assertThat(output.getArguments(), hasSize(2)); + assertThat(output.getArguments().get(0), is("a")); + assertThat(output.getArguments().get(1), is("b")); + } + + @Test + public void daemonRunWithSystemProperties() throws Exception { + String stdout = execute(0, exeEchoDaemon1, "--run", "-Da=1", "-Db=2"); + String json = findJson(stdout); + + LaunchData output = this.readValue(json, LaunchData.class); + + assertThat(output.getConfirm(), is("Hello World!")); + assertThat(output.getArguments(), hasSize(0)); + assertThat(output.getSystemProperties(), hasEntry("a", "1")); + assertThat(output.getSystemProperties(), hasEntry("b", "2")); + } + + @Test + public void daemonExec() throws Exception { + // do not run this test on windows + assumeFalse(isWindows()); + + // make sure the file does not exist between tests + Path path = Paths.get("run/test-launch-data.json"); + Files.deleteIfExists(path); + + // by setting working dir mode to APP_HOME - this will be relative + // in case we are running on windows fix the path + execute(0, exeEchoDaemon1, "--exec", "--data-file", path.toString().replace('\\', '/')); + + // give daemon just a little time to write its data file + Thread.sleep(1000L); + + String stdout = execute(0, exeCat, path.toString().replace('\\', '/')); + String json = this.findJson(stdout); + + LaunchData output = this.readValue(json, LaunchData.class); + + assertThat(output.getConfirm(), is("Hello World!")); + + // -Xrs flag should have been set ONCE + assertThat(output.getJvmArguments(), hasItem("-Xrs")); + long count = 0; + for (String s : output.getJvmArguments()) { + if (s.equals("-Xrs")) { + count++; + } + } + assertThat("-Xrs only occurs once", count, is(1L)); + + // stork always sets launcher.name, launcher.type, launcher.app.dir + // daemons that use the --run command are considered a CONSOLE app by stork + assertThat(output.getSystemProperties(), hasEntry("launcher.name", "echo-daemon1")); + assertThat(output.getSystemProperties(), hasEntry("launcher.type", "DAEMON")); + assertThat(output.getSystemProperties(), hasKey("launcher.app.dir")); + + // only do these on local since its hard to get correct path via ssh + if (isLocal()) { + // verify working directory was retained + Path ourWorkingDir = Paths.get((String)System.getProperty("user.dir")); + Path appWorkingDir = Paths.get((String)output.getSystemProperties().get("user.dir")); + Path appHomeDir = Paths.get((String)output.getSystemProperties().get("launcher.app.dir")); + + assertThat(appWorkingDir, is(not((ourWorkingDir)))); + assertThat(appWorkingDir, is(appHomeDir)); + } + } +} From a335804dd473c8f7069d080916b054a45539a4a5 Mon Sep 17 00:00:00 2001 From: Mike Gager Date: Tue, 18 Nov 2025 23:44:18 -0500 Subject: [PATCH 3/3] Suppress SSH command output --- .../java/com/fizzed/stork/deploy/Targets.java | 17 ++++------------- .../com/fizzed/stork/deploy/UnixTarget.java | 14 +++----------- 2 files changed, 7 insertions(+), 24 deletions(-) diff --git a/stork-deploy/src/main/java/com/fizzed/stork/deploy/Targets.java b/stork-deploy/src/main/java/com/fizzed/stork/deploy/Targets.java index e48c0e1..4bc599e 100644 --- a/stork-deploy/src/main/java/com/fizzed/stork/deploy/Targets.java +++ b/stork-deploy/src/main/java/com/fizzed/stork/deploy/Targets.java @@ -91,10 +91,7 @@ static private InitType initType(SshSession ssh) { "elif \\$(systemctl 2>/dev/null| egrep -q .mount); then echo systemd; " + "elif [ -f /etc/init.d/cron ] || [ -f /etc/init.d/crond ]; then echo sysv; " + "else echo unknown; fi") - .pipeOutput(Streamables.captureOutput()) - .pipeError(Streamables.nullOutput()) - .runResult() - .map(Actions::toCaptureOutput) + .runCaptureOutput(false) .asString(); if (initTypeString == null) { @@ -117,13 +114,10 @@ static private Map which(SshSession ssh, List commands) { // doesn't matter if we find it or not String whichString = sshExec(ssh, "which", commands.toArray()) - .pipeOutput(Streamables.captureOutput()) - .pipeError(Streamables.nullOutput()) .exitValues(0, 1, 2) - .runResult() - .map(Actions::toCaptureOutput) + .runCaptureOutput(false) .asString(); - + if (whichString == null) { return Collections.emptyMap(); } @@ -143,11 +137,8 @@ static private Map which(SshSession ssh, List commands) { static private String uname(SshSession ssh) { // doesn't matter if we find it or not return sshExec(ssh, "uname", "-srm") - .pipeOutput(Streamables.captureOutput()) - .pipeError(Streamables.nullOutput()) .exitValues(0) - .runResult() - .map(Actions::toCaptureOutput) + .runCaptureOutput(false) .asString() .trim(); } diff --git a/stork-deploy/src/main/java/com/fizzed/stork/deploy/UnixTarget.java b/stork-deploy/src/main/java/com/fizzed/stork/deploy/UnixTarget.java index 94b19b1..c7693da 100644 --- a/stork-deploy/src/main/java/com/fizzed/stork/deploy/UnixTarget.java +++ b/stork-deploy/src/main/java/com/fizzed/stork/deploy/UnixTarget.java @@ -159,10 +159,7 @@ public Integer getUserId(String user) { try { String userId = sshExec(false, false, "id", "-u", user) - .pipeOutput(Streamables.captureOutput()) - .pipeError(Streamables.nullOutput()) - .runResult() - .map(Actions::toCaptureOutput) + .runCaptureOutput(false) .asString() .trim(); return Integer.valueOf(userId); @@ -180,10 +177,7 @@ public Integer getGroupId(String group) { try { String groupId = sshExec(false, false, "id", "-g", group) - .pipeOutput(Streamables.captureOutput()) - .pipeError(Streamables.nullOutput()) - .runResult() - .map(Actions::toCaptureOutput) + .runCaptureOutput(false) .asString() .trim(); return Integer.valueOf(groupId); @@ -264,9 +258,7 @@ public void startDaemon(Daemon daemon) throws DeployerException { // run a status command so user can see what's up String output = sshExec(true, false, "systemctl", "status", daemon.getName()) - .pipeOutput(Streamables.captureOutput()) - .runResult() - .map(Actions::toCaptureOutput) + .runCaptureOutput(false) .asString(); }