// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.config.model.provision;
import com.yahoo.cloud.config.log.LogdConfig;
import com.yahoo.config.application.api.ApplicationPackage;
import com.yahoo.config.model.api.HostInfo;
import com.yahoo.config.model.deploy.DeployProperties;
import com.yahoo.config.model.deploy.DeployState;
import com.yahoo.config.provision.ClusterMembership;
import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.Flavor;
import com.yahoo.config.provision.RegionName;
import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.Zone;
import com.yahoo.config.provisioning.FlavorsConfig;
import com.yahoo.container.core.ApplicationMetadataConfig;
import com.yahoo.search.config.QrStartConfig;
import com.yahoo.searchdefinition.parser.ParseException;
import com.yahoo.vespa.config.search.core.ProtonConfig;
import com.yahoo.vespa.model.AbstractService;
import com.yahoo.vespa.model.HostResource;
import com.yahoo.vespa.model.HostSystem;
import com.yahoo.vespa.model.VespaModel;
import com.yahoo.vespa.model.admin.Admin;
import com.yahoo.vespa.model.admin.Logserver;
import com.yahoo.vespa.model.admin.Slobrok;
import com.yahoo.vespa.model.container.Container;
import com.yahoo.vespa.model.container.ContainerCluster;
import com.yahoo.vespa.model.content.ContentSearchCluster;
import com.yahoo.vespa.model.content.StorageNode;
import com.yahoo.vespa.model.content.cluster.ContentCluster;
import com.yahoo.vespa.model.search.Dispatch;
import com.yahoo.vespa.model.search.SearchNode;
import com.yahoo.vespa.model.test.VespaModelTester;
import com.yahoo.vespa.model.test.utils.ApplicationPackageUtils;
import com.yahoo.vespa.model.test.utils.VespaModelCreatorWithMockPkg;
import org.junit.Ignore;
import org.junit.Test;
import java.io.StringReader;
import java.util.Collection;
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 static com.yahoo.config.model.test.TestUtil.joinLines;
import static com.yahoo.vespa.defaults.Defaults.getDefaults;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.collection.IsIn.isIn;
import static org.hamcrest.core.Every.everyItem;
import static org.hamcrest.core.IsNot.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
* Test cases for provisioning nodes to entire vespamodels
*
* @author Vegard Havdal
* @author bratseth
*/
public class ModelProvisioningTest {
@Test
public void testNodeCountForJdisc() {
String services =
"\n" +
"\n" +
"\n" +
"\n" +
"" +
" " +
" " +
" " +
" " +
"" +
"" +
" " +
" " +
" " +
" " +
" " +
"" +
"";
String hosts =""
+ " "
+ " node0"
+ " "
+ " "
+ " node1"
+ " "
+ " "
+ " node2"
+ " "
+ " "
+ " node3"
+ " "
+ " "
+ " node4"
+ " "
+ " "
+ " node5"
+ " "
+ "";
VespaModelCreatorWithMockPkg creator = new VespaModelCreatorWithMockPkg(null, services);
VespaModel model = creator.create(new DeployState.Builder().modelHostProvisioner(new InMemoryProvisioner(Hosts.readFrom(new StringReader(hosts)), true)));
ContainerCluster mydisc = model.getContainerClusters().get("mydisc");
ContainerCluster mydisc2 = model.getContainerClusters().get("mydisc2");
assertThat(mydisc.getContainers().size(), is(3));
assertThat(mydisc.getContainers().get(0).getConfigId(), is("mydisc/container.0"));
assertTrue(mydisc.getContainers().get(0).isInitialized());
assertThat(mydisc.getContainers().get(1).getConfigId(), is("mydisc/container.1"));
assertTrue(mydisc.getContainers().get(1).isInitialized());
assertThat(mydisc.getContainers().get(2).getConfigId(), is("mydisc/container.2"));
assertTrue(mydisc.getContainers().get(2).isInitialized());
assertThat(mydisc2.getContainers().size(), is(2));
assertThat(mydisc2.getContainers().get(0).getConfigId(), is("mydisc2/container.0"));
assertTrue(mydisc2.getContainers().get(0).isInitialized());
assertThat(mydisc2.getContainers().get(1).getConfigId(), is("mydisc2/container.1"));
assertTrue(mydisc2.getContainers().get(1).isInitialized());
assertThat(mydisc.getContainers().get(0).getJvmArgs(), is(""));
assertThat(mydisc.getContainers().get(1).getJvmArgs(), is(""));
assertThat(mydisc.getContainers().get(2).getJvmArgs(), is(""));
assertThat(mydisc.getContainers().get(0).getPreLoad(), is(getDefaults().underVespaHome("lib64/vespa/malloc/libvespamalloc.so")));
assertThat(mydisc.getContainers().get(1).getPreLoad(), is(getDefaults().underVespaHome("lib64/vespa/malloc/libvespamalloc.so")));
assertThat(mydisc.getContainers().get(2).getPreLoad(), is(getDefaults().underVespaHome("lib64/vespa/malloc/libvespamalloc.so")));
assertThat(mydisc.getMemoryPercentage(), is(Optional.empty()));
assertThat(mydisc2.getContainers().get(0).getJvmArgs(), is("-verbosegc"));
assertThat(mydisc2.getContainers().get(1).getJvmArgs(), is("-verbosegc"));
assertThat(mydisc2.getContainers().get(0).getPreLoad(), is("lib/blablamalloc.so"));
assertThat(mydisc2.getContainers().get(1).getPreLoad(), is("lib/blablamalloc.so"));
assertThat(mydisc2.getMemoryPercentage(), is(Optional.of(45)));
assertThat(mydisc2.getGCOpts(), is(Optional.of("-XX:+UseParNewGC")));
QrStartConfig.Builder qrStartBuilder = new QrStartConfig.Builder();
mydisc2.getConfig(qrStartBuilder);
QrStartConfig qrsStartConfig = new QrStartConfig(qrStartBuilder);
assertEquals(45, qrsStartConfig.jvm().heapSizeAsPercentageOfPhysicalMemory());
HostSystem hostSystem = model.getHostSystem();
assertNotNull(hostSystem.getHostByHostname("myhost0"));
assertNotNull(hostSystem.getHostByHostname("myhost1"));
assertNotNull(hostSystem.getHostByHostname("myhost2"));
assertNotNull(hostSystem.getHostByHostname("myhost3"));
assertNull(hostSystem.getHostByHostname("Nope"));
}
@Test
public void testNodeCountForContentGroup() {
String xmlWithNodes =
"" +
"" +
"\n" +
" " +
" " +
" " +
" " +
" 2" +
" " +
" " +
" " +
" " +
" " +
"";
VespaModelTester tester = new VespaModelTester();
int numberOfHosts = 2;
tester.addHosts(numberOfHosts);
int numberOfContentNodes = 2;
VespaModel model = tester.createModel(xmlWithNodes, true);
assertThat(model.getRoot().getHostSystem().getHosts().size(), is(numberOfHosts));
final Map contentClusters = model.getContentClusters();
ContentCluster cluster = contentClusters.get("bar");
assertThat(cluster.getRootGroup().getNodes().size(), is(numberOfContentNodes));
int i = 0;
for (StorageNode node : cluster.getRootGroup().getNodes())
assertEquals(i++, node.getDistributionKey());
}
@Test
public void testSeparateClusters() {
String xmlWithNodes =
"" +
"" +
" " +
" " +
" " +
" " +
" " +
" 2" +
" " +
" " +
" " +
" " +
" " +
"";
VespaModelTester tester = new VespaModelTester();
tester.addHosts(3);
VespaModel model = tester.createModel(xmlWithNodes, true);
assertEquals("Nodes in content1", 2, model.getContentClusters().get("content1").getRootGroup().getNodes().size());
assertEquals("Nodes in container1", 1, model.getContainerClusters().get("container1").getContainers().size());
assertEquals("Heap size for container", 60, physicalMemoryPercentage(model.getContainerClusters().get("container1")));
}
@Test
public void testClusterMembership() {
String xmlWithNodes =
"" +
"" +
" " +
" " +
" " +
"";
VespaModelTester tester = new VespaModelTester();
tester.addHosts(1);
VespaModel model = tester.createModel(xmlWithNodes, true);
assertEquals(1, model.getHostSystem().getHosts().size());
HostResource host = model.getHostSystem().getHosts().iterator().next();
assertEquals(1, host.clusterMemberships().size());
ClusterMembership membership = host.clusterMemberships().iterator().next();
assertEquals("container", membership.cluster().type().name());
assertEquals("container1", membership.cluster().id().value());
}
@Test
public void testCombinedCluster() {
String xmlWithNodes =
"" +
"" +
" " +
" " +
" " +
" " +
" " +
" 2" +
" " +
" " +
" " +
" " +
" " +
"";
VespaModelTester tester = new VespaModelTester();
tester.addHosts(2);
VespaModel model = tester.createModel(xmlWithNodes, true);
assertEquals("Nodes in content1", 2, model.getContentClusters().get("content1").getRootGroup().getNodes().size());
assertEquals("Nodes in container1", 2, model.getContainerClusters().get("container1").getContainers().size());
assertEquals("Heap size is lowered with combined clusters",
17, physicalMemoryPercentage(model.getContainerClusters().get("container1")));
}
@Test
public void testCombinedClusterWithJvmArgs() {
String xmlWithNodes =
"" +
"" +
" " +
" " +
" " +
" " +
" " +
" 2" +
" " +
" " +
" " +
" " +
" " +
"";
VespaModelTester tester = new VespaModelTester();
tester.addHosts(2);
VespaModel model = tester.createModel(xmlWithNodes, true);
assertEquals("Nodes in content1", 2, model.getContentClusters().get("content1").getRootGroup().getNodes().size());
assertEquals("Nodes in container1", 2, model.getContainerClusters().get("container1").getContainers().size());
for (Container container : model.getContainerClusters().get("container1").getContainers())
assertTrue(container.getJvmArgs().contains("testarg"));
}
@Test
public void testMultipleCombinedClusters() {
String xmlWithNodes =
"" +
"" +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
" 2" +
" " +
" " +
" " +
" " +
" " +
" " +
" 2" +
" " +
" " +
" " +
" " +
" " +
"";
VespaModelTester tester = new VespaModelTester();
tester.addHosts(5);
VespaModel model = tester.createModel(xmlWithNodes, true);
assertEquals("Nodes in content1", 2, model.getContentClusters().get("content1").getRootGroup().getNodes().size());
assertEquals("Nodes in container1", 2, model.getContainerClusters().get("container1").getContainers().size());
assertEquals("Nodes in content2", 3, model.getContentClusters().get("content2").getRootGroup().getNodes().size());
assertEquals("Nodes in container2", 3, model.getContainerClusters().get("container2").getContainers().size());
}
@Test
public void testNonExistingCombinedClusterReference() {
String xmlWithNodes =
"" +
"" +
" " +
" " +
" " +
"";
VespaModelTester tester = new VespaModelTester();
tester.addHosts(2);
try {
tester.createModel(xmlWithNodes, true);
fail("Expected exception");
}
catch (IllegalArgumentException e) {
assertEquals("container cluster 'container1' references service 'container2' but this service is not defined", e.getMessage());
}
}
@Test
public void testInvalidCombinedClusterReference() {
String xmlWithNodes =
"" +
"" +
" " +
" " +
" " +
" " +
" " +
" " +
"";
VespaModelTester tester = new VespaModelTester();
tester.addHosts(2);
try {
tester.createModel(xmlWithNodes, true);
fail("Expected exception");
}
catch (IllegalArgumentException e) {
assertEquals("container cluster 'container1' references service 'container2', but that is not a content service", e.getMessage());
}
}
@Test
public void testUsingNodesAndGroupCountAttributes() {
String services =
"\n" +
"" +
" " +
" " +
" " +
" " +
" " +
" 2" +
" " +
" " +
" " +
" " +
" " +
" " +
" 1" +
" " +
" " +
" " +
" " +
" " +
"";
int numberOfHosts = 64;
VespaModelTester tester = new VespaModelTester();
tester.addHosts(numberOfHosts);
VespaModel model = tester.createModel(services, true);
assertThat(model.getRoot().getHostSystem().getHosts().size(), is(numberOfHosts));
// Check container cluster
assertEquals(1, model.getContainerClusters().size());
Set containerHosts = model.getContainerClusters().get("foo").getContainers().stream().map(Container::getHost).collect(Collectors.toSet());
assertEquals(10, containerHosts.size());
// Check admin clusters
Admin admin = model.getAdmin();
Set slobrokHosts = admin.getSlobroks().stream().map(Slobrok::getHost).collect(Collectors.toSet());
assertEquals(3, slobrokHosts.size());
assertTrue("Slobroks are assigned from container nodes", containerHosts.containsAll(slobrokHosts));
assertTrue("Logserver is assigned from container nodes", containerHosts.contains(admin.getLogserver().getHost()));
assertEquals("No in-cluster config servers in a hosted environment", 0, admin.getConfigservers().size());
assertEquals("No admin cluster controller when multitenant", null, admin.getClusterControllers());
// Check content clusters
ContentCluster cluster = model.getContentClusters().get("bar");
ContainerCluster clusterControllers = cluster.getClusterControllers();
assertEquals(3, clusterControllers.getContainers().size());
assertEquals("bar-controllers", clusterControllers.getName());
assertEquals("default28", clusterControllers.getContainers().get(0).getHostName());
assertEquals("default54", clusterControllers.getContainers().get(1).getHostName());
assertEquals("default51", clusterControllers.getContainers().get(2).getHostName());
assertEquals(0, cluster.getRootGroup().getNodes().size());
assertEquals(9, cluster.getRootGroup().getSubgroups().size());
assertThat(cluster.getRootGroup().getSubgroups().get(0).getIndex(), is("0"));
assertThat(cluster.getRootGroup().getSubgroups().get(0).getNodes().size(), is(3));
assertThat(cluster.getRootGroup().getSubgroups().get(0).getNodes().get(0).getDistributionKey(), is(0));
assertThat(cluster.getRootGroup().getSubgroups().get(0).getNodes().get(0).getConfigId(), is("bar/storage/0"));
assertEquals("default54", cluster.getRootGroup().getSubgroups().get(0).getNodes().get(0).getHostName());
assertThat(cluster.getRootGroup().getSubgroups().get(0).getNodes().get(1).getDistributionKey(), is(1));
assertThat(cluster.getRootGroup().getSubgroups().get(0).getNodes().get(1).getConfigId(), is("bar/storage/1"));
assertThat(cluster.getRootGroup().getSubgroups().get(0).getNodes().get(2).getDistributionKey(), is(2));
assertThat(cluster.getRootGroup().getSubgroups().get(0).getNodes().get(2).getConfigId(), is("bar/storage/2"));
assertThat(cluster.getRootGroup().getSubgroups().get(1).getIndex(), is("1"));
assertThat(cluster.getRootGroup().getSubgroups().get(1).getNodes().size(), is(3));
assertThat(cluster.getRootGroup().getSubgroups().get(1).getNodes().get(0).getDistributionKey(), is(3));
assertThat(cluster.getRootGroup().getSubgroups().get(1).getNodes().get(0).getConfigId(), is("bar/storage/3"));
assertEquals("default51", cluster.getRootGroup().getSubgroups().get(1).getNodes().get(0).getHostName());
assertThat(cluster.getRootGroup().getSubgroups().get(1).getNodes().get(1).getDistributionKey(), is(4));
assertThat(cluster.getRootGroup().getSubgroups().get(1).getNodes().get(1).getConfigId(), is("bar/storage/4"));
assertThat(cluster.getRootGroup().getSubgroups().get(1).getNodes().get(2).getDistributionKey(), is(5));
assertThat(cluster.getRootGroup().getSubgroups().get(1).getNodes().get(2).getConfigId(), is("bar/storage/5"));
// ...
assertEquals("default48", cluster.getRootGroup().getSubgroups().get(2).getNodes().get(0).getHostName());
// ...
assertThat(cluster.getRootGroup().getSubgroups().get(8).getIndex(), is("8"));
assertThat(cluster.getRootGroup().getSubgroups().get(8).getNodes().size(), is(3));
assertThat(cluster.getRootGroup().getSubgroups().get(8).getNodes().get(0).getDistributionKey(), is(24));
assertThat(cluster.getRootGroup().getSubgroups().get(8).getNodes().get(0).getConfigId(), is("bar/storage/24"));
assertThat(cluster.getRootGroup().getSubgroups().get(8).getNodes().get(1).getDistributionKey(), is(25));
assertThat(cluster.getRootGroup().getSubgroups().get(8).getNodes().get(1).getConfigId(), is("bar/storage/25"));
assertThat(cluster.getRootGroup().getSubgroups().get(8).getNodes().get(2).getDistributionKey(), is(26));
assertThat(cluster.getRootGroup().getSubgroups().get(8).getNodes().get(2).getConfigId(), is("bar/storage/26"));
cluster = model.getContentClusters().get("baz");
clusterControllers = cluster.getClusterControllers();
assertEquals(3, clusterControllers.getContainers().size());
assertEquals("baz-controllers", clusterControllers.getName());
assertEquals("default01", clusterControllers.getContainers().get(0).getHostName());
assertEquals("default27", clusterControllers.getContainers().get(1).getHostName());
assertEquals("default26", clusterControllers.getContainers().get(2).getHostName());
assertEquals(0, cluster.getRootGroup().getNodes().size());
assertEquals(27, cluster.getRootGroup().getSubgroups().size());
assertThat(cluster.getRootGroup().getSubgroups().get(0).getIndex(), is("0"));
assertThat(cluster.getRootGroup().getSubgroups().get(0).getNodes().size(), is(1));
assertThat(cluster.getRootGroup().getSubgroups().get(0).getNodes().get(0).getDistributionKey(), is(0));
assertThat(cluster.getRootGroup().getSubgroups().get(0).getNodes().get(0).getConfigId(), is("baz/storage/0"));
assertEquals("default27", cluster.getRootGroup().getSubgroups().get(0).getNodes().get(0).getHostName());
assertThat(cluster.getRootGroup().getSubgroups().get(1).getIndex(), is("1"));
assertThat(cluster.getRootGroup().getSubgroups().get(1).getNodes().size(), is(1));
assertThat(cluster.getRootGroup().getSubgroups().get(1).getNodes().get(0).getDistributionKey(), is(1));
assertThat(cluster.getRootGroup().getSubgroups().get(1).getNodes().get(0).getConfigId(), is("baz/storage/1"));
assertEquals("default26", cluster.getRootGroup().getSubgroups().get(1).getNodes().get(0).getHostName());
// ...
assertEquals("default25", cluster.getRootGroup().getSubgroups().get(2).getNodes().get(0).getHostName());
// ...
assertThat(cluster.getRootGroup().getSubgroups().get(26).getIndex(), is("26"));
assertThat(cluster.getRootGroup().getSubgroups().get(26).getNodes().size(), is(1));
assertThat(cluster.getRootGroup().getSubgroups().get(26).getNodes().get(0).getDistributionKey(), is(26));
assertThat(cluster.getRootGroup().getSubgroups().get(26).getNodes().get(0).getConfigId(), is("baz/storage/26"));
}
@Test
public void testGroupsOfSize1() {
String services =
"\n" +
"" +
" " +
" " +
" " +
" " +
" " +
" 1" +
" " +
" " +
" " +
" " +
" " +
"";
int numberOfHosts = 18;
VespaModelTester tester = new VespaModelTester();
tester.addHosts(numberOfHosts);
VespaModel model = tester.createModel(services, true);
assertThat(model.getRoot().getHostSystem().getHosts().size(), is(numberOfHosts));
// Check content cluster
ContentCluster cluster = model.getContentClusters().get("bar");
ContainerCluster clusterControllers = cluster.getClusterControllers();
assertEquals(3, clusterControllers.getContainers().size());
assertEquals("bar-controllers", clusterControllers.getName());
assertEquals("default01", clusterControllers.getContainers().get(0).getHostName());
assertEquals("default08", clusterControllers.getContainers().get(1).getHostName());
assertEquals("default07", clusterControllers.getContainers().get(2).getHostName());
assertEquals(0, cluster.getRootGroup().getNodes().size());
assertEquals(8, cluster.getRootGroup().getSubgroups().size());
assertEquals(8, cluster.distributionBits());
// first group
assertThat(cluster.getRootGroup().getSubgroups().get(0).getIndex(), is("0"));
assertThat(cluster.getRootGroup().getSubgroups().get(0).getNodes().size(), is(1));
assertThat(cluster.getRootGroup().getSubgroups().get(0).getNodes().get(0).getDistributionKey(), is(0));
assertThat(cluster.getRootGroup().getSubgroups().get(0).getNodes().get(0).getConfigId(), is("bar/storage/0"));
assertEquals("default08", cluster.getRootGroup().getSubgroups().get(0).getNodes().get(0).getHostName());
// second group
assertThat(cluster.getRootGroup().getSubgroups().get(1).getIndex(), is("1"));
assertThat(cluster.getRootGroup().getSubgroups().get(1).getNodes().size(), is(1));
assertThat(cluster.getRootGroup().getSubgroups().get(1).getNodes().get(0).getDistributionKey(), is(1));
assertThat(cluster.getRootGroup().getSubgroups().get(1).getNodes().get(0).getConfigId(), is("bar/storage/1"));
assertEquals("default07", cluster.getRootGroup().getSubgroups().get(1).getNodes().get(0).getHostName());
// ... last group
assertThat(cluster.getRootGroup().getSubgroups().get(7).getIndex(), is("7"));
assertThat(cluster.getRootGroup().getSubgroups().get(7).getNodes().size(), is(1));
assertThat(cluster.getRootGroup().getSubgroups().get(7).getNodes().get(0).getDistributionKey(), is(7));
assertThat(cluster.getRootGroup().getSubgroups().get(7).getNodes().get(0).getConfigId(), is("bar/storage/7"));
assertEquals("default01", cluster.getRootGroup().getSubgroups().get(7).getNodes().get(0).getHostName());
}
@Test
public void testExplicitNonDedicatedClusterControllers() {
String services =
"\n" +
"" +
" " +
" " +
" " +
" " +
" " +
" 2" +
" " +
" " +
" " +
" " +
" " +
" " +
"";
int numberOfHosts = 19;
VespaModelTester tester = new VespaModelTester();
tester.addHosts(numberOfHosts);
VespaModel model = tester.createModel(services, true);
assertThat(model.getRoot().getHostSystem().getHosts().size(), is(numberOfHosts));
// Check content clusters
ContentCluster cluster = model.getContentClusters().get("bar");
ContainerCluster clusterControllers = cluster.getClusterControllers();
assertEquals( 8, cluster.distributionBits());
assertEquals("We get the closest odd number", 5, clusterControllers.getContainers().size());
assertEquals("bar-controllers", clusterControllers.getName());
assertEquals("default01", clusterControllers.getContainers().get(0).getHostName());
assertEquals("default02", clusterControllers.getContainers().get(1).getHostName());
assertEquals("default04", clusterControllers.getContainers().get(2).getHostName());
assertEquals("default05", clusterControllers.getContainers().get(3).getHostName());
assertEquals("default09", clusterControllers.getContainers().get(4).getHostName());
assertEquals("default09", cluster.getRootGroup().getSubgroups().get(0).getNodes().get(0).getHostName());
assertEquals("default08", cluster.getRootGroup().getSubgroups().get(0).getNodes().get(1).getHostName());
assertEquals("default06", cluster.getRootGroup().getSubgroups().get(1).getNodes().get(0).getHostName());
assertEquals("default03", cluster.getRootGroup().getSubgroups().get(2).getNodes().get(0).getHostName());
}
@Test
public void testClusterControllersWithGroupSize2() {
String services =
"\n" +
"" +
" " +
" " +
" " +
" " +
" " +
" 2" +
" " +
" " +
" " +
" " +
" " +
"";
int numberOfHosts = 18;
VespaModelTester tester = new VespaModelTester();
tester.addHosts(numberOfHosts);
VespaModel model = tester.createModel(services, true);
assertThat(model.getRoot().getHostSystem().getHosts().size(), is(numberOfHosts));
// Check content clusters
ContentCluster cluster = model.getContentClusters().get("bar");
ContainerCluster clusterControllers = cluster.getClusterControllers();
assertEquals("We get the closest odd number", 3, clusterControllers.getContainers().size());
assertEquals("bar-controllers", clusterControllers.getName());
assertEquals("default01", clusterControllers.getContainers().get(0).getHostName());
assertEquals("default08", clusterControllers.getContainers().get(1).getHostName());
assertEquals("default06", clusterControllers.getContainers().get(2).getHostName());
}
@Test
public void testClusterControllersCanSupplementWithAllContainerClusters() throws ParseException {
String services =
"\n" +
"" +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
" 2" +
" " +
" " +
" " +
" " +
" " +
" " +
"";
int numberOfHosts = 5;
VespaModelTester tester = new VespaModelTester();
tester.addHosts(numberOfHosts);
VespaModel model = tester.createModel(services, true);
assertThat(model.getRoot().getHostSystem().getHosts().size(), is(numberOfHosts));
ContentCluster cluster = model.getContentClusters().get("bar");
ContainerCluster clusterControllers = cluster.getClusterControllers();
assertEquals(1, clusterControllers.getContainers().size()); // TODO: Expected 5 with this feature reactivated
}
@Test
public void testClusterControllersAreNotPlacedOnRetiredNodes() {
String services =
"\n" +
"" +
" " +
" " +
" " +
" " +
" " +
" 2" +
" " +
" " +
" " +
" " +
" " +
"";
int numberOfHosts = 19;
VespaModelTester tester = new VespaModelTester();
tester.addHosts(numberOfHosts);
VespaModel model = tester.createModel(services, true, "default09", "default06", "default03");
assertThat(model.getRoot().getHostSystem().getHosts().size(), is(numberOfHosts));
// Check content clusters
ContentCluster cluster = model.getContentClusters().get("bar");
ContainerCluster clusterControllers = cluster.getClusterControllers();
assertEquals(3, clusterControllers.getContainers().size());
assertEquals("bar-controllers", clusterControllers.getName());
assertEquals("Skipping retired default09", "default01", clusterControllers.getContainers().get(0).getHostName());
assertEquals("Skipping retired default06", "default08", clusterControllers.getContainers().get(1).getHostName());
assertEquals("Skipping retired default03", "default05", clusterControllers.getContainers().get(2).getHostName());
}
@Test
public void testSlobroksClustersAreExpandedToIncludeRetiredNodes() {
String services =
"\n" +
"" +
" " +
" " +
" " +
" " +
"";
int numberOfHosts = 10;
VespaModelTester tester = new VespaModelTester();
tester.addHosts(numberOfHosts);
VespaModel model = tester.createModel(services, true, "default09");
assertThat(model.getRoot().getHostSystem().getHosts().size(), is(numberOfHosts));
// Check slobroks clusters
assertEquals("Includes retired node", 1+3, model.getAdmin().getSlobroks().size());
assertEquals("default01", model.getAdmin().getSlobroks().get(0).getHostName());
assertEquals("default02", model.getAdmin().getSlobroks().get(1).getHostName());
assertEquals("default10", model.getAdmin().getSlobroks().get(2).getHostName());
assertEquals("Included in addition because it is retired", "default09", model.getAdmin().getSlobroks().get(3).getHostName());
}
@Test
public void testSlobroksClustersAreExpandedToIncludeRetiredNodesWhenRetiredComesLast() throws ParseException {
String services =
"\n" +
"" +
" " +
" " +
" " +
" " +
"";
int numberOfHosts = 10;
VespaModelTester tester = new VespaModelTester();
tester.addHosts(numberOfHosts);
VespaModel model = tester.createModel(services, true, "default09", "default08");
assertThat(model.getRoot().getHostSystem().getHosts().size(), is(numberOfHosts));
// Check slobroks clusters
assertEquals("Includes retired node", 3+2, model.getAdmin().getSlobroks().size());
assertEquals("default01", model.getAdmin().getSlobroks().get(0).getHostName());
assertEquals("default02", model.getAdmin().getSlobroks().get(1).getHostName());
assertEquals("default10", model.getAdmin().getSlobroks().get(2).getHostName());
assertEquals("Included in addition because it is retired", "default08", model.getAdmin().getSlobroks().get(3).getHostName());
assertEquals("Included in addition because it is retired", "default09", model.getAdmin().getSlobroks().get(4).getHostName());
}
@Test
public void testSlobroksAreSpreadOverAllContainerClusters() {
String services =
"\n" +
"" +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
"";
int numberOfHosts = 13;
VespaModelTester tester = new VespaModelTester();
tester.addHosts(numberOfHosts);
VespaModel model = tester.createModel(services, true, "default12", "default03", "default02");
assertThat(model.getRoot().getHostSystem().getHosts().size(), is(numberOfHosts));
// Check slobroks clusters
// ... from cluster default
assertEquals("Includes retired node", 3+3, model.getAdmin().getSlobroks().size());
assertEquals("default04", model.getAdmin().getSlobroks().get(0).getHostName());
assertEquals("default13", model.getAdmin().getSlobroks().get(1).getHostName());
assertEquals("Included in addition because it is retired", "default12", model.getAdmin().getSlobroks().get(2).getHostName());
// ... from cluster bar
assertEquals("default01", model.getAdmin().getSlobroks().get(3).getHostName());
assertEquals("Included in addition because it is retired", "default02", model.getAdmin().getSlobroks().get(4).getHostName());
assertEquals("Included in addition because it is retired", "default03", model.getAdmin().getSlobroks().get(5).getHostName());
}
@Test
public void testSlobroksAreSpreadOverAllContainerClustersExceptNodeAdmin() {
String services =
"\n" +
"" +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
"";
int numberOfHosts = 13;
VespaModelTester tester = new VespaModelTester();
tester.addHosts(numberOfHosts);
tester.setApplicationId("hosted-vespa", "routing", "default");
VespaModel model = tester.createModel(services, true);
assertThat(model.getRoot().getHostSystem().getHosts().size(), is(numberOfHosts));
Set routingHosts = getClusterHostnames(model, "routing");
assertEquals(10, routingHosts.size());
Set nodeAdminHosts = getClusterHostnames(model, "node-admin");
assertEquals(3, nodeAdminHosts.size());
Set slobrokHosts = model.getAdmin().getSlobroks().stream()
.map(AbstractService::getHostName)
.collect(Collectors.toSet());
assertEquals(3, slobrokHosts.size());
assertThat(slobrokHosts, everyItem(isIn(routingHosts)));
assertThat(slobrokHosts, everyItem(not(isIn(nodeAdminHosts))));
}
private Set getClusterHostnames(VespaModel model, String clusterId) {
return model.getHosts().stream()
.filter(host -> host.getServices().stream()
.anyMatch(serviceInfo -> Objects.equals(
serviceInfo.getProperty("clustername"),
Optional.of(clusterId))))
.map(HostInfo::getHostname)
.collect(Collectors.toSet());
}
@Test
public void test2ContentNodesProduces1ClusterController() {
String services =
"\n" +
"" +
" " +
" 2" +
" " +
" " +
" " +
" " +
" " +
"";
int numberOfHosts = 2;
VespaModelTester tester = new VespaModelTester();
tester.addHosts(numberOfHosts);
VespaModel model = tester.createModel(services, true);
assertThat(model.getRoot().getHostSystem().getHosts().size(), is(numberOfHosts));
ContentCluster cluster = model.getContentClusters().get("bar");
ContainerCluster clusterControllers = cluster.getClusterControllers();
assertEquals(1, clusterControllers.getContainers().size());
}
@Test
public void test2ContentNodesWithContainerClusterProducesMixedClusterControllerCluster() throws ParseException {
String services =
"\n" +
"" +
" " +
" " +
" " +
" " +
" 2" +
" " +
" " +
" " +
" " +
" " +
"";
VespaModelTester tester = new VespaModelTester();
tester.addHosts(5);
VespaModel model = tester.createModel(services, true);
ContentCluster cluster = model.getContentClusters().get("bar");
ContainerCluster clusterControllers = cluster.getClusterControllers();
assertEquals(1, clusterControllers.getContainers().size()); // TODO: Expected 3 with this feature reactivated
}
@Ignore // TODO: unignore when feature is enabled again
@Test
public void test2ContentNodesOn2ClustersWithContainerClusterProducesMixedClusterControllerCluster() throws ParseException {
String services =
"\n" +
"" +
" " +
" " +
" " +
" " +
" 2" +
" " +
" " +
" " +
" " +
" " +
" " +
" 2" +
" " +
" " +
" " +
" " +
" " +
"";
VespaModelTester tester = new VespaModelTester();
// use different flavors to make the test clearer
tester.addHosts("container-node", 3);
tester.addHosts("content1-node", 2);
tester.addHosts("content2-node", 2);
VespaModel model = tester.createModel(services, true);
ContentCluster cluster1 = model.getContentClusters().get("content1");
ContainerCluster clusterControllers1 = cluster1.getClusterControllers();
assertEquals(1, clusterControllers1.getContainers().size());
assertEquals("content1-node0", clusterControllers1.getContainers().get(0).getHostName());
assertEquals("content1-node1", clusterControllers1.getContainers().get(1).getHostName());
assertEquals("container-node0", clusterControllers1.getContainers().get(2).getHostName());
ContentCluster cluster2 = model.getContentClusters().get("content2");
ContainerCluster clusterControllers2 = cluster2.getClusterControllers();
assertEquals(3, clusterControllers2.getContainers().size());
assertEquals("content2-node0", clusterControllers2.getContainers().get(0).getHostName());
assertEquals("content2-node1", clusterControllers2.getContainers().get(1).getHostName());
assertEquals("We do not pick the container used to supplement another cluster",
"container-node1", clusterControllers2.getContainers().get(2).getHostName());
}
@Test
public void testExplicitDedicatedClusterControllers() {
String services =
"\n" +
"" +
" " +
" " +
" " +
" " +
" 2" +
" " +
" " +
" " +
" " +
" " +
" " +
"";
int numberOfHosts = 23;
VespaModelTester tester = new VespaModelTester();
tester.addHosts(numberOfHosts);
VespaModel model = tester.createModel(services, true);
assertThat(model.getRoot().getHostSystem().getHosts().size(), is(numberOfHosts));
// Check content clusters
ContentCluster cluster = model.getContentClusters().get("bar");
ContainerCluster clusterControllers = cluster.getClusterControllers();
assertEquals(4, clusterControllers.getContainers().size());
assertEquals("bar-controllers", clusterControllers.getName());
assertEquals("default04", clusterControllers.getContainers().get(0).getHostName());
assertEquals("default03", clusterControllers.getContainers().get(1).getHostName());
assertEquals("default02", clusterControllers.getContainers().get(2).getHostName());
assertEquals("default01", clusterControllers.getContainers().get(3).getHostName());
}
@Test
public void testLogserverContainerWhenDedicatedLogserver() {
String services =
"\n" +
"" +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
"";
boolean useDedicatedNodeForLogserver = false;
testContainerOnLogserverHost(services, useDedicatedNodeForLogserver);
}
@Test
public void testImplicitLogserverContainer() {
String services =
"\n" +
"" +
" " +
" " +
" " +
"";
boolean useDedicatedNodeForLogserver = true;
testContainerOnLogserverHost(services, useDedicatedNodeForLogserver);
}
@Test
public void testUsingNodesAndGroupCountAttributesAndGettingTooFewNodes() {
String services =
"" +
"" +
" " +
" " + // Ignored
" " +
" " +
" 4" +
" " +
" " +
" " +
" " +
" 3" +
" " +
"";
int numberOfHosts = 6; // We only have 6 content nodes -> 3 groups with redundancy 2 in each
VespaModelTester tester = new VespaModelTester();
tester.addHosts(numberOfHosts);
VespaModel model = tester.createModel(services, false);
assertThat(model.getRoot().getHostSystem().getHosts().size(), is(numberOfHosts));
ContentCluster cluster = model.getContentClusters().get("bar");
assertEquals(2*3, cluster.redundancy().effectiveInitialRedundancy()); // Reduced from 3*3
assertEquals(2*3, cluster.redundancy().effectiveFinalRedundancy()); // Reduced from 3*4
assertEquals(2*3, cluster.redundancy().effectiveReadyCopies()); // Reduced from 3*3
assertEquals("2|2|*", cluster.getRootGroup().getPartitions().get()); // Reduced from 4|4|*
assertEquals(0, cluster.getRootGroup().getNodes().size());
assertEquals(3, cluster.getRootGroup().getSubgroups().size());
assertThat(cluster.getRootGroup().getSubgroups().get(0).getIndex(), is("0"));
assertThat(cluster.getRootGroup().getSubgroups().get(0).getNodes().size(), is(2));
assertThat(cluster.getRootGroup().getSubgroups().get(0).getNodes().get(0).getDistributionKey(), is(0));
assertThat(cluster.getRootGroup().getSubgroups().get(0).getNodes().get(0).getConfigId(), is("bar/storage/0"));
assertThat(cluster.getRootGroup().getSubgroups().get(0).getNodes().get(1).getDistributionKey(), is(1));
assertThat(cluster.getRootGroup().getSubgroups().get(0).getNodes().get(1).getConfigId(), is("bar/storage/1"));
assertThat(cluster.getRootGroup().getSubgroups().get(1).getIndex(), is("1"));
assertThat(cluster.getRootGroup().getSubgroups().get(1).getNodes().size(), is(2));
assertThat(cluster.getRootGroup().getSubgroups().get(1).getNodes().get(0).getDistributionKey(), is(2));
assertThat(cluster.getRootGroup().getSubgroups().get(1).getNodes().get(0).getConfigId(), is("bar/storage/2"));
assertThat(cluster.getRootGroup().getSubgroups().get(1).getNodes().get(1).getDistributionKey(), is(3));
assertThat(cluster.getRootGroup().getSubgroups().get(1).getNodes().get(1).getConfigId(), is("bar/storage/3"));
assertThat(cluster.getRootGroup().getSubgroups().get(2).getIndex(), is("2"));
assertThat(cluster.getRootGroup().getSubgroups().get(2).getNodes().size(), is(2));
assertThat(cluster.getRootGroup().getSubgroups().get(2).getNodes().get(0).getDistributionKey(), is(4));
assertThat(cluster.getRootGroup().getSubgroups().get(2).getNodes().get(0).getConfigId(), is("bar/storage/4"));
assertThat(cluster.getRootGroup().getSubgroups().get(2).getNodes().get(1).getDistributionKey(), is(5));
assertThat(cluster.getRootGroup().getSubgroups().get(2).getNodes().get(1).getConfigId(), is("bar/storage/5"));
}
@Test
public void testUsingNodesCountAttributesAndGettingTooFewNodes() {
String services =
"" +
"" +
" " +
" " + // Ignored
" " +
" " +
" 12" +
" " +
" " +
" " +
" " +
" 5" +
" 7" +
" " +
"";
int numberOfHosts = 4;
VespaModelTester tester = new VespaModelTester();
tester.addHosts(numberOfHosts);
VespaModel model = tester.createModel(services, false);
assertThat(model.getRoot().getHostSystem().getHosts().size(), is(numberOfHosts));
ContentCluster cluster = model.getContentClusters().get("bar");
assertEquals(4, cluster.redundancy().effectiveInitialRedundancy());
assertEquals(4, cluster.redundancy().effectiveFinalRedundancy());
assertEquals(4, cluster.redundancy().effectiveReadyCopies());
assertEquals(4, cluster.getSearch().getIndexed().getDispatchSpec().getGroups().size());
assertFalse(cluster.getRootGroup().getPartitions().isPresent());
assertEquals(4, cluster.getRootGroup().getNodes().size());
assertEquals(0, cluster.getRootGroup().getSubgroups().size());
assertThat(cluster.getRootGroup().getNodes().size(), is(4));
assertThat(cluster.getRootGroup().getNodes().get(0).getDistributionKey(), is(0));
assertThat(cluster.getRootGroup().getNodes().get(0).getConfigId(), is("bar/storage/0"));
assertThat(cluster.getRootGroup().getNodes().get(1).getDistributionKey(), is(1));
assertThat(cluster.getRootGroup().getNodes().get(1).getConfigId(), is("bar/storage/1"));
assertThat(cluster.getRootGroup().getNodes().get(2).getDistributionKey(), is(2));
assertThat(cluster.getRootGroup().getNodes().get(2).getConfigId(), is("bar/storage/2"));
assertThat(cluster.getRootGroup().getNodes().get(3).getDistributionKey(), is(3));
assertThat(cluster.getRootGroup().getNodes().get(3).getConfigId(), is("bar/storage/3"));
}
@Test
public void testUsingNodesAndGroupCountAttributesAndGettingJustOneNode() throws ParseException {
String services =
"\n" +
"" +
" " +
" " + // Ignored
" " +
" " +
" 4" +
" " +
" " +
" " +
" " +
" 3" +
" " +
"";
int numberOfHosts = 1; // We only have 1 content node -> 1 groups with redundancy 1
VespaModelTester tester = new VespaModelTester();
tester.addHosts(numberOfHosts);
VespaModel model = tester.createModel(services, false);
assertThat(model.getRoot().getHostSystem().getHosts().size(), is(numberOfHosts));
ContentCluster cluster = model.getContentClusters().get("bar");
ContainerCluster clusterControllers = cluster.getClusterControllers();
assertEquals(1, clusterControllers.getContainers().size());
assertEquals("bar-controllers", clusterControllers.getName());
assertEquals("default01", clusterControllers.getContainers().get(0).getHostName());
assertEquals(1, cluster.redundancy().effectiveInitialRedundancy()); // Reduced from 3*3
assertEquals(1, cluster.redundancy().effectiveFinalRedundancy()); // Reduced from 3*4
assertEquals(1, cluster.redundancy().effectiveReadyCopies()); // Reduced from 3*3
assertFalse(cluster.getRootGroup().getPartitions().isPresent()); // 1 group - > flattened -> no distribution
assertEquals(1, cluster.getRootGroup().getNodes().size());
assertEquals(0, cluster.getRootGroup().getSubgroups().size());
assertThat(cluster.getRootGroup().getNodes().size(), is(1));
assertThat(cluster.getRootGroup().getNodes().get(0).getDistributionKey(), is(0));
assertThat(cluster.getRootGroup().getNodes().get(0).getConfigId(), is("bar/storage/0"));
}
@Test(expected = IllegalArgumentException.class)
public void testRequiringMoreNodesThanAreAvailable() throws ParseException {
String services =
"\n" +
"" +
" " +
" 1" +
" " +
" " +
" " +
" " +
" " +
"";
int numberOfHosts = 2;
VespaModelTester tester = new VespaModelTester();
tester.addHosts(numberOfHosts);
tester.createModel(services, false);
}
@Test
public void testUsingNodesCountAttributesAndGettingJustOneNode() {
String services =
"\n" +
"" +
" " +
" " + // Ignored
" " +
" " +
" 12" +
" " +
" " +
" " +
" " +
" 5" +
" 7" +
" " +
"";
int numberOfHosts = 1;
VespaModelTester tester = new VespaModelTester();
tester.addHosts(numberOfHosts);
VespaModel model = tester.createModel(services, false);
assertThat(model.getRoot().getHostSystem().getHosts().size(), is(numberOfHosts));
ContentCluster cluster = model.getContentClusters().get("bar");
assertEquals(1, cluster.redundancy().effectiveInitialRedundancy());
assertEquals(1, cluster.redundancy().effectiveFinalRedundancy());
assertEquals(1, cluster.redundancy().effectiveReadyCopies());
assertEquals(1, cluster.getSearch().getIndexed().getDispatchSpec().getGroups().size());
assertFalse(cluster.getRootGroup().getPartitions().isPresent());
assertEquals(1, cluster.getRootGroup().getNodes().size());
assertEquals(0, cluster.getRootGroup().getSubgroups().size());
assertThat(cluster.getRootGroup().getNodes().size(), is(1));
assertThat(cluster.getRootGroup().getNodes().get(0).getDistributionKey(), is(0));
assertThat(cluster.getRootGroup().getNodes().get(0).getConfigId(), is("bar/storage/0"));
}
@Test
public void testRequestingSpecificFlavors() {
String services =
"\n" +
"" +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
"";
int totalHosts = 23;
VespaModelTester tester = new VespaModelTester();
tester.addHosts("logserver-flavor", 1);
tester.addHosts("slobrok-flavor", 2);
tester.addHosts("container-flavor", 4);
tester.addHosts("controller-foo-flavor", 2);
tester.addHosts("content-foo-flavor", 5);
tester.addHosts("controller-bar-flavor", 3);
tester.addHosts("content-bar-flavor", 6);
VespaModel model = tester.createModel(services, true, 0); // fails unless the right flavors+counts are requested
assertThat(model.getRoot().getHostSystem().getHosts().size(), is(totalHosts));
}
@Test
public void testJDiscOnly() {
String services =
"\n" +
"" +
" " +
" " +
"";
int numberOfHosts = 3;
VespaModelTester tester = new VespaModelTester();
tester.addHosts(numberOfHosts);
VespaModel model = tester.createModel(services, true);
assertEquals(numberOfHosts, model.getRoot().getHostSystem().getHosts().size());
assertEquals(3, model.getContainerClusters().get("jdisc").getContainers().size());
assertNotNull(model.getAdmin().getLogserver());
assertEquals(3, model.getAdmin().getSlobroks().size());
}
@Test
public void testUsingHostaliasWithProvisioner() {
String services =
"\n" +
"" +
"" +
" \n"+
"\n" +
"" +
" " +
" " +
" " +
" " +
" " +
" " +
"" +
"";
VespaModelTester tester = new VespaModelTester();
tester.addHosts(1);
VespaModel model = tester.createModel(services, true);
assertEquals(1, model.getRoot().getHostSystem().getHosts().size());
assertEquals(1, model.getAdmin().getSlobroks().size());
}
@Test
public void testThatStandaloneSyntaxWorksOnHostedVespa() {
String services =
"" +
"" +
" " +
" " +
" " +
"";
VespaModelTester tester = new VespaModelTester();
tester.addHosts(1);
VespaModel model = tester.createModel(services, true);
assertThat(model.getHosts().size(), is(1));
assertThat(model.getContainerClusters().size(), is(1));
}
@Test
public void testNoNodeTagMeans1Node() {
String services =
"\n" +
"" +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
"";
VespaModelTester tester = new VespaModelTester();
tester.addHosts(1);
VespaModel model = tester.createModel(services, true);
assertEquals(1, model.getRoot().getHostSystem().getHosts().size());
assertEquals(1, model.getAdmin().getSlobroks().size());
assertEquals(1, model.getContainerClusters().get("foo").getContainers().size());
assertEquals(1, model.getContentClusters().get("bar").getRootGroup().countNodes());
}
@Test
public void testNoNodeTagMeans1NodeNoContent() {
String services =
"\n" +
"" +
" " +
" " +
" " +
" " +
"";
VespaModelTester tester = new VespaModelTester();
tester.addHosts(1);
VespaModel model = tester.createModel(services, true);
assertEquals(1, model.getRoot().getHostSystem().getHosts().size());
assertEquals(1, model.getAdmin().getSlobroks().size());
assertEquals(1, model.getContainerClusters().get("foo").getContainers().size());
}
@Test
public void testNoNodeTagMeans1NodeNonHosted() {
String services =
"\n" +
"" +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
"";
VespaModelTester tester = new VespaModelTester();
tester.setHosted(false);
tester.addHosts(1);
VespaModel model = tester.createModel(services, true);
assertEquals(1, model.getRoot().getHostSystem().getHosts().size());
assertEquals(1, model.getAdmin().getSlobroks().size());
assertEquals(1, model.getContainerClusters().get("foo").getContainers().size());
assertEquals(1, model.getContentClusters().get("bar").getRootGroup().recursiveGetNodes().size());
}
@Test
public void testSingleNodeNonHosted() {
String services =
"\n" +
"" +
" " +
" " +
" " +
" "+
" " +
" " +
" " +
" " +
" " +
" "+
" " +
"";
VespaModelTester tester = new VespaModelTester();
tester.setHosted(false);
tester.addHosts(1);
VespaModel model = tester.createModel(services, true);
assertEquals(1, model.getRoot().getHostSystem().getHosts().size());
assertEquals(1, model.getAdmin().getSlobroks().size());
assertEquals(1, model.getContainerClusters().get("foo").getContainers().size());
assertEquals(1, model.getContentClusters().get("bar").getRootGroup().countNodes());
}
/** Recreate the combination used in some factory tests */
@Test
public void testMultitenantButNotHosted() {
String services =
"" +
"" +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
" 2" +
" " +
" " +
" " +
" " +
" " +
" " +
" 0" +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
" ";
VespaModel model = createNonProvisionedMultitenantModel(services);
assertThat(model.getRoot().getHostSystem().getHosts().size(), is(1));
ContentCluster content = model.getContentClusters().get("storage");
assertEquals(2, content.getRootGroup().getNodes().size());
ContainerCluster controller = content.getClusterControllers();
assertEquals(1, controller.getContainers().size());
}
@Test
public void testModelWithReferencedIndexingCluster() {
String services =
"\n" +
"\n" +
"\n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
"\n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
"\n" +
" \n" +
" \n" +
" 1.0\n" +
" \n" +
" 2\n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
"\n" +
"";
VespaModel model = createNonProvisionedMultitenantModel(services);
assertThat(model.getRoot().getHostSystem().getHosts().size(), is(1));
ContentCluster content = model.getContentClusters().get("storage");
assertEquals(1, content.getRootGroup().getNodes().size());
ContainerCluster controller = content.getClusterControllers();
assertEquals(1, controller.getContainers().size());
}
@Test
public void testSharedNodesNotHosted() {
String hosts =
"\n" +
"\n" +
" \n" +
" vespa-1\n" +
" \n" +
" \n" +
" vespa-2\n" +
" \n" +
" \n" +
" vespa-3\n" +
" \n" +
"";
String services =
"\n" +
"\n" +
"\n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
"\n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
"\n" +
" \n" +
" \n" +
" 1.0\n" +
" \n" +
" 2\n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
"\n" +
"";
VespaModel model = createNonProvisionedModel(false, hosts, services);
assertEquals(3, model.getRoot().getHostSystem().getHosts().size());
ContentCluster content = model.getContentClusters().get("storage");
assertEquals(3, content.getRootGroup().getNodes().size());
}
@Test
public void testMultitenantButNotHostedSharedContentNode() {
String services =
"" +
"" +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
" 2" +
" " +
" " +
" " +
" " +
" " +
" " +
" 0" +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
" 2" +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
" ";
VespaModel model = createNonProvisionedMultitenantModel(services);
assertThat(model.getRoot().getHostSystem().getHosts().size(), is(1));
ContentCluster content = model.getContentClusters().get("storage");
assertEquals(2, content.getRootGroup().getNodes().size());
ContainerCluster controller = content.getClusterControllers();
assertEquals(1, controller.getContainers().size());
}
private VespaModel createNonProvisionedMultitenantModel(String services) {
return createNonProvisionedModel(true, null, services);
}
private VespaModel createNonProvisionedModel(boolean multitenant, String hosts, String services) {
VespaModelCreatorWithMockPkg modelCreatorWithMockPkg = new VespaModelCreatorWithMockPkg(hosts, services, ApplicationPackageUtils.generateSearchDefinition("type1"));
ApplicationPackage appPkg = modelCreatorWithMockPkg.appPkg;
DeployState deployState = new DeployState.Builder().applicationPackage(appPkg).
properties((new DeployProperties.Builder()).multitenant(multitenant).build()).
build();
return modelCreatorWithMockPkg.create(false, deployState);
}
@Test
public void testThatTldConfigIdsAreDeterministic() {
String services =
"\n" +
"" +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
" " +
" 2" +
" " +
" " +
" " +
" " +
" " +
" " +
" 2" +
" " +
" " +
" " +
" " +
" " +
"";
int numberOfHosts = 8;
{
VespaModelTester tester = new VespaModelTester();
tester.addHosts(numberOfHosts);
// Nodes used will be default0, default1, .. and so on.
VespaModel model = tester.createModel(services, true);
assertThat(model.getRoot().getHostSystem().getHosts().size(), is(numberOfHosts));
Map contentClusters = model.getContentClusters();
assertEquals(2, contentClusters.size());
checkThatTldAndContainerRunningOnSameHostHaveSameId(
model.getContainerClusters().values(),
model.getContentClusters().values(),
0);
}
{
VespaModelTester tester = new VespaModelTester();
tester.addHosts(numberOfHosts + 1);
// Start numbering nodes with index 1 and retire first node
// Nodes used will be default1, default2, .. and so on. Containers will start with index 1, not 0 as they are in the test above
VespaModel model = tester.createModel(services, true, 1, "default0");
assertThat(model.getRoot().getHostSystem().getHosts().size(), is(numberOfHosts));
Map contentClusters = model.getContentClusters();
assertEquals(2, contentClusters.size());
checkThatTldAndContainerRunningOnSameHostHaveSameId(
model.getContainerClusters().values(),
model.getContentClusters().values(),
1);
}
}
private void checkThatTldAndContainerRunningOnSameHostHaveSameId(Collection containerClusters,
Collection contentClusters,
int startIndexForContainerIds) {
for (ContentCluster contentCluster : contentClusters) {
String contentClusterName = contentCluster.getName();
int i = 0;
for (ContainerCluster containerCluster : containerClusters) {
String containerClusterName = containerCluster.getName();
for (int j = 0; j < 2; j++) {
Dispatch tld = contentCluster.getSearch().getIndexed().getTLDs().get(2 * i + j);
Container container = containerCluster.getContainers().get(j);
int containerConfigIdIndex = j + startIndexForContainerIds;
assertEquals(container.getHostName(), tld.getHostname());
assertEquals(contentClusterName + "/search/cluster." + contentClusterName + "/tlds/" +
containerClusterName + "." + containerConfigIdIndex + ".tld." + containerConfigIdIndex,
tld.getConfigId());
assertEquals(containerClusterName + "/" + "container." + containerConfigIdIndex,
container.getConfigId());
}
i++;
}
}
}
private int physicalMemoryPercentage(ContainerCluster cluster) {
QrStartConfig.Builder b = new QrStartConfig.Builder();
cluster.getConfig(b);
return new QrStartConfig(b).jvm().heapSizeAsPercentageOfPhysicalMemory();
}
@Test
public void require_that_proton_config_is_tuned_based_on_node_flavor() {
String services = joinLines("",
"",
" ",
" ",
" ",
" ",
" ",
" ",
"");
VespaModelTester tester = new VespaModelTester();
tester.addHosts(createFlavorFromDiskSetting("content-test-flavor", false), 2);
VespaModel model = tester.createModel(services, true, 0);
ContentSearchCluster cluster = model.getContentClusters().get("test").getSearch();
assertEquals(2, cluster.getSearchNodes().size());
assertEquals(40, getProtonConfig(cluster, 0).hwinfo().disk().writespeed(), 0.001);
assertEquals(40, getProtonConfig(cluster, 1).hwinfo().disk().writespeed(), 0.001);
}
private static Flavor createFlavorFromDiskSetting(String name, boolean fastDisk) {
return new Flavor(new FlavorsConfig.Flavor(new FlavorsConfig.Flavor.Builder().
name(name).fastDisk(fastDisk)));
}
private static ProtonConfig getProtonConfig(ContentSearchCluster cluster, int searchNodeIdx) {
ProtonConfig.Builder builder = new ProtonConfig.Builder();
List searchNodes = cluster.getSearchNodes();
assertTrue(searchNodeIdx < searchNodes.size());
searchNodes.get(searchNodeIdx).getConfig(builder);
return new ProtonConfig(builder);
}
@Test
public void require_that_config_override_and_explicit_proton_tuning_have_precedence_over_default_node_flavor_tuning() {
String services = joinLines("",
"",
" ",
" ",
" 2000",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" 1000",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
"");
VespaModelTester tester = new VespaModelTester();
tester.addHosts("default", 1);
tester.addHosts(createFlavorFromMemoryAndDisk("content-test-flavor", 128, 100), 1);
VespaModel model = tester.createModel(services, true, 0);
ContentSearchCluster cluster = model.getContentClusters().get("test").getSearch();
ProtonConfig cfg = getProtonConfig(model, cluster.getSearchNodes().get(0).getConfigId());
assertEquals(2000, cfg.flush().memory().maxtlssize()); // from config override
assertEquals(1000, cfg.flush().memory().maxmemory()); // from explicit tuning
assertEquals((long) 16 * GB, cfg.flush().memory().each().maxmemory()); // from default node flavor tuning
}
private static long GB = 1024 * 1024 * 1024;
private static Flavor createFlavorFromMemoryAndDisk(String name, int memoryGb, int diskGb) {
return new Flavor(new FlavorsConfig.Flavor(new FlavorsConfig.Flavor.Builder().
name(name).minMainMemoryAvailableGb(memoryGb).minDiskAvailableGb(diskGb)));
}
private static ProtonConfig getProtonConfig(VespaModel model, String configId) {
ProtonConfig.Builder builder = new ProtonConfig.Builder();
model.getConfig(builder, configId);
return new ProtonConfig(builder);
}
// Tests that a container is allocated on logserver host and that
// it is able to get config
private void testContainerOnLogserverHost(String services, boolean useDedicatedNodeForLogserver) {
int numberOfHosts = 2;
VespaModelTester tester = new VespaModelTester();
tester.useDedicatedNodeForLogserver(useDedicatedNodeForLogserver);
tester.addHosts(numberOfHosts);
Zone zone = new Zone(SystemName.cd, Environment.dev, RegionName.defaultName());
VespaModel model = tester.createModel(zone, services, true);
assertThat(model.getRoot().getHostSystem().getHosts().size(), is(numberOfHosts));
Admin admin = model.getAdmin();
Logserver logserver = admin.getLogserver();
HostResource hostResource = logserver.getHostResource();
assertNotNull(hostResource.getService("logserver"));
assertNotNull(hostResource.getService("container"));
// Test that the container gets config
String configId = admin.getLogserver().getHostResource().getService("container").getConfigId();
ApplicationMetadataConfig.Builder builder = new ApplicationMetadataConfig.Builder();
model.getConfig(builder, configId);
ApplicationMetadataConfig cfg = new ApplicationMetadataConfig(builder);
assertEquals(1, cfg.generation());
LogdConfig.Builder logdConfigBuilder = new LogdConfig.Builder();
model.getConfig(logdConfigBuilder, configId);
LogdConfig logdConfig = new LogdConfig(logdConfigBuilder);
// Logd should use logserver (forward logs to it)
assertTrue(logdConfig.logserver().use());
}
}