summaryrefslogtreecommitdiffstats
path: root/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/ResumeTest.java
blob: c7489fc28aa3560a22946dbb7d9c3504648d6ba6 (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
// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.node.admin.integrationTests;

import com.yahoo.vespa.applicationmodel.HostName;
import com.yahoo.vespa.hosted.node.admin.ContainerNodeSpec;
import com.yahoo.vespa.hosted.node.admin.nodeadmin.NodeAdmin;
import com.yahoo.vespa.hosted.node.admin.nodeadmin.NodeAdminImpl;
import com.yahoo.vespa.hosted.node.admin.nodeadmin.NodeAdminStateUpdater;
import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgent;
import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgentImpl;
import com.yahoo.vespa.hosted.dockerapi.ContainerName;
import com.yahoo.vespa.hosted.dockerapi.DockerImage;
import com.yahoo.vespa.hosted.node.admin.nodeagent.DockerOperations;
import com.yahoo.vespa.hosted.node.admin.noderepository.NodeState;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;

import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.StringStartsWith.startsWith;
import static org.junit.Assert.assertThat;

/**
 * Scenario test for NodeAdminStateUpdater.
 *
 * @author dybis
 */
public class ResumeTest {
    @Before
    public void resetMocks() {
        try {
            OrchestratorMock.semaphore.acquire();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        MaintenanceSchedulerMock.reset();
        OrchestratorMock.reset();
        NodeRepoMock.reset();
        DockerMock.reset();
    }

    @After
    public void after() {
        OrchestratorMock.semaphore.release();
    }

    @Test
    public void test() throws InterruptedException {
        NodeRepoMock nodeRepositoryMock = new NodeRepoMock();
        MaintenanceSchedulerMock maintenanceSchedulerMock = new MaintenanceSchedulerMock();
        OrchestratorMock orchestratorMock = new OrchestratorMock();
        DockerMock dockerMock = new DockerMock();

        Function<HostName, NodeAgent> nodeAgentFactory = (hostName) ->
                new NodeAgentImpl(hostName, nodeRepositoryMock, orchestratorMock, new DockerOperations(dockerMock), maintenanceSchedulerMock);
        NodeAdmin nodeAdmin = new NodeAdminImpl(dockerMock, nodeAgentFactory, maintenanceSchedulerMock, 100);

        NodeRepoMock.addContainerNodeSpec(new ContainerNodeSpec(
                new HostName("host1"),
                Optional.of(new DockerImage("dockerImage")),
                new ContainerName("container"),
                NodeState.ACTIVE,
                Optional.of(1L),
                Optional.of(1L),
                Optional.of(1d),
                Optional.of(1d),
                Optional.of(1d)));

        NodeAdminStateUpdater updater = new NodeAdminStateUpdater(nodeRepositoryMock, nodeAdmin, 1, 1, orchestratorMock, "basehostname");

        // Wait for node admin to be notified with node repo state and the docker container has been started
        while (nodeAdmin.getListOfHosts().size() == 0) {
            Thread.sleep(10);
        }

        while (!DockerMock.getRequests().startsWith("startContainer with DockerImage: DockerImage { imageId=dockerImage }, " +
                "HostName: host1, ContainerName: ContainerName { name=container }, InetAddress: null, minCpuCores: 1.0, " +
                "minDiskAvailableGb: 1.0, minMainMemoryAvailableGb: 1.0\n")) {
            Thread.sleep(10);
        }

        assertThat(DockerMock.getRequests(), startsWith("startContainer with DockerImage: DockerImage { imageId=dockerImage }, " +
                "HostName: host1, ContainerName: ContainerName { name=container }, InetAddress: null, minCpuCores: 1.0, " +
                "minDiskAvailableGb: 1.0, minMainMemoryAvailableGb: 1.0\n"));


        // Check that NodeRepo has received the PATCH update
        while (!NodeRepoMock.getRequests().startsWith("updateNodeAttributes with HostName: host1, " +
                "restartGeneration: 1, DockerImage: DockerImage { imageId=dockerImage }, containerVespaVersion: null\n")) {
            Thread.sleep(10);
        }

        assertThat(NodeRepoMock.getRequests(), startsWith("updateNodeAttributes with HostName: host1, restartGeneration: 1," +
                " DockerImage: DockerImage { imageId=dockerImage }, containerVespaVersion: null\n"));

        // Force orchestrator to reject the suspend
        OrchestratorMock.setForceGroupSuspendResponse(Optional.of("Orchestrator reject suspend"));

        // At this point NodeAdmin should be fine with the suspend and it is up to Orchestrator
        while (!updater.setResumeStateAndCheckIfResumed(NodeAdminStateUpdater.State.SUSPENDED)
                .equals(Optional.of("Orchestrator reject suspend"))) {
            Thread.sleep(5);
        }
        assertThat(updater.setResumeStateAndCheckIfResumed(NodeAdminStateUpdater.State.SUSPENDED), is(Optional.of("Orchestrator reject suspend")));

        //Make orchestrator allow suspend requests
        OrchestratorMock.setForceGroupSuspendResponse(Optional.empty());
        assertThat(updater.setResumeStateAndCheckIfResumed(NodeAdminStateUpdater.State.SUSPENDED), is(Optional.empty()));

        // Now, change data in node repo, should not propagate.
        NodeRepoMock.clearContainerNodeSpecs();

        // New node repo state should have not propagated to node admin
        Thread.sleep(2);
        assertThat(nodeAdmin.getListOfHosts().size(), is(1));

        // Now resume
        assertThat(updater.setResumeStateAndCheckIfResumed(NodeAdminStateUpdater.State.RESUMED), is(Optional.empty()));

        // Now node repo state should propagate to node admin again
        while (nodeAdmin.getListOfHosts().size() != 0) {
            Thread.sleep(1);
        }

        final String[] allRequests = OrchestratorMock.getRequests().split("\n");
        final List<String> noRepeatingRequests = new ArrayList<>();
        for (String request : allRequests) {
            if (!noRepeatingRequests.contains(request)) {
                noRepeatingRequests.add(request);
            }
        }

        List<String> expectedRequests = Arrays.asList("Resume for host1",
                "Suspend with parent: basehostname and hostnames: [host1] - Forced response: Optional[Orchestrator reject suspend]",
                "Suspend with parent: basehostname and hostnames: [host1] - Forced response: Optional.empty");

        // Check that the orchestrator did receive and properly responded to the previous requests
        assertThat(noRepeatingRequests, is(expectedRequests));

        updater.deconstruct();
    }
}