Improve and test `jetty.sh` behaviors (#10753)
* Issue #10696 - Addressing start-stop-daemon behaviors in jetty.sh * disable internal pid-file management of start-stop-daemon * IssueDo not test for file system permissions if user is root, or process will switch to JETTY_USER * Fixing bad UID / JETTY_USER condition * Avoid FS test with setuid use as well * Fixing stop behavior * Adding jetty.sh docker testing --------- Signed-off-by: Joakim Erdfelt <joakim.erdfelt@gmail.com> Signed-off-by: Olivier Lamy <olamy@apache.org> Co-authored-by: Olivier Lamy <olamy@apache.org>
This commit is contained in:
parent
8b5deea657
commit
92f62a10f9
|
@ -216,6 +216,46 @@ 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 [ -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
|
||||
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.."
|
||||
|
@ -240,6 +280,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"
|
||||
}
|
||||
|
||||
|
||||
|
@ -249,6 +293,7 @@ dumpEnv()
|
|||
CONFIGS=()
|
||||
NO_START=0
|
||||
DEBUG=0
|
||||
USE_START_STOP_DAEMON=1
|
||||
|
||||
while [[ $1 = -* ]]; do
|
||||
case $1 in
|
||||
|
@ -404,7 +449,6 @@ case "`uname`" in
|
|||
CYGWIN*) JETTY_STATE="`cygpath -w $JETTY_STATE`";;
|
||||
esac
|
||||
|
||||
|
||||
JETTY_ARGS=(${JETTY_ARGS[*]} "jetty.state=$JETTY_STATE" "jetty.pid=$JETTY_PID")
|
||||
|
||||
##################################################
|
||||
|
@ -412,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
|
||||
|
@ -427,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
|
||||
|
||||
##################################################
|
||||
|
@ -507,8 +553,22 @@ case "`uname`" in
|
|||
CYGWIN*) JETTY_START="`cygpath -w $JETTY_START`";;
|
||||
esac
|
||||
|
||||
# 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
|
||||
else
|
||||
USE_START_STOP_DAEMON=0
|
||||
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[*]})
|
||||
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 ))
|
||||
|
@ -531,24 +591,12 @@ case "$ACTION" in
|
|||
exit
|
||||
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
|
||||
fi
|
||||
testFileSystemPermissions
|
||||
|
||||
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,13 +604,14 @@ case "$ACTION" in
|
|||
CH_USER="--chuid $JETTY_USER"
|
||||
fi
|
||||
|
||||
echo ${RUN_ARGS[@]} --start-log-file="$JETTY_START_LOG" | xargs start-stop-daemon \
|
||||
# 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"
|
||||
|
@ -618,25 +667,41 @@ case "$ACTION" in
|
|||
|
||||
stop)
|
||||
echo -n "Stopping Jetty: "
|
||||
# Stop from a service file
|
||||
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
|
||||
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 (( USE_START_STOP_DAEMON )) ; then
|
||||
(( 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
|
||||
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.ImageFromDockerfile;
|
||||
import org.testcontainers.images.builder.dockerfile.DockerfileBuilder;
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
private ImageFromDSL parentImage;
|
||||
|
||||
public ImageFromDSL(ImageFromDSL baseImage, String suffix, Consumer<DockerfileBuilder> builderConsumer)
|
||||
{
|
||||
this(baseImage.getDockerImageName() + "-" + suffix, builderConsumer);
|
||||
this.parentImage = baseImage;
|
||||
}
|
||||
|
||||
public ImageFromDSL(String name, Consumer<DockerfileBuilder> builderConsumer)
|
||||
{
|
||||
super(name, false);
|
||||
withDockerfileFromBuilder(builderConsumer);
|
||||
}
|
||||
|
||||
public ImageFromDSL getParentImage()
|
||||
{
|
||||
return parentImage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return getDockerImageName();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.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
|
||||
{
|
||||
public static final String REGISTRY = "registry.jetty.org";
|
||||
public static final String REPOSITORY = REGISTRY + "/jetty-sh";
|
||||
|
||||
private Path jettyHomePath;
|
||||
|
||||
public ImageOS(String osid, Consumer<DockerfileBuilder> 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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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;
|
||||
|
||||
/**
|
||||
* An OS Image of linux specific for running on Amazon AWS.
|
||||
* 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)
|
||||
*/
|
||||
public class ImageOSAmazonCorretto11 extends ImageOS
|
||||
{
|
||||
public ImageOSAmazonCorretto11()
|
||||
{
|
||||
super("amazoncorretto-jdk11",
|
||||
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());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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;
|
||||
|
||||
/**
|
||||
* An OS Image of Ubuntu Linux 22.04,
|
||||
* Adding JDK17 toolchain.
|
||||
* it comes with start-stop-daemon installed
|
||||
*/
|
||||
public class ImageOSUbuntuJammyJDK17 extends ImageOS
|
||||
{
|
||||
public ImageOSUbuntuJammyJDK17()
|
||||
{
|
||||
super("ubuntu-22.04-jdk17",
|
||||
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()
|
||||
);
|
||||
withFileFromFile("/opt/jetty/", getJettyHomeDir());
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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 ImageUserRoot extends ImageFromDSL
|
||||
{
|
||||
public ImageUserRoot(ImageOS osImage)
|
||||
{
|
||||
super(osImage, "user-root", 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());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,221 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.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;
|
||||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.tests.distribution.AbstractJettyHomeTest;
|
||||
import org.eclipse.jetty.toolchain.test.MavenPaths;
|
||||
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;
|
||||
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.
|
||||
*/
|
||||
public class JettyShStartTest extends AbstractJettyHomeTest
|
||||
{
|
||||
private static final Logger LOG = LoggerFactory.getLogger(JettyShStartTest.class);
|
||||
|
||||
public static Stream<Arguments> jettyImages()
|
||||
{
|
||||
List<ImageFromDSL> images = new ArrayList<>();
|
||||
|
||||
// Loop through all OS images
|
||||
for (ImageOS osImage : List.of(new ImageOSUbuntuJammyJDK17(), new ImageOSAmazonCorretto11()))
|
||||
{
|
||||
// Establish user Images based on OS Image
|
||||
List<ImageFromDSL> userImages = new ArrayList<>();
|
||||
userImages.add(new ImageUserRoot(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().map(Arguments::of);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("jettyImages")
|
||||
public void testStartStopJettyBase(ImageFromDSL jettyImage) throws Exception
|
||||
{
|
||||
ensureParentImagesExist(jettyImage);
|
||||
|
||||
try (GenericContainer<?> genericContainer = new GenericContainer<>(jettyImage))
|
||||
{
|
||||
genericContainer.withImagePullPolicy(PullPolicy.defaultPolicy());
|
||||
genericContainer.setWaitStrategy(new ShellStrategy().withCommand("id"));
|
||||
|
||||
genericContainer.withExposedPorts(80, 8080) // jetty
|
||||
.withCommand("/bin/sh", "-c", "while true; do pwd | nc -l -p 80; done")
|
||||
.withStartupAttempts(2)
|
||||
.withStartupCheckStrategy(new IsRunningStartupCheckStrategy())
|
||||
.start();
|
||||
|
||||
LOG.info("Started: " + jettyImage.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) + "/");
|
||||
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 (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
|
||||
* ----
|
||||
* Stopping Jetty: OK\n
|
||||
* ----
|
||||
* STOUT Example 2
|
||||
* ----
|
||||
* Stopping 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))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureParentImagesExist(ImageFromDSL jettyImage)
|
||||
{
|
||||
// The build stack for images
|
||||
Stack<ImageFromDSL> 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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0"?>
|
||||
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
|
||||
|
||||
<Configure id="Server" class="org.eclipse.jetty.server.Server">
|
||||
</Configure>
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0"?>
|
||||
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
|
||||
|
||||
<Configure id="Server" class="org.eclipse.jetty.server.Server">
|
||||
</Configure>
|
|
@ -0,0 +1,4 @@
|
|||
# test configurations with spaces
|
||||
'dir one/test1.xml'
|
||||
'dir two/test2.xml'
|
||||
--module=state,pid
|
1
tests/test-distribution/src/test/resources/bases/spaces-with-conf/logs/.gitignore
vendored
Normal file
1
tests/test-distribution/src/test/resources/bases/spaces-with-conf/logs/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
*.log
|
|
@ -0,0 +1,5 @@
|
|||
[exec]
|
||||
-showversion
|
||||
-XX:+PrintCommandLineFlags
|
||||
-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
|
|
@ -0,0 +1 @@
|
|||
--module=test
|
|
@ -1,4 +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.eclipse.jetty.LEVEL=INFO
|
||||
org.testcontainers.LEVEL=INFO
|
||||
#org.testcontainers.LEVEL=DEBUG
|
||||
#org.eclipse.jetty.LEVEL=DEBUG
|
||||
#org.eclipse.jetty.tests.distribution.LEVEL=DEBUG
|
||||
|
|
Loading…
Reference in New Issue