Skip to content

Commit

Permalink
Merge pull request #79 from nmiyake/dockerTypeRefactor
Browse files Browse the repository at this point in the history
Make default localMachine() choose DockerType based on environment
  • Loading branch information
hpryce authored Jun 17, 2016
2 parents d81225d + 8d840f5 commit 1aed4aa
Show file tree
Hide file tree
Showing 9 changed files with 165 additions and 96 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,26 @@
import static com.palantir.docker.compose.configuration.EnvironmentVariables.DOCKER_TLS_VERIFY;
import static java.util.stream.Collectors.joining;

import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

public final class DaemonEnvironmentValidator {
public final class DaemonEnvironmentValidator implements EnvironmentValidator {

private static final Set<String> ILLEGAL_VARIABLES = ImmutableSet.of(DOCKER_TLS_VERIFY, DOCKER_HOST, DOCKER_CERT_PATH);
private static final Supplier<DaemonEnvironmentValidator> SUPPLIER = Suppliers.memoize(
() -> new DaemonEnvironmentValidator());

public static DaemonEnvironmentValidator instance() {
return SUPPLIER.get();
}

private DaemonEnvironmentValidator() {}

public static Map<String, String> validate(Map<String, String> dockerEnvironment) {
public void validateEnvironmentVariables(Map<String, String> dockerEnvironment) {
Set<String> invalidVariables = ILLEGAL_VARIABLES.stream()
.filter(dockerEnvironment::containsKey)
.collect(Collectors.toSet());
Expand All @@ -42,7 +50,6 @@ public static Map<String, String> validate(Map<String, String> dockerEnvironment
"These variables were set: ",
". They cannot be set when connecting to a local docker daemon."));
checkState(invalidVariables.isEmpty(), errorMessage);
return dockerEnvironment;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,41 @@
*/
package com.palantir.docker.compose.configuration;

import com.google.common.base.StandardSystemProperty;
import java.util.Map;
import java.util.Optional;

public enum DockerType {
public enum DockerType implements HostIpResolver, EnvironmentValidator {
DAEMON(DaemonEnvironmentValidator.instance(), new DaemonHostIpResolver()),
REMOTE(RemoteEnvironmentValidator.instance(), new RemoteHostIpResolver());

DAEMON, REMOTE;
private final EnvironmentValidator validator;
private final HostIpResolver resolver;

public static final String MAC_OS = "Mac";
DockerType(EnvironmentValidator validator, HostIpResolver resolver) {
this.validator = validator;
this.resolver = resolver;
}

@Override
public void validateEnvironmentVariables(Map<String, String> dockerEnvironment) {
validator.validateEnvironmentVariables(dockerEnvironment);
}

@Override
public String resolveIp(String dockerHost) {
return resolver.resolveIp(dockerHost);
}

public static DockerType getDefaultLocalDockerType() {
if (StandardSystemProperty.OS_NAME.value().startsWith(MAC_OS)) {
return REMOTE;
} else {
return DAEMON;
public static Optional<DockerType> getFirstValidDockerTypeForEnvironment(Map<String, String> environment) {
for (DockerType currType : DockerType.values()) {
try {
currType.validateEnvironmentVariables(environment);
return Optional.of(currType);
} catch (IllegalStateException e) {
// ignore and try next type
}
}
return Optional.empty();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright 2016 Palantir Technologies, Inc. All rights reserved.
*
* 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 com.palantir.docker.compose.configuration;

import java.util.Map;

public interface EnvironmentValidator {

/**
* Validates that the entries in the provided map are valid for the current environment.
* The provided map represents the environment variables that should be used for the
* process, where the keys are the environment variable names and the values are the values.
* If the validator determines the state represented by the map is invalid (either because
* required values are missing or forbidden values are present), the method should throw
* an exception.
*/
void validateEnvironmentVariables(Map<String, String> dockerEnvironment);

}
Original file line number Diff line number Diff line change
Expand Up @@ -30,45 +30,43 @@
import java.util.Set;
import java.util.stream.Collectors;

public class RemoteEnvironmentValidator {
public final class RemoteEnvironmentValidator implements EnvironmentValidator {

private static final Set<String> SECURE_VARIABLES = ImmutableSet.of(DOCKER_TLS_VERIFY, DOCKER_CERT_PATH);
private static final RemoteEnvironmentValidator VALIDATOR = new RemoteEnvironmentValidator();

private final Map<String, String> dockerEnvironment;
public static RemoteEnvironmentValidator instance() {
return VALIDATOR;
}

public RemoteEnvironmentValidator(Map<String, String> dockerEnvironment) {
this.dockerEnvironment = dockerEnvironment;
private RemoteEnvironmentValidator() {
}

public Map<String, String> validate() {
Collection<String> missingVariables = getMissingEnvVariables();
public void validateEnvironmentVariables(Map<String, String> dockerEnvironment) {
Collection<String> missingVariables = getMissingEnvVariables(dockerEnvironment);
String errorMessage = missingVariables.stream()
.collect(joining(", ",
"Missing required environment variables: ",
". Please run `docker-machine env <machine-name>` and "
+ "ensure they are set on the DockerComposition."));

Preconditions.checkState(missingVariables.isEmpty(), errorMessage);
return dockerEnvironment;
}

private Collection<String> getMissingEnvVariables() {
Collection<String> requiredVariables = Sets.union(newHashSet(DOCKER_HOST), secureVariablesRequired());
private Collection<String> getMissingEnvVariables(Map<String, String> dockerEnvironment) {
Collection<String> requiredVariables = Sets.union(newHashSet(DOCKER_HOST),
secureVariablesRequired(dockerEnvironment));
return requiredVariables.stream()
.filter(envVariable -> Strings.isNullOrEmpty(dockerEnvironment.get(envVariable)))
.collect(Collectors.toSet());
}

private Set<String> secureVariablesRequired() {
return certVerificationEnabled() ? SECURE_VARIABLES : newHashSet();
private Set<String> secureVariablesRequired(Map<String, String> dockerEnvironment) {
return certVerificationEnabled(dockerEnvironment) ? SECURE_VARIABLES : newHashSet();
}

private boolean certVerificationEnabled() {
private boolean certVerificationEnabled(Map<String, String> dockerEnvironment) {
return dockerEnvironment.containsKey(DOCKER_TLS_VERIFY);
}

public static Map<String, String> validate(Map<String, String> dockerEnvironment) {
return new RemoteEnvironmentValidator(dockerEnvironment).validate();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,20 @@

import com.google.common.collect.ImmutableMap;
import com.palantir.docker.compose.configuration.AdditionalEnvironmentValidator;
import com.palantir.docker.compose.configuration.DaemonEnvironmentValidator;
import com.palantir.docker.compose.configuration.DaemonHostIpResolver;
import com.palantir.docker.compose.configuration.DockerType;
import com.palantir.docker.compose.configuration.HostIpResolver;
import com.palantir.docker.compose.configuration.RemoteEnvironmentValidator;
import com.palantir.docker.compose.configuration.RemoteHostIpResolver;
import com.palantir.docker.compose.execution.DockerConfiguration;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DockerMachine implements DockerConfiguration {

private static final Logger log = LoggerFactory.getLogger(DockerMachine.class);
private static final DockerType FALLBACK_DOCKER_TYPE = DockerType.DAEMON;

private final String hostIp;
private final Map<String, String> environment;

Expand All @@ -59,7 +61,15 @@ private void augmentGivenEnvironment(Map<String, String> environmentToAugment) {
}

public static LocalBuilder localMachine() {
return localMachine(DockerType.getDefaultLocalDockerType());
Map<String, String> systemEnv = System.getenv();
Optional<DockerType> dockerType = DockerType.getFirstValidDockerTypeForEnvironment(systemEnv);
if (!dockerType.isPresent()) {
log.debug(
"Failed to determine Docker type (daemon or remote) based on current environment. "
+ "Proceeding with {} as the type.", FALLBACK_DOCKER_TYPE);
}

return new LocalBuilder(dockerType.orElse(FALLBACK_DOCKER_TYPE), systemEnv);
}

public static LocalBuilder localMachine(DockerType dockerType) {
Expand Down Expand Up @@ -88,22 +98,15 @@ public LocalBuilder withEnvironment(Map<String, String> newEnvironment) {
}

public DockerMachine build() {
HostIpResolver hostIp;
if (DockerType.DAEMON == dockerType) {
DaemonEnvironmentValidator.validate(systemEnvironment);
hostIp = new DaemonHostIpResolver();
} else {
RemoteEnvironmentValidator.validate(systemEnvironment);
hostIp = new RemoteHostIpResolver();
}
dockerType.validateEnvironmentVariables(systemEnvironment);
AdditionalEnvironmentValidator.validate(additionalEnvironment);
Map<String, String> environment = ImmutableMap.<String, String>builder()
.putAll(systemEnvironment)
.putAll(additionalEnvironment)
.build();

String dockerHost = systemEnvironment.getOrDefault(DOCKER_HOST, "");
return new DockerMachine(hostIp.resolveIp(dockerHost), environment);
return new DockerMachine(dockerType.resolveIp(dockerHost), environment);
}
}

Expand Down Expand Up @@ -146,7 +149,7 @@ public RemoteBuilder withEnvironment(Map<String, String> newEnvironment) {
}

public DockerMachine build() {
RemoteEnvironmentValidator.validate(dockerEnvironment);
DockerType.REMOTE.validateEnvironmentVariables(dockerEnvironment);
AdditionalEnvironmentValidator.validate(additionalEnvironment);

String dockerHost = dockerEnvironment.getOrDefault(DOCKER_HOST, "");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@
import static com.palantir.docker.compose.configuration.EnvironmentVariables.DOCKER_CERT_PATH;
import static com.palantir.docker.compose.configuration.EnvironmentVariables.DOCKER_HOST;
import static com.palantir.docker.compose.configuration.EnvironmentVariables.DOCKER_TLS_VERIFY;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;

import com.google.common.collect.ImmutableMap;
import java.util.Map;
Expand All @@ -39,7 +37,7 @@ public void validate_successfully_when_docker_environment_does_not_contain_docke
.put("ANOTHER_VARIABLE", "ANOTHER_VALUE")
.build();

assertThat(DaemonEnvironmentValidator.validate(variables), is(variables));
DaemonEnvironmentValidator.instance().validateEnvironmentVariables(variables);
}

@Test
Expand All @@ -56,7 +54,7 @@ public void throw_exception_when_docker_environment_contains_illegal_docker_vari
exception.expectMessage(DOCKER_CERT_PATH);
exception.expectMessage(DOCKER_TLS_VERIFY);
exception.expectMessage("They cannot be set when connecting to a local docker daemon");
assertThat(DaemonEnvironmentValidator.validate(variables), is(variables));
DaemonEnvironmentValidator.instance().validateEnvironmentVariables(variables);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright 2016 Palantir Technologies, Inc. All rights reserved.
*
* 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 com.palantir.docker.compose.configuration;

import static com.palantir.docker.compose.configuration.EnvironmentVariables.DOCKER_CERT_PATH;
import static com.palantir.docker.compose.configuration.EnvironmentVariables.DOCKER_HOST;
import static com.palantir.docker.compose.configuration.EnvironmentVariables.DOCKER_TLS_VERIFY;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;

import com.google.common.collect.ImmutableMap;
import java.util.Map;
import java.util.Optional;
import org.junit.Test;

public class DockerTypeShould {

@Test
public void return_remote_as_first_valid_type_if_environment_is_illegal_for_daemon() {
Map<String, String> variables = ImmutableMap.<String, String>builder()
.put(DOCKER_HOST, "tcp://192.168.99.100:2376")
.put(DOCKER_TLS_VERIFY, "1")
.put(DOCKER_CERT_PATH, "/path/to/certs")
.build();
assertThat(DockerType.getFirstValidDockerTypeForEnvironment(variables), is(Optional.of(DockerType.REMOTE)));
}

@Test
public void return_daemon_as_first_valid_type_if_environment_is_illegal_for_remote() {
Map<String, String> variables = ImmutableMap.of();
assertThat(DockerType.getFirstValidDockerTypeForEnvironment(variables), is(Optional.of(DockerType.DAEMON)));
}

@Test
public void return_absent_as_first_valid_type_if_environment_is_illegal_for_all() {
Map<String, String> variables = ImmutableMap.<String, String>builder()
.put(DOCKER_TLS_VERIFY, "1")
.build();
assertThat(DockerType.getFirstValidDockerTypeForEnvironment(variables), is(Optional.empty()));
}

}

This file was deleted.

Loading

0 comments on commit 1aed4aa

Please sign in to comment.