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

com.avast.gradle.dockercompose.ComposeSettings.groovy Maven / Gradle / Ivy

The newest version!
package com.avast.gradle.dockercompose

import groovy.transform.CompileStatic
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.MapProperty
import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.TaskProvider
import org.gradle.process.JavaForkOptions
import org.gradle.process.ProcessForkOptions

import javax.inject.Inject
import java.nio.charset.StandardCharsets
import java.security.MessageDigest
import java.time.Duration

@CompileStatic
abstract class ComposeSettings {
    transient final TasksConfigurator tasksConfigurator
    final DockerExecutor dockerExecutor

    abstract ListProperty getUseComposeFiles()
    abstract ListProperty getStartedServices()
    abstract Property getIncludeDependencies()
    abstract MapProperty getScale()

    abstract ListProperty getBuildAdditionalArgs()
    abstract ListProperty getPullAdditionalArgs()
    abstract ListProperty getUpAdditionalArgs()
    abstract ListProperty getDownAdditionalArgs()
    abstract ListProperty getComposeAdditionalArgs()

    abstract Property getBuildBeforeUp()
    abstract Property getBuildBeforePull()

    abstract Property getRemoveOrphans()
    abstract Property getForceRecreate()
    abstract Property getNoRecreate()

    abstract Property getStopContainers()
    abstract Property getRemoveContainers()
    abstract Property getRetainContainersOnStartupFailure()
    abstract Property getRemoveImages()
    abstract Property getRemoveVolumes()

    abstract Property getIgnorePullFailure()
    abstract Property getIgnorePushFailure()
    abstract ListProperty getPushServices()

    abstract Property getWaitForTcpPorts()
    abstract ListProperty getTcpPortsToIgnoreWhenWaiting()
    abstract Property getWaitAfterTcpProbeFailure()
    abstract Property getWaitForTcpPortsTimeout()
    abstract Property getWaitForTcpPortsDisconnectionProbeTimeout()
    abstract Property getWaitAfterHealthyStateProbeFailure()
    abstract Property getWaitForHealthyStateTimeout()
    abstract Property getCheckContainersRunning()

    abstract Property getCaptureContainersOutput()
    abstract RegularFileProperty getCaptureContainersOutputToFile()
    abstract DirectoryProperty getCaptureContainersOutputToFiles()
    abstract RegularFileProperty getComposeLogToFile()
    abstract DirectoryProperty getContainerLogToDir()

    protected String customProjectName
    protected Boolean customProjectNameSet
    protected String safeProjectNamePrefix
    void setProjectName(String customProjectName) {
        this.customProjectName = customProjectName
        this.customProjectNameSet = true
    }
    private Provider projectNameProvider
    Provider getProjectName() {
        this.projectNameProvider
    }
    String projectNamePrefix
    String nestedName

    abstract Property getExecutable()
    abstract Property getUseDockerComposeV2()
    abstract Property getDockerExecutable()
    abstract MapProperty getEnvironment()

    abstract DirectoryProperty getDockerComposeWorkingDirectory()
    abstract Property getDockerComposeStopTimeout()

    @Inject
    ComposeSettings(Project project, String name = '', String parentName = '') {
        this.nestedName = parentName + name
        this.safeProjectNamePrefix = generateSafeProjectNamePrefix(project)
        this.projectNameProvider = project.provider({
            if (customProjectNameSet) {
                return customProjectName
            }
            else if (projectNamePrefix) {
                return nestedName ? "${projectNamePrefix}_${nestedName}" : projectNamePrefix
            }
            else {
                return nestedName ? "${safeProjectNamePrefix}_${nestedName}" : safeProjectNamePrefix
            }
        }).map{ String projectName ->
            // docker-compose project names must be lowercase
            projectName.toLowerCase()
        }

        useComposeFiles.empty()
        startedServices.empty()
        includeDependencies.set(false)
        scale.empty()

        buildAdditionalArgs.empty()
        pullAdditionalArgs.empty()
        upAdditionalArgs.empty()
        downAdditionalArgs.empty()
        composeAdditionalArgs.empty()

        buildBeforeUp.set(true)
        buildBeforePull.set(true)

        removeOrphans.set(false)
        forceRecreate.set(false)
        noRecreate.set(false)

        stopContainers.set(true)
        removeContainers.set(true)
        retainContainersOnStartupFailure.set(false)
        removeImages.set(RemoveImages.None)
        removeVolumes.set(true)

        ignorePullFailure.set(false)
        ignorePushFailure.set(false)
        pushServices.empty()

        waitForTcpPorts.set(true)
        tcpPortsToIgnoreWhenWaiting.empty()
        waitAfterTcpProbeFailure.set(Duration.ofSeconds(1))
        waitForTcpPortsTimeout.set(Duration.ofMinutes(15))
        waitForTcpPortsDisconnectionProbeTimeout.set(Duration.ofMillis(1000))
        waitAfterHealthyStateProbeFailure.set(Duration.ofSeconds(5))
        waitForHealthyStateTimeout.set(Duration.ofMinutes(15))
        checkContainersRunning.set(true)

        captureContainersOutput.set(false)

        executable.set('docker-compose')
        useDockerComposeV2.set(true)
        dockerExecutable.set('docker')

        dockerComposeStopTimeout.set(Duration.ofSeconds(10))

        this.containerLogToDir.set(project.buildDir.toPath().resolve('containers-logs').toFile())

        this.dockerExecutor = project.objects.newInstance(DockerExecutor, this)
        this.tasksConfigurator = new TasksConfigurator(this, project, name)
    }

    private static String generateSafeProjectNamePrefix(Project project) {
        def fullPathMd5 = MessageDigest.getInstance("MD5").digest(project.projectDir.absolutePath.toString().getBytes(StandardCharsets.UTF_8)).encodeHex().toString()
        "${fullPathMd5}_${project.name.replace('.', '_')}"
    }

    protected ComposeSettings cloneAsNested(String name) {
        def r = tasksConfigurator.newComposeSettings(name, this.nestedName)

        r.includeDependencies.set(includeDependencies.get())

        r.buildAdditionalArgs.set(new ArrayList(this.buildAdditionalArgs.get()))
        r.pullAdditionalArgs.set(new ArrayList(this.pullAdditionalArgs.get()))
        r.upAdditionalArgs.set(new ArrayList(this.upAdditionalArgs.get()))
        r.downAdditionalArgs.set(new ArrayList(this.downAdditionalArgs.get()))
        r.composeAdditionalArgs.set(new ArrayList(this.composeAdditionalArgs.get()))

        r.buildBeforeUp.set(this.buildBeforeUp.get())
        r.buildBeforePull.set(this.buildBeforePull.get())

        r.removeOrphans.set(this.removeOrphans.get())
        r.forceRecreate.set(this.forceRecreate.get())
        r.noRecreate.set(this.noRecreate.get())

        r.stopContainers.set(stopContainers.get())
        r.removeContainers.set(removeContainers.get())
        r.retainContainersOnStartupFailure.set(retainContainersOnStartupFailure.get())
        r.removeImages.set(removeImages.get())
        r.removeVolumes.set(removeVolumes.get())

        r.ignorePullFailure.set(ignorePullFailure.get())
        r.ignorePushFailure.set(ignorePushFailure.get())

        r.waitForTcpPorts.set(this.waitForTcpPorts.get())
        r.tcpPortsToIgnoreWhenWaiting.set(new ArrayList(this.tcpPortsToIgnoreWhenWaiting.get()))
        r.waitAfterTcpProbeFailure.set(waitAfterTcpProbeFailure.get())
        r.waitForTcpPortsTimeout.set(waitForTcpPortsTimeout.get())
        r.waitForTcpPortsDisconnectionProbeTimeout.set(waitForTcpPortsDisconnectionProbeTimeout.get())
        r.waitAfterHealthyStateProbeFailure.set(waitAfterHealthyStateProbeFailure.get())
        r.waitForHealthyStateTimeout.set(waitForHealthyStateTimeout.get())
        r.checkContainersRunning.set(checkContainersRunning.get())

        r.captureContainersOutput.set(captureContainersOutput.get())

        r.projectNamePrefix = this.projectNamePrefix

        r.executable.set(this.executable.get())
        r.useDockerComposeV2.set(this.useDockerComposeV2.get())
        r.dockerExecutable.set(this.dockerExecutable.get())
        r.environment.set(new HashMap(this.environment.get()))

        r.dockerComposeWorkingDirectory.set(this.dockerComposeWorkingDirectory.getOrNull())
        r.dockerComposeStopTimeout.set(this.dockerComposeStopTimeout.get())
        r
    }

    void isRequiredBy(Task task) {
        tasksConfigurator.isRequiredByCore(task, false)
    }

    void isRequiredBy(TaskProvider taskProvider) {
        taskProvider.configure { tasksConfigurator.isRequiredByCore(it, true) }
    }

    Map getServicesInfos() {
        tasksConfigurator.getServicesInfos()
    }

    void exposeAsEnvironment(ProcessForkOptions task) {
        servicesInfos.values().each { serviceInfo ->
            serviceInfo.containerInfos.each { instanceName, si ->
                if (instanceName.endsWith('_1') || instanceName.endsWith('-1')) {
                    task.environment << createEnvironmentVariables(serviceInfo.name.toUpperCase(), si)
                }
                task.environment << createEnvironmentVariables(instanceName.toUpperCase(), si)
            }
        }
    }

    void exposeAsSystemProperties(JavaForkOptions task) {
        servicesInfos.values().each { serviceInfo ->
            serviceInfo.containerInfos.each { instanceName, si ->
                if(instanceName.endsWith('_1') || instanceName.endsWith('-1')) {
                    task.systemProperties << createSystemProperties(serviceInfo.name, si)
                }
                task.systemProperties << createSystemProperties(instanceName, si)
            }
        }
    }

    protected Map createEnvironmentVariables(String variableName, ContainerInfo ci) {
        def serviceName = replaceV2Separator(variableName)
        Map environmentVariables = [:]
        environmentVariables.put("${serviceName}_HOST".toString(), ci.host)
        environmentVariables.put("${serviceName}_CONTAINER_HOSTNAME".toString(), ci.containerHostname)
        ci.tcpPorts.each { environmentVariables.put("${serviceName}_TCP_${it.key}".toString(), it.value) }
        ci.udpPorts.each { environmentVariables.put("${serviceName}_UDP_${it.key}".toString(), it.value) }
        environmentVariables
    }

    protected Map createSystemProperties(String variableName, ContainerInfo ci) {
        def serviceName = replaceV2Separator(variableName)
        Map systemProperties = [:]
        systemProperties.put("${serviceName}.host".toString(), ci.host)
        systemProperties.put("${serviceName}.containerHostname".toString(), ci.containerHostname)
        ci.tcpPorts.each { systemProperties.put("${serviceName}.tcp.${it.key}".toString(), it.value) }
        ci.udpPorts.each { systemProperties.put("${serviceName}.udp.${it.key}".toString(), it.value) }
        systemProperties
    }

    static String replaceV2Separator(String serviceName) {
        serviceName.replaceAll('-(\\d+)$', '_$1')
    }
}

enum RemoveImages {
    None,
    Local, // images that don't have a custom name set by the `image` field
    All
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy