Skip to content

Commit

Permalink
Revert "use new 'neo4j dry-run' mechanism to start neo4j process in 4…
Browse files Browse the repository at this point in the history
….3 (#295)"

This reverts commit b3f3a26.
  • Loading branch information
glindroth committed Jun 3, 2021
1 parent b3f3a26 commit e0c2d43
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 151 deletions.
27 changes: 6 additions & 21 deletions docker-image-src/4.3/docker-entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -501,30 +501,15 @@ if [ "${cmd}" == "dump-config" ]; then
exit 0
fi

# this prints out a command for us to run.
# the command is something like: `java ...[lots of java options]... neo4j.mainClass ...[some neo4j options]...`
function get_neo4j_run_cmd {

local extraArgs=()

if [ "${EXTENDED_CONF+"yes"}" == "yes" ]; then
extraArgs+=("--expand-commands")
fi

if running_as_root; then
gosu neo4j:neo4j neo4j console --dry-run "${extraArgs[@]}"
else
neo4j console --dry-run "${extraArgs[@]}"
fi
}

# Use su-exec to drop privileges to neo4j user
# Note that su-exec, despite its name, does not replicate the
# functionality of exec, so we need to use both
if [ "${cmd}" == "neo4j" ]; then
# separate declaration and use of get_neo4j_run_cmd so that error codes are correctly surfaced
neo4j_console_cmd="$(get_neo4j_run_cmd)"
eval "${exec_cmd} ${neo4j_console_cmd?:No Neo4j command was generated}"
if [ "${EXTENDED_CONF+"yes"}" == "yes" ]; then
${exec_cmd} neo4j console --expand-commands
else
${exec_cmd} neo4j console
fi
else
${exec_cmd} "$@"
${exec_cmd} "$@"
fi
160 changes: 31 additions & 129 deletions src/test/java/com/neo4j/docker/TestExtendedConf.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,16 @@
import com.neo4j.docker.utils.SetContainerUser;
import com.neo4j.docker.utils.TestSettings;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testcontainers.containers.BindMode;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.output.OutputFrame;
import org.testcontainers.containers.output.Slf4jLogConsumer;
import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy;
import org.testcontainers.containers.wait.strategy.Wait;

import java.io.BufferedReader;
Expand All @@ -26,72 +24,47 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.PosixFilePermission;
import java.time.Instant;
import java.util.HashSet;
import java.util.Optional;
import java.util.concurrent.TimeoutException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class TestExtendedConf
{
private static final Logger log = LoggerFactory.getLogger( TestExtendedConf.class );

@BeforeAll
static void ensureFeaturePresent()
{
Assumptions.assumeTrue( TestSettings.NEO4J_VERSION.isAtLeastVersion( new Neo4jVersion( 4,2,1 ) ),
Assumptions.assumeTrue( TestSettings.NEO4J_VERSION.isAtLeastVersion( new Neo4jVersion( 4,2,0 ) ),
"Extended configuration feature not available before 4.2" );
}

protected GenericContainer createContainerNoWait(String password)
{
GenericContainer container = createContainer( password );
// we have to override the default wait strategy with something that will return more quickly
container.setWaitStrategy( new LogMessageWaitStrategy().withRegEx( ".+" ) );
return container;
}

protected GenericContainer createContainer(String password)
protected GenericContainer createContainer()
{
return new GenericContainer(TestSettings.IMAGE_ID)
.withEnv("NEO4J_AUTH", password == null || password.isEmpty() ? "none" : "neo4j/" + password)
.withEnv("NEO4J_ACCEPT_LICENSE_AGREEMENT", "yes")
.withEnv("NEO4J_AUTH", "none")
.withEnv("NEO4J_ACCEPT_LICENSE_AGREEMENT", "yes")
.withEnv( "EXTENDED_CONF", "yeppers" )
.withExposedPorts(7474, 7687)
.withExposedPorts(7474, 7687)
.waitingFor( Wait.forHttp( "/" ).forPort( 7474 ).forStatusCode( 200 ) )
.withLogConsumer(new Slf4jLogConsumer( log ));
.withLogConsumer(new Slf4jLogConsumer( log));
}


@ParameterizedTest
@ValueSource(strings = {"", "secretN30"})
public void shouldStartWithExtendedConf(String password)
@Test
public void shouldStartWithExtendedConf()
{
try(GenericContainer container = createContainer(password))
try(GenericContainer container = createContainer())
{
container.setWaitStrategy( Wait.forHttp( "/" ).forPort( 7474 ).forStatusCode( 200 ) );
container.start();

Assertions.assertTrue( container.isRunning() );
assertPasswordChangedLogIsCorrect( password, container );
}
}

private void assertPasswordChangedLogIsCorrect( String password, GenericContainer container )
{
if ( password.isEmpty()) {
Assertions.assertFalse( container.getLogs( OutputFrame.OutputType.STDOUT).contains( "Changed password for user 'neo4j'." ) );
} else {
Assertions.assertTrue( container.getLogs( OutputFrame.OutputType.STDOUT).contains( "Changed password for user 'neo4j'." ) );
}
}
}

@ParameterizedTest
@ValueSource(strings = {"", "secretN30"})
void testReadsTheExtendedConfFile_defaultUser(String password) throws Exception
@Ignore
@Test
void testReadsTheExtendedConfFile_defaultUser() throws Exception
{
// set up test folders
Path testOutputFolder = HostFileSystemOperations.createTempFolder( "extendedConfIsRead-" );
Expand All @@ -102,79 +75,17 @@ void testReadsTheExtendedConfFile_defaultUser(String password) throws Exception
Path confFile = Paths.get( "src", "test", "resources", "confs", "ExtendedConf.conf" );
Files.copy( confFile, confFolder.resolve( "neo4j.conf" ) );
setFileOwnerToNeo4j( confFolder.resolve( "neo4j.conf" ) );
chmodConfFilePermissions( confFolder.resolve( "neo4j.conf" ) );
chmod600( confFolder.resolve( "neo4j.conf" ) );

// start container
try(GenericContainer container = createContainer(password))
{
runContainerAndVerify( container, confFolder, logsFolder, password );
}
}

@ParameterizedTest
@ValueSource( strings = {"", "secretN30"} )
void testInvalidExtendedConfFile_nonRootUser( String password ) throws Exception
{
// set up test folders
Path testOutputFolder = HostFileSystemOperations.createTempFolder( "extendedConfIsRead-" );
Path confFolder = HostFileSystemOperations.createTempFolder( "conf-", testOutputFolder );
Path logsFolder = HostFileSystemOperations.createTempFolder( "logs-", testOutputFolder );

// copy configuration file and set permissions
Path confFile = Paths.get( "src", "test", "resources", "confs", "InvalidExtendedConf.conf" );
Files.copy( confFile, confFolder.resolve( "neo4j.conf" ) );
chmodConfFilePermissions( confFolder.resolve( "neo4j.conf" ) );

try ( GenericContainer container = createContainerNoWait( password ) )
{
SetContainerUser.nonRootUser( container );
container.withFileSystemBind( "/etc/passwd", "/etc/passwd", BindMode.READ_ONLY );
container.withFileSystemBind( "/etc/group", "/etc/group", BindMode.READ_ONLY );
HostFileSystemOperations.mountHostFolderAsVolume( container, confFolder, "/conf" );
container.start();

// expect the container to exit promptly
Instant deadline = Instant.now().plusSeconds( 60 );
while ( container.isRunning() )
{
Thread.sleep( 1000 );
if ( Instant.now().isAfter( deadline ) )
{
throw new TimeoutException( "Timed out waiting for container to exit" );
}
}
Assertions.assertFalse( container.isRunning() );

// An error occurred so we expect a non-zero exit code
Assertions.assertNotEquals( 0, container.getCurrentContainerInfo().getState().getExitCodeLong() );

String logs = container.getLogs();

// check that error messages from neo4j are visible in docker logs
Assertions.assertTrue( logs.contains( "Error evaluating value for setting 'dbms.logs.http.rotation.keep_number'" ) );

// check that error messages from the command that failed are visible in docker logs
Assertions.assertTrue( logs.contains( "this is an error message from inside neo4j config command expansion" ) );

// check that the error is only encountered once (i.e. we quit the docker entrypoint the first time it was encountered)
Assertions.assertEquals( 1, countOccurrences( Pattern.compile( "Error evaluating value for setting" ), logs ) );
}
}

private int countOccurrences( Pattern pattern, String inString )
{
Matcher matcher = pattern.matcher( inString );
int count = 0;
while ( matcher.find() )
try(GenericContainer container = createContainer())
{
count = count + 1;
runContainerAndVerify( container, confFolder, logsFolder );
}
return count;
}

@ParameterizedTest
@ValueSource(strings = {"", "secretN30"})
void testReadsTheExtendedConfFile_nonRootUser(String password) throws Exception
@Test
void testReadsTheExtendedConfFile_nonRootUser() throws Exception
{
// set up test folders
Path testOutputFolder = HostFileSystemOperations.createTempFolder( "extendedConfIsRead-" );
Expand All @@ -184,18 +95,18 @@ void testReadsTheExtendedConfFile_nonRootUser(String password) throws Exception
// copy configuration file and set permissions
Path confFile = Paths.get( "src", "test", "resources", "confs", "ExtendedConf.conf" );
Files.copy( confFile, confFolder.resolve( "neo4j.conf" ) );
chmodConfFilePermissions( confFolder.resolve( "neo4j.conf" ) );
chmod600( confFolder.resolve( "neo4j.conf" ) );

try(GenericContainer container = createContainer(password))
try(GenericContainer container = createContainer())
{
SetContainerUser.nonRootUser( container );
container.withFileSystemBind( "/etc/passwd", "/etc/passwd", BindMode.READ_ONLY );
container.withFileSystemBind( "/etc/group", "/etc/group", BindMode.READ_ONLY );
runContainerAndVerify( container, confFolder, logsFolder, password );
runContainerAndVerify( container, confFolder, logsFolder );
}
}

private void runContainerAndVerify(GenericContainer container, Path confFolder, Path logsFolder, String password) throws Exception
private void runContainerAndVerify(GenericContainer container, Path confFolder, Path logsFolder) throws Exception
{
HostFileSystemOperations.mountHostFolderAsVolume( container, confFolder, "/conf" );
HostFileSystemOperations.mountHostFolderAsVolume( container, logsFolder, "/logs" );
Expand All @@ -210,25 +121,16 @@ private void runContainerAndVerify(GenericContainer container, Path confFolder,
Optional<String> isMatch = lines.filter( s -> s.contains("dbms.logs.http.rotation.keep_number=20")).findFirst();
lines.close();
Assertions.assertTrue( isMatch.isPresent(), "dbms.max_databases was not set correctly");

//Check the password was changed if set
assertPasswordChangedLogIsCorrect( password, container );
}

private void chmodConfFilePermissions( Path file ) throws IOException
private void chmod600(Path file) throws IOException
{

HashSet<PosixFilePermission> permissions = new HashSet<PosixFilePermission>()
{{
add( PosixFilePermission.OWNER_READ );
add( PosixFilePermission.OWNER_WRITE );
}};

if ( TestSettings.NEO4J_VERSION.isAtLeastVersion( new Neo4jVersion( 4, 3, 0 ) ) )
{
permissions.add( PosixFilePermission.GROUP_READ );
}
Files.setPosixFilePermissions( file, permissions );
Files.setPosixFilePermissions( file,
new HashSet<PosixFilePermission>()
{{
add( PosixFilePermission.OWNER_READ );
add( PosixFilePermission.OWNER_WRITE );
}} );
}

private void setFileOwnerToNeo4j(Path file) throws Exception
Expand Down
1 change: 0 additions & 1 deletion src/test/resources/confs/InvalidExtendedConf.conf

This file was deleted.

0 comments on commit e0c2d43

Please sign in to comment.