Skip to content

Commit

Permalink
Test upgrading from the latest published minor version to the current…
Browse files Browse the repository at this point in the history
… dev version
  • Loading branch information
Andrew Jefferson committed Jun 15, 2020
1 parent e87f9aa commit c050d4e
Show file tree
Hide file tree
Showing 2 changed files with 155 additions and 6 deletions.
160 changes: 154 additions & 6 deletions src/test/java/com/neo4j/docker/TestUpgrade.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,29 @@
import com.neo4j.docker.utils.HostFileSystemOperations;
import com.neo4j.docker.utils.Neo4jVersion;
import com.neo4j.docker.utils.TestSettings;
import org.eclipse.collections.impl.block.factory.Comparators;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.output.Slf4jLogConsumer;
import org.testcontainers.containers.wait.strategy.Wait;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class TestUpgrade
{
Expand All @@ -22,11 +37,14 @@ public class TestUpgrade
private GenericContainer makeContainer( String image )
{
GenericContainer container = new GenericContainer( image );
container.withEnv( "NEO4J_AUTH", user + "/" + password )
.withEnv( "NEO4J_ACCEPT_LICENSE_AGREEMENT", "yes" )
.withExposedPorts( 7474 )
.withExposedPorts( 7687 )
.withLogConsumer( new Slf4jLogConsumer( log ) );
container = container.withEnv( "NEO4J_AUTH", user + "/" + password )
.withEnv( "NEO4J_ACCEPT_LICENSE_AGREEMENT", "yes" )
.withExposedPorts( 7474 )
.withExposedPorts( 7687 )
.withLogConsumer( new Slf4jLogConsumer( log ) );
container.setWaitStrategy( Wait.forHttp( "/" ).forPort( 7474 ).forStatusCode( 200 ) );
container = container.withStartupTimeout( Duration.ofMinutes( 2 ) );
;
return container;
}

Expand Down Expand Up @@ -59,5 +77,135 @@ void canUpgradeFromBeforeFilePermissionFix35() throws Exception
}
}

// todo add test for 4.0 when I can figure out how to close a container cleanly
@Test
void canUpgradeFromReleasedVersion() throws Exception
{
var targetNeo4jVersion = TestSettings.NEO4J_VERSION;

// If this is the very first in a new major series (i.e. a .0.0 release) then this test isn't expected to work
Assumptions.assumeFalse( targetNeo4jVersion.major == 0 && targetNeo4jVersion.minor == 0 );

// TODO: update this when moving to the next minor release
// I am taking a guess here that we will have published 4.1.0 by 1st of July
if ( Instant.now().isBefore( Instant.parse( "2020-07-01T00:00:00.00Z" ) ) )
{
// This sort-of-hack is necessary when we cut a new minor branch before the previous minor branch has been published to dockerhub.
Assumptions.assumeTrue( Neo4jVersion.NEO4J_VERSION_420.isNewerThan( targetNeo4jVersion ) );
}
String fromImageName = dockerImageToUpgradeFrom( targetNeo4jVersion );

var testMountDirPrefix = String.format( "upgrade-from-%s", fromImageName ).replaceAll( ":", "_" );
Path testMountDir = HostFileSystemOperations.createTempFolder( testMountDirPrefix );

// TODO: test /plugins and /ssl directories
Map<String,@NotNull Path> readonlyMounts = createDirectories( testMountDir, List.of( "/conf" ) );
Map<String,@NotNull Path> writableMounts = createDirectories( testMountDir, List.of( "/data", "/logs", "/metrics" ) );

// write a value to neo4j.conf
try ( var confFile = new FileWriter( readonlyMounts.get( "/conf" ).resolve( "neo4j.conf" ).toFile() ) )
{
confFile.write( "dbms.memory.pagecache.size=8m" );
}

var allMounts = new HashMap<String,Path>();
allMounts.putAll( readonlyMounts );
allMounts.putAll( writableMounts );

try ( var container = makeContainer( fromImageName ) )
{
allMounts.forEach( ( containerMount, hostFolder ) -> HostFileSystemOperations.mountHostFolderAsVolume( container, hostFolder, containerMount ) );

final var startTime = Instant.now().toEpochMilli();
container.start();
DatabaseIO db = new DatabaseIO( container );
db.putInitialDataIntoContainer( user, password );

validateConfig( db );

// validate that writes actually went to the mounted directories
writableMounts.forEach( ( containerMount, hostFolder ) -> assertDirectoryModifiedSince( hostFolder, startTime ) );
container.stop();
}

// when
try ( var container = makeContainer( TestSettings.IMAGE_ID ) )
{
allMounts.forEach( ( k, v ) -> HostFileSystemOperations.mountHostFolderAsVolume( container, v, k ) );

final var startTime = Instant.now().toEpochMilli();
container.start();
DatabaseIO db = new DatabaseIO( container );

// then
// verify that data from previous version is still present (and that reads work)
db.verifyDataInContainer( user, password );

// verify config still loaded
validateConfig( db );

// check that writes work
db.putInitialDataIntoContainer( user, password );

// validate that writes updated the mounted directories
writableMounts.forEach( ( containerMount, hostFolder ) -> assertDirectoryModifiedSince( hostFolder, startTime ) );
container.stop();
}
}

@NotNull
private Map<String,@NotNull Path> createDirectories( Path testMountDirectory, List<String> strings )
{
return strings
.stream()
.collect( Collectors.toMap( c -> c, c -> createDirectory( testMountDirectory, c.replaceFirst( "/", "" ) ) ) );
}

private String dockerImageToUpgradeFrom( Neo4jVersion targetNeo4jVersion )
{
// The most recent minor release that we expect to already have been released.
var minorVersionToUpgradeFrom = targetNeo4jVersion.patch == 0 ? targetNeo4jVersion.minor - 1 : targetNeo4jVersion.minor;

// given
return String.format( "neo4j:%d.%d%s", targetNeo4jVersion.major, minorVersionToUpgradeFrom,
(TestSettings.EDITION == TestSettings.Edition.ENTERPRISE) ? "-enterprise" : "" );
}

private void validateConfig( DatabaseIO db )
{
var configValue = db.runCypherProcedure( user, password,
"CALL dbms.listConfig() YIELD name, value WHERE name='dbms.memory.pagecache.size' RETURN value" );
Assertions.assertEquals( "8m", configValue.get( 0 ).get( "value" ).asString() );
}

/**
* Checks that the {@code mountDirectory} contains at lease one file that has been modified after the {@code startTimestamp}
*
* @param mountDirectory path to local directory mounted into the docker container.
* @param startTimestamp timestamp (milliseconds since epoch) to check for modifications since.
*/
private void assertDirectoryModifiedSince( Path mountDirectory, long startTimestamp )
{
log.info( "Checking {}", mountDirectory );
var dir = mountDirectory.toFile();
Assertions.assertTrue( dir.isDirectory() );
var files = dir.listFiles();
Assertions.assertTrue( files.length > 0 );
var lastModified = Arrays.stream( files ).max( Comparators.byLongFunction( File::lastModified ) );
Assertions.assertTrue( lastModified.get().lastModified() > startTimestamp );
}

@NotNull
private Path createDirectory( Path mount, String s )
{
var subfolder = mount.resolve( s );
log.info( "created folder " + subfolder.toString() + " to test upgrade" );
try
{
return Files.createDirectories( subfolder );
}
catch ( IOException e )
{
throw new RuntimeException( e );
}
}
}
1 change: 1 addition & 0 deletions src/test/java/com/neo4j/docker/utils/Neo4jVersion.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public class Neo4jVersion
//public static final Neo4jVersion LATEST_2X_VERSION = new Neo4jVersion(2,3,12);
//public static final Neo4jVersion LATEST_32_VERSION = new Neo4jVersion(3,2,14);
public static final Neo4jVersion NEO4J_VERSION_400 = new Neo4jVersion(4,0,0);
public static final Neo4jVersion NEO4J_VERSION_420 = new Neo4jVersion(4,2,0);

public final int major;
public final int minor;
Expand Down

0 comments on commit c050d4e

Please sign in to comment.