summaryrefslogtreecommitdiffstats
path: root/node-admin
diff options
context:
space:
mode:
authorValerij Fredriksen <valerijf@oath.com>2017-11-30 13:26:51 +0100
committerValerij Fredriksen <valerijf@oath.com>2017-11-30 15:06:21 +0100
commitf0b82b2885a1a2bccd24e00ecee83aad1dc45d94 (patch)
tree5abde388c51509fcf787969aeafdf86a9ee4f982 /node-admin
parentb46ab5dc9c298e855e47eea2a9a4f8e4f9012dcf (diff)
Node-Admin: Use HTTPS against config servers
Diffstat (limited to 'node-admin')
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/DockerOperationsImpl.java6
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/StorageMaintainer.java5
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/noderepository/NodeRepositoryImpl.java12
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/orchestrator/OrchestratorImpl.java8
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/provider/NodeAdminProvider.java9
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/util/ConfigServerHttpRequestExecutor.java82
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/util/Environment.java41
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/noderepository/NodeRepositoryImplTest.java24
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/orchestrator/OrchestratorImplTest.java15
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/util/ConfigServerHttpRequestExecutorTest.java69
10 files changed, 133 insertions, 138 deletions
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/DockerOperationsImpl.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/DockerOperationsImpl.java
index d3af55cdff5..d2863468ee7 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/DockerOperationsImpl.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/DockerOperationsImpl.java
@@ -18,11 +18,13 @@ import com.yahoo.vespa.hosted.node.admin.util.PrefixLogger;
import java.io.IOException;
import java.net.Inet6Address;
import java.net.InetAddress;
+import java.net.URI;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.stream.Collectors;
import java.util.stream.Stream;
import static com.yahoo.vespa.defaults.Defaults.getDefaults;
@@ -99,7 +101,9 @@ public class DockerOperationsImpl implements DockerOperations {
InetAddress nodeInetAddress = environment.getInetAddressForHost(nodeSpec.hostname);
final boolean isIPv6 = nodeInetAddress instanceof Inet6Address;
- String configServers = String.join(",", environment.getConfigServerHosts());
+ String configServers = environment.getConfigServerUris().stream()
+ .map(URI::getHost)
+ .collect(Collectors.joining(","));
Docker.CreateContainerCommand command = docker.createContainerCommand(
nodeSpec.wantedDockerImage.get(),
ContainerResources.from(nodeSpec.minCpuCores, nodeSpec.minMainMemoryAvailableGb),
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/StorageMaintainer.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/StorageMaintainer.java
index 763291bf86b..f41d7deb04b 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/StorageMaintainer.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/StorageMaintainer.java
@@ -21,6 +21,7 @@ import com.yahoo.vespa.hosted.node.admin.util.SecretAgentScheduleMaker;
import java.io.IOException;
import java.io.InputStreamReader;
+import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -310,8 +311,8 @@ public class StorageMaintainer {
* @throws RuntimeException if exit code != 0
*/
public String getHardwareDivergence() {
- String configServers = environment.getConfigServerHosts().stream()
- .map(configServer -> "http://" + configServer + ":" + 4080)
+ String configServers = environment.getConfigServerUris().stream()
+ .map(URI::getHost)
.collect(Collectors.joining(","));
return executeMaintainer("com.yahoo.vespa.hosted.node.verification.spec.SpecVerifier", configServers);
}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/noderepository/NodeRepositoryImpl.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/noderepository/NodeRepositoryImpl.java
index 8283c90e43d..b5c439e209a 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/noderepository/NodeRepositoryImpl.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/noderepository/NodeRepositoryImpl.java
@@ -31,11 +31,9 @@ public class NodeRepositoryImpl implements NodeRepository {
private static final PrefixLogger NODE_ADMIN_LOGGER = PrefixLogger.getNodeAdminLogger(NodeRepositoryImpl.class);
private final ConfigServerHttpRequestExecutor requestExecutor;
- private final int port;
- public NodeRepositoryImpl(ConfigServerHttpRequestExecutor requestExecutor, int port) {
+ public NodeRepositoryImpl(ConfigServerHttpRequestExecutor requestExecutor) {
this.requestExecutor = requestExecutor;
- this.port = port;
}
@Override
@@ -43,7 +41,6 @@ public class NodeRepositoryImpl implements NodeRepository {
try {
final GetNodesResponse nodesForHost = requestExecutor.get(
"/nodes/v2/node/?parentHost=" + baseHostName + "&recursive=true",
- port,
GetNodesResponse.class);
if (nodesForHost.nodes == null) {
@@ -71,7 +68,6 @@ public class NodeRepositoryImpl implements NodeRepository {
public Optional<ContainerNodeSpec> getContainerNodeSpec(String hostName) {
try {
GetNodesResponse.Node nodeResponse = requestExecutor.get("/nodes/v2/node/" + hostName,
- port,
GetNodesResponse.Node.class);
if (nodeResponse == null) {
return Optional.empty();
@@ -86,7 +82,7 @@ public class NodeRepositoryImpl implements NodeRepository {
public List<ContainerAclSpec> getContainerAclSpecs(String hostName) {
try {
final String path = String.format("/nodes/v2/acl/%s?children=true", hostName);
- final GetAclResponse response = requestExecutor.get(path, port, GetAclResponse.class);
+ final GetAclResponse response = requestExecutor.get(path, GetAclResponse.class);
return response.trustedNodes.stream()
.map(node -> new ContainerAclSpec(
node.hostname, node.ipAddress, ContainerName.fromHostname(node.trustedBy)))
@@ -96,7 +92,7 @@ public class NodeRepositoryImpl implements NodeRepository {
}
}
- public static ContainerNodeSpec createContainerNodeSpec(GetNodesResponse.Node node)
+ private static ContainerNodeSpec createContainerNodeSpec(GetNodesResponse.Node node)
throws IllegalArgumentException, NullPointerException {
Objects.requireNonNull(node.nodeState, "Unknown node state");
Node.State nodeState = Node.State.valueOf(node.nodeState);
@@ -145,7 +141,6 @@ public class NodeRepositoryImpl implements NodeRepository {
public void updateNodeAttributes(final String hostName, final NodeAttributes nodeAttributes) {
UpdateNodeAttributesResponse response = requestExecutor.patch(
"/nodes/v2/node/" + hostName,
- port,
new UpdateNodeAttributesRequestBody(nodeAttributes),
UpdateNodeAttributesResponse.class);
@@ -170,7 +165,6 @@ public class NodeRepositoryImpl implements NodeRepository {
private void markNodeToState(String hostName, String state) {
NodeMessageResponse response = requestExecutor.put(
"/nodes/v2/state/" + state + "/" + hostName,
- port,
Optional.empty(), /* body */
NodeMessageResponse.class);
NODE_ADMIN_LOGGER.info(response.message);
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/orchestrator/OrchestratorImpl.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/orchestrator/OrchestratorImpl.java
index d1e996f8e93..7093f3f12e7 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/orchestrator/OrchestratorImpl.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/orchestrator/OrchestratorImpl.java
@@ -26,11 +26,9 @@ public class OrchestratorImpl implements Orchestrator {
= ORCHESTRATOR_PATH_PREFIX + HostSuspensionApi.PATH_PREFIX;
private final ConfigServerHttpRequestExecutor requestExecutor;
- private final int port;
- public OrchestratorImpl(ConfigServerHttpRequestExecutor requestExecutor, int port) {
+ public OrchestratorImpl(ConfigServerHttpRequestExecutor requestExecutor) {
this.requestExecutor = requestExecutor;
- this.port = port;
}
@Override
@@ -38,7 +36,6 @@ public class OrchestratorImpl implements Orchestrator {
UpdateHostResponse response;
try {
response = requestExecutor.put(getSuspendPath(hostName),
- port,
Optional.empty(), /* body */
UpdateHostResponse.class);
} catch (HttpException.NotFoundException n) {
@@ -61,7 +58,6 @@ public class OrchestratorImpl implements Orchestrator {
try {
batchOperationResult = requestExecutor.put(
ORCHESTRATOR_PATH_PREFIX_HOST_SUSPENSION_API,
- port,
Optional.of(new BatchHostSuspendRequest(parentHostName, hostNames)),
BatchOperationResult.class);
} catch (HttpException e) {
@@ -81,7 +77,7 @@ public class OrchestratorImpl implements Orchestrator {
UpdateHostResponse response;
try {
String path = getSuspendPath(hostName);
- response = requestExecutor.delete(path, port, UpdateHostResponse.class);
+ response = requestExecutor.delete(path, UpdateHostResponse.class);
} catch (HttpException.NotFoundException n) {
throw new OrchestratorNotFoundException("Failed to resume " + hostName + ", host not found");
} catch (HttpException e) {
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/provider/NodeAdminProvider.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/provider/NodeAdminProvider.java
index 109dbab924c..a343e431b5a 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/provider/NodeAdminProvider.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/provider/NodeAdminProvider.java
@@ -29,8 +29,6 @@ import java.time.Clock;
import java.time.Duration;
import java.util.function.Function;
-import static com.yahoo.vespa.defaults.Defaults.getDefaults;
-
/**
* Set up node admin for production.
*
@@ -38,7 +36,6 @@ import static com.yahoo.vespa.defaults.Defaults.getDefaults;
*/
public class NodeAdminProvider implements Provider<NodeAdminStateUpdater> {
- private static final int WEB_SERVICE_PORT = getDefaults().vespaWebServicePort();
private static final Duration NODE_AGENT_SCAN_INTERVAL = Duration.ofSeconds(30);
private static final Duration NODE_ADMIN_CONVERGE_STATE_INTERVAL = Duration.ofSeconds(30);
@@ -51,9 +48,9 @@ public class NodeAdminProvider implements Provider<NodeAdminStateUpdater> {
ProcessExecuter processExecuter = new ProcessExecuter();
Environment environment = new Environment();
- ConfigServerHttpRequestExecutor requestExecutor = ConfigServerHttpRequestExecutor.create(environment.getConfigServerHosts());
- NodeRepository nodeRepository = new NodeRepositoryImpl(requestExecutor, WEB_SERVICE_PORT);
- Orchestrator orchestrator = new OrchestratorImpl(requestExecutor, WEB_SERVICE_PORT);
+ ConfigServerHttpRequestExecutor requestExecutor = ConfigServerHttpRequestExecutor.create(environment.getConfigServerUris());
+ NodeRepository nodeRepository = new NodeRepositoryImpl(requestExecutor);
+ Orchestrator orchestrator = new OrchestratorImpl(requestExecutor);
DockerOperations dockerOperations = new DockerOperationsImpl(docker, environment, processExecuter);
StorageMaintainer storageMaintainer = new StorageMaintainer(docker, processExecuter, metricReceiver, environment, clock);
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/util/ConfigServerHttpRequestExecutor.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/util/ConfigServerHttpRequestExecutor.java
index e825fa4ac8f..92bfb8b288c 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/util/ConfigServerHttpRequestExecutor.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/util/ConfigServerHttpRequestExecutor.java
@@ -12,18 +12,28 @@ import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.config.Registry;
+import org.apache.http.config.RegistryBuilder;
+import org.apache.http.conn.socket.ConnectionSocketFactory;
+import org.apache.http.conn.socket.PlainConnectionSocketFactory;
+import org.apache.http.conn.ssl.NoopHostnameVerifier;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+import org.apache.http.ssl.SSLContextBuilder;
+
import java.io.IOException;
import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.security.GeneralSecurityException;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
-import java.util.Set;
/**
* Retries request on config server a few times before giving up. Assumes that all requests should be sent with
@@ -33,11 +43,11 @@ import java.util.Set;
*/
public class ConfigServerHttpRequestExecutor {
private static final PrefixLogger NODE_ADMIN_LOGGER = PrefixLogger.getNodeAdminLogger(ConfigServerHttpRequestExecutor.class);
+ private static final int MAX_LOOPS = 2;
private final ObjectMapper mapper = new ObjectMapper();
private final CloseableHttpClient client;
- private final List<String> configServerHosts;
- private final static int MAX_LOOPS = 2;
+ private final List<URI> configServerHosts;
@Override
public void finalize() throws Throwable {
@@ -50,31 +60,45 @@ public class ConfigServerHttpRequestExecutor {
super.finalize();
}
- public static ConfigServerHttpRequestExecutor create(Set<String> configServerHosts) {
- if (configServerHosts.isEmpty()) {
- throw new IllegalStateException("Environment setting for config servers missing or empty.");
- }
+ public static ConfigServerHttpRequestExecutor create(Collection<URI> configServerUris) {
+ PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(getConnectionSocketFactoryRegistry());
+ cm.setMaxTotal(200); // Increase max total connections to 200, which should be enough
+
+ return new ConfigServerHttpRequestExecutor(randomizeConfigServerUris(configServerUris),
+ HttpClientBuilder.create()
+ .disableAutomaticRetries()
+ .setUserAgent("node-admin")
+ .setConnectionManager(cm).build());
+ }
- PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
- // Increase max total connections to 200, which should be enough
- cm.setMaxTotal(200);
- return new ConfigServerHttpRequestExecutor(configServerHosts,
- HttpClientBuilder.create().disableAutomaticRetries().setConnectionManager(cm).build());
+ private static Registry<ConnectionSocketFactory> getConnectionSocketFactoryRegistry() {
+ try {
+ SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(
+ new SSLContextBuilder().loadTrustMaterial(null, (arg0, arg1) -> true).build(),
+ NoopHostnameVerifier.INSTANCE);
+
+ return RegistryBuilder.<ConnectionSocketFactory>create()
+ .register("http", PlainConnectionSocketFactory.getSocketFactory())
+ .register("https", sslSocketFactory)
+ .build();
+ } catch (GeneralSecurityException e) {
+ throw new RuntimeException("Failed to create SSL context", e);
+ }
}
- ConfigServerHttpRequestExecutor(Set<String> configServerHosts, CloseableHttpClient client) {
- this.configServerHosts = randomizeConfigServerHosts(configServerHosts);
+ ConfigServerHttpRequestExecutor(List<URI> configServerHosts, CloseableHttpClient client) {
+ this.configServerHosts = configServerHosts;
this.client = client;
}
public interface CreateRequest {
- HttpUriRequest createRequest(String configserver) throws JsonProcessingException, UnsupportedEncodingException;
+ HttpUriRequest createRequest(URI configServerUri) throws JsonProcessingException, UnsupportedEncodingException;
}
private <T> T tryAllConfigServers(CreateRequest requestFactory, Class<T> wantedReturnType) {
Exception lastException = null;
for (int loopRetry = 0; loopRetry < MAX_LOOPS; loopRetry++) {
- for (String configServer : configServerHosts) {
+ for (URI configServer : configServerHosts) {
final CloseableHttpResponse response;
try {
response = client.execute(requestFactory.createRequest(configServer));
@@ -118,9 +142,9 @@ public class ConfigServerHttpRequestExecutor {
+ configServerHosts + ") failed, last as follows:", lastException);
}
- public <T> T put(String path, int port, Optional<Object> bodyJsonPojo, Class<T> wantedReturnType) {
+ public <T> T put(String path, Optional<Object> bodyJsonPojo, Class<T> wantedReturnType) {
return tryAllConfigServers(configServer -> {
- HttpPut put = new HttpPut("http://" + configServer + ":" + port + path);
+ HttpPut put = new HttpPut(configServer.resolve(path));
setContentTypeToApplicationJson(put);
if (bodyJsonPojo.isPresent()) {
put.setEntity(new StringEntity(mapper.writeValueAsString(bodyJsonPojo.get())));
@@ -129,28 +153,28 @@ public class ConfigServerHttpRequestExecutor {
}, wantedReturnType);
}
- public <T> T patch(String path, int port, Object bodyJsonPojo, Class<T> wantedReturnType) {
+ public <T> T patch(String path, Object bodyJsonPojo, Class<T> wantedReturnType) {
return tryAllConfigServers(configServer -> {
- HttpPatch patch = new HttpPatch("http://" + configServer + ":" + port + path);
+ HttpPatch patch = new HttpPatch(configServer.resolve(path));
setContentTypeToApplicationJson(patch);
patch.setEntity(new StringEntity(mapper.writeValueAsString(bodyJsonPojo)));
return patch;
}, wantedReturnType);
}
- public <T> T delete(String path, int port, Class<T> wantedReturnType) {
+ public <T> T delete(String path, Class<T> wantedReturnType) {
return tryAllConfigServers(configServer ->
- new HttpDelete("http://" + configServer + ":" + port + path), wantedReturnType);
+ new HttpDelete(configServer.resolve(path)), wantedReturnType);
}
- public <T> T get(String path, int port, Class<T> wantedReturnType) {
+ public <T> T get(String path, Class<T> wantedReturnType) {
return tryAllConfigServers(configServer ->
- new HttpGet("http://" + configServer + ":" + port + path), wantedReturnType);
+ new HttpGet(configServer.resolve(path)), wantedReturnType);
}
- public <T> T post(String path, int port, Object bodyJsonPojo, Class<T> wantedReturnType) {
+ public <T> T post(String path, Object bodyJsonPojo, Class<T> wantedReturnType) {
return tryAllConfigServers(configServer -> {
- HttpPost post = new HttpPost("http://" + configServer + ":" + port + path);
+ HttpPost post = new HttpPost(configServer.resolve(path));
setContentTypeToApplicationJson(post);
post.setEntity(new StringEntity(mapper.writeValueAsString(bodyJsonPojo)));
return post;
@@ -161,9 +185,9 @@ public class ConfigServerHttpRequestExecutor {
request.setHeader(HttpHeaders.CONTENT_TYPE, "application/json");
}
- // Shuffle config server hosts to balance load
- private List<String> randomizeConfigServerHosts(Set<String> configServerHosts) {
- List<String> shuffledConfigServerHosts = new ArrayList<>(configServerHosts);
+ // Shuffle config server URIs to balance load
+ private static List<URI> randomizeConfigServerUris(Collection<URI> configServerUris) {
+ List<URI> shuffledConfigServerHosts = new ArrayList<>(configServerUris);
Collections.shuffle(shuffledConfigServerHosts);
return shuffledConfigServerHosts;
}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/util/Environment.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/util/Environment.java
index 65a4d84b4c3..2b5e7990617 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/util/Environment.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/util/Environment.java
@@ -6,6 +6,7 @@ import com.yahoo.net.HostName;
import com.yahoo.vespa.hosted.dockerapi.ContainerName;
import java.net.InetAddress;
+import java.net.URI;
import java.net.UnknownHostException;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -15,9 +16,7 @@ import java.time.Instant;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
-import java.util.HashSet;
import java.util.List;
-import java.util.Set;
import java.util.TimeZone;
import java.util.stream.Collectors;
@@ -31,13 +30,15 @@ public class Environment {
private static final DateFormat filenameFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
public static final String APPLICATION_STORAGE_CLEANUP_PATH_PREFIX = "cleanup_";
- private static final String ENV_CONFIGSERVERS = "services__addr_configserver";
+ private static final String ENV_CONFIGSERVER_SCHEME = "CONFIG_SERVER_SCHEME";
+ private static final String ENV_CONFIGSERVER_HOSTS = "CONFIG_SERVER_ADDRESS";
+ private static final String ENV_CONFIGSERVER_PORT = "CONFIG_SERVER_PORT";
private static final String ENVIRONMENT = "ENVIRONMENT";
private static final String REGION = "REGION";
private static final String LOGSTASH_NODES = "LOGSTASH_NODES";
private static final String COREDUMP_FEED_ENDPOINT = "COREDUMP_FEED_ENDPOINT";
- private final Set<String> configServerHosts;
+ private final List<URI> configServerHosts;
private final String environment;
private final String region;
private final String parentHostHostname;
@@ -51,7 +52,7 @@ public class Environment {
}
public Environment() {
- this(getConfigServerHostsFromEnvironment(),
+ this(getConfigServerUrlsFromEnvironment(),
getEnvironmentVariable(ENVIRONMENT),
getEnvironmentVariable(REGION),
HostName.getLocalhost(),
@@ -61,7 +62,7 @@ public class Environment {
getEnvironmentVariable(COREDUMP_FEED_ENDPOINT));
}
- public Environment(Set<String> configServerHosts,
+ public Environment(List<URI> configServerHosts,
String environment,
String region,
String parentHostHostname,
@@ -79,7 +80,7 @@ public class Environment {
this.feedEndpoint = feedEndpoint;
}
- public Set<String> getConfigServerHosts() { return configServerHosts; }
+ public List<URI> getConfigServerUris() { return configServerHosts; }
public String getEnvironment() {
return environment;
@@ -95,7 +96,9 @@ public class Environment {
private static String getEnvironmentVariable(String name) {
final String value = System.getenv(name);
- if (value == null) throw new IllegalStateException(String.format("Environment variable %s not set", name));
+ if (Strings.isNullOrEmpty(value)) {
+ throw new IllegalStateException(String.format("Environment variable %s not set", name));
+ }
return value;
}
@@ -103,14 +106,14 @@ public class Environment {
return getEnvironment() + "." + getRegion();
}
- private static Set<String> getConfigServerHostsFromEnvironment() {
- String configServerHosts = System.getenv(ENV_CONFIGSERVERS);
- if (configServerHosts == null) {
- return Collections.emptySet();
- }
+ private static List<URI> getConfigServerUrlsFromEnvironment() {
+ String scheme = getEnvironmentVariable(ENV_CONFIGSERVER_SCHEME);
+ String configServerHosts = getEnvironmentVariable(ENV_CONFIGSERVER_HOSTS);
+ String port = getEnvironmentVariable(ENV_CONFIGSERVER_PORT);
- final List<String> hostNameStrings = Arrays.asList(configServerHosts.split("[,\\s]+"));
- return new HashSet<>(hostNameStrings);
+ return Arrays.stream(configServerHosts.split("[,\\s]+"))
+ .map(hostname -> URI.create(scheme + "://" + hostname + ":" + port))
+ .collect(Collectors.toList());
}
private static List<String> getLogstashNodesFromEnvironment() {
@@ -188,7 +191,7 @@ public class Environment {
}
public static class Builder {
- private Set<String> configServerHosts = Collections.emptySet();
+ private List<URI> configServerHosts = Collections.emptyList();
private String environment;
private String region;
private String parentHostHostname;
@@ -197,8 +200,10 @@ public class Environment {
private List<String> logstashNodes = Collections.emptyList();
private String feedEndpoint;
- public Builder configServerHosts(String... hosts) {
- configServerHosts = Arrays.stream(hosts).collect(Collectors.toSet());
+ public Builder configServerUris(String... hosts) {
+ configServerHosts = Arrays.stream(hosts)
+ .map(URI::create)
+ .collect(Collectors.toList());
return this;
}
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/noderepository/NodeRepositoryImplTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/noderepository/NodeRepositoryImplTest.java
index 7456b07d2f4..45f5de3d1a5 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/noderepository/NodeRepositoryImplTest.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/noderepository/NodeRepositoryImplTest.java
@@ -17,6 +17,7 @@ import org.junit.Test;
import java.io.IOException;
import java.net.ServerSocket;
+import java.net.URI;
import java.time.Instant;
import java.util.Collections;
import java.util.List;
@@ -35,9 +36,7 @@ import static org.junit.Assert.fail;
*/
public class NodeRepositoryImplTest {
private JDisc container;
- private int port;
- private final ConfigServerHttpRequestExecutor requestExecutor = ConfigServerHttpRequestExecutor.create(
- Collections.singleton("127.0.0.1"));
+ private ConfigServerHttpRequestExecutor requestExecutor;
private int findRandomOpenPort() throws IOException {
@@ -56,14 +55,15 @@ public class NodeRepositoryImplTest {
*/
@Before
public void startContainer() throws Exception {
- port = findRandomOpenPort();
- System.err.println("PORT IS " + port);
+ final int port = findRandomOpenPort();
+ requestExecutor = ConfigServerHttpRequestExecutor.create(
+ Collections.singleton(URI.create("http://127.0.0.1:" + port)));
container = JDisc.fromServicesXml(ContainerConfig.servicesXmlV2(port), Networking.enable);
}
private void waitForJdiscContainerToServe() throws InterruptedException {
Instant start = Instant.now();
- NodeRepository nodeRepositoryApi = new NodeRepositoryImpl(requestExecutor, port);
+ NodeRepository nodeRepositoryApi = new NodeRepositoryImpl(requestExecutor);
while (Instant.now().minusSeconds(120).isBefore(start)) {
try {
nodeRepositoryApi.getContainersToRun("foobar");
@@ -85,7 +85,7 @@ public class NodeRepositoryImplTest {
@Test
public void testGetContainersToRunApi() throws IOException, InterruptedException {
waitForJdiscContainerToServe();
- NodeRepository nodeRepositoryApi = new NodeRepositoryImpl(requestExecutor, port);
+ NodeRepository nodeRepositoryApi = new NodeRepositoryImpl(requestExecutor);
String dockerHostHostname = "dockerhost1.yahoo.com";
final List<ContainerNodeSpec> containersToRun = nodeRepositoryApi.getContainersToRun(dockerHostHostname);
@@ -104,7 +104,7 @@ public class NodeRepositoryImplTest {
@Test
public void testGetContainer() throws InterruptedException, IOException {
waitForJdiscContainerToServe();
- NodeRepository nodeRepositoryApi = new NodeRepositoryImpl(requestExecutor, port);
+ NodeRepository nodeRepositoryApi = new NodeRepositoryImpl(requestExecutor);
String hostname = "host4.yahoo.com";
Optional<ContainerNodeSpec> nodeSpec = nodeRepositoryApi.getContainerNodeSpec(hostname);
assertThat(nodeSpec.isPresent(), is(true));
@@ -114,7 +114,7 @@ public class NodeRepositoryImplTest {
@Test
public void testGetContainerForNonExistingNode() throws InterruptedException, IOException {
waitForJdiscContainerToServe();
- NodeRepository nodeRepositoryApi = new NodeRepositoryImpl(requestExecutor, port);
+ NodeRepository nodeRepositoryApi = new NodeRepositoryImpl(requestExecutor);
String hostname = "host-that-does-not-exist";
Optional<ContainerNodeSpec> nodeSpec = nodeRepositoryApi.getContainerNodeSpec(hostname);
assertFalse(nodeSpec.isPresent());
@@ -123,7 +123,7 @@ public class NodeRepositoryImplTest {
@Test
public void testUpdateNodeAttributes() throws InterruptedException, IOException {
waitForJdiscContainerToServe();
- NodeRepository nodeRepositoryApi = new NodeRepositoryImpl(requestExecutor, port);
+ NodeRepository nodeRepositoryApi = new NodeRepositoryImpl(requestExecutor);
String hostname = "host4.yahoo.com";
nodeRepositoryApi.updateNodeAttributes(
hostname,
@@ -136,7 +136,7 @@ public class NodeRepositoryImplTest {
@Test(expected = RuntimeException.class)
public void testUpdateNodeAttributesWithBadValue() throws InterruptedException, IOException {
waitForJdiscContainerToServe();
- NodeRepository nodeRepositoryApi = new NodeRepositoryImpl(requestExecutor, port);
+ NodeRepository nodeRepositoryApi = new NodeRepositoryImpl(requestExecutor);
String hostname = "host4.yahoo.com";
nodeRepositoryApi.updateNodeAttributes(
hostname,
@@ -148,7 +148,7 @@ public class NodeRepositoryImplTest {
@Test
public void testMarkAsReady() throws InterruptedException, IOException {
- NodeRepository nodeRepositoryApi = new NodeRepositoryImpl(requestExecutor, port);
+ NodeRepository nodeRepositoryApi = new NodeRepositoryImpl(requestExecutor);
waitForJdiscContainerToServe();
nodeRepositoryApi.markAsDirty("host5.yahoo.com");
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/orchestrator/OrchestratorImplTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/orchestrator/OrchestratorImplTest.java
index 0a11ddd1e62..779a0a6a376 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/orchestrator/OrchestratorImplTest.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/orchestrator/OrchestratorImplTest.java
@@ -24,15 +24,12 @@ public class OrchestratorImplTest {
private static final String hostName = "host123.yahoo.com";
private final ConfigServerHttpRequestExecutor requestExecutor = mock(ConfigServerHttpRequestExecutor.class);
- private final int port = 1234;
- private final OrchestratorImpl orchestrator = new OrchestratorImpl(requestExecutor, port);
-
+ private final OrchestratorImpl orchestrator = new OrchestratorImpl(requestExecutor);
@Test
public void testSuspendCall() {
when(requestExecutor.put(
OrchestratorImpl.ORCHESTRATOR_PATH_PREFIX_HOST_API + "/" + hostName+ "/suspended",
- port,
Optional.empty(),
UpdateHostResponse.class
)).thenReturn(new UpdateHostResponse(hostName, null));
@@ -44,7 +41,6 @@ public class OrchestratorImplTest {
public void testSuspendCallWithFailureReason() {
when(requestExecutor.put(
OrchestratorImpl.ORCHESTRATOR_PATH_PREFIX_HOST_API + "/" + hostName+ "/suspended",
- port,
Optional.empty(),
UpdateHostResponse.class
)).thenReturn(new UpdateHostResponse(hostName, new HostStateChangeDenialReason("hostname", "fail")));
@@ -56,7 +52,6 @@ public class OrchestratorImplTest {
public void testSuspendCallWithNotFound() {
when(requestExecutor.put(
any(String.class),
- any(Integer.class),
any(),
any()
)).thenThrow(new HttpException.NotFoundException("Not Found"));
@@ -68,7 +63,6 @@ public class OrchestratorImplTest {
public void testSuspendCallWithSomeOtherException() {
when(requestExecutor.put(
any(String.class),
- any(Integer.class),
any(),
any()
)).thenThrow(new RuntimeException("Some parameter was wrong"));
@@ -81,7 +75,6 @@ public class OrchestratorImplTest {
public void testResumeCall() {
when(requestExecutor.delete(
OrchestratorImpl.ORCHESTRATOR_PATH_PREFIX_HOST_API + "/" + hostName+ "/suspended",
- port,
UpdateHostResponse.class
)).thenReturn(new UpdateHostResponse(hostName, null));
@@ -92,7 +85,6 @@ public class OrchestratorImplTest {
public void testResumeCallWithFailureReason() {
when(requestExecutor.delete(
OrchestratorImpl.ORCHESTRATOR_PATH_PREFIX_HOST_API + "/" + hostName+ "/suspended",
- port,
UpdateHostResponse.class
)).thenReturn(new UpdateHostResponse(hostName, new HostStateChangeDenialReason("hostname", "fail")));
@@ -103,7 +95,6 @@ public class OrchestratorImplTest {
public void testResumeCallWithNotFound() {
when(requestExecutor.delete(
any(String.class),
- any(Integer.class),
any()
)).thenThrow(new HttpException.NotFoundException("Not Found"));
@@ -114,7 +105,6 @@ public class OrchestratorImplTest {
public void testResumeCallWithSomeOtherException() {
when(requestExecutor.put(
any(String.class),
- any(Integer.class),
any(),
any()
)).thenThrow(new RuntimeException("Some parameter was wrong"));
@@ -130,7 +120,6 @@ public class OrchestratorImplTest {
when(requestExecutor.put(
OrchestratorImpl.ORCHESTRATOR_PATH_PREFIX_HOST_SUSPENSION_API,
- port,
Optional.of(new BatchHostSuspendRequest(parentHostName, hostNames)),
BatchOperationResult.class
)).thenReturn(BatchOperationResult.successResult());
@@ -146,7 +135,6 @@ public class OrchestratorImplTest {
when(requestExecutor.put(
OrchestratorImpl.ORCHESTRATOR_PATH_PREFIX_HOST_SUSPENSION_API,
- port,
Optional.of(new BatchHostSuspendRequest(parentHostName, hostNames)),
BatchOperationResult.class
)).thenReturn(new BatchOperationResult(failureReason));
@@ -162,7 +150,6 @@ public class OrchestratorImplTest {
when(requestExecutor.put(
OrchestratorImpl.ORCHESTRATOR_PATH_PREFIX_HOST_SUSPENSION_API,
- port,
Optional.of(new BatchHostSuspendRequest(parentHostName, hostNames)),
BatchOperationResult.class
)).thenThrow(new RuntimeException(exceptionMessage));
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/util/ConfigServerHttpRequestExecutorTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/util/ConfigServerHttpRequestExecutorTest.java
index d431f175b1c..990d047b546 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/util/ConfigServerHttpRequestExecutorTest.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/util/ConfigServerHttpRequestExecutorTest.java
@@ -3,19 +3,21 @@ package com.yahoo.vespa.hosted.node.admin.util;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
-import com.yahoo.collections.ArraySet;
-import org.apache.http.HttpEntity;
-import org.apache.http.StatusLine;
+import org.apache.http.HttpVersion;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
+import org.apache.http.entity.BasicHttpEntity;
import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.message.BasicStatusLine;
import org.junit.Test;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.net.URI;
import java.nio.charset.StandardCharsets;
-import java.util.Set;
+import java.util.Arrays;
+import java.util.List;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.arrayContainingInAnyOrder;
@@ -38,11 +40,14 @@ public class ConfigServerHttpRequestExecutorTest {
@JsonIgnoreProperties(ignoreUnknown = true)
public static class TestPojo {
@JsonProperty("foo")
- public String foo;
+ String foo;
@JsonProperty("error-code")
- public Integer errorCode;
+ Integer errorCode;
}
+ private final String uri1 = "http://host1:666";
+ private final String uri2 = "http://host2:666";
+ private final List<URI> configServers = Arrays.asList(URI.create(uri1), URI.create(uri2));
private final StringBuilder mockLog = new StringBuilder();
private int mockReturnCode = 200;
@@ -51,16 +56,18 @@ public class ConfigServerHttpRequestExecutorTest {
when(httpMock.execute(any())).thenAnswer(invocationOnMock -> {
HttpGet get = (HttpGet) invocationOnMock.getArguments()[0];
mockLog.append(get.getMethod()).append(" ").append(get.getURI()).append(" ");
- CloseableHttpResponse response = mock(CloseableHttpResponse.class);
- StatusLine statusLine = mock(StatusLine.class);
- when(statusLine.getStatusCode()).thenReturn(mockReturnCode);
- when(response.getStatusLine()).thenReturn(statusLine);
if (mockReturnCode == 100000) throw new RuntimeException("FAIL");
- HttpEntity entity = mock(HttpEntity.class);
- when(response.getEntity()).thenReturn(entity);
+
+ BasicStatusLine statusLine = new BasicStatusLine(HttpVersion.HTTP_1_1, mockReturnCode, null);
+ BasicHttpEntity entity = new BasicHttpEntity();
String returnMessage = "{\"foo\":\"bar\", \"no\":3, \"error-code\": " + mockReturnCode + "}";
InputStream stream = new ByteArrayInputStream(returnMessage.getBytes(StandardCharsets.UTF_8));
- when(entity.getContent()).thenReturn(stream);
+ entity.setContent(stream);
+
+ CloseableHttpResponse response = mock(CloseableHttpResponse.class);
+ when(response.getEntity()).thenReturn(entity);
+ when(response.getStatusLine()).thenReturn(statusLine);
+
return response;
});
doNothing().when(httpMock).close();
@@ -69,75 +76,61 @@ public class ConfigServerHttpRequestExecutorTest {
@Test
public void testBasicParsingSingleServer() throws Exception {
- Set<String> configServers = new ArraySet<>(2);
- configServers.add("host1");
- configServers.add("host2");
ConfigServerHttpRequestExecutor executor = new ConfigServerHttpRequestExecutor(configServers, createClientMock());
- TestPojo answer = executor.get("/path", 666, TestPojo.class);
+ TestPojo answer = executor.get("/path", TestPojo.class);
assertThat(answer.foo, is("bar"));
assertLogStringContainsGETForAHost();
}
@Test(expected = HttpException.class)
public void testBasicFailure() throws Exception {
- Set<String> configServers = new ArraySet<>(2);
- configServers.add("host1");
- configServers.add("host2");
// Server is returning 400, no retries.
mockReturnCode = 400;
ConfigServerHttpRequestExecutor executor = new ConfigServerHttpRequestExecutor(configServers, createClientMock());
- TestPojo testPojo = executor.get("/path", 666, TestPojo.class);
+ TestPojo testPojo = executor.get("/path", TestPojo.class);
assertEquals(testPojo.errorCode.intValue(), mockReturnCode);
assertLogStringContainsGETForAHost();
}
@Test
public void testBasicSuccessWithNoRetries() throws Exception {
- Set<String> configServers = new ArraySet<>(2);
- configServers.add("host1");
- configServers.add("host2");
// Server is returning 201, no retries.
mockReturnCode = 201;
ConfigServerHttpRequestExecutor executor = new ConfigServerHttpRequestExecutor(configServers, createClientMock());
- TestPojo testPojo = executor.get("/path", 666, TestPojo.class);
+ TestPojo testPojo = executor.get("/path", TestPojo.class);
assertEquals(testPojo.errorCode.intValue(), mockReturnCode);
assertLogStringContainsGETForAHost();
}
@Test
public void testRetries() throws Exception {
- Set<String> configServers = new ArraySet<>(2);
- configServers.add("host1");
- configServers.add("host2");
// Client is throwing exception, should be retries.
mockReturnCode = 100000;
ConfigServerHttpRequestExecutor executor =
new ConfigServerHttpRequestExecutor(configServers, createClientMock());
try {
- executor.get("/path", 666, TestPojo.class);
+ executor.get("/path", TestPojo.class);
fail("Expected failure");
} catch (Exception e) {
// ignore
}
String[] log = mockLog.toString().split(" ");
+ System.out.println(Arrays.toString(log));
assertThat(log, arrayContainingInAnyOrder("GET http://host1:666/path", "GET http://host2:666/path",
"GET http://host1:666/path", "GET http://host2:666/path"));
}
@Test
public void testRetriesOnBadHttpResponseCode() throws Exception {
- Set<String> configServers = new ArraySet<>(2);
- configServers.add("host1");
- configServers.add("host2");
// Client is throwing exception, should be retries.
mockReturnCode = 503;
ConfigServerHttpRequestExecutor executor =
new ConfigServerHttpRequestExecutor(configServers, createClientMock());
try {
- executor.get("/path", 666, TestPojo.class);
+ executor.get("/path", TestPojo.class);
fail("Expected failure");
} catch (Exception e) {
// ignore
@@ -151,14 +144,11 @@ public class ConfigServerHttpRequestExecutorTest {
@Test
public void testNotFound() throws Exception {
- Set<String> configServers = new ArraySet<>(2);
- configServers.add("host1");
- configServers.add("host2");
// Server is returning 404, special exception is thrown.
mockReturnCode = 404;
ConfigServerHttpRequestExecutor executor = new ConfigServerHttpRequestExecutor(configServers, createClientMock());
try {
- executor.get("/path", 666, TestPojo.class);
+ executor.get("/path", TestPojo.class);
fail("Expected exception");
} catch (HttpException.NotFoundException e) {
// ignore
@@ -168,14 +158,11 @@ public class ConfigServerHttpRequestExecutorTest {
@Test
public void testConflict() throws Exception {
- Set<String> configServers = new ArraySet<>(2);
- configServers.add("host1");
- configServers.add("host2");
// Server is returning 409, no exception is thrown.
mockReturnCode = 409;
ConfigServerHttpRequestExecutor executor =
new ConfigServerHttpRequestExecutor(configServers, createClientMock());
- executor.get("/path", 666, TestPojo.class);
+ executor.get("/path", TestPojo.class);
assertLogStringContainsGETForAHost();
}