diff options
8 files changed, 124 insertions, 85 deletions
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/VespaModelFactory.java b/config-model/src/main/java/com/yahoo/vespa/model/VespaModelFactory.java index 0dccc2c9750..b3bcab01ed8 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/VespaModelFactory.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/VespaModelFactory.java @@ -30,6 +30,7 @@ import org.xml.sax.SAXException; import java.io.IOException; import java.io.Reader; +import java.time.Clock; import java.util.ArrayList; import java.util.List; import java.util.logging.Logger; @@ -44,6 +45,7 @@ public class VespaModelFactory implements ModelFactory { private static final Logger log = Logger.getLogger(VespaModelFactory.class.getName()); private final ConfigModelRegistry configModelRegistry; private final Zone zone; + private final Clock clock; @Inject public VespaModelFactory(ComponentRegistry<ConfigModelPlugin> pluginRegistry, Zone zone) { @@ -55,9 +57,13 @@ public class VespaModelFactory implements ModelFactory { } this.configModelRegistry = new MapConfigModelRegistry(modelBuilders); this.zone = zone; + this.clock = Clock.systemUTC(); } public VespaModelFactory(ConfigModelRegistry configModelRegistry) { + this(configModelRegistry, Clock.systemUTC()); + } + public VespaModelFactory(ConfigModelRegistry configModelRegistry, Clock clock) { if (configModelRegistry == null) { this.configModelRegistry = new NullConfigModelRegistry(); log.info("Will not load config models from plugins, as no registry is available"); @@ -65,6 +71,7 @@ public class VespaModelFactory implements ModelFactory { this.configModelRegistry = configModelRegistry; } this.zone = Zone.defaultZone(); + this.clock = clock; } @Override @@ -95,7 +102,8 @@ public class VespaModelFactory implements ModelFactory { .properties(createDeployProperties(modelContext.properties())) .modelHostProvisioner(createHostProvisioner(modelContext)) .rotations(modelContext.properties().rotations()) - .zone(zone); + .zone(zone) + .now(clock.instant()); modelContext.previousModel().ifPresent(builder::previousModel); return builder.build(); } diff --git a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/ValidationOverrideTest.java b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/ValidationOverrideTest.java index 3354e9320fc..57e3cfa9dfa 100644 --- a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/ValidationOverrideTest.java +++ b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/ValidationOverrideTest.java @@ -1,6 +1,7 @@ // Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.model.application.validation; +import com.yahoo.test.ManualClock; import com.yahoo.vespa.model.application.validation.xml.ValidationOverridesXMLReader; import org.junit.Test; import org.xml.sax.SAXException; @@ -32,7 +33,7 @@ public class ValidationOverrideTest { { ValidationOverrides overrides = new ValidationOverridesXMLReader().read(Optional.of(new StringReader(validationOverrides)), - at("2000-01-01T23:59:00")); + ManualClock.at("2000-01-01T23:59:00")); assertOverridden("indexing-change", overrides); assertOverridden("indexing-mode-change", overrides); assertNotOverridden("field-type-change", overrides); @@ -40,7 +41,7 @@ public class ValidationOverrideTest { { ValidationOverrides overrides = new ValidationOverridesXMLReader().read(Optional.of(new StringReader(validationOverrides)), - at("2000-01-02T00:00:00")); + ManualClock.at("2000-01-02T00:00:00")); assertNotOverridden("indexing-change", overrides); assertOverridden("indexing-mode-change", overrides); assertNotOverridden("field-type-change", overrides); @@ -48,7 +49,7 @@ public class ValidationOverrideTest { { ValidationOverrides overrides = new ValidationOverridesXMLReader().read(Optional.of(new StringReader(validationOverrides)), - at("2000-01-04T00:00:00")); + ManualClock.at("2000-01-04T00:00:00")); assertNotOverridden("indexing-change", overrides); assertNotOverridden("indexing-mode-change", overrides); assertNotOverridden("field-type-change", overrides); @@ -65,7 +66,7 @@ public class ValidationOverrideTest { try { new ValidationOverridesXMLReader().read(Optional.of(new StringReader(validationOverrides)), - at("2000-01-01T23:59:00")); + ManualClock.at("2000-01-01T23:59:00")); fail("Expected validation interval override validation validation failure"); } catch (IllegalArgumentException e) { @@ -75,10 +76,6 @@ public class ValidationOverrideTest { } } - private Instant at(String utcIsoTime) { - return LocalDateTime.parse(utcIsoTime, DateTimeFormatter.ISO_DATE_TIME).atZone(ZoneOffset.UTC).toInstant(); - } - private void assertOverridden(String validationId, ValidationOverrides overrides) { overrides.invalid(ValidationId.from(validationId).get(), "message"); // should not throw exception } diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/Deployment.java b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/Deployment.java index 925b442ea3a..3956e5d9a8f 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/Deployment.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/Deployment.java @@ -173,12 +173,13 @@ public class Deployment implements com.yahoo.config.provision.Deployment { private void checkIfActiveHasChanged(LocalSession session, LocalSession currentActiveSession, boolean ignoreStaleSessionFailure) { long activeSessionAtCreate = session.getActiveSessionAtCreate(); - log.log(LogLevel.DEBUG, currentActiveSession.logPre()+"active session id at create time=" + activeSessionAtCreate); + log.log(LogLevel.DEBUG, currentActiveSession.logPre() + "active session id at create time=" + activeSessionAtCreate); if (activeSessionAtCreate == 0) return; // No active session at create long sessionId = session.getSessionId(); long currentActiveSessionSessionId = currentActiveSession.getSessionId(); - log.log(LogLevel.DEBUG, currentActiveSession.logPre()+"sessionId=" + sessionId + ", current active session=" + currentActiveSessionSessionId); + log.log(LogLevel.DEBUG, currentActiveSession.logPre() + "sessionId=" + sessionId + + ", current active session=" + currentActiveSessionSessionId); if (currentActiveSession.isNewerThan(activeSessionAtCreate) && currentActiveSessionSessionId != sessionId) { String errMsg = currentActiveSession.logPre()+"Cannot activate session " + @@ -186,7 +187,7 @@ public class Deployment implements com.yahoo.config.provision.Deployment { currentActiveSessionSessionId + ") has changed since session " + sessionId + " was created (was " + activeSessionAtCreate + " at creation time)"; if (ignoreStaleSessionFailure) { - log.warning(errMsg+ " (Continuing because of force.)"); + log.warning(errMsg + " (Continuing because of force.)"); } else { throw new IllegalStateException(errMsg); } @@ -198,7 +199,8 @@ public class Deployment implements com.yahoo.config.provision.Deployment { private void checkIfActiveIsNewerThanSessionToBeActivated(long sessionId, long currentActiveSessionId) { if (sessionId < currentActiveSessionId) { throw new IllegalArgumentException("It is not possible to activate session " + sessionId + - ", because it is older than current active session (" + currentActiveSessionId + ")"); + ", because it is older than current active session (" + + currentActiveSessionId + ")"); } } } diff --git a/configserver/src/test/apps/validationOverride/validation-overrides.xml b/configserver/src/test/apps/validationOverride/validation-overrides.xml index 28e25cd793d..1f209d21c94 100644 --- a/configserver/src/test/apps/validationOverride/validation-overrides.xml +++ b/configserver/src/test/apps/validationOverride/validation-overrides.xml @@ -1,3 +1,3 @@ <validation-overrides> - <allow until="2016-09-01">skip-old-config-models</allow> + <allow until="2016-10-10">skip-old-config-models</allow> </validation-overrides>
\ No newline at end of file diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/DeployTester.java b/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/DeployTester.java index 21196a1f57b..be31b5c1fc9 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/DeployTester.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/DeployTester.java @@ -1,10 +1,16 @@ package com.yahoo.vespa.config.server.deploy; import com.yahoo.cloud.config.ConfigserverConfig; +import com.yahoo.config.application.api.ApplicationPackage; import com.yahoo.config.model.NullConfigModelRegistry; import com.yahoo.config.model.api.HostProvisioner; +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.deploy.DeployState; import com.yahoo.config.model.provision.InMemoryProvisioner; +import com.yahoo.config.model.test.MockApplicationPackage; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.ApplicationName; import com.yahoo.config.provision.Capacity; @@ -14,6 +20,7 @@ import com.yahoo.config.provision.HostSpec; import com.yahoo.config.provision.InstanceName; import com.yahoo.config.provision.ProvisionLogger; import com.yahoo.config.provision.Provisioner; +import com.yahoo.config.provision.Version; import com.yahoo.path.Path; import com.yahoo.transaction.NestedTransaction; import com.yahoo.vespa.config.server.ApplicationRepository; @@ -30,6 +37,7 @@ import com.yahoo.vespa.config.server.tenant.Tenants; import com.yahoo.vespa.config.server.zookeeper.ConfigCurator; 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.apache.curator.framework.CuratorFramework; import org.junit.Before; @@ -38,6 +46,10 @@ import java.io.File; import java.io.IOException; 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.Collection; import java.util.Collections; import java.util.List; @@ -56,7 +68,7 @@ public class DeployTester { private ApplicationId id; public DeployTester(String appPath) { - this(appPath, Collections.singletonList(createDefaultModelFactory())); + this(appPath, Collections.singletonList(createDefaultModelFactory(Clock.systemUTC()))); } public DeployTester(String appPath, List<ModelFactory> modelFactories) { @@ -73,7 +85,11 @@ public class DeployTester { public Tenant tenant() { return tenants.defaultTenant(); } - public static ModelFactory createDefaultModelFactory() { return new VespaModelFactory(new NullConfigModelRegistry()); } + /** Create the model factory which will be used in production */ + public static ModelFactory createDefaultModelFactory(Clock clock) { return new VespaModelFactory(new NullConfigModelRegistry(), clock); } + + /** Create a model factory which always fails validation */ + public static ModelFactory createFailingModelFactory(Version version) { return new FailingModelFactory(version); } /** * Do the initial "deploy" with the existing API-less code as the deploy API doesn't support first deploys yet. @@ -136,4 +152,34 @@ public class DeployTester { } + private static class FailingModelFactory implements ModelFactory { + + private final Version version; + + public FailingModelFactory(Version version) { + this.version = version; + } + + @Override + public Version getVersion() { return version; } + + @Override + public Model createModel(ModelContext modelContext) { + try { + Instant now = LocalDate.parse("2000-01-01", DateTimeFormatter.ISO_DATE).atStartOfDay().atZone(ZoneOffset.UTC).toInstant(); + ApplicationPackage application = new MockApplicationPackage.Builder().withEmptyHosts().withEmptyServices().build(); + DeployState deployState = new DeployState.Builder().applicationPackage(application).now(now).build(); + return new VespaModel(deployState); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public ModelCreateResult createAndValidateModel(ModelContext modelContext, boolean ignoreValidationErrors) { + throw new IllegalArgumentException("Validation fails"); + } + + } + } diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/HostedDeployTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/HostedDeployTest.java index 585b988bffc..4f7febd54cc 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/HostedDeployTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/HostedDeployTest.java @@ -1,48 +1,19 @@ // Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.config.server.deploy; -import com.yahoo.cloud.config.ConfigserverConfig; -import com.yahoo.config.model.NullConfigModelRegistry; -import com.yahoo.config.model.api.HostProvisioner; -import com.yahoo.config.model.provision.InMemoryProvisioner; -import com.yahoo.config.provision.ApplicationId; -import com.yahoo.config.provision.ApplicationName; -import com.yahoo.config.provision.Capacity; -import com.yahoo.config.provision.ClusterSpec; -import com.yahoo.config.provision.HostFilter; -import com.yahoo.config.provision.HostSpec; -import com.yahoo.config.provision.InstanceName; -import com.yahoo.config.provision.ProvisionLogger; -import com.yahoo.config.provision.Provisioner; -import com.yahoo.path.Path; -import com.yahoo.transaction.NestedTransaction; -import com.yahoo.vespa.config.server.ApplicationRepository; -import com.yahoo.vespa.config.server.TestComponentRegistry; -import com.yahoo.vespa.config.server.TestWithCurator; -import com.yahoo.vespa.config.server.modelfactory.ModelFactoryRegistry; -import com.yahoo.vespa.config.server.monitoring.Metrics; -import com.yahoo.vespa.config.server.tenant.Tenant; -import com.yahoo.vespa.config.server.tenant.Tenants; -import com.yahoo.vespa.config.server.tenant.TestWithTenant; -import com.yahoo.vespa.config.server.TimeoutBudget; -import com.yahoo.vespa.config.server.provision.HostProvisionerProvider; -import com.yahoo.vespa.config.server.session.LocalSession; -import com.yahoo.vespa.config.server.session.PrepareParams; -import com.yahoo.vespa.config.server.session.SilentDeployLogger; -import com.yahoo.vespa.model.VespaModelFactory; -import org.junit.Before; +import com.yahoo.config.model.api.ModelFactory; +import com.yahoo.config.provision.Version; +import com.yahoo.test.ManualClock; import org.junit.Test; -import java.io.File; import java.io.IOException; -import java.time.Clock; import java.time.Duration; -import java.util.Collection; -import java.util.Collections; +import java.util.ArrayList; import java.util.List; import java.util.Optional; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; /** * @author bratseth @@ -53,8 +24,8 @@ public class HostedDeployTest { public void testRedeploy() throws InterruptedException, IOException { DeployTester tester = new DeployTester("src/test/apps/hosted/"); tester.deployApp("myApp"); - Optional<com.yahoo.config.provision.Deployment> deployment = tester.redeployFromLocalActive(); - + + Optional<com.yahoo.config.provision.Deployment> deployment = tester.redeployFromLocalActive(); assertTrue(deployment.isPresent()); deployment.get().prepare(); deployment.get().activate(); @@ -62,13 +33,42 @@ public class HostedDeployTest { @Test public void testRedeployAfterExpiredValidationOverride() throws InterruptedException, IOException { - DeployTester tester = new DeployTester("src/test/apps/validationOverride/"); + // Old version of model fails, but application disables loading old models until 2016-10-10, so deployment works + ManualClock clock = new ManualClock("2016-10-09T00:00:00"); + List<ModelFactory> modelFactories = new ArrayList<>(); + modelFactories.add(DeployTester.createDefaultModelFactory(clock)); + modelFactories.add(DeployTester.createFailingModelFactory(Version.fromIntValues(1, 0, 0))); // older than default + DeployTester tester = new DeployTester("src/test/apps/validationOverride/", modelFactories); tester.deployApp("myApp"); - Optional<com.yahoo.config.provision.Deployment> deployment = tester.redeployFromLocalActive(); - assertTrue(deployment.isPresent()); - deployment.get().prepare(); - deployment.get().activate(); + // Redeployment from local active works + { + Optional<com.yahoo.config.provision.Deployment> deployment = tester.redeployFromLocalActive(); + assertTrue(deployment.isPresent()); + deployment.get().prepare(); + deployment.get().activate(); + } + + clock.advance(Duration.ofDays(2)); // validation override expires + + // Redeployment from local active also works after the validation override expires + { + Optional<com.yahoo.config.provision.Deployment> deployment = tester.redeployFromLocalActive(); + assertTrue(deployment.isPresent()); + deployment.get().prepare(); + deployment.get().activate(); + } + + // However, redeployment from the outside fails after this date + { + try { + tester.deployApp("myApp"); + fail("Expected redeployment to fail"); + } + catch (IllegalArgumentException expected) { + // success + } + } } } diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/RedeployTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/RedeployTest.java index 5a2f13c34ee..ebfcca5c6a7 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/RedeployTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/deploy/RedeployTest.java @@ -16,6 +16,7 @@ import com.yahoo.config.provision.InstanceName; import com.yahoo.config.provision.TenantName; import com.yahoo.config.provision.Version; import com.yahoo.path.Path; +import com.yahoo.test.ManualClock; import com.yahoo.vespa.config.server.ApplicationRepository; import com.yahoo.vespa.config.server.TestComponentRegistry; import com.yahoo.vespa.config.server.TestWithCurator; @@ -78,8 +79,8 @@ public class RedeployTest { @Test public void testNoRedeploy() { List<ModelFactory> modelFactories = new ArrayList<>(); - modelFactories.add(DeployTester.createDefaultModelFactory()); - modelFactories.add(new OldNonWorkingModelFactory()); + modelFactories.add(DeployTester.createDefaultModelFactory(Clock.systemUTC())); + modelFactories.add(DeployTester.createFailingModelFactory(Version.fromIntValues(1, 0, 0))); DeployTester tester = new DeployTester("ignored/app/path", modelFactories); ApplicationId id = ApplicationId.from(TenantName.from("default"), ApplicationName.from("default"), @@ -87,30 +88,4 @@ public class RedeployTest { assertFalse(tester.redeployFromLocalActive(id).isPresent()); } - private static class OldNonWorkingModelFactory implements ModelFactory { - - @Override - public Version getVersion() { - return Version.fromIntValues(1, 0, 0); - } - - @Override - public Model createModel(ModelContext modelContext) { - try { - Instant now = LocalDate.parse("2000-01-01", DateTimeFormatter.ISO_DATE).atStartOfDay().atZone(ZoneOffset.UTC).toInstant(); - ApplicationPackage application = new MockApplicationPackage.Builder().withEmptyHosts().withEmptyServices().build(); - DeployState deployState = new DeployState.Builder().applicationPackage(application).now(now).build(); - return new VespaModel(deployState); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - @Override - public ModelCreateResult createAndValidateModel(ModelContext modelContext, boolean ignoreValidationErrors) { - throw new IllegalArgumentException("Validation fails"); - } - - } - } diff --git a/testutil/src/main/java/com/yahoo/test/ManualClock.java b/testutil/src/main/java/com/yahoo/test/ManualClock.java index b8325b3e3fc..1ffc7aa77da 100644 --- a/testutil/src/main/java/com/yahoo/test/ManualClock.java +++ b/testutil/src/main/java/com/yahoo/test/ManualClock.java @@ -3,7 +3,10 @@ package com.yahoo.test; import java.time.Clock; import java.time.Instant; +import java.time.LocalDateTime; import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; import java.time.temporal.TemporalAmount; /** A clock which initially has the time of its creation but can only be advanced by calling advance */ @@ -13,6 +16,10 @@ public class ManualClock extends Clock { public ManualClock() {} + public ManualClock(String utcIsoTime) { + this(at(utcIsoTime)); + } + public ManualClock(Instant currentTime) { this.currentTime = currentTime; } @@ -33,4 +40,8 @@ public class ManualClock extends Clock { @Override public long millis() { return currentTime.toEpochMilli(); } + public static Instant at(String utcIsoTime) { + return LocalDateTime.parse(utcIsoTime, DateTimeFormatter.ISO_DATE_TIME).atZone(ZoneOffset.UTC).toInstant(); + } + } |