From 45d37c4ff1d72809724bd2a6178a5772ace5426a Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Tue, 10 Oct 2023 11:12:50 -0500 Subject: [PATCH 01/20] Issue #10696 - Addressing start-stop-daemon behaviors in jetty.sh Signed-off-by: Joakim Erdfelt --- jetty-home/src/main/resources/bin/jetty.sh | 52 +++++++++++++++----- jetty-home/src/main/resources/etc/jetty.conf | 2 +- 2 files changed, 40 insertions(+), 14 deletions(-) diff --git a/jetty-home/src/main/resources/bin/jetty.sh b/jetty-home/src/main/resources/bin/jetty.sh index bca4af8ea1b4..3080322977ce 100755 --- a/jetty-home/src/main/resources/bin/jetty.sh +++ b/jetty-home/src/main/resources/bin/jetty.sh @@ -240,6 +240,10 @@ dumpEnv() echo "JETTY_START_TIMEOUT = $JETTY_START_TIMEOUT" echo "JETTY_SYS_PROPS = $JETTY_SYS_PROPS" echo "RUN_ARGS = ${RUN_ARGS[*]}" + echo "ID = $(id)" + echo "JETTY_USER = $JETTY_USER" + echo "USE_START_STOP_DAEMON = $USE_START_STOP_DAEMON" + echo "START_STOP_DAEMON = $START_STOP_DAEMON_AVAILABLE" } @@ -404,7 +408,6 @@ case "`uname`" in CYGWIN*) JETTY_STATE="`cygpath -w $JETTY_STATE`";; esac - JETTY_ARGS=(${JETTY_ARGS[*]} "jetty.state=$JETTY_STATE" "jetty.pid=$JETTY_PID") ################################################## @@ -507,6 +510,25 @@ case "`uname`" in CYGWIN*) JETTY_START="`cygpath -w $JETTY_START`";; esac +# Determine if we are using start-stop-daemon or not +if [ -z "$USE_START_STOP_DAEMON" ] +then + USE_START_STOP_DAEMON=1 +fi + +START_STOP_DAEMON_AVAILABLE=0 + +if (( USE_START_STOP_DAEMON )) +then + if [ $UID -eq 0 ] && type start-stop-daemon > /dev/null 2>&1 + then + START_STOP_DAEMON_AVAILABLE=1 + else + USE_START_STOP_DAEMON=0 + JETTY_ARGS=(${JETTY_ARGS[*]} "--module=pid" "jetty.pid=$JETTY_PID") + fi +fi + # Collect the dry-run (of opts,path,main,args) from the jetty.base configuration JETTY_DRY_RUN=$("$JAVA" -jar "$JETTY_START" --dry-run=opts,path,main,args ${JETTY_ARGS[*]} ${JAVA_OPTIONS[*]}) RUN_ARGS=($JETTY_SYS_PROPS ${JETTY_DRY_RUN[@]}) @@ -531,24 +553,28 @@ case "$ACTION" in exit fi - if ! touch "$JETTY_PID" + # If not changing users, test for file system permissions. + if [ -z "$JETTY_USER"] then - echo "** ERROR: Unable to touch file: $JETTY_PID" - echo " Correct issues preventing use of \$JETTY_PID and try again." - exit 1 - fi + if ! touch "$JETTY_PID" + then + echo "** ERROR: Unable to touch file: $JETTY_PID" + echo " Correct issues preventing use of \$JETTY_PID and try again." + exit 1 + fi - if ! touch "$JETTY_STATE" - then - echo "** ERROR: Unable to touch file: $JETTY_STATE" - echo " Correct issues preventing use of \$JETTY_STATE and try again." - exit 1 + if ! touch "$JETTY_STATE" + then + echo "** ERROR: Unable to touch file: $JETTY_STATE" + echo " Correct issues preventing use of \$JETTY_STATE and try again." + exit 1 + fi fi echo -n "Starting Jetty: " # Startup from a service file - if [ $UID -eq 0 ] && type start-stop-daemon > /dev/null 2>&1 + if (( USE_START_STOP_DAEMON )) then unset CH_USER if [ -n "$JETTY_USER" ] @@ -556,7 +582,7 @@ case "$ACTION" in CH_USER="--chuid $JETTY_USER" fi - echo ${RUN_ARGS[@]} --start-log-file="$JETTY_START_LOG" | xargs start-stop-daemon \ + echo ${RUN_ARGS[@]} | xargs start-stop-daemon \ --start $CH_USER \ --pidfile "$JETTY_PID" \ --chdir "$JETTY_BASE" \ diff --git a/jetty-home/src/main/resources/etc/jetty.conf b/jetty-home/src/main/resources/etc/jetty.conf index 1dfe2076ced3..66ba6b850c59 100644 --- a/jetty-home/src/main/resources/etc/jetty.conf +++ b/jetty-home/src/main/resources/etc/jetty.conf @@ -8,4 +8,4 @@ # Each line in this file becomes an argument to start.jar # in addition to those found in the start.ini file # ======================================================= ---module=pid,state +--module=state From fb97bd966cecbdadfdf59b4d9a6cf5849a1e6810 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Tue, 10 Oct 2023 11:24:09 -0500 Subject: [PATCH 02/20] Issue #10696 - Restoring pid in jetty.conf Signed-off-by: Joakim Erdfelt --- jetty-home/src/main/resources/etc/jetty.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jetty-home/src/main/resources/etc/jetty.conf b/jetty-home/src/main/resources/etc/jetty.conf index 66ba6b850c59..1dfe2076ced3 100644 --- a/jetty-home/src/main/resources/etc/jetty.conf +++ b/jetty-home/src/main/resources/etc/jetty.conf @@ -8,4 +8,4 @@ # Each line in this file becomes an argument to start.jar # in addition to those found in the start.ini file # ======================================================= ---module=state +--module=pid,state From ca0824293e17ee094ccb4f0df5023b1b7c30adba Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Tue, 10 Oct 2023 11:24:39 -0500 Subject: [PATCH 03/20] Issue #10696 - disable internal pid-file management of start-stop-daemon Signed-off-by: Joakim Erdfelt --- jetty-home/src/main/resources/bin/jetty.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/jetty-home/src/main/resources/bin/jetty.sh b/jetty-home/src/main/resources/bin/jetty.sh index 3080322977ce..9d7d78e6e9e2 100755 --- a/jetty-home/src/main/resources/bin/jetty.sh +++ b/jetty-home/src/main/resources/bin/jetty.sh @@ -525,7 +525,6 @@ then START_STOP_DAEMON_AVAILABLE=1 else USE_START_STOP_DAEMON=0 - JETTY_ARGS=(${JETTY_ARGS[*]} "--module=pid" "jetty.pid=$JETTY_PID") fi fi @@ -582,13 +581,14 @@ case "$ACTION" in CH_USER="--chuid $JETTY_USER" fi + # use of --pidfile /dev/null disables internal pidfile + # management of the start-stop-daemon (see man page) echo ${RUN_ARGS[@]} | xargs start-stop-daemon \ --start $CH_USER \ - --pidfile "$JETTY_PID" \ + --pidfile /dev/null \ --chdir "$JETTY_BASE" \ --background \ - --output "${JETTY_RUN}/start-stop.log" - --make-pidfile \ + --output "${JETTY_RUN}/start-stop.log" \ --startas "$JAVA" \ -- (( DEBUG )) && echo "Starting: start-stop-daemon" From 17c8b4d4859e0df3f65ff24a91acc0e732b7283f Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Tue, 10 Oct 2023 11:32:25 -0500 Subject: [PATCH 04/20] Issue #10696 - Do not test for file system permissions if user is root, or process will switch to JETTY_USER Signed-off-by: Joakim Erdfelt --- jetty-home/src/main/resources/bin/jetty.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jetty-home/src/main/resources/bin/jetty.sh b/jetty-home/src/main/resources/bin/jetty.sh index 9d7d78e6e9e2..5147d7b3725d 100755 --- a/jetty-home/src/main/resources/bin/jetty.sh +++ b/jetty-home/src/main/resources/bin/jetty.sh @@ -552,8 +552,8 @@ case "$ACTION" in exit fi - # If not changing users, test for file system permissions. - if [ -z "$JETTY_USER"] + # Do not test for file system permissions if user is root, or process will switch to JETTY_USER + if [ $UID -ne 0] && [ -z "$JETTY_USER"] then if ! touch "$JETTY_PID" then From 1c87984a7358fb023e37c1ad8a135f2a5e6f55e6 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Tue, 10 Oct 2023 12:53:37 -0500 Subject: [PATCH 05/20] Issue #10696 - Fixing bad UID / JETTY_USER condition Signed-off-by: Joakim Erdfelt --- jetty-home/src/main/resources/bin/jetty.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jetty-home/src/main/resources/bin/jetty.sh b/jetty-home/src/main/resources/bin/jetty.sh index 5147d7b3725d..90d724b02433 100755 --- a/jetty-home/src/main/resources/bin/jetty.sh +++ b/jetty-home/src/main/resources/bin/jetty.sh @@ -553,7 +553,7 @@ case "$ACTION" in fi # Do not test for file system permissions if user is root, or process will switch to JETTY_USER - if [ $UID -ne 0] && [ -z "$JETTY_USER"] + if [ $UID -ne 0 ] && [ -z "$JETTY_USER" ] then if ! touch "$JETTY_PID" then From 4e1316bce2be288d541e7f303fd78641c402be2e Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Tue, 10 Oct 2023 14:35:28 -0500 Subject: [PATCH 06/20] Issue #10696 - Avoid FS test with setuid use as well Signed-off-by: Joakim Erdfelt --- jetty-home/src/main/resources/bin/jetty.sh | 69 ++++++++++++++-------- 1 file changed, 46 insertions(+), 23 deletions(-) diff --git a/jetty-home/src/main/resources/bin/jetty.sh b/jetty-home/src/main/resources/bin/jetty.sh index 90d724b02433..f8324abd20d1 100755 --- a/jetty-home/src/main/resources/bin/jetty.sh +++ b/jetty-home/src/main/resources/bin/jetty.sh @@ -216,6 +216,48 @@ pidKill() fi } +testFileSystemPermissions() +{ + # Don't test file system permissions if user is root + if [ $UID -eq 0 ] ; then + (( DEBUG )) && echo "Not testing file system permissions: uid is 0" + return 0 + fi + + # Don't test if JETTY_USER is specified + # as the Jetty process will switch to a different + # user id on startup + if [ -z "$JETTY_USER"] ; then + (( DEBUG )) && echo "Not testing file system permissions: JETTY_USER=$JETTY_USER" + return 0 + fi + + # Don't test if setuid is specified + # as the Jetty process will switch to a different + # user id on startup + if expr "${JETTY_ARGS[*]}" : '.*setuid.*' >/dev/null + then + (( DEBUG )) && echo "Not testing file system permissions: setuid in use" + return 0 + fi + + # Test if PID can be written from this userid + if ! touch "$JETTY_PID" + then + echo "** ERROR: Unable to touch file: $JETTY_PID" + echo " Correct issues preventing use of \$JETTY_PID and try again." + exit 1 + fi + + # Test if STATE can be written from this userid + if ! touch "$JETTY_STATE" + then + echo "** ERROR: Unable to touch file: $JETTY_STATE" + echo " Correct issues preventing use of \$JETTY_STATE and try again." + exit 1 + fi +} + readConfig() { (( DEBUG )) && echo "Reading $1.." @@ -253,6 +295,7 @@ dumpEnv() CONFIGS=() NO_START=0 DEBUG=0 +USE_START_STOP_DAEMON=1 while [[ $1 = -* ]]; do case $1 in @@ -510,16 +553,12 @@ case "`uname`" in CYGWIN*) JETTY_START="`cygpath -w $JETTY_START`";; esac -# Determine if we are using start-stop-daemon or not -if [ -z "$USE_START_STOP_DAEMON" ] -then - USE_START_STOP_DAEMON=1 -fi - +# Determine if we can use start-stop-daemon or not START_STOP_DAEMON_AVAILABLE=0 if (( USE_START_STOP_DAEMON )) then + # only if root user is executing jetty.sh, and the start-stop-daemon exists if [ $UID -eq 0 ] && type start-stop-daemon > /dev/null 2>&1 then START_STOP_DAEMON_AVAILABLE=1 @@ -552,23 +591,7 @@ case "$ACTION" in exit fi - # Do not test for file system permissions if user is root, or process will switch to JETTY_USER - if [ $UID -ne 0 ] && [ -z "$JETTY_USER" ] - then - if ! touch "$JETTY_PID" - then - echo "** ERROR: Unable to touch file: $JETTY_PID" - echo " Correct issues preventing use of \$JETTY_PID and try again." - exit 1 - fi - - if ! touch "$JETTY_STATE" - then - echo "** ERROR: Unable to touch file: $JETTY_STATE" - echo " Correct issues preventing use of \$JETTY_STATE and try again." - exit 1 - fi - fi + testFileSystemPermissions echo -n "Starting Jetty: " From 60e3d0b8e6899e3378fb28a8fee2168559d53217 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Tue, 10 Oct 2023 14:41:13 -0500 Subject: [PATCH 07/20] Issue #10696 - Correcting JETTY_USER test Signed-off-by: Joakim Erdfelt --- jetty-home/src/main/resources/bin/jetty.sh | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/jetty-home/src/main/resources/bin/jetty.sh b/jetty-home/src/main/resources/bin/jetty.sh index f8324abd20d1..9fd13d02816e 100755 --- a/jetty-home/src/main/resources/bin/jetty.sh +++ b/jetty-home/src/main/resources/bin/jetty.sh @@ -225,16 +225,14 @@ testFileSystemPermissions() fi # Don't test if JETTY_USER is specified - # as the Jetty process will switch to a different - # user id on startup - if [ -z "$JETTY_USER"] ; then + # as the Jetty process will switch to a different user id on startup + if [ -n "$JETTY_USER"] ; then (( DEBUG )) && echo "Not testing file system permissions: JETTY_USER=$JETTY_USER" return 0 fi # Don't test if setuid is specified - # as the Jetty process will switch to a different - # user id on startup + # as the Jetty process will switch to a different user id on startup if expr "${JETTY_ARGS[*]}" : '.*setuid.*' >/dev/null then (( DEBUG )) && echo "Not testing file system permissions: setuid in use" From 7a203cd969388f19ad9675482670f05650943d96 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Fri, 13 Oct 2023 14:43:30 -0500 Subject: [PATCH 08/20] Issue #10696 - Fixing stop behavior Signed-off-by: Joakim Erdfelt --- jetty-home/src/main/resources/bin/jetty.sh | 34 ++++++++++++++++------ 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/jetty-home/src/main/resources/bin/jetty.sh b/jetty-home/src/main/resources/bin/jetty.sh index 9fd13d02816e..6a32827c5adb 100755 --- a/jetty-home/src/main/resources/bin/jetty.sh +++ b/jetty-home/src/main/resources/bin/jetty.sh @@ -665,25 +665,41 @@ case "$ACTION" in stop) echo -n "Stopping Jetty: " - # Stop from a service file + if [ ! -r "$JETTY_PID" ] ; then + echo "** ERROR: no pid found at $JETTY_PID" + exit 1 + fi + + PID=$(tail -1 "$JETTY_PID") + if [ -z "$PID" ] ; then + echo "** ERROR: no pid found in $JETTY_PID" + exit 1 + fi + + # Stopping service started with start-stop-daemon if [ $UID -eq 0 ] && type start-stop-daemon > /dev/null 2>&1; then - start-stop-daemon -K -p"$JETTY_PID" -d"$JETTY_HOME" -a "$JAVA" -s HUP + (( DEBUG )) && echo "Issuing HUP to $PID" + start-stop-daemon --stop \ + --pid "$PID" \ + --chdir "$JETTY_BASE" \ + --startas "$JAVA" \ + --signal HUP TIMEOUT=30 while running "$JETTY_PID"; do + (( DEBUG )) && echo "Issuing KILL to $PID" if (( TIMEOUT-- == 0 )); then - start-stop-daemon -K -p"$JETTY_PID" -d"$JETTY_HOME" -a "$JAVA" -s KILL + start-stop-daemon --stop \ + --pid "$PID" \ + --chdir "$JETTY_BASE" \ + --startas "$JAVA" \ + --signal KILL fi sleep 1 done else - # Stop from a non-service path - if [ ! -r "$JETTY_PID" ] ; then - echo "** ERROR: no pid found at $JETTY_PID" - exit 1 - fi - + # Stopping from non-service start pidKill "$JETTY_PID" 30 fi From 2dc878c5e8b164f69d0df08ffc60208f6d68bdc7 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Tue, 17 Oct 2023 16:53:05 -0500 Subject: [PATCH 09/20] WIP for jetty.sh docker testing Signed-off-by: Joakim Erdfelt --- .../jettysh/JettyShStartTest.java | 103 ++++++++++++++++++ .../test/resources/jetty-logging.properties | 2 + 2 files changed, 105 insertions(+) create mode 100644 tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/JettyShStartTest.java diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/JettyShStartTest.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/JettyShStartTest.java new file mode 100644 index 000000000000..a8b373dfd1bf --- /dev/null +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/JettyShStartTest.java @@ -0,0 +1,103 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.tests.distribution.jettysh; + +import java.io.IOException; +import java.nio.file.Path; + +import org.eclipse.jetty.tests.hometester.JettyHomeTester; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.testcontainers.containers.Container; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.startupcheck.IsRunningStartupCheckStrategy; +import org.testcontainers.images.builder.ImageFromDockerfile; + +/** + * Test of jetty-home/bin/jetty.sh as generic start mechanism. + */ +public class JettyShStartTest +{ + private static Path jettyHomePath; + + @BeforeAll + public static void initJettyHome() throws Exception + { + String jettyVersion = System.getProperty("jettyVersion"); + JettyHomeTester homeTester = JettyHomeTester.Builder.newInstance() + .jettyVersion(jettyVersion) + .mavenLocalRepository(System.getProperty("mavenRepoPath")) + .build(); + jettyHomePath = homeTester.getJettyHome(); + } + + @Test + public void testStartStop() throws IOException, InterruptedException + { + ImageFromDockerfile ubuntuImg = new ImageFromDockerfile("jetty-sh:ubuntu-22.04", false) + .withDockerfileFromBuilder(builder -> + builder + .from("ubuntu:22.04") + .run("apt update ; " + + "apt -y upgrade ; " + + "apt install -y openjdk-17-jdk-headless ; " + + "apt install -y curl vim") + .env("TEST_DIR", "/var/test") + .env("JETTY_HOME", "$TEST_DIR/jetty-home") + .env("JETTY_BASE", "$TEST_DIR/jetty-base") + .user("root") + .copy("/opt/jetty/", "${JETTY_HOME}/") + .run( + "chmod ugo+x ${JETTY_HOME}/bin/jetty.sh ; " + + "mkdir -p ${JETTY_BASE} ; " + + "useradd --home-dir=${JETTY_BASE} --shell=/bin/bash jetty ; " + + "chown jetty:jetty ${JETTY_BASE} ; " + + "chmod a+w ${JETTY_BASE}") + .user("jetty") + .workDir("${JETTY_BASE}") + .run("java -jar ${JETTY_HOME}/start.jar --add-modules=http,deploy") + // .cmd("${JETTY_HOME}/bin/jetty.sh start") + .cmd("bash") + .build()) + .withFileFromFile("/opt/jetty/", jettyHomePath.toFile()); + + try (GenericContainer genericContainer = new GenericContainer<>(ubuntuImg)) + { + genericContainer.withExposedPorts(8080) + // .waitingFor(new LogMessageWaitStrategy().withRegEx(".*Starting Jetty:.* started.*")) + // .waitingFor(new LogMessageWaitStrategy().withRegEx(":~$")) + .withStartupCheckStrategy(new IsRunningStartupCheckStrategy()) + // .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) + .start(); + + String listing = genericContainer.getLogs(); + System.err.println("### Logs"); + System.err.println(listing); + // assertThat(listing, containsString("oejs.Server:main: jetty-10.")); + + // System.err.println("== start.jar --list-config =="); + // execCommand(genericContainer, "java", "-jar", "/var/test/jetty-home/start.jar", "--list-config"); + System.err.println("== jetty.sh status =="); + execCommand(genericContainer, "bin/jetty.sh", "status"); + } + } + + private void execCommand(GenericContainer genericContainer, String... command) throws IOException, InterruptedException + { + Container.ExecResult result = genericContainer.execInContainer(command); + System.err.println("Exit code: " + result.getExitCode()); + System.err.println("StdOut: " + result.getStdout()); + System.err.println("StdErr: " + result.getStderr()); + } +} diff --git a/tests/test-distribution/src/test/resources/jetty-logging.properties b/tests/test-distribution/src/test/resources/jetty-logging.properties index ef1afe439643..b1e62960887a 100644 --- a/tests/test-distribution/src/test/resources/jetty-logging.properties +++ b/tests/test-distribution/src/test/resources/jetty-logging.properties @@ -1,4 +1,6 @@ # Jetty Logging using jetty-slf4j-impl org.eclipse.jetty.logging.appender.MESSAGE_ESCAPE=false +org.eclipse.jetty.logging.appender.NAME_CONDENSE=false +org.testcontainers.LEVEL=DEBUG #org.eclipse.jetty.LEVEL=DEBUG #org.eclipse.jetty.tests.distribution.LEVEL=DEBUG From a2c7348e57317da6dbdaea4e299df47ec64e620f Mon Sep 17 00:00:00 2001 From: Olivier Lamy Date: Wed, 18 Oct 2023 15:57:20 +1000 Subject: [PATCH 10/20] make this working :) Signed-off-by: Olivier Lamy --- .../jettysh/JettyShStartTest.java | 34 ++++++++++++++++--- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/JettyShStartTest.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/JettyShStartTest.java index a8b373dfd1bf..6a23b1c2ed9e 100644 --- a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/JettyShStartTest.java +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/JettyShStartTest.java @@ -16,14 +16,19 @@ import java.io.IOException; import java.nio.file.Path; +import com.github.dockerjava.api.DockerClient; import org.eclipse.jetty.tests.hometester.JettyHomeTester; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.testcontainers.containers.Container; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.startupcheck.IsRunningStartupCheckStrategy; +import org.testcontainers.containers.startupcheck.StartupCheckStrategy; import org.testcontainers.images.builder.ImageFromDockerfile; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; + /** * Test of jetty-home/bin/jetty.sh as generic start mechanism. */ @@ -52,12 +57,13 @@ public void testStartStop() throws IOException, InterruptedException .run("apt update ; " + "apt -y upgrade ; " + "apt install -y openjdk-17-jdk-headless ; " + - "apt install -y curl vim") + "apt install -y curl vim netcat ") .env("TEST_DIR", "/var/test") .env("JETTY_HOME", "$TEST_DIR/jetty-home") .env("JETTY_BASE", "$TEST_DIR/jetty-base") .user("root") .copy("/opt/jetty/", "${JETTY_HOME}/") + //.withStatement(new MultiArgsStatement("COPY", "--from=eclipse-temurin:17.0.4.1_1-jdk", "/opt/java/openjdk", "/opt/java/jdk17")) .run( "chmod ugo+x ${JETTY_HOME}/bin/jetty.sh ; " + "mkdir -p ${JETTY_BASE} ; " + @@ -66,18 +72,25 @@ public void testStartStop() throws IOException, InterruptedException "chmod a+w ${JETTY_BASE}") .user("jetty") .workDir("${JETTY_BASE}") + //.env("JAVA_HOME", "/opt/java/jdk17") .run("java -jar ${JETTY_HOME}/start.jar --add-modules=http,deploy") // .cmd("${JETTY_HOME}/bin/jetty.sh start") - .cmd("bash") + //.cmd("bash") + .env("PATH", "$PATH:/var/test/jetty-home/bin/") + .entryPoint("/var/test/jetty-home/bin/jetty.sh", "start") .build()) .withFileFromFile("/opt/jetty/", jettyHomePath.toFile()); try (GenericContainer genericContainer = new GenericContainer<>(ubuntuImg)) { genericContainer.withExposedPorts(8080) + //.withCommand("/var/test/jetty-home/bin/jetty.sh", "status") + //.withCommand("java", "-jar", "/var/test/jetty-home/start.jar", "--list-config") // .waitingFor(new LogMessageWaitStrategy().withRegEx(".*Starting Jetty:.* started.*")) // .waitingFor(new LogMessageWaitStrategy().withRegEx(":~$")) .withStartupCheckStrategy(new IsRunningStartupCheckStrategy()) + //.withStartupCheckStrategy(new OneShotStartupCheckStrategy()) + //.withStartupCheckStrategy(new TrueStartupCheckStrategy()) // .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) .start(); @@ -87,17 +100,28 @@ public void testStartStop() throws IOException, InterruptedException // assertThat(listing, containsString("oejs.Server:main: jetty-10.")); // System.err.println("== start.jar --list-config =="); - // execCommand(genericContainer, "java", "-jar", "/var/test/jetty-home/start.jar", "--list-config"); + //execCommand(genericContainer, "java", "-jar", "/var/test/jetty-home/start.jar", "--list-config"); System.err.println("== jetty.sh status =="); - execCommand(genericContainer, "bin/jetty.sh", "status"); + String stdOut = execCommand(genericContainer, "jetty.sh", "status"); + assertThat(stdOut, containsString("Jetty running pid")); + } + } + + private static class TrueStartupCheckStrategy extends StartupCheckStrategy + { + @Override + public StartupStatus checkStartupState(DockerClient dockerClient, String containerId) + { + return StartupStatus.SUCCESSFUL; } } - private void execCommand(GenericContainer genericContainer, String... command) throws IOException, InterruptedException + private String execCommand(GenericContainer genericContainer, String... command) throws IOException, InterruptedException { Container.ExecResult result = genericContainer.execInContainer(command); System.err.println("Exit code: " + result.getExitCode()); System.err.println("StdOut: " + result.getStdout()); System.err.println("StdErr: " + result.getStderr()); + return result.getStdout(); } } From 0c0a3c200f00eb1d8dbfd7f4b9b9317eb5479d2d Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Wed, 18 Oct 2023 15:56:03 -0500 Subject: [PATCH 11/20] WIP for jetty.sh docker testing Signed-off-by: Joakim Erdfelt --- jetty-home/src/main/resources/bin/jetty.sh | 8 +- .../distribution/jettysh/ImageFromDSL.java | 34 +++ .../jettysh/JettyShStartTest.java | 260 +++++++++++++++--- .../bases/spaces-with-conf/dir one/test1.xml | 5 + .../bases/spaces-with-conf/dir two/test2.xml | 5 + .../bases/spaces-with-conf/etc/jetty.conf | 3 + .../bases/spaces-with-conf/modules/test.mod | 5 + .../bases/spaces-with-conf/start.d/test.ini | 1 + 8 files changed, 275 insertions(+), 46 deletions(-) create mode 100644 tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageFromDSL.java create mode 100644 tests/test-distribution/src/test/resources/bases/spaces-with-conf/dir one/test1.xml create mode 100644 tests/test-distribution/src/test/resources/bases/spaces-with-conf/dir two/test2.xml create mode 100644 tests/test-distribution/src/test/resources/bases/spaces-with-conf/etc/jetty.conf create mode 100644 tests/test-distribution/src/test/resources/bases/spaces-with-conf/modules/test.mod create mode 100644 tests/test-distribution/src/test/resources/bases/spaces-with-conf/start.d/test.ini diff --git a/jetty-home/src/main/resources/bin/jetty.sh b/jetty-home/src/main/resources/bin/jetty.sh index 6a32827c5adb..bc584f57c0be 100755 --- a/jetty-home/src/main/resources/bin/jetty.sh +++ b/jetty-home/src/main/resources/bin/jetty.sh @@ -456,6 +456,7 @@ JETTY_ARGS=(${JETTY_ARGS[*]} "jetty.state=$JETTY_STATE" "jetty.pid=$JETTY_PID") ################################################## if [ -f "$JETTY_CONF" ] && [ -r "$JETTY_CONF" ] then + (( DEBUG )) && echo "$JETTY_CONF: (begin read) JETTY_ARGS.length=${#JETTY_ARGS[@]}" while read -r CONF do if expr "$CONF" : '#' >/dev/null ; then @@ -471,16 +472,17 @@ then do if [ -r "$XMLFILE" ] && [ -f "$XMLFILE" ] then - JETTY_ARGS=(${JETTY_ARGS[*]} "$XMLFILE") + JETTY_ARGS[${#JETTY_ARGS[@]}]="$XMLFILE" else echo "** WARNING: Cannot read '$XMLFILE' specified in '$JETTY_CONF'" fi done else # assume it's a command line parameter (let start.jar deal with its validity) - JETTY_ARGS=(${JETTY_ARGS[*]} "$CONF") + JETTY_ARGS[${#JETTY_ARGS[@]}]="$CONF" fi done < "$JETTY_CONF" + (( DEBUG )) && echo "$JETTY_CONF: (finished read) JETTY_ARGS.length=${#JETTY_ARGS[@]}" fi ################################################## @@ -566,7 +568,7 @@ then fi # Collect the dry-run (of opts,path,main,args) from the jetty.base configuration -JETTY_DRY_RUN=$("$JAVA" -jar "$JETTY_START" --dry-run=opts,path,main,args ${JETTY_ARGS[*]} ${JAVA_OPTIONS[*]}) +JETTY_DRY_RUN=$("$JAVA" -jar "$JETTY_START" --dry-run=opts,path,main,args ${JETTY_ARGS} ${JAVA_OPTIONS[*]}) RUN_ARGS=($JETTY_SYS_PROPS ${JETTY_DRY_RUN[@]}) if (( DEBUG )) diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageFromDSL.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageFromDSL.java new file mode 100644 index 000000000000..f3c4dc6cabc6 --- /dev/null +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageFromDSL.java @@ -0,0 +1,34 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.tests.distribution.jettysh; + +import org.testcontainers.images.builder.ImageFromDockerfile; + +/** + * Simple ImageFromDSL that has a toString that reports the docker image name. + * (so that test case execution makes sense) + */ +public class ImageFromDSL extends ImageFromDockerfile +{ + public ImageFromDSL(String name) + { + super(name, false); + } + + @Override + public String toString() + { + return getDockerImageName(); + } +} diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/JettyShStartTest.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/JettyShStartTest.java index 6a23b1c2ed9e..2a3d205109bd 100644 --- a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/JettyShStartTest.java +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/JettyShStartTest.java @@ -13,27 +13,50 @@ package org.eclipse.jetty.tests.distribution.jettysh; -import java.io.IOException; +import java.net.URI; import java.nio.file.Path; +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; import com.github.dockerjava.api.DockerClient; +import org.awaitility.Awaitility; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.tests.distribution.AbstractJettyHomeTest; import org.eclipse.jetty.tests.hometester.JettyHomeTester; +import org.eclipse.jetty.toolchain.test.MavenPaths; import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.testcontainers.containers.Container; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.startupcheck.IsRunningStartupCheckStrategy; import org.testcontainers.containers.startupcheck.StartupCheckStrategy; +import org.testcontainers.containers.wait.strategy.ShellStrategy; import org.testcontainers.images.builder.ImageFromDockerfile; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * Test of jetty-home/bin/jetty.sh as generic start mechanism. */ -public class JettyShStartTest +public class JettyShStartTest extends AbstractJettyHomeTest { + private static final String JETTY_REGISTRY = "registry.jetty.org/"; + + private static final String JETTY_MODE = "jetty-mode"; + private static final String MODE_USER_CHANGE = "user-change"; + private static final String MODE_ROOT_ONLY = "root-only"; + private static final String JETTY_BASE_MODE = "jetty-base-mode"; + private static final boolean DEBUG_JETTY_SH = false; + private static Path jettyHomePath; @BeforeAll @@ -47,67 +70,226 @@ public static void initJettyHome() throws Exception jettyHomePath = homeTester.getJettyHome(); } - @Test - public void testStartStop() throws IOException, InterruptedException + private static ImageFromDockerfile getUbuntuOSImage() { - ImageFromDockerfile ubuntuImg = new ImageFromDockerfile("jetty-sh:ubuntu-22.04", false) + return new ImageFromDSL(JETTY_REGISTRY + "jetty-sh:ubuntu-22.04") .withDockerfileFromBuilder(builder -> builder .from("ubuntu:22.04") .run("apt update ; " + "apt -y upgrade ; " + "apt install -y openjdk-17-jdk-headless ; " + - "apt install -y curl vim netcat ") + "apt install -y curl vim net-tools ") .env("TEST_DIR", "/var/test") .env("JETTY_HOME", "$TEST_DIR/jetty-home") .env("JETTY_BASE", "$TEST_DIR/jetty-base") + .env("PATH", "$PATH:${JETTY_HOME}/bin/") .user("root") + // Configure /etc/default/jetty + .run("echo \"JETTY_HOME=${JETTY_HOME}\" > /etc/default/jetty ; " + + "echo \"JETTY_BASE=${JETTY_BASE}\" >> /etc/default/jetty" + + (DEBUG_JETTY_SH ? " ; echo \"DEBUG=0\" >> /etc/default/jetty" : "")) + // setup Jetty Home .copy("/opt/jetty/", "${JETTY_HOME}/") - //.withStatement(new MultiArgsStatement("COPY", "--from=eclipse-temurin:17.0.4.1_1-jdk", "/opt/java/openjdk", "/opt/java/jdk17")) - .run( - "chmod ugo+x ${JETTY_HOME}/bin/jetty.sh ; " + + .env("PATH", "$PATH:${JETTY_HOME}/bin/") + .run("chmod ugo+x ${JETTY_HOME}/bin/jetty.sh") + .build()) + .withFileFromFile("/opt/jetty/", jettyHomePath.toFile()); + } + + /** + * A docker image with no JETTY_USER set, everything executes as `root`. + */ + private static ImageFromDockerfile buildWithRootNoUserChange(ImageFromDockerfile osImage) + { + String name = osImage.get(); + System.out.println("buildWithRootNoUserChange: name=" + name); + + return new ImageFromDSL(osImage.getDockerImageName() + "-no-user-change") + .withDockerfileFromBuilder(builder -> + builder + .from(osImage.getDockerImageName()) + .label(JETTY_MODE, MODE_ROOT_ONLY) + .run("mkdir -p ${JETTY_BASE} ; " + + "chmod u+x ${JETTY_HOME}/bin/jetty.sh ; " + + "chmod a+w ${JETTY_BASE}") + // Configure Jetty Base + .workDir("${JETTY_BASE}") + .build()); + } + + /** + * A docker image with JETTY_USER set to id `jetty`. + * JETTY_HOME is owned by `root`. + * JETTY_BASE is owned by `jetty` + */ + private static ImageFromDockerfile buildWithUserChangeToJettyId(ImageFromDockerfile osImage) + { + String name = osImage.get(); + System.out.println("buildWithUserChangeToJettyUser: name=" + name); + + return new ImageFromDSL(osImage.getDockerImageName() + "-user-change") + .withDockerfileFromBuilder(builder -> + builder + .from(osImage.getDockerImageName()) + .label(JETTY_MODE, MODE_USER_CHANGE) + // setup "jetty" user and Jetty Base directory + .run("chmod ugo+x ${JETTY_HOME}/bin/jetty.sh ; " + "mkdir -p ${JETTY_BASE} ; " + "useradd --home-dir=${JETTY_BASE} --shell=/bin/bash jetty ; " + "chown jetty:jetty ${JETTY_BASE} ; " + - "chmod a+w ${JETTY_BASE}") + "chmod a+w ${JETTY_BASE} ; " + + "echo \"JETTY_USER=jetty\" > /etc/default/jetty") // user change .user("jetty") + // Configure Jetty Base .workDir("${JETTY_BASE}") - //.env("JAVA_HOME", "/opt/java/jdk17") + .build()); + } + + public static Stream jettyImages() + { + List images = new ArrayList<>(); + + List osImages = List.of( + getUbuntuOSImage() + ); + + for (ImageFromDockerfile osImage : osImages) + { + images.add(Arguments.of(buildWithRootNoUserChange(osImage))); + images.add(Arguments.of(buildWithUserChangeToJettyId(osImage))); + } + + return images.stream(); + } + + @ParameterizedTest + @MethodSource("jettyImages") + public void testStartStopBasicJettyBase(ImageFromDockerfile jettyImage) throws Exception + { + String name = jettyImage.get(); + System.out.println("testStartStopBasicJettyBase: name=" + name); + + ImageFromDockerfile image = new ImageFromDSL(jettyImage.getDockerImageName() + "-basic-base") + .withDockerfileFromBuilder(builder -> + builder + .from(jettyImage.getDockerImageName()) + .label(JETTY_BASE_MODE, "basic") + // Create a basic configuration of jetty-base .run("java -jar ${JETTY_HOME}/start.jar --add-modules=http,deploy") - // .cmd("${JETTY_HOME}/bin/jetty.sh start") - //.cmd("bash") - .env("PATH", "$PATH:/var/test/jetty-home/bin/") - .entryPoint("/var/test/jetty-home/bin/jetty.sh", "start") + .build()); + + try (GenericContainer genericContainer = new GenericContainer<>(image)) + { + genericContainer.setWaitStrategy(new ShellStrategy().withCommand("id")); + + genericContainer.withExposedPorts(8080) // jetty + .withStartupAttempts(2) + .withStartupCheckStrategy(new IsRunningStartupCheckStrategy()) + .start(); + + System.out.println("Started: " + image.getDockerImageName()); + + System.err.println("== jetty.sh start =="); + Container.ExecResult result = genericContainer.execInContainer("/var/test/jetty-home/bin/jetty.sh", "start"); + assertThat(result.getExitCode(), is(0)); + /* + * Example successful output + * ---- + * STDOUT: + * Starting Jetty: . started + * OK Wed Oct 18 19:29:35 UTC 2023 + * ---- + */ + Awaitility.await().atMost(Duration.ofSeconds(5)).until(result::getStdout, + allOf( + containsString("Starting Jetty:"), + containsString("\nOK ") + )); + + startHttpClient(); + + URI containerUriRoot = URI.create("http://" + genericContainer.getHost() + ":" + genericContainer.getMappedPort(8080) + "/"); + System.err.printf("Container URI Root: %s%n", containerUriRoot); + + ContentResponse response = client.GET(containerUriRoot); + assertEquals(HttpStatus.NOT_FOUND_404, response.getStatus(), new ResponseDetails(response)); + assertThat(response.getContentAsString(), containsString("Powered by Eclipse Jetty:// Server")); + + System.err.println("== jetty.sh status =="); + result = genericContainer.execInContainer("/var/test/jetty-home/bin/jetty.sh", "status"); + assertThat(result.getExitCode(), is(0)); + Awaitility.await().atMost(Duration.ofSeconds(5)).until(result::getStdout, + containsString("Jetty running pid")); + } + } + + @ParameterizedTest + @MethodSource("jettyImages") + public void testStartStopComplexJettyBase(ImageFromDockerfile jettyImage) throws Exception + { + String name = jettyImage.get(); + System.out.println("testStartStopComplexJettyBase: name=" + name); + + Path basesWithSpaces = MavenPaths.findTestResourceDir("bases/spaces-with-conf"); + + ImageFromDockerfile image = new ImageFromDSL(jettyImage.getDockerImageName() + "-complex-base") + .withDockerfileFromBuilder(builder -> + builder + .from(jettyImage.getDockerImageName()) + .label(JETTY_BASE_MODE, "basic") + // Create a basic configuration of jetty-base + .run("java -jar ${JETTY_HOME}/start.jar --add-modules=http,deploy") + .copy("/tests/bases-with-spaces/", "${JETTY_BASE}/") .build()) - .withFileFromFile("/opt/jetty/", jettyHomePath.toFile()); + .withFileFromFile("/tests/bases-with-spaces/", basesWithSpaces.toFile()); - try (GenericContainer genericContainer = new GenericContainer<>(ubuntuImg)) + try (GenericContainer genericContainer = new GenericContainer<>(image)) { - genericContainer.withExposedPorts(8080) - //.withCommand("/var/test/jetty-home/bin/jetty.sh", "status") - //.withCommand("java", "-jar", "/var/test/jetty-home/start.jar", "--list-config") - // .waitingFor(new LogMessageWaitStrategy().withRegEx(".*Starting Jetty:.* started.*")) - // .waitingFor(new LogMessageWaitStrategy().withRegEx(":~$")) + genericContainer.setWaitStrategy(new ShellStrategy().withCommand("id")); + + genericContainer.withExposedPorts(8080) // jetty + .withStartupAttempts(2) .withStartupCheckStrategy(new IsRunningStartupCheckStrategy()) - //.withStartupCheckStrategy(new OneShotStartupCheckStrategy()) - //.withStartupCheckStrategy(new TrueStartupCheckStrategy()) - // .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) .start(); - String listing = genericContainer.getLogs(); - System.err.println("### Logs"); - System.err.println(listing); - // assertThat(listing, containsString("oejs.Server:main: jetty-10.")); + System.out.println("Started: " + image.getDockerImageName()); + + System.err.println("== jetty.sh start =="); + Container.ExecResult result = genericContainer.execInContainer("/var/test/jetty-home/bin/jetty.sh", "start"); +// assertThat(result.getExitCode(), is(0)); + /* + * Example successful output + * ---- + * STDOUT: + * Starting Jetty: . started + * OK Wed Oct 18 19:29:35 UTC 2023 + * ---- + */ + Awaitility.await().atMost(Duration.ofSeconds(5)).until(result::getStdout, + allOf( + containsString("Starting Jetty:"), + containsString("\nOK ") + )); + + startHttpClient(); + + URI containerUriRoot = URI.create("http://" + genericContainer.getHost() + ":" + genericContainer.getMappedPort(8080) + "/"); + System.err.printf("Container URI Root: %s%n", containerUriRoot); + + ContentResponse response = client.GET(containerUriRoot); + assertEquals(HttpStatus.NOT_FOUND_404, response.getStatus(), new ResponseDetails(response)); + assertThat(response.getContentAsString(), containsString("Powered by Eclipse Jetty:// Server")); - // System.err.println("== start.jar --list-config =="); - //execCommand(genericContainer, "java", "-jar", "/var/test/jetty-home/start.jar", "--list-config"); System.err.println("== jetty.sh status =="); - String stdOut = execCommand(genericContainer, "jetty.sh", "status"); - assertThat(stdOut, containsString("Jetty running pid")); + result = genericContainer.execInContainer("/var/test/jetty-home/bin/jetty.sh", "status"); + assertThat(result.getExitCode(), is(0)); + Awaitility.await().atMost(Duration.ofSeconds(5)).until(result::getStdout, + containsString("Jetty running pid")); } } - private static class TrueStartupCheckStrategy extends StartupCheckStrategy + static class NoopStartupCheckStrategy extends StartupCheckStrategy { @Override public StartupStatus checkStartupState(DockerClient dockerClient, String containerId) @@ -116,12 +298,4 @@ public StartupStatus checkStartupState(DockerClient dockerClient, String contain } } - private String execCommand(GenericContainer genericContainer, String... command) throws IOException, InterruptedException - { - Container.ExecResult result = genericContainer.execInContainer(command); - System.err.println("Exit code: " + result.getExitCode()); - System.err.println("StdOut: " + result.getStdout()); - System.err.println("StdErr: " + result.getStderr()); - return result.getStdout(); - } } diff --git a/tests/test-distribution/src/test/resources/bases/spaces-with-conf/dir one/test1.xml b/tests/test-distribution/src/test/resources/bases/spaces-with-conf/dir one/test1.xml new file mode 100644 index 000000000000..b16b24aae946 --- /dev/null +++ b/tests/test-distribution/src/test/resources/bases/spaces-with-conf/dir one/test1.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/tests/test-distribution/src/test/resources/bases/spaces-with-conf/dir two/test2.xml b/tests/test-distribution/src/test/resources/bases/spaces-with-conf/dir two/test2.xml new file mode 100644 index 000000000000..b16b24aae946 --- /dev/null +++ b/tests/test-distribution/src/test/resources/bases/spaces-with-conf/dir two/test2.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/tests/test-distribution/src/test/resources/bases/spaces-with-conf/etc/jetty.conf b/tests/test-distribution/src/test/resources/bases/spaces-with-conf/etc/jetty.conf new file mode 100644 index 000000000000..8d07a347c738 --- /dev/null +++ b/tests/test-distribution/src/test/resources/bases/spaces-with-conf/etc/jetty.conf @@ -0,0 +1,3 @@ +# test configurations with spaces +dir one/test1.xml +dir two/test2.xml \ No newline at end of file diff --git a/tests/test-distribution/src/test/resources/bases/spaces-with-conf/modules/test.mod b/tests/test-distribution/src/test/resources/bases/spaces-with-conf/modules/test.mod new file mode 100644 index 000000000000..d7f80f0065fa --- /dev/null +++ b/tests/test-distribution/src/test/resources/bases/spaces-with-conf/modules/test.mod @@ -0,0 +1,5 @@ +[exec] +-showversion +-XX:+PrintCommandLineFlags +-Xlog:gc*:file=/tmp/logs/gc.log:time,level,tags +-XX:ErrorFile=/tmp/logs/jvm_crash_pid_%p.log \ No newline at end of file diff --git a/tests/test-distribution/src/test/resources/bases/spaces-with-conf/start.d/test.ini b/tests/test-distribution/src/test/resources/bases/spaces-with-conf/start.d/test.ini new file mode 100644 index 000000000000..066141861728 --- /dev/null +++ b/tests/test-distribution/src/test/resources/bases/spaces-with-conf/start.d/test.ini @@ -0,0 +1 @@ +--module=test \ No newline at end of file From 0f704b947adf976b10c284cedf450620945df423 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Thu, 19 Oct 2023 11:40:52 -0500 Subject: [PATCH 12/20] WIP for jetty.sh docker testing Signed-off-by: Joakim Erdfelt --- jetty-home/src/main/resources/bin/jetty.sh | 14 +++++++++++--- .../distribution/jettysh/JettyShStartTest.java | 10 ++++++++-- .../bases/spaces-with-conf/etc/jetty.conf | 5 +++-- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/jetty-home/src/main/resources/bin/jetty.sh b/jetty-home/src/main/resources/bin/jetty.sh index bc584f57c0be..60c81758121e 100755 --- a/jetty-home/src/main/resources/bin/jetty.sh +++ b/jetty-home/src/main/resources/bin/jetty.sh @@ -472,14 +472,14 @@ then do if [ -r "$XMLFILE" ] && [ -f "$XMLFILE" ] then - JETTY_ARGS[${#JETTY_ARGS[@]}]="$XMLFILE" + JETTY_ARGS[${#JETTY_ARGS[@]}]=$XMLFILE else echo "** WARNING: Cannot read '$XMLFILE' specified in '$JETTY_CONF'" fi done else # assume it's a command line parameter (let start.jar deal with its validity) - JETTY_ARGS[${#JETTY_ARGS[@]}]="$CONF" + JETTY_ARGS[${#JETTY_ARGS[@]}]=$CONF fi done < "$JETTY_CONF" (( DEBUG )) && echo "$JETTY_CONF: (finished read) JETTY_ARGS.length=${#JETTY_ARGS[@]}" @@ -568,7 +568,15 @@ then fi # Collect the dry-run (of opts,path,main,args) from the jetty.base configuration -JETTY_DRY_RUN=$("$JAVA" -jar "$JETTY_START" --dry-run=opts,path,main,args ${JETTY_ARGS} ${JAVA_OPTIONS[*]}) +if (( DEBUG )) +then + echo "WITH-AT:" "$JAVA" -jar "$JETTY_START" --dry-run=opts,path,main,args ${JETTY_ARGS[@]} ${JAVA_OPTIONS[*]} + echo "-----" + echo "WITH-GLOB:" "$JAVA" -jar "$JETTY_START" --dry-run=opts,path,main,args ${JETTY_ARGS[*]} ${JAVA_OPTIONS[*]} + echo "-----" + echo "WITHOUT-GLOB:" "$JAVA" -jar "$JETTY_START" --dry-run=opts,path,main,args ${JETTY_ARGS} ${JAVA_OPTIONS[*]} +fi +JETTY_DRY_RUN=$("$JAVA" -jar "$JETTY_START" --dry-run=opts,path,main,args ${JETTY_ARGS[*]} ${JAVA_OPTIONS[*]}) RUN_ARGS=($JETTY_SYS_PROPS ${JETTY_DRY_RUN[@]}) if (( DEBUG )) diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/JettyShStartTest.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/JettyShStartTest.java index 2a3d205109bd..bf1dd9d5a0f4 100644 --- a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/JettyShStartTest.java +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/JettyShStartTest.java @@ -35,6 +35,7 @@ import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.startupcheck.IsRunningStartupCheckStrategy; import org.testcontainers.containers.startupcheck.StartupCheckStrategy; +import org.testcontainers.containers.wait.strategy.DockerHealthcheckWaitStrategy; import org.testcontainers.containers.wait.strategy.ShellStrategy; import org.testcontainers.images.builder.ImageFromDockerfile; @@ -87,7 +88,8 @@ private static ImageFromDockerfile getUbuntuOSImage() .user("root") // Configure /etc/default/jetty .run("echo \"JETTY_HOME=${JETTY_HOME}\" > /etc/default/jetty ; " + - "echo \"JETTY_BASE=${JETTY_BASE}\" >> /etc/default/jetty" + + "echo \"JETTY_BASE=${JETTY_BASE}\" >> /etc/default/jetty ; " + + "echo \"JETTY_RUN=${JETTY_BASE}\" >> /etc/default/jetty ; " + (DEBUG_JETTY_SH ? " ; echo \"DEBUG=0\" >> /etc/default/jetty" : "")) // setup Jetty Home .copy("/opt/jetty/", "${JETTY_HOME}/") @@ -177,13 +179,16 @@ public void testStartStopBasicJettyBase(ImageFromDockerfile jettyImage) throws E .label(JETTY_BASE_MODE, "basic") // Create a basic configuration of jetty-base .run("java -jar ${JETTY_HOME}/start.jar --add-modules=http,deploy") + .entryPoint("/bin/sh", "-c", "while true; do pwd | nc -l -p 80; done") .build()); try (GenericContainer genericContainer = new GenericContainer<>(image)) { genericContainer.setWaitStrategy(new ShellStrategy().withCommand("id")); + // genericContainer.setWaitStrategy(new DockerHealthcheckWaitStrategy()); - genericContainer.withExposedPorts(8080) // jetty + genericContainer.withExposedPorts(80, 8080) // jetty + // .withCommand("/bin/sh", "-c", "while true; do pwd | nc -l -p 80; done") .withStartupAttempts(2) .withStartupCheckStrategy(new IsRunningStartupCheckStrategy()) .start(); @@ -232,6 +237,7 @@ public void testStartStopComplexJettyBase(ImageFromDockerfile jettyImage) throws System.out.println("testStartStopComplexJettyBase: name=" + name); Path basesWithSpaces = MavenPaths.findTestResourceDir("bases/spaces-with-conf"); + System.out.println("basesWithSpaces (src): " + basesWithSpaces); ImageFromDockerfile image = new ImageFromDSL(jettyImage.getDockerImageName() + "-complex-base") .withDockerfileFromBuilder(builder -> diff --git a/tests/test-distribution/src/test/resources/bases/spaces-with-conf/etc/jetty.conf b/tests/test-distribution/src/test/resources/bases/spaces-with-conf/etc/jetty.conf index 8d07a347c738..365800cdc6cc 100644 --- a/tests/test-distribution/src/test/resources/bases/spaces-with-conf/etc/jetty.conf +++ b/tests/test-distribution/src/test/resources/bases/spaces-with-conf/etc/jetty.conf @@ -1,3 +1,4 @@ # test configurations with spaces -dir one/test1.xml -dir two/test2.xml \ No newline at end of file +'dir one/test1.xml' +'dir two/test2.xml' +--module=state,pid From 707e7035cf869882bd0dcabc5d28e4c0122fec5c Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Thu, 19 Oct 2023 14:15:50 -0500 Subject: [PATCH 13/20] Working jetty.sh docker testing Signed-off-by: Joakim Erdfelt --- jetty-home/src/main/resources/bin/jetty.sh | 12 +- .../distribution/jettysh/ImageFromDSL.java | 11 +- .../tests/distribution/jettysh/ImageOS.java | 29 ++++ .../jettysh/ImageOSUbuntuJammyLTS.java | 44 ++++++ .../distribution/jettysh/ImageUserChange.java | 40 +++++ .../jettysh/ImageUserRootOnly.java | 33 ++++ .../jettysh/JettyShStartTest.java | 147 ++++-------------- .../bases/spaces-with-conf/logs/.gitignore | 1 + .../bases/spaces-with-conf/modules/test.mod | 7 +- 9 files changed, 194 insertions(+), 130 deletions(-) create mode 100644 tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageOS.java create mode 100644 tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageOSUbuntuJammyLTS.java create mode 100644 tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageUserChange.java create mode 100644 tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageUserRootOnly.java create mode 100644 tests/test-distribution/src/test/resources/bases/spaces-with-conf/logs/.gitignore diff --git a/jetty-home/src/main/resources/bin/jetty.sh b/jetty-home/src/main/resources/bin/jetty.sh index 60c81758121e..474a0fa54f27 100755 --- a/jetty-home/src/main/resources/bin/jetty.sh +++ b/jetty-home/src/main/resources/bin/jetty.sh @@ -226,7 +226,7 @@ testFileSystemPermissions() # Don't test if JETTY_USER is specified # as the Jetty process will switch to a different user id on startup - if [ -n "$JETTY_USER"] ; then + if [ -n "$JETTY_USER" ] ; then (( DEBUG )) && echo "Not testing file system permissions: JETTY_USER=$JETTY_USER" return 0 fi @@ -568,15 +568,7 @@ then fi # Collect the dry-run (of opts,path,main,args) from the jetty.base configuration -if (( DEBUG )) -then - echo "WITH-AT:" "$JAVA" -jar "$JETTY_START" --dry-run=opts,path,main,args ${JETTY_ARGS[@]} ${JAVA_OPTIONS[*]} - echo "-----" - echo "WITH-GLOB:" "$JAVA" -jar "$JETTY_START" --dry-run=opts,path,main,args ${JETTY_ARGS[*]} ${JAVA_OPTIONS[*]} - echo "-----" - echo "WITHOUT-GLOB:" "$JAVA" -jar "$JETTY_START" --dry-run=opts,path,main,args ${JETTY_ARGS} ${JAVA_OPTIONS[*]} -fi -JETTY_DRY_RUN=$("$JAVA" -jar "$JETTY_START" --dry-run=opts,path,main,args ${JETTY_ARGS[*]} ${JAVA_OPTIONS[*]}) +JETTY_DRY_RUN=$(echo "${JETTY_ARGS[*]} ${JAVA_OPTIONS[*]}" | xargs "$JAVA" -jar "$JETTY_START" --dry-run=opts,path,main,args) RUN_ARGS=($JETTY_SYS_PROPS ${JETTY_DRY_RUN[@]}) if (( DEBUG )) diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageFromDSL.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageFromDSL.java index f3c4dc6cabc6..0f12df751e41 100644 --- a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageFromDSL.java +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageFromDSL.java @@ -13,7 +13,10 @@ package org.eclipse.jetty.tests.distribution.jettysh; +import java.util.function.Consumer; + import org.testcontainers.images.builder.ImageFromDockerfile; +import org.testcontainers.images.builder.dockerfile.DockerfileBuilder; /** * Simple ImageFromDSL that has a toString that reports the docker image name. @@ -21,9 +24,15 @@ */ public class ImageFromDSL extends ImageFromDockerfile { - public ImageFromDSL(String name) + public ImageFromDSL(ImageFromDockerfile baseImage, String suffix, Consumer builderConsumer) + { + this(baseImage.getDockerImageName() + "-" + suffix, builderConsumer); + } + + public ImageFromDSL(String name, Consumer builderConsumer) { super(name, false); + withDockerfileFromBuilder(builderConsumer); } @Override diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageOS.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageOS.java new file mode 100644 index 000000000000..dd582647a290 --- /dev/null +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageOS.java @@ -0,0 +1,29 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.tests.distribution.jettysh; + +import java.util.function.Consumer; + +import org.testcontainers.images.builder.dockerfile.DockerfileBuilder; + +public abstract class ImageOS extends ImageFromDSL +{ + public static final String REGISTRY = "registry.jetty.org"; + public static final String REPOSITORY = REGISTRY + "/jetty-sh"; + + public ImageOS(String osid, Consumer builderConsumer) + { + super(REPOSITORY + ":" + osid, builderConsumer); + } +} diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageOSUbuntuJammyLTS.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageOSUbuntuJammyLTS.java new file mode 100644 index 000000000000..7184f1545e2f --- /dev/null +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageOSUbuntuJammyLTS.java @@ -0,0 +1,44 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.tests.distribution.jettysh; + +public class ImageOSUbuntuJammyLTS extends ImageOS +{ + public ImageOSUbuntuJammyLTS() + { + super("ubuntu-22.04", + builder -> + builder + .from("ubuntu:22.04") + .run("apt update ; " + + "apt -y upgrade ; " + + "apt install -y openjdk-17-jdk-headless ; " + + "apt install -y curl vim net-tools ") + .env("TEST_DIR", "/var/test") + .env("JETTY_HOME", "$TEST_DIR/jetty-home") + .env("JETTY_BASE", "$TEST_DIR/jetty-base") + .env("PATH", "$PATH:${JETTY_HOME}/bin/") + .user("root") + // Configure /etc/default/jetty + .run("echo \"JETTY_HOME=${JETTY_HOME}\" > /etc/default/jetty ; " + + "echo \"JETTY_BASE=${JETTY_BASE}\" >> /etc/default/jetty ; " + + "echo \"JETTY_RUN=${JETTY_BASE}\" >> /etc/default/jetty ") + // setup Jetty Home + .copy("/opt/jetty/", "${JETTY_HOME}/") + .env("PATH", "$PATH:${JETTY_HOME}/bin/") + .run("chmod ugo+x ${JETTY_HOME}/bin/jetty.sh") + .build() + ); + } +} diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageUserChange.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageUserChange.java new file mode 100644 index 000000000000..eaf3e86066cd --- /dev/null +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageUserChange.java @@ -0,0 +1,40 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.tests.distribution.jettysh; + +/** + * A docker image with JETTY_USER set to id `jetty`. + * JETTY_HOME is owned by `root`. + * JETTY_BASE is owned by `jetty` + */ +public class ImageUserChange extends ImageFromDSL +{ + public ImageUserChange(ImageOS osImage) + { + super(osImage, "user-change", builder -> + builder + .from(osImage.getDockerImageName()) + // setup "jetty" user and Jetty Base directory + .run("chmod ugo+x ${JETTY_HOME}/bin/jetty.sh ; " + + "mkdir -p ${JETTY_BASE} ; " + + "useradd --home-dir=${JETTY_BASE} --shell=/bin/bash jetty ; " + + "chown jetty:jetty ${JETTY_BASE} ; " + + "chmod a+w ${JETTY_BASE} ; " + + "echo \"JETTY_USER=jetty\" > /etc/default/jetty") // user change + .user("jetty") + // Configure Jetty Base + .workDir("${JETTY_BASE}") + .build()); + } +} diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageUserRootOnly.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageUserRootOnly.java new file mode 100644 index 000000000000..da03843847f4 --- /dev/null +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageUserRootOnly.java @@ -0,0 +1,33 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.tests.distribution.jettysh; + +/** + * A docker image with no JETTY_USER set, everything executes as `root`. + */ +public class ImageUserRootOnly extends ImageFromDSL +{ + public ImageUserRootOnly(ImageOS osImage) + { + super(osImage, "user-root-only", builder -> + builder + .from(osImage.getDockerImageName()) + .run("mkdir -p ${JETTY_BASE} ; " + + "chmod u+x ${JETTY_HOME}/bin/jetty.sh ; " + + "chmod a+w ${JETTY_BASE}") + // Configure Jetty Base + .workDir("${JETTY_BASE}") + .build()); + } +} diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/JettyShStartTest.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/JettyShStartTest.java index bf1dd9d5a0f4..9c4ddd3df31e 100644 --- a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/JettyShStartTest.java +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/JettyShStartTest.java @@ -20,7 +20,6 @@ import java.util.List; import java.util.stream.Stream; -import com.github.dockerjava.api.DockerClient; import org.awaitility.Awaitility; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.http.HttpStatus; @@ -34,8 +33,6 @@ import org.testcontainers.containers.Container; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.startupcheck.IsRunningStartupCheckStrategy; -import org.testcontainers.containers.startupcheck.StartupCheckStrategy; -import org.testcontainers.containers.wait.strategy.DockerHealthcheckWaitStrategy; import org.testcontainers.containers.wait.strategy.ShellStrategy; import org.testcontainers.images.builder.ImageFromDockerfile; @@ -71,95 +68,14 @@ public static void initJettyHome() throws Exception jettyHomePath = homeTester.getJettyHome(); } - private static ImageFromDockerfile getUbuntuOSImage() - { - return new ImageFromDSL(JETTY_REGISTRY + "jetty-sh:ubuntu-22.04") - .withDockerfileFromBuilder(builder -> - builder - .from("ubuntu:22.04") - .run("apt update ; " + - "apt -y upgrade ; " + - "apt install -y openjdk-17-jdk-headless ; " + - "apt install -y curl vim net-tools ") - .env("TEST_DIR", "/var/test") - .env("JETTY_HOME", "$TEST_DIR/jetty-home") - .env("JETTY_BASE", "$TEST_DIR/jetty-base") - .env("PATH", "$PATH:${JETTY_HOME}/bin/") - .user("root") - // Configure /etc/default/jetty - .run("echo \"JETTY_HOME=${JETTY_HOME}\" > /etc/default/jetty ; " + - "echo \"JETTY_BASE=${JETTY_BASE}\" >> /etc/default/jetty ; " + - "echo \"JETTY_RUN=${JETTY_BASE}\" >> /etc/default/jetty ; " + - (DEBUG_JETTY_SH ? " ; echo \"DEBUG=0\" >> /etc/default/jetty" : "")) - // setup Jetty Home - .copy("/opt/jetty/", "${JETTY_HOME}/") - .env("PATH", "$PATH:${JETTY_HOME}/bin/") - .run("chmod ugo+x ${JETTY_HOME}/bin/jetty.sh") - .build()) - .withFileFromFile("/opt/jetty/", jettyHomePath.toFile()); - } - - /** - * A docker image with no JETTY_USER set, everything executes as `root`. - */ - private static ImageFromDockerfile buildWithRootNoUserChange(ImageFromDockerfile osImage) - { - String name = osImage.get(); - System.out.println("buildWithRootNoUserChange: name=" + name); - - return new ImageFromDSL(osImage.getDockerImageName() + "-no-user-change") - .withDockerfileFromBuilder(builder -> - builder - .from(osImage.getDockerImageName()) - .label(JETTY_MODE, MODE_ROOT_ONLY) - .run("mkdir -p ${JETTY_BASE} ; " + - "chmod u+x ${JETTY_HOME}/bin/jetty.sh ; " + - "chmod a+w ${JETTY_BASE}") - // Configure Jetty Base - .workDir("${JETTY_BASE}") - .build()); - } - - /** - * A docker image with JETTY_USER set to id `jetty`. - * JETTY_HOME is owned by `root`. - * JETTY_BASE is owned by `jetty` - */ - private static ImageFromDockerfile buildWithUserChangeToJettyId(ImageFromDockerfile osImage) - { - String name = osImage.get(); - System.out.println("buildWithUserChangeToJettyUser: name=" + name); - - return new ImageFromDSL(osImage.getDockerImageName() + "-user-change") - .withDockerfileFromBuilder(builder -> - builder - .from(osImage.getDockerImageName()) - .label(JETTY_MODE, MODE_USER_CHANGE) - // setup "jetty" user and Jetty Base directory - .run("chmod ugo+x ${JETTY_HOME}/bin/jetty.sh ; " + - "mkdir -p ${JETTY_BASE} ; " + - "useradd --home-dir=${JETTY_BASE} --shell=/bin/bash jetty ; " + - "chown jetty:jetty ${JETTY_BASE} ; " + - "chmod a+w ${JETTY_BASE} ; " + - "echo \"JETTY_USER=jetty\" > /etc/default/jetty") // user change - .user("jetty") - // Configure Jetty Base - .workDir("${JETTY_BASE}") - .build()); - } - public static Stream jettyImages() { List images = new ArrayList<>(); - List osImages = List.of( - getUbuntuOSImage() - ); - - for (ImageFromDockerfile osImage : osImages) + for (ImageOS osImage : List.of(new ImageOSUbuntuJammyLTS())) { - images.add(Arguments.of(buildWithRootNoUserChange(osImage))); - images.add(Arguments.of(buildWithUserChangeToJettyId(osImage))); + images.add(Arguments.of(new ImageUserRootOnly(osImage))); + images.add(Arguments.of(new ImageUserChange(osImage))); } return images.stream(); @@ -172,23 +88,20 @@ public void testStartStopBasicJettyBase(ImageFromDockerfile jettyImage) throws E String name = jettyImage.get(); System.out.println("testStartStopBasicJettyBase: name=" + name); - ImageFromDockerfile image = new ImageFromDSL(jettyImage.getDockerImageName() + "-basic-base") - .withDockerfileFromBuilder(builder -> - builder - .from(jettyImage.getDockerImageName()) - .label(JETTY_BASE_MODE, "basic") - // Create a basic configuration of jetty-base - .run("java -jar ${JETTY_HOME}/start.jar --add-modules=http,deploy") - .entryPoint("/bin/sh", "-c", "while true; do pwd | nc -l -p 80; done") - .build()); + ImageFromDockerfile image = new ImageFromDSL(jettyImage, "basic-base", builder -> + builder + .from(jettyImage.getDockerImageName()) + .label(JETTY_BASE_MODE, "basic") + // Create a basic configuration of jetty-base + .run("java -jar ${JETTY_HOME}/start.jar --add-modules=http,deploy") + .build()); try (GenericContainer genericContainer = new GenericContainer<>(image)) { genericContainer.setWaitStrategy(new ShellStrategy().withCommand("id")); - // genericContainer.setWaitStrategy(new DockerHealthcheckWaitStrategy()); genericContainer.withExposedPorts(80, 8080) // jetty - // .withCommand("/bin/sh", "-c", "while true; do pwd | nc -l -p 80; done") + .withCommand("/bin/sh", "-c", "while true; do pwd | nc -l -p 80; done") .withStartupAttempts(2) .withStartupCheckStrategy(new IsRunningStartupCheckStrategy()) .start(); @@ -239,15 +152,24 @@ public void testStartStopComplexJettyBase(ImageFromDockerfile jettyImage) throws Path basesWithSpaces = MavenPaths.findTestResourceDir("bases/spaces-with-conf"); System.out.println("basesWithSpaces (src): " + basesWithSpaces); - ImageFromDockerfile image = new ImageFromDSL(jettyImage.getDockerImageName() + "-complex-base") - .withDockerfileFromBuilder(builder -> - builder - .from(jettyImage.getDockerImageName()) - .label(JETTY_BASE_MODE, "basic") - // Create a basic configuration of jetty-base - .run("java -jar ${JETTY_HOME}/start.jar --add-modules=http,deploy") - .copy("/tests/bases-with-spaces/", "${JETTY_BASE}/") - .build()) + ImageFromDockerfile image = new ImageFromDSL(jettyImage, "complex-base", builder -> + { + builder + .from(jettyImage.getDockerImageName()) + .label(JETTY_BASE_MODE, "complex") + // Create a basic configuration of jetty-base + .run("java -jar ${JETTY_HOME}/start.jar --add-modules=http,deploy ; " + + "mkdir /tmp/logs") + .copy("/tests/bases-with-spaces/", "${JETTY_BASE}/"); + if (jettyImage instanceof ImageUserChange) + { + builder.user("root") + .run("chown -R jetty:jetty $JETTY_BASE") + .user("jetty"); + } + builder.build(); + } + ) .withFileFromFile("/tests/bases-with-spaces/", basesWithSpaces.toFile()); try (GenericContainer genericContainer = new GenericContainer<>(image)) @@ -255,6 +177,7 @@ public void testStartStopComplexJettyBase(ImageFromDockerfile jettyImage) throws genericContainer.setWaitStrategy(new ShellStrategy().withCommand("id")); genericContainer.withExposedPorts(8080) // jetty + .withCommand("/bin/sh", "-c", "while true; do pwd | nc -l -p 80; done") .withStartupAttempts(2) .withStartupCheckStrategy(new IsRunningStartupCheckStrategy()) .start(); @@ -294,14 +217,4 @@ public void testStartStopComplexJettyBase(ImageFromDockerfile jettyImage) throws containsString("Jetty running pid")); } } - - static class NoopStartupCheckStrategy extends StartupCheckStrategy - { - @Override - public StartupStatus checkStartupState(DockerClient dockerClient, String containerId) - { - return StartupStatus.SUCCESSFUL; - } - } - } diff --git a/tests/test-distribution/src/test/resources/bases/spaces-with-conf/logs/.gitignore b/tests/test-distribution/src/test/resources/bases/spaces-with-conf/logs/.gitignore new file mode 100644 index 000000000000..bf0824e596ed --- /dev/null +++ b/tests/test-distribution/src/test/resources/bases/spaces-with-conf/logs/.gitignore @@ -0,0 +1 @@ +*.log \ No newline at end of file diff --git a/tests/test-distribution/src/test/resources/bases/spaces-with-conf/modules/test.mod b/tests/test-distribution/src/test/resources/bases/spaces-with-conf/modules/test.mod index d7f80f0065fa..3983027701cd 100644 --- a/tests/test-distribution/src/test/resources/bases/spaces-with-conf/modules/test.mod +++ b/tests/test-distribution/src/test/resources/bases/spaces-with-conf/modules/test.mod @@ -1,5 +1,8 @@ [exec] -showversion -XX:+PrintCommandLineFlags --Xlog:gc*:file=/tmp/logs/gc.log:time,level,tags --XX:ErrorFile=/tmp/logs/jvm_crash_pid_%p.log \ No newline at end of file +# This line is for JDK 21+ (the "gc*" is only supported on newest JVMs) +# -Xlog:gc*:file=/var/test/jetty-base/logs/gc.log:time,level,tags +# This line is for JDK 19 and older +-Xlog:gc:file=/var/test/jetty-base/logs/gc.log:time,level,tags +-XX:ErrorFile=/var/test/jetty-base/logs/jvm_crash_pid_%p.log \ No newline at end of file From f60b9acf5ec81e15d0b77b32a3337ce6afab4474 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Thu, 19 Oct 2023 14:22:49 -0500 Subject: [PATCH 14/20] Cleanup test implementation Signed-off-by: Joakim Erdfelt --- .../tests/distribution/jettysh/ImageOS.java | 26 ++++++++++++++++++ .../jettysh/ImageOSUbuntuJammyLTS.java | 1 + .../jettysh/JettyShStartTest.java | 27 +------------------ .../test/resources/jetty-logging.properties | 4 ++- 4 files changed, 31 insertions(+), 27 deletions(-) diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageOS.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageOS.java index dd582647a290..1744ded579d5 100644 --- a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageOS.java +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageOS.java @@ -13,8 +13,11 @@ package org.eclipse.jetty.tests.distribution.jettysh; +import java.io.File; +import java.nio.file.Path; import java.util.function.Consumer; +import org.eclipse.jetty.tests.hometester.JettyHomeTester; import org.testcontainers.images.builder.dockerfile.DockerfileBuilder; public abstract class ImageOS extends ImageFromDSL @@ -22,8 +25,31 @@ public abstract class ImageOS extends ImageFromDSL public static final String REGISTRY = "registry.jetty.org"; public static final String REPOSITORY = REGISTRY + "/jetty-sh"; + private Path jettyHomePath; + public ImageOS(String osid, Consumer builderConsumer) { super(REPOSITORY + ":" + osid, builderConsumer); } + + protected File getJettyHomeDir() + { + if (jettyHomePath == null) + { + String jettyVersion = System.getProperty("jettyVersion"); + try + { + JettyHomeTester homeTester = JettyHomeTester.Builder.newInstance() + .jettyVersion(jettyVersion) + .mavenLocalRepository(System.getProperty("mavenRepoPath")) + .build(); + jettyHomePath = homeTester.getJettyHome(); + } + catch (Exception e) + { + throw new RuntimeException("Unable to get unpacked JETTY_HOME dir", e); + } + } + return jettyHomePath.toFile(); + } } diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageOSUbuntuJammyLTS.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageOSUbuntuJammyLTS.java index 7184f1545e2f..e932ae2b3ec2 100644 --- a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageOSUbuntuJammyLTS.java +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageOSUbuntuJammyLTS.java @@ -40,5 +40,6 @@ public ImageOSUbuntuJammyLTS() .run("chmod ugo+x ${JETTY_HOME}/bin/jetty.sh") .build() ); + withFileFromFile("/opt/jetty/", getJettyHomeDir()); } } diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/JettyShStartTest.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/JettyShStartTest.java index 9c4ddd3df31e..240d822bcb7c 100644 --- a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/JettyShStartTest.java +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/JettyShStartTest.java @@ -24,9 +24,7 @@ import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.tests.distribution.AbstractJettyHomeTest; -import org.eclipse.jetty.tests.hometester.JettyHomeTester; import org.eclipse.jetty.toolchain.test.MavenPaths; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -47,27 +45,6 @@ */ public class JettyShStartTest extends AbstractJettyHomeTest { - private static final String JETTY_REGISTRY = "registry.jetty.org/"; - - private static final String JETTY_MODE = "jetty-mode"; - private static final String MODE_USER_CHANGE = "user-change"; - private static final String MODE_ROOT_ONLY = "root-only"; - private static final String JETTY_BASE_MODE = "jetty-base-mode"; - private static final boolean DEBUG_JETTY_SH = false; - - private static Path jettyHomePath; - - @BeforeAll - public static void initJettyHome() throws Exception - { - String jettyVersion = System.getProperty("jettyVersion"); - JettyHomeTester homeTester = JettyHomeTester.Builder.newInstance() - .jettyVersion(jettyVersion) - .mavenLocalRepository(System.getProperty("mavenRepoPath")) - .build(); - jettyHomePath = homeTester.getJettyHome(); - } - public static Stream jettyImages() { List images = new ArrayList<>(); @@ -91,7 +68,6 @@ public void testStartStopBasicJettyBase(ImageFromDockerfile jettyImage) throws E ImageFromDockerfile image = new ImageFromDSL(jettyImage, "basic-base", builder -> builder .from(jettyImage.getDockerImageName()) - .label(JETTY_BASE_MODE, "basic") // Create a basic configuration of jetty-base .run("java -jar ${JETTY_HOME}/start.jar --add-modules=http,deploy") .build()); @@ -156,7 +132,6 @@ public void testStartStopComplexJettyBase(ImageFromDockerfile jettyImage) throws { builder .from(jettyImage.getDockerImageName()) - .label(JETTY_BASE_MODE, "complex") // Create a basic configuration of jetty-base .run("java -jar ${JETTY_HOME}/start.jar --add-modules=http,deploy ; " + "mkdir /tmp/logs") @@ -186,7 +161,7 @@ public void testStartStopComplexJettyBase(ImageFromDockerfile jettyImage) throws System.err.println("== jetty.sh start =="); Container.ExecResult result = genericContainer.execInContainer("/var/test/jetty-home/bin/jetty.sh", "start"); -// assertThat(result.getExitCode(), is(0)); + assertThat(result.getExitCode(), is(0)); /* * Example successful output * ---- diff --git a/tests/test-distribution/src/test/resources/jetty-logging.properties b/tests/test-distribution/src/test/resources/jetty-logging.properties index b1e62960887a..4b707608b275 100644 --- a/tests/test-distribution/src/test/resources/jetty-logging.properties +++ b/tests/test-distribution/src/test/resources/jetty-logging.properties @@ -1,6 +1,8 @@ # Jetty Logging using jetty-slf4j-impl org.eclipse.jetty.logging.appender.MESSAGE_ESCAPE=false org.eclipse.jetty.logging.appender.NAME_CONDENSE=false -org.testcontainers.LEVEL=DEBUG +org.eclipse.jetty.LEVEL=INFO +org.testcontainers.LEVEL=INFO +#org.testcontainers.LEVEL=DEBUG #org.eclipse.jetty.LEVEL=DEBUG #org.eclipse.jetty.tests.distribution.LEVEL=DEBUG From 8be8ff50ccc294800d5d5565eda0c64004bcf24a Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Thu, 19 Oct 2023 14:26:58 -0500 Subject: [PATCH 15/20] Correct javadoc Signed-off-by: Joakim Erdfelt --- .../jetty/tests/distribution/jettysh/ImageFromDSL.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageFromDSL.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageFromDSL.java index 0f12df751e41..1025b22fc9de 100644 --- a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageFromDSL.java +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageFromDSL.java @@ -19,8 +19,8 @@ import org.testcontainers.images.builder.dockerfile.DockerfileBuilder; /** - * Simple ImageFromDSL that has a toString that reports the docker image name. - * (so that test case execution makes sense) + * Simplify the use of {@link ImageFromDockerfile} to get some sanity in naming convention + * and {@link #toString()} behaviors so that Test execution makes sense. */ public class ImageFromDSL extends ImageFromDockerfile { From 270728c5898a6bb46d5fc1655a4c4c17067062c2 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Thu, 19 Oct 2023 14:38:29 -0500 Subject: [PATCH 16/20] Cleaner testing Signed-off-by: Joakim Erdfelt --- .../jettysh/JettyShStartTest.java | 135 ++++++------------ 1 file changed, 44 insertions(+), 91 deletions(-) diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/JettyShStartTest.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/JettyShStartTest.java index 240d822bcb7c..0490a91ccc03 100644 --- a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/JettyShStartTest.java +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/JettyShStartTest.java @@ -32,7 +32,6 @@ import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.startupcheck.IsRunningStartupCheckStrategy; import org.testcontainers.containers.wait.strategy.ShellStrategy; -import org.testcontainers.images.builder.ImageFromDockerfile; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.allOf; @@ -47,32 +46,61 @@ public class JettyShStartTest extends AbstractJettyHomeTest { public static Stream jettyImages() { - List images = new ArrayList<>(); + List images = new ArrayList<>(); + // Loop through all OS images for (ImageOS osImage : List.of(new ImageOSUbuntuJammyLTS())) { - images.add(Arguments.of(new ImageUserRootOnly(osImage))); - images.add(Arguments.of(new ImageUserChange(osImage))); + // Establish user Images based on OS Image + List userImages = new ArrayList<>(); + userImages.add(new ImageUserRootOnly(osImage)); + userImages.add(new ImageUserChange(osImage)); + + // Loop through user Images to establish various JETTY_BASE configurations + for (ImageFromDSL userImage: userImages) + { + // Basic JETTY_BASE + images.add(new ImageFromDSL(userImage, "base-basic", builder -> + builder + .from(userImage.getDockerImageName()) + // Create a basic configuration of jetty-base + .run("java -jar ${JETTY_HOME}/start.jar --add-modules=http,deploy") + .build())); + + // Complex JETTY_BASE with spaces + Path basesWithSpaces = MavenPaths.findTestResourceDir("bases/spaces-with-conf"); + ImageFromDSL baseComplexWithSpacesImage = new ImageFromDSL(userImage, "base-complex-with-spaces", builder -> + { + builder + .from(userImage.getDockerImageName()) + // Create a basic configuration of jetty-base + .run("java -jar ${JETTY_HOME}/start.jar --add-modules=http,deploy") + .copy("/tests/bases-with-spaces/", "${JETTY_BASE}/"); + if (userImage instanceof ImageUserChange) + { + // Make sure we change the ownership of JETTY_BASE if we are testing a user change mode + builder.user("root") + .run("chown -R jetty:jetty $JETTY_BASE") + .user("jetty"); + } + builder.build(); + }); + baseComplexWithSpacesImage.withFileFromFile("/tests/bases-with-spaces/", basesWithSpaces.toFile()); + images.add(baseComplexWithSpacesImage); + } } - return images.stream(); + return images.stream().map(Arguments::of); } @ParameterizedTest @MethodSource("jettyImages") - public void testStartStopBasicJettyBase(ImageFromDockerfile jettyImage) throws Exception + public void testStartStopJettyBase(ImageFromDSL jettyImage) throws Exception { String name = jettyImage.get(); - System.out.println("testStartStopBasicJettyBase: name=" + name); - - ImageFromDockerfile image = new ImageFromDSL(jettyImage, "basic-base", builder -> - builder - .from(jettyImage.getDockerImageName()) - // Create a basic configuration of jetty-base - .run("java -jar ${JETTY_HOME}/start.jar --add-modules=http,deploy") - .build()); + System.out.println("testStartStopJettyBase: name=" + name); - try (GenericContainer genericContainer = new GenericContainer<>(image)) + try (GenericContainer genericContainer = new GenericContainer<>(jettyImage)) { genericContainer.setWaitStrategy(new ShellStrategy().withCommand("id")); @@ -82,82 +110,7 @@ public void testStartStopBasicJettyBase(ImageFromDockerfile jettyImage) throws E .withStartupCheckStrategy(new IsRunningStartupCheckStrategy()) .start(); - System.out.println("Started: " + image.getDockerImageName()); - - System.err.println("== jetty.sh start =="); - Container.ExecResult result = genericContainer.execInContainer("/var/test/jetty-home/bin/jetty.sh", "start"); - assertThat(result.getExitCode(), is(0)); - /* - * Example successful output - * ---- - * STDOUT: - * Starting Jetty: . started - * OK Wed Oct 18 19:29:35 UTC 2023 - * ---- - */ - Awaitility.await().atMost(Duration.ofSeconds(5)).until(result::getStdout, - allOf( - containsString("Starting Jetty:"), - containsString("\nOK ") - )); - - startHttpClient(); - - URI containerUriRoot = URI.create("http://" + genericContainer.getHost() + ":" + genericContainer.getMappedPort(8080) + "/"); - System.err.printf("Container URI Root: %s%n", containerUriRoot); - - ContentResponse response = client.GET(containerUriRoot); - assertEquals(HttpStatus.NOT_FOUND_404, response.getStatus(), new ResponseDetails(response)); - assertThat(response.getContentAsString(), containsString("Powered by Eclipse Jetty:// Server")); - - System.err.println("== jetty.sh status =="); - result = genericContainer.execInContainer("/var/test/jetty-home/bin/jetty.sh", "status"); - assertThat(result.getExitCode(), is(0)); - Awaitility.await().atMost(Duration.ofSeconds(5)).until(result::getStdout, - containsString("Jetty running pid")); - } - } - - @ParameterizedTest - @MethodSource("jettyImages") - public void testStartStopComplexJettyBase(ImageFromDockerfile jettyImage) throws Exception - { - String name = jettyImage.get(); - System.out.println("testStartStopComplexJettyBase: name=" + name); - - Path basesWithSpaces = MavenPaths.findTestResourceDir("bases/spaces-with-conf"); - System.out.println("basesWithSpaces (src): " + basesWithSpaces); - - ImageFromDockerfile image = new ImageFromDSL(jettyImage, "complex-base", builder -> - { - builder - .from(jettyImage.getDockerImageName()) - // Create a basic configuration of jetty-base - .run("java -jar ${JETTY_HOME}/start.jar --add-modules=http,deploy ; " + - "mkdir /tmp/logs") - .copy("/tests/bases-with-spaces/", "${JETTY_BASE}/"); - if (jettyImage instanceof ImageUserChange) - { - builder.user("root") - .run("chown -R jetty:jetty $JETTY_BASE") - .user("jetty"); - } - builder.build(); - } - ) - .withFileFromFile("/tests/bases-with-spaces/", basesWithSpaces.toFile()); - - try (GenericContainer genericContainer = new GenericContainer<>(image)) - { - genericContainer.setWaitStrategy(new ShellStrategy().withCommand("id")); - - genericContainer.withExposedPorts(8080) // jetty - .withCommand("/bin/sh", "-c", "while true; do pwd | nc -l -p 80; done") - .withStartupAttempts(2) - .withStartupCheckStrategy(new IsRunningStartupCheckStrategy()) - .start(); - - System.out.println("Started: " + image.getDockerImageName()); + System.out.println("Started: " + jettyImage.getDockerImageName()); System.err.println("== jetty.sh start =="); Container.ExecResult result = genericContainer.execInContainer("/var/test/jetty-home/bin/jetty.sh", "start"); From 10d5dadc36288e243e721d9e439db4f7040ecc1e Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Thu, 19 Oct 2023 15:27:58 -0500 Subject: [PATCH 17/20] Adding AmazonCorretto OS testing Signed-off-by: Joakim Erdfelt --- .../distribution/jettysh/ImageFromDSL.java | 10 +++- .../jettysh/ImageOSAmazonCorretto11.java | 43 +++++++++++++++++ .../jettysh/JettyShStartTest.java | 46 ++++++++++++++++--- 3 files changed, 92 insertions(+), 7 deletions(-) create mode 100644 tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageOSAmazonCorretto11.java diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageFromDSL.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageFromDSL.java index 1025b22fc9de..c149ef957535 100644 --- a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageFromDSL.java +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageFromDSL.java @@ -24,9 +24,12 @@ */ public class ImageFromDSL extends ImageFromDockerfile { - public ImageFromDSL(ImageFromDockerfile baseImage, String suffix, Consumer builderConsumer) + private ImageFromDSL parentImage; + + public ImageFromDSL(ImageFromDSL baseImage, String suffix, Consumer builderConsumer) { this(baseImage.getDockerImageName() + "-" + suffix, builderConsumer); + this.parentImage = baseImage; } public ImageFromDSL(String name, Consumer builderConsumer) @@ -35,6 +38,11 @@ public ImageFromDSL(String name, Consumer builderConsumer) withDockerfileFromBuilder(builderConsumer); } + public ImageFromDSL getParentImage() + { + return parentImage; + } + @Override public String toString() { diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageOSAmazonCorretto11.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageOSAmazonCorretto11.java new file mode 100644 index 000000000000..5c36c829a96b --- /dev/null +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageOSAmazonCorretto11.java @@ -0,0 +1,43 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.tests.distribution.jettysh; + +public class ImageOSAmazonCorretto11 extends ImageOS +{ + public ImageOSAmazonCorretto11() + { + super("amazoncorretto-11", + builder -> + builder + .from("amazoncorretto:11.0.20") + .run("yum update -y ; " + + "yum install -y curl tar gzip vim shadow-utils net-tools") + .env("TEST_DIR", "/var/test") + .env("JETTY_HOME", "$TEST_DIR/jetty-home") + .env("JETTY_BASE", "$TEST_DIR/jetty-base") + .env("PATH", "$PATH:${JETTY_HOME}/bin/") + .user("root") + // Configure /etc/default/jetty + .run("echo \"JETTY_HOME=${JETTY_HOME}\" > /etc/default/jetty ; " + + "echo \"JETTY_BASE=${JETTY_BASE}\" >> /etc/default/jetty ; " + + "echo \"JETTY_RUN=${JETTY_BASE}\" >> /etc/default/jetty ") + // setup Jetty Home + .copy("/opt/jetty/", "${JETTY_HOME}/") + .env("PATH", "$PATH:${JETTY_HOME}/bin/") + .run("chmod ugo+x ${JETTY_HOME}/bin/jetty.sh") + .build() + ); + withFileFromFile("/opt/jetty/", getJettyHomeDir()); + } +} diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/JettyShStartTest.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/JettyShStartTest.java index 0490a91ccc03..80090f4cff14 100644 --- a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/JettyShStartTest.java +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/JettyShStartTest.java @@ -18,6 +18,7 @@ import java.time.Duration; import java.util.ArrayList; import java.util.List; +import java.util.Stack; import java.util.stream.Stream; import org.awaitility.Awaitility; @@ -28,10 +29,13 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.testcontainers.containers.Container; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.startupcheck.IsRunningStartupCheckStrategy; import org.testcontainers.containers.wait.strategy.ShellStrategy; +import org.testcontainers.images.PullPolicy; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.allOf; @@ -44,12 +48,14 @@ */ public class JettyShStartTest extends AbstractJettyHomeTest { + private static final Logger LOG = LoggerFactory.getLogger(JettyShStartTest.class); + public static Stream jettyImages() { List images = new ArrayList<>(); // Loop through all OS images - for (ImageOS osImage : List.of(new ImageOSUbuntuJammyLTS())) + for (ImageOS osImage : List.of(new ImageOSUbuntuJammyLTS(), new ImageOSAmazonCorretto11())) { // Establish user Images based on OS Image List userImages = new ArrayList<>(); @@ -57,7 +63,7 @@ public static Stream jettyImages() userImages.add(new ImageUserChange(osImage)); // Loop through user Images to establish various JETTY_BASE configurations - for (ImageFromDSL userImage: userImages) + for (ImageFromDSL userImage : userImages) { // Basic JETTY_BASE images.add(new ImageFromDSL(userImage, "base-basic", builder -> @@ -97,11 +103,11 @@ public static Stream jettyImages() @MethodSource("jettyImages") public void testStartStopJettyBase(ImageFromDSL jettyImage) throws Exception { - String name = jettyImage.get(); - System.out.println("testStartStopJettyBase: name=" + name); + ensureParentImagesExist(jettyImage); try (GenericContainer genericContainer = new GenericContainer<>(jettyImage)) { + genericContainer.withImagePullPolicy(PullPolicy.defaultPolicy()); genericContainer.setWaitStrategy(new ShellStrategy().withCommand("id")); genericContainer.withExposedPorts(80, 8080) // jetty @@ -110,7 +116,7 @@ public void testStartStopJettyBase(ImageFromDSL jettyImage) throws Exception .withStartupCheckStrategy(new IsRunningStartupCheckStrategy()) .start(); - System.out.println("Started: " + jettyImage.getDockerImageName()); + LOG.info("Started: " + jettyImage.getDockerImageName()); System.err.println("== jetty.sh start =="); Container.ExecResult result = genericContainer.execInContainer("/var/test/jetty-home/bin/jetty.sh", "start"); @@ -132,7 +138,7 @@ public void testStartStopJettyBase(ImageFromDSL jettyImage) throws Exception startHttpClient(); URI containerUriRoot = URI.create("http://" + genericContainer.getHost() + ":" + genericContainer.getMappedPort(8080) + "/"); - System.err.printf("Container URI Root: %s%n", containerUriRoot); + LOG.debug("Container URI Root: {}", containerUriRoot); ContentResponse response = client.GET(containerUriRoot); assertEquals(HttpStatus.NOT_FOUND_404, response.getStatus(), new ResponseDetails(response)); @@ -145,4 +151,32 @@ public void testStartStopJettyBase(ImageFromDSL jettyImage) throws Exception containsString("Jetty running pid")); } } + + private void ensureParentImagesExist(ImageFromDSL jettyImage) + { + // The build stack for images + Stack images = new Stack<>(); + + ImageFromDSL parent = jettyImage; + while ((parent = parent.getParentImage()) != null) + { + images.push(parent); + } + + // Create the images (allowing testcontainers cache to do its thing) + while (!images.isEmpty()) + { + ImageFromDSL image = images.pop(); + createImage(image); + } + } + + private void createImage(ImageFromDSL image) + { + LOG.debug("Create Image: {}", image.getDockerImageName()); + try (GenericContainer container = new GenericContainer<>(image)) + { + container.start(); + } + } } From c8424bddf406b3c1c40302c82367c4fb9523bc8d Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Fri, 20 Oct 2023 08:29:53 -0500 Subject: [PATCH 18/20] Better descriptions of OS purposes Signed-off-by: Joakim Erdfelt --- .../tests/distribution/jettysh/ImageOSAmazonCorretto11.java | 6 ++++++ .../tests/distribution/jettysh/ImageOSUbuntuJammyLTS.java | 3 +++ 2 files changed, 9 insertions(+) diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageOSAmazonCorretto11.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageOSAmazonCorretto11.java index 5c36c829a96b..9ca76406edfb 100644 --- a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageOSAmazonCorretto11.java +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageOSAmazonCorretto11.java @@ -13,6 +13,12 @@ package org.eclipse.jetty.tests.distribution.jettysh; +/** + * An OS Image of linux specific for running on Amazon AWS. + * This is based on Amazon Linux 2 (which is based on Alpine 3) + * This image does NOT come with start-stop-daemon installed. + * Instead of apt, it uses yum (the redhat package manager) + */ public class ImageOSAmazonCorretto11 extends ImageOS { public ImageOSAmazonCorretto11() diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageOSUbuntuJammyLTS.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageOSUbuntuJammyLTS.java index e932ae2b3ec2..9f294452e8bf 100644 --- a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageOSUbuntuJammyLTS.java +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageOSUbuntuJammyLTS.java @@ -13,6 +13,9 @@ package org.eclipse.jetty.tests.distribution.jettysh; +/** + * An OS Image of Ubuntu Linux 22.04, it comes with start-stop-daemon installed + */ public class ImageOSUbuntuJammyLTS extends ImageOS { public ImageOSUbuntuJammyLTS() From 2357c8820b93a9d6a3a5ad2f7f7ac40cffd72a78 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Fri, 20 Oct 2023 08:30:08 -0500 Subject: [PATCH 19/20] Adding `jetty.sh stop` testing Signed-off-by: Joakim Erdfelt --- .../jettysh/JettyShStartTest.java | 41 ++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/JettyShStartTest.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/JettyShStartTest.java index 80090f4cff14..c11e86ce48e8 100644 --- a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/JettyShStartTest.java +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/JettyShStartTest.java @@ -14,11 +14,13 @@ package org.eclipse.jetty.tests.distribution.jettysh; import java.net.URI; +import java.nio.channels.AsynchronousCloseException; import java.nio.file.Path; import java.time.Duration; import java.util.ArrayList; import java.util.List; import java.util.Stack; +import java.util.concurrent.ExecutionException; import java.util.stream.Stream; import org.awaitility.Awaitility; @@ -39,9 +41,13 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.matchesRegex; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; /** * Test of jetty-home/bin/jetty.sh as generic start mechanism. @@ -140,15 +146,48 @@ public void testStartStopJettyBase(ImageFromDSL jettyImage) throws Exception URI containerUriRoot = URI.create("http://" + genericContainer.getHost() + ":" + genericContainer.getMappedPort(8080) + "/"); LOG.debug("Container URI Root: {}", containerUriRoot); + System.err.println("== Attempt GET request to service =="); ContentResponse response = client.GET(containerUriRoot); assertEquals(HttpStatus.NOT_FOUND_404, response.getStatus(), new ResponseDetails(response)); assertThat(response.getContentAsString(), containsString("Powered by Eclipse Jetty:// Server")); - System.err.println("== jetty.sh status =="); + System.err.println("== jetty.sh status (should be running) =="); result = genericContainer.execInContainer("/var/test/jetty-home/bin/jetty.sh", "status"); assertThat(result.getExitCode(), is(0)); Awaitility.await().atMost(Duration.ofSeconds(5)).until(result::getStdout, containsString("Jetty running pid")); + + System.err.println("== jetty.sh stop =="); + result = genericContainer.execInContainer("/var/test/jetty-home/bin/jetty.sh", "stop"); + assertThat(result.getExitCode(), is(0)); + /* Looking for output from jetty.sh indicating a stopped jetty. + * STDOUT Example 1 + * ---- + * Stopped Jetty: OK\n + * ---- + * STOUT Example 2 + * ---- + * Stopped Jetty: .Killed 12345\n + * OK\n + * ---- + */ + Awaitility.await().atMost(Duration.ofSeconds(5)).until(result::getStdout, + matchesRegex("Stopping Jetty: .*[\n]?OK[\n]")); + + System.err.println("== jetty.sh status (should be stopped) =="); + result = genericContainer.execInContainer("/var/test/jetty-home/bin/jetty.sh", "status"); + assertThat(result.getExitCode(), is(1)); + Awaitility.await().atMost(Duration.ofSeconds(5)).until(result::getStdout, + containsString("Jetty NOT running")); + + System.err.println("== Attempt GET request to non-existent service =="); + client.setConnectTimeout(1000); + Exception failedGetException = assertThrows(Exception.class, () -> client.GET(containerUriRoot)); + // GET failure can result in either exception below (which one is based on timing / race) + assertThat(failedGetException, anyOf( + instanceOf(ExecutionException.class), + instanceOf(AsynchronousCloseException.class)) + ); } } From 8a15525af41277bfde1093aa8d6998014edfa6cb Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Mon, 23 Oct 2023 12:32:28 -0500 Subject: [PATCH 20/20] Updates per review Signed-off-by: Joakim Erdfelt --- jetty-home/src/main/resources/bin/jetty.sh | 2 +- .../distribution/jettysh/ImageOSAmazonCorretto11.java | 5 +++-- ...buntuJammyLTS.java => ImageOSUbuntuJammyJDK17.java} | 10 ++++++---- .../{ImageUserRootOnly.java => ImageUserRoot.java} | 6 +++--- .../tests/distribution/jettysh/JettyShStartTest.java | 8 ++++---- .../resources/bases/spaces-with-conf/modules/test.mod | 5 +---- 6 files changed, 18 insertions(+), 18 deletions(-) rename tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/{ImageOSUbuntuJammyLTS.java => ImageOSUbuntuJammyJDK17.java} (88%) rename tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/{ImageUserRootOnly.java => ImageUserRoot.java} (87%) diff --git a/jetty-home/src/main/resources/bin/jetty.sh b/jetty-home/src/main/resources/bin/jetty.sh index 474a0fa54f27..1aa18617d833 100755 --- a/jetty-home/src/main/resources/bin/jetty.sh +++ b/jetty-home/src/main/resources/bin/jetty.sh @@ -679,7 +679,7 @@ case "$ACTION" in fi # Stopping service started with start-stop-daemon - if [ $UID -eq 0 ] && type start-stop-daemon > /dev/null 2>&1; then + if (( USE_START_STOP_DAEMON )) ; then (( DEBUG )) && echo "Issuing HUP to $PID" start-stop-daemon --stop \ --pid "$PID" \ diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageOSAmazonCorretto11.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageOSAmazonCorretto11.java index 9ca76406edfb..a00483181536 100644 --- a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageOSAmazonCorretto11.java +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageOSAmazonCorretto11.java @@ -15,7 +15,8 @@ /** * An OS Image of linux specific for running on Amazon AWS. - * This is based on Amazon Linux 2 (which is based on Alpine 3) + * This is based on Amazon Linux 2 (which is based on Alpine 3). + * Amazon Corretto JDK 11 is installed. * This image does NOT come with start-stop-daemon installed. * Instead of apt, it uses yum (the redhat package manager) */ @@ -23,7 +24,7 @@ public class ImageOSAmazonCorretto11 extends ImageOS { public ImageOSAmazonCorretto11() { - super("amazoncorretto-11", + super("amazoncorretto-jdk11", builder -> builder .from("amazoncorretto:11.0.20") diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageOSUbuntuJammyLTS.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageOSUbuntuJammyJDK17.java similarity index 88% rename from tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageOSUbuntuJammyLTS.java rename to tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageOSUbuntuJammyJDK17.java index 9f294452e8bf..963960b85407 100644 --- a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageOSUbuntuJammyLTS.java +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageOSUbuntuJammyJDK17.java @@ -14,13 +14,15 @@ package org.eclipse.jetty.tests.distribution.jettysh; /** - * An OS Image of Ubuntu Linux 22.04, it comes with start-stop-daemon installed + * An OS Image of Ubuntu Linux 22.04, + * Adding JDK17 toolchain. + * it comes with start-stop-daemon installed */ -public class ImageOSUbuntuJammyLTS extends ImageOS +public class ImageOSUbuntuJammyJDK17 extends ImageOS { - public ImageOSUbuntuJammyLTS() + public ImageOSUbuntuJammyJDK17() { - super("ubuntu-22.04", + super("ubuntu-22.04-jdk17", builder -> builder .from("ubuntu:22.04") diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageUserRootOnly.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageUserRoot.java similarity index 87% rename from tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageUserRootOnly.java rename to tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageUserRoot.java index da03843847f4..fd9c81c3c068 100644 --- a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageUserRootOnly.java +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/ImageUserRoot.java @@ -16,11 +16,11 @@ /** * A docker image with no JETTY_USER set, everything executes as `root`. */ -public class ImageUserRootOnly extends ImageFromDSL +public class ImageUserRoot extends ImageFromDSL { - public ImageUserRootOnly(ImageOS osImage) + public ImageUserRoot(ImageOS osImage) { - super(osImage, "user-root-only", builder -> + super(osImage, "user-root", builder -> builder .from(osImage.getDockerImageName()) .run("mkdir -p ${JETTY_BASE} ; " + diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/JettyShStartTest.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/JettyShStartTest.java index c11e86ce48e8..7172aec203d1 100644 --- a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/JettyShStartTest.java +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/jettysh/JettyShStartTest.java @@ -61,11 +61,11 @@ public static Stream jettyImages() List images = new ArrayList<>(); // Loop through all OS images - for (ImageOS osImage : List.of(new ImageOSUbuntuJammyLTS(), new ImageOSAmazonCorretto11())) + for (ImageOS osImage : List.of(new ImageOSUbuntuJammyJDK17(), new ImageOSAmazonCorretto11())) { // Establish user Images based on OS Image List userImages = new ArrayList<>(); - userImages.add(new ImageUserRootOnly(osImage)); + userImages.add(new ImageUserRoot(osImage)); userImages.add(new ImageUserChange(osImage)); // Loop through user Images to establish various JETTY_BASE configurations @@ -163,11 +163,11 @@ public void testStartStopJettyBase(ImageFromDSL jettyImage) throws Exception /* Looking for output from jetty.sh indicating a stopped jetty. * STDOUT Example 1 * ---- - * Stopped Jetty: OK\n + * Stopping Jetty: OK\n * ---- * STOUT Example 2 * ---- - * Stopped Jetty: .Killed 12345\n + * Stopping Jetty: .Killed 12345\n * OK\n * ---- */ diff --git a/tests/test-distribution/src/test/resources/bases/spaces-with-conf/modules/test.mod b/tests/test-distribution/src/test/resources/bases/spaces-with-conf/modules/test.mod index 3983027701cd..3c36a1e656a5 100644 --- a/tests/test-distribution/src/test/resources/bases/spaces-with-conf/modules/test.mod +++ b/tests/test-distribution/src/test/resources/bases/spaces-with-conf/modules/test.mod @@ -1,8 +1,5 @@ [exec] -showversion -XX:+PrintCommandLineFlags -# This line is for JDK 21+ (the "gc*" is only supported on newest JVMs) -# -Xlog:gc*:file=/var/test/jetty-base/logs/gc.log:time,level,tags -# This line is for JDK 19 and older --Xlog:gc:file=/var/test/jetty-base/logs/gc.log:time,level,tags +-Xlog:gc*:file=/var/test/jetty-base/logs/gc.log:time,level,tags -XX:ErrorFile=/var/test/jetty-base/logs/jvm_crash_pid_%p.log \ No newline at end of file