aboutsummaryrefslogtreecommitdiffstats
path: root/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/changemanagement/ChangeManagementApiHandlerTest.java
blob: 8a915d72b2570a992ef7d8b5637eeed69855e41d (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
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.controller.restapi.changemanagement;

import com.yahoo.application.container.handler.Request;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.vespa.athenz.api.AthenzIdentity;
import com.yahoo.vespa.athenz.api.AthenzUser;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.Node;
import com.yahoo.vespa.hosted.controller.api.integration.vcmr.ChangeRequest;
import com.yahoo.vespa.hosted.controller.api.integration.vcmr.ChangeRequestSource;
import com.yahoo.vespa.hosted.controller.api.integration.vcmr.HostAction;
import com.yahoo.vespa.hosted.controller.api.integration.vcmr.VespaChangeRequest;
import com.yahoo.vespa.hosted.controller.restapi.ContainerTester;
import com.yahoo.vespa.hosted.controller.restapi.ControllerContainerTest;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.File;
import java.time.Instant;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class ChangeManagementApiHandlerTest extends ControllerContainerTest {

    private static final String responses = "src/test/java/com/yahoo/vespa/hosted/controller/restapi/changemanagement/responses/";
    private static final AthenzIdentity operator = AthenzUser.fromUserId("operatorUser");
    private static final String changeRequestId = "id123";

    private ContainerTester tester;

    @BeforeEach
    public void before() {
        tester = new ContainerTester(container, responses);
        addUserToHostedOperatorRole(operator);
        tester.serviceRegistry().configServer().nodeRepository().putNodes(ZoneId.from("prod.us-east-3"), createNodes());
        tester.controller().curator().writeChangeRequest(createChangeRequest());

    }

    @Test
    void test_api() {
        assertFile(new Request("http://localhost:8080/changemanagement/v1/assessment", "{\"zone\":\"prod.us-east-3\", \"hosts\": [\"host1\"]}", Request.Method.POST), "initial.json");
        assertFile(new Request("http://localhost:8080/changemanagement/v1/assessment", "{\"zone\":\"prod.us-east-3\", \"switches\": [\"switch1\"]}", Request.Method.POST), "initial.json");
        assertFile(new Request("http://localhost:8080/changemanagement/v1/vcmr"), "vcmrs.json");
    }

    @Test
    void deletes_vcmr() {
        assertEquals(1, tester.controller().curator().readChangeRequests().size());
        assertFile(new Request("http://localhost:8080/changemanagement/v1/vcmr/" + changeRequestId, "", Request.Method.DELETE), "vcmr.json");
        assertEquals(0, tester.controller().curator().readChangeRequests().size());
    }

    @Test
    void get_vcmr() {
        assertFile(new Request("http://localhost:8080/changemanagement/v1/vcmr/" + changeRequestId, "", Request.Method.GET), "vcmr.json");
    }

    @Test
    void patch_vcmr() {
        var payload = "{" +
                "\"approval\": \"REJECTED\"," +
                "\"status\": \"COMPLETED\"," +
                "\"actionPlan\": {" +
                "   \"hosts\": [{" +
                "       \"hostname\": \"host1\"," +
                "       \"state\": \"REQUIRES_OPERATOR_ACTION\"," +
                "       \"lastUpdated\": \"2021-05-10T14:08:15Z\"" +
                "}]}" +
                "}";
        assertFile(new Request("http://localhost:8080/changemanagement/v1/vcmr/" + changeRequestId, payload, Request.Method.PATCH), "patched-vcmr.json");
        var changeRequest = tester.controller().curator().readChangeRequest(changeRequestId).orElseThrow();
        assertEquals(ChangeRequest.Approval.REJECTED, changeRequest.getApproval());
        assertEquals(VespaChangeRequest.Status.COMPLETED, changeRequest.getStatus());
    }

    private void assertFile(Request request, String filename) {
        addIdentityToRequest(request, operator);
        tester.assertResponse(request, new File(filename));
    }

    private VespaChangeRequest createChangeRequest() {
        var instant = Instant.ofEpochMilli(9001);
        var date = ZonedDateTime.ofInstant(instant, java.time.ZoneId.of("UTC"));
        var source = new ChangeRequestSource("aws", "id321", "url", ChangeRequestSource.Status.STARTED, date, date, "N/A");
        var actionPlan = List.of(
                new HostAction("host1", HostAction.State.RETIRING, instant),
                new HostAction("host2", HostAction.State.RETIRED, instant)
        );

        return new VespaChangeRequest(
                changeRequestId,
                source,
                List.of("switch1"),
                List.of("host1", "host2"),
                ChangeRequest.Approval.APPROVED,
                ChangeRequest.Impact.VERY_HIGH,
                VespaChangeRequest.Status.IN_PROGRESS,
                actionPlan,
                ZoneId.defaultId()
        );
    }

    private List<Node> createNodes() {
        List<Node> nodes = new ArrayList<>();
        nodes.add(createNode("node1", "host1", "default", 0 ));
        nodes.add(createNode("node2", "host1", "default", 0 ));
        nodes.add(createNode("node3", "host1", "default", 0 ));
        nodes.add(createNode("node4", "host2", "default", 0 ));
        nodes.add(createHost("host1", "switch1"));
        nodes.add(createHost("host2", "switch2"));
        return nodes;
    }

    private Node createNode(String nodename, String hostname, String clusterId, int group) {
        return Node.builder()
                   .hostname(nodename)
                   .parentHostname(hostname).state(Node.State.active)
                   .owner(ApplicationId.from("mytenant", "myapp", "default"))
                   .type(com.yahoo.config.provision.NodeType.tenant)
                   .clusterId(clusterId)
                   .group(String.valueOf(group))
                   .clusterType(Node.ClusterType.content)
                   .build();
    }

    private Node createHost(String hostname, String switchName) {
        return Node.builder()
                   .hostname(hostname)
                   .switchHostname(switchName)
                   .owner(ApplicationId.from("mytenant", "myapp", "default"))
                   .type(com.yahoo.config.provision.NodeType.host)
                   .clusterId("host")
                   .group("0")
                   .build();
    }

}