diff options
author | Leandro Alves <ldalves@gmail.com> | 2018-12-10 11:20:07 +0100 |
---|---|---|
committer | Andreas Eriksen <andreer@pvv.ntnu.no> | 2018-12-10 11:20:07 +0100 |
commit | bec7ce30bf9263ab2d8b212b935e1514b105a8b5 (patch) | |
tree | 1fa60f963c3fd03d261ecae76a657a939f425f52 /controller-server | |
parent | d23084a0a2d4447d3fa9748c3239fa4ec9773e6b (diff) |
cost resources by property (#7789)
Diffstat (limited to 'controller-server')
13 files changed, 326 insertions, 4 deletions
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java index f6978ef70ac..fafd27aaded 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java @@ -14,7 +14,9 @@ import com.yahoo.vespa.hosted.controller.api.integration.organization.OwnershipI import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneId; import com.yahoo.vespa.hosted.controller.maintenance.config.MaintainerConfig; import com.yahoo.vespa.hosted.controller.persistence.CuratorDb; +import com.yahoo.vespa.hosted.controller.restapi.cost.CostReportConsumer; +import java.time.Clock; import java.time.Duration; import java.util.Collections; import java.util.List; @@ -48,13 +50,15 @@ public class ControllerMaintenance extends AbstractComponent { private final OsVersionStatusUpdater osVersionStatusUpdater; private final JobRunner jobRunner; private final ContactInformationMaintainer contactInformationMaintainer; + private final CostReportMaintainer costReportMaintainer; @SuppressWarnings("unused") // instantiated by Dependency Injection public ControllerMaintenance(MaintainerConfig maintainerConfig, ApiAuthorityConfig apiAuthorityConfig, Controller controller, CuratorDb curator, JobControl jobControl, Metric metric, Chef chefClient, DeploymentIssues deploymentIssues, OwnershipIssues ownershipIssues, NameService nameService, NodeRepositoryClientInterface nodeRepositoryClient, - ContactRetriever contactRetriever) { + ContactRetriever contactRetriever, + CostReportConsumer reportConsumer) { Duration maintenanceInterval = Duration.ofMinutes(maintainerConfig.intervalMinutes()); this.jobControl = jobControl; deploymentExpirer = new DeploymentExpirer(controller, maintenanceInterval, jobControl); @@ -74,6 +78,7 @@ public class ControllerMaintenance extends AbstractComponent { osUpgraders = osUpgraders(controller, jobControl); osVersionStatusUpdater = new OsVersionStatusUpdater(controller, maintenanceInterval, jobControl); contactInformationMaintainer = new ContactInformationMaintainer(controller, Duration.ofHours(12), jobControl, contactRetriever); + costReportMaintainer = new CostReportMaintainer(controller, Duration.ofHours(2), reportConsumer, jobControl, nodeRepositoryClient, Clock.systemUTC()); } public Upgrader upgrader() { return upgrader; } @@ -100,6 +105,7 @@ public class ControllerMaintenance extends AbstractComponent { osVersionStatusUpdater.deconstruct(); jobRunner.deconstruct(); contactInformationMaintainer.deconstruct(); + costReportMaintainer.deconstruct(); } /** Create one OS upgrader per cloud found in the zone registry of controller */ diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/CostReportMaintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/CostReportMaintainer.java new file mode 100644 index 00000000000..77febb71ca6 --- /dev/null +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/CostReportMaintainer.java @@ -0,0 +1,47 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.maintenance; + +import com.google.inject.Inject; +import com.yahoo.config.provision.SystemName; +import com.yahoo.vespa.hosted.controller.Controller; +import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeRepositoryClientInterface; +import com.yahoo.vespa.hosted.controller.restapi.cost.CostCalculator; +import com.yahoo.vespa.hosted.controller.restapi.cost.CostReportConsumer; + +import java.time.Clock; +import java.time.Duration; +import java.util.*; +import java.util.logging.Logger; + +/** + * Periodically calculate and store cost allocation for properties. + * + * @author ldalves + * @author andreer + */ +public class CostReportMaintainer extends Maintainer { + + private static final Logger log = Logger.getLogger(CostReportMaintainer.class.getName()); + + private final CostReportConsumer consumer; + private final NodeRepositoryClientInterface nodeRepository; + private final Clock clock; + + @Inject + @SuppressWarnings("WeakerAccess") + public CostReportMaintainer(Controller controller, Duration interval, + CostReportConsumer consumer, + JobControl jobControl, + NodeRepositoryClientInterface nodeRepository, + Clock clock) { + super(controller, interval, jobControl, "CostReportMaintainer", EnumSet.of(SystemName.main)); + this.consumer = consumer; + this.nodeRepository = Objects.requireNonNull(nodeRepository, "node repository must be non-null"); + this.clock = clock; + } + + @Override + protected void maintain() { + consumer.Consume(CostCalculator.toCsv(CostCalculator.calculateCost(nodeRepository, controller(), clock))); + } +} diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/cost/CostApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/cost/CostApiHandler.java new file mode 100644 index 00000000000..cdec0f8da74 --- /dev/null +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/cost/CostApiHandler.java @@ -0,0 +1,41 @@ +package com.yahoo.vespa.hosted.controller.restapi.cost; + +import com.yahoo.container.jdisc.HttpRequest; +import com.yahoo.container.jdisc.HttpResponse; +import com.yahoo.container.jdisc.LoggingRequestHandler; +import com.yahoo.restapi.Path; +import com.yahoo.vespa.hosted.controller.Controller; +import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeRepositoryClientInterface; +import com.yahoo.vespa.hosted.controller.restapi.ErrorResponse; +import com.yahoo.vespa.hosted.controller.restapi.StringResponse; + +import java.time.Clock; + +import static com.yahoo.jdisc.http.HttpRequest.Method.GET; + +public class CostApiHandler extends LoggingRequestHandler { + + private final Controller controller; + private final NodeRepositoryClientInterface nodeRepository; + + public CostApiHandler(Context ctx, Controller controller, NodeRepositoryClientInterface nodeRepository) { + super(ctx); + this.controller = controller; + this.nodeRepository = nodeRepository; + } + + @Override + public HttpResponse handle(HttpRequest request) { + if (request.getMethod() != GET) { + return ErrorResponse.methodNotAllowed("Method '" + request.getMethod() + "' is not supported"); + } + + Path path = new Path(request.getUri().getPath()); + + if (path.matches("/cost/v1/csv")) { + return new StringResponse(CostCalculator.toCsv(CostCalculator.calculateCost(nodeRepository, controller, Clock.systemUTC()))); + } + + return ErrorResponse.notFoundError("Nothing at " + path); + } +} diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/cost/CostCalculator.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/cost/CostCalculator.java new file mode 100644 index 00000000000..d6e77e6d381 --- /dev/null +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/cost/CostCalculator.java @@ -0,0 +1,95 @@ +package com.yahoo.vespa.hosted.controller.restapi.cost; + +import com.yahoo.config.provision.Environment; +import com.yahoo.vespa.hosted.controller.Controller; +import com.yahoo.vespa.hosted.controller.api.identifiers.Property; +import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeRepositoryClientInterface; +import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeRepositoryNode; +import com.yahoo.vespa.hosted.controller.api.integration.zone.CloudName; +import com.yahoo.vespa.hosted.controller.tenant.AthenzTenant; + +import java.time.Clock; +import java.time.LocalDate; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static com.yahoo.yolean.Exceptions.uncheck; + +public class CostCalculator { + public static Map<Property, ResourceAllocation> calculateCost(NodeRepositoryClientInterface nodeRepository, Controller controller, Clock clock) { + + String date = LocalDate.now(clock).toString(); + + List<NodeRepositoryNode> nodes = controller.zoneRegistry().zones() + .reachable().in(Environment.prod).ofCloud(CloudName.from("yahoo")).ids().stream() + .flatMap(zoneId -> uncheck(() -> nodeRepository.listNodes(zoneId, true).nodes().stream())) + .filter(node -> node.getOwner() != null && !node.getOwner().getTenant().equals("hosted-vespa")) + .collect(Collectors.toList()); + + ResourceAllocation total = ResourceAllocation.from(date, nodes, null); + + Map<String, Property> propertyByTenantName = controller.tenants().asList().stream() + .filter(AthenzTenant.class::isInstance) + .collect(Collectors.toMap( + tenant -> tenant.name().value(), + tenant -> ((AthenzTenant) tenant).property() + )); + + return nodes.stream() + .filter(node -> propertyByTenantName.containsKey(node.getOwner().tenant)) + .collect(Collectors.groupingBy( + node -> propertyByTenantName.get(node.getOwner().tenant), + Collectors.collectingAndThen( + Collectors.toList(), + (tenantNodes) -> ResourceAllocation.from(date, tenantNodes, total) + ) + )); + } + + static class ResourceAllocation { + final double cpuCores; + final double memoryGb; + final double diskGb; + final String date; + final ResourceAllocation total; + + private ResourceAllocation(String date, double cpuCores, double memoryGb, double diskGb, ResourceAllocation total) { + this.date = date; + this.cpuCores = cpuCores; + this.memoryGb = memoryGb; + this.diskGb = diskGb; + this.total = total; + } + + private static ResourceAllocation from(String date, List<NodeRepositoryNode> nodes, ResourceAllocation total) { + return new ResourceAllocation( + date, + nodes.stream().mapToDouble(NodeRepositoryNode::getMinCpuCores).sum(), + nodes.stream().mapToDouble(NodeRepositoryNode::getMinMainMemoryAvailableGb).sum(), + nodes.stream().mapToDouble(NodeRepositoryNode::getMinDiskAvailableGb).sum(), + total + ); + } + + private double usageFraction() { + return (cpuCores / total.cpuCores + memoryGb / total.memoryGb + diskGb / total.diskGb) / 3; + } + } + + public static String toCsv(Map<Property, ResourceAllocation> resourceShareByProperty) { + String header = "Date,Property,Reserved Cpu Cores,Reserved Memory GB,Reserved Disk Space GB,Usage Fraction\n"; + String entries = resourceShareByProperty.entrySet().stream() + .sorted((Comparator.comparingDouble(entry -> entry.getValue().usageFraction()))) + .map(propertyEntry -> { + ResourceAllocation r = propertyEntry.getValue(); + return Stream.of(r.date, propertyEntry.getKey(), r.cpuCores, r.memoryGb, r.diskGb, r.usageFraction()) + .map(Object::toString).collect(Collectors.joining(",")); + }) + .collect(Collectors.joining("\n")); + return header + entries; + } + +} diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/cost/CostReportConsumer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/cost/CostReportConsumer.java new file mode 100644 index 00000000000..51a664160ab --- /dev/null +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/cost/CostReportConsumer.java @@ -0,0 +1,5 @@ +package com.yahoo.vespa.hosted.controller.restapi.cost; + +public interface CostReportConsumer { + void Consume(String csv); +} diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/cost/NoopCostReportConsumer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/cost/NoopCostReportConsumer.java new file mode 100644 index 00000000000..f1406da1d4b --- /dev/null +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/cost/NoopCostReportConsumer.java @@ -0,0 +1,15 @@ +package com.yahoo.vespa.hosted.controller.restapi.cost; + +import com.google.inject.Inject; +import com.yahoo.component.AbstractComponent; + +public class NoopCostReportConsumer implements CostReportConsumer { + + @Inject + public NoopCostReportConsumer() {} + + @Override + public void Consume(String csv) { + // discard into the void + } +} diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/cost/package-info.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/cost/package-info.java new file mode 100644 index 00000000000..a96ae5488fa --- /dev/null +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/cost/package-info.java @@ -0,0 +1,5 @@ +// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +@ExportPackage +package com.yahoo.vespa.hosted.controller.restapi.cost; + +import com.yahoo.osgi.annotation.ExportPackage; diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTester.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTester.java index 639511959df..f966fea8f7a 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTester.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTester.java @@ -248,7 +248,7 @@ public final class ControllerTester { TenantName name = TenantName.from(tenantName); Optional<Tenant> existing = controller().tenants().tenant(name); if (existing.isPresent()) return name; - AthenzTenant tenant = AthenzTenant.create(name, createDomain(domainName), new Property("app1Property"), + AthenzTenant tenant = AthenzTenant.create(name, createDomain(domainName), new Property("Property"+propertyId), Optional.ofNullable(propertyId) .map(Object::toString) .map(PropertyId::new), contact); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/NodeRepositoryClientMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/NodeRepositoryClientMock.java index 1b12b441272..14de73e3f75 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/NodeRepositoryClientMock.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/NodeRepositoryClientMock.java @@ -4,6 +4,7 @@ package com.yahoo.vespa.hosted.controller.integration; import com.yahoo.vespa.hosted.controller.api.integration.noderepository.MaintenanceJobList; import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeList; import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeMembership; +import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeOwner; import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeRepositoryClientInterface; import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeRepositoryNode; import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeState; @@ -34,7 +35,9 @@ public class NodeRepositoryClientMock implements NodeRepositoryClientInterface { @Override public NodeList listNodes(ZoneId zone, boolean recursive) { - throw new UnsupportedOperationException(); + NodeRepositoryNode nodeA = createNodeA(); + NodeRepositoryNode nodeB = createNodeB(); + return new NodeList(Arrays.asList(nodeA, nodeB)); } @Override @@ -49,6 +52,14 @@ public class NodeRepositoryClientMock implements NodeRepositoryClientInterface { node.setHostname("hostA"); node.setCost(10); node.setFlavor("C-2B/24/500"); + node.setMinCpuCores(24d); + node.setMinDiskAvailableGb(500d); + node.setMinMainMemoryAvailableGb(24d); + NodeOwner owner = new NodeOwner(); + owner.tenant = "lsbe"; + owner.application = "local-search"; + owner.instance = "default"; + node.setOwner(owner); NodeMembership membership = new NodeMembership(); membership.clusterid = "clusterA"; membership.clustertype = "container"; @@ -61,6 +72,14 @@ public class NodeRepositoryClientMock implements NodeRepositoryClientInterface { node.setHostname("hostB"); node.setCost(20); node.setFlavor("C-2C/24/500"); + node.setMinCpuCores(40d); + node.setMinDiskAvailableGb(500d); + node.setMinMainMemoryAvailableGb(24d); + NodeOwner owner = new NodeOwner(); + owner.tenant = "mediasearch"; + owner.application = "imagesearch"; + owner.instance = "default"; + node.setOwner(owner); NodeMembership membership = new NodeMembership(); membership.clusterid = "clusterB"; membership.clustertype = "content"; diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/CostReportMaintainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/CostReportMaintainerTest.java new file mode 100644 index 00000000000..f8da14aec56 --- /dev/null +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/CostReportMaintainerTest.java @@ -0,0 +1,30 @@ +package com.yahoo.vespa.hosted.controller.maintenance; + +import com.yahoo.vespa.hosted.controller.ControllerTester; +import com.yahoo.vespa.hosted.controller.integration.NodeRepositoryClientMock; +import com.yahoo.vespa.hosted.controller.restapi.cost.CostReportConsumer; +import org.junit.Assert; +import org.junit.Test; + +import java.time.Clock; +import java.time.Duration; +import java.time.Instant; +import java.time.ZoneId; + +public class CostReportMaintainerTest { + + @Test + public void maintain() { + ControllerTester tester = new ControllerTester(); + + CostReportConsumer mockConsumer = csv -> Assert.assertEquals(csv, + "Date,Property,Reserved Cpu Cores,Reserved Memory GB,Reserved Disk Space GB,Usage Fraction\n" + + "1970-01-01,Property1,120.0,120.0,2500.0,0.4583333333333333\n" + + "1970-01-01,Property2,200.0,120.0,2500.0,0.5416666666666666"); + + tester.createTenant("lsbe", "local-search", 1L); + tester.createTenant("mediasearch", "msbe", 2L); + CostReportMaintainer maintainer = new CostReportMaintainer(tester.controller(), Duration.ofDays(1), mockConsumer, new JobControl(tester.curator()), new NodeRepositoryClientMock(), Clock.fixed(Instant.EPOCH, ZoneId.of("UTC"))); + maintainer.maintain(); + } +}
\ No newline at end of file diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java index ce69f32a21e..a2dea41b444 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java @@ -70,6 +70,7 @@ public class ControllerContainerTest { " <component id='com.yahoo.vespa.hosted.controller.api.integration.entity.MemoryEntityService'/>\n" + " <component id='com.yahoo.vespa.hosted.controller.api.integration.github.GitHubMock'/>\n" + " <component id='com.yahoo.vespa.hosted.controller.api.integration.stubs.LoggingDeploymentIssues'/>\n" + + " <component id='com.yahoo.vespa.hosted.controller.restapi.cost.NoopCostReportConsumer'/>\n" + " <component id='com.yahoo.vespa.hosted.controller.api.integration.stubs.DummyOwnershipIssues'/>\n" + " <component id='com.yahoo.vespa.hosted.controller.api.integration.stubs.MockRunDataStore'/>\n" + " <component id='com.yahoo.vespa.hosted.controller.api.integration.organization.MockContactRetriever'/>\n" + @@ -109,6 +110,9 @@ public class ControllerContainerTest { " <handler id='com.yahoo.vespa.hosted.controller.restapi.os.OsApiHandler'>\n" + " <binding>http://*/os/v1/*</binding>\n" + " </handler>\n" + + " <handler id='com.yahoo.vespa.hosted.controller.restapi.cost.CostApiHandler'>\n" + + " <binding>http://*/cost/v1/*</binding>\n" + + " </handler>\n" + " <handler id='com.yahoo.vespa.hosted.controller.restapi.screwdriver.ScrewdriverApiHandler'>\n" + " <binding>http://*/screwdriver/v1/*</binding>\n" + " </handler>\n" + diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/maintenance.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/maintenance.json index 6a71e524ae4..dcee9717ecc 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/maintenance.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/maintenance.json @@ -13,7 +13,10 @@ "name": "ContactInformationMaintainer" }, { - "name": "DefaultOsUpgrader" + "name": "CostReportMaintainer" + }, + { + "name":"DefaultOsUpgrader" }, { "name": "DeploymentExpirer" diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/cost/CostApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/cost/CostApiTest.java new file mode 100644 index 00000000000..bc03c9e87b3 --- /dev/null +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/cost/CostApiTest.java @@ -0,0 +1,52 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.restapi.cost; + +import com.yahoo.application.container.handler.Request; +import com.yahoo.config.provision.SystemName; +import com.yahoo.vespa.athenz.api.AthenzIdentity; +import com.yahoo.vespa.athenz.api.AthenzUser; +import com.yahoo.vespa.hosted.controller.api.integration.zone.CloudName; +import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneId; +import com.yahoo.vespa.hosted.controller.integration.ZoneRegistryMock; +import com.yahoo.vespa.hosted.controller.restapi.ContainerControllerTester; +import com.yahoo.vespa.hosted.controller.restapi.ControllerContainerTest; +import org.junit.Before; +import org.junit.Test; + +/** + * @author andreer + */ +public class CostApiTest extends ControllerContainerTest { + + private static final String responses = "src/test/java/com/yahoo/vespa/hosted/controller/restapi/cost/responses/"; + private static final AthenzIdentity operator = AthenzUser.fromUserId("operatorUser"); + private static final CloudName cloud1 = CloudName.from("yahoo"); + private static final CloudName cloud2 = CloudName.from("cloud2"); + private static final ZoneId zone1 = ZoneId.from("prod", "us-east-3", cloud1.value()); + private static final ZoneId zone2 = ZoneId.from("prod", "us-west-1", cloud1.value()); + private static final ZoneId zone3 = ZoneId.from("prod", "eu-west-1", cloud2.value()); + + private ContainerControllerTester tester; + + @Before + public void before() { + tester = new ContainerControllerTester(container, responses); + zoneRegistryMock().setSystemName(SystemName.cd) + .setZones(zone1, zone2, zone3); + } + + @Test + public void test_api() { + assertResponse(new Request("http://localhost:8080/cost/v1/csv"), "Date,Property,Reserved Cpu Cores,Reserved Memory GB,Reserved Disk Space GB,Usage Fraction\n", 200); + } + + private ZoneRegistryMock zoneRegistryMock() { + return (ZoneRegistryMock) tester.containerTester().container().components() + .getComponent(ZoneRegistryMock.class.getName()); + } + + private void assertResponse(Request request, String body, int statusCode) { + addIdentityToRequest(request, operator); + tester.assertResponse(request, body, statusCode); + } +} |