aboutsummaryrefslogtreecommitdiffstats
path: root/config-model/src/test/java/com/yahoo/vespa/model/VespaModelFactoryTest.java
blob: 9bcf29aa8f82d1cebaaddceb8e20586009d3f2c2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
// 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 =
                "<?xml version='1.0' encoding='utf-8' ?>\n" +
                        "<hosts>\n" +
                        "  <host name='" + hostName + "'>\n" +
                        "    <alias>proxy1</alias>\n" +
                        "  </host>\n" +
                        "</hosts>";

        String services =
                "<?xml version='1.0' encoding='utf-8' ?>\n" +
                        "<services version='1.0' xmlns:deploy='vespa'>\n" +
                        "    <admin version='2.0'>\n" +
                        "        <adminserver hostalias='proxy1' />\n" +
                        "    </admin>" +
                        "    <container id='" + routingClusterName + "' version='1.0'>\n" +
                        "        <nodes type='proxy'/>\n" +
                        "    </container>\n" +
                        "</services>";

        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<HostSpec> 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<HostInfo> 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();
    }

}