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

io.github.bonigarcia.wdm.docker.DockerService Maven / Gradle / Ivy

Go to download

Automated driver management and other helper features for Selenium WebDriver in Java

There is a newer version: 5.9.2
Show newest version
/*
 * (C) Copyright 2021 Boni Garcia (https://bonigarcia.github.io/)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
package io.github.bonigarcia.wdm.docker;

import static io.github.bonigarcia.wdm.WebDriverManager.isDockerAvailable;
import static io.github.bonigarcia.wdm.config.Config.isNullOrEmpty;
import static io.github.bonigarcia.wdm.docker.DockerHost.defaultAddress;
import static io.github.bonigarcia.wdm.versions.Shell.runAndWait;
import static java.lang.String.format;
import static java.lang.System.currentTimeMillis;
import static java.lang.invoke.MethodHandles.lookup;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Locale.ROOT;
import static java.util.stream.Collectors.toList;
import static org.apache.commons.lang3.SystemUtils.IS_OS_LINUX;
import static org.slf4j.LoggerFactory.getLogger;

import java.io.IOException;
import java.net.URI;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;

import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.async.ResultCallback.Adapter;
import com.github.dockerjava.api.command.CreateContainerCmd;
import com.github.dockerjava.api.exception.DockerException;
import com.github.dockerjava.api.model.Bind;
import com.github.dockerjava.api.model.Capability;
import com.github.dockerjava.api.model.ExposedPort;
import com.github.dockerjava.api.model.Frame;
import com.github.dockerjava.api.model.HostConfig;
import com.github.dockerjava.api.model.Mount;
import com.github.dockerjava.api.model.PortBinding;
import com.github.dockerjava.api.model.Ports;
import com.github.dockerjava.api.model.Ports.Binding;
import com.github.dockerjava.api.model.PullResponseItem;
import com.github.dockerjava.api.model.TmpfsOptions;
import com.github.dockerjava.core.DefaultDockerClientConfig;
import com.github.dockerjava.core.DockerClientBuilder;
import com.github.dockerjava.core.DockerClientConfig;
import com.github.dockerjava.httpclient5.ApacheDockerHttpClient;
import com.github.dockerjava.httpclient5.ApacheDockerHttpClient.Builder;

import io.github.bonigarcia.wdm.cache.ResolutionCache;
import io.github.bonigarcia.wdm.config.Config;
import io.github.bonigarcia.wdm.config.DriverManagerType;
import io.github.bonigarcia.wdm.config.WebDriverManagerException;
import io.github.bonigarcia.wdm.docker.DockerContainer.DockerBuilder;
import io.github.bonigarcia.wdm.docker.DockerHubTags.DockerHubTag;
import io.github.bonigarcia.wdm.online.HttpClient;
import io.github.bonigarcia.wdm.versions.VersionComparator;

/**
 * Docker Service.
 *
 * @author Boni Garcia
 * @since 5.0.0
 */
public class DockerService {

    final Logger log = getLogger(lookup().lookupClass());

    public static final String NETWORK_HOST = "host";
    private static final String DEFAULT_GATEWAY = "172.17.0.1";
    private static final String BETA = "beta";
    private static final String DEV = "dev";
    private static final String LATEST_MINUS = "latest-";
    private static final String RECORDING_EXT = ".mp4";
    private static final String SEPARATOR = "_";
    private static final String DATE_FORMAT = "yyyy.MM.dd_HH.mm.ss.SSS";
    private static final int POLL_TIME_MSEC = 500;

    private Config config;
    private HttpClient httpClient;
    private DockerClient dockerClient;
    private ResolutionCache resolutionCache;
    private URI dockerHostUri;

    public DockerService(Config config, HttpClient httpClient,
            ResolutionCache resolutionCache) {
        this.config = config;
        this.httpClient = httpClient;
        this.resolutionCache = resolutionCache;

        if (config.isDockerLocalFallback() && !isRunningInsideDocker()
                && !isDockerAvailable()) {
            log.warn(
                    "Docker is not available in your machine... local browsers are used instead");
        } else {
            this.dockerClient = createDockerClient();
        }
    }

    private DockerClient createDockerClient() {
        String dockerDaemonUrl = config.getDockerDaemonUrl();
        String dockerHost = isNullOrEmpty(dockerDaemonUrl)
                ? DockerHost.fromEnv().endpoint()
                : dockerDaemonUrl;
        return getDockerClient(dockerHost);
    }

    private DockerClient getDockerClient(String dockerHost) {
        DefaultDockerClientConfig.Builder dockerClientConfigBuilder = DefaultDockerClientConfig
                .createDefaultConfigBuilder();
        if (!isNullOrEmpty(dockerHost)) {
            dockerClientConfigBuilder.withDockerHost(dockerHost);
        }
        DockerClientConfig dockerClientConfig = dockerClientConfigBuilder
                .build();
        dockerHostUri = dockerClientConfig.getDockerHost();
        ApacheDockerHttpClient dockerHttpClient = new Builder()
                .dockerHost(dockerHostUri).build();

        return DockerClientBuilder.getInstance(dockerClientConfig)
                .withDockerHttpClient(dockerHttpClient).build();
    }

    public String getHost(String containerId, String network) {
        String host = getDefaultHost();
        if (IS_OS_LINUX && isRunningInsideDocker()) {
            host = getGateway(containerId, network);
            log.debug(
                    "WebDriverManager running inside a Docker container. Using gateway address: {}",
                    host);
        }
        return host;
    }

    private boolean isCommandResultPresent(String command) {
        String[] commandArray = new String[] { "bash", "-c", command };
        String commandOutput = runAndWait(false, commandArray);
        return !isNullOrEmpty(commandOutput);
    }

    public boolean isRunningInsideDocker(){
        return isCommandResultPresent("cat /proc/self/cgroup | grep docker") ||
                isCommandResultPresent("cat /proc/self/mountinfo | grep docker/containers");
    }

    public String getDefaultHost() {
        return Optional.ofNullable(dockerHostUri.getHost())
                .orElse(defaultAddress());
    }

    public String getGateway(String containerId, String network) {
        String gateway = dockerClient.inspectContainerCmd(containerId).exec()
                .getNetworkSettings().getNetworks().get(network).getGateway();
        if (isNullOrEmpty(gateway)) {
            return DEFAULT_GATEWAY;
        }
        return gateway;
    }

    public String getAddress(String containerId, String network) {
        return dockerClient.inspectContainerCmd(containerId).exec()
                .getNetworkSettings().getNetworks().get(network).getIpAddress();
    }

    public synchronized String startContainer(DockerContainer dockerContainer)
            throws DockerException {
        String imageId = dockerContainer.getImageId();
        log.info("Starting Docker container {}", imageId);
        HostConfig hostConfigBuilder = new HostConfig();
        String containerId;

        try (CreateContainerCmd containerConfigBuilder = dockerClient
                .createContainerCmd(imageId)) {

            boolean privileged = dockerContainer.isPrivileged();
            if (privileged) {
                log.trace("Using privileged mode");
                hostConfigBuilder.withPrivileged(true);
            }
            if (dockerContainer.isSysadmin()) {
                log.trace("Adding sysadmin capability");
                hostConfigBuilder.withCapAdd(Capability.SYS_ADMIN);
            }

            Optional shmSize = dockerContainer.getShmSize();
            if (shmSize.isPresent()) {
                log.trace("Using shm size: {}", shmSize.get());
                hostConfigBuilder.withShmSize(shmSize.get());
            }

            Optional network = dockerContainer.getNetwork();
            if (network.isPresent()) {
                log.trace("Using network: {}", network.get());
                hostConfigBuilder.withNetworkMode(network.get());
            }
            List exposedPorts = dockerContainer.getExposedPorts();
            if (!exposedPorts.isEmpty()) {
                log.trace("Using exposed ports: {}", exposedPorts);
                containerConfigBuilder.withExposedPorts(exposedPorts.stream()
                        .map(ExposedPort::parse).collect(Collectors.toList()));
                hostConfigBuilder.withPortBindings(exposedPorts.stream()
                        .map(PortBinding::parse).collect(Collectors.toList()));
                hostConfigBuilder.withPublishAllPorts(true);
            }
            Optional> binds = dockerContainer.getBinds();
            if (binds.isPresent()) {
                log.trace("Using binds: {}", binds.get());
                hostConfigBuilder.withBinds(binds.get());
            }

            Optional> mounts = dockerContainer.getMounts();
            if (mounts.isPresent()) {
                log.trace("Using mounts: {}", mounts.get());
                hostConfigBuilder.withMounts(mounts.get());
            }

            Optional> envs = dockerContainer.getEnvs();
            if (envs.isPresent()) {
                log.trace("Using envs: {}", envs.get());
                containerConfigBuilder
                        .withEnv(envs.get().toArray(new String[] {}));
            }
            Optional> cmd = dockerContainer.getCmd();
            if (cmd.isPresent()) {
                log.trace("Using cmd: {}", cmd.get());
                containerConfigBuilder
                        .withCmd(cmd.get().toArray(new String[] {}));
            }
            Optional> entryPoint = dockerContainer.getEntryPoint();
            if (entryPoint.isPresent()) {
                log.trace("Using entryPoint: {}", entryPoint.get());
                containerConfigBuilder.withEntrypoint(
                        entryPoint.get().toArray(new String[] {}));
            }

            hostConfigBuilder.withExtraHosts(dockerContainer.getExtraHosts());

            containerId = containerConfigBuilder
                    .withHostConfig(hostConfigBuilder).exec().getId();

            dockerClient.startContainerCmd(containerId).exec();
        }

        return containerId;
    }

    public String execCommandInContainer(String containerId,
            String... command) {
        String commandStr = Arrays.toString(command);
        log.trace("Running command {} in container {}", commandStr,
                containerId);
        String execId = dockerClient.execCreateCmd(containerId).withCmd(command)
                .withAttachStdout(true).withAttachStderr(true).exec().getId();
        final StringBuilder output = new StringBuilder();
        try {
            dockerClient.execStartCmd(execId).exec(new Adapter() {
                @Override
                public void onNext(Frame object) {
                    output.append(new String(object.getPayload(), UTF_8));
                    super.onNext(object);
                }
            }).awaitCompletion();
        } catch (InterruptedException e) {
            log.error("Exception execution command {} on container {}",
                    commandStr, containerId, e);
            Thread.currentThread().interrupt();
        }
        log.trace("Result of command {} in container {}: {}", commandStr,
                containerId, output);
        return output.toString();
    }

    public String getBindPort(String containerId, String exposed)
            throws DockerException {

        String bindPort = null;
        int waitTimeoutSec = config.getTimeout();
        long timeoutMs = System.currentTimeMillis()
                + Duration.ofSeconds(waitTimeoutSec).toMillis();
        do {
            Ports ports = dockerClient.inspectContainerCmd(containerId).exec()
                    .getNetworkSettings().getPorts();
            Binding[] exposedPort = ports.getBindings()
                    .get(ExposedPort.parse(exposed));
            log.trace("Port list {} -- Exposed port {} = {}", ports, exposed,
                    exposedPort);

            if (ports.getBindings() == null || exposedPort == null
                    || ports.getBindings().isEmpty()
                    || exposedPort.length == 0) {
                String dockerImage = dockerClient
                        .inspectContainerCmd(containerId).exec().getConfig()
                        .getImage();
                if (currentTimeMillis() > timeoutMs) {
                    throw new WebDriverManagerException("Timeout of "
                            + waitTimeoutSec
                            + " getting bind port in container " + dockerImage);
                } else {
                    try {
                        log.trace("Port {} is not bindable in container {}",
                                exposed, dockerImage);
                        Thread.sleep(POLL_TIME_MSEC);
                    } catch (InterruptedException e) {
                        log.warn("Interrupted exception getting bind port", e);
                        Thread.currentThread().interrupt();
                    }
                }
            } else {
                bindPort = exposedPort[0].getHostPortSpec();
            }
        } while (bindPort == null);

        return bindPort;
    }

    public void pullImageIfNecessary(String cacheKey, String imageId,
            String imageVersion) throws DockerException {
        if (!config.getDockerAvoidPulling()
                && !resolutionCache.checkKeyInResolutionCache(cacheKey)) {
            try {
                log.info(
                        "Pulling Docker image {} (this might take some time, but only the first time)",
                        imageId);
                dockerClient.pullImageCmd(imageId)
                        .exec(new Adapter() {
                        }).awaitCompletion();
                log.trace("Docker image {} pulled", imageId);

                if (!config.isAvoidResolutionCache()) {
                    resolutionCache.putValueInResolutionCacheIfEmpty(cacheKey,
                            imageVersion, config.getTtlForBrowsers());
                }
            } catch (Exception e) {
                log.warn("Exception pulling image {}: {}", imageId,
                        e.getMessage());
            }
        }
    }

    public synchronized void stopAndRemoveContainer(
            DockerContainer dockerContainer) {
        String containerId = dockerContainer.getContainerId();
        String imageId = dockerContainer.getImageId();

        log.info("Stopping Docker container {}", imageId);
        try {
            stopContainer(containerId);
            removeContainer(containerId);
        } catch (Exception e) {
            log.warn("Exception stopping container {}", imageId, e);
        }
    }

    public synchronized void stopContainer(String containerId)
            throws DockerException {
        int stopTimeoutSec = config.getDockerStopTimeoutSec();
        if (stopTimeoutSec == 0) {
            log.trace("Killing container {}", containerId);
            dockerClient.killContainerCmd(containerId).exec();
        } else {
            log.trace("Stopping container {} (timeout {} seconds)", containerId,
                    stopTimeoutSec);
            dockerClient.stopContainerCmd(containerId)
                    .withTimeout(stopTimeoutSec).exec();
        }
    }

    public synchronized void removeContainer(String containerId)
            throws DockerException {
        log.trace("Removing container {}", containerId);
        int stopTimeoutSec = config.getDockerStopTimeoutSec();
        if (stopTimeoutSec == 0) {
            dockerClient.removeContainerCmd(containerId).withForce(true).exec();
        } else {
            dockerClient.removeContainerCmd(containerId).exec();
        }
    }

    public void close() throws IOException {
        dockerClient.close();
    }

    public void updateDockerClient(String dockerHost) {
        log.debug("Updating Docker client using {}", dockerHost);
        dockerClient = getDockerClient(dockerHost);
    }

    public String getImageVersionFromDockerHub(
            DriverManagerType driverManagerType, String cacheKey,
            String browserName, String browserVersion, boolean androidEnabled) {
        String latestVersion = null;

        if (!resolutionCache.checkKeyInResolutionCache(cacheKey, false)) {
            VersionComparator versionComparator = new VersionComparator();
            List browserList = null;
            DockerHubService dockerHubService = new DockerHubService(config,
                    httpClient);
            List dockerHubTags;
            String tagPreffix = browserName + SEPARATOR;
            int minusIndex = getMinusIndex(browserVersion);

            String dockerBrowserImageFormat = config
                    .getDockerBrowserSelenoidImageFormat();
            switch (driverManagerType) {
            case CHROME:
            case FIREFOX:
                if (androidEnabled) {
                    dockerBrowserImageFormat = String.format(
                            config.getDockerBrowserMobileImageFormat(),
                            browserName, "");
                }
                dockerHubTags = dockerHubService
                        .listTags(dockerBrowserImageFormat);

                if (androidEnabled) {
                    browserList = dockerHubTags.stream()
                            .map(DockerHubTag::getName)
                            .sorted(versionComparator::compare)
                            .collect(toList());
                } else {
                    browserList = dockerHubTags.stream()
                            .filter(p -> p.getName().startsWith(tagPreffix))
                            .map(p -> p.getName().replace(tagPreffix, ""))
                            .sorted(versionComparator::compare)
                            .collect(toList());
                }
                latestVersion = browserList
                        .get(browserList.size() - 1 - minusIndex);
                break;

            case OPERA:
                dockerHubTags = dockerHubService
                        .listTags(dockerBrowserImageFormat);
                browserList = dockerHubTags.stream()
                        .filter(p -> p.getName().startsWith(tagPreffix))
                        .map(p -> p.getName().replace(tagPreffix, ""))
                        .sorted(versionComparator::compare).skip(1)
                        .collect(toList());
                latestVersion = browserList
                        .get(browserList.size() - 1 - minusIndex);
                break;

            case EDGE:
            case SAFARI:
                String dockerBrowserAerokubeImageFormat = String.format(
                        config.getDockerBrowserAerokubeImageFormat(),
                        browserName, "");
                dockerHubTags = dockerHubService
                        .listTags(dockerBrowserAerokubeImageFormat);
                browserList = dockerHubTags.stream().map(DockerHubTag::getName)
                        .sorted(versionComparator::compare).collect(toList());
                latestVersion = browserList
                        .get(browserList.size() - 1 - minusIndex);
                break;

            default:
                throw new WebDriverManagerException(
                        driverManagerType.getBrowserName()
                                + " is not available as Docker container");
            }
            if (minusIndex == 0) {
                log.debug("The latest version of {} in Docker Hub is {}",
                        driverManagerType.getBrowserName(), latestVersion);
            } else {
                log.debug("The version-{} of {} in Docker Hub is {}",
                        minusIndex, driverManagerType.getBrowserName(),
                        latestVersion);
            }

        } else {
            latestVersion = resolutionCache
                    .getValueFromResolutionCache(cacheKey);
        }

        return latestVersion;
    }

    public int getMinusIndex(String browserVersion) {
        int minusIndex = 0;
        if (isBrowserVersionLatestMinus(browserVersion)) {
            minusIndex = Integer.parseInt(browserVersion
                    .substring(browserVersion.indexOf(LATEST_MINUS)
                            + LATEST_MINUS.length()));
        }
        return minusIndex;
    }

    public String getDockerImage(String browserName, String browserVersion,
            boolean androidEnabled) {
        String dockerImageFormat;
        String dockerImage;
        switch (browserName) {
        case "edge":
        case "safari":
            dockerImageFormat = config.getDockerBrowserAerokubeImageFormat();
            dockerImage = String.format(dockerImageFormat, browserName,
                    browserVersion);
            break;

        default:
            dockerImageFormat = getDockerImageFormat(browserVersion,
                    androidEnabled);
            dockerImage = String.format(dockerImageFormat, browserName,
                    browserVersion);
            break;
        }

        log.trace("Docker image: {}", dockerImage);
        return dockerImage;
    }

    public String getDockerImageFormat(String browserVersion,
            boolean androidEnabled) {
        String dockerImageFormat;
        if (isBrowserVersionBetaOrDev(browserVersion)) {
            dockerImageFormat = config.getDockerBrowserTwilioImageFormat();
        } else if (androidEnabled) {
            dockerImageFormat = config.getDockerBrowserMobileImageFormat();
        } else {
            dockerImageFormat = config.getDockerBrowserSelenoidImageFormat();
        }
        return dockerImageFormat;
    }

    public boolean isBrowserVersionWildCard(String browserVersion) {
        return isBrowserVersionBetaOrDev(browserVersion)
                || isBrowserVersionLatestMinus(browserVersion);
    }

    public boolean isBrowserVersionBetaOrDev(String browserVersion) {
        return browserVersion.equalsIgnoreCase(BETA)
                || browserVersion.equalsIgnoreCase(DEV);
    }

    public boolean isBrowserVersionLatestMinus(String browserVersion) {
        return browserVersion.toLowerCase(ROOT).contains(LATEST_MINUS);
    }

    private String getPrefixedDockerImage(String dockerImage) {
        String newDockerImage = dockerImage;
        String prefix = config.getDockerPrivateEndpoint();
        if (StringUtils.isNotBlank(prefix)) {
            newDockerImage = String.format("%s/%s", prefix, dockerImage);
        }
        return newDockerImage;
    }

    public DockerContainer startNoVncContainer(String dockerImage,
            String cacheKey, String browserVersion,
            DockerContainer browserContainer) {

        dockerImage = getPrefixedDockerImage(dockerImage);
        // pull image
        pullImageIfNecessary(cacheKey, dockerImage, browserVersion);

        // exposed ports
        List exposedPorts = new ArrayList<>();
        String dockerNoVncPort = String.valueOf(config.getDockerNoVncPort());
        exposedPorts.add(dockerNoVncPort);

        // envs
        List envs = new ArrayList<>();
        envs.add("AUTOCONNECT=true");
        envs.add("VIEW_ONLY=" + config.isDockerViewOnly());
        envs.add("VNC_PASSWORD=" + config.getDockerVncPassword());
        String vncAddress = browserContainer.getAddress();
        String vncPort = String.valueOf(config.getDockerVncPort());
        envs.add("VNC_SERVER=" + vncAddress + ":" + vncPort);

        // network
        String network = config.getDockerNetwork();

        // extra hosts
        List extraHosts = config.getDockerExtraHosts();

        // builder
        DockerContainer noVncContainer = DockerContainer
                .dockerBuilder(dockerImage).exposedPorts(exposedPorts)
                .network(network).extraHosts(extraHosts).envs(envs).build();

        String containerId = startContainer(noVncContainer);

        noVncContainer.setContainerId(containerId);
        String noVncHost = getDefaultHost();
        String noVncPort = isHost(network) ? dockerNoVncPort
                : getBindPort(containerId, dockerNoVncPort + "/tcp");
        String noVncUrlFormat = "http://%s:%s/";
        String noVncUrl = format(noVncUrlFormat, noVncHost, noVncPort);
        noVncContainer.setContainerUrl(noVncUrl);

        return noVncContainer;
    }

    public DockerContainer startBrowserContainer(String dockerImage,
            String cacheKey, String browserVersion, boolean androidEnabled) {
        dockerImage = getPrefixedDockerImage(dockerImage);
        // pull image
        pullImageIfNecessary(cacheKey, dockerImage, browserVersion);

        // exposed ports
        List exposedPorts = new ArrayList<>();
        String dockerBrowserPort = String
                .valueOf(config.getDockerBrowserPort());
        exposedPorts.add(dockerBrowserPort);

        // shmSize
        long shmSize = config.getDockerMemSizeBytes(config.getDockerShmSize());

        // mounts
        List mounts = new ArrayList<>();
        Mount tmpfsMount = new Mount()
                .withTmpfsOptions(new TmpfsOptions().withSizeBytes(config
                        .getDockerMemSizeBytes(config.getDockerTmpfsSize())))
                .withTarget(config.getDockerTmpfsMount());
        mounts.add(tmpfsMount);

        // binds
        List binds = new ArrayList<>();
        String dockerVolumes = config.getDockerVolumes();
        if (!isNullOrEmpty(dockerVolumes)) {
            List volumeList = Arrays.asList(dockerVolumes.split(","));
            log.trace("Using custom volumes: {}", volumeList);
            binds.addAll(volumeList);
        }

        // envs
        List envs = new ArrayList<>();
        envs.add("TZ=" + config.getDockerTimezone());
        envs.add("LANGUAGE=" + config.getDockerLang());
        envs.add("SCREEN_RESOLUTION=" + config.getDockerScreenResolution());
        envs.addAll(config.getDockerEnvVariables());

        String dockerVncPort = String.valueOf(config.getDockerVncPort());
        if (config.isDockerEnabledVnc()) {
            envs.add("ENABLE_VNC=true");
            exposedPorts.add(dockerVncPort);
        }
        if (androidEnabled) {
            envs.add("QTWEBENGINE_DISABLE_SANDBOX=1");
        }
        if (isChromeAllowedOrigins(dockerImage, browserVersion)) {
            envs.add("DRIVER_ARGS=--whitelisted-ips= --allowed-origins=*");
        }

        // network
        String network = config.getDockerNetwork();

        // extra hosts
        List extraHosts = config.getDockerExtraHosts();

        // builder
        DockerBuilder dockerBuilder = DockerContainer.dockerBuilder(dockerImage)
                .exposedPorts(exposedPorts).network(network).mounts(mounts)
                .binds(binds).shmSize(shmSize).envs(envs).extraHosts(extraHosts)
                .sysadmin();
        if (androidEnabled) {
            dockerBuilder = dockerBuilder.privileged();
        }
        DockerContainer browserContainer = dockerBuilder.build();

        String containerId = startContainer(browserContainer);
        browserContainer.setContainerId(containerId);
        String gateway = getGateway(containerId, network);
        browserContainer.setGateway(gateway);
        String browserHost = getHost(containerId, network);
        String browserPort = isHost(network) ? dockerBrowserPort
                : getBindPort(containerId, dockerBrowserPort + "/tcp");
        String browserUrlFormat = "http://%s:%s/";
        if (dockerImage.contains("firefox") || androidEnabled) {
            browserUrlFormat += "wd/hub";
        }

        String browserUrl = format(browserUrlFormat, browserHost, browserPort);
        browserContainer.setContainerUrl(browserUrl);
        String address = getAddress(containerId, network);
        browserContainer.setAddress(address);
        log.trace("Browser remote URL {}", browserUrl);

        if (config.isDockerEnabledVnc()) {
            String vncPort = isHost(network) ? dockerVncPort
                    : getBindPort(containerId, dockerVncPort + "/tcp");
            browserContainer.setVncPort(vncPort);
            String vncAddress = format("vnc://%s:%s/", getDefaultHost(),
                    vncPort);
            log.debug("VNC server URL: {}", vncAddress);
            browserContainer.setVncAddress(vncAddress);
        }

        return browserContainer;
    }

    private boolean isHost(String dockerNetwork) {
        return dockerNetwork.equalsIgnoreCase(NETWORK_HOST);
    }

    private boolean isChromeAllowedOrigins(String dockerImage,
            String browserVersion) {
        if (dockerImage.contains("chrome")) {
            browserVersion = browserVersion
                    .replaceAll(config.getBrowserVersionDetectionRegex(), "");
            return new VersionComparator().compare(browserVersion, "95") >= 0;
        }
        return false;
    }

    public DockerContainer startRecorderContainer(String dockerImage,
            String cacheKey, String recorderVersion,
            DockerContainer browserContainer) {
        dockerImage = getPrefixedDockerImage(dockerImage);
        // pull image
        pullImageIfNecessary(cacheKey, dockerImage, recorderVersion);

        // network
        String network = config.getDockerNetwork();

        // envs
        List envs = new ArrayList<>();
        String browserAddress = isHost(network) ? browserContainer.getGateway()
                : browserContainer.getAddress();
        envs.add("BROWSER_CONTAINER_NAME=" + browserAddress);
        Path recordingPath = getRecordingPath(browserContainer);
        envs.add("FILE_NAME=" + recordingPath.getFileName().toString());
        envs.add("VIDEO_SIZE=" + config.getDockerVideoSize());
        envs.add("FRAME_RATE=" + config.getDockerRecordingFrameRate());

        // extra hosts
        List extraHosts = config.getDockerExtraHosts();

        // binds
        List binds = new ArrayList<>();
        binds.add(recordingPath.toAbsolutePath().getParent().toString()
                + ":/data");
        String dockerVolumes = config.getDockerVolumes();
        if (!isNullOrEmpty(dockerVolumes)) {
            List volumeList = Arrays.asList(dockerVolumes.split(","));
            log.trace("Using custom volumes: {}", volumeList);
            binds.addAll(volumeList);
        }

        // builder
        DockerContainer recorderContainer = DockerContainer
                .dockerBuilder(dockerImage).network(network).envs(envs)
                .binds(binds).extraHosts(extraHosts).sysadmin().build();

        String containerId = startContainer(recorderContainer);
        recorderContainer.setContainerId(containerId);
        recorderContainer.setRecordingPath(recordingPath);

        return recorderContainer;
    }

    public Path getRecordingPath(DockerContainer browserContainer) {
        Path recordingPath;
        Path dockerRecordingPath = config.getDockerRecordingOutput();

        if (dockerRecordingPath.toString().toLowerCase(ROOT)
                .endsWith(RECORDING_EXT)) {
            recordingPath = dockerRecordingPath;
        } else {
            String sessionId = browserContainer.getSessionId();
            Date now = new Date();
            SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);
            String recordingFileName = browserContainer.getBrowserName()
                    + SEPARATOR + dateFormat.format(now) + SEPARATOR + sessionId
                    + RECORDING_EXT;
            String prefix = config.getDockerRecordingPrefix();
            if (!isNullOrEmpty(prefix)) {
                recordingFileName = prefix + recordingFileName;
            }
            recordingPath = Paths.get(dockerRecordingPath.toString(),
                    recordingFileName);
        }

        return recordingPath;
    }

    public String getVersionFromImage(String dockerImage) {
        return dockerImage.substring(dockerImage.indexOf(":") + 1);
    }

    public DockerClient getDockerClient() {
        return dockerClient;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy