// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.model; import com.yahoo.config.model.NullConfigModelRegistry; import com.yahoo.config.model.api.ApplicationClusterEndpoint; import com.yahoo.config.model.api.ContainerEndpoint; import com.yahoo.config.model.deploy.DeployState; import com.yahoo.config.model.deploy.TestProperties; import com.yahoo.config.model.provision.InMemoryProvisioner; import com.yahoo.config.model.test.MockApplicationPackage; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.ApplicationName; import com.yahoo.config.provision.Capacity; import com.yahoo.config.provision.Cloud; import com.yahoo.config.provision.CloudAccount; import com.yahoo.config.provision.CloudName; import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.ClusterSpec.Id; import com.yahoo.config.provision.Environment; import com.yahoo.config.provision.InstanceName; import com.yahoo.config.provision.RegionName; import com.yahoo.config.provision.SystemName; import com.yahoo.config.provision.TenantName; import com.yahoo.config.provision.Zone; import org.junit.jupiter.api.Test; import java.time.Duration; import java.util.List; import java.util.Map; import java.util.Set; import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author bratseth */ public class ClusterInfoTest { @Test void bcp_deadline_is_passed_in_cluster_info() throws Exception { var servicesXml = """ 2 """; var deploymentXml = """ us-west-1 us-east-1 eu-west-1 us-east-1 us-west-1 us-east-1 eu-west-1 """; var requestedInUsEast1 = requestedCapacityIn("default", "us-east-1", servicesXml, deploymentXml); assertEquals(Duration.ofMinutes(0), requestedInUsEast1.get(new ClusterSpec.Id("testcontainer")).clusterInfo().bcpDeadline()); assertEquals(Duration.ofMinutes(0), requestedInUsEast1.get(new ClusterSpec.Id("testcontent")).clusterInfo().bcpDeadline()); var requestedInUsWest1 = requestedCapacityIn("default", "us-west-1", servicesXml, deploymentXml); assertEquals(Duration.ofMinutes(30), requestedInUsWest1.get(new ClusterSpec.Id("testcontainer")).clusterInfo().bcpDeadline()); assertEquals(Duration.ofMinutes(30), requestedInUsWest1.get(new ClusterSpec.Id("testcontent")).clusterInfo().bcpDeadline()); var requestedInEuWest1 = requestedCapacityIn("default", "eu-west-1", servicesXml, deploymentXml); assertEquals(Duration.ofHours(48), requestedInEuWest1.get(new ClusterSpec.Id("testcontainer")).clusterInfo().bcpDeadline()); assertEquals(Duration.ofHours(48), requestedInEuWest1.get(new ClusterSpec.Id("testcontent")).clusterInfo().bcpDeadline()); } /** A high default deadline only will cause no resources to be set aside for BCP. */ @Test void specifying_only_default_deadline_is_possible() throws Exception { var servicesXml = """ 2 """; var deploymentXml = """ us-east-1 us-west-1 """; var requestedInUsEast1 = requestedCapacityIn("default", "us-east-1", servicesXml, deploymentXml); assertEquals(Duration.ofHours(48), requestedInUsEast1.get(new ClusterSpec.Id("testcontainer")).clusterInfo().bcpDeadline()); assertEquals(Duration.ofHours(48), requestedInUsEast1.get(new ClusterSpec.Id("testcontent")).clusterInfo().bcpDeadline()); var requestedInUsWest1 = requestedCapacityIn("default", "us-west-1", servicesXml, deploymentXml); assertEquals(Duration.ofHours(48), requestedInUsWest1.get(new ClusterSpec.Id("testcontainer")).clusterInfo().bcpDeadline()); assertEquals(Duration.ofHours(48), requestedInUsWest1.get(new ClusterSpec.Id("testcontent")).clusterInfo().bcpDeadline()); } /** This implicitly creates one BCP group (which gives the same result as above). */ @Test void specifying_bcp_without_explicit_groups() throws Exception { var servicesXml = """ 2 """; var deploymentXml = """ us-east-1 us-west-1 """; var requestedInUsEast1 = requestedCapacityIn("default", "us-east-1", servicesXml, deploymentXml); assertEquals(Duration.ofHours(48), requestedInUsEast1.get(new ClusterSpec.Id("testcontainer")).clusterInfo().bcpDeadline()); assertEquals(Duration.ofHours(48), requestedInUsEast1.get(new ClusterSpec.Id("testcontent")).clusterInfo().bcpDeadline()); var requestedInUsWest1 = requestedCapacityIn("default", "us-west-1", servicesXml, deploymentXml); assertEquals(Duration.ofHours(48), requestedInUsWest1.get(new ClusterSpec.Id("testcontainer")).clusterInfo().bcpDeadline()); assertEquals(Duration.ofHours(48), requestedInUsWest1.get(new ClusterSpec.Id("testcontent")).clusterInfo().bcpDeadline()); } @Test void default_bcp_with_multiple_instances() throws Exception { var servicesXml = """ 2 """; var deploymentXml = """ us-east-1 us-west-1 us-east-1 eu-west-1 """; var requestedInI1UsEast1 = requestedCapacityIn("i1", "us-east-1", servicesXml, deploymentXml); assertEquals(Duration.ofHours(48), requestedInI1UsEast1.get(new ClusterSpec.Id("testcontainer")).clusterInfo().bcpDeadline()); assertEquals(Duration.ofHours(48), requestedInI1UsEast1.get(new ClusterSpec.Id("testcontent")).clusterInfo().bcpDeadline()); var requestedInI1UsWest1 = requestedCapacityIn("i1", "us-west-1", servicesXml, deploymentXml); assertEquals(Duration.ofHours(48), requestedInI1UsWest1.get(new ClusterSpec.Id("testcontainer")).clusterInfo().bcpDeadline()); assertEquals(Duration.ofHours(48), requestedInI1UsWest1.get(new ClusterSpec.Id("testcontent")).clusterInfo().bcpDeadline()); var requestedInI2UsEast1 = requestedCapacityIn("i2", "us-east-1", servicesXml, deploymentXml); assertEquals(Duration.ofHours(48), requestedInI2UsEast1.get(new ClusterSpec.Id("testcontainer")).clusterInfo().bcpDeadline()); assertEquals(Duration.ofHours(48), requestedInI2UsEast1.get(new ClusterSpec.Id("testcontent")).clusterInfo().bcpDeadline()); var requestedInI2UsWest1 = requestedCapacityIn("i2", "eu-west-1", servicesXml, deploymentXml); assertEquals(Duration.ofHours(48), requestedInI2UsWest1.get(new ClusterSpec.Id("testcontainer")).clusterInfo().bcpDeadline()); assertEquals(Duration.ofHours(48), requestedInI2UsWest1.get(new ClusterSpec.Id("testcontent")).clusterInfo().bcpDeadline()); } @Test void host_ttl_requires_cloud_account() throws Exception { var servicesXml = """ """; var deploymentXml = """ us-east-1 us-north-1 us-west-1 """; Cloud gcp = Cloud.builder().name(CloudName.GCP).account(CloudAccount.from("vespaz")).allowEnclave(true).build(); CloudAccount account = CloudAccount.from("gcp:foobar"); assertEquals(Duration.ofHours(24), requestedCapacityIn(account, gcp, "default", "us-east-1", servicesXml, deploymentXml).get(new ClusterSpec.Id("testcontainer")).clusterInfo().hostTTL()); assertEquals(Duration.ZERO, requestedCapacityIn(account, gcp, "default", "us-north-1", servicesXml, deploymentXml).get(new ClusterSpec.Id("testcontainer")).clusterInfo().hostTTL()); assertEquals(Duration.ZERO, requestedCapacityIn(CloudAccount.empty, gcp, "default", "us-west-1", servicesXml, deploymentXml).get(new Id("testcontainer")).clusterInfo().hostTTL()); } private Map requestedCapacityIn(String instance, String region, String servicesXml, String deploymentXml) throws Exception { return requestedCapacityIn(null, Cloud.defaultCloud(), instance, region, servicesXml, deploymentXml); } private Map requestedCapacityIn(CloudAccount account, Cloud cloud, String instance, String region, String servicesXml, String deploymentXml) throws Exception { var applicationPackage = new MockApplicationPackage.Builder() .withServices(servicesXml) .withDeploymentSpec(deploymentXml) .build(); var provisioner = new InMemoryProvisioner(10, true); var deployState = new DeployState.Builder() .applicationPackage(applicationPackage) .zone(new Zone(cloud, SystemName.Public, Environment.prod, RegionName.from(region))) .properties(new TestProperties().setHostedVespa(true) .setCloudAccount(account) .setApplicationId(ApplicationId.from(TenantName.defaultName(), ApplicationName.defaultName(), InstanceName.from(instance))) .setZone(new Zone(Environment.prod, RegionName.from(region)))) .endpoints(Set.of(new ContainerEndpoint("testcontainer", ApplicationClusterEndpoint.Scope.zone, List.of("tc.example.com")))) .modelHostProvisioner(provisioner) .provisioned(provisioner.provisioned()) .build(); new VespaModel(new NullConfigModelRegistry(), deployState); return deployState.provisioned().all(); } }