aboutsummaryrefslogtreecommitdiffstats
path: root/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/resources/ApplicationSuspensionRequestHandlerTest.java
blob: 021cea6cd5cff9e073aeec6437c2bc371363b7b8 (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
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.orchestrator.resources;

import com.fasterxml.jackson.core.type.TypeReference;
import com.yahoo.cloud.config.ConfigserverConfig;
import com.yahoo.config.provision.Zone;
import com.yahoo.container.jdisc.HttpRequestBuilder;
import com.yahoo.container.jdisc.HttpResponse;
import com.yahoo.jdisc.core.SystemTimer;
import com.yahoo.jdisc.test.MockMetric;
import com.yahoo.restapi.RestApiTestDriver;
import com.yahoo.vespa.curator.mock.MockCurator;
import com.yahoo.vespa.flags.InMemoryFlagSource;
import com.yahoo.vespa.orchestrator.DummyServiceMonitor;
import com.yahoo.vespa.orchestrator.Orchestrator;
import com.yahoo.vespa.orchestrator.OrchestratorImpl;
import com.yahoo.vespa.orchestrator.config.OrchestratorConfig;
import com.yahoo.vespa.orchestrator.controller.ClusterControllerClientFactoryMock;
import com.yahoo.vespa.orchestrator.status.ZkStatusService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
import java.util.Set;

import static com.yahoo.jdisc.http.HttpRequest.Method;
import static org.junit.jupiter.api.Assertions.assertEquals;

/**
 * Tests the implementation of the orchestrator Application API.
 *
 * @author smorgrav
 * @author bjorncs
 */
class ApplicationSuspensionRequestHandlerTest {

    private static final String RESOURCE_1 = "mediasearch:imagesearch:default";
    private static final String RESOURCE_2 = "test-tenant-id:application:instance";
    private static final String INVALID_RESOURCE_NAME = "something_without_colons";

    private RestApiTestDriver testDriver;

    @BeforeEach
    void createHandler() {
        DummyServiceMonitor serviceMonitor = new DummyServiceMonitor();
        Orchestrator orchestrator = new OrchestratorImpl(
                new OrchestratorConfig(new OrchestratorConfig.Builder()), new ConfigserverConfig(new ConfigserverConfig.Builder()), new ClusterControllerClientFactoryMock(),
                new ZkStatusService(new MockCurator(), new MockMetric(), new SystemTimer(), serviceMonitor),
                serviceMonitor,
                new InMemoryFlagSource(),
                Zone.defaultZone());
        var handler = new ApplicationSuspensionRequestHandler(RestApiTestDriver.createHandlerTestContext(), orchestrator);
        testDriver = RestApiTestDriver.newBuilder(handler).build();
    }


    @Test
    void get_all_suspended_applications_return_empty_list_initially() {
        HttpResponse httpResponse = executeRequest(Method.GET, "", null);
        assertEquals(200, httpResponse.getStatus());
        Set<String> set = parseResponseContent(httpResponse, new TypeReference<>() {});
        assertEquals(0, set.size());
    }

    @Test
    void invalid_application_id_throws_http_400() {
        HttpResponse httpResponse = executeRequest(Method.POST, "", INVALID_RESOURCE_NAME);
        assertEquals(400, httpResponse.getStatus());
    }

    @Test
    void get_application_status_returns_404_for_not_suspended_and_204_for_suspended() {
        // Get on application that is not suspended
        HttpResponse httpResponse = executeRequest(Method.GET, "/"+RESOURCE_1, null);
        assertEquals(404, httpResponse.getStatus());

        // Post application
        httpResponse = executeRequest(Method.POST, "", RESOURCE_1);
        assertEquals(204, httpResponse.getStatus());

        // Get on the application that now should be in suspended
        httpResponse = executeRequest(Method.GET, "/"+RESOURCE_1, null);
        assertEquals(204, httpResponse.getStatus());
    }

    @Test
    void delete_works_on_suspended_and_not_suspended_applications() {
        // Delete an application that is not suspended
        HttpResponse httpResponse = executeRequest(Method.DELETE, "/"+RESOURCE_1, null);
        assertEquals(204, httpResponse.getStatus());

        // Put application in suspend
        httpResponse = executeRequest(Method.POST, "", RESOURCE_1);
        assertEquals(204, httpResponse.getStatus());

        // Check that it is in suspend
        httpResponse = executeRequest(Method.GET, "/"+RESOURCE_1, null);
        assertEquals(204, httpResponse.getStatus());

        // Delete it
        httpResponse = executeRequest(Method.DELETE, "/"+RESOURCE_1, null);
        assertEquals(204, httpResponse.getStatus());

        // Check that it is not in suspend anymore
        httpResponse = executeRequest(Method.GET, "/"+RESOURCE_1, null);
        assertEquals(404, httpResponse.getStatus());
    }

    @Test
    void list_applications_returns_the_correct_list_of_suspended_applications() {
        // Test that initially we have the empty set
        HttpResponse httpResponse = executeRequest(Method.GET, "", null);
        assertEquals(200, httpResponse.getStatus());
        Set<String> set = parseResponseContent(httpResponse, new TypeReference<>() {});
        assertEquals(0, set.size());

        // Add a couple of applications to maintenance
        executeRequest(Method.POST, "", RESOURCE_1);
        executeRequest(Method.POST, "", RESOURCE_2);

        // Test that we get them back
        httpResponse = executeRequest(Method.GET, "", null);
        assertEquals(200, httpResponse.getStatus());
        set = parseResponseContent(httpResponse, new TypeReference<>() {});
        assertEquals(2, set.size());

        // Remove suspend for the first resource
        executeRequest(Method.DELETE, "/"+RESOURCE_1, null);

        // Test that we are back to the start with the empty set
        httpResponse = executeRequest(Method.GET, "", null);
        assertEquals(200, httpResponse.getStatus());
        set = parseResponseContent(httpResponse, new TypeReference<>() {});
        assertEquals(1, set.size());
        assertEquals(RESOURCE_2, set.iterator().next());
    }

    private HttpResponse executeRequest(Method method, String relativePath, String applicationId) {
        String fullPath = "/orchestrator/v1/suspensions/applications" + relativePath;
        var builder = HttpRequestBuilder.create(method, fullPath);
        if (applicationId != null) {
            builder.withRequestContent(new ByteArrayInputStream(applicationId.getBytes(StandardCharsets.UTF_8)));
        }
        return testDriver.executeRequest(builder.build());
    }

    private <T> T parseResponseContent(HttpResponse response, TypeReference<T> type) {
        assertEquals(200, response.getStatus());
        return testDriver.parseJacksonResponseContent(response, type);
    }
}