testcontainers-java: Permissions error with config mounted into MySQL container

11:42:05.293 [Test worker] ERROR 🐳 [mysql:5.7.22] - Container log output (if any) will follow:
11:42:05.304 [tc-okhttp-stream-474827747] INFO 🐳 [mysql:5.7.22] - STDERR: 
11:42:05.305 [tc-okhttp-stream-474827747] INFO 🐳 [mysql:5.7.22] - STDERR: ERROR: mysqld failed while attempting to check config
11:42:05.305 [tc-okhttp-stream-474827747] INFO 🐳 [mysql:5.7.22] - STDERR: command was: "mysqld --verbose --help"
11:42:05.305 [tc-okhttp-stream-474827747] INFO 🐳 [mysql:5.7.22] - STDERR: 
11:42:05.306 [tc-okhttp-stream-474827747] INFO 🐳 [mysql:5.7.22] - STDERR: mysqld: Can't read dir of '/etc/mysql/conf.d/' (Errcode: 13 - Permission denied)
11:42:05.306 [tc-okhttp-stream-474827747] INFO 🐳 [mysql:5.7.22] - STDERR: mysqld: [ERROR] Fatal error in defaults handling. Program aborted!

We see this in the log (look on the null): Cmd: com.github.dockerjava.core.command.CopyArchiveToContainerCmdImpl@2c8c6bb[cp ,<null>, ,6ca1ea3ce4b2582536fac69ceeb991670930b1ac65a6b2206c1ab88f3c990b65,:,/etc/mysql/] 11:41:45.064 [Test worker] INFO 🐳 [mysql:5.7.22] - Starting container with ID: 6ca1ea3ce4b2582536fac69ceeb991670930b1ac65a6b2206c1ab88f3c990b65

If we override the MySQLContainer class and just rmove this line from the configure function: optionallyMapResourceParameterAsVolume(MY_CNF_CONFIG_OVERRIDE_PARAM_NAME, "/etc/mysql/conf.d", "mysql-default-conf");

Everything is working. Looks like it is copying invalid file to the container

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 1
  • Comments: 16 (3 by maintainers)

Most upvoted comments

I am configuring my Test Container with an URL

spring.datasource.url=jdbc:tc:mysql:5.7.28:///scm_ccs?user=root&password=&TC_TMPFS=/testtmpfs-ccs:rw&TC_MY_CNF=mysql.test.cnf&TC_INITSCRIPT=tc_init_mysql.sql

When the container runs it is stopping with mysqld: Can’t read dir of ‘/etc/mysql/conf.d’ (Errcode 20 - Not a directory)

2021-10-26 17_52_15-Container instance

When I looked at the source code and one of the work arounds I was wondering if there is not simply a / missing in the file org.testcontainers.containers.MySQLContainer in line 64 at the end of the string “/etc/mysql/conf.d”. Shouldn’t it be “/etc/mysql/conf.d/”?

I experienced the same issue. @rottenha is correct about the root cause. Just want to give a workaround before the fix. Before starting the container, you can set the MY_CNF_CONFIG_OVERRIDE_PARAM_NAME(which value is TC_MY_CNF) to null in the container parameter, like this:

MySQLContainer mysql = new MySQLContainer("mysql:5.7");
mysql.addParameter("TC_MY_CNF", null);
mysql.start();

This is actually a bug in the testcontainers implementation in general, checking 1.14.3 and 1.15-rc2, that it almost always never explicitly sets the file permission on resources copied from the host system into a container, but instead relies on the file permissions being correctly set on the host system. This is a major problem, affecting multiple containers when trying to run the test suites.

In this particular case, regarding MySQLContainer, the automatic configuration is done in methods MySQLContainer.configure() and parent class method JdbcDatabaseContainer.optionallyMapResourceParameterAsVolume(), which internally calls MountableFile.forClasspathResource(resourceName) instead of MountableFile.forClasspathResource(resourceName, fileMode).

A sound approach and a proper fix is to explicitly set the file permissions in the implementation class MySQLContainer.configure() that knows about the requirements and change the calls appropriately.

Second, the approach of implicit file permissions in MountableFile.forClassPathResource() should be deprecated in favour of explicitly setting them, i.e. normal use case should be to explicitly set them, unless explicitly file permission are set to be default from the host system.

I am also facing this problem. A workaround which seems to work OK for me and is in the spirit of “copy config files in, rather than volume mount”, is something like:

 mysql = (MySQLContainer) new MySQLContainer().withDatabaseName("some_database")
        .withCopyFileToContainer(MountableFile.forClasspathResource("db/testMySQL.cnf"), "/etc/mysql/conf.d/")
        .withLogConsumer(logConsumer);

I haven’t dug into this much further than that but I assume the ultimate PR would do something similar in the MySQLContainer class?