diff options
author | Øyvind Grønnesby <ogr@ogr.no> | 2021-10-11 12:02:31 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-10-11 12:02:31 +0200 |
commit | 292ba88e1a7254982deb5894248109b539e6ee1f (patch) | |
tree | ee1ac4b6943c593f4c883c5f2f6241608f83dfad /controller-api/src/main | |
parent | 11a50513dc58eeb52d290fe3dc1d826f27069d26 (diff) |
Revert "Revert "Revert "Billing refactoring and cleanup"""
Diffstat (limited to 'controller-api/src/main')
11 files changed, 46 insertions, 771 deletions
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/ServiceRegistry.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/ServiceRegistry.java index 93e3f5585c8..4714b74ba94 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/ServiceRegistry.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/ServiceRegistry.java @@ -7,8 +7,6 @@ import com.yahoo.vespa.hosted.controller.api.integration.aws.RoleService; import com.yahoo.vespa.hosted.controller.api.integration.aws.CloudEventFetcher; import com.yahoo.vespa.hosted.controller.api.integration.aws.ResourceTagger; 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.PlanRegistry; import com.yahoo.vespa.hosted.controller.api.integration.certificates.EndpointCertificateProvider; import com.yahoo.vespa.hosted.controller.api.integration.certificates.EndpointCertificateValidator; import com.yahoo.vespa.hosted.controller.api.integration.configserver.ConfigServer; @@ -27,7 +25,6 @@ import com.yahoo.vespa.hosted.controller.api.integration.organization.OwnershipI import com.yahoo.vespa.hosted.controller.api.integration.organization.SystemMonitor; import com.yahoo.vespa.hosted.controller.api.integration.resource.CostReportConsumer; import com.yahoo.vespa.hosted.controller.api.integration.resource.MeteringClient; -import com.yahoo.vespa.hosted.controller.api.integration.resource.ResourceDatabaseClient; import com.yahoo.vespa.hosted.controller.api.integration.routing.GlobalRoutingService; import com.yahoo.vespa.hosted.controller.api.integration.secrets.TenantSecretService; import com.yahoo.vespa.hosted.controller.api.integration.vcmr.ChangeRequestClient; @@ -91,10 +88,6 @@ public interface ServiceRegistry { BillingController billingController(); - ResourceDatabaseClient resourceDatabase(); - - BillingDatabaseClient billingDatabase(); - ContainerRegistry containerRegistry(); TenantSecretService tenantSecretService(); @@ -106,6 +99,4 @@ public interface ServiceRegistry { AccessControlService accessControlService(); HorizonClient horizonClient(); - - PlanRegistry planRegistry(); } diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/BillingController.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/BillingController.java index 61f8844482c..91916975146 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/BillingController.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/BillingController.java @@ -12,112 +12,55 @@ import java.util.Map; import java.util.Optional; import java.util.Set; -/** - * A service that controls creation of bills based on the resource usage of a tenant, controls the quota for a - * tenant, and controls the plan the tenant is on. - * - * @author ogronnesby - * @author olaa - */ public interface BillingController { - /** - * Get the plan ID for the given tenant. - * This method will not fail if the tenant does not exist, it will return the default plan for that tenant instead. - */ PlanId getPlan(TenantName tenant); - /** - * Return the list of tenants with the given plan. - * @param existing All existing tenants in the system - * @param planId The ID of the plan to filter existing tenants on. - * @return The tenants that have the given plan. - */ List<TenantName> tenantsWithPlan(List<TenantName> existing, PlanId planId); - /** The display name of the given plan */ String getPlanDisplayName(PlanId planId); - /** - * The quota for the given tenant. - * This method will return default quota for tenants that do not exist. - */ Quota getQuota(TenantName tenant); /** - * Set the plan for the current tenant. Checks some pre-conditions to see if the tenant is eligible for the - * given plan. - * @param tenant The name of the tenant. - * @param planId The ID of the plan to change to. - * @param hasDeployments Does the tenant have active deployments. * @return String containing error message if something went wrong. Empty otherwise */ PlanResult setPlan(TenantName tenant, PlanId planId, boolean hasDeployments); - /** - * Create a bill of unbilled use for the given tenant in the given time period. - * @param tenant The name of the tenant. - * @param startTime The start of the billing period - * @param endTime The end of the billing period - * @param agent The agent that creates the bill - * @return The ID of the new bill. - */ - Bill.Id createBillForPeriod(TenantName tenant, ZonedDateTime startTime, ZonedDateTime endTime, String agent); + Invoice.Id createInvoiceForPeriod(TenantName tenant, ZonedDateTime startTime, ZonedDateTime endTime, String agent); - /** - * Create an unpersisted bill of unbilled use for the given tenant from the end of last bill until the given date. - * This is used to show "unbilled use" in the Console. - * @param tenant The name of the tenant. - * @param until The end date of the unbilled use period. - * @return A bill with the resource use and cost. - */ - Bill createUncommittedBill(TenantName tenant, LocalDate until); + Invoice createUncommittedInvoice(TenantName tenant, LocalDate until); - /** Run {createUncommittedBill} for all tenants with unbilled use */ - Map<TenantName, Bill> createUncommittedBills(LocalDate until); + Map<TenantName, Invoice> createUncommittedInvoices(LocalDate until); - /** Get line items that have been manually added to a tenant, but is not yet part of a bill */ - List<Bill.LineItem> getUnusedLineItems(TenantName tenant); + List<Invoice.LineItem> getUnusedLineItems(TenantName tenant); - /** Get the payment instrument for the given tenant */ Optional<PaymentInstrument> getDefaultInstrument(TenantName tenant); - /** Get the auth token needed to talk to payment services */ String createClientToken(String tenant, String userId); - /** Delete a payment instrument from the list of the tenant's instruments */ boolean deleteInstrument(TenantName tenant, String userId, String instrumentId); - /** Change the status of the given bill */ - void updateBillStatus(Bill.Id billId, String agent, String status); + void updateInvoiceStatus(Invoice.Id invoiceId, String agent, String status); - /** Add a line item to the given bill */ void addLineItem(TenantName tenant, String description, BigDecimal amount, String agent); - /** Delete a line item - only available for unused line items */ void deleteLineItem(String lineItemId); - /** Set the given payment instrument as the active instrument for the tenant */ boolean setActivePaymentInstrument(InstrumentOwner paymentInstrument); - /** List the payment instruments from the tenant */ InstrumentList listInstruments(TenantName tenant, String userId); - /** Get all bills for the given tenant */ - List<Bill> getBillsForTenant(TenantName tenant); + List<Invoice> getInvoicesForTenant(TenantName tenant); - /** Get all bills from the system */ - List<Bill> getBills(); + List<Invoice> getInvoices(); - /** Delete billing contact information from the tenant */ void deleteBillingInfo(TenantName tenant, Set<User> users, boolean isPrivileged); - /** Get the bill collection method for the given tenant */ default CollectionMethod getCollectionMethod(TenantName tenant) { return CollectionMethod.NONE; } - /** Set the bill collection method for the given tenant */ default CollectionResult setCollectionMethod(TenantName tenant, CollectionMethod method) { return CollectionResult.error("Method not implemented"); } diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/BillingDatabaseClient.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/BillingDatabaseClient.java deleted file mode 100644 index 4891fe0ffa7..00000000000 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/BillingDatabaseClient.java +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.hosted.controller.api.integration.billing; - -import com.yahoo.config.provision.TenantName; - -import java.time.ZonedDateTime; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -/** - * Interface that talks about bills in the billing API. It is a layer on top of the SQL - * database where we store data about bills. - * - * @author olaa - * @author ogronnesby - */ -public interface BillingDatabaseClient { - - boolean setActivePaymentInstrument(InstrumentOwner paymentInstrument); - - Optional<InstrumentOwner> getDefaultPaymentInstrumentForTenant(TenantName from); - - /** - * Create a completely new Bill in the open state with no LineItems. - * - * @param tenant The name of the tenant the bill is for - * @param agent The agent that created the bill - * @return The Id of the new bill - */ - Bill.Id createBill(TenantName tenant, ZonedDateTime startTime, ZonedDateTime endTime, String agent); - - /** - * Read the given bill from the data source - * - * @param billId The Id of the bill to retrieve - * @return The Bill if it exists, Optional.empty() if not. - */ - Optional<Bill> readBill(Bill.Id billId); - - /** - * Get all bills for a given tenant, ordered by date - * - * @param tenant The name of the tenant - * @return List of all bills ordered by date - */ - List<Bill> readBillsForTenant(TenantName tenant); - - /** - * Read all bills, ordered by date - * @return List of all bills ordered by date - */ - List<Bill> readBills(); - - /** - * Add a line item to an open bill - * - * @param lineItem - * @param billId The optional ID of the bill this line item is for - * @return The Id of the new line item - * @throws RuntimeException if the bill is not in OPEN state - */ - String addLineItem(TenantName tenantName, Bill.LineItem lineItem, Optional<Bill.Id> billId); - - /** - * Set status for the given bill - * - * @param billId The ID of the bill this status is for - * @param agent The agent that added the status - * @param status The new status of the bill - */ - void setStatus(Bill.Id billId, String agent, String status); - - List<Bill.LineItem> getUnusedLineItems(TenantName tenantName); - - /** - * Delete a line item - * This is only allowed if the line item has not yet been associated with an bill - * - * @param lineItemId The ID of the line item - * @throws RuntimeException if the line item is associated with an bill - */ - void deleteLineItem(String lineItemId); - - /** - * Associate all uncommitted line items to a given bill - * This is only allowed if the line item has not already been associated with an bill - * - * @param tenantName The tenant we want to commit line items for - * @param billId The ID of the line item - * @throws RuntimeException if the line item is already associated with an bill - */ - void commitLineItems(TenantName tenantName, Bill.Id billId); - - /** - * Return the plan for the given tenant - * - * @param tenantName The tenant to retrieve the plan for - * @return Optional.of the plan if present in DN, else Optional.empty - */ - Optional<Plan> getPlan(TenantName tenantName); - - /** - * Return the plan for the given tenants if present. - * If the database does not know of the tenant, the tenant is not included in the result. - */ - Map<TenantName, Optional<Plan>> getPlans(List<TenantName> tenants); - - /** - * Set the current plan for the given tenant - * - * @param tenantName The tenant to set the plan for - * @param plan The plan to use - */ - void setPlan(TenantName tenantName, Plan plan); - - /** - * Deactivates the default payment instrument for a tenant, if it exists. - * Used during tenant deletion - */ - void deactivateDefaultPaymentInstrument(TenantName tenant); - - /** - * Get the current collection method for the tenant - if one has persisted - * @return Optional.empty if no collection method has been persisted for the tenant - */ - Optional<CollectionMethod> getCollectionMethod(TenantName tenantName); - - /** - * Set the collection method for the tenant - * @param tenantName The name of the tenant to set collection method for - * @param collectionMethod The collection method for the tenant - */ - void setCollectionMethod(TenantName tenantName, CollectionMethod collectionMethod); -} 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 deleted file mode 100644 index f53025a2e6d..00000000000 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/BillingDatabaseClientMock.java +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.hosted.controller.api.integration.billing; - -import com.yahoo.config.provision.TenantName; - -import java.time.Clock; -import java.time.LocalDate; -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.UUID; -import java.util.stream.Collectors; - -/** - * @author olaa - */ -public class BillingDatabaseClientMock implements BillingDatabaseClient { - private final Clock clock; - private final PlanRegistry planRegistry; - private final Map<TenantName, Plan> tenantPlans = new HashMap<>(); - private final Map<Bill.Id, TenantName> invoices = new HashMap<>(); - private final Map<Bill.Id, List<Bill.LineItem>> lineItems = new HashMap<>(); - private final Map<TenantName, List<Bill.LineItem>> uncommittedLineItems = new HashMap<>(); - - 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 ZonedDateTime startTime = LocalDate.of(2020, 4, 1).atStartOfDay(ZoneId.of("UTC")); - private final ZonedDateTime endTime = LocalDate.of(2020, 5, 1).atStartOfDay(ZoneId.of("UTC")); - - private final List<InstrumentOwner> paymentInstruments = new ArrayList<>(); - private final Map<TenantName, CollectionMethod> collectionMethods = new HashMap<>(); - - public BillingDatabaseClientMock(Clock clock, PlanRegistry planRegistry) { - this.clock = clock; - this.planRegistry = planRegistry; - } - - @Override - public boolean setActivePaymentInstrument(InstrumentOwner paymentInstrument) { - return paymentInstruments.add(paymentInstrument); - } - - @Override - public Optional<InstrumentOwner> getDefaultPaymentInstrumentForTenant(TenantName tenantName) { - return paymentInstruments.stream() - .filter(paymentInstrument -> paymentInstrument.getTenantName().equals(tenantName)) - .findFirst(); - } - - public String getStatus(Bill.Id invoiceId) { - return statuses.get(invoiceId).current(); - } - - @Override - public Bill.Id createBill(TenantName tenant, ZonedDateTime startTime, ZonedDateTime endTime, String agent) { - var invoiceId = Bill.Id.generate(); - invoices.put(invoiceId, tenant); - statuses.computeIfAbsent(invoiceId, l -> Bill.StatusHistory.open(clock)); - startTimes.put(invoiceId, startTime); - endTimes.put(invoiceId, endTime); - return invoiceId; - } - - @Override - public Optional<Bill> readBill(Bill.Id billId) { - var invoice = Optional.ofNullable(invoices.get(billId)); - var lines = lineItems.getOrDefault(billId, List.of()); - 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)); - } - - @Override - public String addLineItem(TenantName tenantName, Bill.LineItem lineItem, Optional<Bill.Id> invoiceId) { - var lineItemId = UUID.randomUUID().toString(); - invoiceId.ifPresentOrElse( - invoice -> lineItems.computeIfAbsent(invoice, l -> new ArrayList<>()).add(lineItem), - () -> uncommittedLineItems.computeIfAbsent(tenantName, l -> new ArrayList<>()).add(lineItem) - ); - return lineItemId; - } - - @Override - public void setStatus(Bill.Id invoiceId, String agent, String status) { - statuses.computeIfAbsent(invoiceId, k -> Bill.StatusHistory.open(clock)) - .getHistory() - .put(ZonedDateTime.now(), status); - } - - @Override - public List<Bill.LineItem> getUnusedLineItems(TenantName tenantName) { - return uncommittedLineItems.getOrDefault(tenantName, new ArrayList<>()); - } - - @Override - public void deleteLineItem(String lineItemId) { - uncommittedLineItems.values() - .forEach(list -> - list.removeIf(lineItem -> lineItem.id().equals(lineItemId)) - ); - } - - @Override - public void commitLineItems(TenantName tenantName, Bill.Id invoiceId) { - - } - - @Override - public Optional<Plan> getPlan(TenantName tenantName) { - return Optional.ofNullable(tenantPlans.get(tenantName)); - } - - @Override - public Map<TenantName, Optional<Plan>> getPlans(List<TenantName> tenants) { - return tenantPlans.entrySet().stream() - .filter(entry -> tenants.contains(entry.getKey())) - .collect(Collectors.toMap( - entry -> entry.getKey(), - entry -> planRegistry.plan(entry.getValue().id()) - )); - } - - @Override - public void setPlan(TenantName tenantName, Plan plan) { - tenantPlans.put(tenantName, plan); - } - - @Override - public void deactivateDefaultPaymentInstrument(TenantName tenant) { - paymentInstruments.removeIf(instrumentOwner -> instrumentOwner.getTenantName().equals(tenant)); - } - - @Override - public Optional<CollectionMethod> getCollectionMethod(TenantName tenantName) { - return Optional.ofNullable(collectionMethods.get(tenantName)); - } - - @Override - public void setCollectionMethod(TenantName tenantName, CollectionMethod collectionMethod) { - collectionMethods.put(tenantName, collectionMethod); - } - - @Override - public List<Bill> readBillsForTenant(TenantName tenant) { - return invoices.entrySet().stream() - .filter(entry -> entry.getValue().equals(tenant)) - .map(Map.Entry::getKey) - .map(invoiceId -> { - var items = lineItems.getOrDefault(invoiceId, List.of()); - var status = statuses.get(invoiceId); - var start = startTimes.get(invoiceId); - var end = endTimes.get(invoiceId); - return new Bill(invoiceId, tenant, status, items, start, end); - }) - .collect(Collectors.toList()); - } - - @Override - public List<Bill> readBills() { - return invoices.keySet().stream() - .map(invoiceId -> { - var tenant = invoices.get(invoiceId); - var items = lineItems.getOrDefault(invoiceId, List.of()); - var status = statuses.get(invoiceId); - var start = startTimes.get(invoiceId); - var end = endTimes.get(invoiceId); - return new Bill(invoiceId, tenant, status, items, start, end); - }) - .collect(Collectors.toList()); - } -} diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/Bill.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/Invoice.java index d1af5b428de..3789021ae8e 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/Bill.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/Invoice.java @@ -20,18 +20,18 @@ import java.util.function.Function; /** - * An Bill is an identifier with a status (with history) and line items. A line item is the meat and - * potatoes of the content of the bill, and are a history of items. Most line items are connected to + * An Invoice is an identifier with a status (with history) and line items. A line item is the meat and + * potatoes of the content of the invoice, and are a history of items. Most line items are connected to * a given deployment in Vespa Cloud, but they can also be manually added to e.g. give a discount or represent * support. * <p> * All line items have a Plan associated with them - which was used to map from utilization to an actual price. * <p> - * The bill has a status history, but only the latest status is exposed through this API. + * The invoice has a status history, but only the latest status is exposed through this API. * * @author ogronnesby */ -public class Bill { +public class Invoice { private static final BigDecimal SCALED_ZERO = new BigDecimal("0.00"); private final Id id; @@ -41,7 +41,7 @@ public class Bill { private final ZonedDateTime startTime; private final ZonedDateTime endTime; - public Bill(Id id, TenantName tenant, StatusHistory statusHistory, List<LineItem> lineItems, ZonedDateTime startTime, ZonedDateTime endTime) { + public Invoice(Id id, TenantName tenant, StatusHistory statusHistory, List<LineItem> lineItems, ZonedDateTime startTime, ZonedDateTime endTime) { this.id = id; this.tenant = tenant; this.lineItems = List.copyOf(lineItems); @@ -141,8 +141,8 @@ public class Bill { public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - Id billId = (Id) o; - return value.equals(billId.value); + Id invoiceId = (Id) o; + return value.equals(invoiceId.value); } @Override @@ -152,14 +152,14 @@ public class Bill { @Override public String toString() { - return "BillId{" + + return "InvoiceId{" + "value='" + value + '\'' + '}'; } } /** - * Represents a chargeable line on a bill. + * Represents a chargeable line on an invoice. */ public static class LineItem { private final String id; 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 f4d3577aeec..8816c4eb57b 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 @@ -26,9 +26,9 @@ public class MockBillingController implements BillingController { private final Clock clock; Map<TenantName, PlanId> plans = new HashMap<>(); Map<TenantName, PaymentInstrument> activeInstruments = new HashMap<>(); - Map<TenantName, List<Bill>> committedBills = new HashMap<>(); - Map<TenantName, Bill> uncommittedBills = new HashMap<>(); - Map<TenantName, List<Bill.LineItem>> unusedLineItems = new HashMap<>(); + Map<TenantName, List<Invoice>> committedInvoices = new HashMap<>(); + Map<TenantName, Invoice> uncommittedInvoices = new HashMap<>(); + Map<TenantName, List<Invoice.LineItem>> unusedLineItems = new HashMap<>(); Map<TenantName, CollectionMethod> collectionMethod = new HashMap<>(); public MockBillingController(Clock clock) { @@ -64,32 +64,32 @@ public class MockBillingController implements BillingController { } @Override - public Bill.Id createBillForPeriod(TenantName tenant, ZonedDateTime startTime, ZonedDateTime endTime, String agent) { - var billId = Bill.Id.of("id-123"); - committedBills.computeIfAbsent(tenant, l -> new ArrayList<>()) - .add(new Bill( - billId, + public Invoice.Id createInvoiceForPeriod(TenantName tenant, ZonedDateTime startTime, ZonedDateTime endTime, String agent) { + var invoiceId = Invoice.Id.of("id-123"); + committedInvoices.computeIfAbsent(tenant, l -> new ArrayList<>()) + .add(new Invoice( + invoiceId, tenant, - Bill.StatusHistory.open(clock), + Invoice.StatusHistory.open(clock), List.of(), startTime, endTime )); - return billId; + return invoiceId; } @Override - public Bill createUncommittedBill(TenantName tenant, LocalDate until) { - return uncommittedBills.getOrDefault(tenant, emptyBill()); + public Invoice createUncommittedInvoice(TenantName tenant, LocalDate until) { + return uncommittedInvoices.getOrDefault(tenant, emptyInvoice()); } @Override - public Map<TenantName, Bill> createUncommittedBills(LocalDate until) { - return uncommittedBills; + public Map<TenantName, Invoice> createUncommittedInvoices(LocalDate until) { + return uncommittedInvoices; } @Override - public List<Bill.LineItem> getUnusedLineItems(TenantName tenant) { + public List<Invoice.LineItem> getUnusedLineItems(TenantName tenant) { return unusedLineItems.getOrDefault(tenant, List.of()); } @@ -110,18 +110,18 @@ public class MockBillingController implements BillingController { } @Override - public void updateBillStatus(Bill.Id billId, String agent, String status) { + public void updateInvoiceStatus(Invoice.Id invoiceId, String agent, String status) { var now = clock.instant().atZone(ZoneOffset.UTC); - committedBills.values().stream() + committedInvoices.values().stream() .flatMap(List::stream) - .filter(bill -> billId.equals(bill.id())) - .forEach(bill -> bill.statusHistory().history.put(now, status)); + .filter(invoice -> invoiceId.equals(invoice.id())) + .forEach(invoice -> invoice.statusHistory().history.put(now, status)); } @Override public void addLineItem(TenantName tenant, String description, BigDecimal amount, String agent) { unusedLineItems.computeIfAbsent(tenant, l -> new ArrayList<>()) - .add(new Bill.LineItem( + .add(new Invoice.LineItem( "line-item-id", description, amount, @@ -152,13 +152,13 @@ public class MockBillingController implements BillingController { } @Override - public List<Bill> getBillsForTenant(TenantName tenant) { - return committedBills.getOrDefault(tenant, List.of()); + public List<Invoice> getInvoicesForTenant(TenantName tenant) { + return committedInvoices.getOrDefault(tenant, List.of()); } @Override - public List<Bill> getBills() { - return committedBills.values().stream().flatMap(Collection::stream).collect(Collectors.toList()); + public List<Invoice> getInvoices() { + return committedInvoices.values().stream().flatMap(Collection::stream).collect(Collectors.toList()); } @Override @@ -191,17 +191,17 @@ public class MockBillingController implements BillingController { "country"); } - public void addBill(TenantName tenantName, Bill bill, boolean committed) { + public void addInvoice(TenantName tenantName, Invoice invoice, boolean committed) { if (committed) - committedBills.computeIfAbsent(tenantName, i -> new ArrayList<>()) - .add(bill); + committedInvoices.computeIfAbsent(tenantName, i -> new ArrayList<>()) + .add(invoice); else - uncommittedBills.put(tenantName, bill); + uncommittedInvoices.put(tenantName, invoice); } - private Bill emptyBill() { + private Invoice emptyInvoice() { var start = clock.instant().atZone(ZoneOffset.UTC); var end = clock.instant().atZone(ZoneOffset.UTC); - return new Bill(Bill.Id.of("empty"), TenantName.defaultName(), Bill.StatusHistory.open(clock), List.of(), start, end); + return new Invoice(Invoice.Id.of("empty"), TenantName.defaultName(), Invoice.StatusHistory.open(clock), List.of(), start, end); } } diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/PlanRegistry.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/PlanRegistry.java deleted file mode 100644 index d64d6e3ea04..00000000000 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/PlanRegistry.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.yahoo.vespa.hosted.controller.api.integration.billing; - -import java.util.Optional; - -/** - * Registry of all current plans we have support for - * - * @author ogronnesby - */ -public interface PlanRegistry { - - /** Get the default plan */ - Plan defaultPlan(); - - /** Get a plan given a plan ID */ - Optional<Plan> plan(PlanId planId); - - /** Get a plan give a plan ID */ - default Optional<Plan> plan(String planId) { - return plan(PlanId.from(planId)); - } - -} diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/PlanRegistryMock.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/PlanRegistryMock.java deleted file mode 100644 index 60eddbd24ff..00000000000 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/PlanRegistryMock.java +++ /dev/null @@ -1,122 +0,0 @@ -package com.yahoo.vespa.hosted.controller.api.integration.billing; - -import com.yahoo.config.provision.NodeResources; -import com.yahoo.vespa.hosted.controller.api.integration.resource.CostInfo; -import com.yahoo.vespa.hosted.controller.api.integration.resource.ResourceUsage; - -import java.math.BigDecimal; -import java.math.RoundingMode; -import java.util.Optional; -import java.util.stream.Stream; - -public class PlanRegistryMock implements PlanRegistry { - - public static final Plan freeTrial = new MockPlan("trial", false, 0, 0, 0, 200, "Free Trial - for testing purposes"); - public static final Plan paidPlan = new MockPlan("paid", true, "0.09", "0.009", "0.0003", 500, "Paid Plan - for testing purposes"); - public static final Plan nonePlan = new MockPlan("none", false, 0, 0, 0, 0, "None Plan - for testing purposes"); - - @Override - public Plan defaultPlan() { - return freeTrial; - } - - @Override - public Optional<Plan> plan(PlanId planId) { - return Stream.of(freeTrial, paidPlan, nonePlan) - .filter(p -> p.id().equals(planId)) - .findAny(); - } - - private static class MockPlan implements Plan { - private final PlanId planId; - private final String description; - private final CostCalculator costCalculator; - private final QuotaCalculator quotaCalculator; - private final boolean billed; - - public MockPlan(String planId, boolean billed, double cpuPrice, double memPrice, double dgbPrice, int quota, String description) { - this(PlanId.from(planId), billed, new MockCostCalculator(cpuPrice, memPrice, dgbPrice), () -> Quota.unlimited().withBudget(quota), description); - } - - public MockPlan(String planId, boolean billed, String cpuPrice, String memPrice, String dgbPrice, int quota, String description) { - this(PlanId.from(planId), billed, new MockCostCalculator(cpuPrice, memPrice, dgbPrice), () -> Quota.unlimited().withBudget(quota), description); - } - - public MockPlan(PlanId planId, boolean billed, MockCostCalculator calculator, QuotaCalculator quota, String description) { - this.planId = planId; - this.billed = billed; - this.costCalculator = calculator; - this.quotaCalculator = quota; - this.description = description; - } - - @Override - public PlanId id() { - return planId; - } - - @Override - public String displayName() { - return description; - } - - @Override - public CostCalculator calculator() { - return costCalculator; - } - - @Override - public QuotaCalculator quota() { - return quotaCalculator; - } - - @Override - public boolean isBilled() { - return billed; - } - } - - private static class MockCostCalculator implements CostCalculator { - private static final BigDecimal millisPerHour = BigDecimal.valueOf(60 * 60 * 1000); - private final BigDecimal cpuHourCost; - private final BigDecimal memHourCost; - private final BigDecimal dgbHourCost; - - public MockCostCalculator(String cpuPrice, String memPrice, String dgbPrice) { - this(new BigDecimal(cpuPrice), new BigDecimal(memPrice), new BigDecimal(dgbPrice)); - } - - public MockCostCalculator(double cpuPrice, double memPrice, double dgbPrice) { - this(BigDecimal.valueOf(cpuPrice), BigDecimal.valueOf(memPrice), BigDecimal.valueOf(dgbPrice)); - } - - public MockCostCalculator(BigDecimal cpuPrice, BigDecimal memPrice, BigDecimal dgbPrice) { - this.cpuHourCost = cpuPrice; - this.memHourCost = memPrice; - this.dgbHourCost = dgbPrice; - } - - @Override - public CostInfo calculate(ResourceUsage usage) { - var cpuCost = usage.getCpuMillis().multiply(cpuHourCost).divide(millisPerHour, RoundingMode.HALF_UP).setScale(2, RoundingMode.HALF_UP); - var memCost = usage.getMemoryMillis().multiply(memHourCost).divide(millisPerHour, RoundingMode.HALF_UP).setScale(2, RoundingMode.HALF_UP); - var dgbCost = usage.getDiskMillis().multiply(dgbHourCost).divide(millisPerHour, RoundingMode.HALF_UP).setScale(2, RoundingMode.HALF_UP); - - return new CostInfo( - usage.getApplicationId(), - usage.getZoneId(), - usage.getCpuMillis().divide(millisPerHour, RoundingMode.HALF_UP), - usage.getMemoryMillis().divide(millisPerHour, RoundingMode.HALF_UP), - usage.getDiskMillis().divide(millisPerHour, RoundingMode.HALF_UP), - cpuCost, - memCost, - dgbCost - ); - } - - @Override - public double calculate(NodeResources resources) { - return resources.cost(); - } - } -} diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceDatabaseClient.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceDatabaseClient.java deleted file mode 100644 index 2f277193231..00000000000 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceDatabaseClient.java +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.hosted.controller.api.integration.resource; - -import com.yahoo.config.provision.ApplicationName; -import com.yahoo.config.provision.TenantName; - -import java.time.LocalDate; -import java.time.YearMonth; -import java.time.temporal.ChronoUnit; -import java.util.Collection; -import java.util.List; -import java.util.Set; - -/** - * @author olaa - */ -public interface ResourceDatabaseClient { - - void writeResourceSnapshots(Collection<ResourceSnapshot> snapshots); - - List<ResourceSnapshot> getResourceSnapshotsForMonth(TenantName tenantName, ApplicationName applicationName, YearMonth month); - - List<ResourceUsage> getResourceSnapshotsForPeriod(TenantName tenantName, long startTimestamp, long endTimestamp); - - void refreshMaterializedView(); - - Set<YearMonth> getMonthsWithSnapshotsForTenant(TenantName tenantName); - - List<ResourceSnapshot> getRawSnapshotHistoryForTenant(TenantName tenantName, YearMonth yearMonth); - - Set<TenantName> getTenants(); - - default List<ResourceUsage> getResourceSnapshotsForMonth(TenantName tenantName, YearMonth month) { - return getResourceSnapshotsForPeriod(tenantName, getMonthStartTimeStamp(month), getMonthEndTimeStamp(month)); - } - - private long getMonthStartTimeStamp(YearMonth month) { - LocalDate startOfMonth = LocalDate.of(month.getYear(), month.getMonth(), 1); - return startOfMonth.atStartOfDay(java.time.ZoneId.of("UTC")) - .toInstant() - .toEpochMilli(); - } - private long getMonthEndTimeStamp(YearMonth month) { - LocalDate startOfMonth = LocalDate.of(month.getYear(), month.getMonth(), 1); - return startOfMonth.plus(1, ChronoUnit.MONTHS) - .atStartOfDay(java.time.ZoneId.of("UTC")) - .toInstant() - .toEpochMilli(); - } - -} diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceDatabaseClientMock.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceDatabaseClientMock.java deleted file mode 100644 index 5a4d250ea9d..00000000000 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceDatabaseClientMock.java +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.hosted.controller.api.integration.resource; - -import com.yahoo.config.provision.ApplicationName; -import com.yahoo.config.provision.TenantName; -import com.yahoo.vespa.hosted.controller.api.integration.billing.Plan; -import com.yahoo.vespa.hosted.controller.api.integration.billing.PlanRegistry; - -import java.math.BigDecimal; -import java.time.Duration; -import java.time.Instant; -import java.time.LocalDate; -import java.time.YearMonth; -import java.time.ZoneId; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -/** - * @author olaa - */ -public class ResourceDatabaseClientMock implements ResourceDatabaseClient { - - PlanRegistry planRegistry; - Map<TenantName, Plan> planMap = new HashMap<>(); - List<ResourceSnapshot> resourceSnapshots = new ArrayList<>(); - private boolean hasRefreshedMaterializedView = false; - - public ResourceDatabaseClientMock(PlanRegistry planRegistry) { - this.planRegistry = planRegistry; - } - - @Override - public void writeResourceSnapshots(Collection<ResourceSnapshot> items) { - this.resourceSnapshots.addAll(items); - } - - @Override - public List<ResourceSnapshot> getResourceSnapshotsForMonth(TenantName tenantName, ApplicationName applicationName, YearMonth month) { - return resourceSnapshots.stream() - .filter(resourceSnapshot -> { - LocalDate snapshotDate = LocalDate.ofInstant(resourceSnapshot.getTimestamp(), ZoneId.of("UTC")); - return YearMonth.from(snapshotDate).equals(month) && - snapshotDate.getYear() == month.getYear() && - resourceSnapshot.getApplicationId().tenant().equals(tenantName) && - resourceSnapshot.getApplicationId().application().equals(applicationName); - }) - .collect(Collectors.toList()); - } - - @Override - public Set<YearMonth> getMonthsWithSnapshotsForTenant(TenantName tenantName) { - return Collections.emptySet(); - } - - @Override - public List<ResourceSnapshot> getRawSnapshotHistoryForTenant(TenantName tenantName, YearMonth yearMonth) { - return resourceSnapshots; - } - - @Override - public Set<TenantName> getTenants() { - return resourceSnapshots.stream() - .map(snapshot -> snapshot.getApplicationId().tenant()) - .collect(Collectors.toSet()); - } - - private List<ResourceUsage> resourceUsageFromSnapshots(Plan plan, List<ResourceSnapshot> snapshots) { - snapshots.sort(Comparator.comparing(ResourceSnapshot::getTimestamp)); - - return IntStream.range(0, snapshots.size()) - .mapToObj(idx -> { - var a = snapshots.get(idx); - var b = (idx + 1) < snapshots.size() ? snapshots.get(idx + 1) : null; - var start = a.getTimestamp(); - var end = Optional.ofNullable(b).map(ResourceSnapshot::getTimestamp).orElse(start.plusSeconds(120)); - var d = BigDecimal.valueOf(Duration.between(start, end).toMillis()); - return new ResourceUsage( - a.getApplicationId(), - a.getZoneId(), - plan, - BigDecimal.valueOf(a.getCpuCores()).multiply(d), - BigDecimal.valueOf(a.getMemoryGb()).multiply(d), - BigDecimal.valueOf(a.getDiskGb()).multiply(d) - ); - }) - .collect(Collectors.toList()); - } - - private ResourceUsage resourceUsageAdd(ResourceUsage a, ResourceUsage b) { - assert a.getApplicationId().equals(b.getApplicationId()); - assert a.getZoneId().equals(b.getZoneId()); - assert a.getPlan().equals(b.getPlan()); - return new ResourceUsage( - a.getApplicationId(), - a.getZoneId(), - a.getPlan(), - a.getCpuMillis().add(b.getCpuMillis()), - a.getMemoryMillis().add(b.getMemoryMillis()), - a.getDiskMillis().add(b.getDiskMillis()) - ); - } - - @Override - public List<ResourceUsage> getResourceSnapshotsForPeriod(TenantName tenantName, long start, long end) { - var tenantPlan = planMap.getOrDefault(tenantName, planRegistry.defaultPlan()); - - var snapshotsPerDeployment = resourceSnapshots.stream() - .filter(snapshot -> snapshot.getTimestamp().isAfter(Instant.ofEpochMilli(start))) - .filter(snapshot -> snapshot.getTimestamp().isBefore(Instant.ofEpochMilli(end))) - .filter(snapshot -> snapshot.getApplicationId().tenant().equals(tenantName)) - .collect(Collectors.groupingBy( - usage -> Objects.hash(usage.getApplicationId(), usage.getZoneId(), tenantPlan.id().value()) - )) - .values().stream() - .map(snapshots -> resourceUsageFromSnapshots(tenantPlan, snapshots)) - .map(usages -> usages.stream().reduce(this::resourceUsageAdd)) - .filter(Optional::isPresent) - .map(Optional::get) - .collect(Collectors.toList()); - - return snapshotsPerDeployment; - } - - @Override - public void refreshMaterializedView() { - hasRefreshedMaterializedView = true; - } - - public void setPlan(TenantName tenant, Plan plan) { - planMap.put(tenant, plan); - } - - public boolean hasRefreshedMaterializedView() { - return hasRefreshedMaterializedView; - } -} diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceSnapshot.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceSnapshot.java index 319b9239ae4..a8d9c4b1f8a 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceSnapshot.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceSnapshot.java @@ -31,10 +31,6 @@ public class ResourceSnapshot { this.zoneId = zoneId; } - public static ResourceSnapshot from(ApplicationId applicationId, int nodes, double cpuCores, double memoryGb, double diskGb, Instant timestamp, ZoneId zoneId) { - return new ResourceSnapshot(applicationId, cpuCores * nodes, memoryGb * nodes, diskGb * nodes, timestamp, zoneId); - } - public static ResourceSnapshot from(List<Node> nodes, Instant timestamp, ZoneId zoneId) { Set<ApplicationId> applicationIds = nodes.stream() .filter(node -> node.owner().isPresent()) |