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

pl.allegro.tech.servicemesh.envoycontrol.config.testcontainers.GenericContainer.kt Maven / Gradle / Ivy

There is a newer version: 0.22.1
Show newest version
package pl.allegro.tech.servicemesh.envoycontrol.config.testcontainers

import com.github.dockerjava.api.command.InspectContainerResponse
import org.testcontainers.containers.BindMode
import org.testcontainers.containers.Network
import org.testcontainers.images.builder.ImageFromDockerfile
import org.testcontainers.images.builder.dockerfile.statement.Statement
import org.testcontainers.containers.GenericContainer as BaseGenericContainer

open class GenericContainer> : BaseGenericContainer {
    constructor(image: ImageFromDockerfile) : super(image)
    constructor(dockerImageName: String) : super(dockerImageName)
    constructor(statements: List) : super(
            ImageFromDockerfile().withDockerfileFromBuilder { builder ->
                statements.forEach {
                    builder.withStatement(it)
                }
            }
    )

    val logRecorder: LogRecorder = LogRecorder()

    private val HOST_IP_SCRIPT = "testcontainers/host_ip.sh"
    private val HOST_IP_SCRIPT_DEST = "/usr/local/bin/host_ip.sh"

    companion object {
        const val allInterfaces = "0.0.0.0"
    }

    override fun configure() {
        super.configure()
        withClasspathResourceMapping(HOST_IP_SCRIPT, HOST_IP_SCRIPT_DEST, BindMode.READ_ONLY)
    }

    override fun withClasspathResourceMapping(
        resourcePath: String?,
        containerPath: String?,
        mode: BindMode?
    ): SELF {
        return if (notAlreadyMounted(containerPath)) {
            super.withClasspathResourceMapping(resourcePath, containerPath, mode)
        } else {
            this.self()
        }
    }

    override fun containerIsStarting(containerInfo: InspectContainerResponse?) {
        followOutput(logRecorder)
    }

    fun addHost(host: String, ip: String) {
        execInContainer("sh", "-c", "echo \"$ip\t$host\" >> /etc/hosts")
    }

    fun removeHost(host: String) {
        execInContainer("sh", "-c", "grep -v '[[:blank:]]$host\$' /etc/hosts > /tmp/hosts")
        // docker does not allow changing inode of mounted files
        execInContainer("sh", "-c", "cat /tmp/hosts > /etc/hosts")
    }

    fun notAlreadyMounted(destination: String?) = binds.none { it.volume.path == destination }

    fun hostIp(): String {
        val result = execInContainer(HOST_IP_SCRIPT_DEST)

        if (result.stderr.isNotEmpty() or result.stdout.isEmpty()) {
            throw ContainerUnableToObtainHostIpException("stderr: ${result.stderr}")
        }

        return result.stdout.trim()
    }

    fun ipAddress(): String {
        return containerInfo
            .networkSettings
            .networks[(network as Network.NetworkImpl).name]!!
            .ipAddress!!
    }

    fun gatewayIp(): String {
        return containerInfo
            .networkSettings
            .networks[(network as Network.NetworkImpl).name]!!
            .gateway!!
    }

    open fun sigstop() {
        sendSignal("STOP")
    }

    open fun sigcont() {
        sendSignal("CONT")
    }

    fun blockTrafficTo(ip: String) {
        runCommands(
            arrayOf(
                "iptables -A INPUT -s $ip -j DROP",
                "iptables -A OUTPUT -d $ip -j DROP"
            )
        )
    }

    fun unblockTrafficTo(ip: String) {
        runCommands(
            arrayOf(
                "iptables -D INPUT -s $ip -j DROP",
                "iptables -D OUTPUT -d $ip -j DROP"
            )
        )
    }

    fun clearAllIptablesRules() {
        runCommands(
            arrayOf(
                "iptables -t nat -F",
                "iptables -t mangle -F",
                "iptables -F",
                "iptables -X"
            )
        )
    }

    fun runCommands(commands: Array) {
        commands.forEach { command ->
            val result = execInContainer(*(command.split(" ").toTypedArray()))
            if (result.exitCode != 0) {
                throw CommandFailedException(command, result.exitCode, result.stdout, result.stderr, this.containerName())
            }
        }
    }

    fun restart() = dockerClient.restartContainerCmd(containerId).exec()

    private fun sendSignal(signal: String) {
        getDockerClient()
            .killContainerCmd(getContainerId())
            .withSignal(signal)
            .exec()
    }

    /**
     * The container host name is randomly generated based on the first 12 characters of the container ID.
     * https://developer.ibm.com/articles/dm-1602-db2-docker-trs/
     */
    fun containerName() = containerId.substring(0, 12)
}

class ContainerUnableToObtainHostIpException(msg: String) : RuntimeException(msg)
class CommandFailedException(
    cmd: String,
    exitCode: Int,
    stdout: String,
    stderr: String,
    containerName: String
) : RuntimeException(
    "Command '$cmd' failed in container $containerName with exit code $exitCode; stderr: $stderr, stdout: $stdout"
)




© 2015 - 2024 Weber Informatics LLC | Privacy Policy