I want to run SchemaSpy container against PosgtreSQL database to generate the documentation. Here is the approach I came up with:
/bin/sh
).execInContainer
to run the SchemaSpy app itself.execInContainer
to put the result changes in a tarball.copyFileFromContainer
to copy the tarball from the container to the OS.Here is the source code:
@DBTest
class SchemaSpyTest extends IntegrationSuite {
private final GenericContainer<?> SCHEMA_SPY =
new GenericContainer<>(DockerImageName.parse("schemaspy/schemaspy:6.1.0"))
.withNetworkAliases("schemaspy")
.withCreateContainerCmdModifier(cmd -> cmd.withEntrypoint("/bin/sh"))
.withNetwork(NETWORK)
.withLogConsumer(new Slf4jLogConsumer(LoggerFactory.getLogger("SchemaSpy")));
@Test
@SneakyThrows
void test() {
SCHEMA_SPY.start();
SCHEMA_SPY.execInContainer(
"java -jar schemaspy-6.1.0.jar -t pgsql11 -db %s -host postgres -u %s -p %s -debug"
.formatted(POSTGRES.getDatabaseName(), POSTGRES.getUsername(), POSTGRES.getPassword())
);
SCHEMA_SPY.execInContainer("tar", "-czvf", "/output/output.tar.gz", "/output");
SCHEMA_SPY.copyFileFromContainer(
"/output/output.tar.gz",
Path.of(getClass().getResource("/").getPath(), "output.tar.gz")
.toAbsolutePath()
.toString()
);
SCHEMA_SPY.stop();
}
}
@Testcontainers
public class IntegrationSuite {
protected static final Network NETWORK = Network.newNetwork();
@Container
protected static final PostgreSQLContainer<?> POSTGRES =
new PostgreSQLContainer<>(DockerImageName.parse("postgres:13.5"))
.withNetworkAliases("postgres")
.withNetwork(NETWORK);
@DynamicPropertySource
static void setProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", POSTGRES::getJdbcUrl);
registry.add("spring.datasource.username", POSTGRES::getUsername);
registry.add("spring.datasource.password", POSTGRES::getPassword);
}
}
I expected that /bin/sh
entrypoint will make container run indefinitely until I manually stop it. Actually, that's what happens on SCHEMA_SPY.start()
row:
/bin/sh
command executes.So, execInContainer
operations fail because the container is already stopped in that moment.
Is there are any workaround to overcome this issue?
Finally, I managed to run SchemaSpy with Testcontainers. Look at the code below:
@DBTest
class SchemaCrawlerTest extends IntegrationSuite {
@Test
@SneakyThrows
void schemaSpy() {
// The @Cleanup is lombok annotation that generates try with resources
@Cleanup final var schemaSpy =
new GenericContainer<>(DockerImageName.parse("schemaspy/schemaspy:6.1.0"))
.withNetworkAliases("schemaspy")
.withNetwork(NETWORK)
.withLogConsumer(new Slf4jLogConsumer(LoggerFactory.getLogger("SchemaSpy")))
.withCreateContainerCmdModifier(cmd -> cmd.withEntrypoint(""))
.withCommand("sleep 500000");
schemaSpy.start();
final var generateDocCommand = schemaSpy.execInContainer(
"java",
"-jar", "/schemaspy-6.1.0.jar",
"-t", "pgsql11",
"-db", POSTGRES.getDatabaseName(),
"-host", "postgres",
"-u", POSTGRES.getUsername(),
"-p", POSTGRES.getPassword(),
"-o", "/output",
"-dp", "/drivers_inc",
"-debug"
);
if (generateDocCommand.getExitCode() != 0) {
fail("Output: %s, error: %s".formatted(generateDocCommand.getStdout(), generateDocCommand.getStderr()));
}
schemaSpy.execInContainer("tar", "-czvf", "/output/output.tar.gz", "/output");
schemaSpy.copyFileFromContainer(
"/output/output.tar.gz",
Path.of(getClass().getResource("/").getPath(), "output.tar.gz")
.toAbsolutePath()
.toString()
);
schemaSpy.stop();
}
}
@Testcontainers
public class IntegrationSuite {
protected static final Network NETWORK = Network.newNetwork();
@Container
protected static final PostgreSQLContainer<?> POSTGRES =
new PostgreSQLContainer<>(DockerImageName.parse("postgres:13.5"))
.withNetworkAliases("postgres")
.withNetwork(NETWORK);
@DynamicPropertySource
static void setProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", POSTGRES::getJdbcUrl);
registry.add("spring.datasource.username", POSTGRES::getUsername);
registry.add("spring.datasource.password", POSTGRES::getPassword);
}
}
The idea is simple:
sleep 500000
. So, it doesn't stop automatically.execInContainer
method.Now you can unpack the archive and host it on GitHub/GitLab pages