aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/BillingDatabaseClientMock.java15
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/BillingReporterMock.java2
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/InvoiceUpdate.java12
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/MockBillingController.java13
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/BillingReportMaintainer.java7
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java2
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/BillingReportMaintainerTest.java52
7 files changed, 85 insertions, 18 deletions
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/BillingDatabaseClientMock.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/BillingDatabaseClientMock.java
index 300c1658c29..ee7679f54ca 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/BillingDatabaseClientMock.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/BillingDatabaseClientMock.java
@@ -29,6 +29,7 @@ public class BillingDatabaseClientMock implements BillingDatabaseClient {
private final Map<Bill.Id, Bill.StatusHistory> statuses = new HashMap<>();
private final Map<Bill.Id, ZonedDateTime> startTimes = new HashMap<>();
private final Map<Bill.Id, ZonedDateTime> endTimes = new HashMap<>();
+ private final Map<Bill.Id, String> exportedInvoiceIds = new HashMap<>();
private final ZonedDateTime startTime = LocalDate.of(2020, 4, 1).atStartOfDay(ZoneId.of("UTC"));
private final ZonedDateTime endTime = LocalDate.of(2020, 5, 1).atStartOfDay(ZoneId.of("UTC"));
@@ -74,7 +75,8 @@ public class BillingDatabaseClientMock implements BillingDatabaseClient {
var status = statuses.getOrDefault(billId, Bill.StatusHistory.open(clock));
var start = startTimes.getOrDefault(billId, startTime);
var end = endTimes.getOrDefault(billId, endTime);
- return invoice.map(tenant -> new Bill(billId, tenant, status, lines, start, end));
+ var exportedId = exportedInvoiceId(billId);
+ return invoice.map(tenant -> new Bill(billId, tenant, status, lines, start, end, exportedId));
}
@Override
@@ -157,7 +159,7 @@ public class BillingDatabaseClientMock implements BillingDatabaseClient {
var status = statuses.get(invoiceId);
var start = startTimes.get(invoiceId);
var end = endTimes.get(invoiceId);
- return new Bill(invoiceId, tenant, status, items, start, end);
+ return new Bill(invoiceId, tenant, status, items, start, end, exportedInvoiceId(invoiceId));
})
.toList();
}
@@ -171,7 +173,7 @@ public class BillingDatabaseClientMock implements BillingDatabaseClient {
var status = statuses.get(invoiceId);
var start = startTimes.get(invoiceId);
var end = endTimes.get(invoiceId);
- return new Bill(invoiceId, tenant, status, items, start, end);
+ return new Bill(invoiceId, tenant, status, items, start, end, exportedInvoiceId(invoiceId));
})
.toList();
}
@@ -180,9 +182,14 @@ public class BillingDatabaseClientMock implements BillingDatabaseClient {
public void maintain() {}
@Override
- public void setExportedInvoiceId(Bill.Id billId, String invoiceId) { }
+ public void setExportedInvoiceId(Bill.Id billId, String invoiceId) {
+ exportedInvoiceIds.put(billId, invoiceId);
+ }
@Override
public void setExportedInvoiceItemId(String lineItemId, String invoiceItemId) { }
+ private String exportedInvoiceId(Bill.Id billId) {
+ return exportedInvoiceIds.getOrDefault(billId, null);
+ }
}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/BillingReporterMock.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/BillingReporterMock.java
index 93bca72f05f..9531745556f 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/BillingReporterMock.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/BillingReporterMock.java
@@ -21,7 +21,7 @@ public class BillingReporterMock implements BillingReporter {
@Override
public InvoiceUpdate maintainInvoice(Bill bill) {
- return InvoiceUpdate.empty();
+ return new InvoiceUpdate(0,0,1);
}
}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/InvoiceUpdate.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/InvoiceUpdate.java
index 20d0dac02b6..6ca3cf6ebb1 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/InvoiceUpdate.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/InvoiceUpdate.java
@@ -14,12 +14,6 @@ public record InvoiceUpdate(int itemsAdded, int itemsRemoved, int itemsModified)
return new InvoiceUpdate(0, 0, 0);
}
- public InvoiceUpdate add(InvoiceUpdate other) {
- return new InvoiceUpdate(itemsAdded + other.itemsAdded,
- itemsRemoved + other.itemsRemoved,
- itemsModified + other.itemsModified);
- }
-
public static class Counter {
private int itemsAdded = 0;
private int itemsRemoved = 0;
@@ -37,6 +31,12 @@ public record InvoiceUpdate(int itemsAdded, int itemsRemoved, int itemsModified)
itemsModified++;
}
+ public void add(InvoiceUpdate other) {
+ itemsAdded += other.itemsAdded;
+ itemsRemoved += other.itemsRemoved;
+ itemsModified += other.itemsModified;
+ }
+
public InvoiceUpdate finish() {
return new InvoiceUpdate(itemsAdded, itemsRemoved, itemsModified);
}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/MockBillingController.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/MockBillingController.java
index 8ef14dd60ba..1b66fbb7bb4 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/MockBillingController.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/MockBillingController.java
@@ -2,6 +2,7 @@
package com.yahoo.vespa.hosted.controller.api.integration.billing;
import com.yahoo.config.provision.TenantName;
+import com.yahoo.vespa.hosted.controller.tenant.CloudTenant;
import java.math.BigDecimal;
import java.time.Clock;
@@ -22,6 +23,7 @@ import java.util.stream.Stream;
public class MockBillingController implements BillingController {
private final Clock clock;
+ private final BillingDatabaseClient dbClient;
PlanId defaultPlan = PlanId.from("trial");
List<TenantName> tenants = new ArrayList<>();
@@ -32,8 +34,9 @@ public class MockBillingController implements BillingController {
Map<TenantName, List<Bill.LineItem>> unusedLineItems = new HashMap<>();
Map<TenantName, CollectionMethod> collectionMethod = new HashMap<>();
- public MockBillingController(Clock clock) {
+ public MockBillingController(Clock clock, BillingDatabaseClient dbClient) {
this.clock = clock;
+ this.dbClient = dbClient;
}
@Override
@@ -203,6 +206,14 @@ public class MockBillingController implements BillingController {
return count < limit;
}
+ @Override
+ public String exportBill(Bill bill, String exportMethod, CloudTenant tenant) {
+ // Replace bill with a copy with exportedId set
+ var exportedId = "EXT-ID-123";
+ dbClient.setExportedInvoiceId(bill.id(), exportedId);
+ return exportedId;
+ }
+
public void setTenants(List<TenantName> tenants) {
this.tenants = tenants;
}
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 9cb45158bcb..15396649d2f 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,6 +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.BillingController;
import com.yahoo.vespa.hosted.controller.api.integration.billing.BillingDatabaseClient;
import com.yahoo.vespa.hosted.controller.api.integration.billing.BillingReporter;
@@ -62,17 +63,17 @@ public class BillingReportMaintainer extends ControllerMaintainer {
});
}
- private InvoiceUpdate maintainInvoices() {
+ 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.
.toList();
- var updates = InvoiceUpdate.empty();
+ var updates = new InvoiceUpdate.Counter();
for (var bill : billsNeedingMaintenance) {
updates.add(reporter.maintainInvoice(bill));
}
- return updates;
+ return updates.finish();
}
private Map<TenantName, CloudTenant> cloudTenants() {
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 e8e3de697dc..67f77b9a872 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
@@ -89,7 +89,6 @@ public class ServiceRegistryMock extends AbstractComponent implements ServiceReg
private final MockEnclaveAccessService mockAMIService = new MockEnclaveAccessService();
private final MockResourceTagger mockResourceTagger = new MockResourceTagger();
private final MockRoleService roleService = new MockRoleService();
- private final MockBillingController billingController = new MockBillingController(clock);
private final ArtifactRegistryMock containerRegistry = new ArtifactRegistryMock();
private final NoopTenantSecretService tenantSecretService = new NoopTenantSecretService();
private final NoopEndpointSecretManager secretManager = new NoopEndpointSecretManager();
@@ -100,6 +99,7 @@ public class ServiceRegistryMock extends AbstractComponent implements ServiceReg
private final PlanRegistry planRegistry = new PlanRegistryMock();
private final ResourceDatabaseClient resourceDb = new ResourceDatabaseClientMock(planRegistry);
private final BillingDatabaseClient billingDb = new BillingDatabaseClientMock(clock, planRegistry);
+ private final MockBillingController billingController = new MockBillingController(clock, billingDb);
private final RoleMaintainerMock roleMaintainer = new RoleMaintainerMock();
private final MockPricingController pricingController = new MockPricingController();
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 9e1aa64beae..bc6aefdd35c 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,13 +4,20 @@ 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.Bill;
+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;
import com.yahoo.vespa.hosted.controller.tenant.CloudTenant;
import org.junit.jupiter.api.Test;
import java.time.Duration;
+import java.time.LocalDate;
+import java.time.ZoneOffset;
+import java.util.List;
+import java.util.Map;
import java.util.Optional;
+import java.util.TreeMap;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -39,8 +46,49 @@ public class BillingReportMaintainerTest {
assertNotNull(b1.orElseThrow().reference());
}
+ @Test
+ void only_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");
+
+ billingController.setPlan(t1, PlanRegistryMock.paidPlan.id(), false, true);
+
+
+ billingController.exportBill(billingDb.readBill(bill2).get(), "FOO", cloudTenant(t1));
+ var updates = maintainer.maintainInvoices();
+ assertEquals(new InvoiceUpdate(0, 0, 1), updates);
+
+ var exportedBill = billingDb.readBill(bill2).get();
+ assertEquals("EXT-ID-123", exportedBill.getExportedId().get());
+ assertTrue(billingDb.readBill(bill1).get().getExportedId().isEmpty());
+ }
+
+ private CloudTenant cloudTenant(TenantName tenantName) {
+ return tester.controller().tenants().require(tenantName, CloudTenant.class);
+ }
+
private Optional<BillingReference> billingReference(TenantName tenantName) {
- var t = tester.controller().tenants().require(tenantName, CloudTenant.class);
- return t.billingReference();
+ return cloudTenant(tenantName).billingReference();
}
+
+ static Bill createBill(TenantName tenant) {
+ 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")));
+ return new Bill(
+ Bill.Id.of("bill-id-1"),
+ tenant,
+ statusHistory,
+ List.of(),
+ start,
+ end
+ );
+ }
+
}