summaryrefslogtreecommitdiffstats
path: root/controller-server/src
diff options
context:
space:
mode:
authorØyvind Grønnesby <oyving@verizonmedia.com>2021-11-03 12:39:12 +0100
committerØyvind Grønnesby <oyving@verizonmedia.com>2021-11-03 12:39:12 +0100
commit41bf6baf7256cfa2619457bb47a935b61d0e7a7b (patch)
tree5151e9a6a5c1d8b1ccba6cb02abd344a4b7763da /controller-server/src
parent8cbeb2b57269e64ea05ccf9f1044f57843a6e80c (diff)
Make the bill end date inclusive instead of exclusive
Before the end date for the billing was used to calculate startOfDay to generate the timestamp we actually use. Now we are changing it to .plusDays(1).startOfDay to make it so UI will effectively use the end of today as the timestamp. Also added some more implementations to ManualClock to get some tests to work.
Diffstat (limited to 'controller-server/src')
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandler.java12
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandlerV2.java36
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandlerTest.java9
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandlerV2Test.java7
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/responses/tenant-billing-view3
5 files changed, 38 insertions, 29 deletions
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 b8c8ce4e63a..3afc2c8fc15 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
@@ -111,8 +111,8 @@ public class BillingApiHandler extends LoggingRequestHandler {
.map(bill -> {
return new Object[] {
bill.id().value(), bill.tenant().value(),
- bill.getStartTime().format(DateTimeFormatter.ISO_LOCAL_DATE),
- bill.getEndTime().format(DateTimeFormatter.ISO_LOCAL_DATE),
+ bill.getStartDate().format(DateTimeFormatter.ISO_LOCAL_DATE),
+ bill.getEndDate().format(DateTimeFormatter.ISO_LOCAL_DATE),
bill.sumCpuHours(), bill.sumMemoryHours(), bill.sumDiskHours(),
bill.sumCpuCost(), bill.sumMemoryCost(), bill.sumDiskCost(),
bill.sumAdditionalCost()
@@ -246,7 +246,7 @@ public class BillingApiHandler extends LoggingRequestHandler {
LocalDate startDate = LocalDate.parse(getInspectorFieldOrThrow(inspector, "startTime"));
LocalDate endDate = LocalDate.parse(getInspectorFieldOrThrow(inspector, "endTime"));
ZonedDateTime startTime = startDate.atStartOfDay(ZoneId.of("UTC"));
- ZonedDateTime endTime = endDate.atStartOfDay(ZoneId.of("UTC"));
+ ZonedDateTime endTime = endDate.plusDays(1).atStartOfDay(ZoneId.of("UTC"));
var billId = billingController.createBillForPeriod(tenantName, startTime, endTime, userId);
@@ -332,7 +332,7 @@ public class BillingApiHandler extends LoggingRequestHandler {
if (currentUsage == null) return;
cursor.setString("amount", currentUsage.sum().toPlainString());
cursor.setString("status", "accrued");
- cursor.setString("from", currentUsage.getStartTime().format(DATE_TIME_FORMATTER));
+ cursor.setString("from", currentUsage.getStartDate().format(DATE_TIME_FORMATTER));
var itemsCursor = cursor.setArray("items");
currentUsage.lineItems().forEach(lineItem -> {
var itemCursor = itemsCursor.addObject();
@@ -363,8 +363,8 @@ public class BillingApiHandler extends LoggingRequestHandler {
private void renderBillToCursor(Cursor billCursor, Bill bill) {
billCursor.setString("id", bill.id().value());
- billCursor.setString("from", bill.getStartTime().format(DATE_TIME_FORMATTER));
- billCursor.setString("to", bill.getEndTime().format(DATE_TIME_FORMATTER));
+ billCursor.setString("from", bill.getStartDate().format(DATE_TIME_FORMATTER));
+ billCursor.setString("to", bill.getEndDate().format(DATE_TIME_FORMATTER));
billCursor.setString("amount", bill.sum().toString());
billCursor.setString("status", bill.status());
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 101321e4da3..f18d38e971b 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
@@ -35,6 +35,7 @@ import java.time.format.DateTimeFormatter;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
+import java.util.logging.Level;
/**
* @author ogronnesby
@@ -178,7 +179,7 @@ public class BillingApiHandlerV2 extends RestApiRequestHandler<BillingApiHandler
private Slime tenantUsage(RestApi.RequestContext requestContext) {
var tenantName = TenantName.from(requestContext.pathParameters().getStringOrThrow("tenant"));
var tenant = tenants.require(tenantName, CloudTenant.class);
- var untilAt = untilParameter(requestContext);
+ var untilAt = untilParameter(requestContext).orElseGet(clock::instant);
var usage = billing.createUncommittedBill(tenant.name(), untilAt.atZone(ZoneOffset.UTC).toLocalDate());
var slime = new Slime();
@@ -189,7 +190,7 @@ public class BillingApiHandlerV2 extends RestApiRequestHandler<BillingApiHandler
// --------- ACCOUNTANT API ----------
private Slime accountant(RestApi.RequestContext requestContext) {
- var untilAt = untilParameter(requestContext);
+ var untilAt = untilParameter(requestContext).orElseGet(clock::instant);
var usagePerTenant = billing.createUncommittedBills(untilAt.atZone(ZoneOffset.UTC).toLocalDate());
var response = new Slime();
@@ -200,7 +201,7 @@ public class BillingApiHandlerV2 extends RestApiRequestHandler<BillingApiHandler
tenantResponse.setString("tenant", tenant.name().value());
tenantResponse.setString("plan", billing.getPlan(tenant.name()).value());
tenantResponse.setString("collection", billing.getCollectionMethod(tenant.name()).name());
- tenantResponse.setString("lastBill", usage.map(Bill::getStartTime).map(DateTimeFormatter.ISO_DATE::format).orElse(null));
+ tenantResponse.setString("lastBill", usage.map(Bill::getStartDate).map(DateTimeFormatter.ISO_DATE::format).orElse(null));
tenantResponse.setString("unbilled", usage.map(Bill::sum).map(BigDecimal::toPlainString).orElse("0.00"));
});
@@ -210,7 +211,7 @@ public class BillingApiHandlerV2 extends RestApiRequestHandler<BillingApiHandler
private Slime previewBill(RestApi.RequestContext requestContext) {
var tenantName = TenantName.from(requestContext.pathParameters().getStringOrThrow("tenant"));
var tenant = tenants.require(tenantName, CloudTenant.class);
- var untilAt = untilParameter(requestContext);
+ var untilAt = untilParameter(requestContext).orElseGet(this::startOfDayTodayUTC);
var usage = billing.createUncommittedBill(tenant.name(), untilAt.atZone(ZoneOffset.UTC).toLocalDate());
@@ -229,7 +230,7 @@ public class BillingApiHandlerV2 extends RestApiRequestHandler<BillingApiHandler
var tenant = tenants.require(tenantName, CloudTenant.class);
var startAt = LocalDate.parse(getInspectorFieldOrThrow(body, "from")).atStartOfDay(ZoneOffset.UTC);
- var endAt = LocalDate.parse(getInspectorFieldOrThrow(body, "to")).atStartOfDay(ZoneOffset.UTC);
+ var endAt = LocalDate.parse(getInspectorFieldOrThrow(body, "to")).plusDays(1).atStartOfDay(ZoneOffset.UTC);
var invoiceId = billing.createBillForPeriod(tenant.name(), startAt, endAt, security.principal().getName());
@@ -246,23 +247,23 @@ public class BillingApiHandlerV2 extends RestApiRequestHandler<BillingApiHandler
private void invoiceSummaryToSlime(Cursor slime, Bill bill) {
slime.setString("id", bill.id().value());
- slime.setString("from", bill.getStartTime().format(DateTimeFormatter.ISO_LOCAL_DATE));
- slime.setString("to", bill.getStartTime().format(DateTimeFormatter.ISO_LOCAL_DATE));
+ 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());
}
private void usageToSlime(Cursor slime, Bill bill) {
- slime.setString("from", bill.getStartTime().format(DateTimeFormatter.ISO_LOCAL_DATE));
- slime.setString("to", bill.getStartTime().format(DateTimeFormatter.ISO_LOCAL_DATE));
+ slime.setString("from", bill.getStartDate().format(DateTimeFormatter.ISO_LOCAL_DATE));
+ slime.setString("to", bill.getEndTime().format(DateTimeFormatter.ISO_LOCAL_DATE));
slime.setString("total", bill.sum().toString());
toSlime(slime.setArray("items"), bill.lineItems());
}
private void toSlime(Cursor slime, Bill bill) {
slime.setString("id", bill.id().value());
- slime.setString("from", bill.getStartTime().format(DateTimeFormatter.ISO_LOCAL_DATE));
- slime.setString("to", bill.getStartTime().format(DateTimeFormatter.ISO_LOCAL_DATE));
+ 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());
toSlime(slime.setArray("statusHistory"), bill.statusHistory());
@@ -308,8 +309,8 @@ public class BillingApiHandlerV2 extends RestApiRequestHandler<BillingApiHandler
private List<Object[]> toCsv(Bill bill) {
return List.<Object[]>of(new Object[]{
bill.id().value(), bill.tenant().value(),
- bill.getStartTime().format(DateTimeFormatter.ISO_DATE),
- bill.getEndTime().format(DateTimeFormatter.ISO_DATE),
+ bill.getStartDate().format(DateTimeFormatter.ISO_DATE),
+ bill.getEndDate().format(DateTimeFormatter.ISO_DATE),
bill.sumCpuHours(), bill.sumMemoryHours(), bill.sumDiskHours(),
bill.sumCpuCost(), bill.sumMemoryCost(), bill.sumDiskCost(),
bill.sumAdditionalCost()
@@ -318,11 +319,14 @@ public class BillingApiHandlerV2 extends RestApiRequestHandler<BillingApiHandler
// ---------- END INVOICE RENDERING ----------
- private Instant untilParameter(RestApi.RequestContext ctx) {
+ private Optional<Instant> untilParameter(RestApi.RequestContext ctx) {
return ctx.queryParameters().getString("until")
.map(LocalDate::parse)
- .map(date -> date.plusDays(1).atStartOfDay(ZoneOffset.UTC).toInstant())
- .orElseGet(clock::instant);
+ .map(date -> date.plusDays(1).atStartOfDay(ZoneOffset.UTC).toInstant());
+ }
+
+ private Instant startOfDayTodayUTC() {
+ return LocalDate.now(clock.withZone(ZoneOffset.UTC)).atStartOfDay(ZoneOffset.UTC).toInstant();
}
private static String getInspectorFieldOrThrow(Inspector inspector, String field) {
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 3c4ed8bc8df..74ed1a0bb10 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
@@ -122,8 +122,11 @@ public class BillingApiHandlerTest extends ControllerContainerCloudTest {
bills = billingController.getBillsForTenant(tenant);
assertEquals(1, bills.size());
Bill bill = bills.get(0);
- assertEquals(bill.getStartTime().toString(), "2020-04-20T00:00Z[UTC]");
- assertEquals(bill.getEndTime().toString(), "2020-05-20T00:00Z[UTC]");
+ assertEquals("2020-04-20T00:00Z[UTC]", bill.getStartTime().toString());
+ assertEquals("2020-05-21T00:00Z[UTC]", bill.getEndTime().toString());
+
+ assertEquals("2020-04-20", bill.getStartDate().toString());
+ assertEquals("2020-05-20", bill.getEndDate().toString());
}
@Test
@@ -223,7 +226,7 @@ public class BillingApiHandlerTest extends ControllerContainerCloudTest {
static Bill createBill() {
var start = LocalDate.of(2020, 5, 23).atStartOfDay(ZoneOffset.UTC);
- var end = start.plusDays(5);
+ 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("id-1"),
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandlerV2Test.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandlerV2Test.java
index 178cb6f9909..c41616dbba5 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandlerV2Test.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandlerV2Test.java
@@ -13,6 +13,7 @@ import com.yahoo.vespa.hosted.controller.security.CloudTenantSpec;
import org.junit.Before;
import org.junit.Test;
+import java.time.Duration;
import java.time.Instant;
import java.util.Set;
@@ -105,10 +106,10 @@ public class BillingApiHandlerV2Test extends ControllerContainerCloudTest {
@Test
public void require_tenant_invoice() {
var listRequest = request("/billing/v2/tenant/" + tenant + "/bill").roles(tenantReader);
- tester.assertResponse(listRequest, "{\"invoices\":[{\"id\":\"id-1\",\"from\":\"2020-05-23\",\"to\":\"2020-05-23\",\"total\":\"123.00\",\"status\":\"OPEN\"}]}");
+ tester.assertResponse(listRequest, "{\"invoices\":[{\"id\":\"id-1\",\"from\":\"2020-05-23\",\"to\":\"2020-05-28\",\"total\":\"123.00\",\"status\":\"OPEN\"}]}");
var singleRequest = request("/billing/v2/tenant/" + tenant + "/bill/id-1").roles(tenantReader);
- tester.assertResponse(singleRequest, "{\"id\":\"id-1\",\"from\":\"2020-05-23\",\"to\":\"2020-05-23\",\"total\":\"123.00\",\"status\":\"OPEN\",\"statusHistory\":[{\"at\":\"2020-05-23T00:00:00Z\",\"status\":\"OPEN\"}],\"items\":[{\"id\":\"some-id\",\"description\":\"description\",\"amount\":\"123.00\",\"plan\":\"some-plan\",\"planName\":\"Plan with id: some-plan\",\"cpu\":{},\"memory\":{},\"disk\":{}}]}");
+ tester.assertResponse(singleRequest, "{\"id\":\"id-1\",\"from\":\"2020-05-23\",\"to\":\"2020-05-28\",\"total\":\"123.00\",\"status\":\"OPEN\",\"statusHistory\":[{\"at\":\"2020-05-23T00:00:00Z\",\"status\":\"OPEN\"}],\"items\":[{\"id\":\"some-id\",\"description\":\"description\",\"amount\":\"123.00\",\"plan\":\"some-plan\",\"planName\":\"Plan with id: some-plan\",\"cpu\":{},\"memory\":{},\"disk\":{}}]}");
}
@Test
@@ -126,7 +127,7 @@ public class BillingApiHandlerV2Test extends ControllerContainerCloudTest {
@Test
public void require_accountant_tenant_preview() {
var accountantRequest = request("/billing/v2/accountant/preview/tenant/tenant1").roles(Role.hostedAccountant());
- tester.assertResponse(accountantRequest, "{\"id\":\"empty\",\"from\":\"2021-04-13\",\"to\":\"2021-04-13\",\"total\":\"0.00\",\"status\":\"OPEN\",\"statusHistory\":[{\"at\":\"2021-04-13T00:00:00Z\",\"status\":\"OPEN\"}],\"items\":[]}");
+ tester.assertResponse(accountantRequest, "{\"id\":\"empty\",\"from\":\"2021-04-13\",\"to\":\"2021-04-12\",\"total\":\"0.00\",\"status\":\"OPEN\",\"statusHistory\":[{\"at\":\"2021-04-13T00:00:00Z\",\"status\":\"OPEN\"}],\"items\":[]}");
}
@Test
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/responses/tenant-billing-view b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/responses/tenant-billing-view
index c0deca3d671..e5588c45677 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/responses/tenant-billing-view
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/responses/tenant-billing-view
@@ -21,7 +21,8 @@
{
"id":"id-1",
"from":"2020-05-23",
- "to":"2020-05-28","amount":"123.00",
+ "to":"2020-05-28",
+ "amount":"123.00",
"status":"OPEN",
"statusHistory":[
{