All Downloads are FREE. Search and download functionalities are using the official Maven repository.

sirius.kernel.DockerHelper Maven / Gradle / Ivy

Go to download

Provides common core classes and the microkernel powering all Sirius applications

There is a newer version: 12.9.1
Show newest version
/*
 * Made with all the love in the world
 * by scireum in Remshalden, Germany
 *
 * Copyright by scireum GmbH
 * http://www.scireum.de - [email protected]
 */

package sirius.kernel;

import com.palantir.docker.compose.configuration.DockerComposeFiles;
import com.palantir.docker.compose.configuration.ProjectName;
import com.palantir.docker.compose.connection.Cluster;
import com.palantir.docker.compose.connection.Container;
import com.palantir.docker.compose.connection.ContainerCache;
import com.palantir.docker.compose.connection.DockerMachine;
import com.palantir.docker.compose.connection.ImmutableCluster;
import com.palantir.docker.compose.execution.DefaultDockerCompose;
import com.palantir.docker.compose.execution.Docker;
import com.palantir.docker.compose.execution.DockerCompose;
import com.palantir.docker.compose.execution.DockerComposeExecutable;
import com.palantir.docker.compose.execution.DockerExecutable;
import com.palantir.docker.compose.execution.RetryingDockerCompose;
import sirius.kernel.commons.Strings;
import sirius.kernel.commons.Tuple;
import sirius.kernel.commons.Wait;
import sirius.kernel.di.Initializable;
import sirius.kernel.di.std.ConfigValue;
import sirius.kernel.di.std.Register;
import sirius.kernel.health.Log;
import sirius.kernel.settings.PortMapper;

import java.io.File;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.stream.Collectors;

/**
 * Initializes Docker Composer if requested by the framework.
 * 

* This basically uses docker.file from the system config to determine which * compser file to use and start / stops docker for each test run or staging environment. *

* Also it provides a {@link PortMapper} to map the desired production ports to the * ones provided by the docker containers. */ @Register(classes = {Initializable.class, Killable.class}) public class DockerHelper extends PortMapper implements Initializable, Killable { private static final int MAX_WAIT_SECONDS = 10; @ConfigValue("docker.project") private String project; @ConfigValue("docker.hostIp") private String hostIp; @ConfigValue("docker.file") private String dockerfile; @ConfigValue("docker.retryAttempts") private int retryAttempts; @ConfigValue("docker.pull") private boolean pull; @ConfigValue("docker.keepRunning") private boolean keepRunning; private static final Log LOG = Log.get("docker"); private DockerCompose dockerCompose; private Cluster cluster; private DockerMachine machine; private DockerExecutable executable; @Override public int getPriority() { return 10; } @Override protected Tuple map(String service, String host, int port) { if (dockerCompose == null) { return Tuple.create(host, port); } return Tuple.create(machine.getIp(), containers().container(service).port(port).getExternalPort()); } private DockerMachine machine() { if (machine == null) { if (Strings.isEmpty(hostIp)) { machine = DockerMachine.localMachine().build(); } else { LOG.INFO("Using hostIp: %s", hostIp); machine = new DockerMachine(hostIp, System.getenv()); } } return machine; } private DockerExecutable dockerExecutable() { if (executable == null) { executable = DockerExecutable.builder().dockerConfiguration(this.machine()).build(); } return executable; } private Cluster containers() { if (cluster == null) { cluster = ImmutableCluster.builder() .ip(this.machine().getIp()) .containerCache(new ContainerCache(this.docker(), this.dockerCompose)) .build(); } return cluster; } private Docker docker() { return new Docker(this.dockerExecutable()); } private DockerComposeExecutable dockerComposeExecutable() { return DockerComposeExecutable.builder() .dockerComposeFiles(DockerComposeFiles.from(dockerfile)) .dockerConfiguration(this.machine()) .projectName(projectName()) .build(); } private ProjectName projectName() { return Strings.isFilled(project) && !Sirius.isStartedAsTest() ? ProjectName.fromString(project) : ProjectName.random(); } @Override public void initialize() throws Exception { determineEffectiveDockerFile(); if (Strings.isFilled(dockerfile)) { LOG.INFO("Starting docker compose using: %s", dockerfile); this.dockerCompose = new RetryingDockerCompose(retryAttempts, new DefaultDockerCompose(dockerComposeExecutable(), machine())); if (pull) { try { LOG.INFO("Executing docker-compose pull..."); dockerCompose.pull(); } catch (Exception e) { LOG.WARN("docker-compose pull failed: %s (%s)", e.getMessage(), e.getClass().getName()); } } try { LOG.INFO("Executing docker-compose up..."); dockerCompose.up(); } catch (Exception e) { LOG.WARN("docker-compose up failed: %s (%s)", e.getMessage(), e.getClass().getName()); } awaitClusterHealth(); PortMapper.setMapper(this); } else { LOG.INFO("No docker file is present - skipping...."); } } private void determineEffectiveDockerFile() throws URISyntaxException { if (Strings.isEmpty(dockerfile)) { return; } if (new File(dockerfile).exists()) { return; } URL dockerResource = getClass().getResource(dockerfile.startsWith("/") ? dockerfile : "/" + dockerfile); if (dockerResource != null && dockerResource.toURI() != null && new File(dockerResource.toURI()).exists()) { dockerfile = new File(dockerResource.toURI()).getAbsolutePath(); } else { dockerfile = null; } } private void awaitClusterHealth() { try { containers().allContainers().forEach(this::awaitContainerStart); } catch (Exception e) { LOG.SEVERE(e); } } private void awaitContainerStart(Container container) { LOG.INFO("Waiting for '%s' to become ready...", container.getContainerName()); try { int retries = MAX_WAIT_SECONDS; while (container.areAllPortsOpen().failed()) { Wait.seconds(1); if (retries-- <= 0) { LOG.WARN("Failed to start '%s' - Ports: %s", container.getContainerName(), container.ports().stream().map(Object::toString).collect(Collectors.joining(", "))); return; } } LOG.INFO("Container '%s' is ONLINE - Ports: %s", container.getContainerName(), container.ports().stream().map(Object::toString).collect(Collectors.joining(", "))); } catch (Exception e) { LOG.SEVERE(e); } } @Override public void awaitTermination() { if (Strings.isEmpty(dockerfile)) { return; } if (keepRunning) { return; } if (Sirius.isStartedAsTest()) { try { LOG.INFO("Executing docker-compose kill..."); dockerCompose.kill(); } catch (Exception e) { LOG.WARN("docker-compose kill failed: %s (%s)", e.getMessage(), e.getClass().getName()); } try { LOG.INFO("Executing docker-compose down..."); dockerCompose.down(); } catch (Exception e) { LOG.WARN("docker-compose down failed: %s (%s)", e.getMessage(), e.getClass().getName()); } try { LOG.INFO("Executing docker-compose rm..."); dockerCompose.rm(); } catch (Exception e) { LOG.WARN("docker-compose rm failed: %s (%s)", e.getMessage(), e.getClass().getName()); } } else { try { LOG.INFO("Executing docker-compose stop..."); containers().allContainers().forEach(c -> { try { LOG.INFO("Executing docker-compose stop for '%s'...", c.getContainerName()); dockerCompose.stop(c); } catch (Exception e) { LOG.WARN("docker-compose stop for '%s' failed: %s (%s)", c.getContainerName(), e.getMessage(), e.getClass().getName()); } }); } catch (Exception e) { LOG.WARN("docker-compose stop failed: %s (%s)", e.getMessage(), e.getClass().getName()); } } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy