com.avast.gradle.dockercompose.DockerExecutor.groovy Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gradle-docker-compose-plugin Show documentation
Show all versions of gradle-docker-compose-plugin Show documentation
Simplifies usage of Docker Compose for integration testing in Gradle environment.
The newest version!
package com.avast.gradle.dockercompose
import org.gradle.api.logging.Logger
import org.gradle.api.logging.Logging
import org.gradle.process.ExecOperations
import org.gradle.process.ExecSpec
import org.yaml.snakeyaml.Yaml
import javax.inject.Inject
class DockerExecutor {
private final ComposeSettings settings
private final ExecOperations exec
private static final Logger logger = Logging.getLogger(DockerExecutor.class);
@Inject
DockerExecutor(ComposeSettings settings, ExecOperations exec) {
this.settings = settings
this.exec = exec
}
String execute(String... args) {
def exec = this.exec
def settings = this.settings
new ByteArrayOutputStream().withStream { os ->
def er = exec.exec { ExecSpec e ->
e.environment = System.getenv() + settings.environment.get()
def finalArgs = [settings.dockerExecutable.get()]
finalArgs.addAll(args)
e.commandLine finalArgs
e.standardOutput = os
e.ignoreExitValue = true
}
def stdout = os.toString().trim()
if (er.exitValue != 0) {
throw new RuntimeException("Exit-code ${er.exitValue} when calling ${settings.dockerExecutable.get()}, stdout: $stdout")
}
stdout
}
}
List getDockerInfo() {
def asString = execute('info')
logger.debug("Docker info: $asString")
asString.readLines()
}
String getDockerPlatform() {
String osType = getDockerInfo().collect { it.trim() }.find { it.startsWith('OSType:') }
osType == null || osType.empty ? System.getProperty("os.name") : osType.substring('OSType:'.length()).trim()
}
String getContainerPlatform(Map inspection) {
def platform = inspection.Platform as String
platform ?: getDockerPlatform()
}
Map getInspection(String containerId) {
getInspections(containerId).values().find()
}
Map> getInspections(String... containersIds) {
def asString = execute(*['inspect', *containersIds])
logger.debug("Inspections for containers ${containersIds.join(', ')}: $asString")
Map[] inspections = new Yaml().load(asString)
def r = inspections.collectEntries { [it.Id, it] }
def notFoundInspections = containersIds.findAll { !r.containsKey(it) }
if (notFoundInspections) {
throw new RuntimeException('docker inspect didn\'t return inspection for these containers: ' + notFoundInspections.join(', '))
}
r
}
Map getNetworkInspection(String networkName) {
def asString = execute('network', 'inspect', networkName)
logger.debug("Inspection for network $networkName: $asString")
(new Yaml().load(asString))[0] as Map
}
String getNetworkGateway(String networkName) {
def networkInspection = getNetworkInspection(networkName)
if (networkInspection) {
Map ipam = networkInspection.IPAM
if (ipam) {
Map[] ipamConfig = ipam.Config
if (ipamConfig && ipamConfig.size() > 0) {
return ipamConfig[0].Gateway
}
}
}
null
}
String getNetworkDriver(String networkName) {
def networkInspection = getNetworkInspection(networkName)
networkInspection ? networkInspection.Driver as String : ""
}
String getContainerLogs(String containerId) {
execute('logs', '--follow=false', containerId)
}
ServiceHost getContainerHost(Map inspection, String serviceName, Logger logger = this.logger) {
String servicesHost = settings.environment.get()['SERVICES_HOST'] ?: System.getenv('SERVICES_HOST')
if (servicesHost) {
logger.lifecycle("SERVICES_HOST environment variable detected - will be used as hostname of service $serviceName ($servicesHost)'")
return new ServiceHost(host: servicesHost, type: ServiceHostType.RemoteDockerHost)
}
String dockerHost = settings.environment.get()['DOCKER_HOST'] ?: System.getenv('DOCKER_HOST')
if (dockerHost) {
def host = dockerHost.toURI().host ?: 'localhost'
logger.lifecycle("DOCKER_HOST environment variable detected - will be used as hostname of service $serviceName ($host)'")
return new ServiceHost(host: host, type: ServiceHostType.RemoteDockerHost)
}
if (isWSL()) {
return new ServiceHost(host: 'localhost', type: ServiceHostType.LocalHost)
}
Map networkSettings = inspection.NetworkSettings
Map networks = networkSettings.Networks
Map.Entry firstNetworkPair = networks.find()
if (isWindows() && getContainerPlatform(inspection).toLowerCase().contains('win') && firstNetworkPair && "nat".equalsIgnoreCase(getNetworkDriver(firstNetworkPair.key))) {
logger.lifecycle("Will use direct access to the container of $serviceName")
return new ServiceHost(host: firstNetworkPair.value.IPAddress, type: ServiceHostType.DirectContainerAccess)
}
if (isMac() || isWindows()) {
logger.lifecycle("Will use localhost as host of $serviceName")
return new ServiceHost(host: 'localhost', type: ServiceHostType.LocalHost)
}
String networkMode = (String)inspection.HostConfig.NetworkMode ?: ''
if (networkMode.startsWith('container:')) {
String linkedContainerId = networkMode.substring('container:'.length())
logger.lifecycle("Reading container host of $serviceName from linked container $linkedContainerId")
return getContainerHost(getInspection(linkedContainerId), linkedContainerId, logger)
}
String gateway
if (networks && networks.every { it.key.toLowerCase().equals("host") }) {
gateway = 'localhost'
logger.lifecycle("Will use $gateway as host of $serviceName because it is using HOST network")
return new ServiceHost(host: gateway, type: ServiceHostType.Host)
} else if (networks && networks.size() > 0) {
gateway = firstNetworkPair.value.Gateway
if (!gateway) {
logger.lifecycle("Gateway cannot be read from container inspection - trying to read from network inspection (network '${firstNetworkPair.key}')")
gateway = getNetworkGateway(firstNetworkPair.key)
}
logger.lifecycle("Will use $gateway (network ${firstNetworkPair.key}) as host of $serviceName")
return new ServiceHost(host: gateway, type: ServiceHostType.NetworkGateway)
}
if (networkSettings.Gateway) { // networks not specified (older Docker versions)
gateway = networkSettings.Gateway
logger.lifecycle("Will use $gateway as host of $serviceName")
return new ServiceHost(host: gateway, type: ServiceHostType.NetworkGateway)
}
logger.warn("Will use 'localhost' as host of $serviceName (as a fallback)")
return new ServiceHost(host: 'localhost', type: ServiceHostType.LocalHost)
}
Map getTcpPortsMapping(String serviceName, Map inspection, ServiceHost host) {
getPortsMapping("TCP", serviceName, inspection, host)
}
Map getUdpPortsMapping(String serviceName, Map inspection, ServiceHost host) {
getPortsMapping("UDP", serviceName, inspection, host)
}
Map getPortsMapping(String protocol, String serviceName, Map inspection, ServiceHost host) {
Map ports = [:]
inspection.NetworkSettings.Ports.each { String exposedPortWithProtocol, forwardedPortsInfos ->
def (String exposedPortAsString, String pr) = exposedPortWithProtocol.split('/')
if (!protocol.equalsIgnoreCase(pr)) {
return // from closure
}
int exposedPort = exposedPortAsString as int
if (!forwardedPortsInfos || forwardedPortsInfos.isEmpty()) {
logger.debug("No forwarded $protocol port for service '$serviceName:$exposedPort'")
} else {
switch (host.type) {
case ServiceHostType.LocalHost:
case ServiceHostType.NetworkGateway:
case ServiceHostType.RemoteDockerHost:
if (forwardedPortsInfos.size() > 1) {
logger.warn("More forwarded $protocol ports for service '$serviceName:$exposedPort $forwardedPortsInfos'. Will use the first one.")
}
def forwardedPortInfo = forwardedPortsInfos.first()
int forwardedPort = forwardedPortInfo.HostPort as int
logger.info("Exposed $protocol port on service '$serviceName:$exposedPort' will be available as $forwardedPort")
ports.put(exposedPort, forwardedPort)
break
case ServiceHostType.Host:
logger.info("Exposed $protocol port on service '$serviceName:$exposedPort' will be available as $exposedPort because it uses HOST network")
ports.put(exposedPort, exposedPort)
break;
case ServiceHostType.DirectContainerAccess:
logger.info("Exposed $protocol port on service '$serviceName:$exposedPort' will be available as $exposedPort because it uses direct access to the container")
ports.put(exposedPort, exposedPort)
break;
default:
throw new IllegalArgumentException("Unknown ServiceHostType '${host.type}' for service '$serviceName'")
break
}
}
}
ports
}
private static boolean isMac() {
System.getProperty("os.name").toLowerCase().startsWith("mac")
}
private static boolean isWindows() {
System.getProperty("os.name").toLowerCase().startsWith("win")
}
private static boolean isWSL() {
System.getProperty("os.version").toLowerCase().contains('wsl')
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy