diff options
author | Harald Musum <musum@verizonmedia.com> | 2020-10-05 11:48:37 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-10-05 11:48:37 +0200 |
commit | 7e01ac092d8bf6002987a57c47d726eeac4db1c1 (patch) | |
tree | 915b2734c27f41ad8708ba48dd6fff77b43ff7bb | |
parent | 13092df7574517948618c335a588bf8b46eab049 (diff) |
Revert "Revert "Move code out of RemoteSesion""
13 files changed, 410 insertions, 411 deletions
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java b/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java index 5bbe9c967cf..510569355f5 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java @@ -605,7 +605,8 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye if (tenant == null) throw new NotFoundException("Tenant '" + applicationId.tenant() + "' not found"); long sessionId = getSessionIdForApplication(tenant, applicationId); RemoteSession session = getRemoteSession(tenant, sessionId); - return session.ensureApplicationLoaded().getForVersionOrLatest(version, clock.instant()); + SessionRepository sessionRepository = tenant.getSessionRepository(); + return sessionRepository.ensureApplicationLoaded(session).getForVersionOrLatest(version, clock.instant()); } catch (NotFoundException e) { log.log(Level.WARNING, "Failed getting application for '" + applicationId + "': " + e.getMessage()); throw e; diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSession.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSession.java index 9417a798d1f..1eeac5c7280 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSession.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/RemoteSession.java @@ -1,21 +1,11 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.config.server.session; -import com.yahoo.config.application.api.ApplicationPackage; -import com.yahoo.config.provision.AllocatedHosts; import com.yahoo.config.provision.TenantName; -import com.yahoo.lang.SettableOptional; -import com.yahoo.transaction.Transaction; -import com.yahoo.vespa.config.server.GlobalComponentRegistry; import com.yahoo.vespa.config.server.application.ApplicationSet; -import com.yahoo.vespa.config.server.modelfactory.ActivatedModelsBuilder; -import com.yahoo.vespa.curator.Curator; -import org.apache.zookeeper.KeeperException; -import java.time.Clock; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; +import java.util.Objects; +import java.util.Optional; /** * A RemoteSession represents a session created on another config server. This session can @@ -25,91 +15,48 @@ import java.util.logging.Logger; */ public class RemoteSession extends Session { - private static final Logger log = Logger.getLogger(RemoteSession.class.getName()); - private ApplicationSet applicationSet = null; - private final ActivatedModelsBuilder applicationLoader; - private final Clock clock; + private final Optional<ApplicationSet> applicationSet; /** * Creates a session. This involves loading the application, validating it and distributing it. * * @param tenant The name of the tenant creating session * @param sessionId The session id for this session. - * @param componentRegistry a registry of global components * @param zooKeeperClient a SessionZooKeeperClient instance */ - public RemoteSession(TenantName tenant, - long sessionId, - GlobalComponentRegistry componentRegistry, - SessionZooKeeperClient zooKeeperClient) { - super(tenant, sessionId, zooKeeperClient); - this.applicationLoader = new ActivatedModelsBuilder(tenant, sessionId, zooKeeperClient, componentRegistry); - this.clock = componentRegistry.getClock(); - } - - void prepare() { - Curator.CompletionWaiter waiter = sessionZooKeeperClient.getPrepareWaiter(); - ensureApplicationLoaded(); - notifyCompletion(waiter); - } - - private ApplicationSet loadApplication() { - ApplicationPackage applicationPackage = sessionZooKeeperClient.loadApplicationPackage(); - - // Read hosts allocated on the config server instance which created this - SettableOptional<AllocatedHosts> allocatedHosts = new SettableOptional<>(applicationPackage.getAllocatedHosts()); - - return ApplicationSet.fromList(applicationLoader.buildModels(getApplicationId(), - sessionZooKeeperClient.readDockerImageRepository(), - sessionZooKeeperClient.readVespaVersion(), - applicationPackage, - allocatedHosts, - clock.instant())); + RemoteSession(TenantName tenant, long sessionId, SessionZooKeeperClient zooKeeperClient) { + this(tenant, sessionId, zooKeeperClient, Optional.empty()); } - public synchronized ApplicationSet ensureApplicationLoaded() { - return applicationSet == null ? applicationSet = loadApplication() : applicationSet; + /** + * Creates a remote session, with application set + * + * @param tenant The name of the tenant creating session + * @param sessionId The session id for this session. + * @param zooKeeperClient a SessionZooKeeperClient instance + * @param applicationSet current application set for this session + */ + RemoteSession(TenantName tenant, long sessionId, SessionZooKeeperClient zooKeeperClient, Optional<ApplicationSet> applicationSet) { + super(tenant, sessionId, zooKeeperClient); + this.applicationSet = applicationSet; } - public synchronized void deactivate() { - applicationSet = null; + Optional<ApplicationSet> applicationSet() { + return applicationSet; } - void confirmUpload() { - Curator.CompletionWaiter waiter = sessionZooKeeperClient.getUploadWaiter(); - log.log(Level.FINE, "Notifying upload waiter for session " + getSessionId()); - notifyCompletion(waiter); - log.log(Level.FINE, "Done notifying upload for session " + getSessionId()); + public synchronized RemoteSession activated(ApplicationSet applicationSet) { + Objects.requireNonNull(applicationSet, "applicationSet cannot be null"); + return new RemoteSession(tenant, sessionId, sessionZooKeeperClient, Optional.of(applicationSet)); } - void notifyCompletion(Curator.CompletionWaiter completionWaiter) { - try { - completionWaiter.notifyCompletion(); - } catch (RuntimeException e) { - // Throw only if we get something else than NoNodeException or NodeExistsException. - // NoNodeException might happen when the session is no longer in use (e.g. the app using this session - // has been deleted) and this method has not been called yet for the previous session operation on a - // minority of the config servers. - // NodeExistsException might happen if an event for this node is delivered more than once, in that case - // this is a no-op - Set<Class<? extends KeeperException>> acceptedExceptions = Set.of(KeeperException.NoNodeException.class, - KeeperException.NodeExistsException.class); - Class<? extends Throwable> exceptionClass = e.getCause().getClass(); - if (acceptedExceptions.contains(exceptionClass)) - log.log(Level.FINE, "Not able to notify completion for session " + getSessionId() + - " (" + completionWaiter + ")," + - " node " + (exceptionClass.equals(KeeperException.NoNodeException.class) - ? "has been deleted" - : "already exists")); - else - throw e; - } + public synchronized RemoteSession deactivated() { + return new RemoteSession(tenant, sessionId, sessionZooKeeperClient, Optional.empty()); } - public void delete() { - Transaction transaction = sessionZooKeeperClient.deleteTransaction(); - transaction.commit(); - transaction.close(); + @Override + public String toString() { + return super.toString() + ",application set=" + applicationSet; } } diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/Session.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/Session.java index ea17b8a2640..dad9e48ce19 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/Session.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/Session.java @@ -1,4 +1,4 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.config.server.session; import com.yahoo.component.Version; @@ -28,7 +28,7 @@ import java.util.Optional; */ public abstract class Session implements Comparable<Session> { - private final long sessionId; + protected final long sessionId; protected final TenantName tenant; protected final SessionZooKeeperClient sessionZooKeeperClient; protected final Optional<ApplicationPackage> applicationPackage; diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java index edc7a070063..87f1987ce8e 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java @@ -1,4 +1,4 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.config.server.session; import com.google.common.collect.HashMultiset; @@ -8,9 +8,11 @@ import com.yahoo.config.application.api.ApplicationPackage; import com.yahoo.config.application.api.DeployLogger; import com.yahoo.config.model.application.provider.DeployData; import com.yahoo.config.model.application.provider.FilesApplicationPackage; +import com.yahoo.config.provision.AllocatedHosts; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.TenantName; import com.yahoo.io.IOUtils; +import com.yahoo.lang.SettableOptional; import com.yahoo.path.Path; import com.yahoo.transaction.AbstractTransaction; import com.yahoo.transaction.NestedTransaction; @@ -22,6 +24,7 @@ import com.yahoo.vespa.config.server.application.TenantApplications; import com.yahoo.vespa.config.server.configchange.ConfigChangeActions; import com.yahoo.vespa.config.server.deploy.TenantFileSystemDirs; import com.yahoo.vespa.config.server.filedistribution.FileDirectory; +import com.yahoo.vespa.config.server.modelfactory.ActivatedModelsBuilder; import com.yahoo.vespa.config.server.monitoring.MetricUpdater; import com.yahoo.vespa.config.server.monitoring.Metrics; import com.yahoo.vespa.config.server.tenant.TenantRepository; @@ -35,6 +38,7 @@ import com.yahoo.vespa.flags.Flags; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.recipes.cache.ChildData; import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent; +import org.apache.zookeeper.KeeperException; import java.io.File; import java.io.FilenameFilter; @@ -50,6 +54,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; import java.util.logging.Level; @@ -259,13 +264,25 @@ public class SessionRepository { if (session.getStatus() == Session.Status.ACTIVATE) continue; if (sessionHasExpired(session.getCreateTime(), expiryTime, clock)) { log.log(Level.FINE, () -> "Remote session " + sessionId + " for " + tenantName + " has expired, deleting it"); - session.delete(); + deleteRemoteSessionFromZooKeeper(session); deleted++; } } return deleted; } + public void deactivate(RemoteSession remoteSession) { + RemoteSession session = remoteSession.deactivated(); + remoteSessionCache.put(session.getSessionId(), session); + } + + public void deleteRemoteSessionFromZooKeeper(RemoteSession session) { + SessionZooKeeperClient sessionZooKeeperClient = createSessionZooKeeperClient(session.getSessionId()); + Transaction transaction = sessionZooKeeperClient.deleteTransaction(); + transaction.commit(); + transaction.close(); + } + // TODO: Delete after 7.294 has been rolled out everywhere public int deleteExpiredLocks(Clock clock, Duration expiryTime) { int deleted = 0; @@ -333,7 +350,7 @@ public class SessionRepository { RemoteSession session = createRemoteSession(sessionId); if (session.getStatus() == Session.Status.NEW) { log.log(Level.FINE, () -> session.logPre() + "Confirming upload for session " + sessionId); - session.confirmUpload(); + confirmUpload(session); } if (distributeApplicationPackage()) createLocalSessionUsingDistributedApplicationPackage(sessionId); @@ -343,24 +360,20 @@ public class SessionRepository { long sessionId = session.getSessionId(); Curator.CompletionWaiter waiter = createSessionZooKeeperClient(sessionId).getActiveWaiter(); log.log(Level.FINE, () -> session.logPre() + "Getting session from repo: " + sessionId); - ApplicationSet app = session.ensureApplicationLoaded(); + ApplicationSet app = ensureApplicationLoaded(session); log.log(Level.FINE, () -> session.logPre() + "Reloading config for " + sessionId); applicationRepo.reloadConfig(app); log.log(Level.FINE, () -> session.logPre() + "Notifying " + waiter); - session.notifyCompletion(waiter); + notifyCompletion(waiter, session); log.log(Level.INFO, session.logPre() + "Session activated: " + sessionId); } - public void deactivate(RemoteSession remoteSession) { - remoteSession.deactivate(); - } - public void delete(RemoteSession remoteSession) { long sessionId = remoteSession.getSessionId(); // TODO: Change log level to FINE when debugging is finished log.log(Level.INFO, () -> remoteSession.logPre() + "Deactivating and deleting remote session " + sessionId); - remoteSession.deactivate(); - remoteSession.delete(); + deactivate(remoteSession); + deleteRemoteSessionFromZooKeeper(remoteSession); remoteSessionCache.remove(sessionId); LocalSession localSession = getLocalSession(sessionId); if (localSession != null) { @@ -370,10 +383,6 @@ public class SessionRepository { } } - void prepare(RemoteSession session) { - session.prepare(); - } - boolean distributeApplicationPackage() { return distributeApplicationPackage.value(); } @@ -383,7 +392,7 @@ public class SessionRepository { if (watcher != null) watcher.close(); RemoteSession session = remoteSessionCache.remove(sessionId); if (session != null) { - session.deactivate(); + deactivate(session); } metrics.incRemovedSessions(); } @@ -392,13 +401,86 @@ public class SessionRepository { for (ApplicationId applicationId : applicationRepo.activeApplications()) { if (applicationRepo.requireActiveSessionOf(applicationId) == session.getSessionId()) { log.log(Level.FINE, () -> "Found active application for session " + session.getSessionId() + " , loading it"); - applicationRepo.reloadConfig(session.ensureApplicationLoaded()); + applicationRepo.reloadConfig(ensureApplicationLoaded(session)); log.log(Level.INFO, session.logPre() + "Application activated successfully: " + applicationId + " (generation " + session.getSessionId() + ")"); return; } } } + void prepareRemoteSession(RemoteSession session) { + SessionZooKeeperClient sessionZooKeeperClient = createSessionZooKeeperClient(session.getSessionId()); + Curator.CompletionWaiter waiter = sessionZooKeeperClient.getPrepareWaiter(); + ensureApplicationLoaded(session); + notifyCompletion(waiter, session); + } + + public ApplicationSet ensureApplicationLoaded(RemoteSession session) { + + if (session.applicationSet().isPresent()) { + return session.applicationSet().get(); + } + + ApplicationSet applicationSet = loadApplication(session); + RemoteSession activated = session.activated(applicationSet); + long sessionId = activated.getSessionId(); + remoteSessionCache.put(sessionId, activated); + addSessionStateWatcher(sessionId, activated); + + return applicationSet; + } + + void confirmUpload(RemoteSession session) { + Curator.CompletionWaiter waiter = session.getSessionZooKeeperClient().getUploadWaiter(); + long sessionId = session.getSessionId(); + log.log(Level.FINE, "Notifying upload waiter for session " + sessionId); + notifyCompletion(waiter, session); + log.log(Level.FINE, "Done notifying upload for session " + sessionId); + } + + void notifyCompletion(Curator.CompletionWaiter completionWaiter, RemoteSession session) { + try { + completionWaiter.notifyCompletion(); + } catch (RuntimeException e) { + // Throw only if we get something else than NoNodeException or NodeExistsException. + // NoNodeException might happen when the session is no longer in use (e.g. the app using this session + // has been deleted) and this method has not been called yet for the previous session operation on a + // minority of the config servers. + // NodeExistsException might happen if an event for this node is delivered more than once, in that case + // this is a no-op + Set<Class<? extends KeeperException>> acceptedExceptions = Set.of(KeeperException.NoNodeException.class, + KeeperException.NodeExistsException.class); + Class<? extends Throwable> exceptionClass = e.getCause().getClass(); + if (acceptedExceptions.contains(exceptionClass)) + log.log(Level.FINE, "Not able to notify completion for session " + session.getSessionId() + + " (" + completionWaiter + ")," + + " node " + (exceptionClass.equals(KeeperException.NoNodeException.class) + ? "has been deleted" + : "already exists")); + else + throw e; + } + } + + private ApplicationSet loadApplication(RemoteSession session) { + log.log(Level.FINE, () -> "Loading application for " + session); + SessionZooKeeperClient sessionZooKeeperClient = createSessionZooKeeperClient(session.getSessionId()); + ApplicationPackage applicationPackage = sessionZooKeeperClient.loadApplicationPackage(); + ActivatedModelsBuilder builder = new ActivatedModelsBuilder(session.getTenantName(), + session.getSessionId(), + sessionZooKeeperClient, + componentRegistry); + // Read hosts allocated on the config server instance which created this + SettableOptional<AllocatedHosts> allocatedHosts = new SettableOptional<>(applicationPackage.getAllocatedHosts()); + + return ApplicationSet.fromList(builder.buildModels(session.getApplicationId(), + sessionZooKeeperClient.readDockerImageRepository(), + sessionZooKeeperClient.readVespaVersion(), + applicationPackage, + allocatedHosts, + clock.instant())); + } + private void nodeChanged() { zkWatcherExecutor.execute(() -> { Multiset<Session.Status> sessionMetrics = HashMultiset.create(); @@ -444,7 +526,7 @@ public class SessionRepository { public synchronized RemoteSession createRemoteSession(long sessionId) { SessionZooKeeperClient sessionZKClient = createSessionZooKeeperClient(sessionId); - RemoteSession session = new RemoteSession(tenantName, sessionId, componentRegistry, sessionZKClient); + RemoteSession session = new RemoteSession(tenantName, sessionId, sessionZKClient); remoteSessionCache.put(sessionId, session); loadSessionIfActive(session); addSessionStateWatcher(sessionId, session); @@ -566,7 +648,7 @@ public class SessionRepository { try { long currentActiveSessionId = applicationRepo.requireActiveSessionOf(appId); RemoteSession currentActiveSession = getRemoteSession(currentActiveSessionId); - currentActiveApplicationSet = Optional.ofNullable(currentActiveSession.ensureApplicationLoaded()); + currentActiveApplicationSet = Optional.ofNullable(ensureApplicationLoaded(currentActiveSession)); } catch (IllegalArgumentException e) { // Do nothing if we have no currently active session } diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionStateWatcher.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionStateWatcher.java index d6d08aaac6c..aa69de47964 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionStateWatcher.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionStateWatcher.java @@ -51,7 +51,7 @@ public class SessionStateWatcher { break; case PREPARE: createLocalSession(sessionId); - sessionRepository.prepare(session); + sessionRepository.prepareRemoteSession(session); break; case ACTIVATE: createLocalSession(sessionId); diff --git a/configserver/src/test/apps/app-major-version-2/deployment.xml b/configserver/src/test/apps/app-major-version-2/deployment.xml new file mode 100644 index 00000000000..7523c104b7e --- /dev/null +++ b/configserver/src/test/apps/app-major-version-2/deployment.xml @@ -0,0 +1 @@ +<deployment version='1.0' major-version='2'/> diff --git a/configserver/src/test/apps/app-major-version-2/hosts.xml b/configserver/src/test/apps/app-major-version-2/hosts.xml new file mode 100644 index 00000000000..f4256c9fc81 --- /dev/null +++ b/configserver/src/test/apps/app-major-version-2/hosts.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8" ?> +<!-- Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<hosts> + <host name="mytesthost"> + <alias>node1</alias> + </host> +</hosts> diff --git a/configserver/src/test/apps/app-major-version-2/searchdefinitions/music.sd b/configserver/src/test/apps/app-major-version-2/searchdefinitions/music.sd new file mode 100644 index 00000000000..7670e78f22b --- /dev/null +++ b/configserver/src/test/apps/app-major-version-2/searchdefinitions/music.sd @@ -0,0 +1,50 @@ +# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +# A basic search definition - called music, should be saved to music.sd +search music { + + # It contains one document type only - called music as well + document music { + + field title type string { + indexing: summary | index # How this field should be indexed + # index-to: title, default # Create two indexes + weight: 75 # Ranking importancy of this field, used by the built in nativeRank feature + } + + field artist type string { + indexing: summary | attribute | index + # index-to: artist, default + + weight: 25 + } + + field year type int { + indexing: summary | attribute + } + + # Increase query + field popularity type int { + indexing: summary | attribute + } + + field url type uri { + indexing: summary | index + } + + } + + rank-profile default inherits default { + first-phase { + expression: nativeRank(title,artist) + attribute(popularity) + } + + } + + rank-profile textmatch inherits default { + first-phase { + expression: nativeRank(title,artist) + } + + } + +} diff --git a/configserver/src/test/apps/app-major-version-2/services.xml b/configserver/src/test/apps/app-major-version-2/services.xml new file mode 100644 index 00000000000..509d7786be0 --- /dev/null +++ b/configserver/src/test/apps/app-major-version-2/services.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8" ?> +<!-- Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<services version="1.0"> + + <admin version="2.0"> + <adminserver hostalias="node1"/> + <logserver hostalias="node1" /> + </admin> + + <content version="1.0"> + <redundancy>2</redundancy> + <documents> + <document type="music" mode="index"/> + </documents> + <nodes> + <node hostalias="node1" distribution-key="0"/> + </nodes> + + </content> + + <container version="1.0"> + <document-processing compressdocuments="true"> + <chain id="ContainerWrapperTest"> + <documentprocessor id="com.yahoo.vespa.config.AppleDocProc"/> + </chain> + </document-processing> + + <config name="project.specific"> + <value>someval</value> + </config> + + <nodes> + <node hostalias="node1" /> + </nodes> + + </container> + +</services> diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandlerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandlerTest.java index 9e4ca8dfb2c..7e41adf6ddc 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandlerTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/http/v2/ApplicationHandlerTest.java @@ -1,4 +1,4 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.config.server.http.v2; import com.fasterxml.jackson.databind.ObjectMapper; diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/RpcServerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/RpcServerTest.java index ae6bd5feeab..4d7702eec9e 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/RpcServerTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/rpc/RpcServerTest.java @@ -1,4 +1,4 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.config.server.rpc; import com.yahoo.cloud.config.ConfigserverConfig; @@ -64,7 +64,8 @@ public class RpcServerTest { ApplicationRepository applicationRepository = tester.applicationRepository(); applicationRepository.deploy(testApp, new PrepareParams.Builder().applicationId(applicationId).build()); TenantApplications applicationRepo = tester.tenant().getApplicationRepo(); - applicationRepo.reloadConfig(applicationRepository.getActiveSession(applicationId).ensureApplicationLoaded()); + ApplicationSet applicationSet = tester.tenant().getSessionRepository().ensureApplicationLoaded(applicationRepository.getActiveSession(applicationId)); + applicationRepo.reloadConfig(applicationSet); testPrintStatistics(tester); testGetConfig(tester); testEnabled(tester); diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/session/RemoteSessionTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/session/RemoteSessionTest.java deleted file mode 100644 index cda3e09d3a8..00000000000 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/session/RemoteSessionTest.java +++ /dev/null @@ -1,299 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.config.server.session; - -import com.yahoo.cloud.config.ConfigserverConfig; -import com.yahoo.component.Version; -import com.yahoo.config.application.api.ApplicationPackage; -import com.yahoo.config.model.NullConfigModelRegistry; -import com.yahoo.config.model.api.Model; -import com.yahoo.config.model.api.ModelContext; -import com.yahoo.config.model.api.ModelCreateResult; -import com.yahoo.config.model.api.ModelFactory; -import com.yahoo.config.model.api.ValidationParameters; -import com.yahoo.config.model.deploy.DeployState; -import com.yahoo.config.model.test.MockApplicationPackage; -import com.yahoo.config.provision.ApplicationId; -import com.yahoo.config.provision.TenantName; -import com.yahoo.vespa.config.server.TestComponentRegistry; -import com.yahoo.vespa.config.server.application.ApplicationSet; -import com.yahoo.vespa.config.server.application.PermanentApplicationPackage; -import com.yahoo.vespa.config.server.modelfactory.ModelFactoryRegistry; -import com.yahoo.vespa.curator.Curator; -import com.yahoo.vespa.curator.mock.MockCurator; -import com.yahoo.vespa.model.VespaModel; -import com.yahoo.vespa.model.VespaModelFactory; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; - -import java.io.IOException; -import java.time.Clock; -import java.time.Instant; -import java.time.LocalDate; -import java.time.ZoneOffset; -import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Optional; - -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; - -/** - * @author Ulf Lilleengen - * @author bratseth - */ -public class RemoteSessionTest { - - private static final TenantName tenantName = TenantName.from("default"); - - private Curator curator; - - @Rule - public TemporaryFolder temporaryFolder = new TemporaryFolder(); - - @Before - public void setupTest() { - curator = new MockCurator(); - } - - @Test - public void require_that_session_is_initialized() { - Clock clock = Clock.systemUTC(); - Session session = createSession(2, clock); - assertThat(session.getSessionId(), is(2L)); - session = createSession(Long.MAX_VALUE, clock); - assertThat(session.getSessionId(), is(Long.MAX_VALUE)); - } - - @Test - public void require_that_applications_are_loaded() { - RemoteSession session = createSession(3, Arrays.asList(new MockModelFactory(), new VespaModelFactory(new NullConfigModelRegistry()))); - session.prepare(); - ApplicationSet applicationSet = session.ensureApplicationLoaded(); - assertNotNull(applicationSet); - assertThat(applicationSet.getApplicationGeneration(), is(3L)); - assertThat(applicationSet.getForVersionOrLatest(Optional.empty(), Instant.now()).getId().application().value(), is("foo")); - assertNotNull(applicationSet.getForVersionOrLatest(Optional.empty(), Instant.now()).getModel()); - session.deactivate(); - - applicationSet = session.ensureApplicationLoaded(); - assertNotNull(applicationSet); - assertThat(applicationSet.getApplicationGeneration(), is(3L)); - assertThat(applicationSet.getForVersionOrLatest(Optional.empty(), Instant.now()).getId().application().value(), is("foo")); - assertNotNull(applicationSet.getForVersionOrLatest(Optional.empty(), Instant.now()).getModel()); - } - - @Test(expected = IllegalArgumentException.class) - public void require_that_new_invalid_application_throws_exception() { - MockModelFactory failingFactory = new MockModelFactory(); - failingFactory.vespaVersion = new Version(1, 2, 0); - failingFactory.throwOnLoad = true; - - MockModelFactory okFactory = new MockModelFactory(); - okFactory.vespaVersion = new Version(1, 1, 0); - okFactory.throwOnLoad = false; - - RemoteSession session = createSession(3, Arrays.asList(okFactory, failingFactory)); - session.prepare(); - } - - @Test - public void require_that_old_invalid_application_does_not_throw_exception_if_skipped_also_across_major_versions() { - MockModelFactory failingFactory = new MockModelFactory(); - failingFactory.vespaVersion = new Version(1, 0, 0); - failingFactory.throwOnLoad = true; - - MockModelFactory okFactory = - new MockModelFactory("<validation-overrides><allow until='2000-01-30'>skip-old-config-models</allow></validation-overrides>"); - okFactory.vespaVersion = new Version(2, 0, 0); - okFactory.throwOnLoad = false; - - RemoteSession session = createSession(3, Arrays.asList(okFactory, failingFactory), failingFactory.clock()); - session.prepare(); - } - - @Test - public void require_that_an_application_package_can_limit_to_one_major_version() { - ApplicationPackage application = - new MockApplicationPackage.Builder().withServices("<services version='1.0'/>") - .withDeploymentSpec("<deployment version='1.0' major-version='2'/>") - .build(); - assertTrue(application.getMajorVersion().isPresent()); - assertEquals(2, (int)application.getMajorVersion().get()); - - MockModelFactory failingFactory = new MockModelFactory(); - failingFactory.vespaVersion = new Version(3, 0, 0); - failingFactory.throwErrorOnLoad = true; - - MockModelFactory okFactory = new MockModelFactory(); - okFactory.vespaVersion = new Version(2, 0, 0); - okFactory.throwErrorOnLoad = false; - - SessionZooKeeperClient zkc = new MockSessionZKClient(curator, tenantName, 3, application); - RemoteSession session = createSession(3, zkc, Arrays.asList(okFactory, failingFactory)); - session.prepare(); - - // Does not cause an error because model version 3 is skipped - } - - @Test - public void require_that_an_application_package_can_limit_to_one_higher_major_version() { - ApplicationPackage application = - new MockApplicationPackage.Builder().withServices("<services version='1.0'/>") - .withDeploymentSpec("<deployment version='1.0' major-version='3'/>") - .build(); - assertTrue(application.getMajorVersion().isPresent()); - assertEquals(3, (int)application.getMajorVersion().get()); - - MockModelFactory failingFactory = new MockModelFactory(); - failingFactory.vespaVersion = new Version(4, 0, 0); - failingFactory.throwErrorOnLoad = true; - - MockModelFactory okFactory = new MockModelFactory(); - okFactory.vespaVersion = new Version(2, 0, 0); - okFactory.throwErrorOnLoad = false; - - SessionZooKeeperClient zkc = new MockSessionZKClient(curator, tenantName, 3, application); - RemoteSession session = createSession(4, zkc, Arrays.asList(okFactory, failingFactory)); - session.prepare(); - - // Does not cause an error because model version 4 is skipped - } - - @Test - public void require_that_session_status_is_updated() { - SessionZooKeeperClient zkc = new MockSessionZKClient(curator, tenantName, 3); - RemoteSession session = createSession(3, zkc, Clock.systemUTC()); - assertThat(session.getStatus(), is(Session.Status.NEW)); - zkc.writeStatus(Session.Status.PREPARE); - assertThat(session.getStatus(), is(Session.Status.PREPARE)); - } - - @Test - public void require_that_permanent_app_is_used() throws IOException { - Optional<PermanentApplicationPackage> permanentApp = Optional.of(new PermanentApplicationPackage( - new ConfigserverConfig(new ConfigserverConfig.Builder() - .applicationDirectory(temporaryFolder.newFolder("appdir").getAbsolutePath())))); - MockModelFactory mockModelFactory = new MockModelFactory(); - try { - int sessionId = 3; - SessionZooKeeperClient zkc = new MockSessionZKClient(curator, tenantName, sessionId); - createSession(sessionId, zkc, Collections.singletonList(mockModelFactory), permanentApp, Clock.systemUTC()).ensureApplicationLoaded(); - } catch (Exception e) { - e.printStackTrace(); - // ignore, we're not interested in deploy errors as long as the below state is OK. - } - assertNotNull(mockModelFactory.modelContext); - assertTrue(mockModelFactory.modelContext.permanentApplicationPackage().isPresent()); - } - - private RemoteSession createSession(long sessionId, Clock clock) { - return createSession(sessionId, Collections.singletonList(new VespaModelFactory(new NullConfigModelRegistry())), clock); - } - - private RemoteSession createSession(long sessionId, SessionZooKeeperClient zkc, Clock clock) { - return createSession(sessionId, zkc, Collections.singletonList(new VespaModelFactory(new NullConfigModelRegistry())), clock); - } - - private RemoteSession createSession(long sessionId, List<ModelFactory> modelFactories) { - SessionZooKeeperClient zkc = new MockSessionZKClient(curator, tenantName, sessionId); - return createSession(sessionId, zkc, modelFactories, Clock.systemUTC()); - } - - private RemoteSession createSession(long sessionId, List<ModelFactory> modelFactories, Clock clock) { - SessionZooKeeperClient zkc = new MockSessionZKClient(curator, tenantName, sessionId); - return createSession(sessionId, zkc, modelFactories, clock); - } - - private RemoteSession createSession(long sessionId, SessionZooKeeperClient zkc, List<ModelFactory> modelFactories) { - return createSession(sessionId, zkc, modelFactories, Optional.empty(), Clock.systemUTC()); - } - - private RemoteSession createSession(long sessionId, SessionZooKeeperClient zkc, List<ModelFactory> modelFactories, Clock clock) { - return createSession(sessionId, zkc, modelFactories, Optional.empty(), clock); - } - - private RemoteSession createSession(long sessionId, SessionZooKeeperClient zkc, - List<ModelFactory> modelFactories, - Optional<PermanentApplicationPackage> permanentApplicationPackage, - Clock clock) { - zkc.writeStatus(Session.Status.NEW); - zkc.writeApplicationId(new ApplicationId.Builder().applicationName("foo").instanceName("bim").build()); - TestComponentRegistry.Builder registryBuilder = new TestComponentRegistry.Builder() - .curator(curator) - .clock(clock) - .modelFactoryRegistry(new ModelFactoryRegistry(modelFactories)); - permanentApplicationPackage.ifPresent(registryBuilder::permanentApplicationPackage); - - return new RemoteSession(tenantName, sessionId, registryBuilder.build(), zkc); - } - - private static class MockModelFactory implements ModelFactory { - - /** Throw a RuntimeException on load - this is handled gracefully during model building */ - boolean throwOnLoad = false; - - /** Throw an Error on load - this is useful to propagate this condition all the way to the test */ - boolean throwErrorOnLoad = false; - - ModelContext modelContext; - public Version vespaVersion = new Version(1, 2, 3); - - /** The validation overrides of this, or null if none */ - private final String validationOverrides; - - private final Clock clock = Clock.fixed(LocalDate.parse("2000-01-01", DateTimeFormatter.ISO_DATE).atStartOfDay().atZone(ZoneOffset.UTC).toInstant(), ZoneOffset.UTC); - - MockModelFactory() { this(null); } - - MockModelFactory(String validationOverrides) { - this.validationOverrides = validationOverrides; - } - - @Override - public Version version() { - return vespaVersion; - } - - /** Returns the clock used by this, which is fixed at the instant 2000-01-01T00:00:00 */ - public Clock clock() { return clock; } - - @Override - public Model createModel(ModelContext modelContext) { - if (throwErrorOnLoad) - throw new Error("Foo"); - if (throwOnLoad) - throw new IllegalArgumentException("Foo"); - this.modelContext = modelContext; - return loadModel(); - } - - Model loadModel() { - try { - ApplicationPackage application = new MockApplicationPackage.Builder().withEmptyHosts().withEmptyServices().withValidationOverrides(validationOverrides).build(); - DeployState deployState = new DeployState.Builder().applicationPackage(application).now(clock.instant()).build(); - return new VespaModel(deployState); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - @Override - public ModelCreateResult createAndValidateModel(ModelContext modelContext, ValidationParameters validationParameters) { - if (throwErrorOnLoad) - throw new Error("Foo"); - if (throwOnLoad) - throw new IllegalArgumentException("Foo"); - this.modelContext = modelContext; - return new ModelCreateResult(loadModel(), new ArrayList<>()); - } - } - -} diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionRepositoryTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionRepositoryTest.java index b89b63aed46..aeff4029440 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionRepositoryTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionRepositoryTest.java @@ -1,15 +1,27 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.config.server.session; import com.yahoo.cloud.config.ConfigserverConfig; +import com.yahoo.component.Version; +import com.yahoo.config.application.api.ApplicationPackage; +import com.yahoo.config.model.api.Model; +import com.yahoo.config.model.api.ModelContext; +import com.yahoo.config.model.api.ModelCreateResult; +import com.yahoo.config.model.api.ModelFactory; +import com.yahoo.config.model.api.ValidationParameters; +import com.yahoo.config.model.deploy.DeployState; +import com.yahoo.config.model.test.MockApplicationPackage; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.TenantName; import com.yahoo.text.Utf8; import com.yahoo.vespa.config.server.ApplicationRepository; import com.yahoo.vespa.config.server.GlobalComponentRegistry; import com.yahoo.vespa.config.server.TestComponentRegistry; +import com.yahoo.vespa.config.server.application.ApplicationSet; import com.yahoo.vespa.config.server.application.OrchestratorMock; +import com.yahoo.vespa.config.server.http.InvalidApplicationException; import com.yahoo.vespa.config.server.http.SessionHandlerTest; +import com.yahoo.vespa.config.server.modelfactory.ModelFactoryRegistry; import com.yahoo.vespa.config.server.tenant.TenantRepository; import com.yahoo.vespa.config.server.zookeeper.ConfigCurator; import com.yahoo.vespa.config.util.ConfigUtils; @@ -17,16 +29,25 @@ import com.yahoo.vespa.curator.Curator; import com.yahoo.vespa.curator.mock.MockCurator; import com.yahoo.vespa.flags.FlagSource; import com.yahoo.vespa.flags.InMemoryFlagSource; +import com.yahoo.vespa.model.VespaModel; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import java.io.File; +import java.time.Clock; import java.time.Duration; import java.time.Instant; +import java.time.LocalDate; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; import java.util.function.LongPredicate; import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; @@ -49,13 +70,17 @@ public class SessionRepositoryTest { public TemporaryFolder temporaryFolder = new TemporaryFolder(); public void setup() throws Exception { - setup(new InMemoryFlagSource()); + setup(new InMemoryFlagSource()); } private void setup(FlagSource flagSource) throws Exception { + setup(flagSource, new TestComponentRegistry.Builder()); + } + + private void setup(FlagSource flagSource, TestComponentRegistry.Builder componentRegistryBuilder) throws Exception { curator = new MockCurator(); File configserverDbDir = temporaryFolder.newFolder().getAbsoluteFile(); - GlobalComponentRegistry globalComponentRegistry = new TestComponentRegistry.Builder() + GlobalComponentRegistry globalComponentRegistry = componentRegistryBuilder .curator(curator) .configServerConfig(new ConfigserverConfig.Builder() .configServerDBDir(configserverDbDir.getAbsolutePath()) @@ -84,6 +109,12 @@ public class SessionRepositoryTest { assertNotNull(sessionRepository.getLocalSession(secondSessionId)); assertNull(sessionRepository.getLocalSession(secondSessionId + 1)); + ApplicationSet applicationSet = sessionRepository.ensureApplicationLoaded(sessionRepository.getRemoteSession(firstSessionId)); + assertNotNull(applicationSet); + assertEquals(2, applicationSet.getApplicationGeneration()); + assertEquals(applicationId.application(), applicationSet.getForVersionOrLatest(Optional.empty(), Instant.now()).getId().application()); + assertNotNull(applicationSet.getForVersionOrLatest(Optional.empty(), Instant.now()).getModel()); + sessionRepository.close(); // All created sessions are deleted assertNull(sessionRepository.getLocalSession(firstSessionId)); @@ -127,8 +158,7 @@ public class SessionRepositoryTest { assertStatusChange(sessionId, Session.Status.PREPARE); assertStatusChange(sessionId, Session.Status.ACTIVATE); - com.yahoo.path.Path session = TenantRepository.getSessionsPath(tenantName).append("" + sessionId); - curator.delete(session); + sessionRepository.delete(sessionRepository.getRemoteSession(sessionId)); assertSessionRemoved(sessionId); assertNull(sessionRepository.getRemoteSession(sessionId)); } @@ -150,6 +180,81 @@ public class SessionRepositoryTest { assertThat(sessionRepository.getRemoteSessions().size(), is(1)); } + @Test(expected = InvalidApplicationException.class) + public void require_that_new_invalid_application_throws_exception() throws Exception { + MockModelFactory failingFactory = new MockModelFactory(); + failingFactory.vespaVersion = new Version(1, 2, 0); + failingFactory.throwOnLoad = true; + + MockModelFactory okFactory = new MockModelFactory(); + okFactory.vespaVersion = new Version(1, 1, 0); + okFactory.throwOnLoad = false; + + TestComponentRegistry.Builder registryBuilder = new TestComponentRegistry.Builder() + .modelFactoryRegistry(new ModelFactoryRegistry(List.of(okFactory, failingFactory))); + setup(new InMemoryFlagSource(), registryBuilder); + + deploy(); + } + + @Test + public void require_that_old_invalid_application_does_not_throw_exception_if_skipped_also_across_major_versions() throws Exception { + MockModelFactory failingFactory = new MockModelFactory(); + failingFactory.vespaVersion = new Version(1, 0, 0); + failingFactory.throwOnLoad = true; + + MockModelFactory okFactory = + new MockModelFactory("<validation-overrides><allow until='2000-01-30'>skip-old-config-models</allow></validation-overrides>"); + okFactory.vespaVersion = new Version(2, 0, 0); + okFactory.throwOnLoad = false; + + TestComponentRegistry.Builder registryBuilder = new TestComponentRegistry.Builder() + .modelFactoryRegistry(new ModelFactoryRegistry(List.of(okFactory, failingFactory))); + setup(new InMemoryFlagSource(), registryBuilder); + + deploy(); + } + + @Test + public void require_that_an_application_package_can_limit_to_one_major_version() throws Exception { + MockModelFactory failingFactory = new MockModelFactory(); + failingFactory.vespaVersion = new Version(3, 0, 0); + failingFactory.throwErrorOnLoad = true; + + MockModelFactory okFactory = new MockModelFactory(); + okFactory.vespaVersion = new Version(2, 0, 0); + okFactory.throwErrorOnLoad = false; + + TestComponentRegistry.Builder registryBuilder = new TestComponentRegistry.Builder() + .modelFactoryRegistry(new ModelFactoryRegistry(List.of(okFactory, failingFactory))); + setup(new InMemoryFlagSource(), registryBuilder); + + File testApp = new File("src/test/apps/app-major-version-2"); + deploy(applicationId, testApp); + + // Does not cause an error because model version 3 is skipped + } + + @Test + public void require_that_an_application_package_can_limit_to_one_higher_major_version() throws Exception { + MockModelFactory failingFactory = new MockModelFactory(); + failingFactory.vespaVersion = new Version(3, 0, 0); + failingFactory.throwErrorOnLoad = true; + + MockModelFactory okFactory = new MockModelFactory(); + okFactory.vespaVersion = new Version(1, 0, 0); + okFactory.throwErrorOnLoad = false; + + TestComponentRegistry.Builder registryBuilder = new TestComponentRegistry.Builder() + .modelFactoryRegistry(new ModelFactoryRegistry(List.of(okFactory, failingFactory))); + setup(new InMemoryFlagSource(), registryBuilder); + + File testApp = new File("src/test/apps/app-major-version-2"); + deploy(applicationId, testApp); + + // Does not cause an error because model version 3 is skipped + } + private void createSession(long sessionId, boolean wait) { SessionZooKeeperClient zkc = new SessionZooKeeperClient(curator, ConfigCurator.create(curator), @@ -165,6 +270,7 @@ public class SessionRepositoryTest { private void assertStatusChange(long sessionId, Session.Status status) throws Exception { com.yahoo.path.Path statePath = sessionRepository.getSessionStatePath(sessionId); + System.out.println("Setting and asserting state for " + statePath); curator.create(statePath); curator.framework().setData().forPath(statePath.getAbsolute(), Utf8.toBytes(status.toString())); assertRemoteSessionStatus(sessionId, status); @@ -187,7 +293,7 @@ public class SessionRepositoryTest { } private void waitFor(LongPredicate predicate, long sessionId) { - long endTime = System.currentTimeMillis() + 60_000; + long endTime = System.currentTimeMillis() + 5_000; boolean ok; do { ok = predicate.test(sessionId); @@ -204,8 +310,73 @@ public class SessionRepositoryTest { } private long deploy(ApplicationId applicationId) { + return deploy(applicationId, testApp); + } + + private long deploy(ApplicationId applicationId, File testApp) { applicationRepository.deploy(testApp, new PrepareParams.Builder().applicationId(applicationId).build()); return applicationRepository.getActiveSession(applicationId).getSessionId(); } + private static class MockModelFactory implements ModelFactory { + + /** Throw a RuntimeException on load - this is handled gracefully during model building */ + boolean throwOnLoad = false; + + /** Throw an Error on load - this is useful to propagate this condition all the way to the test */ + boolean throwErrorOnLoad = false; + + ModelContext modelContext; + public Version vespaVersion = new Version(1, 2, 3); + + /** The validation overrides of this, or null if none */ + private final String validationOverrides; + + private final Clock clock = Clock.fixed(LocalDate.parse("2000-01-01", DateTimeFormatter.ISO_DATE).atStartOfDay().atZone(ZoneOffset.UTC).toInstant(), ZoneOffset.UTC); + + MockModelFactory() { this(null); } + + MockModelFactory(String validationOverrides) { + this.validationOverrides = validationOverrides; + } + + @Override + public Version version() { + return vespaVersion; + } + + /** Returns the clock used by this, which is fixed at the instant 2000-01-01T00:00:00 */ + public Clock clock() { return clock; } + + @Override + public Model createModel(ModelContext modelContext) { + if (throwErrorOnLoad) + throw new Error("error on load"); + if (throwOnLoad) + throw new IllegalArgumentException("exception on load"); + this.modelContext = modelContext; + return loadModel(); + } + + Model loadModel() { + try { + ApplicationPackage application = new MockApplicationPackage.Builder().withEmptyHosts().withEmptyServices().withValidationOverrides(validationOverrides).build(); + DeployState deployState = new DeployState.Builder().applicationPackage(application).now(clock.instant()).build(); + return new VespaModel(deployState); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public ModelCreateResult createAndValidateModel(ModelContext modelContext, ValidationParameters validationParameters) { + if (throwErrorOnLoad) + throw new Error("error on load"); + if (throwOnLoad) + throw new IllegalArgumentException("exception on load"); + this.modelContext = modelContext; + return new ModelCreateResult(loadModel(), new ArrayList<>()); + } + } + } |