summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHÃ¥kon Hallingstad <hakon@verizonmedia.com>2021-06-24 11:39:26 +0200
committerGitHub <noreply@github.com>2021-06-24 11:39:26 +0200
commit2bcd621f1957d13e7d14f7831af927009a891077 (patch)
tree91bc8d3de81ee46c6b1c99b3f28f122e43599994
parent6c57e01614712bc23bff60cd055e9964d6e2cc40 (diff)
parent54792b76404ed27a95c3238a8850d720f6a38cc9 (diff)
Merge pull request #18389 from vespa-engine/mpolden/defer-encryption
Add feature flag for deferring host encryption
-rw-r--r--flags/src/main/java/com/yahoo/vespa/flags/Flags.java6
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/HostEncrypter.java26
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/HostEncrypterTest.java31
3 files changed, 60 insertions, 3 deletions
diff --git a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
index 54e3b5d8cc9..6e72ded830f 100644
--- a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
+++ b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
@@ -293,6 +293,12 @@ public class Flags {
"Takes effect on next internal redeployment",
APPLICATION_ID);
+ public static final UnboundListFlag<String> DEFER_APPLICATION_ENCRYPTION = defineListFlag(
+ "defer-application-encryption", List.of(), String.class,
+ List.of("mpolden", "hakonhall"), "2021-06-23", "2021-10-01",
+ "List of applications where encryption of their host should be deferred",
+ "Takes effect on next run of HostEncrypter");
+
/** WARNING: public for testing: All flags should be defined in {@link Flags}. */
public static UnboundBooleanFlag defineFeatureFlag(String flagId, boolean defaultValue, List<String> owners,
String createdAt, String expiresAt, String description,
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/HostEncrypter.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/HostEncrypter.java
index 6d88e43630a..a4569f03d82 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/HostEncrypter.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/HostEncrypter.java
@@ -2,10 +2,12 @@
package com.yahoo.vespa.hosted.provision.maintenance;
import com.yahoo.component.Version;
+import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.NodeType;
import com.yahoo.jdisc.Metric;
import com.yahoo.vespa.flags.Flags;
import com.yahoo.vespa.flags.IntFlag;
+import com.yahoo.vespa.flags.ListFlag;
import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeList;
import com.yahoo.vespa.hosted.provision.NodeRepository;
@@ -20,6 +22,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;
+import java.util.stream.Collectors;
/**
* This maintainer triggers encryption of hosts that have unencrypted disk.
@@ -36,10 +39,12 @@ public class HostEncrypter extends NodeRepositoryMaintainer {
private static final Logger LOG = Logger.getLogger(HostEncrypter.class.getName());
private final IntFlag maxEncryptingHosts;
+ private final ListFlag<String> deferApplicationEncryption;
public HostEncrypter(NodeRepository nodeRepository, Duration interval, Metric metric) {
super(nodeRepository, interval, metric);
this.maxEncryptingHosts = Flags.MAX_ENCRYPTING_HOSTS.bindTo(nodeRepository.flagSource());
+ this.deferApplicationEncryption = Flags.DEFER_APPLICATION_ENCRYPTION.bindTo(nodeRepository.flagSource());
}
@Override
@@ -48,8 +53,6 @@ public class HostEncrypter extends NodeRepositoryMaintainer {
NodeList allNodes = nodeRepository().nodes().list();
for (var nodeType : NodeType.values()) {
if (!nodeType.isHost()) continue;
- // TODO: Require a minimum number of proxies in Orchestrator. For now skip proxy hosts.
- if (nodeType == NodeType.proxyhost) continue;
if (upgradingVespa(allNodes, nodeType)) continue;
unencryptedHosts(allNodes, nodeType).forEach(host -> encrypt(host, now));
}
@@ -78,12 +81,18 @@ public class HostEncrypter extends NodeRepositoryMaintainer {
// Encrypt hosts not containing stateful clusters with retiring nodes, up to limit
List<Node> hostsToEncrypt = new ArrayList<>(hostLimit);
+
+ Set<ApplicationId> deferredApplications = deferApplicationEncryption.value().stream()
+ .map(ApplicationId::fromSerializedForm)
+ .collect(Collectors.toSet());
NodeList candidates = hostsOfTargetType.state(Node.State.active)
.not().encrypted()
.not().encrypting()
+ .matching(host -> encryptHost(host, allNodes, deferredApplications))
// Require an OS version supporting encryption
.matching(node -> node.status().osVersion().current()
- .orElse(Version.emptyVersion).getMajor() >= 8);
+ .orElse(Version.emptyVersion)
+ .getMajor() >= 8);
for (Node host : candidates) {
if (hostsToEncrypt.size() == hostLimit) break;
@@ -106,6 +115,17 @@ public class HostEncrypter extends NodeRepositoryMaintainer {
return Math.max(0, limit - hosts.encrypting().size());
}
+ private boolean encryptHost(Node host, NodeList allNodes, Set<ApplicationId> deferredApplications) {
+ // TODO: Require a minimum number of proxies in Orchestrator. For now skip proxy hosts.
+ if (host.type() == NodeType.proxyhost) return false;
+
+ Set<ApplicationId> applicationsOnHost = allNodes.childrenOf(host).stream()
+ .filter(node -> node.allocation().isPresent())
+ .map(node -> node.allocation().get().owner())
+ .collect(Collectors.toSet());
+ return Collections.disjoint(applicationsOnHost, deferredApplications);
+ }
+
private void encrypt(Node host, Instant now) {
LOG.info("Retiring and encrypting " + host);
nodeRepository().nodes().encrypt(host.hostname(), Agent.HostEncrypter, now);
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/HostEncrypterTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/HostEncrypterTest.java
index 5cae181f87d..1e5d57263fa 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/HostEncrypterTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/HostEncrypterTest.java
@@ -22,8 +22,10 @@ import java.time.Instant;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
+import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Supplier;
+import java.util.stream.Collectors;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -47,6 +49,35 @@ public class HostEncrypterTest {
}
@Test
+ public void deferred_hosts_are_not_encrypted() {
+ int hostCount = 4;
+ int proxyHostCount = 1;
+ ApplicationId app1 = ApplicationId.from("t1", "a1", "i1");
+ ApplicationId app2 = ApplicationId.from("t2", "a2", "i2");
+ provisionHosts(hostCount);
+ deployApplication(app1);
+ deployApplication(app2);
+
+ ApplicationId proxyHostApp = ApplicationId.from("hosted-vespa", "proxy-host", "default");
+ List<Node> proxyHosts = tester.makeReadyNodes(proxyHostCount, "default", NodeType.proxyhost, 10);
+ tester.patchNodes(proxyHosts, (host) -> host.with(host.status().withOsVersion(host.status().osVersion().withCurrent(Optional.of(Version.fromString("8.0"))))));
+ tester.prepareAndActivateInfraApplication(proxyHostApp, NodeType.proxyhost);
+
+ tester.flagSource()
+ .withIntFlag(Flags.MAX_ENCRYPTING_HOSTS.id(), hostCount + proxyHostCount)
+ .withListFlag(Flags.DEFER_APPLICATION_ENCRYPTION.id(), List.of(app2.serializedForm()), String.class);
+ encrypter.maintain();
+ NodeList allNodes = tester.nodeRepository().nodes().list();
+ NodeList encryptingHosts = allNodes.encrypting().parents();
+
+ assertEquals(1, encryptingHosts.size());
+ assertEquals("Host of included application is encrypted", Set.of(app1),
+ allNodes.childrenOf(encryptingHosts.asList().get(0)).stream()
+ .map(node -> node.allocation().get().owner())
+ .collect(Collectors.toSet()));
+ }
+
+ @Test
public void encrypt_hosts() {
tester.flagSource().withIntFlag(Flags.MAX_ENCRYPTING_HOSTS.id(), 3);
Supplier<NodeList> hosts = () -> tester.nodeRepository().nodes().list().nodeType(NodeType.host);