diff options
42 files changed, 339 insertions, 234 deletions
diff --git a/bundle-plugin/pom.xml b/bundle-plugin/pom.xml index d53c2c94d5c..8f52187357f 100644 --- a/bundle-plugin/pom.xml +++ b/bundle-plugin/pom.xml @@ -19,6 +19,10 @@ </prerequisites> <dependencies> <dependency> + <groupId>com.google.guava</groupId> + <artifactId>guava</artifactId> + </dependency> + <dependency> <groupId>org.apache.maven</groupId> <artifactId>maven-plugin-api</artifactId> <version>3.8.5</version> diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/application/HttpProxy.java b/configserver/src/main/java/com/yahoo/vespa/config/server/application/HttpProxy.java index 5f7ace38a2c..66fd84e11d3 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/application/HttpProxy.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/application/HttpProxy.java @@ -17,14 +17,11 @@ import com.yahoo.vespa.config.server.http.NotFoundException; import com.yahoo.vespa.config.server.http.SimpleHttpFetcher; import java.io.ByteArrayOutputStream; -import java.io.FilterOutputStream; import java.io.IOException; import java.io.OutputStream; import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; import java.util.List; import java.util.logging.Logger; -import java.util.regex.Pattern; import static java.nio.charset.StandardCharsets.UTF_8; @@ -63,7 +60,7 @@ public class HttpProxy { .orElseThrow(() -> new NotFoundException("Failed to find HTTP state port")); HttpURL url = HttpURL.create(Scheme.http, DomainName.of(host.getHostname()), port.getPort(), path, query); - HttpResponse response = fetcher.get(new Params(2000), // 2_000 ms read timeout + HttpResponse response = fetcher.get(new Params(29_000), // 29 sec (30 sec on controller) url.asURI()); return forwardedUrl == null ? response : new UrlRewritingProxyResponse(response, url, forwardedUrl); } diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/application/HttpProxyTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/application/HttpProxyTest.java index 80e998521a9..e3820ff99be 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/application/HttpProxyTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/application/HttpProxyTest.java @@ -2,13 +2,13 @@ package com.yahoo.vespa.config.server.application; import ai.vespa.http.HttpURL; +import ai.vespa.http.HttpURL.Path; import ai.vespa.http.HttpURL.Query; import com.yahoo.config.model.api.HostInfo; import com.yahoo.config.model.api.Model; import com.yahoo.config.model.api.ServiceInfo; import com.yahoo.config.provision.ClusterSpec; import com.yahoo.container.jdisc.HttpResponse; -import ai.vespa.http.HttpURL.Path; import com.yahoo.slime.Slime; import com.yahoo.slime.SlimeUtils; import com.yahoo.vespa.config.server.http.HttpFetcher; @@ -21,8 +21,6 @@ import org.mockito.ArgumentCaptor; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.URI; -import java.net.URL; -import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collections; @@ -30,7 +28,6 @@ import static com.yahoo.config.model.api.container.ContainerServiceType.CLUSTERC import static com.yahoo.vespa.config.server.application.MockModel.createServiceInfo; import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertSame; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doAnswer; @@ -53,7 +50,7 @@ public class HttpProxyTest { } @Test - public void testNormalGet() throws Exception { + public void testNormalGet() { ArgumentCaptor<HttpFetcher.Params> actualParams = ArgumentCaptor.forClass(HttpFetcher.Params.class); ArgumentCaptor<URI> actualUrl = ArgumentCaptor.forClass(URI.class); HttpResponse response = new StaticResponse(200, "application/json", "body"); @@ -64,7 +61,7 @@ public class HttpProxyTest { Query.parse("foo=bar")); assertEquals(1, actualParams.getAllValues().size()); - assertEquals(2000, actualParams.getValue().readTimeoutMs); + assertEquals(29000, actualParams.getValue().readTimeoutMs); assertEquals(1, actualUrl.getAllValues().size()); assertEquals(URI.create("http://" + hostname + ":" + port + "/clustercontroller-status/v1/clusterName?foo=bar"), diff --git a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/JettyConnectionLogger.java b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/JettyConnectionLogger.java index 4e3fd3f29b3..2e2eb257b6a 100644 --- a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/JettyConnectionLogger.java +++ b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/JettyConnectionLogger.java @@ -311,7 +311,7 @@ class JettyConnectionLogger extends AbstractLifeCycle implements Connection.List this.sslSubjectAlternativeNames = X509CertificateUtils.getSubjectAlternativeNames(peerCertificate).stream() .map(SubjectAlternativeName::getValue) .collect(Collectors.toList()); - this.sslPeerIssuerSubject = peerCertificate.getIssuerDN().getName(); + this.sslPeerIssuerSubject = peerCertificate.getIssuerX500Principal().getName(); this.sslPeerEncodedCertificate = peerCertificate.getEncoded(); } catch (SSLPeerUnverifiedException | CertificateEncodingException e) { // Throw if peer is not authenticated (e.g when client auth is disabled) diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/zone/ZoneRegistry.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/zone/ZoneRegistry.java index 893b7a1b1dc..935ba17eed6 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/zone/ZoneRegistry.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/zone/ZoneRegistry.java @@ -81,6 +81,9 @@ public interface ZoneRegistry { /** Returns a URL where an informative dashboard can be found. */ URI dashboardUrl(); + /** Returns a URL which displays information about the given tenant. */ + URI dashboardUrl(TenantName id); + /** Returns a URL which displays information about the given application. */ URI dashboardUrl(ApplicationId id); 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 3be5345b377..0f7cbcee4ab 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 @@ -287,7 +287,7 @@ public class ApplicationController { if (oldest == null || version.isBefore(oldest)) oldest = version; - if (run.status() == RunStatus.success) + if (run.hasSucceeded()) return Optional.of(oldest); } // If no successful run was found, ask the node repository in the relevant zone. diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Controller.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Controller.java index ca8f6a3ae33..48e663e7feb 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Controller.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Controller.java @@ -13,7 +13,6 @@ import com.yahoo.config.provision.zone.ZoneApi; import com.yahoo.container.jdisc.secretstore.SecretStore; import com.yahoo.jdisc.Metric; import com.yahoo.transaction.Mutex; -import com.yahoo.vespa.curator.Lock; import com.yahoo.vespa.flags.FlagSource; import com.yahoo.vespa.hosted.controller.api.integration.ServiceRegistry; import com.yahoo.vespa.hosted.controller.api.integration.maven.MavenRepository; @@ -124,7 +123,7 @@ public class Controller extends AbstractComponent { auditLogger = new AuditLogger(curator, clock); jobControl = new JobControl(new JobControlFlags(curator, flagSource)); archiveBucketDb = new CuratorArchiveBucketDb(this); - notifier = new Notifier(curator, serviceRegistry.mailer()); + notifier = new Notifier(curator, serviceRegistry.zoneRegistry(), serviceRegistry.mailer()); notificationsDb = new NotificationsDb(this); supportAccessControl = new SupportAccessControl(this); diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentStatus.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentStatus.java index 0595245b3d7..cb2958745d0 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentStatus.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentStatus.java @@ -321,7 +321,7 @@ public class DeploymentStatus { .type(type).asList().stream() .flatMap(status -> RunList.from(status) .on(versions) - .status(RunStatus.success) + .matching(Run::hasSucceeded) .asList().stream() .map(Run::start)) .min(naturalOrder()); @@ -860,7 +860,7 @@ public class DeploymentStatus { Optional<Instant> end = Optional.empty(); for (Run run : job.runs().descendingMap().values()) { if (run.versions().targetsMatch(change)) { - if (run.status() == RunStatus.success) end = run.end(); + if (run.hasSucceeded()) end = run.end(); } else if (dependent.equals(job())) // If strict completion, consider only last time this change was deployed. break; @@ -887,7 +887,7 @@ public class DeploymentStatus { Optional<Instant> deployedAt = status.jobSteps().get(prodId).completedAt(change, Optional.of(prodId)); return (dependent.equals(job()) ? job.lastTriggered().filter(run -> deployedAt.map(at -> ! run.start().isBefore(at)).orElse(false)).stream() : job.runs().values().stream()) - .filter(run -> run.status() == RunStatus.success) + .filter(Run::hasSucceeded) .filter(run -> run.versions().targetsMatch(change)) .flatMap(run -> run.end().stream()).findFirst(); } @@ -907,7 +907,7 @@ public class DeploymentStatus { status.systemVersion))) .orElseGet(() -> (change.platform().isEmpty() || change.platform().get().equals(run.versions().targetPlatform())) && (change.revision().isEmpty() || change.revision().get().equals(run.versions().targetRevision())))) - .status(RunStatus.success) + .matching(Run::hasSucceeded) .asList().stream() .map(run -> run.end().get()) .max(naturalOrder()); 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 ce4bb70c174..28d9439b457 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 @@ -23,8 +23,6 @@ import com.yahoo.security.SignatureAlgorithm; import com.yahoo.security.X509CertificateBuilder; import com.yahoo.security.X509CertificateUtils; import com.yahoo.text.Text; -import com.yahoo.vespa.flags.FetchVector; -import com.yahoo.vespa.flags.Flags; import com.yahoo.vespa.hosted.controller.Application; import com.yahoo.vespa.hosted.controller.Controller; import com.yahoo.vespa.hosted.controller.Instance; @@ -36,7 +34,6 @@ import com.yahoo.vespa.hosted.controller.api.integration.configserver.Node; import com.yahoo.vespa.hosted.controller.api.integration.configserver.NodeFilter; import com.yahoo.vespa.hosted.controller.api.integration.configserver.PrepareResponse; import com.yahoo.vespa.hosted.controller.api.integration.configserver.ServiceConvergence; -import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion; import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType; import com.yahoo.vespa.hosted.controller.api.integration.deployment.RevisionId; import com.yahoo.vespa.hosted.controller.api.integration.deployment.RunId; @@ -90,6 +87,7 @@ import static com.yahoo.vespa.hosted.controller.api.integration.configserver.Nod import static com.yahoo.vespa.hosted.controller.deployment.RunStatus.deploymentFailed; import static com.yahoo.vespa.hosted.controller.deployment.RunStatus.error; import static com.yahoo.vespa.hosted.controller.deployment.RunStatus.installationFailed; +import static com.yahoo.vespa.hosted.controller.deployment.RunStatus.noTests; import static com.yahoo.vespa.hosted.controller.deployment.RunStatus.nodeAllocationFailure; import static com.yahoo.vespa.hosted.controller.deployment.RunStatus.reset; import static com.yahoo.vespa.hosted.controller.deployment.RunStatus.running; @@ -638,6 +636,7 @@ public class InternalStepRunner implements StepRunner { return Optional.of(running); } + @SuppressWarnings("fallthrough") private Optional<RunStatus> endTests(RunId id, boolean isSetup, DualLogger logger) { Optional<Deployment> deployment = deployment(id.application(), id.type()); if (deployment.isEmpty()) { @@ -679,12 +678,14 @@ public class InternalStepRunner implements StepRunner { controller.jobController().updateTestReport(id); return Optional.of(error); case NO_TESTS: - TesterCloud.Suite suite = TesterCloud.Suite.of(id.type(), isSetup); - logger.log(INFO, "No tests were found in the test package, for test suite '" + suite + "'"); - logger.log(INFO, "The test package must either contain basic HTTP tests under 'tests/<suite-name>/', " + - "or a Java test bundle under 'components/' with at least one test with the annotation " + - "for this suite. See docs.vespa.ai/en/testing.html for details."); - return Optional.of(allowNoTests(id.application()) ? running : testFailure); + if ( ! isSetup) { // TODO: consider changing this Later™ + TesterCloud.Suite suite = TesterCloud.Suite.of(id.type(), isSetup); + logger.log(INFO, "No tests were found in the test package, for test suite '" + suite + "'"); + logger.log(INFO, "The test package should either contain basic HTTP tests under 'tests/<suite-name>/', " + + "or a Java test bundle under 'components/' with at least one test with the annotation " + + "for this suite. See docs.vespa.ai/en/testing.html for details."); + return Optional.of(noTests); + } case SUCCESS: logger.log("Tests completed successfully."); controller.jobController().updateTestReport(id); @@ -694,12 +695,6 @@ public class InternalStepRunner implements StepRunner { } } - private boolean allowNoTests(ApplicationId appId) { - return Flags.ALLOW_NO_TESTS.bindTo(controller.flagSource()) - .with(FetchVector.Dimension.TENANT_ID, appId.tenant().value()) - .value(); - } - private Optional<RunStatus> copyVespaLogs(RunId id, DualLogger logger) { if (deployment(id.application(), id.type()).isPresent()) try { @@ -835,6 +830,10 @@ public class InternalStepRunner implements StepRunner { case testFailure: updater.accept("one or more verification tests against the deployment failed. Please review test output in the deployment job log."); return; + case noTests: + controller.notificationsDb().setNotification(source, Notification.Type.deployment, Notification.Level.warning, + "no tests were found for this job type. Please review test output in the deployment job log."); + return; case error: case endpointCertificateTimeout: break; @@ -849,6 +848,7 @@ public class InternalStepRunner implements StepRunner { switch (run.status()) { case running: case aborted: + case noTests: case success: return Optional.empty(); case nodeAllocationFailure: @@ -861,11 +861,11 @@ public class InternalStepRunner implements StepRunner { return Optional.of(mails.testFailure(run.id(), recipients)); case error: case endpointCertificateTimeout: - return Optional.of(mails.systemError(run.id(), recipients)); + break; default: logger.log(WARNING, "Don't know what mail to send for run status '" + run.status() + "'"); - return Optional.of(mails.systemError(run.id(), recipients)); } + return Optional.of(mails.systemError(run.id(), recipients)); } /** Returns the deployment of the real application in the zone of the given job, if it exists. */ diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobController.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobController.java index 74730a78e31..b9ce094c1c0 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobController.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobController.java @@ -400,7 +400,7 @@ public class JobController { locked(id.application(), id.type(), runs -> { runs.put(run.id(), finishedRun); long last = id.number(); - long successes = runs.values().stream().filter(old -> old.status() == RunStatus.success).count(); + long successes = runs.values().stream().filter(Run::hasSucceeded).count(); var oldEntries = runs.entrySet().iterator(); for (var old = oldEntries.next(); old.getKey().number() <= last - historyLength @@ -409,7 +409,7 @@ public class JobController { // Make sure we keep the last success and the first failing if ( successes == 1 - && old.getValue().status() == RunStatus.success + && old.getValue().hasSucceeded() && ! old.getValue().start().isBefore(controller.clock().instant().minus(maxHistoryAge))) { oldEntries.next(); continue; diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobList.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobList.java index 639702128d3..387ea755414 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobList.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobList.java @@ -120,7 +120,7 @@ public class JobList extends AbstractFilteringList<JobStatus, JobList> { /** Returns the jobs with successful runs matching the given versions — targets only for system test, everything present otherwise. */ public JobList successOn(Versions versions) { - return matching(job -> ! RunList.from(job).status(RunStatus.success).on(versions).isEmpty()); + return matching(job -> ! RunList.from(job).matching(Run::hasSucceeded).on(versions).isEmpty()); } // ----------------------------------- JobRun filtering diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobMetrics.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobMetrics.java index 874b1828f5f..e95e515685f 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobMetrics.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobMetrics.java @@ -21,6 +21,7 @@ public class JobMetrics { public static final String deploymentFailure = "deployment.deploymentFailure"; public static final String convergenceFailure = "deployment.convergenceFailure"; public static final String testFailure = "deployment.testFailure"; + public static final String noTests = "deployment.noTests"; public static final String error = "deployment.error"; public static final String abort = "deployment.abort"; public static final String success = "deployment.success"; @@ -56,6 +57,7 @@ public class JobMetrics { case deploymentFailed: return deploymentFailure; case installationFailed: return convergenceFailure; case testFailure: return testFailure; + case noTests: return noTests; case error: return error; case aborted: return abort; case success: return success; diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobStatus.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobStatus.java index aad5d510261..45bf508f026 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobStatus.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobStatus.java @@ -60,7 +60,7 @@ public class JobStatus { } public boolean isSuccess() { - return lastStatus().isPresent() && lastStatus().get() == RunStatus.success; + return lastCompleted.map(last -> ! last.hasFailed()).orElse(false); } public boolean isRunning() { @@ -90,18 +90,17 @@ public class JobStatus { static Optional<Run> lastSuccess(NavigableMap<RunId, Run> runs) { return runs.descendingMap().values().stream() - .filter(run -> run.status() == RunStatus.success) + .filter(Run::hasSucceeded) .findFirst(); } static Optional<Run> firstFailing(NavigableMap<RunId, Run> runs) { Run failed = null; - loop: for (Run run : runs.descendingMap().values()) - switch (run.status()) { - case running: continue loop; - case success: break loop; - default: failed = run; - } + for (Run run : runs.descendingMap().values()) { + if ( ! run.hasEnded()) continue; + if ( ! run.hasFailed()) break; + failed = run; + } return Optional.ofNullable(failed); } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/Run.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/Run.java index dcde21c8cf5..03cc6c6ba8d 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/Run.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/Run.java @@ -13,6 +13,7 @@ import java.util.Optional; import java.util.stream.Collectors; import static com.yahoo.vespa.hosted.controller.deployment.RunStatus.aborted; +import static com.yahoo.vespa.hosted.controller.deployment.RunStatus.noTests; import static com.yahoo.vespa.hosted.controller.deployment.RunStatus.running; import static com.yahoo.vespa.hosted.controller.deployment.RunStatus.success; import static com.yahoo.vespa.hosted.controller.deployment.Step.Status.succeeded; @@ -80,8 +81,9 @@ public class Run { EnumMap<Step, StepInfo> steps = new EnumMap<>(this.steps); steps.put(step.get(), stepInfo.with(Step.Status.of(status))); - return new Run(id, steps, versions, isRedeployment, start, end, sleepUntil, this.status == running ? status : this.status, - lastTestRecord, lastVespaLogTimestamp, noNodesDownSince, convergenceSummary, testerCertificate, dryRun, reason); + RunStatus newStatus = hasFailed() || status == running ? this.status : status; + return new Run(id, steps, versions, isRedeployment, start, end, sleepUntil, newStatus, lastTestRecord, + lastVespaLogTimestamp, noNodesDownSince, convergenceSummary, testerCertificate, dryRun, reason); } /** Returns a new Run with a new start time*/ @@ -210,7 +212,7 @@ public class Run { /** Returns whether the run has failed, and should switch to its run-always steps. */ public boolean hasFailed() { - return status != running && status != success; + return status != running && status != success && status != noTests; } /** Returns whether the run has ended, i.e., has become inactive, and can no longer be updated. */ @@ -218,6 +220,8 @@ public class Run { return end.isPresent(); } + public boolean hasSucceeded() { return hasEnded() && ! hasFailed(); } + /** Returns the target, and possibly source, versions for this run. */ public Versions versions() { return versions; diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/RunStatus.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/RunStatus.java index 0bb4a30425e..9ca634b19fd 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/RunStatus.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/RunStatus.java @@ -26,6 +26,9 @@ public enum RunStatus { /** The verification tests failed. */ testFailure, + /** No tests, for a test job. */ + noTests, + /** An unexpected error occurred. */ error, diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/Step.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/Step.java index 14d6b35f6ad..82d154dcf03 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/Step.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/Step.java @@ -113,6 +113,7 @@ public enum Step { case success : throw new AssertionError("Unexpected run status '" + status + "'!"); case reset : case aborted : return unfinished; + case noTests : case running : return succeeded; default : return failed; } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/notify/Notifier.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/notify/Notifier.java index 594908a3bc1..7692752f3ca 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/notify/Notifier.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/notify/Notifier.java @@ -1,16 +1,21 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.hosted.controller.notify; +import com.yahoo.config.provision.ApplicationId; +import com.yahoo.config.provision.Environment; import com.yahoo.text.Text; +import com.yahoo.vespa.hosted.controller.api.integration.deployment.RunId; import com.yahoo.vespa.hosted.controller.api.integration.organization.Mail; import com.yahoo.vespa.hosted.controller.api.integration.organization.Mailer; import com.yahoo.vespa.hosted.controller.api.integration.organization.MailerException; +import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneRegistry; import com.yahoo.vespa.hosted.controller.notification.Notification; import com.yahoo.vespa.hosted.controller.notification.NotificationSource; import com.yahoo.vespa.hosted.controller.persistence.CuratorDb; import com.yahoo.vespa.hosted.controller.tenant.CloudTenant; import com.yahoo.vespa.hosted.controller.tenant.TenantContacts; +import java.net.URI; import java.util.Collection; import java.util.List; import java.util.Objects; @@ -25,12 +30,14 @@ import java.util.stream.Collectors; */ public class Notifier { private final CuratorDb curatorDb; + private final ZoneRegistry zoneRegistry; private final Mailer mailer; private static final Logger log = Logger.getLogger(Notifier.class.getName()); - public Notifier(CuratorDb curatorDb, Mailer mailer) { + public Notifier(CuratorDb curatorDb, ZoneRegistry zoneRegistry, Mailer mailer) { this.curatorDb = Objects.requireNonNull(curatorDb); + this.zoneRegistry = Objects.requireNonNull(zoneRegistry); this.mailer = Objects.requireNonNull(mailer); } @@ -56,7 +63,12 @@ public class Notifier { private boolean skipSource(NotificationSource source) { // Limit sources to production systems only. Dev and test systems cause too much noise at the moment. - return source.jobType().map(t -> !t.isProduction()).orElse(false); + if (source.zoneId().map(z -> z.environment() != Environment.prod).orElse(false)) { + return true; + } else if (source.jobType().map(t -> !t.isProduction()).orElse(false)) { + return true; + } + return false; } public void dispatch(Notification notification) { @@ -82,12 +94,30 @@ public class Notifier { } private Mail mailOf(Notification n, Collection<String> recipients) { - var subject = Text.format("[%s] Vespa Notification for %s", n.level().toString().toUpperCase(), n.type().name()); + var source = n.source(); + var subject = Text.format("[%s] %s Vespa Notification for %s - %s", n.level().toString().toUpperCase(), n.type().name(), source.tenant(), source.application()); var body = new StringBuilder(); body.append("Source: ").append(n.source().toString()).append("\n") .append("\n") - .append(String.join("\n", n.messages())); + .append(String.join("\n", n.messages())) + .append("\n") + .append(url(source).toString()); return new Mail(recipients, subject.toString(), body.toString()); } + private URI url(NotificationSource source) { + if (source.application().isPresent() && source.instance().isPresent()) { + if (source.jobType().isPresent() && source.runNumber().isPresent()) { + return zoneRegistry.dashboardUrl( + new RunId(ApplicationId.from(source.tenant(), + source.application().get(), + source.instance().get()), + source.jobType().get(), + source.runNumber().getAsLong())); + } + return zoneRegistry.dashboardUrl(ApplicationId.from(source.tenant(), source.application().get(), source.instance().get())); + } + return zoneRegistry.dashboardUrl(source.tenant()); + } + } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/RunSerializer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/RunSerializer.java index 731e5790a64..143aaaeabb8 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/RunSerializer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/RunSerializer.java @@ -35,6 +35,7 @@ import static com.yahoo.vespa.hosted.controller.deployment.RunStatus.deploymentF import static com.yahoo.vespa.hosted.controller.deployment.RunStatus.endpointCertificateTimeout; import static com.yahoo.vespa.hosted.controller.deployment.RunStatus.error; import static com.yahoo.vespa.hosted.controller.deployment.RunStatus.installationFailed; +import static com.yahoo.vespa.hosted.controller.deployment.RunStatus.noTests; import static com.yahoo.vespa.hosted.controller.deployment.RunStatus.nodeAllocationFailure; import static com.yahoo.vespa.hosted.controller.deployment.RunStatus.reset; import static com.yahoo.vespa.hosted.controller.deployment.RunStatus.running; @@ -342,6 +343,7 @@ class RunSerializer { case deploymentFailed : return "deploymentFailed"; case installationFailed : return "installationFailed"; case testFailure : return "testFailure"; + case noTests : return "noTests"; case error : return "error"; case success : return "success"; case aborted : return "aborted"; @@ -354,11 +356,11 @@ class RunSerializer { static RunStatus runStatusOf(String status) { switch (status) { case "running" : return running; - case "outOfCapacity" : return nodeAllocationFailure; // TODO: Remove after March 2022 case "nodeAllocationFailure" : return nodeAllocationFailure; case "endpointCertificateTimeout" : return endpointCertificateTimeout; case "deploymentFailed" : return deploymentFailed; case "installationFailed" : return installationFailed; + case "noTests" : return noTests; case "testFailure" : return testFailure; case "error" : return error; case "success" : return success; diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/deployment/Badges.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/deployment/Badges.java index 1fe5ebfa9a9..7b4f2fec853 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/deployment/Badges.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/deployment/Badges.java @@ -15,6 +15,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Optional; import static java.util.stream.Collectors.toList; @@ -51,14 +52,18 @@ public class Badges { return widthOf(text, 11); } - static String colorOf(Run run, Boolean wasOk) { + static String colorOf(Run run, Optional<RunStatus> previous) { switch (run.status()) { - case running: - return wasOk ? "url(#run-on-success)" : "url(#run-on-failure)"; - case success: - return success; - default: - return failure; + case running: switch (previous.orElse(RunStatus.success)) { + case success: return "url(#run-on-success)"; + case aborted: + case noTests: return "url(#run-on-warning)"; + default: return "url(#run-on-failure)"; + } + case success: return success; + case aborted: + case noTests: return warning; + default: return failure; } } @@ -71,9 +76,10 @@ public class Badges { static final double xPad = 6; static final double logoSize = 16; static final String dark = "#404040"; - static final String success = "#00f244"; + static final String success = "#00f844"; static final String running = "#ab83ff"; static final String failure = "#bf103c"; + static final String warning = "#bd890b"; static void addText(List<String> texts, String text, double x, double width) { addText(texts, text, x, width, 11); @@ -116,13 +122,11 @@ public class Badges { .limit(length) .collect(toList()); - boolean isOk = status.lastCompleted().map(run -> run.status() == RunStatus.success).orElse(true); - text = lastTriggered.id().type().jobName(); textWidth = widthOf(text); dx = xPad + textWidth + xPad; addShade(sections, x, dx); - sections.add(" <rect x='" + (x - 6) + "' rx='3' width='" + (dx + 6) + "' height='20' fill='" + colorOf(lastTriggered, isOk) + "'/>\n"); + sections.add(" <rect x='" + (x - 6) + "' rx='3' width='" + (dx + 6) + "' height='20' fill='" + colorOf(lastTriggered, status.lastStatus()) + "'/>\n"); addShadow(sections, x + dx); addText(texts, text, x + dx / 2, textWidth); x += dx; @@ -130,7 +134,7 @@ public class Badges { dx = xPad * (192.0 / (32 + runs.size())); // Broader sections with shorter history. for (Run run : runs) { addShade(sections, x, dx); - sections.add(" <rect x='" + (x - 6) + "' rx='3' width='" + (dx + 6) + "' height='20' fill='" + colorOf(run, null) + "'/>\n"); + sections.add(" <rect x='" + (x - 6) + "' rx='3' width='" + (dx + 6) + "' height='20' fill='" + colorOf(run, Optional.empty()) + "'/>\n"); addShadow(sections, x + dx); dx *= Math.pow(0.3, 1.0 / (runs.size() + 8)); // Gradually narrowing sections with age. x += dx; @@ -179,7 +183,7 @@ public class Badges { text = nameOf(run.id().type()); textWidth = widthOf(text, isTest ? 9 : 11); dx = xPad + textWidth + (isTest ? 0 : xPad); - boolean wasOk = jobs.get(run.id().job()).flatMap(JobStatus::lastStatus).map(RunStatus.success::equals).orElse(true); + Optional<RunStatus> previous = jobs.get(run.id().job()).flatMap(JobStatus::lastStatus); addText(texts, text, x + (dx - (isTest ? xPad : 0)) / 2, textWidth, isTest ? 9 : 11); @@ -197,10 +201,10 @@ public class Badges { // Add colored section for job ... if (test == null) - sections.add(" <rect x='" + (x - 16) + "' rx='3' width='" + (dx + 16) + "' height='20' fill='" + colorOf(run, wasOk) + "'/>\n"); + sections.add(" <rect x='" + (x - 16) + "' rx='3' width='" + (dx + 16) + "' height='20' fill='" + colorOf(run, previous) + "'/>\n"); // ... with a slant if a test is next. else - sections.add(" <polygon points='" + (x - 6) + " 0 " + (x - 6) + " 20 " + (x + dx - 7) + " 20 " + (x + dx + 1) + " 0' fill='" + colorOf(run, wasOk) + "'/>\n"); + sections.add(" <polygon points='" + (x - 6) + " 0 " + (x - 6) + " 20 " + (x + dx - 7) + " 20 " + (x + dx + 1) + " 0' fill='" + colorOf(run, previous) + "'/>\n"); // Cast a shadow onto the next zone ... if (test == null) @@ -255,6 +259,13 @@ public class Badges { " <animate attributeName='x1' values='-110%;150%;20%;-110%' dur='6s' repeatCount='indefinite' />\n" + " <animate attributeName='x2' values='-10%;250%;120%;-10%' dur='6s' repeatCount='indefinite' />\n" + " </linearGradient>\n" + + // Running color sloshing back and forth on top of the warning color. + " <linearGradient id='run-on-warning' x1='40%' x2='80%' y2='0%'>\n" + + " <stop offset='0' stop-color='" + running + "' />\n" + + " <stop offset='1' stop-color='" + warning + "' />\n" + + " <animate attributeName='x1' values='-110%;150%;20%;-110%' dur='6s' repeatCount='indefinite' />\n" + + " <animate attributeName='x2' values='-10%;250%;120%;-10%' dur='6s' repeatCount='indefinite' />\n" + + " </linearGradient>\n" + // Running color sloshing back and forth on top of the success color. " <linearGradient id='run-on-success' x1='40%' x2='80%' y2='0%'>\n" + " <stop offset='0' stop-color='" + running + "' />\n" + diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentContext.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentContext.java index f18733fbcde..3765f815e49 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentContext.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentContext.java @@ -28,6 +28,7 @@ import com.yahoo.vespa.hosted.controller.api.integration.deployment.RevisionId; import com.yahoo.vespa.hosted.controller.api.integration.deployment.RunId; import com.yahoo.vespa.hosted.controller.api.integration.deployment.SourceRevision; import com.yahoo.vespa.hosted.controller.api.integration.deployment.TesterCloud; +import com.yahoo.vespa.hosted.controller.api.integration.deployment.TesterCloud.Status; import com.yahoo.vespa.hosted.controller.api.integration.deployment.TesterId; import com.yahoo.vespa.hosted.controller.application.Deployment; import com.yahoo.vespa.hosted.controller.application.EndpointId; @@ -402,8 +403,34 @@ public class DeploymentContext { return runJob(type, instanceId); } + /** Runs the job, failing tests with noTests status, or with regular testFailure. */ + public DeploymentContext failTests(JobType type, boolean noTests) { + if ( ! type.isTest()) throw new IllegalArgumentException(type + " does not run tests"); + var job = new JobId(instanceId, type); + triggerJobs(); + doDeploy(job); + if (job.type().isDeployment()) { + doUpgrade(job); + doConverge(job); + if (job.type().environment().isManuallyDeployed()) + return this; + } + + RunId id = currentRun(job).id(); + ZoneId zone = zone(job); + + assertEquals(unfinished, jobs.run(id).get().stepStatuses().get(Step.endTests)); + tester.cloud().set(noTests ? Status.NO_TESTS : Status.FAILURE); + runner.advance(currentRun(job)); + assertTrue(jobs.run(id).get().hasEnded()); + assertEquals(noTests, jobs.run(id).get().hasSucceeded()); + assertTrue(configServer().nodeRepository().list(zone, NodeFilter.all().applications(TesterId.of(instanceId).id())).isEmpty()); + + return this; + } + /** Pulls the ready job trigger, and then runs the whole of job for the given instance, successfully. */ - public DeploymentContext runJob(JobType type, ApplicationId instance) { + private DeploymentContext runJob(JobType type, ApplicationId instance) { var job = new JobId(instance, type); triggerJobs(); doDeploy(job); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunnerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunnerTest.java index 98cbf33fb2b..15f729e7a55 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunnerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunnerTest.java @@ -24,6 +24,7 @@ import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType; import com.yahoo.vespa.hosted.controller.api.integration.deployment.RunId; import com.yahoo.vespa.hosted.controller.api.integration.deployment.TestReport; import com.yahoo.vespa.hosted.controller.api.integration.deployment.TesterCloud; +import com.yahoo.vespa.hosted.controller.api.integration.deployment.TesterCloud.Status; import com.yahoo.vespa.hosted.controller.api.integration.stubs.MockMailer; import com.yahoo.vespa.hosted.controller.application.SystemApplication; import com.yahoo.vespa.hosted.controller.application.pkg.ApplicationPackage; @@ -47,6 +48,8 @@ import java.util.List; import java.util.Optional; import java.util.concurrent.Executors; import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Supplier; import static com.yahoo.vespa.hosted.controller.api.integration.LogEntry.Type.error; import static com.yahoo.vespa.hosted.controller.api.integration.LogEntry.Type.info; @@ -55,6 +58,7 @@ import static com.yahoo.vespa.hosted.controller.deployment.DeploymentContext.app import static com.yahoo.vespa.hosted.controller.deployment.DeploymentTester.instanceId; import static com.yahoo.vespa.hosted.controller.deployment.RunStatus.deploymentFailed; import static com.yahoo.vespa.hosted.controller.deployment.RunStatus.installationFailed; +import static com.yahoo.vespa.hosted.controller.deployment.RunStatus.noTests; import static com.yahoo.vespa.hosted.controller.deployment.RunStatus.running; import static com.yahoo.vespa.hosted.controller.deployment.RunStatus.success; import static com.yahoo.vespa.hosted.controller.deployment.Step.Status.failed; @@ -62,6 +66,7 @@ import static com.yahoo.vespa.hosted.controller.deployment.Step.Status.succeeded import static com.yahoo.vespa.hosted.controller.deployment.Step.Status.unfinished; import static java.time.temporal.ChronoUnit.SECONDS; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; @@ -123,22 +128,11 @@ public class InternalStepRunnerTest { } @Test - // TODO jonmv: Change to only wait for restarts, and remove triggering of restarts from runner. public void restartsServicesAndWaitsForRestartAndReboot() { RunId id = app.newRun(JobType.productionUsCentral1); ZoneId zone = id.type().zone(system()); HostName host = tester.configServer().hostFor(instanceId, zone); - tester.configServer().setConfigChangeActions(new ConfigChangeActions(List.of(new RestartAction("cluster", - "container", - "search", - List.of(new ServiceInfo("queries", - "search", - "config", - host.value())), - List.of("Restart it!"))), - List.of(), - List.of())); tester.runner().run(); assertEquals(succeeded, tester.jobs().run(id).get().stepStatuses().get(Step.deployReal)); @@ -269,6 +263,27 @@ public class InternalStepRunnerTest { } @Test + public void noTestsThenErrorIsError() { + RunId id = app.startSystemTestTests(); + Run run = tester.jobs().run(id).get(); + run = run.with(noTests, new LockedStep(() -> { }, Step.endTests)); + assertFalse(run.hasFailed()); + run = run.with(RunStatus.error, new LockedStep(() -> { }, Step.deactivateReal)); + assertTrue(run.hasFailed()); + assertEquals(RunStatus.error, run.status()); + } + + @Test + public void noTestsThenSuccessIsNoTests() { + RunId id = app.startSystemTestTests(); + tester.cloud().set(Status.NO_TESTS); + tester.runner().run(); + assertEquals(succeeded, tester.jobs().run(id).get().stepStatuses().get(Step.endTests)); + Run run = tester.jobs().run(id).get(); + assertEquals(noTests, run.status()); + } + + @Test public void testsFailIfTesterRestarts() { RunId id = app.startSystemTestTests(); tester.cloud().set(TesterCloud.Status.NOT_STARTED); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerMock.java index 644d60fbe72..1ed84659f58 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerMock.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerMock.java @@ -98,7 +98,6 @@ public class ConfigServerMock extends AbstractComponent implements ConfigServer private Version lastPrepareVersion = null; private Consumer<ApplicationId> prepareException = null; - private ConfigChangeActions configChangeActions = null; private Supplier<String> log = () -> "INFO - All good"; @Inject @@ -106,11 +105,6 @@ public class ConfigServerMock extends AbstractComponent implements ConfigServer bootstrap(zoneRegistry.zones().all().ids(), SystemApplication.notController()); } - /** Sets the ConfigChangeActions that will be returned on next deployment. */ - public void setConfigChangeActions(ConfigChangeActions configChangeActions) { - this.configChangeActions = configChangeActions; - } - /** Assigns a reserved tenant node to the given deployment, with initial versions. */ public void provision(ZoneId zone, ApplicationId application, ClusterSpec.Id clusterId) { var current = new ClusterResources(2, 1, new NodeResources(2, 8, 50, 1, slow, remote)); @@ -428,10 +422,7 @@ public class ConfigServerMock extends AbstractComponent implements ConfigServer PrepareResponse prepareResponse = new PrepareResponse(); prepareResponse.message = "foo"; - prepareResponse.configChangeActions = configChangeActions != null - ? configChangeActions - : new ConfigChangeActions(List.of(), List.of(), List.of()); - setConfigChangeActions(null); + prepareResponse.configChangeActions = new ConfigChangeActions(List.of(), List.of(), List.of()); prepareResponse.tenant = new TenantId("tenant"); prepareResponse.log = warnings.getOrDefault(id, Collections.emptyList()); return prepareResponse; diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ZoneRegistryMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ZoneRegistryMock.java index 1f4218af1b6..1d8dba321a2 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ZoneRegistryMock.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ZoneRegistryMock.java @@ -199,6 +199,11 @@ public class ZoneRegistryMock extends AbstractComponent implements ZoneRegistry } @Override + public URI dashboardUrl(TenantName tenantName) { + return URI.create("https://dashboard.tld/" + tenantName); + } + + @Override public URI dashboardUrl(RunId id) { return URI.create("https://dashboard.tld/" + id.application() + "/" + id.type().jobName() + "/" + id.number()); } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/JobRunnerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/JobRunnerTest.java index 9ccd7244392..610d8d4ca9a 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/JobRunnerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/JobRunnerTest.java @@ -413,6 +413,7 @@ public class JobRunnerTest { assertEquals(1, metric.getMetric(context::equals, JobMetrics.nodeAllocationFailure).get().intValue()); assertEquals(1, metric.getMetric(context::equals, JobMetrics.endpointCertificateTimeout).get().intValue()); assertEquals(1, metric.getMetric(context::equals, JobMetrics.testFailure).get().intValue()); + assertEquals(1, metric.getMetric(context::equals, JobMetrics.noTests).get().intValue()); } private void start(JobController jobs, ApplicationId id, JobType type) { diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/notification/NotificationsDbTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/notification/NotificationsDbTest.java index 28be68cf850..75b9b108377 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/notification/NotificationsDbTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/notification/NotificationsDbTest.java @@ -15,6 +15,7 @@ import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType; import com.yahoo.vespa.hosted.controller.api.integration.deployment.RunId; import com.yahoo.vespa.hosted.controller.api.integration.stubs.MockMailer; import com.yahoo.vespa.hosted.controller.application.TenantAndApplicationId; +import com.yahoo.vespa.hosted.controller.integration.ZoneRegistryMock; import com.yahoo.vespa.hosted.controller.notify.Notifier; import com.yahoo.vespa.hosted.controller.persistence.MockCuratorDb; import com.yahoo.vespa.hosted.controller.tenant.CloudTenant; @@ -69,7 +70,7 @@ public class NotificationsDbTest { private final ManualClock clock = new ManualClock(Instant.ofEpochSecond(12345)); private final MockCuratorDb curatorDb = new MockCuratorDb(SystemName.Public); private final MockMailer mailer = new MockMailer(); - private final NotificationsDb notificationsDb = new NotificationsDb(clock, curatorDb, new Notifier(curatorDb, mailer)); + private final NotificationsDb notificationsDb = new NotificationsDb(clock, curatorDb, new Notifier(curatorDb, new ZoneRegistryMock(SystemName.cd), mailer)); @Test public void list_test() { diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/BadgeApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/BadgeApiTest.java index cf6453235d3..c195b623c11 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/BadgeApiTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/BadgeApiTest.java @@ -44,11 +44,15 @@ public class BadgeApiTest extends ControllerContainerTest { .runJob(JobType.productionApSoutheast1) .failDeployment(JobType.testApSoutheast1); application.submit(applicationPackage) - .runJob(JobType.systemTest) + .failTests(JobType.systemTest, true) .runJob(JobType.stagingTest); for (int i = 0; i < 31; i++) - application.failDeployment(JobType.productionUsWest1); + if ((i & 1) == 0) + application.failDeployment(JobType.productionUsWest1); + else + application.triggerJobs().abortJob(JobType.productionUsWest1); application.triggerJobs(); + tester.controller().applications().deploymentTrigger().reTrigger(application.instanceId(), JobType.systemTest, "reason"); tester.controller().applications().deploymentTrigger().reTrigger(application.instanceId(), JobType.testEuWest1, "reason"); tester.assertResponse(authenticatedRequest("http://localhost:8080/badge/v1/tenant/application/default"), diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/responses/history.svg b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/responses/history.svg index 0e30796bae2..c0566ade33a 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/responses/history.svg +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/responses/history.svg @@ -33,9 +33,15 @@ <animate attributeName='x1' values='-110%;150%;20%;-110%' dur='6s' repeatCount='indefinite' /> <animate attributeName='x2' values='-10%;250%;120%;-10%' dur='6s' repeatCount='indefinite' /> </linearGradient> + <linearGradient id='run-on-warning' x1='40%' x2='80%' y2='0%'> + <stop offset='0' stop-color='#ab83ff' /> + <stop offset='1' stop-color='#bd890b' /> + <animate attributeName='x1' values='-110%;150%;20%;-110%' dur='6s' repeatCount='indefinite' /> + <animate attributeName='x2' values='-10%;250%;120%;-10%' dur='6s' repeatCount='indefinite' /> + </linearGradient> <linearGradient id='run-on-success' x1='40%' x2='80%' y2='0%'> <stop offset='0' stop-color='#ab83ff' /> - <stop offset='1' stop-color='#00f244' /> + <stop offset='1' stop-color='#00f844' /> <animate attributeName='x1' values='-110%;150%;20%;-110%' dur='6s' repeatCount='indefinite' /> <animate attributeName='x2' values='-10%;250%;120%;-10%' dur='6s' repeatCount='indefinite' /> </linearGradient> @@ -44,100 +50,100 @@ </clipPath> <g clip-path='url(#rounded)'> <rect x='653.26809109179' rx='3' width='8' height='20' fill='url(#shadow)'/> - <rect x='646.1879570885093' rx='3' width='13.080134003280707' height='20' fill='#00f244'/> + <rect x='646.1879570885093' rx='3' width='13.080134003280707' height='20' fill='#00f844'/> <rect x='646.1879570885093' rx='3' width='13.080134003280707' height='20' fill='url(#shade)'/> <rect x='646.4043039211865' rx='3' width='8' height='20' fill='url(#shadow)'/> - <rect x='639.1078230852286' rx='3' width='13.296480835957981' height='20' fill='#00f244'/> + <rect x='639.1078230852286' rx='3' width='13.296480835957981' height='20' fill='#00f844'/> <rect x='639.1078230852286' rx='3' width='13.296480835957981' height='20' fill='url(#shade)'/> <rect x='639.3307808029524' rx='3' width='8' height='20' fill='url(#shadow)'/> <rect x='631.8113422492706' rx='3' width='13.519438553681752' height='20' fill='#bf103c'/> <rect x='631.8113422492706' rx='3' width='13.519438553681752' height='20' fill='url(#shade)'/> <rect x='632.0411128600877' rx='3' width='8' height='20' fill='url(#shadow)'/> - <rect x='624.2919036955889' rx='3' width='13.749209164498811' height='20' fill='#bf103c'/> + <rect x='624.2919036955889' rx='3' width='13.749209164498811' height='20' fill='#bd890b'/> <rect x='624.2919036955889' rx='3' width='13.749209164498811' height='20' fill='url(#shade)'/> <rect x='624.5286953802824' rx='3' width='8' height='20' fill='url(#shadow)'/> <rect x='616.5426945310901' rx='3' width='13.986000849192376' height='20' fill='#bf103c'/> <rect x='616.5426945310901' rx='3' width='13.986000849192376' height='20' fill='url(#shade)'/> <rect x='616.7867218317995' rx='3' width='8' height='20' fill='url(#shadow)'/> - <rect x='608.5566936818977' rx='3' width='14.230028149901685' height='20' fill='#bf103c'/> + <rect x='608.5566936818977' rx='3' width='14.230028149901685' height='20' fill='#bd890b'/> <rect x='608.5566936818977' rx='3' width='14.230028149901685' height='20' fill='url(#shade)'/> <rect x='608.8081776965013' rx='3' width='8' height='20' fill='url(#shadow)'/> <rect x='600.326665531996' rx='3' width='14.48151216450522' height='20' fill='#bf103c'/> <rect x='600.326665531996' rx='3' width='14.48151216450522' height='20' fill='url(#shade)'/> <rect x='600.5858341144344' rx='3' width='8' height='20' fill='url(#shadow)'/> - <rect x='591.8451533674908' rx='3' width='14.740680746943664' height='20' fill='#bf103c'/> + <rect x='591.8451533674908' rx='3' width='14.740680746943664' height='20' fill='#bd890b'/> <rect x='591.8451533674908' rx='3' width='14.740680746943664' height='20' fill='url(#shade)'/> <rect x='592.1122413342111' rx='3' width='8' height='20' fill='url(#shadow)'/> <rect x='583.1044726205471' rx='3' width='15.007768713664104' height='20' fill='#bf103c'/> <rect x='583.1044726205471' rx='3' width='15.007768713664104' height='20' fill='url(#shade)'/> <rect x='583.3797219632555' rx='3' width='8' height='20' fill='url(#shadow)'/> - <rect x='574.096703906883' rx='3' width='15.283018056372542' height='20' fill='#bf103c'/> + <rect x='574.096703906883' rx='3' width='15.283018056372542' height='20' fill='#bd890b'/> <rect x='574.096703906883' rx='3' width='15.283018056372542' height='20' fill='url(#shade)'/> <rect x='574.380364011798' rx='3' width='8' height='20' fill='url(#shadow)'/> <rect x='564.8136858505105' rx='3' width='15.566678161287442' height='20' fill='#bf103c'/> <rect x='564.8136858505105' rx='3' width='15.566678161287442' height='20' fill='url(#shade)'/> <rect x='565.1060137243161' rx='3' width='8' height='20' fill='url(#shadow)'/> - <rect x='555.2470076892231' rx='3' width='15.859006035092985' height='20' fill='#bf103c'/> + <rect x='555.2470076892231' rx='3' width='15.859006035092985' height='20' fill='#bd890b'/> <rect x='555.2470076892231' rx='3' width='15.859006035092985' height='20' fill='url(#shade)'/> <rect x='555.5482681919269' rx='3' width='8' height='20' fill='url(#shadow)'/> <rect x='545.3880016541301' rx='3' width='16.16026653779677' height='20' fill='#bf103c'/> <rect x='545.3880016541301' rx='3' width='16.16026653779677' height='20' fill='url(#shade)'/> <rect x='545.6984677390362' rx='3' width='8' height='20' fill='url(#shadow)'/> - <rect x='535.2277351163333' rx='3' width='16.470732622702883' height='20' fill='#bf103c'/> + <rect x='535.2277351163333' rx='3' width='16.470732622702883' height='20' fill='#bd890b'/> <rect x='535.2277351163333' rx='3' width='16.470732622702883' height='20' fill='url(#shade)'/> <rect x='535.5476880773482' rx='3' width='8' height='20' fill='url(#shadow)'/> <rect x='524.7570024936304' rx='3' width='16.790685583717845' height='20' fill='#bf103c'/> <rect x='524.7570024936304' rx='3' width='16.790685583717845' height='20' fill='url(#shade)'/> <rect x='525.0867322201259' rx='3' width='8' height='20' fill='url(#shadow)'/> - <rect x='513.9663169099125' rx='3' width='17.12041531021341' height='20' fill='#bf103c'/> + <rect x='513.9663169099125' rx='3' width='17.12041531021341' height='20' fill='#bd890b'/> <rect x='513.9663169099125' rx='3' width='17.12041531021341' height='20' fill='url(#shade)'/> <rect x='514.3061221493763' rx='3' width='8' height='20' fill='url(#shadow)'/> <rect x='502.84590159969906' rx='3' width='17.460220549677203' height='20' fill='#bf103c'/> <rect x='502.84590159969906' rx='3' width='17.460220549677203' height='20' fill='url(#shade)'/> <rect x='503.196090228411' rx='3' width='8' height='20' fill='url(#shadow)'/> - <rect x='491.38568105002184' rx='3' width='17.810409178389147' height='20' fill='#bf103c'/> + <rect x='491.38568105002184' rx='3' width='17.810409178389147' height='20' fill='#bd890b'/> <rect x='491.38568105002184' rx='3' width='17.810409178389147' height='20' fill='url(#shade)'/> <rect x='491.7465703520016' rx='3' width='8' height='20' fill='url(#shadow)'/> <rect x='479.5752718716327' rx='3' width='18.171298480368904' height='20' fill='#bf103c'/> <rect x='479.5752718716327' rx='3' width='18.171298480368904' height='20' fill='url(#shade)'/> <rect x='479.9471888261109' rx='3' width='8' height='20' fill='url(#shadow)'/> - <rect x='467.40397339126383' rx='3' width='18.543215434847077' height='20' fill='#bf103c'/> + <rect x='467.40397339126383' rx='3' width='18.543215434847077' height='20' fill='#bd890b'/> <rect x='467.40397339126383' rx='3' width='18.543215434847077' height='20' fill='url(#shade)'/> <rect x='467.7872549689374' rx='3' width='8' height='20' fill='url(#shadow)'/> <rect x='454.86075795641676' rx='3' width='18.92649701252067' height='20' fill='#bf103c'/> <rect x='454.86075795641676' rx='3' width='18.92649701252067' height='20' fill='url(#shade)'/> <rect x='455.25575142475725' rx='3' width='8' height='20' fill='url(#shadow)'/> - <rect x='441.9342609438961' rx='3' width='19.32149048086113' height='20' fill='#bf103c'/> + <rect x='441.9342609438961' rx='3' width='19.32149048086113' height='20' fill='#bd890b'/> <rect x='441.9342609438961' rx='3' width='19.32149048086113' height='20' fill='url(#shade)'/> <rect x='442.3413241817867' rx='3' width='8' height='20' fill='url(#shadow)'/> <rect x='428.61277046303496' rx='3' width='19.728553718751726' height='20' fill='#bf103c'/> <rect x='428.61277046303496' rx='3' width='19.728553718751726' height='20' fill='url(#shade)'/> <rect x='429.03227228502243' rx='3' width='8' height='20' fill='url(#shadow)'/> - <rect x='414.8842167442832' rx='3' width='20.148055540739207' height='20' fill='#bf103c'/> + <rect x='414.8842167442832' rx='3' width='20.148055540739207' height='20' fill='#bd890b'/> <rect x='414.8842167442832' rx='3' width='20.148055540739207' height='20' fill='url(#shade)'/> <rect x='415.31653723473755' rx='3' width='8' height='20' fill='url(#shadow)'/> <rect x='400.736161203544' rx='3' width='20.58037603119359' height='20' fill='#bf103c'/> <rect x='400.736161203544' rx='3' width='20.58037603119359' height='20' fill='url(#shade)'/> <rect x='401.1816920610293' rx='3' width='8' height='20' fill='url(#shadow)'/> - <rect x='386.1557851723504' rx='3' width='21.025906888678875' height='20' fill='#bf103c'/> + <rect x='386.1557851723504' rx='3' width='21.025906888678875' height='20' fill='#bd890b'/> <rect x='386.1557851723504' rx='3' width='21.025906888678875' height='20' fill='url(#shade)'/> <rect x='386.61493006451815' rx='3' width='8' height='20' fill='url(#shadow)'/> <rect x='371.12987828367153' rx='3' width='21.485051780846597' height='20' fill='#bf103c'/> <rect x='371.12987828367153' rx='3' width='21.485051780846597' height='20' fill='url(#shade)'/> <rect x='371.60305321299876' rx='3' width='8' height='20' fill='url(#shadow)'/> - <rect x='355.6448265028249' rx='3' width='21.958226710173836' height='20' fill='#bf103c'/> + <rect x='355.6448265028249' rx='3' width='21.958226710173836' height='20' fill='#bd890b'/> <rect x='355.6448265028249' rx='3' width='21.958226710173836' height='20' fill='url(#shade)'/> <rect x='356.13246018352817' rx='3' width='8' height='20' fill='url(#shadow)'/> <rect x='339.68659979265107' rx='3' width='22.445860390877083' height='20' fill='#bf103c'/> <rect x='339.68659979265107' rx='3' width='22.445860390877083' height='20' fill='url(#shade)'/> <rect x='340.18913403911733' rx='3' width='8' height='20' fill='url(#shadow)'/> - <rect x='323.24073940177396' rx='3' width='22.948394637343355' height='20' fill='#bf103c'/> + <rect x='323.24073940177396' rx='3' width='22.948394637343355' height='20' fill='#bd890b'/> <rect x='323.24073940177396' rx='3' width='22.948394637343355' height='20' fill='url(#shade)'/> <rect x='323.7586295288612' rx='3' width='8' height='20' fill='url(#shadow)'/> <rect x='306.2923447644306' rx='3' width='23.466284764430597' height='20' fill='#bf103c'/> <rect x='306.2923447644306' rx='3' width='23.466284764430597' height='20' fill='url(#shade)'/> <rect x='306.82606' rx='3' width='8' height='20' fill='url(#shadow)'/> - <rect x='288.82606' rx='3' width='24.0' height='20' fill='#bf103c'/> + <rect x='288.82606' rx='3' width='24.0' height='20' fill='#bd890b'/> <rect x='288.82606' rx='3' width='24.0' height='20' fill='url(#shade)'/> <rect x='288.82606' rx='3' width='8' height='20' fill='url(#shadow)'/> <rect x='163.18729000000002' rx='3' width='131.63876999999997' height='20' fill='url(#run-on-failure)'/> diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/responses/history2.svg b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/responses/history2.svg index 73d65b08b69..e527fa8d80f 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/responses/history2.svg +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/responses/history2.svg @@ -33,9 +33,15 @@ <animate attributeName='x1' values='-110%;150%;20%;-110%' dur='6s' repeatCount='indefinite' /> <animate attributeName='x2' values='-10%;250%;120%;-10%' dur='6s' repeatCount='indefinite' /> </linearGradient> + <linearGradient id='run-on-warning' x1='40%' x2='80%' y2='0%'> + <stop offset='0' stop-color='#ab83ff' /> + <stop offset='1' stop-color='#bd890b' /> + <animate attributeName='x1' values='-110%;150%;20%;-110%' dur='6s' repeatCount='indefinite' /> + <animate attributeName='x2' values='-10%;250%;120%;-10%' dur='6s' repeatCount='indefinite' /> + </linearGradient> <linearGradient id='run-on-success' x1='40%' x2='80%' y2='0%'> <stop offset='0' stop-color='#ab83ff' /> - <stop offset='1' stop-color='#00f244' /> + <stop offset='1' stop-color='#00f844' /> <animate attributeName='x1' values='-110%;150%;20%;-110%' dur='6s' repeatCount='indefinite' /> <animate attributeName='x2' values='-10%;250%;120%;-10%' dur='6s' repeatCount='indefinite' /> </linearGradient> @@ -44,103 +50,103 @@ </clipPath> <g clip-path='url(#rounded)'> <rect x='653.26809109179' rx='3' width='8' height='20' fill='url(#shadow)'/> - <rect x='646.1879570885093' rx='3' width='13.080134003280707' height='20' fill='#00f244'/> + <rect x='646.1879570885093' rx='3' width='13.080134003280707' height='20' fill='#00f844'/> <rect x='646.1879570885093' rx='3' width='13.080134003280707' height='20' fill='url(#shade)'/> <rect x='646.4043039211865' rx='3' width='8' height='20' fill='url(#shadow)'/> <rect x='639.1078230852286' rx='3' width='13.296480835957981' height='20' fill='#bf103c'/> <rect x='639.1078230852286' rx='3' width='13.296480835957981' height='20' fill='url(#shade)'/> <rect x='639.3307808029524' rx='3' width='8' height='20' fill='url(#shadow)'/> - <rect x='631.8113422492706' rx='3' width='13.519438553681752' height='20' fill='#bf103c'/> + <rect x='631.8113422492706' rx='3' width='13.519438553681752' height='20' fill='#bd890b'/> <rect x='631.8113422492706' rx='3' width='13.519438553681752' height='20' fill='url(#shade)'/> <rect x='632.0411128600877' rx='3' width='8' height='20' fill='url(#shadow)'/> <rect x='624.2919036955889' rx='3' width='13.749209164498811' height='20' fill='#bf103c'/> <rect x='624.2919036955889' rx='3' width='13.749209164498811' height='20' fill='url(#shade)'/> <rect x='624.5286953802824' rx='3' width='8' height='20' fill='url(#shadow)'/> - <rect x='616.5426945310901' rx='3' width='13.986000849192376' height='20' fill='#bf103c'/> + <rect x='616.5426945310901' rx='3' width='13.986000849192376' height='20' fill='#bd890b'/> <rect x='616.5426945310901' rx='3' width='13.986000849192376' height='20' fill='url(#shade)'/> <rect x='616.7867218317995' rx='3' width='8' height='20' fill='url(#shadow)'/> <rect x='608.5566936818977' rx='3' width='14.230028149901685' height='20' fill='#bf103c'/> <rect x='608.5566936818977' rx='3' width='14.230028149901685' height='20' fill='url(#shade)'/> <rect x='608.8081776965013' rx='3' width='8' height='20' fill='url(#shadow)'/> - <rect x='600.326665531996' rx='3' width='14.48151216450522' height='20' fill='#bf103c'/> + <rect x='600.326665531996' rx='3' width='14.48151216450522' height='20' fill='#bd890b'/> <rect x='600.326665531996' rx='3' width='14.48151216450522' height='20' fill='url(#shade)'/> <rect x='600.5858341144344' rx='3' width='8' height='20' fill='url(#shadow)'/> <rect x='591.8451533674908' rx='3' width='14.740680746943664' height='20' fill='#bf103c'/> <rect x='591.8451533674908' rx='3' width='14.740680746943664' height='20' fill='url(#shade)'/> <rect x='592.1122413342111' rx='3' width='8' height='20' fill='url(#shadow)'/> - <rect x='583.1044726205471' rx='3' width='15.007768713664104' height='20' fill='#bf103c'/> + <rect x='583.1044726205471' rx='3' width='15.007768713664104' height='20' fill='#bd890b'/> <rect x='583.1044726205471' rx='3' width='15.007768713664104' height='20' fill='url(#shade)'/> <rect x='583.3797219632555' rx='3' width='8' height='20' fill='url(#shadow)'/> <rect x='574.096703906883' rx='3' width='15.283018056372542' height='20' fill='#bf103c'/> <rect x='574.096703906883' rx='3' width='15.283018056372542' height='20' fill='url(#shade)'/> <rect x='574.380364011798' rx='3' width='8' height='20' fill='url(#shadow)'/> - <rect x='564.8136858505105' rx='3' width='15.566678161287442' height='20' fill='#bf103c'/> + <rect x='564.8136858505105' rx='3' width='15.566678161287442' height='20' fill='#bd890b'/> <rect x='564.8136858505105' rx='3' width='15.566678161287442' height='20' fill='url(#shade)'/> <rect x='565.1060137243161' rx='3' width='8' height='20' fill='url(#shadow)'/> <rect x='555.2470076892231' rx='3' width='15.859006035092985' height='20' fill='#bf103c'/> <rect x='555.2470076892231' rx='3' width='15.859006035092985' height='20' fill='url(#shade)'/> <rect x='555.5482681919269' rx='3' width='8' height='20' fill='url(#shadow)'/> - <rect x='545.3880016541301' rx='3' width='16.16026653779677' height='20' fill='#bf103c'/> + <rect x='545.3880016541301' rx='3' width='16.16026653779677' height='20' fill='#bd890b'/> <rect x='545.3880016541301' rx='3' width='16.16026653779677' height='20' fill='url(#shade)'/> <rect x='545.6984677390362' rx='3' width='8' height='20' fill='url(#shadow)'/> <rect x='535.2277351163333' rx='3' width='16.470732622702883' height='20' fill='#bf103c'/> <rect x='535.2277351163333' rx='3' width='16.470732622702883' height='20' fill='url(#shade)'/> <rect x='535.5476880773482' rx='3' width='8' height='20' fill='url(#shadow)'/> - <rect x='524.7570024936304' rx='3' width='16.790685583717845' height='20' fill='#bf103c'/> + <rect x='524.7570024936304' rx='3' width='16.790685583717845' height='20' fill='#bd890b'/> <rect x='524.7570024936304' rx='3' width='16.790685583717845' height='20' fill='url(#shade)'/> <rect x='525.0867322201259' rx='3' width='8' height='20' fill='url(#shadow)'/> <rect x='513.9663169099125' rx='3' width='17.12041531021341' height='20' fill='#bf103c'/> <rect x='513.9663169099125' rx='3' width='17.12041531021341' height='20' fill='url(#shade)'/> <rect x='514.3061221493763' rx='3' width='8' height='20' fill='url(#shadow)'/> - <rect x='502.84590159969906' rx='3' width='17.460220549677203' height='20' fill='#bf103c'/> + <rect x='502.84590159969906' rx='3' width='17.460220549677203' height='20' fill='#bd890b'/> <rect x='502.84590159969906' rx='3' width='17.460220549677203' height='20' fill='url(#shade)'/> <rect x='503.196090228411' rx='3' width='8' height='20' fill='url(#shadow)'/> <rect x='491.38568105002184' rx='3' width='17.810409178389147' height='20' fill='#bf103c'/> <rect x='491.38568105002184' rx='3' width='17.810409178389147' height='20' fill='url(#shade)'/> <rect x='491.7465703520016' rx='3' width='8' height='20' fill='url(#shadow)'/> - <rect x='479.5752718716327' rx='3' width='18.171298480368904' height='20' fill='#bf103c'/> + <rect x='479.5752718716327' rx='3' width='18.171298480368904' height='20' fill='#bd890b'/> <rect x='479.5752718716327' rx='3' width='18.171298480368904' height='20' fill='url(#shade)'/> <rect x='479.9471888261109' rx='3' width='8' height='20' fill='url(#shadow)'/> <rect x='467.40397339126383' rx='3' width='18.543215434847077' height='20' fill='#bf103c'/> <rect x='467.40397339126383' rx='3' width='18.543215434847077' height='20' fill='url(#shade)'/> <rect x='467.7872549689374' rx='3' width='8' height='20' fill='url(#shadow)'/> - <rect x='454.86075795641676' rx='3' width='18.92649701252067' height='20' fill='#bf103c'/> + <rect x='454.86075795641676' rx='3' width='18.92649701252067' height='20' fill='#bd890b'/> <rect x='454.86075795641676' rx='3' width='18.92649701252067' height='20' fill='url(#shade)'/> <rect x='455.25575142475725' rx='3' width='8' height='20' fill='url(#shadow)'/> <rect x='441.9342609438961' rx='3' width='19.32149048086113' height='20' fill='#bf103c'/> <rect x='441.9342609438961' rx='3' width='19.32149048086113' height='20' fill='url(#shade)'/> <rect x='442.3413241817867' rx='3' width='8' height='20' fill='url(#shadow)'/> - <rect x='428.61277046303496' rx='3' width='19.728553718751726' height='20' fill='#bf103c'/> + <rect x='428.61277046303496' rx='3' width='19.728553718751726' height='20' fill='#bd890b'/> <rect x='428.61277046303496' rx='3' width='19.728553718751726' height='20' fill='url(#shade)'/> <rect x='429.03227228502243' rx='3' width='8' height='20' fill='url(#shadow)'/> <rect x='414.8842167442832' rx='3' width='20.148055540739207' height='20' fill='#bf103c'/> <rect x='414.8842167442832' rx='3' width='20.148055540739207' height='20' fill='url(#shade)'/> <rect x='415.31653723473755' rx='3' width='8' height='20' fill='url(#shadow)'/> - <rect x='400.736161203544' rx='3' width='20.58037603119359' height='20' fill='#bf103c'/> + <rect x='400.736161203544' rx='3' width='20.58037603119359' height='20' fill='#bd890b'/> <rect x='400.736161203544' rx='3' width='20.58037603119359' height='20' fill='url(#shade)'/> <rect x='401.1816920610293' rx='3' width='8' height='20' fill='url(#shadow)'/> <rect x='386.1557851723504' rx='3' width='21.025906888678875' height='20' fill='#bf103c'/> <rect x='386.1557851723504' rx='3' width='21.025906888678875' height='20' fill='url(#shade)'/> <rect x='386.61493006451815' rx='3' width='8' height='20' fill='url(#shadow)'/> - <rect x='371.12987828367153' rx='3' width='21.485051780846597' height='20' fill='#bf103c'/> + <rect x='371.12987828367153' rx='3' width='21.485051780846597' height='20' fill='#bd890b'/> <rect x='371.12987828367153' rx='3' width='21.485051780846597' height='20' fill='url(#shade)'/> <rect x='371.60305321299876' rx='3' width='8' height='20' fill='url(#shadow)'/> <rect x='355.6448265028249' rx='3' width='21.958226710173836' height='20' fill='#bf103c'/> <rect x='355.6448265028249' rx='3' width='21.958226710173836' height='20' fill='url(#shade)'/> <rect x='356.13246018352817' rx='3' width='8' height='20' fill='url(#shadow)'/> - <rect x='339.68659979265107' rx='3' width='22.445860390877083' height='20' fill='#bf103c'/> + <rect x='339.68659979265107' rx='3' width='22.445860390877083' height='20' fill='#bd890b'/> <rect x='339.68659979265107' rx='3' width='22.445860390877083' height='20' fill='url(#shade)'/> <rect x='340.18913403911733' rx='3' width='8' height='20' fill='url(#shadow)'/> <rect x='323.24073940177396' rx='3' width='22.948394637343355' height='20' fill='#bf103c'/> <rect x='323.24073940177396' rx='3' width='22.948394637343355' height='20' fill='url(#shade)'/> <rect x='323.7586295288612' rx='3' width='8' height='20' fill='url(#shadow)'/> - <rect x='306.2923447644306' rx='3' width='23.466284764430597' height='20' fill='#bf103c'/> + <rect x='306.2923447644306' rx='3' width='23.466284764430597' height='20' fill='#bd890b'/> <rect x='306.2923447644306' rx='3' width='23.466284764430597' height='20' fill='url(#shade)'/> <rect x='306.82606' rx='3' width='8' height='20' fill='url(#shadow)'/> <rect x='288.82606' rx='3' width='24.0' height='20' fill='#bf103c'/> <rect x='288.82606' rx='3' width='24.0' height='20' fill='url(#shade)'/> <rect x='288.82606' rx='3' width='8' height='20' fill='url(#shadow)'/> - <rect x='163.18729000000002' rx='3' width='131.63876999999997' height='20' fill='#00f244'/> + <rect x='163.18729000000002' rx='3' width='131.63876999999997' height='20' fill='#00f844'/> <rect x='163.18729000000002' rx='3' width='131.63876999999997' height='20' fill='url(#shade)'/> <rect width='169.18729000000002' height='20' fill='#404040'/> <rect x='-6.0' rx='3' width='175.18729000000002' height='20' fill='url(#shade)'/> diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/responses/overview.svg b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/responses/overview.svg index dde2b740e37..a0005ed6d76 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/responses/overview.svg +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/responses/overview.svg @@ -33,9 +33,15 @@ <animate attributeName='x1' values='-110%;150%;20%;-110%' dur='6s' repeatCount='indefinite' /> <animate attributeName='x2' values='-10%;250%;120%;-10%' dur='6s' repeatCount='indefinite' /> </linearGradient> + <linearGradient id='run-on-warning' x1='40%' x2='80%' y2='0%'> + <stop offset='0' stop-color='#ab83ff' /> + <stop offset='1' stop-color='#bd890b' /> + <animate attributeName='x1' values='-110%;150%;20%;-110%' dur='6s' repeatCount='indefinite' /> + <animate attributeName='x2' values='-10%;250%;120%;-10%' dur='6s' repeatCount='indefinite' /> + </linearGradient> <linearGradient id='run-on-success' x1='40%' x2='80%' y2='0%'> <stop offset='0' stop-color='#ab83ff' /> - <stop offset='1' stop-color='#00f244' /> + <stop offset='1' stop-color='#00f844' /> <animate attributeName='x1' values='-110%;150%;20%;-110%' dur='6s' repeatCount='indefinite' /> <animate attributeName='x2' values='-10%;250%;120%;-10%' dur='6s' repeatCount='indefinite' /> </linearGradient> @@ -45,21 +51,21 @@ <g clip-path='url(#rounded)'> <rect x='757.7809900000001' rx='3' width='8' height='20' fill='url(#shadow)'/> <rect x='725.59036' rx='3' width='38.19063' height='20' fill='url(#run-on-success)'/> - <polygon points='635.8470950000001 0 635.8470950000001 20 734.59036 20 742.59036 0' fill='#00f244'/> + <polygon points='635.8470950000001 0 635.8470950000001 20 734.59036 20 742.59036 0' fill='#00f844'/> <rect x='635.8470950000001' rx='3' width='131.74345499999998' height='20' fill='url(#shade)'/> <rect x='635.8470950000001' rx='3' width='8' height='20' fill='url(#shadow)'/> <rect x='603.656465' rx='3' width='38.19063' height='20' fill='#bf103c'/> - <polygon points='486.981225 0 486.981225 20 612.656465 20 620.656465 0' fill='#00f244'/> + <polygon points='486.981225 0 486.981225 20 612.656465 20 620.656465 0' fill='#00f844'/> <rect x='486.981225' rx='3' width='158.67543' height='20' fill='url(#shade)'/> <rect x='486.981225' rx='3' width='8' height='20' fill='url(#shadow)'/> <rect x='348.865175' rx='3' width='144.11604999999997' height='20' fill='url(#run-on-success)'/> <rect x='358.865175' rx='3' width='134.11604999999997' height='20' fill='url(#shade)'/> <rect x='358.865175' rx='3' width='8' height='20' fill='url(#shadow)'/> - <rect x='326.674545' rx='3' width='38.19063' height='20' fill='#00f244'/> + <rect x='326.674545' rx='3' width='38.19063' height='20' fill='#00f844'/> <polygon points='237.71563000000003 0 237.71563000000003 20 335.674545 20 343.674545 0' fill='url(#run-on-failure)'/> <rect x='237.71563000000003' rx='3' width='130.959105' height='20' fill='url(#shade)'/> <rect x='237.71563000000003' rx='3' width='8' height='20' fill='url(#shadow)'/> - <rect x='153.18729000000002' rx='3' width='90.52834000000001' height='20' fill='#00f244'/> + <rect x='153.18729000000002' rx='3' width='90.52834000000001' height='20' fill='url(#run-on-warning)'/> <rect x='163.18729000000002' rx='3' width='80.52834000000001' height='20' fill='url(#shade)'/> <rect width='169.18729000000002' height='20' fill='#404040'/> <rect x='-6.0' rx='3' width='175.18729000000002' height='20' fill='url(#shade)'/> diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/responses/single-done.svg b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/responses/single-done.svg index 3bcbed97499..0bdaa3f30ad 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/responses/single-done.svg +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/responses/single-done.svg @@ -33,9 +33,15 @@ <animate attributeName='x1' values='-110%;150%;20%;-110%' dur='6s' repeatCount='indefinite' /> <animate attributeName='x2' values='-10%;250%;120%;-10%' dur='6s' repeatCount='indefinite' /> </linearGradient> + <linearGradient id='run-on-warning' x1='40%' x2='80%' y2='0%'> + <stop offset='0' stop-color='#ab83ff' /> + <stop offset='1' stop-color='#bd890b' /> + <animate attributeName='x1' values='-110%;150%;20%;-110%' dur='6s' repeatCount='indefinite' /> + <animate attributeName='x2' values='-10%;250%;120%;-10%' dur='6s' repeatCount='indefinite' /> + </linearGradient> <linearGradient id='run-on-success' x1='40%' x2='80%' y2='0%'> <stop offset='0' stop-color='#ab83ff' /> - <stop offset='1' stop-color='#00f244' /> + <stop offset='1' stop-color='#00f844' /> <animate attributeName='x1' values='-110%;150%;20%;-110%' dur='6s' repeatCount='indefinite' /> <animate attributeName='x2' values='-10%;250%;120%;-10%' dur='6s' repeatCount='indefinite' /> </linearGradient> @@ -44,7 +50,7 @@ </clipPath> <g clip-path='url(#rounded)'> <rect x='288.82606' rx='3' width='8' height='20' fill='url(#shadow)'/> - <rect x='163.18729000000002' rx='3' width='131.63876999999997' height='20' fill='#00f244'/> + <rect x='163.18729000000002' rx='3' width='131.63876999999997' height='20' fill='#00f844'/> <rect x='163.18729000000002' rx='3' width='131.63876999999997' height='20' fill='url(#shade)'/> <rect width='169.18729000000002' height='20' fill='#404040'/> <rect x='-6.0' rx='3' width='175.18729000000002' height='20' fill='url(#shade)'/> diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/responses/single-running.svg b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/responses/single-running.svg index 27e967f8e46..1a38228e75d 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/responses/single-running.svg +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/deployment/responses/single-running.svg @@ -33,9 +33,15 @@ <animate attributeName='x1' values='-110%;150%;20%;-110%' dur='6s' repeatCount='indefinite' /> <animate attributeName='x2' values='-10%;250%;120%;-10%' dur='6s' repeatCount='indefinite' /> </linearGradient> + <linearGradient id='run-on-warning' x1='40%' x2='80%' y2='0%'> + <stop offset='0' stop-color='#ab83ff' /> + <stop offset='1' stop-color='#bd890b' /> + <animate attributeName='x1' values='-110%;150%;20%;-110%' dur='6s' repeatCount='indefinite' /> + <animate attributeName='x2' values='-10%;250%;120%;-10%' dur='6s' repeatCount='indefinite' /> + </linearGradient> <linearGradient id='run-on-success' x1='40%' x2='80%' y2='0%'> <stop offset='0' stop-color='#ab83ff' /> - <stop offset='1' stop-color='#00f244' /> + <stop offset='1' stop-color='#00f844' /> <animate attributeName='x1' values='-110%;150%;20%;-110%' dur='6s' repeatCount='indefinite' /> <animate attributeName='x2' values='-10%;250%;120%;-10%' dur='6s' repeatCount='indefinite' /> </linearGradient> diff --git a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java index aa019ae5b39..597774e3ab9 100644 --- a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java +++ b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java @@ -369,13 +369,6 @@ public class Flags { "Takes effect at redeployment", ZONE_ID, APPLICATION_ID); - public static final UnboundBooleanFlag ALLOW_NO_TESTS = defineFeatureFlag( - "allow-no-tests", false, - List.of("jonmv"), "2022-02-28", "2022-06-25", - "Whether test jobs without any tests run are acceptable", - "Takes effect immediately", - TENANT_ID); - public static final UnboundBooleanFlag MERGE_GROUPING_RESULT_IN_SEARCH_INVOKER = defineFeatureFlag( "merge-grouping-result-in-search-invoker", false, List.of("bjorncs", "baldersheim"), "2022-02-23", "2022-08-01", diff --git a/hosted-api/src/main/java/ai/vespa/hosted/api/ControllerHttpClient.java b/hosted-api/src/main/java/ai/vespa/hosted/api/ControllerHttpClient.java index 2860b8878b7..67cd7e1477e 100644 --- a/hosted-api/src/main/java/ai/vespa/hosted/api/ControllerHttpClient.java +++ b/hosted-api/src/main/java/ai/vespa/hosted/api/ControllerHttpClient.java @@ -418,6 +418,8 @@ public abstract class ControllerHttpClient { submission.sourceUrl().ifPresent(url -> rootObject.setString("sourceUrl", url)); submission.authorEmail().ifPresent(email -> rootObject.setString("authorEmail", email)); submission.projectId().ifPresent(projectId -> rootObject.setLong("projectId", projectId)); + submission.risk().ifPresent(risk -> rootObject.setLong("risk", risk)); + submission.description().ifPresent(description -> rootObject.setString("description", description)); return toJson(slime); } diff --git a/hosted-api/src/main/java/ai/vespa/hosted/api/Submission.java b/hosted-api/src/main/java/ai/vespa/hosted/api/Submission.java index d3ebd715da8..173b4946d5a 100644 --- a/hosted-api/src/main/java/ai/vespa/hosted/api/Submission.java +++ b/hosted-api/src/main/java/ai/vespa/hosted/api/Submission.java @@ -20,10 +20,13 @@ public class Submission { private final Path applicationZip; private final Path applicationTestZip; private final Optional<Long> projectId; + private final Optional<Integer> risk; + private final Optional<String> description; public Submission(Optional<String> repository, Optional<String> branch, Optional<String> commit, Optional<String> sourceUrl, Optional<String> authorEmail, - Path applicationZip, Path applicationTestZip, Optional<Long> projectId) { + Path applicationZip, Path applicationTestZip, Optional<Long> projectId, + Optional<Integer> risk, Optional<String> description) { this.repository = repository; this.branch = branch; this.commit = commit; @@ -32,6 +35,8 @@ public class Submission { this.applicationZip = applicationZip; this.applicationTestZip = applicationTestZip; this.projectId = projectId; + this.risk = risk; + this.description = description; } public Optional<String> repository() { return repository; } @@ -42,5 +47,7 @@ public class Submission { public Path applicationZip() { return applicationZip; } public Path applicationTestZip() { return applicationTestZip; } public Optional<Long> projectId() { return projectId; } + public Optional<Integer> risk() { return risk; } + public Optional<String> description() { return description; } } diff --git a/jdisc_core_test/test_bundles/cert-k-pkgs/src/main/java/com/yahoo/jdisc/bundle/k/CertificateK.java b/jdisc_core_test/test_bundles/cert-k-pkgs/src/main/java/com/yahoo/jdisc/bundle/k/CertificateK.java index ad93a7f322c..f8d85233602 100644 --- a/jdisc_core_test/test_bundles/cert-k-pkgs/src/main/java/com/yahoo/jdisc/bundle/k/CertificateK.java +++ b/jdisc_core_test/test_bundles/cert-k-pkgs/src/main/java/com/yahoo/jdisc/bundle/k/CertificateK.java @@ -4,7 +4,7 @@ package com.yahoo.jdisc.bundle.k; /** * @author Simon Thoresen Hult */ -@SuppressWarnings({ "UnusedDeclaration", "deprecation" }) +@SuppressWarnings({ "UnusedDeclaration", "deprecation", "removal" }) public class CertificateK { private final com.google.common.annotations.Beta beta = null; @@ -37,6 +37,7 @@ public class CertificateK { private final com.yahoo.yolean.trace.TraceNode traceNode = null; private final com.sun.security.auth.LdapPrincipal principal = null; private final com.sun.security.auth.module.JndiLoginModule jndiLoginModule = null; + private final java.security.cert.Certificate certificate = null; private final javax.accessibility.Accessible accessible = null; private final javax.annotation.PostConstruct postConstruct = null; private final javax.annotation.processing.FilerException filerException = null; @@ -82,7 +83,7 @@ public class CertificateK { private final javax.security.auth.login.AccountException accountException = null; private final javax.security.auth.spi.LoginModule loginModule = null; private final javax.security.auth.x500.X500Principal x500Principal = null; - private final javax.security.cert.Certificate certificate = null; + private final javax.security.cert.Certificate deprecatedCertificate = null; private final javax.security.sasl.AuthorizeCallback authorizeCallback = null; private final javax.sound.midi.ControllerEventListener controllerEventListener = null; private final javax.sound.midi.spi.MidiDeviceProvider midiDeviceProvider = null; diff --git a/parent/pom.xml b/parent/pom.xml index 763310a4a55..ecf257727d1 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -951,7 +951,7 @@ <maven-javadoc-plugin.version>3.3.1</maven-javadoc-plugin.version> <maven-plugin-tools.version>3.6.1</maven-plugin-tools.version> <maven-resources-plugin.version>3.2.0</maven-resources-plugin.version> - <maven-shade-plugin.version>3.2.4</maven-shade-plugin.version> + <maven-shade-plugin.version>3.3.0</maven-shade-plugin.version> <maven-site-plugin.version>3.9.1</maven-site-plugin.version> <maven-source-plugin.version>3.2.1</maven-source-plugin.version> <mockito.version>4.0.0</mockito.version> diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/IntegerBucketResultNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/IntegerBucketResultNode.java index 4bf33b15ffe..c5d66406fd5 100644 --- a/searchlib/src/main/java/com/yahoo/searchlib/expression/IntegerBucketResultNode.java +++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/IntegerBucketResultNode.java @@ -81,11 +81,11 @@ public class IntegerBucketResultNode extends BucketResultNode { return (classId - rhs.getClassId()); } IntegerBucketResultNode b = (IntegerBucketResultNode)rhs; - long diff = from - b.from; - if (diff == 0) { - diff = to - b.to; - } - return ((diff == 0) ? 0 : ((diff < 0) ? -1 : 1)); + if (from < b.from) return -1; + if (from > b.from) return 1; + if (to < b.to) return -1; + if (to > b.to) return 1; + return 0; } @Override diff --git a/searchlib/src/test/java/com/yahoo/searchlib/expression/IntegerBucketResultNodeTestCase.java b/searchlib/src/test/java/com/yahoo/searchlib/expression/IntegerBucketResultNodeTestCase.java index 00c0739ed9a..9c71625a85e 100644 --- a/searchlib/src/test/java/com/yahoo/searchlib/expression/IntegerBucketResultNodeTestCase.java +++ b/searchlib/src/test/java/com/yahoo/searchlib/expression/IntegerBucketResultNodeTestCase.java @@ -30,4 +30,17 @@ public class IntegerBucketResultNodeTestCase extends ResultNodeTest { assertTrue(dumpNode(bucket).contains("to: 10")); assertCorrectSerialization(bucket, new IntegerBucketResultNode()); } + + private IntegerBucketResultNode createNode(long from, long to) { + return new IntegerBucketResultNode(from, to); + } + + @Test + public void testCmp() { + assertOrder(createNode(Long.MIN_VALUE, 3), createNode(3, 9), createNode(9, Long.MAX_VALUE)); + assertOrder(createNode(6, 9), createNode(7, 9), createNode(8, 9)); + assertOrder(createNode(6, 7), createNode(6, 8), createNode(6, 9)); + assertOrder(createNode(6, 3), createNode(7, 2), createNode(8, 1)); + assertTrue(createNode(6, 8).onCmp(new NullResultNode()) != 0); + } } diff --git a/searchlib/src/vespa/searchlib/features/dotproductfeature.cpp b/searchlib/src/vespa/searchlib/features/dotproductfeature.cpp index 8f75b6ecc7d..1ee5fe90773 100644 --- a/searchlib/src/vespa/searchlib/features/dotproductfeature.cpp +++ b/searchlib/src/vespa/searchlib/features/dotproductfeature.cpp @@ -52,49 +52,6 @@ template class VectorBase<uint32_t, uint32_t, double>; template class IntegerVectorT<int64_t>; - -template <typename Vector, typename Buffer> -DotProductExecutorByCopy<Vector, Buffer>::DotProductExecutorByCopy(const IAttributeVector * attribute, const Vector & queryVector) : - FeatureExecutor(), - _attribute(attribute), - _queryVector(queryVector), - _end(_queryVector.getDimMap().end()), - _buffer(), - _backing() -{ - _buffer.allocate(_attribute->getMaxValueCount()); -} - -template <typename Vector, typename Buffer> -DotProductExecutorByCopy<Vector, Buffer>::DotProductExecutorByCopy(const IAttributeVector * attribute, std::unique_ptr<Vector> queryVector) : - FeatureExecutor(), - _attribute(attribute), - _queryVector(*queryVector), - _end(_queryVector.getDimMap().end()), - _buffer(), - _backing(std::move(queryVector)) -{ - _buffer.allocate(_attribute->getMaxValueCount()); -} - -template <typename Vector, typename Buffer> -DotProductExecutorByCopy<Vector, Buffer>::~DotProductExecutorByCopy() = default; - -template <typename Vector, typename Buffer> -void -DotProductExecutorByCopy<Vector, Buffer>::execute(uint32_t docId) -{ - feature_t val = 0; - _buffer.fill(*_attribute, docId); - for (size_t i = 0; i < _buffer.size(); ++i) { - auto itr = _queryVector.getDimMap().find(_buffer[i].getValue()); - if (itr != _end) { - val += _buffer[i].getWeight() * itr->second; - } - } - outputs().set_number(0, val); -} - StringVector::StringVector() = default; StringVector::~StringVector() = default; @@ -229,6 +186,7 @@ template <typename BaseType> class SingleDotProductByWeightedValueExecutor final : public fef::FeatureExecutor { public: using WeightedSetReadView = attribute::IWeightedSetReadView<BaseType>; + using StoredKeyType = std::conditional_t<std::is_same_v<BaseType,const char*>,vespalib::string,BaseType>; SingleDotProductByWeightedValueExecutor(const WeightedSetReadView * weighted_set_read_view, BaseType key, feature_t value) : _weighted_set_read_view(weighted_set_read_view), _key(key), @@ -247,7 +205,7 @@ public: } private: const WeightedSetReadView* _weighted_set_read_view; - BaseType _key; + StoredKeyType _key; feature_t _value; }; @@ -650,23 +608,38 @@ std::pair<T, feature_t> extractElem(const std::unique_ptr<dotproduct::wset::Inte return extractElem(*v, idx); } -template <typename A, typename V> +size_t extractSize(const dotproduct::wset::StringVector& v) { + return v.getVector().size(); +} + +std::pair<const char*, feature_t> extractElem(const dotproduct::wset::StringVector& v, size_t idx) { + const auto & pair = v.getVector()[idx]; + return std::pair<const char*, feature_t>(pair.first.c_str(), pair.second); +} + +size_t extractSize(const std::unique_ptr<dotproduct::wset::StringVector>& v) { + return extractSize(*v); +} + +std::pair<const char*, feature_t> extractElem(const std::unique_ptr<dotproduct::wset::StringVector>& v, size_t idx) { + return extractElem(*v, idx); +} + +template <typename T, typename V> FeatureExecutor & createForDirectWSetImpl(const IAttributeVector * attribute, V && vector, vespalib::Stash & stash) { using namespace dotproduct::wset; - using T = typename A::BaseType; - const A * iattr = dynamic_cast<const A *>(attribute); using VT = multivalue::WeightedValue<T>; auto weighted_set_read_view = make_multi_value_read_view<VT>(*attribute, stash); - if (!attribute->isImported() && (iattr != nullptr) && weighted_set_read_view != nullptr) { + if (weighted_set_read_view != nullptr) { if (extractSize(vector) == 1) { auto elem = extractElem(vector, 0ul); return stash.create<SingleDotProductByWeightedValueExecutor<T>>(weighted_set_read_view, elem.first, elem.second); } return stash.create<DotProductByWeightedSetReadViewExecutor<T>>(weighted_set_read_view, std::forward<V>(vector)); } - return stash.create<DotProductExecutorByCopy<IntegerVectorT<T>, WeightedIntegerContent>>(attribute, std::forward<V>(vector)); + return stash.create<SingleZeroValueExecutor>(); } template <typename T> @@ -676,7 +649,7 @@ createForDirectIntegerWSet(const IAttributeVector * attribute, const dotproduct: using namespace dotproduct::wset; return vector.empty() ? stash.create<SingleZeroValueExecutor>() - : createForDirectWSetImpl<IntegerAttributeTemplate<T>>(attribute, vector, stash); + : createForDirectWSetImpl<T>(attribute, vector, stash); } FeatureExecutor & @@ -727,14 +700,13 @@ createFromObject(const IAttributeVector * attribute, const fef::Anything & objec } return stash.create<DotProductExecutorByEnum>(weighted_set_enum_read_view, vector); } - return stash.create<DotProductExecutorByCopy<EnumVector, WeightedEnumContent>>(attribute, vector); } else { if (attribute->isStringType()) { const auto & vector = dynamic_cast<const StringVector &>(object); if (vector.empty()) { return stash.create<SingleZeroValueExecutor>(); } - return stash.create<DotProductExecutorByCopy<StringVector, WeightedConstCharContent>>(attribute, vector); + return createForDirectWSetImpl<const char*>(attribute, vector, stash); } else if (attribute->isIntegerType()) { if (attribute->getBasicType() == BasicType::INT32) { return createForDirectIntegerWSet<int32_t>(attribute, dynamic_cast<const IntegerVectorT<int32_t> &>(object), stash); @@ -800,7 +772,7 @@ createForDirectIntegerWSet(const IAttributeVector * attribute, const Property & vector->syncMap(); return vector->empty() ? stash.create<SingleZeroValueExecutor>() - : createForDirectWSetImpl<IntegerAttributeTemplate<T>>(attribute, std::move(vector), stash); + : createForDirectWSetImpl<T>(attribute, std::move(vector), stash); } FeatureExecutor & @@ -822,7 +794,6 @@ createTypedWsetExecutor(const IAttributeVector * attribute, const Property & pro } return stash.create<DotProductExecutorByEnum>(weighted_set_enum_read_view, std::move(vector)); } - return stash.create<DotProductExecutorByCopy<EnumVector, WeightedEnumContent>>(attribute, std::move(vector)); } else { if (attribute->isStringType()) { auto vector = std::make_unique<StringVector>(); @@ -831,7 +802,7 @@ createTypedWsetExecutor(const IAttributeVector * attribute, const Property & pro return stash.create<SingleZeroValueExecutor>(); } vector->syncMap(); - return stash.create<DotProductExecutorByCopy<StringVector, WeightedConstCharContent>>(attribute, std::move(vector)); + return createForDirectWSetImpl<const char*>(attribute, std::move(vector), stash); } else if (attribute->isIntegerType()) { if (attribute->getBasicType() == BasicType::INT32) { return createForDirectIntegerWSet<int32_t>(attribute, prop, stash); diff --git a/searchlib/src/vespa/searchlib/features/dotproductfeature.h b/searchlib/src/vespa/searchlib/features/dotproductfeature.h index 051a08025d8..ee0a85e689b 100644 --- a/searchlib/src/vespa/searchlib/features/dotproductfeature.h +++ b/searchlib/src/vespa/searchlib/features/dotproductfeature.h @@ -65,11 +65,14 @@ public: bool empty() const { return _vector.empty(); } }; +template <typename T> +using NumericVectorBaseT = VectorBase<T, T, feature_t>; + /** * Represents a vector where the dimensions are integers. **/ template<typename T> -class IntegerVectorT : public VectorBase<T, T, feature_t> { +class IntegerVectorT : public NumericVectorBaseT<T> { public: void insert(vespalib::stringref label, vespalib::stringref value) { this->_vector.emplace_back(util::strToNum<T>(label), util::strToNum<feature_t>(value)); @@ -82,10 +85,12 @@ extern template class IntegerVectorT<int64_t>; using IntegerVector = IntegerVectorT<int64_t>; +using StringVectorBase = VectorBase<vespalib::string, const char*, feature_t, ConstCharComparator>; + /** * Represents a vector where the dimensions are string values. **/ -class StringVector : public VectorBase<vespalib::string, const char *, feature_t, ConstCharComparator> { +class StringVector : public StringVectorBase { public: StringVector(); StringVector(StringVector &&) = default; @@ -121,7 +126,7 @@ template <typename BaseType> class DotProductExecutorBase : public fef::FeatureExecutor { public: using AT = multivalue::WeightedValue<BaseType>; - using V = VectorBase<BaseType, BaseType, feature_t>; + using V = std::conditional_t<std::is_same_v<BaseType,const char*>,StringVectorBase,NumericVectorBaseT<BaseType>>; private: const V & _queryVector; const typename V::HashMap::const_iterator _end; @@ -149,25 +154,6 @@ public: ~DotProductByWeightedSetReadViewExecutor(); }; - -/** - * Implements the executor for the dotproduct feature. - */ -template <typename Vector, typename Buffer> -class DotProductExecutorByCopy final : public fef::FeatureExecutor { -private: - const attribute::IAttributeVector * _attribute; - const Vector & _queryVector; - const typename Vector::HashMap::const_iterator _end; - Buffer _buffer; - std::unique_ptr<Vector> _backing; -public: - DotProductExecutorByCopy(const attribute::IAttributeVector * attribute, const Vector & queryVector); - DotProductExecutorByCopy(const attribute::IAttributeVector * attribute, std::unique_ptr<Vector> queryVector); - ~DotProductExecutorByCopy() override; - void execute(uint32_t docId) override; -}; - } namespace array { diff --git a/searchlib/src/vespa/searchlib/tensor/hnsw_index.cpp b/searchlib/src/vespa/searchlib/tensor/hnsw_index.cpp index 5ae26757b0d..8327db769d7 100644 --- a/searchlib/src/vespa/searchlib/tensor/hnsw_index.cpp +++ b/searchlib/src/vespa/searchlib/tensor/hnsw_index.cpp @@ -157,6 +157,7 @@ HnswIndex::shrink_if_needed(uint32_t docid, uint32_t level) uint32_t max_links = max_links_for_level(level); if (old_links.size() > max_links) { HnswCandidateVector neighbors; + neighbors.reserve(old_links.size()); for (uint32_t neighbor_docid : old_links) { double dist = calc_distance(docid, neighbor_docid); neighbors.emplace_back(neighbor_docid, dist); diff --git a/vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/SubmitMojo.java b/vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/SubmitMojo.java index d2bad008003..f6ef17bc1b8 100644 --- a/vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/SubmitMojo.java +++ b/vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/SubmitMojo.java @@ -6,8 +6,6 @@ import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import java.nio.file.Paths; -import java.util.Optional; -import java.util.OptionalLong; /** * Submits a Vespa application package and corresponding test jars to the hosted Vespa API. @@ -41,6 +39,12 @@ public class SubmitMojo extends AbstractVespaMojo { @Parameter(property = "projectId") private String projectId; + @Parameter(property = "risk") + private String risk; + + @Parameter(property = "description") + private String description; + @Override public void doExecute() { applicationZip = firstNonBlank(applicationZip, projectPathOf("target", "application.zip")).orElseThrow(); @@ -48,7 +52,8 @@ public class SubmitMojo extends AbstractVespaMojo { Submission submission = new Submission(optionalOf(repository), optionalOf(branch), optionalOf(commit), optionalOf(sourceUrl), optionalOf(authorEmail), Paths.get(applicationZip), Paths.get(applicationTestZip), - optionalOf(projectId, Long::parseLong)); + optionalOf(projectId, Long::parseLong), optionalOf(risk, Integer::parseInt), + optionalOf(description)); getLog().info(controller.submit(submission, id.tenant(), id.application())); } |