diff options
Diffstat (limited to 'node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/component/Environment.java')
-rw-r--r-- | node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/component/Environment.java | 331 |
1 files changed, 331 insertions, 0 deletions
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/component/Environment.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/component/Environment.java new file mode 100644 index 00000000000..7376a59bd5c --- /dev/null +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/component/Environment.java @@ -0,0 +1,331 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.node.admin.component; + +import com.google.common.base.Strings; +import com.yahoo.vespa.athenz.api.AthenzIdentity; +import com.yahoo.vespa.athenz.api.AthenzService; +import com.yahoo.vespa.defaults.Defaults; +import com.yahoo.vespa.hosted.dockerapi.ContainerName; +import com.yahoo.vespa.hosted.node.admin.config.ConfigServerConfig; +import com.yahoo.vespa.hosted.node.admin.util.InetAddressResolver; +import com.yahoo.vespa.hosted.node.admin.util.KeyStoreOptions; + +import java.net.InetAddress; +import java.net.URI; +import java.net.UnknownHostException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.time.Instant; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Optional; +import java.util.TimeZone; +import java.util.stream.Collectors; + +/** + * Various utilities for getting values from node-admin's environment. Immutable. + * + * @author bakksjo + * @author hmusum + */ +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 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 List<URI> configServerHosts; + private final String environment; + private final String region; + private final String parentHostHostname; + private final InetAddressResolver inetAddressResolver; + private final PathResolver pathResolver; + private final List<String> logstashNodes; + private final Optional<String> feedEndpoint; + private final Optional<KeyStoreOptions> keyStoreOptions; + private final Optional<KeyStoreOptions> trustStoreOptions; + private final Optional<AthenzIdentity> athenzIdentity; + + static { + filenameFormatter.setTimeZone(TimeZone.getTimeZone("UTC")); + } + + public Environment(ConfigServerConfig configServerConfig) { + this(configServerConfig, + getEnvironmentVariable(ENVIRONMENT), + getEnvironmentVariable(REGION), + new PathResolver(), + Optional.of(getEnvironmentVariable(COREDUMP_FEED_ENDPOINT))); + } + + public Environment(ConfigServerConfig configServerConfig, + String hostedEnvironment, + String hostedRegion, + PathResolver pathResolver, + Optional<String> coreDumpFeedEndpoint) { + this(createConfigServerUris( + configServerConfig.scheme(), + configServerConfig.hosts(), + configServerConfig.port()), + + hostedEnvironment, + hostedRegion, + Defaults.getDefaults().vespaHostname(), + new InetAddressResolver(), + pathResolver, + getLogstashNodesFromEnvironment(), + coreDumpFeedEndpoint, + + createKeyStoreOptions( + configServerConfig.keyStoreConfig().path(), + configServerConfig.keyStoreConfig().password().toCharArray(), + configServerConfig.keyStoreConfig().type().name()), + createKeyStoreOptions( + configServerConfig.trustStoreConfig().path(), + configServerConfig.trustStoreConfig().password().toCharArray(), + configServerConfig.trustStoreConfig().type().name()), + createAthenzIdentity( + configServerConfig.athenzDomain(), + configServerConfig.serviceName()) + ); + } + + public Environment(List<URI> configServerHosts, + String environment, + String region, + String parentHostHostname, + InetAddressResolver inetAddressResolver, + PathResolver pathResolver, + List<String> logstashNodes, + Optional<String> feedEndpoint, + Optional<KeyStoreOptions> keyStoreOptions, + Optional<KeyStoreOptions> trustStoreOptions, + Optional<AthenzIdentity> athenzIdentity) { + this.configServerHosts = configServerHosts; + this.environment = environment; + this.region = region; + this.parentHostHostname = parentHostHostname; + this.inetAddressResolver = inetAddressResolver; + this.pathResolver = pathResolver; + this.logstashNodes = logstashNodes; + this.feedEndpoint = feedEndpoint; + this.keyStoreOptions = keyStoreOptions; + this.trustStoreOptions = trustStoreOptions; + this.athenzIdentity = athenzIdentity; + } + + public List<URI> getConfigServerUris() { return configServerHosts; } + + public String getEnvironment() { + return environment; + } + + public String getRegion() { + return region; + } + + public String getParentHostHostname() { + return parentHostHostname; + } + + private static String getEnvironmentVariable(String name) { + final String value = System.getenv(name); + if (Strings.isNullOrEmpty(value)) { + throw new IllegalStateException(String.format("Environment variable %s not set", name)); + } + return value; + } + + public String getZone() { + return getEnvironment() + "." + getRegion(); + } + + private static List<URI> createConfigServerUris(String scheme, List<String> configServerHosts, int port) { + return configServerHosts.stream() + .map(hostname -> URI.create(scheme + "://" + hostname + ":" + port)) + .collect(Collectors.toList()); + } + + private static List<String> getLogstashNodesFromEnvironment() { + String logstashNodes = System.getenv(LOGSTASH_NODES); + if(Strings.isNullOrEmpty(logstashNodes)) { + return Collections.emptyList(); + } + return Arrays.asList(logstashNodes.split("[,\\s]+")); + } + + private static Optional<KeyStoreOptions> createKeyStoreOptions(String pathToKeyStore, char[] password, String type) { + return Optional.ofNullable(pathToKeyStore) + .filter(path -> !Strings.isNullOrEmpty(path)) + .map(path -> new KeyStoreOptions(Paths.get(path), password, type)); + } + + private static Optional<AthenzIdentity> createAthenzIdentity(String athenzDomain, String serviceName) { + if (Strings.isNullOrEmpty(athenzDomain) || Strings.isNullOrEmpty(serviceName)) return Optional.empty(); + return Optional.of(new AthenzService(athenzDomain, serviceName)); + } + + public InetAddress getInetAddressForHost(String hostname) throws UnknownHostException { + return inetAddressResolver.getInetAddressForHost(hostname); + } + + public PathResolver getPathResolver() { + return pathResolver; + } + + public Optional<String> getCoredumpFeedEndpoint() { + return feedEndpoint; + } + + /** + * Absolute path in node admin to directory with processed and reported core dumps + */ + public Path pathInNodeAdminToDoneCoredumps() { + return pathResolver.getApplicationStoragePathForNodeAdmin().resolve("processed-coredumps"); + } + + /** + * Absolute path in node admin container to the node cleanup directory. + */ + public Path pathInNodeAdminToNodeCleanup(ContainerName containerName) { + return pathResolver.getApplicationStoragePathForNodeAdmin() + .resolve(APPLICATION_STORAGE_CLEANUP_PATH_PREFIX + containerName.asString() + + "_" + filenameFormatter.format(Date.from(Instant.now()))); + } + + /** + * Translates an absolute path in node agent container to an absolute path in node admin container. + * @param containerName name of the node agent container + * @param absolutePathInNode absolute path in that container + * @return the absolute path in node admin container pointing at the same inode + */ + public Path pathInNodeAdminFromPathInNode(ContainerName containerName, String absolutePathInNode) { + Path pathInNode = Paths.get(absolutePathInNode); + if (! pathInNode.isAbsolute()) { + throw new IllegalArgumentException("The specified path in node was not absolute: " + absolutePathInNode); + } + + return pathResolver.getApplicationStoragePathForNodeAdmin() + .resolve(containerName.asString()) + .resolve(PathResolver.ROOT.relativize(pathInNode)); + } + + /** + * Translates an absolute path in node agent container to an absolute path in host. + * @param containerName name of the node agent container + * @param absolutePathInNode absolute path in that container + * @return the absolute path in host pointing at the same inode + */ + public Path pathInHostFromPathInNode(ContainerName containerName, String absolutePathInNode) { + Path pathInNode = Paths.get(absolutePathInNode); + if (! pathInNode.isAbsolute()) { + throw new IllegalArgumentException("The specified path in node was not absolute: " + absolutePathInNode); + } + + return pathResolver.getApplicationStoragePathForHost() + .resolve(containerName.asString()) + .resolve(PathResolver.ROOT.relativize(pathInNode)); + } + + public List<String> getLogstashNodes() { + return logstashNodes; + } + + public Optional<KeyStoreOptions> getKeyStoreOptions() { + return keyStoreOptions; + } + + public Optional<KeyStoreOptions> getTrustStoreOptions() { + return trustStoreOptions; + } + + public Optional<AthenzIdentity> getAthenzIdentity() { + return athenzIdentity; + } + + + public static class Builder { + private List<URI> configServerHosts = Collections.emptyList(); + private String environment; + private String region; + private String parentHostHostname; + private InetAddressResolver inetAddressResolver; + private PathResolver pathResolver; + private List<String> logstashNodes = Collections.emptyList(); + private Optional<String> feedEndpoint = Optional.empty(); + private KeyStoreOptions keyStoreOptions; + private KeyStoreOptions trustStoreOptions; + private AthenzIdentity athenzIdentity; + + public Builder configServerUris(String... hosts) { + configServerHosts = Arrays.stream(hosts) + .map(URI::create) + .collect(Collectors.toList()); + return this; + } + + public Builder environment(String environment) { + this.environment = environment; + return this; + } + + public Builder region(String region) { + this.region = region; + return this; + } + + public Builder parentHostHostname(String parentHostHostname) { + this.parentHostHostname = parentHostHostname; + return this; + } + + public Builder inetAddressResolver(InetAddressResolver inetAddressResolver) { + this.inetAddressResolver = inetAddressResolver; + return this; + } + + public Builder pathResolver(PathResolver pathResolver) { + this.pathResolver = pathResolver; + return this; + } + + public Builder logstashNodes(List<String> hosts) { + this.logstashNodes = hosts; + return this; + } + + public Builder feedEndpoint(String feedEndpoint) { + this.feedEndpoint = Optional.of(feedEndpoint); + return this; + } + + public Builder keyStoreOptions(KeyStoreOptions keyStoreOptions) { + this.keyStoreOptions = keyStoreOptions; + return this; + } + + public Builder trustStoreOptions(KeyStoreOptions trustStoreOptions) { + this.trustStoreOptions = trustStoreOptions; + return this; + } + + public Builder athenzIdentity(AthenzIdentity athenzIdentity) { + this.athenzIdentity = athenzIdentity; + return this; + } + + public Environment build() { + return new Environment(configServerHosts, environment, region, parentHostHostname, inetAddressResolver, + pathResolver, logstashNodes, feedEndpoint, + Optional.ofNullable(keyStoreOptions), Optional.ofNullable(trustStoreOptions), + Optional.ofNullable(athenzIdentity)); + } + } +} |