summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java7
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ActivatedModelsBuilder.java5
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ModelsBuilder.java29
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/PreparedModelsBuilder.java30
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/provision/ProvisionerAdapter.java3
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/provision/StaticProvisioner.java27
-rw-r--r--vespajlib/src/main/java/com/yahoo/lang/SettableOptional.java6
7 files changed, 79 insertions, 28 deletions
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java
index 820ad8e530c..70b677b4057 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java
@@ -18,7 +18,7 @@ import java.util.Set;
/**
* Implementation of {@link ModelContext} for configserver.
*
- * @author lulf
+ * @author Ulf Lilleengen
*/
public class ModelContextImpl implements ModelContext {
@@ -83,6 +83,11 @@ public class ModelContextImpl implements ModelContext {
return permanentApplicationPackage;
}
+ /**
+ * Returns the host provisioner to use, or empty to use the default provisioner,
+ * creating hosts from the application package defined hosts
+ */
+ // TODO: Don't allow empty here but create the right provisioner when this is set up instead
@Override
public Optional<HostProvisioner> hostProvisioner() {
return hostProvisioner;
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ActivatedModelsBuilder.java b/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ActivatedModelsBuilder.java
index ae347f310dd..1301f24788f 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ActivatedModelsBuilder.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ActivatedModelsBuilder.java
@@ -13,7 +13,6 @@ import com.yahoo.config.provision.AllocatedHosts;
import com.yahoo.config.provision.TenantName;
import com.yahoo.config.provision.Version;
import com.yahoo.config.provision.Zone;
-import com.yahoo.lang.SettableOptional;
import com.yahoo.log.LogLevel;
import com.yahoo.vespa.config.server.GlobalComponentRegistry;
import com.yahoo.vespa.config.server.tenant.Rotations;
@@ -73,7 +72,7 @@ public class ActivatedModelsBuilder extends ModelsBuilder<Application> {
ApplicationPackage applicationPackage,
ApplicationId applicationId,
com.yahoo.component.Version wantedNodeVespaVersion,
- SettableOptional<AllocatedHosts> ignored, // Ignored since we have this in the app package for activated models
+ Optional<AllocatedHosts> ignored, // Ignored since we have this in the app package for activated models
Instant now) {
log.log(LogLevel.DEBUG, String.format("Loading model version %s for session %s application %s",
modelFactory.getVersion(), appGeneration, applicationId));
@@ -85,7 +84,7 @@ public class ActivatedModelsBuilder extends ModelsBuilder<Application> {
logger,
configDefinitionRepo,
getForVersionOrLatest(applicationPackage.getFileRegistryMap(), modelFactory.getVersion()).orElse(new MockFileRegistry()),
- createHostProvisioner(applicationPackage.getAllocatedHosts()),
+ createStaticProvisioner(applicationPackage.getAllocatedHosts()),
createModelContextProperties(applicationId),
Optional.empty(),
new com.yahoo.component.Version(modelFactory.getVersion().toString()),
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ModelsBuilder.java b/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ModelsBuilder.java
index 55b63126f04..bbd59e0628e 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ModelsBuilder.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ModelsBuilder.java
@@ -112,21 +112,20 @@ public abstract class ModelsBuilder<MODELRESULT extends ModelResult> {
Instant now) {
Version latest = findLatest(versions);
// load latest application version
- MODELRESULT latestApplicationVersion = buildModelVersion(modelFactoryRegistry.getFactory(latest),
+ MODELRESULT latestModelVersion = buildModelVersion(modelFactoryRegistry.getFactory(latest),
applicationPackage,
applicationId,
wantedNodeVespaVersion,
- allocatedHosts,
+ allocatedHosts.asOptional(),
now);
- if ( ! allocatedHosts.isPresent())
- allocatedHosts.set(latestApplicationVersion.getModel().allocatedHosts());
+ allocatedHosts.set(latestModelVersion.getModel().allocatedHosts()); // Update with additional clusters allocated
- if (latestApplicationVersion.getModel().skipOldConfigModels(now))
- return Collections.singletonList(latestApplicationVersion);
+ if (latestModelVersion.getModel().skipOldConfigModels(now))
+ return Collections.singletonList(latestModelVersion);
// load old model versions
List<MODELRESULT> allApplicationVersions = new ArrayList<>();
- allApplicationVersions.add(latestApplicationVersion);
+ allApplicationVersions.add(latestModelVersion);
// TODO: We use the allocated hosts from the newest version when building older model versions.
// This is correct except for the case where an old model specifies a cluster which the new version
@@ -135,12 +134,15 @@ public abstract class ModelsBuilder<MODELRESULT extends ModelResult> {
// clusters and the node repository provisioner as fallback.
for (Version version : versions) {
if (version.equals(latest)) continue; // already loaded
- allApplicationVersions.add(buildModelVersion(modelFactoryRegistry.getFactory(version),
+
+ MODELRESULT modelVersion = buildModelVersion(modelFactoryRegistry.getFactory(version),
applicationPackage,
applicationId,
wantedNodeVespaVersion,
- allocatedHosts,
- now));
+ allocatedHosts.asOptional(),
+ now);
+ allocatedHosts.set(modelVersion.getModel().allocatedHosts()); // Update with additional clusters allocated
+ allApplicationVersions.add(modelVersion);
}
return allApplicationVersions;
}
@@ -161,7 +163,7 @@ public abstract class ModelsBuilder<MODELRESULT extends ModelResult> {
protected abstract MODELRESULT buildModelVersion(ModelFactory modelFactory, ApplicationPackage applicationPackage,
ApplicationId applicationId,
com.yahoo.component.Version wantedNodeVespaVersion,
- SettableOptional<AllocatedHosts> allocatedHosts,
+ Optional<AllocatedHosts> allocatedHosts,
Instant now);
protected ModelContext.Properties createModelContextProperties(ApplicationId applicationId,
@@ -178,9 +180,10 @@ public abstract class ModelsBuilder<MODELRESULT extends ModelResult> {
/**
* Returns a host provisioner returning the previously allocated hosts if available and when on hosted Vespa,
- * returns empty otherwise.
+ * returns empty otherwise, which may either mean that no hosts are allocated or that we are running
+ * non-hosted and should default to use hosts defined in the application package, depending on context
*/
- protected Optional<HostProvisioner> createHostProvisioner(Optional<AllocatedHosts> allocatedHosts) {
+ protected Optional<HostProvisioner> createStaticProvisioner(Optional<AllocatedHosts> allocatedHosts) {
if (hosted && allocatedHosts.isPresent())
return Optional.of(new StaticProvisioner(allocatedHosts.get()));
return Optional.empty();
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/PreparedModelsBuilder.java b/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/PreparedModelsBuilder.java
index cc382eb94d2..d9c25914b6b 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/PreparedModelsBuilder.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/PreparedModelsBuilder.java
@@ -14,7 +14,6 @@ import com.yahoo.config.model.application.provider.FilesApplicationPackage;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.AllocatedHosts;
import com.yahoo.config.provision.Version;
-import com.yahoo.lang.SettableOptional;
import com.yahoo.log.LogLevel;
import com.yahoo.vespa.config.server.application.ApplicationSet;
import com.yahoo.vespa.config.server.host.HostValidator;
@@ -23,6 +22,7 @@ import com.yahoo.vespa.config.server.deploy.ModelContextImpl;
import com.yahoo.vespa.config.server.filedistribution.FileDistributionProvider;
import com.yahoo.vespa.config.server.provision.HostProvisionerProvider;
import com.yahoo.vespa.config.server.provision.ProvisionerAdapter;
+import com.yahoo.vespa.config.server.provision.StaticProvisioner;
import com.yahoo.vespa.config.server.session.FileDistributionFactory;
import com.yahoo.vespa.config.server.session.PrepareParams;
import com.yahoo.vespa.config.server.session.SessionContext;
@@ -82,7 +82,7 @@ public class PreparedModelsBuilder extends ModelsBuilder<PreparedModelsBuilder.P
ApplicationPackage applicationPackage,
ApplicationId applicationId,
com.yahoo.component.Version wantedNodeVespaVersion,
- SettableOptional<AllocatedHosts> activatedHosts,
+ Optional<AllocatedHosts> allocatedHosts,
Instant now) {
Version modelVersion = modelFactory.getVersion();
log.log(LogLevel.DEBUG, "Start building model for Vespa version " + modelVersion);
@@ -90,10 +90,8 @@ public class PreparedModelsBuilder extends ModelsBuilder<PreparedModelsBuilder.P
context.getServerDBSessionDir(),
applicationId);
- // Use already allocated hosts if available, create connection to a host provisioner otherwise
- Optional<HostProvisioner> hostProvisioner =
- activatedHosts.isPresent() ? createHostProvisioner(Optional.of(activatedHosts.get())) :
- createHostProvisionerAdapter(properties);
+ // Use empty on non-hosted systems, use already allocated hosts if available, create connection to a host provisioner otherwise
+ Optional<HostProvisioner> hostProvisioner = createHostProvisioner(allocatedHosts);
Optional<Model> previousModel = currentActiveApplicationSet
.map(set -> set.getForVersionOrLatest(Optional.of(modelVersion), now).getModel());
ModelContext modelContext = new ModelContextImpl(
@@ -116,6 +114,24 @@ public class PreparedModelsBuilder extends ModelsBuilder<PreparedModelsBuilder.P
return new PreparedModelsBuilder.PreparedModelResult(modelVersion, result.getModel(), fileDistributionProvider, result.getConfigChangeActions());
}
+ // This method is an excellent demonstration of what happens when one is too liberal with Optional
+ // -bratseth, who had to write this :-/
+ private Optional<HostProvisioner> createHostProvisioner(Optional<AllocatedHosts> allocatedHosts) {
+ Optional<HostProvisioner> nodeRepositoryProvisioner = createNodeRepositoryProvisioner(properties);
+ if ( ! allocatedHosts.isPresent()) return nodeRepositoryProvisioner;
+
+ Optional<HostProvisioner> staticProvisioner = createStaticProvisioner(allocatedHosts);
+ if ( ! staticProvisioner.isPresent()) return Optional.empty(); // Since we have hosts allocated this means we are on non-hosted
+
+ // The following option should not be possible, but since there is a right action for it we can take it
+ if ( ! nodeRepositoryProvisioner.isPresent())
+ return Optional.of(new StaticProvisioner(allocatedHosts.get()));
+
+ // Nodes are already allocated by a model and we should use them unless this model requests hosts from a
+ // previously unallocated cluster. This allows future models to stop allocate certain clusters.
+ return Optional.of(new StaticProvisioner(allocatedHosts.get(), nodeRepositoryProvisioner.get()));
+ }
+
private Optional<File> getAppDir(ApplicationPackage applicationPackage) {
try {
return applicationPackage instanceof FilesApplicationPackage ?
@@ -131,7 +147,7 @@ public class PreparedModelsBuilder extends ModelsBuilder<PreparedModelsBuilder.P
.collect(Collectors.toList()));
}
- private Optional<HostProvisioner> createHostProvisionerAdapter(ModelContext.Properties properties) {
+ private Optional<HostProvisioner> createNodeRepositoryProvisioner(ModelContext.Properties properties) {
return hostProvisionerProvider.getHostProvisioner().map(
provisioner -> new ProvisionerAdapter(provisioner, properties.applicationId()));
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/provision/ProvisionerAdapter.java b/configserver/src/main/java/com/yahoo/vespa/config/server/provision/ProvisionerAdapter.java
index 82e5281135f..32380b296dd 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/provision/ProvisionerAdapter.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/provision/ProvisionerAdapter.java
@@ -16,7 +16,7 @@ import java.util.*;
* behavior to the config model. Adapts interface from a {@link HostProvisioner} to a
* {@link Provisioner}.
*
- * @author lulf
+ * @author Ulf Lilleengen
*/
public class ProvisionerAdapter implements HostProvisioner {
@@ -30,6 +30,7 @@ public class ProvisionerAdapter implements HostProvisioner {
@Override
public HostSpec allocateHost(String alias) {
+ // Wow. Such mess. TODO: Actually support polymorphy or stop pretending to, see also ModelContextImpl.getHostProvisioner
throw new UnsupportedOperationException("Allocating a single host in a hosted environment is not possible");
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/provision/StaticProvisioner.java b/configserver/src/main/java/com/yahoo/vespa/config/server/provision/StaticProvisioner.java
index c5e8d4b87ea..7e97690331f 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/provision/StaticProvisioner.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/provision/StaticProvisioner.java
@@ -5,6 +5,7 @@ import com.yahoo.config.model.api.HostProvisioner;
import com.yahoo.config.provision.*;
import java.util.List;
+import java.util.Optional;
import java.util.stream.Collectors;
/**
@@ -15,9 +16,24 @@ import java.util.stream.Collectors;
public class StaticProvisioner implements HostProvisioner {
private final AllocatedHosts allocatedHosts;
+
+ /** The fallback provisioner to use for unknown clusters, or null to not fall back */
+ private final HostProvisioner fallback;
+ /**
+ * Creates a static host provisioner with no fallback
+ */
public StaticProvisioner(AllocatedHosts allocatedHosts) {
+ this(allocatedHosts, null);
+ }
+
+ /**
+ * Creates a static host provisioner which will fall back to using the given provisioner
+ * if a request is made for nodes in a cluster which is not present in this allocation.
+ */
+ public StaticProvisioner(AllocatedHosts allocatedHosts, HostProvisioner fallback) {
this.allocatedHosts = allocatedHosts;
+ this.fallback = fallback;
}
@Override
@@ -27,9 +43,14 @@ public class StaticProvisioner implements HostProvisioner {
@Override
public List<HostSpec> prepare(ClusterSpec cluster, Capacity capacity, int groups, ProvisionLogger logger) {
- return allocatedHosts.getHosts().stream()
- .filter(host -> host.membership().isPresent() && matches(host.membership().get().cluster(), cluster))
- .collect(Collectors.toList());
+ List<HostSpec> hostsAlreadyAllocatedToCluster =
+ allocatedHosts.getHosts().stream()
+ .filter(host -> host.membership().isPresent() && matches(host.membership().get().cluster(), cluster))
+ .collect(Collectors.toList());
+ if ( ! hostsAlreadyAllocatedToCluster.isEmpty())
+ return hostsAlreadyAllocatedToCluster;
+ else
+ return fallback.prepare(cluster, capacity, groups, logger);
}
private boolean matches(ClusterSpec nodeCluster, ClusterSpec requestedCluster) {
diff --git a/vespajlib/src/main/java/com/yahoo/lang/SettableOptional.java b/vespajlib/src/main/java/com/yahoo/lang/SettableOptional.java
index 74abd4101a4..00ff06b8f01 100644
--- a/vespajlib/src/main/java/com/yahoo/lang/SettableOptional.java
+++ b/vespajlib/src/main/java/com/yahoo/lang/SettableOptional.java
@@ -1,6 +1,7 @@
package com.yahoo.lang;
import java.util.NoSuchElementException;
+import java.util.Optional;
/**
* An optional which contains a settable value
@@ -30,6 +31,11 @@ public final class SettableOptional<T> {
public void set(T value) {
this.value = value;
}
+
+ public Optional<T> asOptional() {
+ if (value == null) return Optional.empty();
+ return Optional.of(value);
+ }
}