summaryrefslogtreecommitdiffstats
path: root/controller-api
diff options
context:
space:
mode:
authorØyvind Grønnesby <ogr@ogr.no>2021-10-08 16:51:23 +0200
committerGitHub <noreply@github.com>2021-10-08 16:51:23 +0200
commit5d65869d6e99489d22cc273be1f7562d19f45dbb (patch)
tree0e5b990ccb7f275d3d1521ef1495ee37d922c234 /controller-api
parent0cb6ffb214cb7752c2801bb65d4c83d61ba9571c (diff)
Revert "Billing refactoring and cleanup"
Diffstat (limited to 'controller-api')
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/ServiceRegistry.java9
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/BillingController.java71
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/BillingDatabaseClient.java135
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/BillingDatabaseClientMock.java178
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/Invoice.java (renamed from controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/Bill.java)18
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/MockBillingController.java60
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/PlanRegistry.java23
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/PlanRegistryMock.java122
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceDatabaseClient.java51
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceDatabaseClientMock.java146
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceSnapshot.java4
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())