diff options
16 files changed, 125 insertions, 43 deletions
diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/EndpointCertificateSecrets.java b/config-model-api/src/main/java/com/yahoo/config/model/api/EndpointCertificateSecrets.java index 952a0562f1d..38c947504ce 100644 --- a/config-model-api/src/main/java/com/yahoo/config/model/api/EndpointCertificateSecrets.java +++ b/config-model-api/src/main/java/com/yahoo/config/model/api/EndpointCertificateSecrets.java @@ -3,9 +3,9 @@ package com.yahoo.config.model.api; public class EndpointCertificateSecrets { public static final EndpointCertificateSecrets MISSING = new EndpointCertificateSecrets(); - private final String certificate; private final String key; + private final int version; private EndpointCertificateSecrets() { this(null, null); @@ -14,6 +14,13 @@ public class EndpointCertificateSecrets { public EndpointCertificateSecrets(String certificate, String key) { this.certificate = certificate; this.key = key; + this.version = -1; + } + + public EndpointCertificateSecrets(String certificate, String key, int version) { + this.certificate = certificate; + this.key = key; + this.version = version; } public String certificate() { @@ -24,7 +31,15 @@ public class EndpointCertificateSecrets { return key; } + public int version() { + return version; + } + + public static EndpointCertificateSecrets missing(int version) { + return new EndpointCertificateSecrets(null, null, version); + } + public boolean isMissing() { - return this == MISSING; + return this == MISSING || certificate == null || key == null; } } diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/EndpointCertificateSecretsValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/EndpointCertificateSecretsValidator.java index 338c3a71e44..21025c38c13 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/EndpointCertificateSecretsValidator.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/EndpointCertificateSecretsValidator.java @@ -1,7 +1,6 @@ // Copyright Yahoo. 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.config.model.api.EndpointCertificateSecrets; import com.yahoo.config.model.deploy.DeployState; import com.yahoo.config.provision.CertificateNotReadyException; import com.yahoo.vespa.model.VespaModel; @@ -11,9 +10,9 @@ public class EndpointCertificateSecretsValidator extends Validator { /** This check is delayed until validation to allow node provisioning to complete while we are waiting for cert */ @Override public void validate(VespaModel model, DeployState deployState) { - if (deployState.endpointCertificateSecrets().isPresent() && deployState.endpointCertificateSecrets().get() == EndpointCertificateSecrets.MISSING) { - throw new CertificateNotReadyException("TLS enabled, but could not yet retrieve certificate for application " + deployState.getProperties().applicationId().serializedForm()); + if (deployState.endpointCertificateSecrets().isPresent() && deployState.endpointCertificateSecrets().get().isMissing()) { + throw new CertificateNotReadyException("TLS enabled, but could not yet retrieve certificate version %s for application %s" + .formatted(deployState.endpointCertificateSecrets().get().version(), deployState.getProperties().applicationId().serializedForm())); } } - } diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java index 1aab98a115f..5a1103de9a3 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java @@ -744,9 +744,14 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> { private List<ApplicationContainer> createNodes(ApplicationContainerCluster cluster, Element containerElement, Element nodesElement, ConfigModelContext context) { if (nodesElement.hasAttribute("type")) // internal use for hosted system infrastructure nodes return createNodesFromNodeType(cluster, nodesElement, context); - else if (nodesElement.hasAttribute("of")) // hosted node spec referencing a content cluster - return createNodesFromContentServiceReference(cluster, nodesElement, context); - else if (nodesElement.hasAttribute("count")) // regular, hosted node spec + else if (nodesElement.hasAttribute("of")) {// hosted node spec referencing a content cluster + // TODO: Remove support for combined clusters in Vespa 9 + List<ApplicationContainer> containers = createNodesFromContentServiceReference(cluster, nodesElement, context); + log.log(WARNING, "Declaring combined cluster with <nodes of=\"...\"> is deprecated without " + + "replacement, and the feature will be removed in Vespa 9. Use separate container and " + + "content clusters instead"); + return containers; + } else if (nodesElement.hasAttribute("count")) // regular, hosted node spec return createNodesFromNodeCount(cluster, containerElement, nodesElement, context); else if (cluster.isHostedVespa() && cluster.getZone().environment().isManuallyDeployed()) // default to 1 in manual zones return createNodesFromNodeCount(cluster, containerElement, nodesElement, context); diff --git a/config-model/src/test/java/com/yahoo/config/model/provision/ModelProvisioningTest.java b/config-model/src/test/java/com/yahoo/config/model/provision/ModelProvisioningTest.java index 5e56efc4460..f5ff48c0c69 100644 --- a/config-model/src/test/java/com/yahoo/config/model/provision/ModelProvisioningTest.java +++ b/config-model/src/test/java/com/yahoo/config/model/provision/ModelProvisioningTest.java @@ -4,6 +4,7 @@ package com.yahoo.config.model.provision; import com.yahoo.cloud.config.ZookeeperServerConfig; import com.yahoo.cloud.config.log.LogdConfig; import com.yahoo.config.application.api.ApplicationPackage; +import com.yahoo.config.application.api.DeployLogger; import com.yahoo.config.model.api.container.ContainerServiceType; import com.yahoo.config.model.deploy.DeployState; import com.yahoo.config.model.deploy.TestProperties; @@ -40,11 +41,13 @@ import com.yahoo.yolean.Exceptions; import org.junit.Test; import java.io.StringReader; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.function.Function; +import java.util.logging.Level; import java.util.stream.Collectors; import static com.yahoo.config.model.test.TestUtil.joinLines; @@ -268,7 +271,8 @@ public class ModelProvisioningTest { "</services>"; VespaModelTester tester = new VespaModelTester(); tester.addHosts(5); - VespaModel model = tester.createModel(xmlWithNodes, true); + TestLogger logger = new TestLogger(); + VespaModel model = tester.createModel(xmlWithNodes, true, new DeployState.Builder().deployLogger(logger)); assertEquals("Nodes in content1", 2, model.getContentClusters().get("content1").getRootGroup().getNodes().size()); assertEquals("Nodes in container1", 2, model.getContainerClusters().get("container1").getContainers().size()); assertEquals("Heap size is lowered with combined clusters", @@ -278,6 +282,10 @@ public class ModelProvisioningTest { .get("content1"))); assertProvisioned(0, ClusterSpec.Id.from("container1"), ClusterSpec.Type.container, model); assertProvisioned(2, ClusterSpec.Id.from("content1"), ClusterSpec.Id.from("container1"), ClusterSpec.Type.combined, model); + assertEquals(1, logger.msgs().size()); + assertEquals("Declaring combined cluster with <nodes of=\"...\"> is deprecated without replacement, " + + "and the feature will be removed in Vespa 9. Use separate container and content clusters instead", + logger.msgs().get(0).message); } @Test @@ -2295,4 +2303,19 @@ public class ModelProvisioningTest { assertProvisioned(nodeCount, id, null, type, model); } + record TestLogger(List<LogMessage> msgs) implements DeployLogger { + + public TestLogger() { + this(new ArrayList<>()); + } + + @Override + public void log(Level level, String message) { + msgs.add(new LogMessage(level, message)); + } + + record LogMessage(Level level, String message) {} + + } + } diff --git a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/EndpointCertificateSecretsValidatorTest.java b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/EndpointCertificateSecretsValidatorTest.java index 51aa6cb6e42..991606a8e32 100644 --- a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/EndpointCertificateSecretsValidatorTest.java +++ b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/EndpointCertificateSecretsValidatorTest.java @@ -44,11 +44,11 @@ public class EndpointCertificateSecretsValidatorTest { @Test public void missing_certificate_fails_validation() throws Exception { - DeployState deployState = deployState(servicesXml(), deploymentXml(), Optional.of(EndpointCertificateSecrets.MISSING)); + DeployState deployState = deployState(servicesXml(), deploymentXml(), Optional.of(EndpointCertificateSecrets.missing(1))); VespaModel model = new VespaModel(new NullConfigModelRegistry(), deployState); exceptionRule.expect(CertificateNotReadyException.class); - exceptionRule.expectMessage("TLS enabled, but could not yet retrieve certificate for application default:default:default"); + exceptionRule.expectMessage("TLS enabled, but could not yet retrieve certificate version 1 for application default:default:default"); new EndpointCertificateSecretsValidator().validate(model, deployState); } diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilderTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilderTest.java index d91dba0572f..b65570d29cc 100644 --- a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilderTest.java +++ b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilderTest.java @@ -987,7 +987,6 @@ public class ContainerModelBuilderTest extends ContainerModelBuilderTestBase { createModel(root, deployState, null, DomBuilderTest.parse(containerService)); assertFalse(logger.msgs.isEmpty()); - System.out.println(logger.msgs); assertEquals(Level.WARNING, logger.msgs.get(0).getFirst()); assertEquals(Level.WARNING, logger.msgs.get(1).getFirst()); assertEquals("Element 'prod' contains attribute 'global-service-id' deprecated since major version 7. See https://cloud.vespa.ai/en/reference/routing#deprecated-syntax", diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/EndpointCertificateRetriever.java b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/EndpointCertificateRetriever.java index a3ddae8f7aa..4a1e81b9058 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/EndpointCertificateRetriever.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/EndpointCertificateRetriever.java @@ -19,13 +19,7 @@ import java.util.logging.Logger; * * @author andreer */ -public class EndpointCertificateRetriever { - - private final SecretStore secretStore; - - public EndpointCertificateRetriever(SecretStore secretStore) { - this.secretStore = secretStore; - } +public record EndpointCertificateRetriever(SecretStore secretStore) { private static final Logger log = Logger.getLogger(EndpointCertificateRetriever.class.getName()); @@ -40,11 +34,11 @@ public class EndpointCertificateRetriever { verifyKeyMatchesCertificate(endpointCertificateMetadata, cert, key); - return new EndpointCertificateSecrets(cert, key); + return new EndpointCertificateSecrets(cert, key, endpointCertificateMetadata.version()); } catch (RuntimeException e) { log.log(Level.WARNING, "Exception thrown during certificate retrieval", e); // Assume not ready yet - return EndpointCertificateSecrets.MISSING; + return EndpointCertificateSecrets.missing(endpointCertificateMetadata.version()); } } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java index e48ad7596ea..389e931fb98 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java @@ -446,7 +446,7 @@ public class ApplicationController { } /** Deploys an application package for an existing application instance. */ - public ActivateResult deploy(JobId job, boolean deploySourceVersions) { + public ActivateResult deploy(JobId job, boolean deploySourceVersions, Consumer<String> deployLogger) { if (job.application().instance().isTester()) throw new IllegalArgumentException("'" + job.application() + "' is a tester application!"); @@ -479,6 +479,7 @@ public class ApplicationController { applicationPackage = applicationPackage.withTrustedCertificate(run.testerCertificate().get()); endpointCertificateMetadata = endpointCertificates.getMetadata(instance, zone, applicationPackage.deploymentSpec()); + containerEndpoints = controller.routing().of(deployment).prepare(application); } // Release application lock while doing the deployment, which is a lengthy task. @@ -487,6 +488,8 @@ public class ApplicationController { ActivateResult result = deploy(job.application(), applicationPackage, zone, platform, containerEndpoints, endpointCertificateMetadata, run.isDryRun()); + endpointCertificateMetadata.ifPresent(e -> deployLogger.accept("Using CA signed certificate version %s".formatted(e.version()))); + // Record the quota usage for this application var quotaUsage = deploymentQuotaUsage(zone, job.application()); diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java index 813e3454e80..50e6951f8be 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java @@ -174,7 +174,7 @@ public class InternalStepRunner implements StepRunner { private Optional<RunStatus> deployReal(RunId id, boolean setTheStage, DualLogger logger) { Optional<X509Certificate> testerCertificate = controller.jobController().run(id).testerCertificate(); - return deploy(() -> controller.applications().deploy(id.job(), setTheStage), + return deploy(() -> controller.applications().deploy(id.job(), setTheStage, logger::log), controller.jobController().run(id) .stepInfo(setTheStage ? deployInitialReal : deployReal).get() .startTime().get(), diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/notification/Notifier.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/notification/Notifier.java index 49c819548fe..f2c9d55b2a2 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/notification/Notifier.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/notification/Notifier.java @@ -29,6 +29,16 @@ import java.util.stream.Collectors; * @author enygaard */ public class Notifier { + private static final String header = """ + <div style="background: #00598c; height: 55px; width: 100%"> + <img + src="https://vespa.ai/assets/vespa-logo.png" + style="width: auto; height: 34px; margin: 10px" + /> + </div> + <br> + """; + private final CuratorDb curatorDb; private final Mailer mailer; private final FlagSource flagSource; @@ -111,14 +121,15 @@ public class Notifier { public Mail mailOf(FormattedNotification content, Collection<String> recipients) { var notification = content.notification(); var subject = Text.format("[%s] %s Vespa Notification for %s", notification.level().toString().toUpperCase(), content.prettyType(), applicationIdSource(notification.source())); - var body = new StringBuilder(); - body.append(content.messagePrefix()).append("\n") + String body = new StringBuilder() + .append(content.messagePrefix()).append("\n") .append(notification.messages().stream().map(m -> " * " + m).collect(Collectors.joining("\n"))).append("\n") .append("\n") .append("Vespa Console link:\n") - .append(content.uri().toString()); - var html = new StringBuilder(); - html.append(content.messagePrefix()).append("<br>\n") + .append(content.uri().toString()).toString(); + String html = new StringBuilder() + .append(header) + .append(content.messagePrefix()).append("<br>\n") .append("<ul>\n") .append(notification.messages().stream() .map(Notifier::linkify) @@ -126,8 +137,8 @@ public class Notifier { .collect(Collectors.joining("<br>\n"))) .append("</ul>\n") .append("<br>\n") - .append("<a href=\"" + content.uri() + "\">Vespa Console</a>"); - return new Mail(recipients, subject, body.toString(), html.toString()); + .append("<a href=\"" + content.uri() + "\">Vespa Console</a>").toString(); + return new Mail(recipients, subject, body, html); } @VisibleForTesting diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/notification/NotifierTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/notification/NotifierTest.java index 8bf0e584892..4132db74140 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/notification/NotifierTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/notification/NotifierTest.java @@ -68,12 +68,20 @@ public class NotifierTest { var mail = mailer.inbox(email).get(0); assertEquals("[WARNING] Test package Vespa Notification for tenant1.default.default", mail.subject()); - assertEquals("There are problems with tests for default.default<br>\n" + - "<ul>\n" + - "<li>test package has production tests, but no production tests are declared in deployment.xml</li><br>\n" + - "<li>see <a href=\"https://docs.vespa.ai/en/testing.html\">https://docs.vespa.ai/en/testing.html</a> for details on how to write system tests for Vespa</li></ul>\n" + - "<br>\n" + - "<a href=\"https://dashboard.tld/tenant1/default\">Vespa Console</a>", + assertEquals(""" + <div style="background: #00598c; height: 55px; width: 100%"> + <img + src="https://vespa.ai/assets/vespa-logo.png" + style="width: auto; height: 34px; margin: 10px" + /> + </div> + <br> + There are problems with tests for default.default<br> + <ul> + <li>test package has production tests, but no production tests are declared in deployment.xml</li><br> + <li>see <a href="https://docs.vespa.ai/en/testing.html">https://docs.vespa.ai/en/testing.html</a> for details on how to write system tests for Vespa</li></ul> + <br> + <a href="https://dashboard.tld/tenant1/default">Vespa Console</a>""", mail.htmlMessage().get()); } @@ -84,4 +92,4 @@ public class NotifierTest { "No url.", "No url."); data.forEach((input, expected) -> assertEquals(expected, Notifier.linkify(input))); } -}
\ No newline at end of file +} diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-east-1-log-first-part.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-east-1-log-first-part.json index 63869ecfba8..9b391196d55 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-east-1-log-first-part.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-east-1-log-first-part.json @@ -11,6 +11,11 @@ { "at": 0, "type": "info", + "message": "Using CA signed certificate version 0" + }, + { + "at": 0, + "type": "info", "message": "Deployment successful." }, { @@ -49,7 +54,7 @@ } ] }, - "lastId": 7, + "lastId": 8, "steps": { "deployReal": { "status": "succeeded", diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-east-1-log-second-part.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-east-1-log-second-part.json index 175c45eb2cd..4ffac2bf738 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-east-1-log-second-part.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-east-1-log-second-part.json @@ -6,6 +6,11 @@ { "at": 0, "type": "info", + "message": "Found endpoints:" + }, + { + "at": 0, + "type": "info", "message": "- dev.us-east-1" }, { @@ -20,7 +25,7 @@ } ] }, - "lastId": 11, + "lastId": 12, "steps": { "deployReal": { "status": "succeeded", diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/staging-test-log.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/staging-test-log.json index ba65b962a73..a2f62621f5b 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/staging-test-log.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/staging-test-log.json @@ -85,6 +85,11 @@ { "at": 14503000, "type": "info", + "message": "Using CA signed certificate version 0" + }, + { + "at": 14503000, + "type": "info", "message": "Deployment successful." }, { @@ -160,7 +165,7 @@ } ] }, - "lastId": 29, + "lastId": 30, "steps": { "deployTester": { "status": "succeeded", diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/system-test-details.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/system-test-details.json index 3b505bc11fd..a691762c40b 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/system-test-details.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/system-test-details.json @@ -105,6 +105,11 @@ { "at": 1600000000000, "type": "info", + "message": "Using CA signed certificate version 1" + }, + { + "at": 1600000000000, + "type": "info", "message": "Deployment successful." }, { @@ -354,7 +359,7 @@ } ] }, - "lastId": 66, + "lastId": 67, "steps": { "deployTester": { "status": "succeeded", diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/system-test-log.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/system-test-log.json index 5bf6822baff..4e8737d5f67 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/system-test-log.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/system-test-log.json @@ -100,6 +100,11 @@ { "at": 0, "type": "info", + "message": "Using CA signed certificate version 0" + }, + { + "at": 0, + "type": "info", "message": "Deployment successful." }, { @@ -349,7 +354,7 @@ } ] }, - "lastId": 66, + "lastId": 67, "steps": { "deployTester": { "status": "succeeded", |