diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md
index 7b8c908a..620aa022 100644
--- a/DEVELOPMENT.md
+++ b/DEVELOPMENT.md
@@ -172,7 +172,7 @@ mvn test -Dimage=$(cat build//coredb/.image-id-community) -Dadminimage=
1. Select the "EnvFile" tab
2. Make sure "Enable EnvFile" is checked.
3. Click the `+` then click to add a `.env` file.
- 4. In the file selection box select `./build//devenv-enterprise.env` or `./build//devenv-community.env` depending on which one you want to test. If you do not have the `./tmp` directory, build the docker image and it will be created.
+ 4. In the file selection box select `./build//devenv-enterprise.env` or `./build//devenv-community.env` depending on which one you want to test. If you do not have the `./build` directory, build the docker image and it will be created.
5. Rebuilding the Neo4j image will regenerate the `.env` files, so you don't need to worry about keeping the environment up to date.
You should now be able to run unit tests straight from the IDE.
diff --git a/build-docker-image.sh b/build-docker-image.sh
index 7e343b97..2c852461 100755
--- a/build-docker-image.sh
+++ b/build-docker-image.sh
@@ -75,8 +75,6 @@ function get_compatible_dockerfile_for_os_or_error
fi
echo >&2 "${IMAGE_OS} is not a supported operating system for ${version}."
usage
- DOCKERFILE_NAME
-
}
function tarball_name
@@ -167,12 +165,10 @@ cp "$(cached_tarball "${NEO4JVERSION}" "${NEO4JEDITION}")" ${COREDB_LOCALCXT_DIR
# create coredb Dockerfile
cp "${SRC_DIR}/${SERIES}/coredb/${DOCKERFILE_NAME}" "${COREDB_LOCALCXT_DIR}/Dockerfile"
-sed -i \
- -e "s|%%NEO4J_SHA%%|${coredb_sha}|" \
- -e "s|%%NEO4J_TARBALL%%|$(tarball_name "${NEO4JVERSION}" "${NEO4JEDITION}")|" \
- -e "s|%%NEO4J_EDITION%%|${NEO4JEDITION}|" \
- -e "s|%%NEO4J_DIST_SITE%%|${DISTRIBUTION_SITE}|" \
- "${COREDB_LOCALCXT_DIR}/Dockerfile"
+sed -i -e "s|%%NEO4J_SHA%%|${coredb_sha}|" "${COREDB_LOCALCXT_DIR}/Dockerfile"
+sed -i -e "s|%%NEO4J_TARBALL%%|$(tarball_name "${NEO4JVERSION}" "${NEO4JEDITION}")|" "${COREDB_LOCALCXT_DIR}/Dockerfile"
+sed -i -e "s|%%NEO4J_EDITION%%|${NEO4JEDITION}|" "${COREDB_LOCALCXT_DIR}/Dockerfile"
+sed -i -e "s|%%NEO4J_DIST_SITE%%|${DISTRIBUTION_SITE}|" "${COREDB_LOCALCXT_DIR}/Dockerfile"
# copy neo4j-admin sources
mkdir -p ${ADMIN_LOCALCXT_DIR}/local-package
@@ -182,13 +178,10 @@ cp ${SRC_DIR}/${SERIES}/neo4j-admin/*.sh ${ADMIN_LOCALCXT_DIR}/local-package
# create neo4j-admin Dockerfile
cp "${SRC_DIR}/${SERIES}/neo4j-admin/${DOCKERFILE_NAME}" "${ADMIN_LOCALCXT_DIR}/Dockerfile"
-sed -i \
- -e "s|%%NEO4J_SHA%%|${coredb_sha}|" \
- -e "s|%%NEO4J_TARBALL%%|$(tarball_name ${NEO4JVERSION} ${NEO4JEDITION})|" \
- -e "s|%%NEO4J_EDITION%%|${NEO4JEDITION}|" \
- -e "s|%%NEO4J_DIST_SITE%%|${DISTRIBUTION_SITE}|" \
- "${ADMIN_LOCALCXT_DIR}/Dockerfile"
-
+sed -i -e "s|%%NEO4J_SHA%%|${coredb_sha}|" "${ADMIN_LOCALCXT_DIR}/Dockerfile"
+sed -i -e "s|%%NEO4J_TARBALL%%|$(tarball_name ${NEO4JVERSION} ${NEO4JEDITION})|" "${ADMIN_LOCALCXT_DIR}/Dockerfile"
+sed -i -e "s|%%NEO4J_EDITION%%|${NEO4JEDITION}|" "${ADMIN_LOCALCXT_DIR}/Dockerfile"
+sed -i -e "s|%%NEO4J_DIST_SITE%%|${DISTRIBUTION_SITE}|" "${ADMIN_LOCALCXT_DIR}/Dockerfile"
## ==================================================================================
## Finally we are ready to do a docker build...
@@ -219,5 +212,6 @@ echo -n "${admin_image_tag}" > ${ADMIN_LOCALCXT_DIR}/../.image-id-"${NEO4JEDITIO
echo "NEO4JADMIN_IMAGE=$(cat "${ADMIN_LOCALCXT_DIR}"/../.image-id-"${NEO4JEDITION}")"
echo "NEO4J_EDITION=${NEO4JEDITION}"
echo "NEO4J_SKIP_MOUNTED_FOLDER_TARBALLING=true"
-} > ${BUILD_DIR}/devenv-"${NEO4JEDITION}".env
+} > ${BUILD_DIR}/${IMAGE_OS}/devenv-"${NEO4JEDITION}".env
+ln -f ${BUILD_DIR}/${IMAGE_OS}/devenv-"${NEO4JEDITION}".env ${BUILD_DIR}/devenv-"${NEO4JEDITION}".env
diff --git a/docker-image-src/4.4/coredb/docker-entrypoint.sh b/docker-image-src/4.4/coredb/docker-entrypoint.sh
index e693fdd8..cd89cb4a 100755
--- a/docker-image-src/4.4/coredb/docker-entrypoint.sh
+++ b/docker-image-src/4.4/coredb/docker-entrypoint.sh
@@ -126,6 +126,7 @@ function load_plugin_from_location
for filename in ${_location}; do
echo "Installing Plugin '${_plugin_name}' from ${_location} to ${_destination}"
cp --preserve "${filename}" "${_destination}"
+ chmod +rw ${_destination}
done
if ! is_readable "${_destination}"; then
@@ -287,7 +288,6 @@ function set_initial_password
admin_user="${BASH_REMATCH[1]}"
password="${BASH_REMATCH[2]}"
do_reset="${BASH_REMATCH[3]}"
- debug_msg "NEO4J_AUTH has been parsed as user \"${admin_user}\", password \"${password}\", do_reset \"${do_reset}\""
if [ "${password}" == "neo4j" ]; then
echo >&2 "Invalid value for password. It cannot be 'neo4j', which is the default."
diff --git a/docker-image-src/5/coredb/docker-entrypoint.sh b/docker-image-src/5/coredb/docker-entrypoint.sh
index 94141ec1..50d4d0be 100755
--- a/docker-image-src/5/coredb/docker-entrypoint.sh
+++ b/docker-image-src/5/coredb/docker-entrypoint.sh
@@ -126,6 +126,7 @@ function load_plugin_from_location
for filename in ${_location}; do
echo "Installing Plugin '${_plugin_name}' from ${_location} to ${_destination}"
cp --preserve "${filename}" "${_destination}"
+ chmod +wr ${_destination}
done
if ! is_readable "${_destination}"; then
@@ -287,6 +288,8 @@ function add_env_setting_to_conf
function set_initial_password
{
+ # this has an inbuilt assumption that any configuration settings from the environment have already been applied to neo4j.conf
+ # This is for the logic to test whether password length is too short.
local _neo4j_auth="${1}"
# set the neo4j initial password only if you run the database server
@@ -298,13 +301,13 @@ function set_initial_password
admin_user="${BASH_REMATCH[1]}"
password="${BASH_REMATCH[2]}"
do_reset="${BASH_REMATCH[3]}"
- debug_msg "NEO4J_AUTH has been parsed as user \"${admin_user}\", password \"${password}\", do_reset \"${do_reset}\""
if [ "${password}" == "neo4j" ]; then
echo >&2 "Invalid value for password. It cannot be 'neo4j', which is the default."
exit 1
fi
- if [ "${#password}" -lt 8 ]; then
+ local _min_password_length=$(cat "${NEO4J_HOME}"/conf/neo4j.conf | grep dbms.security.auth_minimum_password_length | sed -E 's/.*=(.*)/\1/')
+ if [ "${#password}" -lt "${_min_password_length:-"8"}" ]; then
echo >&2 "Invalid value for password. The minimum password length is 8 characters.
If Neo4j fails to start, you can:
1) Use a stronger password.
diff --git a/src/test/java/com/neo4j/docker/coredb/TestAdminReport.java b/src/test/java/com/neo4j/docker/coredb/TestAdminReport.java
index dbaa9dfd..57e5b1e8 100644
--- a/src/test/java/com/neo4j/docker/coredb/TestAdminReport.java
+++ b/src/test/java/com/neo4j/docker/coredb/TestAdminReport.java
@@ -142,6 +142,42 @@ private void verifyCanWriteToMountedLocation(boolean asCurrentUser, String testF
}
}
+ @Test
+ void shouldErrorIfUserCannotWrite() throws Exception
+ {
+ try(GenericContainer container = createNeo4jContainer(true))
+ {
+ Path reportFolder = temporaryFolderManager.createTempFolderAndMountAsVolume(container,
+ outputFolderNamePrefix,
+ "/reports");
+ temporaryFolderManager.setFolderOwnerToNeo4j( reportFolder );
+ // now will be running as non root, and try to write to a folder owned by 7474
+ container.start();
+ Container.ExecResult execResult = container.execInContainer( "neo4j-admin-report", reportDestinationFlag, "/reports" );
+ Assertions.assertTrue( execResult.getStderr().contains( "Folder /reports is not accessible for user: " ),
+ "Did not error about incorrect file permissions" );
+ }
+ }
+
+ @ParameterizedTest(name = "mountPoint_{0}")
+ @ValueSource(strings = {"/tmp/reports", "/reports"})
+ void shouldReownMountedReportDestinationIfRootDoesNotOwn(String mountPoint) throws Exception
+ {
+ try(GenericContainer container = createNeo4jContainer(false))
+ {
+ Path reportFolder = temporaryFolderManager.createTempFolderAndMountAsVolume(container,
+ outputFolderNamePrefix,
+ mountPoint);
+ temporaryFolderManager.setFolderOwnerToCurrentUser( reportFolder );
+ // now will be running as root, and try to write to a folder owned by 1000
+ container.start();
+ Container.ExecResult execResult = container.execInContainer( "neo4j-admin-report", reportDestinationFlag, mountPoint );
+ Assertions.assertTrue( execResult.getStderr().isEmpty(),
+ "errors were encountered when trying to reown "+mountPoint+".\n"+execResult.getStderr());
+ verifyCreatesReport( reportFolder, execResult );
+ }
+ }
+
@Test
void shouldShowNeo4jAdminHelpText_whenCMD() throws Exception
{
diff --git a/src/test/java/com/neo4j/docker/coredb/TestMounting.java b/src/test/java/com/neo4j/docker/coredb/TestMounting.java
index 56f59b17..2bd2219e 100644
--- a/src/test/java/com/neo4j/docker/coredb/TestMounting.java
+++ b/src/test/java/com/neo4j/docker/coredb/TestMounting.java
@@ -1,6 +1,5 @@
package com.neo4j.docker.coredb;
-import static com.neo4j.docker.utils.StartupDetector.makeContainerWaitForNeo4jReady;
import com.github.dockerjava.api.command.CreateContainerCmd;
import com.github.dockerjava.api.model.Bind;
import com.neo4j.docker.utils.DatabaseIO;
@@ -8,14 +7,6 @@
import com.neo4j.docker.utils.SetContainerUser;
import com.neo4j.docker.utils.TemporaryFolderManager;
import com.neo4j.docker.utils.TestSettings;
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.time.Duration;
-import java.util.Random;
-import java.util.function.Consumer;
-import java.util.stream.Stream;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.Test;
@@ -33,90 +24,100 @@
import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy;
import org.testcontainers.containers.wait.strategy.Wait;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.time.Duration;
+import java.util.Random;
+import java.util.function.Consumer;
+import java.util.stream.Stream;
+
+import static com.neo4j.docker.utils.StartupDetector.makeContainerWaitForNeo4jReady;
+
public class TestMounting
{
- private static Logger log = LoggerFactory.getLogger( TestMounting.class );
+ private static Logger log = LoggerFactory.getLogger( TestMounting.class );
@RegisterExtension
public static TemporaryFolderManager temporaryFolderManager = new TemporaryFolderManager();
- static Stream defaultUserFlagSecurePermissionsFlag()
- {
- // "asUser={0}, secureFlag={1}"
- // expected behaviour is that if you set --user flag, your data should be read/writable
- // if you don't set --user flag then read/writability should be controlled by the secure file permissions flag
- // the asCurrentUser=false, secureflag=true combination is tested separately because the container should fail to start.
- return Stream.of(
- Arguments.arguments( false, false ),
- Arguments.arguments( true, false ),
- Arguments.arguments( true, true ));
- }
-
- private GenericContainer setupBasicContainer( boolean asCurrentUser, boolean isSecurityFlagSet )
- {
- log.info( "Running as user {}, {}",
- asCurrentUser?"non-root":"root",
- isSecurityFlagSet?"with secure file permissions":"with unsecured file permissions" );
-
- GenericContainer container = new GenericContainer( TestSettings.IMAGE_ID );
- container.withExposedPorts( 7474, 7687 )
- .withLogConsumer( new Slf4jLogConsumer( log ) )
- .withEnv( "NEO4J_ACCEPT_LICENSE_AGREEMENT", "yes" )
- .withEnv( "NEO4J_AUTH", "none" );
- makeContainerWaitForNeo4jReady( container, "none" );
- if(asCurrentUser)
- {
- SetContainerUser.nonRootUser( container );
- }
- if(isSecurityFlagSet)
- {
- container.withEnv( "SECURE_FILE_PERMISSIONS", "yes" );
- }
- return container;
- }
-
- private void verifySingleFolder( Path folderToCheck, boolean shouldBeWritable )
- {
- String folderForDiagnostics = folderToCheck.toAbsolutePath().toString();
-
- Assertions.assertTrue( folderToCheck.toFile().exists(), "did not create " + folderForDiagnostics + " folder on host" );
- if( shouldBeWritable )
- {
- Assertions.assertTrue( folderToCheck.toFile().canRead(), "cannot read host "+folderForDiagnostics+" folder" );
- Assertions.assertTrue(folderToCheck.toFile().canWrite(), "cannot write to host "+folderForDiagnostics+" folder" );
- }
- }
-
- private void verifyDataFolderContentsArePresentOnHost( Path dataMount, boolean shouldBeWritable )
- {
- verifySingleFolder( dataMount.resolve( "databases" ), shouldBeWritable );
-
- if(TestSettings.NEO4J_VERSION.isAtLeastVersion( Neo4jVersion.NEO4J_VERSION_400 ))
- {
- verifySingleFolder( dataMount.resolve( "transactions" ), shouldBeWritable );
- }
- }
-
- private void verifyLogsFolderContentsArePresentOnHost( Path logsMount, boolean shouldBeWritable )
- {
- verifySingleFolder( logsMount, shouldBeWritable );
- Assertions.assertTrue( logsMount.resolve( "debug.log" ).toFile().exists(),
- "Neo4j did not write a debug.log file to "+logsMount.toString() );
- Assertions.assertEquals( shouldBeWritable,
- logsMount.resolve( "debug.log" ).toFile().canWrite(),
- String.format( "The debug.log file should %sbe writable", shouldBeWritable ? "" : "not ") );
- }
-
-
- @ParameterizedTest(name = "as current user={0}")
- @ValueSource(booleans = {true, false})
- void canDumpConfig(boolean asCurrentUser) throws Exception
+ static Stream defaultUserFlagSecurePermissionsFlag()
+ {
+ // "asUser={0}, secureFlag={1}"
+ // expected behaviour is that if you set --user flag, your data should be read/writable
+ // if you don't set --user flag then read/writability should be controlled by the secure file permissions flag
+ // the asCurrentUser=false, secureflag=true combination is tested separately because the container should fail to start.
+ return Stream.of(
+ Arguments.arguments( false, false ),
+ Arguments.arguments( true, false ),
+ Arguments.arguments( true, true ) );
+ }
+
+ private GenericContainer setupBasicContainer( boolean asCurrentUser, boolean isSecurityFlagSet )
+ {
+ log.info( "Running as user {}, {}",
+ asCurrentUser ? "non-root" : "root",
+ isSecurityFlagSet ? "with secure file permissions" : "with unsecured file permissions" );
+
+ GenericContainer container = new GenericContainer( TestSettings.IMAGE_ID );
+ container.withExposedPorts( 7474, 7687 )
+ .withLogConsumer( new Slf4jLogConsumer( log ) )
+ .withEnv( "NEO4J_ACCEPT_LICENSE_AGREEMENT", "yes" )
+ .withEnv( "NEO4J_AUTH", "none" );
+ makeContainerWaitForNeo4jReady( container, "none" );
+ if ( asCurrentUser )
+ {
+ SetContainerUser.nonRootUser( container );
+ }
+ if ( isSecurityFlagSet )
+ {
+ container.withEnv( "SECURE_FILE_PERMISSIONS", "yes" );
+ }
+ return container;
+ }
+
+ private void verifySingleFolder( Path folderToCheck, boolean shouldBeWritable )
+ {
+ String folderForDiagnostics = folderToCheck.toAbsolutePath().toString();
+
+ Assertions.assertTrue( folderToCheck.toFile().exists(), "did not create " + folderForDiagnostics + " folder on host" );
+ if ( shouldBeWritable )
+ {
+ Assertions.assertTrue( folderToCheck.toFile().canRead(), "cannot read host " + folderForDiagnostics + " folder" );
+ Assertions.assertTrue( folderToCheck.toFile().canWrite(), "cannot write to host " + folderForDiagnostics + " folder" );
+ }
+ }
+
+ private void verifyDataFolderContentsArePresentOnHost( Path dataMount, boolean shouldBeWritable )
+ {
+ verifySingleFolder( dataMount.resolve( "databases" ), shouldBeWritable );
+
+ if ( TestSettings.NEO4J_VERSION.isAtLeastVersion( Neo4jVersion.NEO4J_VERSION_400 ) )
+ {
+ verifySingleFolder( dataMount.resolve( "transactions" ), shouldBeWritable );
+ }
+ }
+
+ private void verifyLogsFolderContentsArePresentOnHost( Path logsMount, boolean shouldBeWritable )
+ {
+ verifySingleFolder( logsMount, shouldBeWritable );
+ Assertions.assertTrue( logsMount.resolve( "debug.log" ).toFile().exists(),
+ "Neo4j did not write a debug.log file to " + logsMount.toString() );
+ Assertions.assertEquals( shouldBeWritable,
+ logsMount.resolve( "debug.log" ).toFile().canWrite(),
+ String.format( "The debug.log file should %sbe writable", shouldBeWritable ? "" : "not " ) );
+ }
+
+ @ParameterizedTest( name = "as current user={0}" )
+ @ValueSource( booleans = {true, false} )
+ void canDumpConfig( boolean asCurrentUser ) throws Exception
{
File confFile;
Path confMount;
String assertMsg;
String mountPrefix;
- if(asCurrentUser)
+ if ( asCurrentUser )
{
assertMsg = "Conf file was not successfully dumped when running container as current user";
mountPrefix = "candumpconf-user-";
@@ -127,10 +128,10 @@ void canDumpConfig(boolean asCurrentUser) throws Exception
mountPrefix = "candumpconf-root-";
}
- try(GenericContainer container = setupBasicContainer(asCurrentUser, false))
+ try ( GenericContainer container = setupBasicContainer( asCurrentUser, false ) )
{
//Mount /conf
- confMount = temporaryFolderManager.createTempFolderAndMountAsVolume(container, mountPrefix,"/conf" );
+ confMount = temporaryFolderManager.createTempFolderAndMountAsVolume( container, mountPrefix, "/conf" );
confFile = confMount.resolve( "neo4j.conf" ).toFile();
//Start the container
@@ -145,7 +146,7 @@ void canDumpConfig(boolean asCurrentUser) throws Exception
// verify conf file was written
Assertions.assertTrue( confFile.exists(), assertMsg );
// verify conf folder does not have new owner if not running as root
- if(asCurrentUser)
+ if ( asCurrentUser )
{
int fileUID = (Integer) Files.getAttribute( confFile.toPath(), "unix:uid" );
int expectedUID = Integer.parseInt( SetContainerUser.getNonRootUserString().split( ":" )[0] );
@@ -156,7 +157,7 @@ void canDumpConfig(boolean asCurrentUser) throws Exception
@Test
void canDumpConfig_errorsWithoutConfMount() throws Exception
{
- try(GenericContainer container = setupBasicContainer( false, false ))
+ try ( GenericContainer container = setupBasicContainer( false, false ) )
{
container.setWaitStrategy(
Wait.forLogMessage( ".*Config Dumped.*", 1 )
@@ -164,182 +165,184 @@ void canDumpConfig_errorsWithoutConfMount() throws Exception
container.setStartupCheckStrategy( new OneShotStartupCheckStrategy() );
container.setCommand( "dump-config" );
Assertions.assertThrows( ContainerLaunchException.class,
- ()->container.start(),
- "Did not error when dump config requested without mounted /conf folder");
- String stderr = container.getLogs( OutputFrame.OutputType.STDERR);
+ () -> container.start(),
+ "Did not error when dump config requested without mounted /conf folder" );
+ String stderr = container.getLogs( OutputFrame.OutputType.STDERR );
Assertions.assertTrue( stderr.endsWith( "You must mount a folder to /conf so that the configuration file(s) can be dumped to there.\n" ) );
}
}
- @ParameterizedTest(name = "asUser={0}, secureFlag={1}")
- @MethodSource( "defaultUserFlagSecurePermissionsFlag" )
- void testCanMountJustDataFolder(boolean asCurrentUser, boolean isSecurityFlagSet) throws IOException
- {
- Assumptions.assumeTrue(TestSettings.NEO4J_VERSION.isAtLeastVersion( new Neo4jVersion( 3,1,0 ) ),
- "User checks not valid before 3.1" );
-
- try(GenericContainer container = setupBasicContainer( asCurrentUser, isSecurityFlagSet ))
- {
- Path dataMount = temporaryFolderManager.createTempFolderAndMountAsVolume(
- container,
- "canmountjustdata-",
- "/data" );
- container.start();
-
- // neo4j should now have started, so there'll be stuff in the data folder
- // we need to check that stuff is readable and owned by the correct user
- verifyDataFolderContentsArePresentOnHost( dataMount, asCurrentUser );
- }
- }
-
- @ParameterizedTest(name = "asUser={0}, secureFlag={1}")
- @MethodSource( "defaultUserFlagSecurePermissionsFlag" )
- void testCanMountJustLogsFolder(boolean asCurrentUser, boolean isSecurityFlagSet) throws IOException
- {
- Assumptions.assumeTrue(TestSettings.NEO4J_VERSION.isAtLeastVersion( new Neo4jVersion( 3,1,0 ) ),
- "User checks not valid before 3.1" );
-
- try(GenericContainer container = setupBasicContainer( asCurrentUser, isSecurityFlagSet ))
- {
- Path logsMount = temporaryFolderManager.createTempFolderAndMountAsVolume(
- container,
- "canmountjustlogs-",
- "/logs" );
- container.start();
-
- verifyLogsFolderContentsArePresentOnHost( logsMount, asCurrentUser );
- }
- }
-
- @ParameterizedTest(name = "asUser={0}, secureFlag={1}")
- @MethodSource( "defaultUserFlagSecurePermissionsFlag" )
- void testCanMountDataAndLogsFolder(boolean asCurrentUser, boolean isSecurityFlagSet) throws IOException
- {
- Assumptions.assumeTrue(TestSettings.NEO4J_VERSION.isAtLeastVersion( new Neo4jVersion( 3,1,0 ) ),
- "User checks not valid before 3.1" );
-
- try(GenericContainer container = setupBasicContainer( asCurrentUser, isSecurityFlagSet ))
- {
- Path testOutputFolder = temporaryFolderManager.createTempFolder( "canmountdataandlogs-" );
- Path dataMount = temporaryFolderManager.createTempFolderAndMountAsVolume(
- container,
- "data-", "/data", testOutputFolder
- );
- Path logsMount = temporaryFolderManager.createTempFolderAndMountAsVolume(
- container,
- "logs-", "/logs", testOutputFolder
- );
- container.start();
-
- verifyDataFolderContentsArePresentOnHost( dataMount, asCurrentUser );
- verifyLogsFolderContentsArePresentOnHost( logsMount, asCurrentUser );
- }
- }
-
- @Test
- void testCantWriteIfSecureEnabledAndNoPermissions_data() throws IOException
- {
- Assumptions.assumeTrue(TestSettings.NEO4J_VERSION.isAtLeastVersion( new Neo4jVersion( 3,1,0 ) ),
- "User checks not valid before 3.1" );
-
- try(GenericContainer container = setupBasicContainer( false, true ))
- {
- temporaryFolderManager.createTempFolderAndMountAsVolume(
- container,
- "nopermissioninsecuremode-data-",
- "/data" );
-
- // currently Neo4j will try to start and fail. It should be fixed to throw an error and not try starting
- container.setWaitStrategy( Wait.forLogMessage( "[fF]older /data is not accessible for user", 1 )
- .withStartupTimeout( Duration.ofSeconds( 20 ) ) );
- Assertions.assertThrows( org.testcontainers.containers.ContainerLaunchException.class,
- () -> container.start(),
- "Neo4j should not start in secure mode if data folder is unwritable" );
- }
- }
-
- @Test
- void testCantWriteIfSecureEnabledAndNoPermissions_logs() throws IOException
- {
- Assumptions.assumeTrue(TestSettings.NEO4J_VERSION.isAtLeastVersion( new Neo4jVersion( 3,1,0 ) ),
- "User checks not valid before 3.1" );
-
- try(GenericContainer container = setupBasicContainer( false, true ))
- {
- temporaryFolderManager.createTempFolderAndMountAsVolume(
- container,
- "nopermissioninsecuremode-logs-",
- "/logs" );
-
- // currently Neo4j will try to start and fail. It should be fixed to throw an error and not try starting
- container.setWaitStrategy( Wait.forLogMessage( "[fF]older /logs is not accessible for user", 1 )
- .withStartupTimeout( Duration.ofSeconds( 20 ) ) );
- Assertions.assertThrows( org.testcontainers.containers.ContainerLaunchException.class,
- () -> container.start(),
- "Neo4j should not start in secure mode if logs folder is unwritable" );
- }
- }
-
- @ParameterizedTest(name = "as current user={0}")
- @ValueSource(booleans = {true, false})
- void canMountAllTheThings_fileMounts(boolean asCurrentUser) throws Exception
- {
- Path testOutputFolder = temporaryFolderManager.createTempFolder( "mount-everything-" );
- try(GenericContainer container = setupBasicContainer( asCurrentUser, false ))
- {
- temporaryFolderManager.createTempFolderAndMountAsVolume( container, "conf", "/conf", testOutputFolder );
- temporaryFolderManager.createTempFolderAndMountAsVolume( container, "data", "/data", testOutputFolder );
- temporaryFolderManager.createTempFolderAndMountAsVolume( container, "import", "/import", testOutputFolder );
- temporaryFolderManager.createTempFolderAndMountAsVolume( container, "logs", "/logs", testOutputFolder );
- temporaryFolderManager.createTempFolderAndMountAsVolume( container, "metrics", "/metrics", testOutputFolder );
- temporaryFolderManager.createTempFolderAndMountAsVolume( container, "plugins", "/plugins", testOutputFolder );
- container.start();
- DatabaseIO databaseIO = new DatabaseIO( container );
- // do some database writes so that we try writing to writable folders.
- databaseIO.putInitialDataIntoContainer( "neo4j", "none" );
- databaseIO.verifyInitialDataInContainer( "neo4j", "none" );
- }
- }
-
- @ParameterizedTest(name = "as current user={0}")
- @ValueSource(booleans = {true, false})
- void canMountAllTheThings_namedVolumes(boolean asCurrentUser) throws Exception
- {
- String id = String.format( "%04d", new Random().nextInt( 10000 ));
- try(GenericContainer container = setupBasicContainer( asCurrentUser, false ))
- {
- container.withCreateContainerCmdModifier(
- (Consumer) cmd -> cmd.getHostConfig().withBinds(
- Bind.parse("conf-"+id+":/conf"),
- Bind.parse("data-"+id+":/data"),
- Bind.parse("import-"+id+":/import"),
- Bind.parse("logs-"+id+":/logs"),
- //Bind.parse("metrics-"+id+":/metrics"), //todo metrics needs to be writable but we aren't chowning in the dockerfile, so a named volume for metrics will fail
- Bind.parse("plugins-"+id+":/plugins")
- ));
- container.start();
- DatabaseIO databaseIO = new DatabaseIO( container );
- // do some database writes so that we try writing to writable folders.
- databaseIO.putInitialDataIntoContainer( "neo4j", "none" );
- databaseIO.verifyInitialDataInContainer( "neo4j", "none" );
- }
- }
+ @ParameterizedTest( name = "asUser={0}, secureFlag={1}" )
+ @MethodSource( "defaultUserFlagSecurePermissionsFlag" )
+ void testCanMountJustDataFolder( boolean asCurrentUser, boolean isSecurityFlagSet ) throws IOException
+ {
+ Assumptions.assumeTrue( TestSettings.NEO4J_VERSION.isAtLeastVersion( new Neo4jVersion( 3, 1, 0 ) ),
+ "User checks not valid before 3.1" );
+
+ try ( GenericContainer container = setupBasicContainer( asCurrentUser, isSecurityFlagSet ) )
+ {
+ Path dataMount = temporaryFolderManager.createTempFolderAndMountAsVolume(
+ container,
+ "canmountjustdata-",
+ "/data" );
+ container.start();
+
+ // neo4j should now have started, so there'll be stuff in the data folder
+ // we need to check that stuff is readable and owned by the correct user
+ verifyDataFolderContentsArePresentOnHost( dataMount, asCurrentUser );
+ }
+ }
+
+ @ParameterizedTest( name = "asUser={0}, secureFlag={1}" )
+ @MethodSource( "defaultUserFlagSecurePermissionsFlag" )
+ void testCanMountJustLogsFolder( boolean asCurrentUser, boolean isSecurityFlagSet ) throws IOException
+ {
+ Assumptions.assumeTrue( TestSettings.NEO4J_VERSION.isAtLeastVersion( new Neo4jVersion( 3, 1, 0 ) ),
+ "User checks not valid before 3.1" );
+
+ try ( GenericContainer container = setupBasicContainer( asCurrentUser, isSecurityFlagSet ) )
+ {
+ Path logsMount = temporaryFolderManager.createTempFolderAndMountAsVolume(
+ container,
+ "canmountjustlogs-",
+ "/logs" );
+ container.start();
+
+ verifyLogsFolderContentsArePresentOnHost( logsMount, asCurrentUser );
+ }
+ }
+
+ @ParameterizedTest( name = "asUser={0}, secureFlag={1}" )
+ @MethodSource( "defaultUserFlagSecurePermissionsFlag" )
+ void testCanMountDataAndLogsFolder( boolean asCurrentUser, boolean isSecurityFlagSet ) throws IOException
+ {
+ Assumptions.assumeTrue( TestSettings.NEO4J_VERSION.isAtLeastVersion( new Neo4jVersion( 3, 1, 0 ) ),
+ "User checks not valid before 3.1" );
+
+ try ( GenericContainer container = setupBasicContainer( asCurrentUser, isSecurityFlagSet ) )
+ {
+ Path testOutputFolder = temporaryFolderManager.createTempFolder( "canmountdataandlogs-" );
+ Path dataMount = temporaryFolderManager.createTempFolderAndMountAsVolume(
+ container,
+ "data-", "/data", testOutputFolder
+ );
+ Path logsMount = temporaryFolderManager.createTempFolderAndMountAsVolume(
+ container,
+ "logs-", "/logs", testOutputFolder
+ );
+ container.start();
+
+ verifyDataFolderContentsArePresentOnHost( dataMount, asCurrentUser );
+ verifyLogsFolderContentsArePresentOnHost( logsMount, asCurrentUser );
+ }
+ }
+
+ @Test
+ void testCantWriteIfSecureEnabledAndNoPermissions_data() throws IOException
+ {
+ Assumptions.assumeTrue( TestSettings.NEO4J_VERSION.isAtLeastVersion( new Neo4jVersion( 3, 1, 0 ) ),
+ "User checks not valid before 3.1" );
+
+ try ( GenericContainer container = setupBasicContainer( false, true ) )
+ {
+ temporaryFolderManager.createTempFolderAndMountAsVolume(
+ container,
+ "nopermissioninsecuremode-data-",
+ "/data" );
+
+ // currently Neo4j will try to start and fail. It should be fixed to throw an error and not try starting
+ container.setWaitStrategy( Wait.forLogMessage( "[fF]older /data is not accessible for user", 1 )
+ .withStartupTimeout( Duration.ofSeconds( 20 ) ) );
+ Assertions.assertThrows( org.testcontainers.containers.ContainerLaunchException.class,
+ () -> container.start(),
+ "Neo4j should not start in secure mode if data folder is unwritable" );
+ }
+ }
+
+ @Test
+ void testCantWriteIfSecureEnabledAndNoPermissions_logs() throws IOException
+ {
+ Assumptions.assumeTrue( TestSettings.NEO4J_VERSION.isAtLeastVersion( new Neo4jVersion( 3, 1, 0 ) ),
+ "User checks not valid before 3.1" );
+
+ try ( GenericContainer container = setupBasicContainer( false, true ) )
+ {
+ temporaryFolderManager.createTempFolderAndMountAsVolume(
+ container,
+ "nopermissioninsecuremode-logs-",
+ "/logs" );
+
+ // currently Neo4j will try to start and fail. It should be fixed to throw an error and not try starting
+ container.setWaitStrategy( Wait.forLogMessage( "[fF]older /logs is not accessible for user", 1 )
+ .withStartupTimeout( Duration.ofSeconds( 20 ) ) );
+ Assertions.assertThrows( org.testcontainers.containers.ContainerLaunchException.class,
+ () -> container.start(),
+ "Neo4j should not start in secure mode if logs folder is unwritable" );
+ }
+ }
+
+ @ParameterizedTest( name = "as current user={0}" )
+ @ValueSource( booleans = {true, false} )
+ void canMountAllTheThings_fileMounts( boolean asCurrentUser ) throws Exception
+ {
+ Path testOutputFolder = temporaryFolderManager.createTempFolder( "mount-everything-" );
+ try ( GenericContainer container = setupBasicContainer( asCurrentUser, false ) )
+ {
+ temporaryFolderManager.createTempFolderAndMountAsVolume( container, "conf", "/conf", testOutputFolder );
+ temporaryFolderManager.createTempFolderAndMountAsVolume( container, "data", "/data", testOutputFolder );
+ temporaryFolderManager.createTempFolderAndMountAsVolume( container, "import", "/import", testOutputFolder );
+ temporaryFolderManager.createTempFolderAndMountAsVolume( container, "logs", "/logs", testOutputFolder );
+ temporaryFolderManager.createTempFolderAndMountAsVolume( container, "metrics", "/metrics", testOutputFolder );
+ temporaryFolderManager.createTempFolderAndMountAsVolume( container, "plugins", "/plugins", testOutputFolder );
+ container.start();
+ DatabaseIO databaseIO = new DatabaseIO( container );
+ // do some database writes so that we try writing to writable folders.
+ databaseIO.putInitialDataIntoContainer( "neo4j", "none" );
+ databaseIO.verifyInitialDataInContainer( "neo4j", "none" );
+ }
+ }
+
+ @ParameterizedTest( name = "as current user={0}" )
+ @ValueSource( booleans = {true, false} )
+ void canMountAllTheThings_namedVolumes( boolean asCurrentUser ) throws Exception
+ {
+ String id = String.format( "%04d", new Random().nextInt( 10000 ) );
+ try ( GenericContainer container = setupBasicContainer( asCurrentUser, false ) )
+ {
+ container.withCreateContainerCmdModifier(
+ (Consumer) cmd -> cmd.getHostConfig().withBinds(
+ Bind.parse( "conf-" + id + ":/conf" ),
+ Bind.parse( "data-" + id + ":/data" ),
+ Bind.parse( "import-" + id + ":/import" ),
+ Bind.parse( "logs-" + id + ":/logs" ),
+ //Bind.parse("metrics-"+id+":/metrics"), //todo metrics needs to be writable but we aren't chowning in the dockerfile, so a named volume for metrics will fail
+ Bind.parse( "plugins-" + id + ":/plugins" )
+ ) );
+ container.start();
+ DatabaseIO databaseIO = new DatabaseIO( container );
+ // do some database writes so that we try writing to writable folders.
+ databaseIO.putInitialDataIntoContainer( "neo4j", "none" );
+ databaseIO.verifyInitialDataInContainer( "neo4j", "none" );
+ }
+ }
@Test
- void shouldReownSubfilesToNeo4j() throws Exception {
+ void shouldReownSubfilesToNeo4j() throws Exception
+ {
Assumptions.assumeTrue(
- TestSettings.NEO4J_VERSION.isAtLeastVersion(new Neo4jVersion(4, 0, 0)),
- "User checks not valid before 4.0");
+ TestSettings.NEO4J_VERSION.isAtLeastVersion( new Neo4jVersion( 4, 0, 0 ) ),
+ "User checks not valid before 4.0" );
Path logMount = temporaryFolderManager.createTempFolder( "subfileownership-" );
- Path debugLog = logMount.resolve("debug.log");
+ Path debugLog = logMount.resolve( "debug.log" );
// put file in logMount
- Files.write(debugLog, "some log words".getBytes());
+ Files.write( debugLog, "some log words".getBytes() );
// make neo4j own the conf folder but NOT the neo4j.conf
temporaryFolderManager.setFolderOwnerToNeo4j( logMount );
temporaryFolderManager.setFolderOwnerToCurrentUser( debugLog );
- try (GenericContainer container = setupBasicContainer(false, false)) {
+ try ( GenericContainer container = setupBasicContainer( false, false ) )
+ {
temporaryFolderManager.mountHostFolderAsVolume( container, logMount, "/logs" );
container.start();
// if debug.log doesn't get re-owned, neo4j will not start and this test will fail here
diff --git a/src/test/java/com/neo4j/docker/coredb/TestPasswords.java b/src/test/java/com/neo4j/docker/coredb/TestPasswords.java
index 3ceda929..6e659ef3 100644
--- a/src/test/java/com/neo4j/docker/coredb/TestPasswords.java
+++ b/src/test/java/com/neo4j/docker/coredb/TestPasswords.java
@@ -1,5 +1,7 @@
package com.neo4j.docker.coredb;
+import com.neo4j.docker.coredb.configurations.Configuration;
+import com.neo4j.docker.coredb.configurations.Setting;
import com.neo4j.docker.utils.DatabaseIO;
import com.neo4j.docker.utils.Neo4jVersion;
import com.neo4j.docker.utils.SetContainerUser;
@@ -21,6 +23,8 @@
import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy;
import org.testcontainers.containers.wait.strategy.Wait;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.util.concurrent.TimeUnit;
@@ -79,30 +83,11 @@ void testPasswordCantBeNeo4j() throws Exception
failContainer.followOutput( waitingConsumer );
Assertions.assertDoesNotThrow( () -> waitingConsumer.waitUntil(
- frame -> frame.getUtf8String().contains("Invalid value for password" ), 10, TimeUnit.SECONDS ),
+ frame -> frame.getUtf8String().contains("Invalid value for password" ), 20, TimeUnit.SECONDS ),
"did not error due to invalid password" );
}
}
- @Test
- void testWarnAndFailIfPasswordLessThan8Chars() throws Exception
- {
- Assumptions.assumeTrue( TestSettings.NEO4J_VERSION.isAtLeastVersion( new Neo4jVersion( 5,2,0 ) ),
- "Minimum password length introduced in 5.2.0");
- try(GenericContainer failContainer = createContainer( false ))
- {
- failContainer.withEnv( "NEO4J_AUTH", "neo4j/123" )
- .withStartupCheckStrategy( new OneShotStartupCheckStrategy() );
- Assertions.assertThrows( ContainerLaunchException.class, () -> failContainer.start(),
- "Neo4j started even though initial password was too short" );
- String logsOut = failContainer.getLogs();
- Assertions.assertTrue( logsOut.contains( "Invalid value for password" ),
- "did not error due to too short password");
- Assertions.assertFalse( logsOut.contains( "Remote interface available at http://localhost:7474/" ),
- "Neo4j started even though an invalid password was set");
- }
- }
-
@Test
void testDefaultPasswordAndPasswordResetIfNoNeo4jAuthSet()
{
@@ -240,4 +225,85 @@ void testPromptsForPasswordReset()
db.verifyInitialDataInContainer( user, resetPass );
}
}
+
+ @Test
+ void testWarnAndFailIfPasswordLessThan8Chars() throws Exception
+ {
+ Assumptions.assumeTrue( TestSettings.NEO4J_VERSION.isAtLeastVersion( new Neo4jVersion( 5,2,0 ) ),
+ "Minimum password length introduced in 5.2.0");
+ try(GenericContainer failContainer = createContainer( false ))
+ {
+ failContainer.withEnv( "NEO4J_AUTH", "neo4j/123" )
+ .withStartupCheckStrategy( new OneShotStartupCheckStrategy() );
+ Assertions.assertThrows( ContainerLaunchException.class, () -> failContainer.start(),
+ "Neo4j started even though initial password was too short" );
+ String logsOut = failContainer.getLogs();
+ Assertions.assertTrue( logsOut.contains( "Invalid value for password" ),
+ "did not error due to too short password");
+ Assertions.assertFalse( logsOut.contains( "Remote interface available at http://localhost:7474/" ),
+ "Neo4j started even though an invalid password was set");
+ }
+ }
+
+ @Test
+ void testWarnAndFailIfPasswordLessThanOverride() throws Exception
+ {
+ Assumptions.assumeTrue( TestSettings.NEO4J_VERSION.isAtLeastVersion( new Neo4jVersion( 5,2,0 ) ),
+ "Minimum password length introduced in 5.2.0");
+ try(GenericContainer failContainer = createContainer( false ))
+ {
+ failContainer.withEnv( "NEO4J_AUTH", "neo4j/123" )
+ .withEnv(Configuration.getConfigurationNameMap().get( Setting.MINIMUM_PASSWORD_LENGTH ).envName, "20")
+ .withStartupCheckStrategy( new OneShotStartupCheckStrategy() );
+ Assertions.assertThrows( ContainerLaunchException.class, () -> failContainer.start(),
+ "Neo4j started even though initial password was too short" );
+ String logsOut = failContainer.getLogs();
+ Assertions.assertTrue( logsOut.contains( "Invalid value for password" ),
+ "did not error due to too short password");
+ Assertions.assertFalse( logsOut.contains( "Remote interface available at http://localhost:7474/" ),
+ "Neo4j started even though an invalid password was set");
+ }
+ }
+
+ @Test
+ void shouldNotWarnAboutMinimumPasswordLengthIfSettingOverridden_env() throws Exception
+ {
+ Assumptions.assumeTrue( TestSettings.NEO4J_VERSION.isAtLeastVersion( new Neo4jVersion( 5,2,0 ) ),
+ "Minimum password length introduced in 5.2.0");
+ try(GenericContainer container = createContainer( false ))
+ {
+ container.withEnv( "NEO4J_AUTH", "neo4j/123" )
+ .withEnv(Configuration.getConfigurationNameMap().get( Setting.MINIMUM_PASSWORD_LENGTH ).envName, "2");
+ container.start();
+ String logs = container.getLogs();
+ Assertions.assertFalse( logs.contains( "Invalid value for password. The minimum password length is 8 characters." ),
+ "Should not error about minimum password length if overridden.");
+ DatabaseIO db = new DatabaseIO( container );
+ db.putInitialDataIntoContainer( "neo4j", "123" );
+ }
+ }
+
+ @Test
+ void shouldNotWarnAboutMinimumPasswordLengthIfSettingOverridden_conf() throws Exception
+ {
+ Assumptions.assumeTrue( TestSettings.NEO4J_VERSION.isAtLeastVersion( new Neo4jVersion( 5,2,0 ) ),
+ "Minimum password length introduced in 5.2.0");
+ try(GenericContainer container = createContainer( false ))
+ {
+ Path confMount = temporaryFolderManager.createTempFolderAndMountAsVolume(
+ container,
+ "noMinimumPasswordLength-conf-",
+ "/conf" );
+ Files.writeString(confMount.resolve( "neo4j.conf" ),
+ Configuration.getConfigurationNameMap().get( Setting.MINIMUM_PASSWORD_LENGTH ).name+"=2");
+
+ container.withEnv( "NEO4J_AUTH", "neo4j/123" );
+ container.start();
+ String logs = container.getLogs();
+ Assertions.assertFalse( logs.contains( "Invalid value for password. The minimum password length is 8 characters." ),
+ "Should not error about minimum password length if overridden.");
+ DatabaseIO db = new DatabaseIO( container );
+ db.putInitialDataIntoContainer( "neo4j", "123" );
+ }
+ }
}
diff --git a/src/test/java/com/neo4j/docker/coredb/configurations/Configuration.java b/src/test/java/com/neo4j/docker/coredb/configurations/Configuration.java
index c3f286c0..8e3b430c 100644
--- a/src/test/java/com/neo4j/docker/coredb/configurations/Configuration.java
+++ b/src/test/java/com/neo4j/docker/coredb/configurations/Configuration.java
@@ -26,6 +26,7 @@ public class Configuration
put( Setting.MEMORY_HEAP_INITIALSIZE, new Configuration("server.memory.heap.initial_size"));
put( Setting.MEMORY_HEAP_MAXSIZE, new Configuration( "server.memory.heap.max_size"));
put( Setting.MEMORY_PAGECACHE_SIZE, new Configuration("server.memory.pagecache.size"));
+ put( Setting.MINIMUM_PASSWORD_LENGTH, new Configuration("dbms.security.auth_minimum_password_length"));
put( Setting.SECURITY_PROCEDURES_UNRESTRICTED, new Configuration("dbms.security.procedures.unrestricted"));
put( Setting.TXLOG_RETENTION_POLICY, new Configuration("db.tx_log.rotation.retention_policy"));
}};
diff --git a/src/test/java/com/neo4j/docker/coredb/configurations/Setting.java b/src/test/java/com/neo4j/docker/coredb/configurations/Setting.java
index 32f1a389..b2d9a4a2 100644
--- a/src/test/java/com/neo4j/docker/coredb/configurations/Setting.java
+++ b/src/test/java/com/neo4j/docker/coredb/configurations/Setting.java
@@ -17,6 +17,7 @@ public enum Setting
MEMORY_HEAP_INITIALSIZE,
MEMORY_HEAP_MAXSIZE,
MEMORY_PAGECACHE_SIZE,
+ MINIMUM_PASSWORD_LENGTH,
SECURITY_PROCEDURES_UNRESTRICTED,
TXLOG_RETENTION_POLICY
}
diff --git a/src/test/java/com/neo4j/docker/coredb/configurations/TestExtendedConf.java b/src/test/java/com/neo4j/docker/coredb/configurations/TestExtendedConf.java
index 6a06324a..616ac490 100644
--- a/src/test/java/com/neo4j/docker/coredb/configurations/TestExtendedConf.java
+++ b/src/test/java/com/neo4j/docker/coredb/configurations/TestExtendedConf.java
@@ -91,7 +91,7 @@ private void assertPasswordChangedLogIsCorrect( String password, GenericContaine
}
}
- @ParameterizedTest
+ @ParameterizedTest(name = "default_user_password_{0}")
@ValueSource(strings = {"", "supersecretpassword"})
void testReadsTheExtendedConfFile_defaultUser(String password) throws Exception
{
@@ -104,11 +104,13 @@ void testReadsTheExtendedConfFile_defaultUser(String password) throws Exception
Path confFile = testConfsFolder.resolve( "ExtendedConf.conf" );
Files.copy( confFile, confFolder.resolve( "neo4j.conf" ) );
chmodConfFilePermissions( confFolder.resolve( "neo4j.conf" ) );
- temporaryFolderManager.setFolderOwnerToNeo4j( confFolder.resolve( "neo4j.conf" ) );
+ //temporaryFolderManager.setFolderOwnerToNeo4j( confFolder.resolve( "neo4j.conf" ) );
// start container
try(GenericContainer container = createContainer(password))
{
+ container.withEnv( "NEO4J_DEBUG", "yes" )
+ .withEnv( "NEO4J_server_cluster_listen__address", "$(hostname):6000" );
runContainerAndVerify( container, confFolder, logsFolder, password );
}
}
diff --git a/src/test/java/com/neo4j/docker/coredb/plugins/TestBundledPluginInstallation.java b/src/test/java/com/neo4j/docker/coredb/plugins/TestBundledPluginInstallation.java
index ce3c588a..669ea6b3 100644
--- a/src/test/java/com/neo4j/docker/coredb/plugins/TestBundledPluginInstallation.java
+++ b/src/test/java/com/neo4j/docker/coredb/plugins/TestBundledPluginInstallation.java
@@ -61,7 +61,7 @@ private GenericContainer createContainerWithBundledPlugin(String pluginName)
.waitingFor( Wait.forHttp( "/" )
.forPort( DEFAULT_BROWSER_PORT )
.forStatusCode( 200 )
- .withStartupTimeout( Duration.ofSeconds( 45 ) ) );
+ .withStartupTimeout( Duration.ofSeconds( 60 ) ) );
return container;
}
@@ -205,7 +205,8 @@ void testPluginLoadsWithAuthentication() throws Exception
.withEnv( "NEO4J_dbms_bloom_license__file", "/licenses/bloom.license" );
// mounting logs because it's useful for debugging
temporaryFolderManager.createTempFolderAndMountAsVolume( container, "logs", "/logs", testFolder );
-
+ Path licenseFolder = temporaryFolderManager.createTempFolderAndMountAsVolume( container, "license", "/licenses", testFolder );
+ Files.writeString( licenseFolder.resolve("bloom.license"), "notareallicense" );
// make sure the container successfully starts and we can write to it without getting authentication errors
container.start();
DatabaseIO dbio = new DatabaseIO( container );
diff --git a/src/test/java/com/neo4j/docker/coredb/plugins/TestPluginInstallation.java b/src/test/java/com/neo4j/docker/coredb/plugins/TestPluginInstallation.java
index e3812ea0..95620254 100644
--- a/src/test/java/com/neo4j/docker/coredb/plugins/TestPluginInstallation.java
+++ b/src/test/java/com/neo4j/docker/coredb/plugins/TestPluginInstallation.java
@@ -2,14 +2,22 @@
import com.github.dockerjava.api.command.CreateContainerCmd;
import com.google.gson.Gson;
-import com.neo4j.docker.utils.*;
-import java.time.Duration;
+import com.neo4j.docker.utils.DatabaseIO;
+import com.neo4j.docker.utils.HostFileHttpHandler;
+import com.neo4j.docker.utils.HttpServerRule;
+import com.neo4j.docker.utils.Neo4jVersion;
+import com.neo4j.docker.utils.SetContainerUser;
+import com.neo4j.docker.utils.StartupDetector;
+import com.neo4j.docker.utils.TemporaryFolderManager;
+import com.neo4j.docker.utils.TestSettings;
import org.junit.Rule;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.migrationsupport.rules.EnableRuleMigrationSupport;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testcontainers.Testcontainers;
@@ -24,6 +32,7 @@
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
+import java.time.Duration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -34,6 +43,7 @@
import org.neo4j.driver.Record;
+import static com.neo4j.docker.utils.StartupDetector.makeContainerWaitForNeo4jReady;
import static com.neo4j.docker.utils.TestSettings.NEO4J_VERSION;
@EnableRuleMigrationSupport
@@ -55,21 +65,40 @@ private GenericContainer createContainerWithTestingPlugin()
Testcontainers.exposeHostPorts( httpServer.PORT );
GenericContainer container = new GenericContainer( TestSettings.IMAGE_ID );
- container.withEnv( "NEO4J_AUTH", DB_USER+"/"+ DB_PASSWORD)
- .withEnv( "NEO4J_ACCEPT_LICENSE_AGREEMENT", "yes" )
- .withEnv( "NEO4J_DEBUG", "yes" )
- .withEnv( Neo4jPluginEnv.get(), "[\"_testing\"]" )
- .withExposedPorts( 7474, 7687 )
- .withLogConsumer( new Slf4jLogConsumer( log ) );
- StartupDetector.makeContainerWaitForDatabaseReady(container, DB_USER, DB_PASSWORD, "neo4j",
- Duration.ofSeconds(60));
+ container.withEnv( "NEO4J_AUTH", DB_USER + "/" + DB_PASSWORD )
+ .withEnv( "NEO4J_ACCEPT_LICENSE_AGREEMENT", "yes" )
+ .withEnv( "NEO4J_DEBUG", "yes" )
+ .withEnv( Neo4jPluginEnv.get(), "[\"_testing\"]" )
+ .withExposedPorts( 7474, 7687 )
+ .withLogConsumer( new Slf4jLogConsumer( log ) );
+ StartupDetector.makeContainerWaitForDatabaseReady( container, DB_USER, DB_PASSWORD, "neo4j",
+ Duration.ofSeconds( 60 ) );
SetContainerUser.nonRootUser( container );
return container;
}
- private File createTestVersionsJson(Path destinationFolder, String version) throws Exception
+ private GenericContainer setupContainerWithUser( boolean asCurrentUser )
{
- List jsonEntry = Collections.singletonList( new VersionsJsonEntry(version) );
+ log.info( "Running as user {}, {}",
+ asCurrentUser ? "non-root" : "root" );
+
+ GenericContainer container = new GenericContainer( TestSettings.IMAGE_ID );
+ container.withExposedPorts( 7474, 7687 )
+ .withLogConsumer( new Slf4jLogConsumer( log ) )
+ .withEnv( "NEO4J_ACCEPT_LICENSE_AGREEMENT", "yes" )
+ .withEnv( "NEO4J_AUTH", "none" );
+ makeContainerWaitForNeo4jReady( container, "none" );
+ if ( asCurrentUser )
+ {
+ SetContainerUser.nonRootUser( container );
+ }
+
+ return container;
+ }
+
+ private File createTestVersionsJson( Path destinationFolder, String version ) throws Exception
+ {
+ List jsonEntry = Collections.singletonList( new VersionsJsonEntry( version ) );
Gson jsonBuilder = new Gson();
String jsonStr = jsonBuilder.toJson( jsonEntry );
@@ -78,12 +107,12 @@ private File createTestVersionsJson(Path destinationFolder, String version) thro
return outputJsonFile;
}
- private File createTestVersionsJson(Path destinationFolder, Map versionAndJar) throws Exception
+ private File createTestVersionsJson( Path destinationFolder, Map versionAndJar ) throws Exception
{
List jsonEntries = versionAndJar.keySet()
.stream()
.map( key -> new VersionsJsonEntry( key, versionAndJar.get( key ) ) )
- .collect( Collectors.toList());
+ .collect( Collectors.toList() );
Gson jsonBuilder = new Gson();
String jsonStr = jsonBuilder.toJson( jsonEntries );
@@ -94,13 +123,13 @@ private File createTestVersionsJson(Path destinationFolder, Map
private void setupTestPlugin( File versionsJson ) throws Exception
{
- File myPluginJar = new File(getClass().getClassLoader().getResource( "testplugin/"+PLUGIN_JAR ).toURI());
+ File myPluginJar = new File( getClass().getClassLoader().getResource( "testplugin/" + PLUGIN_JAR ).toURI() );
httpServer.registerHandler( versionsJson.getName(), new HostFileHttpHandler( versionsJson, "application/json" ) );
httpServer.registerHandler( PLUGIN_JAR, new HostFileHttpHandler( myPluginJar, "application/java-archive" ) );
}
- private void verifyTestPluginLoaded(DatabaseIO db)
+ private void verifyTestPluginLoaded( DatabaseIO db )
{
// when we check the list of installed procedures...
String listProceduresCypherQuery = NEO4J_VERSION.isAtLeastVersion( new Neo4jVersion( 4, 3, 0 ) ) ?
@@ -109,17 +138,17 @@ private void verifyTestPluginLoaded(DatabaseIO db)
List procedures = db.runCypherQuery( DB_USER, DB_PASSWORD, listProceduresCypherQuery );
// Then the procedure from the test plugin should be listed
Assertions.assertTrue( procedures.stream()
- .anyMatch(x -> x.get( "name" ).asString()
- .equals( "com.neo4j.docker.test.myplugin.defaultValues" ) ),
- "Missing procedure provided by our plugin" );
+ .anyMatch( x -> x.get( "name" ).asString()
+ .equals( "com.neo4j.docker.test.myplugin.defaultValues" ) ),
+ "Missing procedure provided by our plugin" );
// When we call the procedure from the plugin
- List pluginResponse = db.runCypherQuery(DB_USER, DB_PASSWORD,
- "CALL com.neo4j.docker.test.myplugin.defaultValues" );
+ List pluginResponse = db.runCypherQuery( DB_USER, DB_PASSWORD,
+ "CALL com.neo4j.docker.test.myplugin.defaultValues" );
// Then we get the response we expect
- Assertions.assertEquals(1, pluginResponse.size(), "Our procedure should only return a single result");
- Record record = pluginResponse.get(0);
+ Assertions.assertEquals( 1, pluginResponse.size(), "Our procedure should only return a single result" );
+ Record record = pluginResponse.get( 0 );
String message = "Result from calling our procedure doesnt match our expectations";
Assertions.assertEquals( "a string", record.get( "string" ).asString(), message );
@@ -134,11 +163,11 @@ public void testPluginLoads() throws Exception
Path pluginsDir = temporaryFolderManager.createTempFolder( "plugin-" );
File versionsJson = createTestVersionsJson( pluginsDir, NEO4J_VERSION.toString() );
setupTestPlugin( versionsJson );
- try(GenericContainer container = createContainerWithTestingPlugin())
+ try ( GenericContainer container = createContainerWithTestingPlugin() )
{
container.start();
- DatabaseIO db = new DatabaseIO(container);
- verifyTestPluginLoaded(db);
+ DatabaseIO db = new DatabaseIO( container );
+ verifyTestPluginLoaded( db );
}
}
@@ -146,39 +175,39 @@ public void testPluginLoads() throws Exception
public void test_NEO4JLABS_PLUGIN_envWorksIn5() throws Exception
{
Assumptions.assumeTrue( NEO4J_VERSION.isAtLeastVersion( Neo4jVersion.NEO4J_VERSION_500 ),
- "NEO4JLABS_PLUGIN backwards compatibility does not need checking pre 5.x");
+ "NEO4JLABS_PLUGIN backwards compatibility does not need checking pre 5.x" );
Path pluginsDir = temporaryFolderManager.createTempFolder( "plugin-backcompat-" );
File versionsJson = createTestVersionsJson( pluginsDir, NEO4J_VERSION.toString() );
setupTestPlugin( versionsJson );
- try(GenericContainer container = createContainerWithTestingPlugin())
+ try ( GenericContainer container = createContainerWithTestingPlugin() )
{
container.withEnv( Neo4jPluginEnv.PLUGIN_ENV_5X, "" );
container.withEnv( Neo4jPluginEnv.PLUGIN_ENV_4X, "[\"_testing\"]" );
container.start();
- DatabaseIO db = new DatabaseIO(container);
- verifyTestPluginLoaded(db);
+ DatabaseIO db = new DatabaseIO( container );
+ verifyTestPluginLoaded( db );
}
}
@Test
public void test_NEO4J_PLUGIN_envWorksIn44() throws Exception
{
- Assumptions.assumeTrue( NEO4J_VERSION.isAtLeastVersion( new Neo4jVersion( 4,4,18 ) ),
- "NEO4JLABS_PLUGIN did not work in 4.4 before 4.4.18");
+ Assumptions.assumeTrue( NEO4J_VERSION.isAtLeastVersion( new Neo4jVersion( 4, 4, 18 ) ),
+ "NEO4JLABS_PLUGIN did not work in 4.4 before 4.4.18" );
Assumptions.assumeTrue( NEO4J_VERSION.isOlderThan( Neo4jVersion.NEO4J_VERSION_500 ),
"Only checking forwards compatibility in 4.4" );
Path pluginsDir = temporaryFolderManager.createTempFolder( "plugin-forwardcompat-" );
File versionsJson = createTestVersionsJson( pluginsDir, NEO4J_VERSION.toString() );
setupTestPlugin( versionsJson );
- try(GenericContainer container = createContainerWithTestingPlugin())
+ try ( GenericContainer container = createContainerWithTestingPlugin() )
{
container.withEnv( Neo4jPluginEnv.PLUGIN_ENV_5X, "[\"_testing\"]" );
container.withEnv( Neo4jPluginEnv.PLUGIN_ENV_4X, "" );
container.start();
- DatabaseIO db = new DatabaseIO(container);
- verifyTestPluginLoaded(db);
+ DatabaseIO db = new DatabaseIO( container );
+ verifyTestPluginLoaded( db );
}
}
@@ -188,21 +217,21 @@ public void testPluginConfigurationDoesNotOverrideUserSetValues() throws Excepti
Path pluginsDir = temporaryFolderManager.createTempFolder( "plugin-noOverride-" );
File versionsJson = createTestVersionsJson( pluginsDir, NEO4J_VERSION.toString() );
setupTestPlugin( versionsJson );
- try(GenericContainer container = createContainerWithTestingPlugin())
+ try ( GenericContainer container = createContainerWithTestingPlugin() )
{
// When we set a config value explicitly
- container.withEnv("NEO4J_dbms_security_procedures_unrestricted", "foo" );
+ container.withEnv( "NEO4J_dbms_security_procedures_unrestricted", "foo" );
// When we start the neo4j docker container
container.start();
// When we connect to the database with the plugin
// Check that the config remains as set by our env var and is not overridden by the plugin defaults
- DatabaseIO db = new DatabaseIO(container);
- verifyTestPluginLoaded(db);
+ DatabaseIO db = new DatabaseIO( container );
+ verifyTestPluginLoaded( db );
db.verifyConfigurationSetting( DB_USER, DB_PASSWORD,
"dbms.security.procedures.unrestricted",
"foo",
- "neo4j config should not be overridden by plugin");
+ "neo4j config should not be overridden by plugin" );
}
}
@@ -210,18 +239,18 @@ public void testPluginConfigurationDoesNotOverrideUserSetValues() throws Excepti
void invalidPluginNameShouldGiveOptionsAndError()
{
Assumptions.assumeTrue( NEO4J_VERSION.isAtLeastVersion( Neo4jVersion.NEO4J_VERSION_440 ) );
- try(GenericContainer container = new GenericContainer( TestSettings.IMAGE_ID ))
+ try ( GenericContainer container = new GenericContainer( TestSettings.IMAGE_ID ) )
{
// if we try to set a plugin that doesn't exist
container.withEnv( Neo4jPluginEnv.get(), "[\"notarealplugin\"]" )
.withEnv( "NEO4J_ACCEPT_LICENSE_AGREEMENT", "yes" )
.withStartupCheckStrategy( new OneShotStartupCheckStrategy()
- .withTimeout( Duration.ofSeconds( 10 ) ) )
+ .withTimeout( Duration.ofSeconds( 30 ) ) )
.withLogConsumer( new Slf4jLogConsumer( log ) );
Assertions.assertThrows( ContainerLaunchException.class, container::start );
// the container should output a helpful message and quit
String stdout = container.getLogs();
- Assertions.assertTrue( stdout.contains("\"notarealplugin\" is not a known Neo4j plugin. Options are:") );
+ Assertions.assertTrue( stdout.contains( "\"notarealplugin\" is not a known Neo4j plugin. Options are:" ) );
Assertions.assertFalse( stdout.contains( "_testing" ), "Fake _testing plugin is exposed." );
}
}
@@ -230,18 +259,18 @@ void invalidPluginNameShouldGiveOptionsAndError()
void invalidPluginNameShouldGiveOptionsAndError_mulitpleplugins()
{
Assumptions.assumeTrue( NEO4J_VERSION.isAtLeastVersion( Neo4jVersion.NEO4J_VERSION_440 ) );
- try(GenericContainer container = new GenericContainer( TestSettings.IMAGE_ID ))
+ try ( GenericContainer container = new GenericContainer( TestSettings.IMAGE_ID ) )
{
// if we try to set a plugin that doesn't exist
container.withEnv( Neo4jPluginEnv.get(), "[\"apoc\", \"notarealplugin\"]" )
.withEnv( "NEO4J_ACCEPT_LICENSE_AGREEMENT", "yes" )
.withStartupCheckStrategy( new OneShotStartupCheckStrategy()
- .withTimeout( Duration.ofSeconds( 10 ) ) )
+ .withTimeout( Duration.ofSeconds( 30 ) ) )
.withLogConsumer( new Slf4jLogConsumer( log ) );
Assertions.assertThrows( ContainerLaunchException.class, container::start );
// the container should output a helpful message and quit
String stdout = container.getLogs();
- Assertions.assertTrue( stdout.contains("\"notarealplugin\" is not a known Neo4j plugin. Options are:") );
+ Assertions.assertTrue( stdout.contains( "\"notarealplugin\" is not a known Neo4j plugin. Options are:" ) );
Assertions.assertFalse( stdout.contains( "_testing" ), "Fake _testing plugin is exposed." );
}
}
@@ -252,22 +281,22 @@ public void testBrokenVersionsJsonCausesHelpfulError() throws Exception
Assumptions.assumeTrue( NEO4J_VERSION.isAtLeastVersion( Neo4jVersion.NEO4J_VERSION_440 ) );
Path pluginsDir = temporaryFolderManager.createTempFolder( "plugin-broken-versionsjson-" );
// create a versions.json that DOES NOT contain the current neo4j version in its mapping
- File versionsJson = createTestVersionsJson( pluginsDir, "50.0.0");
+ File versionsJson = createTestVersionsJson( pluginsDir, "50.0.0" );
setupTestPlugin( versionsJson );
- try(GenericContainer container = createContainerWithTestingPlugin())
+ try ( GenericContainer container = createContainerWithTestingPlugin() )
{
container.start();
- String startupErrors = container.getLogs( OutputFrame.OutputType.STDERR);
- Assertions.assertTrue( startupErrors.contains( "No compatible \"_testing\" plugin found for Neo4j "+NEO4J_VERSION ),
- "Did not error about plugin compatibility.");
- DatabaseIO db = new DatabaseIO(container);
+ String startupErrors = container.getLogs( OutputFrame.OutputType.STDERR );
+ Assertions.assertTrue( startupErrors.contains( "No compatible \"_testing\" plugin found for Neo4j " + NEO4J_VERSION ),
+ "Did not error about plugin compatibility." );
+ DatabaseIO db = new DatabaseIO( container );
// make sure plugin did not load
List procedures = db.runCypherQuery( DB_USER, DB_PASSWORD,
"SHOW PROCEDURES YIELD name, signature RETURN name, signature" );
Assertions.assertFalse( procedures.stream()
- .anyMatch(x -> x.get( "name" ).asString()
- .equals( "com.neo4j.docker.test.myplugin.defaultValues" ) ),
- "Incompatible test plugin was loaded." );
+ .anyMatch( x -> x.get( "name" ).asString()
+ .equals( "com.neo4j.docker.test.myplugin.defaultValues" ) ),
+ "Incompatible test plugin was loaded." );
}
}
@@ -275,13 +304,13 @@ public void testBrokenVersionsJsonCausesHelpfulError() throws Exception
void testSemanticVersioningPlugin_catchesMatchWithX() throws Exception
{
Path pluginsDir = temporaryFolderManager.createTempFolder( "plugin-semverMatchesX-" );
- File versionsJson = createTestVersionsJson( pluginsDir, NEO4J_VERSION.getBranch()+".x");
+ File versionsJson = createTestVersionsJson( pluginsDir, NEO4J_VERSION.getBranch() + ".x" );
setupTestPlugin( versionsJson );
- try(GenericContainer container = createContainerWithTestingPlugin())
+ try ( GenericContainer container = createContainerWithTestingPlugin() )
{
container.start();
- DatabaseIO db = new DatabaseIO(container);
- verifyTestPluginLoaded(db);
+ DatabaseIO db = new DatabaseIO( container );
+ verifyTestPluginLoaded( db );
}
}
@@ -289,13 +318,13 @@ void testSemanticVersioningPlugin_catchesMatchWithX() throws Exception
void testSemanticVersioningPlugin_catchesMatchWithStar() throws Exception
{
Path pluginsDir = temporaryFolderManager.createTempFolder( "plugin-semverMatchesStar-" );
- File versionsJson = createTestVersionsJson( pluginsDir, NEO4J_VERSION.getBranch()+".*");
+ File versionsJson = createTestVersionsJson( pluginsDir, NEO4J_VERSION.getBranch() + ".*" );
setupTestPlugin( versionsJson );
- try(GenericContainer container = createContainerWithTestingPlugin())
+ try ( GenericContainer container = createContainerWithTestingPlugin() )
{
container.start();
- DatabaseIO db = new DatabaseIO(container);
- verifyTestPluginLoaded(db);
+ DatabaseIO db = new DatabaseIO( container );
+ verifyTestPluginLoaded( db );
}
}
@@ -307,15 +336,15 @@ void testSemanticVersioningPlugin_prefersExactMatch() throws Exception
{{
put( NEO4J_VERSION.toString(), PLUGIN_JAR );
put( NEO4J_VERSION.getBranch() + ".x", "notareal.jar" );
- put( NEO4J_VERSION.major+ ".x.x", "notareal.jar" );
- }});
+ put( NEO4J_VERSION.major + ".x.x", "notareal.jar" );
+ }} );
setupTestPlugin( versionsJson );
- try(GenericContainer container = createContainerWithTestingPlugin())
+ try ( GenericContainer container = createContainerWithTestingPlugin() )
{
container.start();
- DatabaseIO db = new DatabaseIO(container);
+ DatabaseIO db = new DatabaseIO( container );
// if semver did not pick exact version match then it will load a non-existent plugin instead and fail.
- verifyTestPluginLoaded(db);
+ verifyTestPluginLoaded( db );
}
}
@@ -323,58 +352,59 @@ void testSemanticVersioningPlugin_prefersExactMatch() throws Exception
public void testPlugin_originalEntrypointLocation() throws Exception
{
Assumptions.assumeTrue( NEO4J_VERSION.isOlderThan( Neo4jVersion.NEO4J_VERSION_500 ),
- "/docker-entrypoint.sh is permanently moved from 5.0 onwards");
+ "/docker-entrypoint.sh is permanently moved from 5.0 onwards" );
Path pluginsDir = temporaryFolderManager.createTempFolder( "plugin-oldEntrypoint-" );
- File versionsJson = createTestVersionsJson( pluginsDir, NEO4J_VERSION.getBranch()+".x" );
+ File versionsJson = createTestVersionsJson( pluginsDir, NEO4J_VERSION.getBranch() + ".x" );
setupTestPlugin( versionsJson );
- try(GenericContainer container = createContainerWithTestingPlugin())
+ try ( GenericContainer container = createContainerWithTestingPlugin() )
{
container.withCreateContainerCmdModifier(
(Consumer) cmd -> cmd.withEntrypoint( "/docker-entrypoint.sh", "neo4j" ) );
container.start();
- DatabaseIO db = new DatabaseIO(container);
- verifyTestPluginLoaded(db);
+ DatabaseIO db = new DatabaseIO( container );
+ verifyTestPluginLoaded( db );
}
}
-
@Test
void testSemanticVersioningLogic() throws Exception
{
- String major = Integer.toString(NEO4J_VERSION.major);
- String minor = Integer.toString(NEO4J_VERSION.minor);
+ String major = Integer.toString( NEO4J_VERSION.major );
+ String minor = Integer.toString( NEO4J_VERSION.minor );
// testing common neo4j name variants
- List neo4jVersions = new ArrayList() {{
- add(NEO4J_VERSION.toString());
- add(NEO4J_VERSION.toString()+"-drop01.1");
- add(NEO4J_VERSION.toString()+"-drop01");
- add(NEO4J_VERSION.toString()+"-beta04");
+ List neo4jVersions = new ArrayList()
+ {{
+ add( NEO4J_VERSION.toString() );
+ add( NEO4J_VERSION.toString() + "-drop01.1" );
+ add( NEO4J_VERSION.toString() + "-drop01" );
+ add( NEO4J_VERSION.toString() + "-beta04" );
}};
- List matchingCases = new ArrayList() {{
+ List matchingCases = new ArrayList()
+ {{
add( NEO4J_VERSION.toString() );
- add( major+'.'+minor+".x" );
- add( major+'.'+minor+".*" );
+ add( major + '.' + minor + ".x" );
+ add( major + '.' + minor + ".*" );
}};
- List nonMatchingCases = new ArrayList() {{
- add( (NEO4J_VERSION.major+1)+'.'+minor+".x" );
- add( (NEO4J_VERSION.major-1)+'.'+minor+".x" );
- add( major+'.'+(NEO4J_VERSION.minor+1)+".x" );
- add( major+'.'+(NEO4J_VERSION.minor-1)+".x" );
- add( (NEO4J_VERSION.major+1)+'.'+minor+".*" );
- add( (NEO4J_VERSION.major-1)+'.'+minor+".*" );
- add( major+'.'+(NEO4J_VERSION.minor+1)+".*" );
- add( major+'.'+(NEO4J_VERSION.minor-1)+".*" );
+ List nonMatchingCases = new ArrayList()
+ {{
+ add( (NEO4J_VERSION.major + 1) + '.' + minor + ".x" );
+ add( (NEO4J_VERSION.major - 1) + '.' + minor + ".x" );
+ add( major + '.' + (NEO4J_VERSION.minor + 1) + ".x" );
+ add( major + '.' + (NEO4J_VERSION.minor - 1) + ".x" );
+ add( (NEO4J_VERSION.major + 1) + '.' + minor + ".*" );
+ add( (NEO4J_VERSION.major - 1) + '.' + minor + ".*" );
+ add( major + '.' + (NEO4J_VERSION.minor + 1) + ".*" );
+ add( major + '.' + (NEO4J_VERSION.minor - 1) + ".*" );
}};
// Asserting every test case means that if there's a failure, all further tests won't run.
// Instead we're running all tests and saving any failed cases for reporting at the end of the test.
List failedTests = new ArrayList();
-
- try(GenericContainer container = createContainerWithTestingPlugin())
+ try ( GenericContainer container = createContainerWithTestingPlugin() )
{
container.withEnv( Neo4jPluginEnv.get(), "" ); // don't need the _testing plugin for this
container.start();
@@ -382,31 +412,30 @@ void testSemanticVersioningLogic() throws Exception
String semverQuery = "echo \"{\\\"neo4j\\\":\\\"%s\\\"}\" | " +
"jq -L/startup --raw-output \"import \\\"semver\\\" as lib; " +
".neo4j | lib::semver(\\\"%s\\\")\"";
- for(String neoVer : neo4jVersions)
+ for ( String neoVer : neo4jVersions )
{
- for(String ver : matchingCases)
+ for ( String ver : matchingCases )
{
- Container.ExecResult out = container.execInContainer( "sh", "-c", String.format( semverQuery, ver, neoVer) );
- if(! out.getStdout().trim().equals( "true" ) )
+ Container.ExecResult out = container.execInContainer( "sh", "-c", String.format( semverQuery, ver, neoVer ) );
+ if ( !out.getStdout().trim().equals( "true" ) )
{
- failedTests.add( String.format( "%s should match %s but did not", ver, neoVer) );
+ failedTests.add( String.format( "%s should match %s but did not", ver, neoVer ) );
}
}
- for(String ver : nonMatchingCases)
+ for ( String ver : nonMatchingCases )
{
- Container.ExecResult out = container.execInContainer( "sh", "-c", String.format( semverQuery, ver, neoVer) );
- if(! out.getStdout().trim().equals( "false" ) )
+ Container.ExecResult out = container.execInContainer( "sh", "-c", String.format( semverQuery, ver, neoVer ) );
+ if ( !out.getStdout().trim().equals( "false" ) )
{
- failedTests.add( String.format( "%s should NOT match %s but did", ver, neoVer) );
+ failedTests.add( String.format( "%s should NOT match %s but did", ver, neoVer ) );
}
}
}
- if(failedTests.size() > 0)
+ if ( failedTests.size() > 0 )
{
- Assertions.fail(failedTests.stream().collect( Collectors.joining("\n")));
+ Assertions.fail( failedTests.stream().collect( Collectors.joining( "\n" ) ) );
}
}
-
}
private class VersionsJsonEntry
@@ -415,18 +444,42 @@ private class VersionsJsonEntry
String jar;
String _testing;
- VersionsJsonEntry(String neo4j)
+ VersionsJsonEntry( String neo4j )
{
this.neo4j = neo4j;
this._testing = "SNAPSHOT";
- this.jar = "http://host.testcontainers.internal:3000/"+PLUGIN_JAR;
+ this.jar = "http://host.testcontainers.internal:3000/" + PLUGIN_JAR;
}
- VersionsJsonEntry(String neo4j, String jar)
+ VersionsJsonEntry( String neo4j, String jar )
{
this.neo4j = neo4j;
this._testing = "SNAPSHOT";
- this.jar = "http://host.testcontainers.internal:3000/"+jar;
+ this.jar = "http://host.testcontainers.internal:3000/" + jar;
+ }
+ }
+
+ @ParameterizedTest( name = "as current user={0}" )
+ @ValueSource( booleans = {true, false} )
+ void testPluginIsMovedToMountedFolderAndIsLoadedCorrectly( boolean asCurrentUser ) throws Exception
+ {
+ Path testOutputFolder = temporaryFolderManager.createTempFolder( "mount-plugins-only-" );
+ try ( GenericContainer container = setupContainerWithUser( asCurrentUser ) )
+ {
+ var pluginsFolder = temporaryFolderManager.createTempFolderAndMountAsVolume( container, "plugins", "/plugins", testOutputFolder );
+ container.withEnv( "NEO4J_PLUGINS", "[\"bloom\"]" );
+ container.start();
+
+ Assertions.assertTrue( pluginsFolder.resolve( "bloom.jar" ).toFile().exists(), "Did not find bloom.jar in plugins folder" );
+ assertBloomIsLoaded( container );
}
}
+
+ void assertBloomIsLoaded( GenericContainer container )
+ {
+ DatabaseIO databaseIO = new DatabaseIO( container );
+
+ var result = databaseIO.runCypherQuery( "neo4j", "none", "SHOW PROCEDURES YIELD name, description, signature WHERE name STARTS WITH 'bloom'" );
+ Assertions.assertFalse( result.isEmpty(), "Bloom procedures not found in neo4j installation" );
+ }
}
diff --git a/src/test/java/com/neo4j/docker/utils/TemporaryFolderManager.java b/src/test/java/com/neo4j/docker/utils/TemporaryFolderManager.java
index ba520897..43e9b49b 100644
--- a/src/test/java/com/neo4j/docker/utils/TemporaryFolderManager.java
+++ b/src/test/java/com/neo4j/docker/utils/TemporaryFolderManager.java
@@ -64,7 +64,7 @@ public void beforeEach( ExtensionContext extensionContext ) throws Exception
extensionContext.getTestMethod().get().getName();
if(!extensionContext.getDisplayName().startsWith( extensionContext.getTestMethod().get().getName() ))
{
- outputFolderNamePrefix += "_" + extensionContext.getDisplayName() + "-";
+ outputFolderNamePrefix += "_" + extensionContext.getDisplayName().replace( "/", "\\/" ) + "-";
}
else
{