package org.arquillian.cube.docker.impl.client; import java.io.File; import java.net.InetAddress; import java.net.URI; import java.net.URISyntaxException; import java.util.Map; import java.util.Random; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import org.arquillian.cube.docker.impl.util.AbstractCliInternetAddressResolver; import org.arquillian.cube.docker.impl.util.Boot2Docker; import org.arquillian.cube.docker.impl.util.DockerMachine; import org.arquillian.cube.docker.impl.util.GitHubUtil; import org.arquillian.cube.docker.impl.util.HomeResolverUtil; import org.arquillian.cube.docker.impl.util.Machine; import org.arquillian.cube.docker.impl.util.OperatingSystemFamily; import org.arquillian.cube.docker.impl.util.Top; import org.arquillian.cube.impl.util.Strings; import org.arquillian.cube.impl.util.SystemEnvironmentVariables; import org.arquillian.spacelift.Spacelift; import org.arquillian.spacelift.task.net.DownloadTool; import static org.arquillian.cube.docker.impl.client.CubeDockerConfigurator.DOCKER_HOST; public class CubeDockerConfigurationResolver { private static final String UNIX_SOCKET_SCHEME = "unix"; private static final String TCP_SCHEME = "tcp"; private static final String HTTP_SCHEME = "http"; private static final String HTTPS_SCHEME = "https"; private static final String DOCKER_TLS_VERIFY = "DOCKER_TLS_VERIFY"; private static final String DOCKER_CERT_PATH = "DOCKER_CERT_PATH"; private static final String DOCKER_MACHINE_NAME = "DOCKER_MACHINE_NAME"; private static Random random = new Random(); private static Logger log = Logger.getLogger(CubeDockerConfigurationResolver.class.getName()); private final Top top; private final DockerMachine dockerMachine; private final Boot2Docker boot2Docker; private final OperatingSystemFamily operatingSystemFamily; public CubeDockerConfigurationResolver(Top top, DockerMachine dockerMachine, Boot2Docker boot2Docker, OperatingSystemFamily operatingSystemFamily) { this.top = top; this.dockerMachine = dockerMachine; this.boot2Docker = boot2Docker; this.operatingSystemFamily = operatingSystemFamily; } /** * Resolves the configuration. * * @param config * The specified configuration. * * @return The resolved configuration. */ public Map<String, String> resolve(Map<String, String> config) { config = resolveSystemEnvironmentVariables(config); config = resolveDockerInsideDocker(config); config = resolveDownloadDockerMachine(config); config = resolveAutoStartDockerMachine(config); config = resolveDefaultDockerMachine(config); config = resolveServerUriByOperativeSystem(config); config = resolveServerIp(config); config = resolveTlsVerification(config); return config; } private Map<String, String> resolveDockerInsideDocker(Map<String, String> cubeConfiguration) { // if DIND_RESOLUTION property is not set, since by default is enabled, we need to go inside code. if (!cubeConfiguration.containsKey(CubeDockerConfiguration.DIND_RESOLUTION) || Boolean.parseBoolean( cubeConfiguration.get(CubeDockerConfiguration.DIND_RESOLUTION))) { if (top.isSpinning()) { log.fine(String.format( "Your Cube tests are going to run inside a running Docker container. %s property is replaced to %s", CubeDockerConfiguration.DOCKER_URI, OperatingSystemFamily.DIND.getServerUri())); String serverUri = OperatingSystemFamily.DIND.getServerUri(); cubeConfiguration.put(CubeDockerConfiguration.DOCKER_URI, serverUri); } } return cubeConfiguration; } private Map<String, String> resolveDownloadDockerMachine(Map<String, String> config) { if (config.containsKey(CubeDockerConfiguration.DOCKER_MACHINE_NAME)) { final String cliPathExec = config.get(CubeDockerConfiguration.DOCKER_MACHINE_PATH); if (!dockerMachine.isDockerMachineInstalled(cliPathExec)) { String machineVersion = GitHubUtil.getDockerMachineLatestVersion(); String machineCustomPath = config.get(CubeDockerConfiguration.DOCKER_MACHINE_CUSTOM_PATH); File dockerMachineFile = CubeDockerConfiguration.resolveMachinePath(machineCustomPath, machineVersion); String dockerMachinePath = dockerMachineFile.getPath(); boolean dockerMachineFileExist = dockerMachineFile != null && dockerMachineFile.exists(); String machineName = config.get(CubeDockerConfiguration.DOCKER_MACHINE_NAME); String machineUrl = CubeDockerConfiguration.resolveUrl(machineVersion); if (!dockerMachineFileExist) { dockerMachineFile.getParentFile().mkdirs(); Spacelift.task(DownloadTool.class) .from(machineUrl) .to(dockerMachineFile) .execute() .await(); config.put(CubeDockerConfiguration.DOCKER_MACHINE_PATH, dockerMachinePath); dockerMachine.grantPermissionToDockerMachine(dockerMachinePath); String machineDriver = config.get(CubeDockerConfiguration.DOCKER_MACHINE_DRIVER); dockerMachine.createMachine(dockerMachinePath, machineDriver, machineName); } else { config.put(CubeDockerConfiguration.DOCKER_MACHINE_PATH, dockerMachinePath); } } } return config; } private Map<String, String> resolveAutoStartDockerMachine(Map<String, String> config) { if (config.containsKey(CubeDockerConfiguration.DOCKER_MACHINE_NAME)) { final String cliPathExec = config.get(CubeDockerConfiguration.DOCKER_MACHINE_PATH); if (dockerMachine.isDockerMachineInstalled(cliPathExec)) { String machineName = config.get(CubeDockerConfiguration.DOCKER_MACHINE_NAME); final Set<Machine> machines = dockerMachine.list(cliPathExec, "name", machineName); if (machines.size() == 1) { Machine machine = getFirstMachine(machines); if (machine.getState().equalsIgnoreCase("Stopped")) { dockerMachine.startDockerMachine(cliPathExec, machineName); } } else { log.log(Level.SEVERE, String.format( "You are trying to run containers in Docker Machine %s but %s Docker Machines instances are installed.", config.get(CubeDockerConfiguration.DOCKER_MACHINE_NAME), machines)); } } else { log.log(Level.SEVERE, String.format( "You are trying to run containers in Docker Machine %s but no docker-machine installed.", config.get(CubeDockerConfiguration.DOCKER_MACHINE_NAME))); } } return config; } private Map<String, String> resolveDefaultDockerMachine(Map<String, String> config) { // if user has not specified Docker URI host not a docker machine // setting DOCKER_URI to avoid using docker machine although it is installed if (!config.containsKey(CubeDockerConfiguration.DOCKER_URI) && !config.containsKey( CubeDockerConfiguration.DOCKER_MACHINE_NAME)) { log.fine( "No DockerUri or DockerMachine has been set, let's see if there is only one Docker Machine Running."); if (dockerMachine.isDockerMachineInstalled(config.get(CubeDockerConfiguration.DOCKER_MACHINE_PATH))) { // we can inspect if docker machine has one and only one docker machine running, which means that would like to use that one Set<Machine> machines = this.dockerMachine.list(config.get(CubeDockerConfiguration.DOCKER_MACHINE_PATH), "state", "Running"); // if there is only one machine running we can use that one. // if not Cube will resolve the default URI depending on OS (linux socket, boot2docker, ...) if (machines.size() == 1) { log.fine(String.format("One Docker Machine is running (%s) and it is going to be used for tests", machines.iterator().next().getName())); config.put(CubeDockerConfiguration.DOCKER_MACHINE_NAME, getFirstMachine(machines).getName()); } } } return config; } private Machine getFirstMachine(Set<Machine> machines) { return machines.iterator().next(); } private Map<String, String> resolveSystemEnvironmentVariables(Map<String, String> config) { if (!config.containsKey(CubeDockerConfiguration.DOCKER_URI) && isDockerHostSet()) { String dockerHostUri = SystemEnvironmentVariables.getEnvironmentOrPropertyVariable(DOCKER_HOST); config.put(CubeDockerConfiguration.DOCKER_URI, dockerHostUri); } if (!config.containsKey(CubeDockerConfiguration.CERT_PATH) && isDockerCertPathSet()) { String dockerCertPath = SystemEnvironmentVariables.getEnvironmentOrPropertyVariable(DOCKER_CERT_PATH); config.put(CubeDockerConfiguration.CERT_PATH, dockerCertPath); } if (!config.containsKey(CubeDockerConfiguration.DOCKER_MACHINE_NAME) && isDockerMachineNameSet()) { String dockerMachineName = SystemEnvironmentVariables.getEnvironmentOrPropertyVariable(DOCKER_MACHINE_NAME); config.put(CubeDockerConfiguration.DOCKER_MACHINE_NAME, dockerMachineName); } if (!config.containsKey(CubeDockerConfiguration.TLS_VERIFY) && isDockerTlsVerifySet()) { config.put(CubeDockerConfiguration.TLS_VERIFY, Boolean.TRUE.toString()); } return config; } private Map<String, String> resolveServerIp(Map<String, String> config) { String dockerServerUri = config.get(CubeDockerConfiguration.DOCKER_URI); if (containsDockerHostTag(dockerServerUri)) { if (isDockerMachineSet(config)) { dockerServerUri = resolveDockerMachine(dockerServerUri, config.get(CubeDockerConfiguration.DOCKER_MACHINE_NAME), config.get(CubeDockerConfiguration.DOCKER_MACHINE_PATH)); } else { dockerServerUri = resolveBoot2Docker(dockerServerUri, config.get(CubeDockerConfiguration.BOOT2DOCKER_PATH)); } } config.put(CubeDockerConfiguration.DOCKER_URI, dockerServerUri); if (!config.containsKey(CubeDockerConfiguration.CERT_PATH)) { config.put(CubeDockerConfiguration.CERT_PATH, HomeResolverUtil.resolveHomeDirectoryChar(getDefaultTlsDirectory(config))); } resolveDockerServerIp(config, dockerServerUri); return config; } private Map<String, String> resolveTlsVerification(Map<String, String> config) { URI serverUri = URI.create(config.get(CubeDockerConfiguration.DOCKER_URI)); String scheme = serverUri.getScheme(); if (scheme.equals(HTTP_SCHEME)) { config.remove(CubeDockerConfiguration.TLS_VERIFY); } if (scheme.equals(HTTPS_SCHEME)) { config.put(CubeDockerConfiguration.TLS_VERIFY, Boolean.toString(true)); } if (scheme.equals(HTTP_SCHEME) || scheme.equals(HTTPS_SCHEME) || scheme.equals(TCP_SCHEME)) { if (!config.containsKey(CubeDockerConfiguration.TLS_VERIFY)) { config.put(CubeDockerConfiguration.TLS_VERIFY, Boolean.toString(scheme.equals(HTTPS_SCHEME))); } try { // docker-java supports only tcp and unix schemes serverUri = new URI(TCP_SCHEME, serverUri.getSchemeSpecificPart(), serverUri.getFragment()); config.put(CubeDockerConfiguration.DOCKER_URI, serverUri.toString()); } catch (URISyntaxException e) { throw new IllegalArgumentException(e); } } if (!config.containsKey(CubeDockerConfiguration.TLS_VERIFY)) { config.put(CubeDockerConfiguration.TLS_VERIFY, Boolean.toString(true)); if (operatingSystemFamily == OperatingSystemFamily.LINUX) { String dockerServerIp = config.get(CubeDockerConfiguration.DOCKER_SERVER_IP); if (isDockerMachineSet(config)) { if (InetAddress.getLoopbackAddress().getHostAddress().equals(dockerServerIp) || InetAddress.getLoopbackAddress().getHostName().equals(dockerServerIp)) { config.put(CubeDockerConfiguration.TLS_VERIFY, Boolean.toString(false)); } else { config.put(CubeDockerConfiguration.TLS_VERIFY, Boolean.toString(true)); } } else { config.put(CubeDockerConfiguration.TLS_VERIFY, Boolean.toString(false)); } } } if (Boolean.FALSE.toString().equals(config.get(CubeDockerConfiguration.TLS_VERIFY))) { config.remove(CubeDockerConfiguration.CERT_PATH); } return config; } private boolean containsDockerHostTag(String dockerServerUri) { return dockerServerUri.contains(AbstractCliInternetAddressResolver.DOCKERHOST_TAG); } private void resolveDockerServerIp(Map<String, String> config, String dockerServerUri) { URI serverUri = URI.create(dockerServerUri); String serverIp = UNIX_SOCKET_SCHEME.equalsIgnoreCase(serverUri.getScheme()) ? "localhost" : serverUri.getHost(); config.put(CubeDockerConfiguration.DOCKER_SERVER_IP, serverIp); } private boolean isDockerHostSet() { return Strings.isNotNullOrEmpty(SystemEnvironmentVariables.getEnvironmentOrPropertyVariable(DOCKER_HOST)); } private boolean isDockerCertPathSet() { return Strings.isNotNullOrEmpty(SystemEnvironmentVariables.getEnvironmentOrPropertyVariable(DOCKER_CERT_PATH)); } private boolean isDockerTlsVerifySet() { return Strings.isNotNullOrEmpty(SystemEnvironmentVariables.getEnvironmentOrPropertyVariable(DOCKER_TLS_VERIFY)); } private boolean isDockerMachineNameSet() { return Strings.isNotNullOrEmpty(SystemEnvironmentVariables.getEnvironmentOrPropertyVariable(DOCKER_MACHINE_NAME)); } private boolean isDockerMachineSet(Map<String, String> config) { return config.containsKey(CubeDockerConfiguration.DOCKER_MACHINE_NAME); } private String resolveDockerMachine(String tag, String machineName, String dockerMachinePath) { dockerMachine.setMachineName(machineName); return tag.replaceAll(AbstractCliInternetAddressResolver.DOCKERHOST_TAG, dockerMachine.ip(dockerMachinePath, false)); } private String resolveBoot2Docker(String tag, String boot2DockerPath) { return tag.replaceAll(AbstractCliInternetAddressResolver.DOCKERHOST_TAG, boot2Docker.ip(boot2DockerPath, false)); } private String getDefaultTlsDirectory(Map<String, String> config) { if (isDockerMachineSet(config)) { return "~" + File.separator + ".docker" + File.separator + "machine" + File.separator + "machines" + File.separator + config.get(CubeDockerConfiguration.DOCKER_MACHINE_NAME); } else { return "~" + File.separator + ".boot2docker" + File.separator + "certs" + File.separator + "boot2docker-vm"; } } private boolean containsCertPath(Map<String, String> cubeConfiguration) { return cubeConfiguration.containsKey(CubeDockerConfiguration.CERT_PATH); } private Map<String, String> resolveServerUriByOperativeSystem(Map<String, String> cubeConfiguration) { if (!cubeConfiguration.containsKey(CubeDockerConfiguration.DOCKER_URI)) { if (isDockerMachineSet(cubeConfiguration)) { String serverUri = OperatingSystemFamily.MACHINE.getServerUri(); cubeConfiguration.put(CubeDockerConfiguration.DOCKER_URI, serverUri); } else { String serverUri = operatingSystemFamily.getServerUri(); cubeConfiguration.put(CubeDockerConfiguration.DOCKER_URI, serverUri); } } return cubeConfiguration; } }