// 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.application.api.ApplicationPackage;
import com.yahoo.config.model.MockModelContext;
import com.yahoo.config.model.api.HostInfo;
import com.yahoo.config.model.api.HostProvisioner;
import com.yahoo.config.model.api.Model;
import com.yahoo.config.model.api.ModelContext;
import com.yahoo.config.model.api.ModelCreateResult;
import com.yahoo.config.model.api.ServiceInfo;
import com.yahoo.config.model.api.ValidationParameters;
import com.yahoo.config.model.deploy.TestProperties;
import com.yahoo.config.model.test.MockApplicationPackage;
import com.yahoo.config.provision.Capacity;
import com.yahoo.config.provision.ClusterMembership;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.HostSpec;
import com.yahoo.config.provision.NodeResources;
import com.yahoo.config.provision.ProvisionLogger;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.*;
/**
* @author Ulf Lilleengen
*/
public class VespaModelFactoryTest {
private ModelContext testModelContext;
@BeforeEach
public void setupContext() {
testModelContext = new MockModelContext();
}
@Test
void testThatFactoryCanBuildModel() {
VespaModelFactory modelFactory = VespaModelFactory.createTestFactory();
Model model = modelFactory.createModel(testModelContext);
assertNotNull(model);
assertTrue(model instanceof VespaModel);
}
// Uses an application package that throws IllegalArgumentException when validating
@Test
void testThatFactoryModelValidationFailsWithIllegalArgumentException() {
assertThrows(IllegalArgumentException.class, () -> {
VespaModelFactory modelFactory = VespaModelFactory.createTestFactory();
modelFactory.createAndValidateModel(new MockModelContext(createApplicationPackageThatFailsWhenValidating()), new ValidationParameters());
});
}
// Uses a MockApplicationPackage that throws throws UnsupportedOperationException (rethrown as RuntimeException) when validating
@Test
void testThatFactoryModelValidationFails() {
assertThrows(RuntimeException.class, () -> {
VespaModelFactory modelFactory = VespaModelFactory.createTestFactory();
modelFactory.createAndValidateModel(testModelContext, new ValidationParameters());
});
}
@Test
void testThatFactoryModelValidationCanBeIgnored() {
VespaModelFactory modelFactory = VespaModelFactory.createTestFactory();
ModelCreateResult createResult = modelFactory.createAndValidateModel(
new MockModelContext(createApplicationPackageThatFailsWhenValidating()),
new ValidationParameters(ValidationParameters.IgnoreValidationErrors.TRUE));
assertNotNull(createResult.getModel());
assertNotNull(createResult.getConfigChangeActions());
assertTrue(createResult.getConfigChangeActions().isEmpty());
}
@Test
void hostedVespaZoneApplicationAllocatesNodesFromNodeRepo() {
String hostName = "test-host-name";
String routingClusterName = "routing-cluster";
String hosts =
"\n" +
"\n" +
" \n" +
" proxy1\n" +
" \n" +
"";
String services =
"\n" +
"\n" +
" \n" +
" \n" +
" " +
" \n" +
" \n" +
" \n" +
"";
HostProvisioner provisionerToOverride = new HostProvisioner() {
@Override
public HostSpec allocateHost(String alias) {
return new HostSpec(hostName,
NodeResources.unspecified(), NodeResources.unspecified(), NodeResources.unspecified(),
ClusterMembership.from(ClusterSpec.request(ClusterSpec.Type.admin, new ClusterSpec.Id(routingClusterName)).vespaVersion("6.42").build(), 0),
Optional.empty(), Optional.empty(), Optional.empty());
}
@Override
public List prepare(ClusterSpec cluster, Capacity capacity, ProvisionLogger logger) {
return List.of(new HostSpec(hostName,
NodeResources.unspecified(), NodeResources.unspecified(), NodeResources.unspecified(),
ClusterMembership.from(ClusterSpec.request(ClusterSpec.Type.container, new ClusterSpec.Id(routingClusterName)).vespaVersion("6.42").build(), 0),
Optional.empty(), Optional.empty(), Optional.empty()));
}
};
ModelContext modelContext = createMockModelContext(hosts, services, provisionerToOverride);
Model model = VespaModelFactory.createTestFactory().createModel(modelContext);
List allocatedHosts = new ArrayList<>(model.getHosts());
assertEquals(1, allocatedHosts.size());
HostInfo hostInfo = allocatedHosts.get(0);
assertEquals(hostName, hostInfo.getHostname());
assertTrue(hostInfo.getServices().stream()
.map(ServiceInfo::getConfigId)
.anyMatch(configId -> configId.contains(routingClusterName)),
"Routing service should run on host " + hostName);
}
private ModelContext createMockModelContext(String hosts, String services, HostProvisioner provisionerToOverride) {
return new MockModelContext() {
@Override
public ApplicationPackage applicationPackage() {
return new MockApplicationPackage.Builder().withHosts(hosts).withServices(services).build();
}
@Override
public HostProvisioner getHostProvisioner() { return provisionerToOverride; }
@Override
public Properties properties() {
return new TestProperties();
}
};
}
ApplicationPackage createApplicationPackageThatFailsWhenValidating() {
return new MockApplicationPackage.Builder().withEmptyHosts().withEmptyServices().failOnValidateXml().build();
}
}