summaryrefslogtreecommitdiffstats
path: root/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/util/Environment.java
blob: 0415bbc34c2cbd439318dcbb4200749dcff041c2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
// Copyright 2017 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.util;

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 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 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(createConfigServerUris(
                configServerConfig.scheme(),
                configServerConfig.hosts(),
                configServerConfig.port()),

             getEnvironmentVariable(ENVIRONMENT),
             getEnvironmentVariable(REGION),
             Defaults.getDefaults().vespaHostname(),
             new InetAddressResolver(),
             new PathResolver(),
             getLogstashNodesFromEnvironment(),
             getEnvironmentVariable(COREDUMP_FEED_ENDPOINT),

             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,
                       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 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 String feedEndpoint;
        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 = 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));
        }
    }
}