summaryrefslogtreecommitdiffstats
path: root/controller-server
diff options
context:
space:
mode:
Diffstat (limited to 'controller-server')
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Controller.java4
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/MailVerifier.java6
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java3
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/BillingReportMaintainer.java4
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/CloudTrialExpirer.java2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/notification/FormattedNotification.java3
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/notification/MailTemplating.java32
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/notification/NotificationFormatter.java86
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/notification/Notifier.java47
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializer.java5
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java1
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandler.java10
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandlerV2.java9
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/pricing/PricingApiHandler.java218
-rw-r--r--controller-server/src/main/resources/mail/mail-verification.vm6
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/MailVerifierTest.java2
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java8
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ZoneRegistryMock.java33
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/BillingReportMaintainerTest.java20
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/notification/NotificationFormatterTest.java19
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/notification/NotificationsDbTest.java5
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/notification/NotifierTest.java5
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializerTest.java2
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java4
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java12
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandlerTest.java10
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/pricing/PricingApiHandlerTest.java185
-rw-r--r--controller-server/src/test/resources/mail/notification.html8
-rw-r--r--controller-server/src/test/resources/mail/trial-expired.html8
-rw-r--r--controller-server/src/test/resources/mail/trial-expiring-immediately.html8
-rw-r--r--controller-server/src/test/resources/mail/trial-expiring-soon.html8
-rw-r--r--controller-server/src/test/resources/mail/trial-reminder.html8
-rw-r--r--controller-server/src/test/resources/mail/welcome.html8
33 files changed, 149 insertions, 640 deletions
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 bab86e7bfde..0b693bb9894 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
@@ -131,10 +131,10 @@ 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.zoneRegistry(), serviceRegistry.mailer(), flagSource);
+ notifier = new Notifier(curator, serviceRegistry.consoleUrls(), serviceRegistry.mailer(), flagSource);
notificationsDb = new NotificationsDb(this);
supportAccessControl = new SupportAccessControl(this);
- mailVerifier = new MailVerifier(serviceRegistry.zoneRegistry(), tenantController, serviceRegistry.mailer(), curator, clock);
+ mailVerifier = new MailVerifier(serviceRegistry.consoleUrls(), tenantController, serviceRegistry.mailer(), curator, clock);
dataplaneTokenService = new DataplaneTokenService(this);
// Record the version of this controller
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/MailVerifier.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/MailVerifier.java
index 7d8f0d260fc..9ff3206ee06 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/MailVerifier.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/MailVerifier.java
@@ -4,9 +4,9 @@ package com.yahoo.vespa.hosted.controller.application;
import com.yahoo.config.provision.TenantName;
import com.yahoo.vespa.hosted.controller.LockedTenant;
import com.yahoo.vespa.hosted.controller.TenantController;
+import com.yahoo.vespa.hosted.controller.api.integration.ConsoleUrls;
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.zone.ZoneRegistry;
import com.yahoo.vespa.hosted.controller.notification.MailTemplating;
import com.yahoo.vespa.hosted.controller.persistence.CuratorDb;
import com.yahoo.vespa.hosted.controller.tenant.CloudTenant;
@@ -35,12 +35,12 @@ public class MailVerifier {
private final Clock clock;
private final MailTemplating mailTemplating;
- public MailVerifier(ZoneRegistry zoneRegistry, TenantController tenantController, Mailer mailer, CuratorDb curatorDb, Clock clock) {
+ public MailVerifier(ConsoleUrls consoleUrls, TenantController tenantController, Mailer mailer, CuratorDb curatorDb, Clock clock) {
this.tenantController = tenantController;
this.mailer = mailer;
this.curatorDb = curatorDb;
this.clock = clock;
- this.mailTemplating = new MailTemplating(zoneRegistry);
+ this.mailTemplating = new MailTemplating(consoleUrls);
}
public PendingMailVerification sendMailVerification(TenantName tenantName, String email, PendingMailVerification.MailType mailType) {
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 c9719c3dd55..d62e477a51f 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
@@ -86,7 +86,6 @@ 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.RunStatus.testFailure;
import static com.yahoo.vespa.hosted.controller.deployment.Step.Status.succeeded;
-import static com.yahoo.vespa.hosted.controller.deployment.Step.Status.unfinished;
import static com.yahoo.vespa.hosted.controller.deployment.Step.copyVespaLogs;
import static com.yahoo.vespa.hosted.controller.deployment.Step.deactivateReal;
import static com.yahoo.vespa.hosted.controller.deployment.Step.deactivateTester;
@@ -128,7 +127,7 @@ public class InternalStepRunner implements StepRunner {
public InternalStepRunner(Controller controller) {
this.controller = controller;
this.testConfigSerializer = new TestConfigSerializer(controller.system());
- this.mails = new DeploymentFailureMails(controller.zoneRegistry());
+ this.mails = new DeploymentFailureMails(controller.serviceRegistry().consoleUrls());
this.timeouts = Timeouts.of(controller.system());
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/BillingReportMaintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/BillingReportMaintainer.java
index a12c341c1d1..7868c3fe611 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/BillingReportMaintainer.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/BillingReportMaintainer.java
@@ -5,7 +5,7 @@ import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.TenantName;
import com.yahoo.vespa.hosted.controller.Controller;
import com.yahoo.vespa.hosted.controller.LockedTenant;
-import com.yahoo.vespa.hosted.controller.api.integration.billing.Bill;
+import com.yahoo.vespa.hosted.controller.api.integration.billing.BillStatus;
import com.yahoo.vespa.hosted.controller.api.integration.billing.BillingController;
import com.yahoo.vespa.hosted.controller.api.integration.billing.BillingDatabaseClient;
import com.yahoo.vespa.hosted.controller.api.integration.billing.BillingReporter;
@@ -66,7 +66,7 @@ public class BillingReportMaintainer extends ControllerMaintainer {
InvoiceUpdate maintainInvoices() {
var billsNeedingMaintenance = databaseClient.readBills().stream()
.filter(bill -> bill.getExportedId().isPresent())
- .filter(exported -> ! exported.status().equals("ISSUED")) // TODO: This status does not yet exist.
+ .filter(exported -> exported.status() == BillStatus.OPEN)
.toList();
var updates = new InvoiceUpdate.Counter();
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/CloudTrialExpirer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/CloudTrialExpirer.java
index d0426416349..9121c139b00 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/CloudTrialExpirer.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/CloudTrialExpirer.java
@@ -166,7 +166,7 @@ public class CloudTrialExpirer extends ControllerMaintainer {
.with("mailMessageTemplate", "cloud-trial-notification")
.with("cloudTrialMessage", emailMsg)
.with("mailTitle", emailSubject)
- .with("consoleLink", controller().zoneRegistry().dashboardUrl(tenant.name()).toString())
+ .with("consoleLink", controller().serviceRegistry().consoleUrls().tenantOverview(tenant.name()))
.build());
var source = NotificationSource.from(tenant.name());
// Remove previous notification to ensure new notification is sent by email
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/notification/FormattedNotification.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/notification/FormattedNotification.java
index 9402092f789..bed053d592f 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/notification/FormattedNotification.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/notification/FormattedNotification.java
@@ -1,7 +1,6 @@
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.controller.notification;
-import java.net.URI;
import java.util.Objects;
/**
@@ -10,7 +9,7 @@ import java.util.Objects;
*
* @author enygaard
*/
-public record FormattedNotification(Notification notification, String prettyType, String messagePrefix, URI uri) {
+public record FormattedNotification(Notification notification, String prettyType, String messagePrefix, String uri) {
public FormattedNotification {
Objects.requireNonNull(prettyType);
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/notification/MailTemplating.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/notification/MailTemplating.java
index e8fb7289f4c..1c05330702e 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/notification/MailTemplating.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/notification/MailTemplating.java
@@ -1,10 +1,8 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-
package com.yahoo.vespa.hosted.controller.notification;
import com.yahoo.config.provision.TenantName;
-import com.yahoo.restapi.UriBuilder;
-import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneRegistry;
+import com.yahoo.vespa.hosted.controller.api.integration.ConsoleUrls;
import com.yahoo.vespa.hosted.controller.tenant.PendingMailVerification;
import com.yahoo.yolean.Exceptions;
import org.apache.velocity.VelocityContext;
@@ -15,7 +13,6 @@ import org.apache.velocity.runtime.resource.util.StringResourceRepository;
import org.apache.velocity.tools.generic.EscapeTool;
import java.io.StringWriter;
-import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Map;
@@ -43,19 +40,19 @@ public class MailTemplating {
private final VelocityEngine velocity;
private final EscapeTool escapeTool = new EscapeTool();
- private final URI dashboardUri;
+ private final ConsoleUrls consoleUrls;
- public MailTemplating(ZoneRegistry zoneRegistry) {
+ public MailTemplating(ConsoleUrls consoleUrls) {
this.velocity = createTemplateEngine();
- this.dashboardUri = zoneRegistry.dashboardUrl();
+ this.consoleUrls = consoleUrls;
}
public String generateDefaultMailHtml(Template mailBodyTemplate, Map<String, Object> params, TenantName tenant) {
var ctx = createVelocityContext();
- ctx.put("accountNotificationLink", accountNotificationsUri(tenant));
+ ctx.put("accountNotificationLink", consoleUrls.tenantNotifications(tenant));
ctx.put("privacyPolicyLink", "https://legal.yahoo.com/xw/en/yahoo/privacy/topic/b2bprivacypolicy/index.html");
- ctx.put("termsOfServiceLink", consoleUri("terms-of-service-trial.html"));
- ctx.put("supportLink", consoleUri("support"));
+ ctx.put("termsOfServiceLink", consoleUrls.termsOfService());
+ ctx.put("supportLink", consoleUrls.support());
ctx.put("mailBodyTemplate", mailBodyTemplate.getId());
params.forEach(ctx::put);
return render(ctx, Template.MAIL);
@@ -63,9 +60,8 @@ public class MailTemplating {
public String generateMailVerificationHtml(PendingMailVerification pmf) {
var ctx = createVelocityContext();
- ctx.put("consoleLink", dashboardUri.getHost());
+ ctx.put("verifyLink", consoleUrls.verifyEmail(pmf.getVerificationCode()));
ctx.put("email", pmf.getMailAddress());
- ctx.put("code", pmf.getVerificationCode());
return render(ctx, Template.MAIL_VERIFICATION);
}
@@ -102,16 +98,4 @@ public class MailTemplating {
});
repo.putStringResource(name, templateStr);
}
-
- private String accountNotificationsUri(TenantName tenant) {
- return new UriBuilder(dashboardUri)
- .append("tenant/")
- .append(tenant.value())
- .append("account/notifications")
- .toString();
- }
-
- private String consoleUri(String path) {
- return new UriBuilder(dashboardUri).append(path).toString();
- }
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/notification/NotificationFormatter.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/notification/NotificationFormatter.java
index de99d03cc82..243e1af8f35 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/notification/NotificationFormatter.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/notification/NotificationFormatter.java
@@ -2,17 +2,13 @@
package com.yahoo.vespa.hosted.controller.notification;
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.zone.ZoneRegistry;
-import org.apache.http.client.utils.URIBuilder;
+import com.yahoo.vespa.hosted.controller.api.integration.ConsoleUrls;
-import java.net.URI;
-import java.net.URISyntaxException;
import java.util.Objects;
import java.util.Optional;
-import java.util.function.Function;
+
+import static com.yahoo.vespa.hosted.controller.notification.Notifier.notificationLink;
/**
* Created a NotificationContent for a given Notification.
@@ -22,10 +18,10 @@ import java.util.function.Function;
* @author enygaard
*/
public class NotificationFormatter {
- private final ZoneRegistry zoneRegistry;
+ private final ConsoleUrls consoleUrls;
- public NotificationFormatter(ZoneRegistry zoneRegistry) {
- this.zoneRegistry = Objects.requireNonNull(zoneRegistry);
+ public NotificationFormatter(ConsoleUrls consoleUrls) {
+ this.consoleUrls = Objects.requireNonNull(consoleUrls);
}
public FormattedNotification format(Notification n) {
@@ -35,7 +31,7 @@ public class NotificationFormatter {
case testPackage -> testPackage(n);
case reindex -> reindex(n);
case feedBlock -> feedBlock(n);
- default -> new FormattedNotification(n, n.type().name(), "", zoneRegistry.dashboardUrl(n.source().tenant()));
+ default -> new FormattedNotification(n, n.type().name(), "", consoleUrls.tenantOverview(n.source().tenant()));
};
}
@@ -47,8 +43,7 @@ public class NotificationFormatter {
application,
instance,
levelText(n.level(), n.messages().size()));
- var uri = zoneRegistry.dashboardUrl(ApplicationId.from(source.tenant(), application, instance));
- return new FormattedNotification(n, "Application package", message, uri);
+ return new FormattedNotification(n, "Application package", message, notificationLink(consoleUrls, n.source()));
}
private FormattedNotification deployment(Notification n) {
@@ -58,7 +53,7 @@ public class NotificationFormatter {
requirePresent(source.application(), "application"),
requirePresent(source.instance(), "instance"),
levelText(n.level(), n.messages().size()));
- return new FormattedNotification(n,"Deployment", message, jobLink(n.source()));
+ return new FormattedNotification(n,"Deployment", message, notificationLink(consoleUrls, n.source()));
}
private FormattedNotification testPackage(Notification n) {
@@ -68,68 +63,23 @@ public class NotificationFormatter {
n.messages().size() > 1 ? "are problems" : "is a problem",
application,
source.instance().map(i -> "."+i).orElse(""));
- var uri = zoneRegistry.dashboardUrl(source.tenant(), application);
- return new FormattedNotification(n, "Test package", message, uri);
+ return new FormattedNotification(n, "Test package", message, notificationLink(consoleUrls, n.source()));
}
private FormattedNotification reindex(Notification n) {
var message = Text.format("%s is reindexing", clusterInfo(n.source()));
- var source = n.source();
- var application = requirePresent(source.application(), "application");
- var instance = requirePresent(source.instance(), "instance");
- var clusterId = requirePresent(source.clusterId(), "clusterId");
- var zone = requirePresent(source.zoneId(), "zoneId");
- var instanceURI = zoneRegistry.dashboardUrl(ApplicationId.from(source.tenant(), application, instance));
- try {
- var uri = new URIBuilder(instanceURI)
- .setParameter(
- String.format("%s.%s.%s", instance, zone.environment(), zone.region()),
- String.format("clusters,%s=status", clusterId.value()))
- .build();
- return new FormattedNotification(n, "Reindex", message, uri);
- } catch (URISyntaxException e) {
- throw new IllegalArgumentException(e);
- }
+ var application = requirePresent(n.source().application(), "application");
+ var instance = requirePresent(n.source().instance(), "instance");
+ var clusterId = requirePresent(n.source().clusterId(), "clusterId");
+ var zone = requirePresent(n.source().zoneId(), "zoneId");
+ return new FormattedNotification(n, "Reindex", message,
+ consoleUrls.clusterReindexing(ApplicationId.from(n.source().tenant(), application, instance), zone, clusterId));
}
private FormattedNotification feedBlock(Notification n) {
- String type;
- if (n.level() == Notification.Level.warning) {
- type = "Nearly feed blocked";
- } else {
- type = "Feed blocked";
- }
+ String type = n.level() == Notification.Level.warning ? "Nearly feed blocked" : "Feed blocked";
var message = Text.format("%s is %s", clusterInfo(n.source()), type.toLowerCase());
- var source = n.source();
- var application = requirePresent(source.application(), "application");
- var instance = requirePresent(source.instance(), "instance");
- var clusterId = requirePresent(source.clusterId(), "clusterId");
- var zone = requirePresent(source.zoneId(), "zoneId");
- var instanceURI = zoneRegistry.dashboardUrl(ApplicationId.from(source.tenant(), application, instance));
- try {
- var uri = new URIBuilder(instanceURI)
- .setParameter(
- String.format("%s.%s.%s", instance, zone.environment(), zone.region()),
- String.format("clusters,%s", clusterId.value()))
- .build();
- return new FormattedNotification(n, type, message, uri);
- } catch (URISyntaxException e) {
- throw new IllegalArgumentException(e);
- }
- }
-
- private URI jobLink(NotificationSource source) {
- var application = requirePresent(source.application(), "application");
- var instance = requirePresent(source.instance(), "instance");
- var jobType = requirePresent(source.jobType(), "jobType");
- var runNumber = source.runNumber().orElseThrow(() -> new MissingOptionalException("runNumber"));
- var applicationId = ApplicationId.from(source.tenant(), application, instance);
- Function<Environment, URI> link = (Environment env) -> zoneRegistry.dashboardUrl(new RunId(applicationId, jobType, runNumber));
- var environment = jobType.zone().environment();
- return switch (environment) {
- case dev, perf -> link.apply(environment);
- default -> link.apply(Environment.prod);
- };
+ return new FormattedNotification(n, type, message, notificationLink(consoleUrls, n.source()));
}
private String jobText(NotificationSource source) {
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/notification/Notifier.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/notification/Notifier.java
index e3bfb8b4c56..b0b43866fae 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/notification/Notifier.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/notification/Notifier.java
@@ -2,21 +2,22 @@
package com.yahoo.vespa.hosted.controller.notification;
import com.google.common.annotations.VisibleForTesting;
+import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.Environment;
-import com.yahoo.restapi.UriBuilder;
+import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.text.Text;
import com.yahoo.vespa.flags.FetchVector;
import com.yahoo.vespa.flags.FlagSource;
import com.yahoo.vespa.flags.PermanentFlags;
+import com.yahoo.vespa.hosted.controller.api.integration.ConsoleUrls;
+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.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;
@@ -34,8 +35,8 @@ public class Notifier {
private final CuratorDb curatorDb;
private final Mailer mailer;
private final FlagSource flagSource;
+ private final ConsoleUrls consoleUrls;
private final NotificationFormatter formatter;
- private final URI dashboardUri;
private final MailTemplating mailTemplating;
private static final Logger log = Logger.getLogger(Notifier.class.getName());
@@ -43,13 +44,13 @@ public class Notifier {
// Minimal url pattern matcher to detect hardcoded URLs in Notification messages
private static final Pattern urlPattern = Pattern.compile("https://[\\w\\d./]+");
- public Notifier(CuratorDb curatorDb, ZoneRegistry zoneRegistry, Mailer mailer, FlagSource flagSource) {
+ public Notifier(CuratorDb curatorDb, ConsoleUrls consoleUrls, Mailer mailer, FlagSource flagSource) {
this.curatorDb = Objects.requireNonNull(curatorDb);
this.mailer = Objects.requireNonNull(mailer);
this.flagSource = Objects.requireNonNull(flagSource);
- this.formatter = new NotificationFormatter(zoneRegistry);
- this.dashboardUri = zoneRegistry.dashboardUrl();
- this.mailTemplating = new MailTemplating(zoneRegistry);
+ this.consoleUrls = Objects.requireNonNull(consoleUrls);
+ this.formatter = new NotificationFormatter(consoleUrls);
+ this.mailTemplating = new MailTemplating(consoleUrls);
}
public void dispatch(List<Notification> notifications, NotificationSource source) {
@@ -133,7 +134,7 @@ public class Notifier {
.with("mailTitle", "Vespa Cloud Notifications")
.with("notificationHeader", f.messagePrefix())
.with("notificationItems", items)
- .with("consoleLink", notificationLink(f.notification().source()))
+ .with("consoleLink", notificationLink(consoleUrls, f.notification().source()))
.build();
}
@@ -150,24 +151,16 @@ public class Notifier {
return sb.toString();
}
- private String notificationLink(NotificationSource source) {
- var uri = new UriBuilder(dashboardUri);
- uri = uri.append("tenant").append(source.tenant().value());
- if (source.application().isPresent())
- uri = uri.append("application").append(source.application().get().value());
- if (source.isProduction()) {
- uri = uri.append("prod/instance");
- if (source.jobType().isPresent()) {
- uri = uri.append(source.instance().get().value());
- }
- }
- else {
- uri = uri.append("dev/instance/").append(source.instance().get().value());
- }
- if (source.jobType().isPresent()) {
- uri = uri.append("job").append(source.jobType().get().jobName()).append("run").append(String.valueOf(source.runNumber().getAsLong()));
- }
- return uri.toString();
+ static String notificationLink(ConsoleUrls consoleUrls, NotificationSource source) {
+ if (source.application().isEmpty()) return consoleUrls.tenantOverview(source.tenant());
+ if (source.instance().isEmpty()) return consoleUrls.prodApplicationOverview(source.tenant(), source.application().get());
+
+ ApplicationId application = ApplicationId.from(source.tenant(), source.application().get(), source.instance().get());
+ if (source.jobType().isPresent())
+ return consoleUrls.deploymentRun(new RunId(application, source.jobType().get(), source.runNumber().getAsLong()));
+ if (source.clusterId().isPresent())
+ return consoleUrls.clusterOverview(application, source.zoneId().get(), source.clusterId().get());
+ return consoleUrls.instanceOverview(application, source.zoneId().map(ZoneId::environment).orElse(Environment.prod));
}
private static String capitalise(String m) {
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializer.java
index 4404063456c..d5bb47c94b0 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializer.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializer.java
@@ -282,13 +282,10 @@ public class TenantSerializer {
}
private TenantBilling tenantInfoBillingContactFromSlime(Inspector billingObject) {
- //TODO: Remove validity check once emailVerified has been written for all tenants
- var emailVerified = billingObject.field("emailVerified").valid() ?
- billingObject.field("emailVerified").asBool() : true;
return TenantBilling.empty()
.withContact(TenantContact.from(
billingObject.field("name").asString(),
- new Email(billingObject.field("email").asString(), emailVerified),
+ new Email(billingObject.field("email").asString(), billingObject.field("emailVerified").asBool()),
billingObject.field("phone").asString()))
.withAddress(tenantInfoAddressFromSlime(billingObject.field("address")));
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
index 03e6125a73a..6a6c8a51d72 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
@@ -692,6 +692,7 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler {
var contact = root.setObject("contact");
contact.setString("name", billingContact.contact().name());
contact.setString("email", billingContact.contact().email().getEmailAddress());
+ contact.setBool("emailVerified", billingContact.contact().email().isVerified());
contact.setString("phone", billingContact.contact().phone());
toSlime(billingContact.address(), root); // will create "address" on the parent
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandler.java
index 696f759d16e..6dc29ebe08c 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandler.java
@@ -19,12 +19,14 @@ import com.yahoo.vespa.hosted.controller.ApplicationController;
import com.yahoo.vespa.hosted.controller.Controller;
import com.yahoo.vespa.hosted.controller.TenantController;
import com.yahoo.vespa.hosted.controller.api.integration.billing.Bill;
+import com.yahoo.vespa.hosted.controller.api.integration.billing.BillStatus;
import com.yahoo.vespa.hosted.controller.api.integration.billing.BillingController;
import com.yahoo.vespa.hosted.controller.api.integration.billing.CollectionMethod;
import com.yahoo.vespa.hosted.controller.api.integration.billing.InstrumentOwner;
import com.yahoo.vespa.hosted.controller.api.integration.billing.PaymentInstrument;
import com.yahoo.vespa.hosted.controller.api.integration.billing.PlanId;
import com.yahoo.vespa.hosted.controller.api.integration.billing.PlanRegistry;
+import com.yahoo.vespa.hosted.controller.api.integration.billing.StatusHistory;
import com.yahoo.vespa.hosted.controller.api.role.Role;
import com.yahoo.vespa.hosted.controller.api.role.SecurityContext;
import com.yahoo.vespa.hosted.controller.restapi.ErrorResponses;
@@ -238,7 +240,7 @@ public class BillingApiHandler extends ThreadedHttpRequestHandler {
private HttpResponse setBillStatus(HttpRequest request, String billId, String userId) {
Inspector inspector = inspectorOrThrow(request);
String status = getInspectorFieldOrThrow(inspector, "status");
- billingController.updateBillStatus(Bill.Id.of(billId), userId, status);
+ billingController.updateBillStatus(Bill.Id.of(billId), userId, BillStatus.from(status));
return new MessageResponse("Updated status of invoice " + billId);
}
@@ -380,7 +382,7 @@ public class BillingApiHandler extends ThreadedHttpRequestHandler {
billCursor.setString("to", bill.getEndDate().format(DATE_TIME_FORMATTER));
billCursor.setString("amount", bill.sum().toString());
- billCursor.setString("status", bill.status());
+ billCursor.setString("status", bill.status().value());
var statusCursor = billCursor.setArray("statusHistory");
renderStatusHistory(statusCursor, bill.statusHistory());
@@ -392,14 +394,14 @@ public class BillingApiHandler extends ThreadedHttpRequestHandler {
});
}
- private void renderStatusHistory(Cursor cursor, Bill.StatusHistory statusHistory) {
+ private void renderStatusHistory(Cursor cursor, StatusHistory statusHistory) {
statusHistory.getHistory()
.entrySet()
.stream()
.forEach(entry -> {
var c = cursor.addObject();
c.setString("at", entry.getKey().format(DATE_TIME_FORMATTER));
- c.setString("status", entry.getValue());
+ c.setString("status", entry.getValue().value());
});
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandlerV2.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandlerV2.java
index 35d09fd541b..edec869f559 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandlerV2.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandlerV2.java
@@ -25,6 +25,7 @@ import com.yahoo.vespa.hosted.controller.api.integration.billing.Plan;
import com.yahoo.vespa.hosted.controller.api.integration.billing.PlanId;
import com.yahoo.vespa.hosted.controller.api.integration.billing.PlanRegistry;
import com.yahoo.vespa.hosted.controller.api.integration.billing.Quota;
+import com.yahoo.vespa.hosted.controller.api.integration.billing.StatusHistory;
import com.yahoo.vespa.hosted.controller.api.role.Role;
import com.yahoo.vespa.hosted.controller.api.role.SecurityContext;
import com.yahoo.vespa.hosted.controller.restapi.ErrorResponses;
@@ -461,7 +462,7 @@ public class BillingApiHandlerV2 extends RestApiRequestHandler<BillingApiHandler
slime.setString("from", bill.getStartDate().format(DateTimeFormatter.ISO_LOCAL_DATE));
slime.setString("to", bill.getEndDate().format(DateTimeFormatter.ISO_LOCAL_DATE));
slime.setString("total", bill.sum().toString());
- slime.setString("status", bill.status());
+ slime.setString("status", bill.status().value());
}
private void usageToSlime(Cursor slime, Bill bill) {
@@ -476,16 +477,16 @@ public class BillingApiHandlerV2 extends RestApiRequestHandler<BillingApiHandler
slime.setString("from", bill.getStartDate().format(DateTimeFormatter.ISO_LOCAL_DATE));
slime.setString("to", bill.getEndDate().format(DateTimeFormatter.ISO_LOCAL_DATE));
slime.setString("total", bill.sum().toString());
- slime.setString("status", bill.status());
+ slime.setString("status", bill.status().value());
toSlime(slime.setArray("statusHistory"), bill.statusHistory());
toSlime(slime.setArray("items"), bill.lineItems());
}
- private void toSlime(Cursor slime, Bill.StatusHistory history) {
+ private void toSlime(Cursor slime, StatusHistory history) {
history.getHistory().forEach((key, value) -> {
var c = slime.addObject();
c.setString("at", key.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME));
- c.setString("status", value);
+ c.setString("status", value.value());
});
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/pricing/PricingApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/pricing/PricingApiHandler.java
deleted file mode 100644
index 8ca2936eee7..00000000000
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/pricing/PricingApiHandler.java
+++ /dev/null
@@ -1,218 +0,0 @@
-// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.controller.restapi.pricing;
-
-import com.yahoo.collections.Pair;
-import com.yahoo.component.annotation.Inject;
-import com.yahoo.config.provision.ClusterResources;
-import com.yahoo.container.jdisc.HttpRequest;
-import com.yahoo.container.jdisc.HttpResponse;
-import com.yahoo.container.jdisc.ThreadedHttpRequestHandler;
-import com.yahoo.restapi.ErrorResponse;
-import com.yahoo.restapi.Path;
-import com.yahoo.restapi.SlimeJsonResponse;
-import com.yahoo.slime.Cursor;
-import com.yahoo.slime.Slime;
-import com.yahoo.text.Text;
-import com.yahoo.vespa.hosted.controller.Controller;
-import com.yahoo.vespa.hosted.controller.api.integration.billing.Plan;
-import com.yahoo.vespa.hosted.controller.api.integration.pricing.ApplicationResources;
-import com.yahoo.vespa.hosted.controller.api.integration.pricing.PriceInformation;
-import com.yahoo.vespa.hosted.controller.api.integration.pricing.Prices;
-import com.yahoo.vespa.hosted.controller.api.integration.pricing.PricingController;
-import com.yahoo.vespa.hosted.controller.api.integration.pricing.PricingInfo;
-import com.yahoo.vespa.hosted.controller.restapi.ErrorResponses;
-import com.yahoo.yolean.Exceptions;
-
-import java.math.BigDecimal;
-import java.math.RoundingMode;
-import java.net.URLDecoder;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Optional;
-import java.util.logging.Logger;
-
-import static com.yahoo.jdisc.http.HttpRequest.Method.GET;
-import static com.yahoo.restapi.ErrorResponse.methodNotAllowed;
-import static com.yahoo.vespa.hosted.controller.api.integration.pricing.PricingInfo.SupportLevel;
-import static java.math.BigDecimal.ZERO;
-import static java.nio.charset.StandardCharsets.UTF_8;
-
-/**
- * API for calculating price information
- *
- * @author hmusum
- */
-@SuppressWarnings("unused") // Handler
-public class PricingApiHandler extends ThreadedHttpRequestHandler {
-
- private static final Logger log = Logger.getLogger(PricingApiHandler.class.getName());
-
- private final Controller controller;
- private final PricingController pricingController;
-
- @Inject
- public PricingApiHandler(Context parentCtx, Controller controller, PricingController pricingController) {
- super(parentCtx);
- this.controller = controller;
- this.pricingController = pricingController;
- }
-
- @Override
- public HttpResponse handle(HttpRequest request) {
- if (request.getMethod() != GET)
- return methodNotAllowed("Method '" + request.getMethod() + "' is not supported");
-
- try {
- return handleGET(request);
- } catch (IllegalArgumentException e) {
- return ErrorResponse.badRequest(Exceptions.toMessageString(e));
- } catch (RuntimeException e) {
- return ErrorResponses.logThrowing(request, log, e);
- }
- }
-
- private HttpResponse handleGET(HttpRequest request) {
- Path path = new Path(request.getUri());
- if (path.matches("/pricing/v1/pricing")) return pricing(request);
-
- return ErrorResponse.notFoundError(Text.format("No '%s' handler at '%s'", request.getMethod(),
- request.getUri().getPath()));
- }
-
- private HttpResponse pricing(HttpRequest request) {
- String rawQuery = request.getUri().getRawQuery();
- var priceParameters = parseQuery(rawQuery);
- Prices price = calculatePrice(priceParameters);
- return response(price, priceParameters);
- }
-
- private Prices calculatePrice(PriceParameters priceParameters) {
- return pricingController.priceForApplications(priceParameters.appResources, priceParameters.pricingInfo, priceParameters.plan);
- }
-
- private PriceParameters parseQuery(String rawQuery) {
- if (rawQuery == null) throw new IllegalArgumentException("No price information found in query");
- List<String> elements = Arrays.stream(URLDecoder.decode(rawQuery, UTF_8).split("&")).toList();
- return parseQuery(elements);
- }
-
- private PriceParameters parseQuery(List<String> elements) {
- var supportLevel = SupportLevel.BASIC;
- var enclave = false;
- var committedSpend = ZERO;
- var applicationName = "default";
- var plan = controller.serviceRegistry().planRegistry().defaultPlan(); // fallback to default plan if not supplied
- List<ApplicationResources> appResources = new ArrayList<>();
-
- for (Pair<String, String> entry : keysAndValues(elements)) {
- var value = entry.getSecond();
- switch (entry.getFirst().toLowerCase()) {
- case "committedspend" -> committedSpend = new BigDecimal(value);
- case "planid" -> plan = plan(value).orElseThrow(() -> new IllegalArgumentException("Unknown plan id " + value));
- case "supportlevel" -> supportLevel = SupportLevel.valueOf(value.toUpperCase());
- case "application" -> appResources.add(applicationResources(value));
- default -> throw new IllegalArgumentException("Unknown query parameter '" + entry.getFirst() + '\'');
- }
- }
-
- PricingInfo pricingInfo = new PricingInfo(supportLevel, committedSpend);
- return new PriceParameters(List.of(), pricingInfo, plan, appResources);
- }
-
- private ApplicationResources applicationResources(String appResourcesString) {
- List<String> elements = List.of(appResourcesString.split(","));
-
- var vcpu = ZERO;
- var memoryGb = ZERO;
- var diskGb = ZERO;
- var gpuMemoryGb = ZERO;
- var enclaveVcpu = ZERO;
- var enclaveMemoryGb = ZERO;
- var enclaveDiskGb = ZERO;
- var enclaveGpuMemoryGb = ZERO;
-
- for (var element : keysAndValues(elements)) {
- var value = element.getSecond();
- switch (element.getFirst().toLowerCase()) {
- case "vcpu" -> vcpu = new BigDecimal(value);
- case "memorygb" -> memoryGb = new BigDecimal(value);
- case "diskgb" -> diskGb = new BigDecimal(value);
- case "gpumemorygb" -> gpuMemoryGb = new BigDecimal(value);
-
- case "enclavevcpu" -> enclaveVcpu = new BigDecimal(value);
- case "enclavememorygb" -> enclaveMemoryGb = new BigDecimal(value);
- case "enclavediskgb" -> enclaveDiskGb = new BigDecimal(value);
- case "enclavegpumemorygb" -> enclaveGpuMemoryGb = new BigDecimal(value);
-
- default -> throw new IllegalArgumentException("Unknown key '" + element.getFirst() + '\'');
- }
- }
-
- return new ApplicationResources(vcpu, memoryGb, diskGb, gpuMemoryGb,
- enclaveVcpu, enclaveMemoryGb, enclaveDiskGb, enclaveGpuMemoryGb);
- }
-
- private List<Pair<String, String>> keysAndValues(List<String> elements) {
- return elements.stream().map(element -> {
- var index = element.indexOf("=");
- if (index <= 0 || index == element.length() - 1)
- throw new IllegalArgumentException("Error in query parameter, expected '=' between key and value: '" + element + '\'');
- return new Pair<>(element.substring(0, index), element.substring(index + 1));
- })
- .toList();
- }
-
- private Optional<Plan> plan(String element) {
- return controller.serviceRegistry().planRegistry().plan(element);
- }
-
- private static SlimeJsonResponse response(Prices prices, PriceParameters priceParameters) {
- var slime = new Slime();
- Cursor cursor = slime.setObject();
-
- var applicationsArray = cursor.setArray("applications");
- applicationPrices(applicationsArray, prices.priceInformationApplications(), priceParameters);
-
- var priceInfoArray = cursor.setArray("priceInfo");
- addItem(priceInfoArray, "Enclave (minimum $10k per month)", prices.totalPriceInformation().enclaveDiscount());
- addItem(priceInfoArray, "Committed spend", prices.totalPriceInformation().committedAmountDiscount());
-
- setBigDecimal(cursor, "totalAmount", prices.totalPriceInformation().totalAmount());
-
- return new SlimeJsonResponse(slime);
- }
-
- private static void applicationPrices(Cursor applicationPricesArray, List<PriceInformation> applicationPrices, PriceParameters priceParameters) {
- applicationPrices.forEach(priceInformation -> {
- var element = applicationPricesArray.addObject();
- var array = element.setArray("priceInfo");
- addItem(array, supportLevelDescription(priceParameters), priceInformation.listPriceWithSupport());
- addItem(array, "Enclave", priceInformation.enclaveDiscount());
- addItem(array, "Volume discount", priceInformation.volumeDiscount());
- });
- }
-
- private static String supportLevelDescription(PriceParameters priceParameters) {
- String supportLevel = priceParameters.pricingInfo.supportLevel().name();
- return supportLevel.substring(0,1).toUpperCase() + supportLevel.substring(1).toLowerCase() + " support unit price";
- }
-
- private static void addItem(Cursor array, String name, BigDecimal amount) {
- if (amount.compareTo(BigDecimal.ZERO) != 0) {
- var o = array.addObject();
- o.setString("description", name);
- setBigDecimal(o, "amount", amount);
- }
- }
-
- private static void setBigDecimal(Cursor cursor, String name, BigDecimal value) {
- cursor.setString(name, value.setScale(2, RoundingMode.HALF_UP).toPlainString());
- }
-
- private record PriceParameters(List<ClusterResources> clusterResources, PricingInfo pricingInfo, Plan plan,
- List<ApplicationResources> appResources) {
-
- }
-
-}
diff --git a/controller-server/src/main/resources/mail/mail-verification.vm b/controller-server/src/main/resources/mail/mail-verification.vm
index 6905a292ee7..340895812ca 100644
--- a/controller-server/src/main/resources/mail/mail-verification.vm
+++ b/controller-server/src/main/resources/mail/mail-verification.vm
@@ -411,7 +411,7 @@
valign="middle"
>
<a
- href="https://$consoleUrl/verify?code=$code"
+ href="$verifyLink"
style="
display: inline-block;
background: #3b9fde;
@@ -471,9 +471,9 @@
<a
target="_blank"
rel="noopener noreferrer"
- href="https://$consoleUrl/verify?code=$code"
+ href="$verifyLink"
style="color: #3b9fde"
- >https://$consoleUrl/verify?code=$code</a
+ >$verifyLink</a
>
</p>
</div>
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/MailVerifierTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/MailVerifierTest.java
index a7af0916e59..4fbf39f8d8b 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/MailVerifierTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/MailVerifierTest.java
@@ -30,7 +30,7 @@ class MailVerifierTest {
private final ControllerTester tester = new ControllerTester(SystemName.Public);
private final MockMailer mailer = tester.serviceRegistry().mailer();
- private final MailVerifier mailVerifier = new MailVerifier(tester.zoneRegistry(), tester.controller().tenants(), mailer, tester.curator(), tester.clock());
+ private final MailVerifier mailVerifier = new MailVerifier(tester.serviceRegistry().consoleUrls(), tester.controller().tenants(), mailer, tester.curator(), tester.clock());
private static final TenantName tenantName = TenantName.from("scoober");
private static final String mail = "unverified@bar.com";
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java
index 5c6853bccdb..39d867d813d 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java
@@ -10,6 +10,7 @@ import com.yahoo.config.provision.HostName;
import com.yahoo.config.provision.SystemName;
import com.yahoo.test.ManualClock;
import com.yahoo.vespa.hosted.controller.api.identifiers.ControllerVersion;
+import com.yahoo.vespa.hosted.controller.api.integration.ConsoleUrls;
import com.yahoo.vespa.hosted.controller.api.integration.ServiceRegistry;
import com.yahoo.vespa.hosted.controller.api.integration.archive.ArchiveService;
import com.yahoo.vespa.hosted.controller.api.integration.archive.MockArchiveService;
@@ -54,6 +55,7 @@ import com.yahoo.vespa.hosted.controller.api.integration.user.RoleMaintainer;
import com.yahoo.vespa.hosted.controller.api.integration.user.RoleMaintainerMock;
import com.yahoo.vespa.hosted.controller.api.integration.vcmr.MockChangeRequestClient;
+import java.net.URI;
import java.time.Instant;
import java.util.Optional;
@@ -68,6 +70,7 @@ public class ServiceRegistryMock extends AbstractComponent implements ServiceReg
private final ControllerVersion controllerVersion;
private final ZoneRegistryMock zoneRegistryMock;
private final ConfigServerMock configServerMock;
+ private final ConsoleUrls consoleUrls = new ConsoleUrls(URI.create("https://console.tld"));
private final MemoryNameService memoryNameService = new MemoryNameService();
private final MockVpcEndpointService vpcEndpointService = new MockVpcEndpointService(clock, memoryNameService);
private final MockMailer mockMailer = new MockMailer();
@@ -218,6 +221,11 @@ public class ServiceRegistryMock extends AbstractComponent implements ServiceReg
}
@Override
+ public ConsoleUrls consoleUrls() {
+ return consoleUrls;
+ }
+
+ @Override
public MockResourceTagger resourceTagger() {
return mockResourceTagger;
}
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 70198ae35fd..c5b11fe21b0 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
@@ -2,8 +2,6 @@
package com.yahoo.vespa.hosted.controller.integration;
import com.yahoo.component.AbstractComponent;
-import com.yahoo.config.provision.ApplicationId;
-import com.yahoo.config.provision.ApplicationName;
import com.yahoo.config.provision.AthenzDomain;
import com.yahoo.config.provision.CloudAccount;
import com.yahoo.config.provision.CloudName;
@@ -21,7 +19,6 @@ import com.yahoo.text.Text;
import com.yahoo.vespa.athenz.api.AthenzIdentity;
import com.yahoo.vespa.athenz.api.AthenzService;
import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId;
-import com.yahoo.vespa.hosted.controller.api.integration.deployment.RunId;
import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneRegistry;
import java.net.URI;
@@ -220,36 +217,6 @@ public class ZoneRegistryMock extends AbstractComponent implements ZoneRegistry
}
@Override
- public URI dashboardUrl() {
- return URI.create("https://dashboard.tld");
- }
-
- @Override
- public URI dashboardUrl(ApplicationId id) {
- return URI.create("https://dashboard.tld/" + id);
- }
-
- @Override
- public URI dashboardUrl(TenantName tenantName, ApplicationName applicationName) {
- return URI.create("https://dashboard.tld/" + tenantName + "/" + applicationName);
- }
-
- @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());
- }
-
- @Override
- public URI supportUrl() {
- return URI.create("https://help.tld");
- }
-
- @Override
public URI apiUrl() {
return URI.create("https://api.tld:4443/");
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/BillingReportMaintainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/BillingReportMaintainerTest.java
index a224af83401..5cb46664a75 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/BillingReportMaintainerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/BillingReportMaintainerTest.java
@@ -4,6 +4,7 @@ package com.yahoo.vespa.hosted.controller.maintenance;
import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.TenantName;
import com.yahoo.vespa.hosted.controller.ControllerTester;
+import com.yahoo.vespa.hosted.controller.api.integration.billing.BillStatus;
import com.yahoo.vespa.hosted.controller.api.integration.billing.InvoiceUpdate;
import com.yahoo.vespa.hosted.controller.api.integration.billing.PlanRegistryMock;
import com.yahoo.vespa.hosted.controller.tenant.BillingReference;
@@ -43,25 +44,38 @@ public class BillingReportMaintainerTest {
}
@Test
- void only_bills_with_exported_id_are_maintained() {
+ void only_open_bills_with_exported_id_are_maintained() {
var t1 = tester.createTenant("t1");
var billingController = tester.controller().serviceRegistry().billingController();
var billingDb = tester.controller().serviceRegistry().billingDatabase();
var start = LocalDate.of(2020, 5, 23).atStartOfDay(ZoneOffset.UTC);
var end = start.toLocalDate().plusDays(6).atStartOfDay(ZoneOffset.UTC);
+
var bill1 = billingDb.createBill(t1, start, end, "non-exported");
var bill2 = billingDb.createBill(t1, start, end, "exported");
+ var bill3 = billingDb.createBill(t1, start, end, "exported-and-frozen");
+ billingDb.setStatus(bill3, "foo", BillStatus.FROZEN);
billingController.setPlan(t1, PlanRegistryMock.paidPlan.id(), false, true);
tester.controller().serviceRegistry().billingReporter().exportBill(billingDb.readBill(bill2).get(), "FOO", cloudTenant(t1));
+ tester.controller().serviceRegistry().billingReporter().exportBill(billingDb.readBill(bill3).get(), "FOO", cloudTenant(t1));
var updates = maintainer.maintainInvoices();
- assertEquals(new InvoiceUpdate(0, 0, 1), updates);
+ assertEquals(new InvoiceUpdate(1, 0, 0), updates);
+
+ assertTrue(billingDb.readBill(bill1).get().getExportedId().isEmpty());
var exportedBill = billingDb.readBill(bill2).get();
assertEquals("EXT-ID-123", exportedBill.getExportedId().get());
- assertTrue(billingDb.readBill(bill1).get().getExportedId().isEmpty());
+ var lineItems = exportedBill.lineItems();
+ assertEquals(1, lineItems.size());
+ assertEquals("maintained", lineItems.get(0).id());
+
+ var frozenBill = billingDb.readBill(bill3).get();
+ assertEquals("EXT-ID-123", frozenBill.getExportedId().get());
+ assertEquals(0, frozenBill.lineItems().size());
+
}
private CloudTenant cloudTenant(TenantName tenantName) {
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/notification/NotificationFormatterTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/notification/NotificationFormatterTest.java
index 751d0123e40..875487144d9 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/notification/NotificationFormatterTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/notification/NotificationFormatterTest.java
@@ -6,16 +6,16 @@ import com.yahoo.config.provision.ApplicationName;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.InstanceName;
import com.yahoo.config.provision.RegionName;
-import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.TenantName;
import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId;
+import com.yahoo.vespa.hosted.controller.api.integration.ConsoleUrls;
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.application.TenantAndApplicationId;
-import com.yahoo.vespa.hosted.controller.integration.ZoneRegistryMock;
import org.junit.jupiter.api.Test;
+import java.net.URI;
import java.time.Instant;
import java.util.List;
@@ -31,9 +31,8 @@ public class NotificationFormatterTest {
private final ApplicationId applicationId = ApplicationId.from(tenant, application, instance);
private final DeploymentId deploymentId = new DeploymentId(applicationId, ZoneId.defaultId());
private final ClusterSpec.Id cluster = new ClusterSpec.Id("content");
- private final ZoneRegistryMock zoneRegistry = new ZoneRegistryMock(SystemName.Public);
- private final NotificationFormatter formatter = new NotificationFormatter(zoneRegistry);
+ private final NotificationFormatter formatter = new NotificationFormatter(new ConsoleUrls(URI.create("https://console.tld")));
@Test
void applicationPackage() {
@@ -41,7 +40,7 @@ public class NotificationFormatterTest {
var content = formatter.format(notification);
assertEquals("Application package", content.prettyType());
assertEquals("Application package for myapp.beta has 2 warnings", content.messagePrefix());
- assertEquals("https://dashboard.tld/scoober.myapp.beta", content.uri().toString());
+ assertEquals("https://console.tld/tenant/scoober/application/myapp/prod/instance/beta", content.uri());
}
@Test
@@ -51,7 +50,7 @@ public class NotificationFormatterTest {
var content = formatter.format(notification);
assertEquals("Deployment", content.prettyType());
assertEquals("production-default #1001 for myapp.beta has a warning", content.messagePrefix());
- assertEquals("https://dashboard.tld/scoober.myapp.beta/production-default/1001", content.uri().toString());
+ assertEquals("https://console.tld/tenant/scoober/application/myapp/prod/instance/beta/job/production-default/run/1001", content.uri());
}
@Test
@@ -61,7 +60,7 @@ public class NotificationFormatterTest {
var content = formatter.format(notification);
assertEquals("Deployment", content.prettyType());
assertEquals("production-default #1001 for myapp.beta has failed", content.messagePrefix());
- assertEquals("https://dashboard.tld/scoober.myapp.beta/production-default/1001", content.uri().toString());
+ assertEquals("https://console.tld/tenant/scoober/application/myapp/prod/instance/beta/job/production-default/run/1001", content.uri());
}
@Test
@@ -70,7 +69,7 @@ public class NotificationFormatterTest {
var content = formatter.format(notification);
assertEquals("Test package", content.prettyType());
assertEquals("There is a problem with tests for myapp", content.messagePrefix());
- assertEquals("https://dashboard.tld/scoober/myapp", content.uri().toString());
+ assertEquals("https://console.tld/tenant/scoober/application/myapp/prod/instance", content.uri());
}
@Test
@@ -79,7 +78,7 @@ public class NotificationFormatterTest {
var content = formatter.format(notification);
assertEquals("Reindex", content.prettyType());
assertEquals("Cluster content in prod.default for myapp.beta is reindexing", content.messagePrefix());
- assertEquals("https://dashboard.tld/scoober.myapp.beta?beta.prod.default=clusters%2Ccontent%3Dstatus", content.uri().toString());
+ assertEquals("https://console.tld/tenant/scoober/application/myapp/prod/instance/beta?beta.prod.default=clusters%2Ccontent%3Dreindexing", content.uri());
}
@Test
@@ -88,6 +87,6 @@ public class NotificationFormatterTest {
var content = formatter.format(notification);
assertEquals("Nearly feed blocked", content.prettyType());
assertEquals("Cluster content in prod.default for myapp.beta is nearly feed blocked", content.messagePrefix());
- assertEquals("https://dashboard.tld/scoober.myapp.beta?beta.prod.default=clusters%2Ccontent", content.uri().toString());
+ assertEquals("https://console.tld/tenant/scoober/application/myapp/prod/instance/beta?beta.prod.default=clusters%2Ccontent", content.uri());
}
}
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 003a7f59eef..39c3f0f2b74 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
@@ -14,13 +14,13 @@ import com.yahoo.vespa.flags.InMemoryFlagSource;
import com.yahoo.vespa.flags.PermanentFlags;
import com.yahoo.vespa.hosted.controller.api.application.v4.model.ClusterMetrics;
import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId;
+import com.yahoo.vespa.hosted.controller.api.integration.ConsoleUrls;
import com.yahoo.vespa.hosted.controller.api.integration.billing.PlanId;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.ApplicationReindexing;
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.deployment.DeploymentContext;
-import com.yahoo.vespa.hosted.controller.integration.ZoneRegistryMock;
import com.yahoo.vespa.hosted.controller.persistence.MockCuratorDb;
import com.yahoo.vespa.hosted.controller.tenant.ArchiveAccess;
import com.yahoo.vespa.hosted.controller.tenant.CloudTenant;
@@ -31,6 +31,7 @@ import com.yahoo.vespa.hosted.controller.tenant.TenantInfo;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import java.net.URI;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
@@ -84,7 +85,7 @@ public class NotificationsDbTest {
private final MockCuratorDb curatorDb = new MockCuratorDb(SystemName.Public);
private final MockMailer mailer = new MockMailer();
private final FlagSource flagSource = new InMemoryFlagSource().withBooleanFlag(PermanentFlags.NOTIFICATION_DISPATCH_FLAG.id(), true);
- private final NotificationsDb notificationsDb = new NotificationsDb(clock, curatorDb, new Notifier(curatorDb, new ZoneRegistryMock(SystemName.cd), mailer, flagSource));
+ private final NotificationsDb notificationsDb = new NotificationsDb(clock, curatorDb, new Notifier(curatorDb, new ConsoleUrls(URI.create("https://console.tld")), mailer, flagSource));
@Test
void list_test() {
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/notification/NotifierTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/notification/NotifierTest.java
index f64ed3740d2..55531dff72d 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/notification/NotifierTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/notification/NotifierTest.java
@@ -9,9 +9,9 @@ import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.TenantName;
import com.yahoo.vespa.flags.InMemoryFlagSource;
import com.yahoo.vespa.flags.PermanentFlags;
+import com.yahoo.vespa.hosted.controller.api.integration.ConsoleUrls;
import com.yahoo.vespa.hosted.controller.api.integration.billing.PlanId;
import com.yahoo.vespa.hosted.controller.api.integration.stubs.MockMailer;
-import com.yahoo.vespa.hosted.controller.integration.ZoneRegistryMock;
import com.yahoo.vespa.hosted.controller.persistence.MockCuratorDb;
import com.yahoo.vespa.hosted.controller.tenant.ArchiveAccess;
import com.yahoo.vespa.hosted.controller.tenant.CloudTenant;
@@ -23,6 +23,7 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.IOException;
+import java.net.URI;
import java.time.Instant;
import java.util.List;
import java.util.Map;
@@ -64,7 +65,7 @@ public class NotifierTest {
void dispatch() throws IOException {
var mailer = new MockMailer();
var flagSource = new InMemoryFlagSource().withBooleanFlag(PermanentFlags.NOTIFICATION_DISPATCH_FLAG.id(), true);
- var notifier = new Notifier(curatorDb, new ZoneRegistryMock(SystemName.cd), mailer, flagSource);
+ var notifier = new Notifier(curatorDb, new ConsoleUrls(URI.create("https://console.tld")), mailer, flagSource);
var notification = new Notification(Instant.now(), Notification.Type.testPackage, Notification.Level.warning,
NotificationSource.from(ApplicationId.from(tenant, ApplicationName.defaultName(), InstanceName.defaultName())),
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializerTest.java
index cb867c76f1f..9de856971c9 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializerTest.java
@@ -212,7 +212,7 @@ public class TenantSerializerTest {
Slime slime = new Slime();
Cursor parentObject = slime.setObject();
serializer.toSlime(partialInfo, parentObject);
- assertEquals("{\"info\":{\"name\":\"\",\"email\":\"\",\"website\":\"\",\"contactName\":\"\",\"contactEmail\":\"\",\"contactEmailVerified\":true,\"address\":{\"addressLines\":\"\",\"postalCodeOrZip\":\"\",\"city\":\"Hønefoss\",\"stateRegionProvince\":\"\",\"country\":\"\"}}}", slime.toString());
+ assertEquals("{\"info\":{\"name\":\"\",\"email\":\"\",\"website\":\"\",\"contactName\":\"\",\"contactEmail\":\"\",\"contactEmailVerified\":false,\"address\":{\"addressLines\":\"\",\"postalCodeOrZip\":\"\",\"city\":\"Hønefoss\",\"stateRegionProvince\":\"\",\"country\":\"\"}}}", slime.toString());
}
@Test
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java
index 6103b715744..3ada598f4f8 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java
@@ -77,7 +77,6 @@ public class ControllerContainerTest {
<component id='com.yahoo.vespa.hosted.controller.Controller'/>
<component id='com.yahoo.vespa.hosted.controller.integration.ConfigServerProxyMock'/>
<component id='com.yahoo.vespa.hosted.controller.maintenance.ControllerMaintenance'/>
- <component id='com.yahoo.vespa.hosted.controller.api.integration.MockPricingController'/>
<component id='com.yahoo.vespa.hosted.controller.api.integration.stubs.MockMavenRepository'/>
<component id='com.yahoo.vespa.hosted.controller.api.integration.stubs.MockUserManagement'/>
<component id='com.yahoo.vespa.hosted.controller.integration.SecretStoreMock'/>
@@ -118,9 +117,6 @@ public class ControllerContainerTest {
<handler id='com.yahoo.vespa.hosted.controller.restapi.changemanagement.ChangeManagementApiHandler'>
<binding>http://localhost/changemanagement/v1/*</binding>
</handler>
- <handler id='com.yahoo.vespa.hosted.controller.restapi.pricing.PricingApiHandler'>
- <binding>http://localhost/pricing/v1/*</binding>
- </handler>
%s
</container>
""".formatted(system().value(), variablePartXml());
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java
index 90bd2323cb3..2b01d87c903 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java
@@ -78,7 +78,7 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest {
void tenant_info_profile() {
var request = request("/application/v4/tenant/scoober/info/profile", GET)
.roles(Set.of(Role.reader(tenantName)));
- tester.assertResponse(request, "{\"contact\":{\"name\":\"\",\"email\":\"\",\"emailVerified\":true},\"tenant\":{\"company\":\"\",\"website\":\"\"}}", 200);
+ tester.assertResponse(request, "{\"contact\":{\"name\":\"\",\"email\":\"\",\"emailVerified\":false},\"tenant\":{\"company\":\"\",\"website\":\"\"}}", 200);
var updateRequest = request("/application/v4/tenant/scoober/info/profile", PUT)
.data("{\"contact\":{\"name\":\"Some Name\",\"email\":\"foo@example.com\"},\"tenant\":{\"company\":\"Scoober, Inc.\",\"website\":\"https://example.com/\"}}")
@@ -100,7 +100,7 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest {
void tenant_info_billing() {
var request = request("/application/v4/tenant/scoober/info/billing", GET)
.roles(Set.of(Role.reader(tenantName)));
- tester.assertResponse(request, "{\"contact\":{\"name\":\"\",\"email\":\"\",\"phone\":\"\"}}", 200);
+ tester.assertResponse(request, "{\"contact\":{\"name\":\"\",\"email\":\"\",\"emailVerified\":false,\"phone\":\"\"}}", 200);
var fullAddress = "{\"addressLines\":\"addressLines\",\"postalCodeOrZip\":\"postalCodeOrZip\",\"city\":\"city\",\"stateRegionProvince\":\"stateRegionProvince\",\"country\":\"country\"}";
var fullBillingContact = "{\"contact\":{\"name\":\"name\",\"email\":\"foo@example\",\"phone\":\"phone\"},\"address\":" + fullAddress + "}";
@@ -110,7 +110,7 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest {
.roles(Set.of(Role.administrator(tenantName)));
tester.assertResponse(updateRequest, "{\"message\":\"Tenant info updated\"}", 200);
- tester.assertResponse(request, "{\"contact\":{\"name\":\"name\",\"email\":\"foo@example\",\"phone\":\"phone\"},\"address\":{\"addressLines\":\"addressLines\",\"postalCodeOrZip\":\"postalCodeOrZip\",\"city\":\"city\",\"stateRegionProvince\":\"stateRegionProvince\",\"country\":\"country\"}}", 200);
+ tester.assertResponse(request, "{\"contact\":{\"name\":\"name\",\"email\":\"foo@example\",\"emailVerified\":false,\"phone\":\"phone\"},\"address\":{\"addressLines\":\"addressLines\",\"postalCodeOrZip\":\"postalCodeOrZip\",\"city\":\"city\",\"stateRegionProvince\":\"stateRegionProvince\",\"country\":\"country\"}}", 200);
}
@Test
@@ -133,7 +133,7 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest {
var infoRequest =
request("/application/v4/tenant/scoober/info", GET)
.roles(Set.of(Role.reader(tenantName)));
- tester.assertResponse(infoRequest, "{\"name\":\"\",\"email\":\"\",\"website\":\"\",\"contactName\":\"\",\"contactEmail\":\"\",\"contactEmailVerified\":true,\"contacts\":[{\"audiences\":[\"tenant\",\"notifications\"],\"email\":\"developer@scoober\",\"emailVerified\":true}]}", 200);
+ tester.assertResponse(infoRequest, "{\"name\":\"\",\"email\":\"\",\"website\":\"\",\"contactName\":\"\",\"contactEmail\":\"\",\"contactEmailVerified\":false,\"contacts\":[{\"audiences\":[\"tenant\",\"notifications\"],\"email\":\"developer@scoober\",\"emailVerified\":true}]}", 200);
String partialInfo = "{\"contactName\":\"newName\", \"contactEmail\": \"foo@example.com\", \"billingContact\":{\"name\":\"billingName\"}}";
var postPartial =
@@ -150,7 +150,7 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest {
tester.assertResponse(postPartialContacts, "{\"message\":\"Tenant info updated\"}", 200);
// Read back the updated info
- tester.assertResponse(infoRequest, "{\"name\":\"\",\"email\":\"\",\"website\":\"\",\"contactName\":\"newName\",\"contactEmail\":\"foo@example.com\",\"contactEmailVerified\":false,\"billingContact\":{\"name\":\"billingName\",\"email\":\"\",\"emailVerified\":true,\"phone\":\"\"},\"contacts\":[{\"audiences\":[\"tenant\"],\"email\":\"contact1@example.com\",\"emailVerified\":false}]}", 200);
+ tester.assertResponse(infoRequest, "{\"name\":\"\",\"email\":\"\",\"website\":\"\",\"contactName\":\"newName\",\"contactEmail\":\"foo@example.com\",\"contactEmailVerified\":false,\"billingContact\":{\"name\":\"billingName\",\"email\":\"\",\"emailVerified\":false,\"phone\":\"\"},\"contacts\":[{\"audiences\":[\"tenant\"],\"email\":\"contact1@example.com\",\"emailVerified\":false}]}", 200);
String fullAddress = "{\"addressLines\":\"addressLines\",\"postalCodeOrZip\":\"postalCodeOrZip\",\"city\":\"city\",\"stateRegionProvince\":\"stateRegionProvince\",\"country\":\"country\"}";
String fullBillingContact = "{\"name\":\"name\",\"email\":\"foo@example\",\"emailVerified\":false,\"phone\":\"phone\",\"address\":" + fullAddress + "}";
@@ -188,7 +188,7 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest {
var infoRequest =
request("/application/v4/tenant/scoober/info", GET)
.roles(Set.of(Role.reader(tenantName)));
- tester.assertResponse(infoRequest, "{\"name\":\"\",\"email\":\"\",\"website\":\"\",\"contactName\":\"\",\"contactEmail\":\"\",\"contactEmailVerified\":true,\"contacts\":[{\"audiences\":[\"tenant\",\"notifications\"],\"email\":\"developer@scoober\",\"emailVerified\":true}]}", 200);
+ tester.assertResponse(infoRequest, "{\"name\":\"\",\"email\":\"\",\"website\":\"\",\"contactName\":\"\",\"contactEmail\":\"\",\"contactEmailVerified\":false,\"contacts\":[{\"audiences\":[\"tenant\",\"notifications\"],\"email\":\"developer@scoober\",\"emailVerified\":true}]}", 200);
// name needs to be present and not blank
var partialInfoMissingName = "{\"contactName\": \" \"}";
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandlerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandlerTest.java
index 7f68fbf6909..f3147d2adde 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandlerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandlerTest.java
@@ -4,9 +4,11 @@ package com.yahoo.vespa.hosted.controller.restapi.billing;
import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.TenantName;
import com.yahoo.vespa.hosted.controller.api.integration.billing.Bill;
+import com.yahoo.vespa.hosted.controller.api.integration.billing.BillStatus;
import com.yahoo.vespa.hosted.controller.api.integration.billing.CollectionMethod;
import com.yahoo.vespa.hosted.controller.api.integration.billing.MockBillingController;
import com.yahoo.vespa.hosted.controller.api.integration.billing.PlanId;
+import com.yahoo.vespa.hosted.controller.api.integration.billing.StatusHistory;
import com.yahoo.vespa.hosted.controller.api.role.Role;
import com.yahoo.vespa.hosted.controller.restapi.ContainerTester;
import com.yahoo.vespa.hosted.controller.restapi.ControllerContainerCloudTest;
@@ -26,12 +28,10 @@ import java.util.Optional;
import java.util.Set;
import java.util.TreeMap;
-import static com.yahoo.application.container.handler.Request.Method.DELETE;
import static com.yahoo.application.container.handler.Request.Method.GET;
import static com.yahoo.application.container.handler.Request.Method.PATCH;
import static com.yahoo.application.container.handler.Request.Method.POST;
import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* @author olaa
@@ -152,14 +152,14 @@ public class BillingApiHandlerTest extends ControllerContainerCloudTest {
void adding_new_status() {
billingController.addBill(tenant, createBill(), true);
- var requestBody = "{\"status\":\"DONE\"}";
+ var requestBody = "{\"status\":\"CLOSED\"}";
var request = request("/billing/v1/invoice/id-1/status", POST)
.data(requestBody)
.roles(financeAdmin);
tester.assertResponse(request, "{\"message\":\"Updated status of invoice id-1\"}");
var bill = billingController.getBillsForTenant(tenant).get(0);
- assertEquals("DONE", bill.status());
+ assertEquals(BillStatus.CLOSED, bill.status());
}
@Test
@@ -211,7 +211,7 @@ public class BillingApiHandlerTest extends ControllerContainerCloudTest {
static Bill createBill() {
var start = LocalDate.of(2020, 5, 23).atStartOfDay(ZoneOffset.UTC);
var end = start.toLocalDate().plusDays(6).atStartOfDay(ZoneOffset.UTC);
- var statusHistory = new Bill.StatusHistory(new TreeMap<>(Map.of(start, "OPEN")));
+ var statusHistory = new StatusHistory(new TreeMap<>(Map.of(start, BillStatus.OPEN)));
return new Bill(
Bill.Id.of("id-1"),
TenantName.defaultName(),
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/pricing/PricingApiHandlerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/pricing/PricingApiHandlerTest.java
deleted file mode 100644
index f2ce0dfeef2..00000000000
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/pricing/PricingApiHandlerTest.java
+++ /dev/null
@@ -1,185 +0,0 @@
-// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.controller.restapi.pricing;
-
-import com.yahoo.config.provision.SystemName;
-import com.yahoo.vespa.hosted.controller.api.integration.pricing.PricingInfo;
-import com.yahoo.vespa.hosted.controller.restapi.ContainerTester;
-import com.yahoo.vespa.hosted.controller.restapi.ControllerContainerCloudTest;
-import org.junit.jupiter.api.Test;
-
-import java.net.URLEncoder;
-
-import static com.yahoo.vespa.hosted.controller.api.integration.pricing.PricingInfo.SupportLevel.BASIC;
-import static com.yahoo.vespa.hosted.controller.api.integration.pricing.PricingInfo.SupportLevel.COMMERCIAL;
-import static java.nio.charset.StandardCharsets.UTF_8;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-/**
- * @author hmusum
- */
-public class PricingApiHandlerTest extends ControllerContainerCloudTest {
-
- @Test
- void testPricingInfoBasic() {
- tester().assertJsonResponse(request("/pricing/v1/pricing?supportLevel=basic&committedSpend=0"),
- """
- { "applications": [ ], "priceInfo": [ ], "totalAmount": "0.00" }
- """,
- 200);
-
- var request = request("/pricing/v1/pricing?" + urlEncodedPriceInformation1App(BASIC));
- tester().assertJsonResponse(request, """
- {
- "applications": [
- {
- "priceInfo": [
- {"description": "Basic support unit price", "amount": "4.30"},
- {"description": "Volume discount", "amount": "-0.10"}
- ]
- }
- ],
- "priceInfo": [
- {"description": "Committed spend", "amount": "-0.20"}
- ],
- "totalAmount": "4.00"
- }
- """,
- 200);
- }
-
- @Test
- void testPricingInfoBasicEnclave() {
- var request = request("/pricing/v1/pricing?" + urlEncodedPriceInformation1AppEnclave(BASIC));
- tester().assertJsonResponse(request, """
- {
- "applications": [
- {
- "priceInfo": [
- {"description": "Basic support unit price", "amount": "4.30"},
- {"description": "Enclave", "amount": "-0.15"},
- {"description": "Volume discount", "amount": "-0.10"}
- ]
- }
- ],
- "priceInfo": [
- {"description": "Enclave (minimum $10k per month)", "amount": "10.15"},
- {"description": "Committed spend", "amount": "-0.20"}
- ],
- "totalAmount": "3.85"
- }
- """,
- 200);
- }
-
- @Test
- void testPricingInfoCommercialEnclave() {
- var request = request("/pricing/v1/pricing?" + urlEncodedPriceInformation1AppEnclave(COMMERCIAL));
- tester().assertJsonResponse(request, """
- {
- "applications": [
- {
- "priceInfo": [
- {"description": "Commercial support unit price", "amount": "13.30"},
- {"description": "Enclave", "amount": "-0.15"},
- {"description": "Volume discount", "amount": "-0.10"}
- ]
- }
- ],
- "priceInfo": [
- {"description": "Enclave (minimum $10k per month)", "amount": "1.15"},
- {"description": "Committed spend", "amount": "-0.20"}
- ],
- "totalAmount": "12.85"
- }
- """,
- 200);
- }
-
- @Test
- void testPricingInfoCommercialEnclave2Apps() {
- var request = request("/pricing/v1/pricing?" + urlEncodedPriceInformation2AppsEnclave(COMMERCIAL));
- tester().assertJsonResponse(request, """
- {
- "applications": [
- {
- "priceInfo": [
- {"description": "Commercial support unit price", "amount": "13.30"},
- {"description": "Enclave", "amount": "-0.15"},
- {"description": "Volume discount", "amount": "-0.10"}
- ]
- },
- {
- "priceInfo": [
- {"description": "Commercial support unit price", "amount": "13.30"},
- {"description": "Enclave", "amount": "-0.15"},
- {"description": "Volume discount", "amount": "-0.10"}
- ]
- }
- ],
- "priceInfo": [ ],
- "totalAmount": "26.10"
- }
- """,
- 200);
- }
-
- @Test
- void testInvalidRequests() {
- ContainerTester tester = tester();
- tester.assertJsonResponse(request("/pricing/v1/pricing"),
- "{\"error-code\":\"BAD_REQUEST\",\"message\":\"No price information found in query\"}",
- 400);
- tester.assertJsonResponse(request("/pricing/v1/pricing?"),
- "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Error in query parameter, expected '=' between key and value: ''\"}",
- 400);
- tester.assertJsonResponse(request("/pricing/v1/pricing?supportLevel=basic&committedSpend=0&resources"),
- "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Error in query parameter, expected '=' between key and value: 'resources'\"}",
- 400);
- tester.assertJsonResponse(request("/pricing/v1/pricing?supportLevel=basic&committedSpend=0&resources="),
- "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Error in query parameter, expected '=' between key and value: 'resources='\"}",
- 400);
- tester.assertJsonResponse(request("/pricing/v1/pricing?supportLevel=basic&committedSpend=0&key=value"),
- "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Unknown query parameter 'key'\"}",
- 400);
- tester.assertJsonResponse(request("/pricing/v1/pricing?supportLevel=basic&committedSpend=0&application=key%3Dvalue"),
- "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Unknown key 'key'\"}",
- 400);
- }
-
- private ContainerTester tester() {
- ContainerTester tester = new ContainerTester(container, null);
- assertEquals(SystemName.Public, tester.controller().system());
- return tester;
- }
-
- /**
- * 1 app, with 2 clusters (with total resources for all clusters with each having
- * 1 node, with 4 vcpu, 8 Gb memory, 100 Gb disk and no GPU,
- * price will be 20000 + 2000 + 200
- */
- String urlEncodedPriceInformation1App(PricingInfo.SupportLevel supportLevel) {
- return "application=" + URLEncoder.encode("vcpu=4,memoryGb=8,diskGb=100,gpuMemoryGb=0", UTF_8) +
- "&supportLevel=" + supportLevel.name().toLowerCase() + "&committedSpend=20";
- }
-
- /**
- * 1 app, with 2 clusters (with total resources for all clusters with each having
- * 1 node, with 4 vcpu, 8 Gb memory, 100 Gb disk and no GPU,
- * price will be 20000 + 2000 + 200
- */
- String urlEncodedPriceInformation1AppEnclave(PricingInfo.SupportLevel supportLevel) {
- return "application=" + URLEncoder.encode("enclaveVcpu=4,enclaveMemoryGb=8,enclaveDiskGb=100,enclaveGpuMemoryGb=0", UTF_8) +
- "&supportLevel=" + supportLevel.name().toLowerCase() + "&committedSpend=20";
- }
-
- /**
- * 2 apps, with 1 cluster (with total resources for all clusters with each having
- * 1 node, with 4 vcpu, 8 Gb memory, 100 Gb disk and no GPU,
- */
- String urlEncodedPriceInformation2AppsEnclave(PricingInfo.SupportLevel supportLevel) {
- return "application=" + URLEncoder.encode("enclaveVcpu=4,enclaveMemoryGb=8,enclaveDiskGb=100,enclaveGpuMemoryGb=0", UTF_8) +
- "&application=" + URLEncoder.encode("enclaveVcpu=4,enclaveMemoryGb=8,enclaveDiskGb=100,enclaveGpuMemoryGb=0", UTF_8) +
- "&supportLevel=" + supportLevel.name().toLowerCase() + "&committedSpend=0";
- }
-
-}
diff --git a/controller-server/src/test/resources/mail/notification.html b/controller-server/src/test/resources/mail/notification.html
index c8d0037426b..2a0edeea7e1 100644
--- a/controller-server/src/test/resources/mail/notification.html
+++ b/controller-server/src/test/resources/mail/notification.html
@@ -488,7 +488,7 @@
valign="middle"
>
<a
- href="https://dashboard.tld/tenant/tenant1/application/default/prod/instance"
+ href="https://console.tld/tenant/tenant1/application/default/prod/instance/default"
style="
display: inline-block;
background: #005a8e;
@@ -606,7 +606,7 @@
target="_blank"
rel="noopener noreferrer"
style="color: #005a8e"
- href="https://dashboard.tld/terms-of-service-trial.html"
+ href="https://console.tld/terms-of-service-trial.html"
><span style="color: #005a8e"
>Terms of Service</span
></a
@@ -616,7 +616,7 @@
target="_blank"
rel="noopener noreferrer"
style="color: #005a8e"
- href="https://dashboard.tld/support"
+ href="https://console.tld/support"
><span style="color: #005a8e">Support</span></a
>
</p>
@@ -625,7 +625,7 @@
target="_blank"
rel="noopener noreferrer"
style="color: inherit; text-decoration: none"
- href="https://dashboard.tld/tenant/tenant1/account/notifications"
+ href="https://console.tld/tenant/tenant1/account/notifications"
>Click
<span style="color: #005a8e"><u>here</u></span>
to manage your notifications setting.</a
diff --git a/controller-server/src/test/resources/mail/trial-expired.html b/controller-server/src/test/resources/mail/trial-expired.html
index 4e6fda61b33..bdeafe8c7d3 100644
--- a/controller-server/src/test/resources/mail/trial-expired.html
+++ b/controller-server/src/test/resources/mail/trial-expired.html
@@ -485,7 +485,7 @@
valign="middle"
>
<a
- href="https://dashboard.tld/trial-tenant"
+ href="https://console.tld/tenant/trial-tenant"
style="
display: inline-block;
background: #005a8e;
@@ -603,7 +603,7 @@
target="_blank"
rel="noopener noreferrer"
style="color: #005a8e"
- href="https://dashboard.tld/terms-of-service-trial.html"
+ href="https://console.tld/terms-of-service-trial.html"
><span style="color: #005a8e"
>Terms of Service</span
></a
@@ -613,7 +613,7 @@
target="_blank"
rel="noopener noreferrer"
style="color: #005a8e"
- href="https://dashboard.tld/support"
+ href="https://console.tld/support"
><span style="color: #005a8e">Support</span></a
>
</p>
@@ -622,7 +622,7 @@
target="_blank"
rel="noopener noreferrer"
style="color: inherit; text-decoration: none"
- href="https://dashboard.tld/tenant/trial-tenant/account/notifications"
+ href="https://console.tld/tenant/trial-tenant/account/notifications"
>Click
<span style="color: #005a8e"><u>here</u></span>
to manage your notifications setting.</a
diff --git a/controller-server/src/test/resources/mail/trial-expiring-immediately.html b/controller-server/src/test/resources/mail/trial-expiring-immediately.html
index 4b16619fe9c..db89eca195a 100644
--- a/controller-server/src/test/resources/mail/trial-expiring-immediately.html
+++ b/controller-server/src/test/resources/mail/trial-expiring-immediately.html
@@ -485,7 +485,7 @@
valign="middle"
>
<a
- href="https://dashboard.tld/trial-tenant"
+ href="https://console.tld/tenant/trial-tenant"
style="
display: inline-block;
background: #005a8e;
@@ -603,7 +603,7 @@
target="_blank"
rel="noopener noreferrer"
style="color: #005a8e"
- href="https://dashboard.tld/terms-of-service-trial.html"
+ href="https://console.tld/terms-of-service-trial.html"
><span style="color: #005a8e"
>Terms of Service</span
></a
@@ -613,7 +613,7 @@
target="_blank"
rel="noopener noreferrer"
style="color: #005a8e"
- href="https://dashboard.tld/support"
+ href="https://console.tld/support"
><span style="color: #005a8e">Support</span></a
>
</p>
@@ -622,7 +622,7 @@
target="_blank"
rel="noopener noreferrer"
style="color: inherit; text-decoration: none"
- href="https://dashboard.tld/tenant/trial-tenant/account/notifications"
+ href="https://console.tld/tenant/trial-tenant/account/notifications"
>Click
<span style="color: #005a8e"><u>here</u></span>
to manage your notifications setting.</a
diff --git a/controller-server/src/test/resources/mail/trial-expiring-soon.html b/controller-server/src/test/resources/mail/trial-expiring-soon.html
index b4c85173171..17c59240cc4 100644
--- a/controller-server/src/test/resources/mail/trial-expiring-soon.html
+++ b/controller-server/src/test/resources/mail/trial-expiring-soon.html
@@ -485,7 +485,7 @@
valign="middle"
>
<a
- href="https://dashboard.tld/trial-tenant"
+ href="https://console.tld/tenant/trial-tenant"
style="
display: inline-block;
background: #005a8e;
@@ -603,7 +603,7 @@
target="_blank"
rel="noopener noreferrer"
style="color: #005a8e"
- href="https://dashboard.tld/terms-of-service-trial.html"
+ href="https://console.tld/terms-of-service-trial.html"
><span style="color: #005a8e"
>Terms of Service</span
></a
@@ -613,7 +613,7 @@
target="_blank"
rel="noopener noreferrer"
style="color: #005a8e"
- href="https://dashboard.tld/support"
+ href="https://console.tld/support"
><span style="color: #005a8e">Support</span></a
>
</p>
@@ -622,7 +622,7 @@
target="_blank"
rel="noopener noreferrer"
style="color: inherit; text-decoration: none"
- href="https://dashboard.tld/tenant/trial-tenant/account/notifications"
+ href="https://console.tld/tenant/trial-tenant/account/notifications"
>Click
<span style="color: #005a8e"><u>here</u></span>
to manage your notifications setting.</a
diff --git a/controller-server/src/test/resources/mail/trial-reminder.html b/controller-server/src/test/resources/mail/trial-reminder.html
index 2644b187764..fbe0d573538 100644
--- a/controller-server/src/test/resources/mail/trial-reminder.html
+++ b/controller-server/src/test/resources/mail/trial-reminder.html
@@ -485,7 +485,7 @@
valign="middle"
>
<a
- href="https://dashboard.tld/trial-tenant"
+ href="https://console.tld/tenant/trial-tenant"
style="
display: inline-block;
background: #005a8e;
@@ -603,7 +603,7 @@
target="_blank"
rel="noopener noreferrer"
style="color: #005a8e"
- href="https://dashboard.tld/terms-of-service-trial.html"
+ href="https://console.tld/terms-of-service-trial.html"
><span style="color: #005a8e"
>Terms of Service</span
></a
@@ -613,7 +613,7 @@
target="_blank"
rel="noopener noreferrer"
style="color: #005a8e"
- href="https://dashboard.tld/support"
+ href="https://console.tld/support"
><span style="color: #005a8e">Support</span></a
>
</p>
@@ -622,7 +622,7 @@
target="_blank"
rel="noopener noreferrer"
style="color: inherit; text-decoration: none"
- href="https://dashboard.tld/tenant/trial-tenant/account/notifications"
+ href="https://console.tld/tenant/trial-tenant/account/notifications"
>Click
<span style="color: #005a8e"><u>here</u></span>
to manage your notifications setting.</a
diff --git a/controller-server/src/test/resources/mail/welcome.html b/controller-server/src/test/resources/mail/welcome.html
index a21a7cdf45f..2e652532db8 100644
--- a/controller-server/src/test/resources/mail/welcome.html
+++ b/controller-server/src/test/resources/mail/welcome.html
@@ -485,7 +485,7 @@
valign="middle"
>
<a
- href="https://dashboard.tld/trial-tenant"
+ href="https://console.tld/tenant/trial-tenant"
style="
display: inline-block;
background: #005a8e;
@@ -603,7 +603,7 @@
target="_blank"
rel="noopener noreferrer"
style="color: #005a8e"
- href="https://dashboard.tld/terms-of-service-trial.html"
+ href="https://console.tld/terms-of-service-trial.html"
><span style="color: #005a8e"
>Terms of Service</span
></a
@@ -613,7 +613,7 @@
target="_blank"
rel="noopener noreferrer"
style="color: #005a8e"
- href="https://dashboard.tld/support"
+ href="https://console.tld/support"
><span style="color: #005a8e">Support</span></a
>
</p>
@@ -622,7 +622,7 @@
target="_blank"
rel="noopener noreferrer"
style="color: inherit; text-decoration: none"
- href="https://dashboard.tld/tenant/trial-tenant/account/notifications"
+ href="https://console.tld/tenant/trial-tenant/account/notifications"
>Click
<span style="color: #005a8e"><u>here</u></span>
to manage your notifications setting.</a