diff options
author | jonmv <venstad@gmail.com> | 2022-12-01 14:12:33 +0100 |
---|---|---|
committer | jonmv <venstad@gmail.com> | 2022-12-01 14:12:33 +0100 |
commit | 4226f2d67552c31e07a5be772691be213770a1b7 (patch) | |
tree | 8ae23d295a385dd286a6b2b080fc7a8715ab39c8 | |
parent | 2925f225b34ad7fa3eb515bbddcc8c774e514131 (diff) |
Write new settings only on activation
14 files changed, 84 insertions, 66 deletions
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/HostSystem.java b/config-model/src/main/java/com/yahoo/vespa/model/HostSystem.java index fcad1e20c16..2de7e5a1b37 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/HostSystem.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/HostSystem.java @@ -115,7 +115,7 @@ public class HostSystem extends AbstractConfigProducer<Host> { return hostResource; } - /** Returns the hosts owned by the application having this system - i.e all hosts except config servers */ + /** Returns the hosts owned by the application having this system - i.e. all hosts except config servers */ public List<HostResource> getHosts() { return hostname2host.values().stream() .filter(host -> !host.getHost().runsConfigServer()) diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/AccessLogBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/AccessLogBuilder.java index 82b3defa22d..4a264f1eebc 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/AccessLogBuilder.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/AccessLogBuilder.java @@ -99,16 +99,11 @@ public class AccessLogBuilder { } private static AccessLogType logTypeFor(AccessLogTypeLiteral typeLiteral) { - switch (typeLiteral) { - case DISABLED: - return null; - case VESPA: - return AccessLogType.queryAccessLog; - case JSON: - return AccessLogType.jsonAccessLog; - default: - throw new InconsistentSchemaAndCodeError(); - } + return switch (typeLiteral) { + case DISABLED -> null; + case VESPA -> AccessLogType.queryAccessLog; + case JSON -> AccessLogType.jsonAccessLog; + }; } public static Optional<AccessLogComponent> buildIfNotDisabled(DeployState deployState, ContainerCluster<?> cluster, Element accessLogSpec) { diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/InconsistentSchemaAndCodeError.java b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/InconsistentSchemaAndCodeError.java deleted file mode 100644 index e3744f23964..00000000000 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/InconsistentSchemaAndCodeError.java +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.model.container.xml; - -/** - * @author Tony Vaagenes - */ -public class InconsistentSchemaAndCodeError extends Error {} diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterMembership.java b/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterMembership.java index afa3c0a4062..83a2258976e 100644 --- a/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterMembership.java +++ b/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterMembership.java @@ -21,7 +21,8 @@ public class ClusterMembership { protected ClusterMembership() {} - private ClusterMembership(String stringValue, Version vespaVersion, Optional<DockerImage> dockerImageRepo) { + private ClusterMembership(String stringValue, Version vespaVersion, Optional<DockerImage> dockerImageRepo, + LoadBalancerSettings loadBalancerSettings) { String[] components = stringValue.split("/"); if (components.length < 4) throw new RuntimeException("Could not parse '" + stringValue + "' to a cluster membership. " + @@ -49,6 +50,7 @@ public class ClusterMembership { .exclusive(exclusive) .combinedId(combinedId.map(ClusterSpec.Id::from)) .dockerImageRepository(dockerImageRepo) + .loadBalancerSettings(loadBalancerSettings) .stateful(stateful) .build(); this.index = Integer.parseInt(components[3]); @@ -123,7 +125,12 @@ public class ClusterMembership { public String toString() { return stringValue(); } public static ClusterMembership from(String stringValue, Version vespaVersion, Optional<DockerImage> dockerImageRepo) { - return new ClusterMembership(stringValue, vespaVersion, dockerImageRepo); + return from(stringValue, vespaVersion, dockerImageRepo, LoadBalancerSettings.empty); + } + + public static ClusterMembership from(String stringValue, Version vespaVersion, Optional<DockerImage> dockerImageRepo, + LoadBalancerSettings loadBalancerSettings) { + return new ClusterMembership(stringValue, vespaVersion, dockerImageRepo, loadBalancerSettings); } public static ClusterMembership from(ClusterSpec cluster, int index) { diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/serialization/AllocatedHostsSerializer.java b/config-provisioning/src/main/java/com/yahoo/config/provision/serialization/AllocatedHostsSerializer.java index 3973b042bde..01bb0ca45ff 100644 --- a/config-provisioning/src/main/java/com/yahoo/config/provision/serialization/AllocatedHostsSerializer.java +++ b/config-provisioning/src/main/java/com/yahoo/config/provision/serialization/AllocatedHostsSerializer.java @@ -1,10 +1,12 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.config.provision.serialization; +import com.yahoo.component.Version; import com.yahoo.config.provision.AllocatedHosts; import com.yahoo.config.provision.ClusterMembership; import com.yahoo.config.provision.DockerImage; import com.yahoo.config.provision.HostSpec; +import com.yahoo.config.provision.LoadBalancerSettings; import com.yahoo.config.provision.NodeResources; import com.yahoo.slime.ArrayTraverser; import com.yahoo.slime.Cursor; @@ -38,6 +40,8 @@ public class AllocatedHostsSerializer { private static final String hostSpecKey = "hostSpec"; private static final String hostSpecHostNameKey = "hostName"; private static final String hostSpecMembershipKey = "membership"; + private static final String loadBalancerSettingsKey = "loadBalancerSettings"; + private static final String allowedUrnsKey = "allowedUrns"; private static final String realResourcesKey = "realResources"; private static final String advertisedResourcesKey = "advertisedResources"; @@ -81,6 +85,9 @@ public class AllocatedHostsSerializer { host.membership().ifPresent(membership -> { object.setString(hostSpecMembershipKey, membership.stringValue()); object.setString(hostSpecVespaVersionKey, membership.cluster().vespaVersion().toFullString()); + if ( ! membership.cluster().loadBalancerSettings().isEmpty()) + membership.cluster().loadBalancerSettings().allowedUrns() + .forEach(object.setObject(loadBalancerSettingsKey).setArray(allowedUrnsKey)::addString); membership.cluster().dockerImageRepo().ifPresent(repo -> object.setString(hostSpecDockerImageRepoKey, repo.untagged())); }); toSlime(host.realResources(), object.setObject(realResourcesKey)); @@ -125,7 +132,7 @@ public class AllocatedHostsSerializer { nodeResourcesFromSlime(object.field(advertisedResourcesKey)), optionalNodeResourcesFromSlime(object.field(requestedResourcesKey)), // TODO: Make non-optional when we serialize NodeResources.unspecified() membershipFromSlime(object), - optionalString(object.field(hostSpecCurrentVespaVersionKey)).map(com.yahoo.component.Version::new), + optionalString(object.field(hostSpecCurrentVespaVersionKey)).map(Version::new), NetworkPortsSerializer.fromSlime(object.field(hostSpecNetworkPortsKey)), optionalDockerImage(object.field(hostSpecDockerImageRepoKey))); } @@ -211,10 +218,15 @@ public class AllocatedHostsSerializer { private static ClusterMembership membershipFromSlime(Inspector object) { return ClusterMembership.from(object.field(hostSpecMembershipKey).asString(), - com.yahoo.component.Version.fromString(object.field(hostSpecVespaVersionKey).asString()), + Version.fromString(object.field(hostSpecVespaVersionKey).asString()), object.field(hostSpecDockerImageRepoKey).valid() - ? Optional.of(DockerImage.fromString(object.field(hostSpecDockerImageRepoKey).asString())) - : Optional.empty()); + ? Optional.of(DockerImage.fromString(object.field(hostSpecDockerImageRepoKey).asString())) + : Optional.empty(), + object.field(loadBalancerSettingsKey).valid() + ? new LoadBalancerSettings(SlimeUtils.entriesStream(object.field(loadBalancerSettingsKey).field(allowedUrnsKey)) + .map(Inspector::asString) + .toList()) + : LoadBalancerSettings.empty); } private static Optional<String> optionalString(Inspector inspector) { diff --git a/config-provisioning/src/test/java/com/yahoo/config/provision/serialization/AllocatedHostsSerializerTest.java b/config-provisioning/src/test/java/com/yahoo/config/provision/serialization/AllocatedHostsSerializerTest.java index 172e0875767..bcb3b8cd4aa 100644 --- a/config-provisioning/src/test/java/com/yahoo/config/provision/serialization/AllocatedHostsSerializerTest.java +++ b/config-provisioning/src/test/java/com/yahoo/config/provision/serialization/AllocatedHostsSerializerTest.java @@ -6,6 +6,7 @@ import com.yahoo.config.provision.AllocatedHosts; import com.yahoo.config.provision.ClusterMembership; import com.yahoo.config.provision.DockerImage; import com.yahoo.config.provision.HostSpec; +import com.yahoo.config.provision.LoadBalancerSettings; import com.yahoo.config.provision.NetworkPorts; import com.yahoo.config.provision.NodeResources; import org.junit.jupiter.api.Test; @@ -19,6 +20,7 @@ import java.util.Set; import static com.yahoo.config.provision.serialization.AllocatedHostsSerializer.fromJson; import static com.yahoo.config.provision.serialization.AllocatedHostsSerializer.toJson; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; /** * @author bratseth @@ -62,6 +64,15 @@ public class AllocatedHostsSerializerTest { Optional.empty()), Optional.of(Version.fromString("3.4.5")), Optional.empty(), Optional.empty())); + hosts.add(new HostSpec("with-load-balancer-settings", + smallSlowDiskSpeedNode, + bigSlowDiskSpeedNode, + anyDiskSpeedNode, + ClusterMembership.from("container/test/0/0", Version.fromString("6.73.1"), + Optional.empty(), new LoadBalancerSettings(List.of("burn"))), + Optional.empty(), + Optional.empty(), + Optional.empty())); hosts.add(new HostSpec("with-ports", smallSlowDiskSpeedNode, bigSlowDiskSpeedNode, diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/LoadBalancerInstance.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/LoadBalancerInstance.java index 36e42fcf541..fde3c85bdab 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/LoadBalancerInstance.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/LoadBalancerInstance.java @@ -24,11 +24,11 @@ public class LoadBalancerInstance { private final Set<Integer> ports; private final Set<String> networks; private final Set<Real> reals; - private final Optional<LoadBalancerSettings> settings; + private final LoadBalancerSettings settings; private final CloudAccount cloudAccount; public LoadBalancerInstance(Optional<DomainName> hostname, Optional<String> ipAddress, Optional<DnsZone> dnsZone, Set<Integer> ports, - Set<String> networks, Set<Real> reals, Optional<LoadBalancerSettings> settings, CloudAccount cloudAccount) { + Set<String> networks, Set<Real> reals, LoadBalancerSettings settings, CloudAccount cloudAccount) { this.hostname = Objects.requireNonNull(hostname, "hostname must be non-null"); this.ipAddress = Objects.requireNonNull(ipAddress, "ip must be non-null"); this.dnsZone = Objects.requireNonNull(dnsZone, "dnsZone must be non-null"); @@ -74,8 +74,8 @@ public class LoadBalancerInstance { return reals; } - /** Static user-configured settings of this load balancer, if set */ - public Optional<LoadBalancerSettings> settings() { + /** Static user-configured settings of this load balancer */ + public LoadBalancerSettings settings() { return settings; } @@ -89,11 +89,6 @@ public class LoadBalancerInstance { return new LoadBalancerInstance(hostname, ipAddress, dnsZone, ports, networks, reals, settings, cloudAccount); } - /** Returns a copy of this with the given settings */ - public LoadBalancerInstance withSettings(LoadBalancerSettings settings) { - return new LoadBalancerInstance(hostname, ipAddress, dnsZone, ports, networks, reals, Optional.of(settings), cloudAccount); - } - private static Set<Integer> requirePorts(Set<Integer> ports) { Objects.requireNonNull(ports, "ports must be non-null"); if (ports.isEmpty()) { diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/LoadBalancerServiceMock.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/LoadBalancerServiceMock.java index 2b6f64012b1..a756c1fc01a 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/LoadBalancerServiceMock.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/LoadBalancerServiceMock.java @@ -4,6 +4,7 @@ package com.yahoo.vespa.hosted.provision.lb; import ai.vespa.http.DomainName; import com.google.common.collect.ImmutableSet; import com.yahoo.config.provision.ClusterSpec; +import com.yahoo.config.provision.LoadBalancerSettings; import com.yahoo.config.provision.NodeType; import java.util.Collections; @@ -61,7 +62,7 @@ public class LoadBalancerServiceMock implements LoadBalancerService { Collections.singleton(4443), ImmutableSet.of("10.2.3.0/24", "10.4.5.0/24"), spec.reals(), - spec.settings(), + spec.settings().orElse(LoadBalancerSettings.empty), spec.cloudAccount()); instances.put(id, instance); return instance; diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/SharedLoadBalancerService.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/SharedLoadBalancerService.java index 13407a83f53..97e466827a6 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/SharedLoadBalancerService.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/SharedLoadBalancerService.java @@ -37,7 +37,7 @@ public class SharedLoadBalancerService implements LoadBalancerService { Set.of(4443), Set.of(), spec.reals(), - spec.settings(), + spec.settings().orElse(LoadBalancerSettings.empty), spec.cloudAccount()); } diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/LoadBalancerExpirer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/LoadBalancerExpirer.java index 0344c05c1c1..0264d0df837 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/LoadBalancerExpirer.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/LoadBalancerExpirer.java @@ -112,9 +112,9 @@ public class LoadBalancerExpirer extends NodeRepositoryMaintainer { attempts.add(1); LOG.log(Level.INFO, () -> "Removing reals from inactive load balancer " + lb.id() + ": " + Sets.difference(lb.instance().get().reals(), reals)); service.create(new LoadBalancerSpec(lb.id().application(), lb.id().cluster(), reals, - null, lb.instance().get().cloudAccount()), + lb.instance().get().settings(), lb.instance().get().cloudAccount()), true); - db.writeLoadBalancer(lb.with(lb.instance().map(instance -> instance.withReals(reals).withSettings(lb.instance().get().settings().get()))), lb.state()); + db.writeLoadBalancer(lb.with(lb.instance().map(instance -> instance.withReals(reals))), lb.state()); } catch (Exception e) { failed.add(lb.id()); lastException.set(e); diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/LoadBalancerSerializer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/LoadBalancerSerializer.java index 22f6ec97107..9eccb21b756 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/LoadBalancerSerializer.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/LoadBalancerSerializer.java @@ -75,8 +75,8 @@ public class LoadBalancerSerializer { })); loadBalancer.instance() .map(LoadBalancerInstance::settings) - .filter(settings -> ! settings.get().isEmpty()) - .ifPresent(settings -> settings.get().allowedUrns().forEach(root.setObject(settingsField) + .filter(settings -> ! settings.isEmpty()) + .ifPresent(settings -> settings.allowedUrns().forEach(root.setObject(settingsField) .setArray(allowedUrnsField)::addString)); loadBalancer.instance() .map(LoadBalancerInstance::cloudAccount) @@ -112,7 +112,7 @@ public class LoadBalancerSerializer { LoadBalancerSettings settings = loadBalancerSettings(object.field(settingsField)); CloudAccount cloudAccount = optionalString(object.field(cloudAccountField), CloudAccount::from).orElse(CloudAccount.empty); Optional<LoadBalancerInstance> instance = hostname.isEmpty() && ipAddress.isEmpty() ? Optional.empty() : - Optional.of(new LoadBalancerInstance(hostname, ipAddress, dnsZone, ports, networks, reals, Optional.of(settings), cloudAccount)); + Optional.of(new LoadBalancerInstance(hostname, ipAddress, dnsZone, ports, networks, reals, settings, cloudAccount)); return new LoadBalancer(LoadBalancerId.fromSerializedForm(object.field(idField).asString()), instance, diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/LoadBalancerProvisioner.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/LoadBalancerProvisioner.java index 17fc83bce80..26a3f231a31 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/LoadBalancerProvisioner.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/LoadBalancerProvisioner.java @@ -34,6 +34,7 @@ import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.function.Function; @@ -41,6 +42,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; +import static java.util.Objects.requireNonNullElse; import static java.util.stream.Collectors.groupingBy; import static java.util.stream.Collectors.reducing; @@ -94,7 +96,7 @@ public class LoadBalancerProvisioner { ClusterSpec.Id clusterId = effectiveId(cluster); LoadBalancerId loadBalancerId = requireNonClashing(new LoadBalancerId(application, clusterId)); NodeList nodes = nodesOf(clusterId, application); - prepare(loadBalancerId, nodes, cluster.loadBalancerSettings(), requestedNodes.cloudAccount()); + prepare(loadBalancerId, nodes, requestedNodes.cloudAccount()); } } @@ -109,18 +111,21 @@ public class LoadBalancerProvisioner { * Calling this when no load balancer has been prepared for given cluster is a no-op. */ public void activate(Set<ClusterSpec> clusters, NodeList newActive, ApplicationTransaction transaction) { - Set<ClusterSpec.Id> activatingClusters = clusters.stream() - .map(LoadBalancerProvisioner::effectiveId) - .collect(Collectors.toSet()); + Map<ClusterSpec.Id, LoadBalancerSettings> activatingClusters = clusters.stream() + .collect(groupingBy(LoadBalancerProvisioner::effectiveId, + reducing(LoadBalancerSettings.empty, + ClusterSpec::loadBalancerSettings, + (o, n) -> o.isEmpty() ? n : o))); for (var cluster : loadBalancedClustersOf(newActive).entrySet()) { - if ( ! activatingClusters.contains(cluster.getKey())) continue; + if ( ! activatingClusters.containsKey(cluster.getKey())) + continue; Node clusterNode = cluster.getValue().first().get(); if ( ! shouldProvision(transaction.application(), clusterNode.type(), clusterNode.allocation().get().membership().cluster().type())) continue; - activate(transaction, cluster.getKey(), cluster.getValue()); + activate(transaction, cluster.getKey(), activatingClusters.get(cluster.getKey()), cluster.getValue()); } // Deactivate any surplus load balancers, i.e. load balancers for clusters that have been removed - var surplusLoadBalancers = surplusLoadBalancersOf(transaction.application(), activatingClusters); + var surplusLoadBalancers = surplusLoadBalancersOf(transaction.application(), activatingClusters.keySet()); deactivate(surplusLoadBalancers, transaction.nested()); } @@ -185,10 +190,10 @@ public class LoadBalancerProvisioner { return loadBalancerId; } - private void prepare(LoadBalancerId id, NodeList nodes, LoadBalancerSettings loadBalancerSettings, CloudAccount cloudAccount) { + private void prepare(LoadBalancerId id, NodeList nodes, CloudAccount cloudAccount) { Instant now = nodeRepository.clock().instant(); Optional<LoadBalancer> loadBalancer = db.readLoadBalancer(id); - Optional<LoadBalancerInstance> instance = provisionInstance(id, nodes, loadBalancer, loadBalancerSettings, cloudAccount, false); + Optional<LoadBalancerInstance> instance = provisionInstance(id, nodes, loadBalancer, null, cloudAccount); LoadBalancer newLoadBalancer; LoadBalancer.State fromState = null; if (loadBalancer.isEmpty()) { @@ -207,17 +212,14 @@ public class LoadBalancerProvisioner { requireInstance(id, instance, cloudAccount); } - private void activate(ApplicationTransaction transaction, ClusterSpec.Id cluster, NodeList nodes) { + private void activate(ApplicationTransaction transaction, ClusterSpec.Id cluster, LoadBalancerSettings settings, NodeList nodes) { Instant now = nodeRepository.clock().instant(); LoadBalancerId id = new LoadBalancerId(transaction.application(), cluster); Optional<LoadBalancer> loadBalancer = db.readLoadBalancer(id); if (loadBalancer.isEmpty()) throw new IllegalArgumentException("Could not activate load balancer that was never prepared: " + id); if (loadBalancer.get().instance().isEmpty()) throw new IllegalArgumentException("Activating " + id + ", but prepare never provisioned a load balancer instance"); - Optional<LoadBalancerInstance> instance = provisionInstance(id, nodes, loadBalancer, - loadBalancer.get().instance().get().settings().orElseThrow(), - loadBalancer.get().instance().get().cloudAccount(), - true); + Optional<LoadBalancerInstance> instance = provisionInstance(id, nodes, loadBalancer, settings, loadBalancer.get().instance().get().cloudAccount()); LoadBalancer.State state = instance.isPresent() ? LoadBalancer.State.active : loadBalancer.get().state(); LoadBalancer newLoadBalancer = loadBalancer.get().with(instance).with(state, now); db.writeLoadBalancers(List.of(newLoadBalancer), loadBalancer.get().state(), transaction.nested()); @@ -228,8 +230,7 @@ public class LoadBalancerProvisioner { private Optional<LoadBalancerInstance> provisionInstance(LoadBalancerId id, NodeList nodes, Optional<LoadBalancer> currentLoadBalancer, LoadBalancerSettings loadBalancerSettings, - CloudAccount cloudAccount, - boolean activate) { + CloudAccount cloudAccount) { boolean shouldDeactivateRouting = deactivateRouting.with(FetchVector.Dimension.APPLICATION_ID, id.application().serializedForm()) .value(); @@ -239,14 +240,17 @@ public class LoadBalancerProvisioner { } else { reals = realsOf(nodes); } - LoadBalancerSettings settingsToUse = activate ? loadBalancerSettings : null; - if (isUpToDate(currentLoadBalancer, reals, settingsToUse)) - return currentLoadBalancer.get().instance().map(instance -> instance.withSettings(loadBalancerSettings)); + if (isUpToDate(currentLoadBalancer, reals, loadBalancerSettings)) + return currentLoadBalancer.get().instance(); log.log(Level.INFO, () -> "Provisioning instance for " + id + ", targeting: " + reals); try { - return Optional.of(service.create(new LoadBalancerSpec(id.application(), id.cluster(), reals, settingsToUse, cloudAccount), - shouldDeactivateRouting || allowEmptyReals(currentLoadBalancer)) - .withSettings(loadBalancerSettings)); + // Override settings at activation, otherwise keep existing ones. + LoadBalancerSettings settings = loadBalancerSettings != null ? loadBalancerSettings + : currentLoadBalancer.flatMap(LoadBalancer::instance) + .map(LoadBalancerInstance::settings) + .orElse(null); + return Optional.of(service.create(new LoadBalancerSpec(id.application(), id.cluster(), reals, settings, cloudAccount), + shouldDeactivateRouting || allowEmptyReals(currentLoadBalancer))); } catch (Exception e) { log.log(Level.WARNING, e, () -> "Could not (re)configure " + id + ", targeting: " + reals + ". The operation will be retried on next deployment"); @@ -306,7 +310,7 @@ public class LoadBalancerProvisioner { if (loadBalancer.isEmpty()) return false; if (loadBalancer.get().instance().isEmpty()) return false; return loadBalancer.get().instance().get().reals().equals(reals) - && (loadBalancerSettings == null || loadBalancer.get().instance().get().settings().get().equals(loadBalancerSettings)); + && (loadBalancerSettings == null || loadBalancer.get().instance().get().settings().equals(loadBalancerSettings)); } /** Returns whether to allow given load balancer to have no reals */ diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/LoadBalancerSerializerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/LoadBalancerSerializerTest.java index 307c9db38c6..1e0385d152a 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/LoadBalancerSerializerTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/LoadBalancerSerializerTest.java @@ -45,7 +45,7 @@ public class LoadBalancerSerializerTest { new Real(DomainName.of("real-2"), "127.0.0.2", 4080)), - Optional.of(new LoadBalancerSettings(List.of("123"))), + new LoadBalancerSettings(List.of("123")), CloudAccount.from("012345678912"))), LoadBalancer.State.active, now); diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/LoadBalancerProvisionerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/LoadBalancerProvisionerTest.java index a19f986a177..c7a2c94783b 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/LoadBalancerProvisionerTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/LoadBalancerProvisionerTest.java @@ -320,14 +320,14 @@ public class LoadBalancerProvisionerTest { tester.activate(app1, prepare(app1, capacity, clusterRequest(ClusterSpec.Type.container, ClusterSpec.Id.from("c1")))); LoadBalancerList loadBalancers = tester.nodeRepository().loadBalancers().list(); assertEquals(1, loadBalancers.size()); - assertEquals(LoadBalancerSettings.empty, loadBalancers.first().get().instance().get().settings().get()); + assertEquals(LoadBalancerSettings.empty, loadBalancers.first().get().instance().get().settings()); // Next deployment contains new settings LoadBalancerSettings settings = new LoadBalancerSettings(List.of("alice", "bob")); tester.activate(app1, prepare(app1, capacity, clusterRequest(ClusterSpec.Type.container, ClusterSpec.Id.from("c1"), Optional.empty(), settings))); loadBalancers = tester.nodeRepository().loadBalancers().list(); assertEquals(1, loadBalancers.size()); - assertEquals(settings, loadBalancers.first().get().instance().get().settings().get()); + assertEquals(settings, loadBalancers.first().get().instance().get().settings()); } |