2929import org .apache .commons .compress .archivers .tar .TarArchiveInputStream ;
3030import org .apache .commons .compress .archivers .tar .TarArchiveOutputStream ;
3131import org .apache .commons .compress .utils .IOUtils ;
32+ import org .apache .commons .io .FileUtils ;
3233import org .apache .commons .lang .StringUtils ;
3334import org .jetbrains .annotations .NotNull ;
3435import org .jetbrains .annotations .Nullable ;
7576import java .lang .reflect .Method ;
7677import java .lang .reflect .UndeclaredThrowableException ;
7778import java .nio .charset .Charset ;
79+ import java .nio .file .Files ;
7880import java .nio .file .Path ;
7981import java .time .Duration ;
8082import java .time .Instant ;
8789import java .util .LinkedHashSet ;
8890import java .util .List ;
8991import java .util .Map ;
92+ import java .util .Map .Entry ;
9093import java .util .Optional ;
9194import java .util .Set ;
9295import java .util .concurrent .ExecutionException ;
9699import java .util .function .Consumer ;
97100import java .util .stream .Collectors ;
98101import java .util .stream .Stream ;
102+ import java .util .zip .Adler32 ;
103+ import java .util .zip .Checksum ;
99104
100105import static com .google .common .collect .Lists .newArrayList ;
101106import static org .testcontainers .utility .CommandLine .runShellCommand ;
@@ -116,6 +121,8 @@ public class GenericContainer<SELF extends GenericContainer<SELF>>
116121
117122 static final String HASH_LABEL = "org.testcontainers.hash" ;
118123
124+ static final String COPIED_FILES_HASH_LABEL = "org.testcontainers.copied_files.hash" ;
125+
119126 /*
120127 * Default settings
121128 */
@@ -353,6 +360,11 @@ private void tryStart(Instant startedAt) {
353360 }
354361
355362 if (TestcontainersConfiguration .getInstance ().environmentSupportsReuse ()) {
363+ createCommand .getLabels ().put (
364+ COPIED_FILES_HASH_LABEL ,
365+ Long .toHexString (hashCopiedFiles ().getValue ())
366+ );
367+
356368 String hash = hash (createCommand );
357369
358370 containerId = findContainerForReuse (hash ).orElse (null );
@@ -381,7 +393,7 @@ private void tryStart(Instant startedAt) {
381393 if (!reused ) {
382394 containerId = createCommand .exec ().getId ();
383395
384- // TODO add to the hash
396+ // TODO use single "copy" invocation (and calculate an hash of the resulting tar archive)
385397 copyToFileContainerPathMap .forEach (this ::copyFileToContainer );
386398 }
387399
@@ -474,6 +486,36 @@ private void tryStart(Instant startedAt) {
474486 }
475487 }
476488
489+ @ VisibleForTesting
490+ Checksum hashCopiedFiles () {
491+ Checksum checksum = new Adler32 ();
492+ copyToFileContainerPathMap .entrySet ().stream ().sorted (Entry .comparingByValue ()).forEach (entry -> {
493+ byte [] pathBytes = entry .getValue ().getBytes ();
494+ // Add path to the hash
495+ checksum .update (pathBytes , 0 , pathBytes .length );
496+
497+ File file = new File (entry .getKey ().getResolvedPath ());
498+ checksumFile (file , checksum );
499+ });
500+ return checksum ;
501+ }
502+
503+ @ VisibleForTesting
504+ @ SneakyThrows (IOException .class )
505+ void checksumFile (File file , Checksum checksum ) {
506+ Path path = file .toPath ();
507+ checksum .update (MountableFile .getUnixFileMode (path ));
508+ if (file .isDirectory ()) {
509+ try (Stream <Path > stream = Files .walk (path )) {
510+ stream .filter (it -> it != path ).forEach (it -> {
511+ checksumFile (it .toFile (), checksum );
512+ });
513+ }
514+ } else {
515+ FileUtils .checksum (file , checksum );
516+ }
517+ }
518+
477519 @ UnstableAPI
478520 @ SneakyThrows (JsonProcessingException .class )
479521 final String hash (CreateContainerCmd createCommand ) {
@@ -708,7 +750,7 @@ private void applyConfiguration(CreateContainerCmd createCommand) {
708750
709751 Set <Link > allLinks = new HashSet <>();
710752 Set <String > allLinkedContainerNetworks = new HashSet <>();
711- for (Map . Entry <String , LinkableContainer > linkEntries : linkedContainers .entrySet ()) {
753+ for (Entry <String , LinkableContainer > linkEntries : linkedContainers .entrySet ()) {
712754
713755 String alias = linkEntries .getKey ();
714756 LinkableContainer linkableContainer = linkEntries .getValue ();
0 commit comments