tiny-blocks / docker-container
Manage Docker containers programmatically, simplifying the creation, running, and interaction with containers.
Requires
- php: ^8.5
- symfony/process: ^7.4
- tiny-blocks/collection: ^2.0
- tiny-blocks/ksuid: ^1.5
Requires (Dev)
- ext-pdo: *
- dg/bypass-finals: ^1.9
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^11.5
- squizlabs/php_codesniffer: ^4.0
README
Overview
The DockerContainer library provides an interface and implementations to manage Docker containers programmatically.
It simplifies the creation, execution, and interaction with containers, such as adding network configurations, mapping
ports, setting environment variables, and executing commands inside containers.
Designed specifically to support unit tests and integration tests, the library enables developers to simulate
and manage containerized environments with minimal effort, ensuring a seamless testing workflow.
Installation
composer require tiny-blocks/docker-container
How to use
Creating a container
Creates a container from a specified image and optionally a name.
The from method initializes a new container instance with an image and an optional name for identification.
$container = GenericDockerContainer::from(image: 'php:8.3-fpm', name: 'my-container');
Running a container
The run method starts a container.
Optionally, it allows you to execute commands within the container after it has started and define a condition to wait
for using a ContainerWaitAfterStarted instance.
Example with no commands or conditions:
$container->run();
Example with commands only:
$container->run(commands: ['ls', '-la']);
Example with commands and a wait condition:
$container->run(commands: ['ls', '-la'], waitAfterStarted: ContainerWaitForTime::forSeconds(seconds: 5));
Running a container if it doesn't exist
The runIfNotExists method starts a container only if it doesn't already exist.
$container->runIfNotExists();
Example with commands and a wait condition:
$container->runIfNotExists(commands: ['ls', '-la'], waitAfterStarted: ContainerWaitForTime::forSeconds(seconds: 5));
Setting network
The withNetwork method connects the container to a specified Docker network by name.
$container->withNetwork(name: 'my-network');
Setting port mappings
Maps ports between the host and the container. Multiple port mappings are supported.
$container->withPortMapping(portOnHost: 9000, portOnContainer: 9000); $container->withPortMapping(portOnHost: 8080, portOnContainer: 80);
Setting volumes mappings
Maps a volume from the host to the container.
$container->withVolumeMapping(pathOnHost: '/path/on/host', pathOnContainer: '/path/in/container');
Setting environment variables
Sets environment variables inside the container.
$container->withEnvironmentVariable(key: 'XPTO', value: '123');
Disabling auto-remove
Prevents the container from being automatically removed when stopped.
$container->withoutAutoRemove();
Copying files to a container
Copies files or directories from the host machine to the container.
$container->copyToContainer(pathOnHost: '/path/to/files', pathOnContainer: '/path/in/container');
Waiting for a condition
The withWaitBeforeRun method allows the container to pause its execution until a specified condition is met before
starting. A timeout prevents the wait from blocking indefinitely.
$container->withWaitBeforeRun( wait: ContainerWaitForDependency::untilReady( condition: MySQLReady::from(container: $container), timeoutInSeconds: 30 ) );
Setting readiness timeout for MySQL
The withReadinessTimeout method configures how long the MySQL container will wait for the database to become ready
before throwing a ContainerWaitTimeout exception. The default timeout is 30 seconds.
$container = MySQLDockerContainer::from(image: 'mysql:8.1', name: 'my-database') ->withReadinessTimeout(timeoutInSeconds: 60) ->run();
Usage examples
-
When running the containers from the library on a host (your local machine), you need to map the volume
/var/run/docker.sock:/var/run/docker.sock. This ensures that the container has access to the Docker daemon on the host machine, allowing Docker commands to be executed within the container. -
In some cases, it may be necessary to add the
docker-clidependency to your PHP image. This enables the container to interact with Docker from within the container environment.
MySQL and Generic Containers
Before configuring and starting the MySQL container, a PHP container is set up to execute the tests and manage the integration process.
This container runs within a Docker network and uses a volume for the database migrations. The following commands are used to prepare the environment:
-
Create the Docker network:
docker network create tiny-blocks
-
Create the volume for migrations:
docker volume create test-adm-migrations
-
Run the PHP container:
docker run -u root --rm -it --network=tiny-blocks --name test-lib \ -v ${PWD}:/app \ -v ${PWD}/tests/Integration/Database/Migrations:/test-adm-migrations \ -v /var/run/docker.sock:/var/run/docker.sock \ -w /app gustavofreze/php:8.5-alpine bash -c "composer tests"
The MySQL container is configured and started:
$mySQLContainer = MySQLDockerContainer::from(image: 'mysql:8.1', name: 'test-database') ->withNetwork(name: 'tiny-blocks') ->withTimezone(timezone: 'America/Sao_Paulo') ->withUsername(user: 'xpto') ->withPassword(password: '123') ->withDatabase(database: 'test_adm') ->withPortMapping(portOnHost: 3306, portOnContainer: 3306) ->withRootPassword(rootPassword: 'root') ->withGrantedHosts() ->withReadinessTimeout(timeoutInSeconds: 60) ->withoutAutoRemove() ->runIfNotExists();
With the MySQL container started, it is possible to retrieve data, such as the address and JDBC connection URL:
$environmentVariables = $mySQLContainer->getEnvironmentVariables(); $jdbcUrl = $mySQLContainer->getJdbcUrl(); $database = $environmentVariables->getValueBy(key: 'MYSQL_DATABASE'); $username = $environmentVariables->getValueBy(key: 'MYSQL_USER'); $password = $environmentVariables->getValueBy(key: 'MYSQL_PASSWORD');
The Flyway container is configured and only starts and executes migrations after the MySQL container is ready:
$flywayContainer = GenericDockerContainer::from(image: 'flyway/flyway:11.0.0') ->withNetwork(name: 'tiny-blocks') ->copyToContainer(pathOnHost: '/test-adm-migrations', pathOnContainer: '/flyway/sql') ->withVolumeMapping(pathOnHost: '/test-adm-migrations', pathOnContainer: '/flyway/sql') ->withWaitBeforeRun( wait: ContainerWaitForDependency::untilReady( condition: MySQLReady::from( container: $mySQLContainer ), timeoutInSeconds: 30 ) ) ->withEnvironmentVariable(key: 'FLYWAY_URL', value: $jdbcUrl) ->withEnvironmentVariable(key: 'FLYWAY_USER', value: $username) ->withEnvironmentVariable(key: 'FLYWAY_TABLE', value: 'schema_history') ->withEnvironmentVariable(key: 'FLYWAY_SCHEMAS', value: $database) ->withEnvironmentVariable(key: 'FLYWAY_EDITION', value: 'community') ->withEnvironmentVariable(key: 'FLYWAY_PASSWORD', value: $password) ->withEnvironmentVariable(key: 'FLYWAY_LOCATIONS', value: 'filesystem:/flyway/sql') ->withEnvironmentVariable(key: 'FLYWAY_CLEAN_DISABLED', value: 'false') ->withEnvironmentVariable(key: 'FLYWAY_VALIDATE_MIGRATION_NAMING', value: 'true') ->run( commands: ['-connectRetries=15', 'clean', 'migrate'], waitAfterStarted: ContainerWaitForTime::forSeconds(seconds: 5) );
License
Docker container is licensed under MIT.
Contributing
Please follow the contributing guidelines to contribute to the project.