Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature]: Repeatable Network Names so that reuse-hash is constant, and containers are not recreated if they actually exist #7261

Closed
maxant opened this issue Jul 1, 2023 · 4 comments

Comments

@maxant
Copy link

maxant commented Jul 1, 2023

Module

Core

Problem

I am trying to reuse containers so that when I am developing tests, I don't have to wait about a minute for mysql to start up.
The following program works, but the containers are always created, rather than being reused.
After debugging testcontainers, I saw that the hash used to add a label always changes, because the network name is in there, and by default it is based on a random uuid.

It is not currently possible to name the network.
setting network to null in the code below would work, but then redpanda console cannot find redpanda and it seems as though you can only use network aliases if a network is added to the testcontainer?

import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.containers.Network;
import org.testcontainers.lifecycle.Startables;
import org.testcontainers.redpanda.RedpandaContainer;
import org.testcontainers.utility.DockerImageName;

import java.awt.*;
import java.io.IOException;
import java.net.URI;

public class AbstractContainerBaseTest {

    static Network network = Network.newNetwork();
    static MySQLContainer<?> mysql = new MySQLContainer<>("mysql:8.0.33")
            .withDatabaseName("test")
            .withUsername("cdc")
            .withPassword("cdcpwd")
            .withReuse(true)
            .withNetwork(network)
            .withNetworkAliases("mysql")
        ;
    static RedpandaContainer redpanda = new RedpandaContainer("docker.redpanda.com/redpandadata/redpanda:v23.1.12")
            .withNetwork(network)
            .withNetworkAliases("redpanda")
            .withReuse(true);
    static GenericContainer redpandaconsole = new GenericContainer(DockerImageName.parse("redpandadata/console:v2.2.4"))
            .dependsOn(redpanda)
            .withNetwork(network)
            .withExposedPorts(8080)
            .withNetworkAliases("redpandaconsole")
            .withEnv("KAFKA_BROKERS", "redpanda:29092") // check impl of RedpandaContainer, and the port used internallyis 29092
            .withReuse(true);

    static {
        Startables.deepStart(mysql, redpanda, redpandaconsole).join();
        logAndOpenRedpandaConsoleInBrowser();
    }

    private static void logAndOpenRedpandaConsoleInBrowser() {
        String url = ("http://localhost:" + redpandaconsole.getMappedPort(8080));
        try {
            if (Desktop.isDesktopSupported()) {
                Desktop desktop = Desktop.getDesktop();
                if (desktop.isSupported(Desktop.Action.BROWSE)) {
                    desktop.browse(URI.create(url));
                }
            }
        } catch (IOException | InternalError e) {
            e.printStackTrace();
        }
        System.out.println("Redpanda Console running on " + url);
    }

}

Solution

Allow networks to be named.

this might work:

package org.testcontainers.temp;

import com.github.dockerjava.api.command.CreateNetworkCmd;
import org.junit.rules.ExternalResource;
import org.testcontainers.DockerClientFactory;
import org.testcontainers.containers.Network;
import org.testcontainers.utility.ResourceReaper;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;

public class NetworkImpl extends ExternalResource implements Network {

    private final String name;
    private Boolean enableIpv6;
    private String driver;
    private String id;

    private final AtomicBoolean initialized = new AtomicBoolean();

    public NetworkImpl(String name) {
        this.name = name;
    }

    @Override
    public synchronized String getId() {
        if (initialized.compareAndSet(false, true)) {
            boolean success = false;
            try {
                id = create();
                success = true;
            } finally {
                if (!success) {
                    initialized.set(false);
                }
            }
        }
        return id;
    }

    private String create() {
        CreateNetworkCmd createNetworkCmd = DockerClientFactory.instance().client().createNetworkCmd();

        createNetworkCmd.withName(name);
        createNetworkCmd.withCheckDuplicate(true);

        if (enableIpv6 != null) {
            createNetworkCmd.withEnableIpv6(enableIpv6);
        }

        if (driver != null) {
            createNetworkCmd.withDriver(driver);
        }

        Map<String, String> labels = createNetworkCmd.getLabels();
        labels = new HashMap<>(labels != null ? labels : Collections.emptyMap());
        labels.putAll(DockerClientFactory.DEFAULT_LABELS);
        //noinspection deprecation
        labels.putAll(ResourceReaper.instance().getLabels());
        createNetworkCmd.withLabels(labels);

        return createNetworkCmd.exec().getId();
    }

    @Override
    protected void after() {
        close();
    }

    @Override
    public synchronized void close() {
        if (initialized.getAndSet(false)) {
            ResourceReaper.instance().removeNetworkById(id);
        }
    }
}

Benefit

That you can have two containers with one being able to refer to the other, AND that they can have "reuse" set so that when developing tests, it is not necessary to keep bouncing the containers.

Alternatives

cant think of any

Would you like to help contributing this feature?

Yes

maxant added a commit to maxant/testcontainers-java that referenced this issue Jul 2, 2023
@maxant
Copy link
Author

maxant commented Jul 2, 2023

added a PR: #7287

@daithiscully
Copy link

Would love to see this added, currently this is blocking us from using the "reuse" functionality.

@Survival1sm
Copy link

This will be a huge win for my team and I once merged in, we waste considerable time waiting for containers to start up.

@eddumelendez
Copy link
Member

I see two different issues that has been reported in the past, reusable networks issue and reuse redpanda. As you can see in one of my comments I have offered a workaround and an PR improving adding kafka listeners.

I'm closing this issue as a duplicated.

@eddumelendez eddumelendez closed this as not planned Won't fix, can't repro, duplicate, stale Jul 24, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants