summaryrefslogtreecommitdiffstats
path: root/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/InfrastructureProvisionerTest.java
blob: 642e6adfc75f8778f4e0b90bbd4397e0ccd9b207 (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
// 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.provision.maintenance;

import com.yahoo.component.Version;
import com.yahoo.config.provision.ClusterMembership;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.NodeType;
import com.yahoo.config.provision.Provisioner;
import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeRepository;
import com.yahoo.vespa.hosted.provision.NodeRepositoryTester;
import com.yahoo.vespa.hosted.provision.node.Agent;
import com.yahoo.vespa.hosted.provision.node.Allocation;
import com.yahoo.vespa.hosted.provision.node.Generation;
import com.yahoo.vespa.service.monitor.application.ConfigServerApplication;
import com.yahoo.vespa.service.monitor.application.ControllerApplication;
import com.yahoo.vespa.service.monitor.application.HostedVespaApplication;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

import java.time.Duration;
import java.util.Arrays;
import java.util.Optional;

import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

/**
 * @author freva
 */
@RunWith(Parameterized.class)
public class InfrastructureProvisionerTest {

    @Parameters(name = "application={0}")
    public static Iterable<Object[]> parameters() {
        return Arrays.asList(
                new HostedVespaApplication[]{ConfigServerApplication.CONFIG_SERVER_APPLICATION},
                new HostedVespaApplication[]{ControllerApplication.CONTROLLER_APPLICATION}
        );
    }

    private final NodeRepositoryTester tester = new NodeRepositoryTester();
    private final Provisioner provisioner = mock(Provisioner.class);
    private final NodeRepository nodeRepository = tester.nodeRepository();
    private final InfrastructureVersions infrastructureVersions = mock(InfrastructureVersions.class);
    private final InfrastructureProvisioner infrastructureProvisioner = new InfrastructureProvisioner(
            provisioner, nodeRepository, infrastructureVersions, Duration.ofDays(99), new JobControl(nodeRepository.database()));

    private final HostedVespaApplication application;
    private final NodeType nodeType;

    public InfrastructureProvisionerTest(HostedVespaApplication application) {
        this.application = application;
        this.nodeType = application.getCapacity().type();
    }

    @Test
    public void returns_version_if_usable_nodes_on_old_version() {
        Version target = Version.fromString("6.123.456");
        Version oldVersion = Version.fromString("6.122.333");
        when(infrastructureVersions.getTargetVersionFor(eq(nodeType))).thenReturn(Optional.of(target));

        addNode(1, Node.State.failed, Optional.of(oldVersion));
        addNode(2, Node.State.dirty, Optional.empty());
        addNode(3, Node.State.active, Optional.of(oldVersion));

        assertEquals(Optional.of(target), infrastructureProvisioner.getTargetVersion(nodeType));
    }

    @Test
    public void returns_version_if_has_usable_nodes_without_version() {
        Version target = Version.fromString("6.123.456");
        Version oldVersion = Version.fromString("6.122.333");
        when(infrastructureVersions.getTargetVersionFor(eq(nodeType))).thenReturn(Optional.of(target));

        addNode(1, Node.State.failed, Optional.of(oldVersion));
        addNode(2, Node.State.ready, Optional.empty());
        addNode(3, Node.State.active, Optional.of(target));

        assertEquals(Optional.of(target), infrastructureProvisioner.getTargetVersion(nodeType));
    }

    @Test
    public void returns_empty_if_usable_nodes_on_target_version() {
        Version target = Version.fromString("6.123.456");
        Version oldVersion = Version.fromString("6.122.333");
        when(infrastructureVersions.getTargetVersionFor(eq(nodeType))).thenReturn(Optional.of(target));

        addNode(1, Node.State.failed, Optional.of(oldVersion));
        addNode(2, Node.State.parked, Optional.of(target));
        addNode(3, Node.State.active, Optional.of(target));
        addNode(4, Node.State.inactive, Optional.of(target));
        addNode(5, Node.State.dirty, Optional.empty());

        assertEquals(Optional.empty(), infrastructureProvisioner.getTargetVersion(nodeType));
    }

    @Test
    public void returns_empty_if_no_usable_nodes() {
        when(infrastructureVersions.getTargetVersionFor(eq(nodeType))).thenReturn(Optional.of(Version.fromString("6.123.456")));

        // No nodes in node repo
        assertEquals(Optional.empty(), infrastructureProvisioner.getTargetVersion(nodeType));

        // Add nodes in non-provisionable states
        addNode(1, Node.State.dirty, Optional.empty());
        addNode(2, Node.State.failed, Optional.empty());
        assertEquals(Optional.empty(), infrastructureProvisioner.getTargetVersion(nodeType));
    }

    @Test
    public void returns_empty_if_target_version_not_set() {
        when(infrastructureVersions.getTargetVersionFor(eq(nodeType))).thenReturn(Optional.empty());
        assertEquals(Optional.empty(), infrastructureProvisioner.getTargetVersion(nodeType));
    }

    private Node addNode(int id, Node.State state, Optional<Version> wantedVespaVersion) {
        Node node = tester.addNode("id-" + id, "node-" + id, "default", nodeType);
        Optional<Node> nodeWithAllocation = wantedVespaVersion.map(version -> {
            ClusterSpec clusterSpec = ClusterSpec.from(application.getClusterType(), application.getClusterId(), ClusterSpec.Group.from(0), version, false);
            ClusterMembership membership = ClusterMembership.from(clusterSpec, 1);
            Allocation allocation = new Allocation(application.getApplicationId(), membership, new Generation(0, 0), false);
            return node.with(allocation);
        });
        return nodeRepository.database().writeTo(state, nodeWithAllocation.orElse(node), Agent.system, Optional.empty());
    }

}