diff options
author | Bjørn Christian Seime <bjorncs@verizonmedia.com> | 2021-04-12 20:49:31 +0200 |
---|---|---|
committer | Bjørn Christian Seime <bjorncs@verizonmedia.com> | 2021-04-12 20:57:24 +0200 |
commit | 97e6a063049a93d7a78301f29c27148f72dcef67 (patch) | |
tree | b515dcb09a5b6e1380e16ca4aaafe373f45a9d18 /orchestrator/src/test | |
parent | 8cf392f9573d4061bef71e426c4940426dd121a7 (diff) |
Convert remaining JAX-RS resources to request handlers
Diffstat (limited to 'orchestrator/src/test')
-rw-r--r-- | orchestrator/src/test/java/com/yahoo/vespa/orchestrator/resources/ApplicationSuspensionRequestHandlerTest.java | 157 | ||||
-rw-r--r-- | orchestrator/src/test/java/com/yahoo/vespa/orchestrator/resources/HostRequestHandlerTest.java (renamed from orchestrator/src/test/java/com/yahoo/vespa/orchestrator/resources/host/HostResourceTest.java) | 185 | ||||
-rw-r--r-- | orchestrator/src/test/java/com/yahoo/vespa/orchestrator/resources/HostSuspensionHandlerTest.java | 9 | ||||
-rw-r--r-- | orchestrator/src/test/java/com/yahoo/vespa/orchestrator/resources/InstanceRequestHandlerTest.java | 132 | ||||
-rw-r--r-- | orchestrator/src/test/java/com/yahoo/vespa/orchestrator/resources/appsuspension/ApplicationSuspensionResourceTest.java | 168 | ||||
-rw-r--r-- | orchestrator/src/test/java/com/yahoo/vespa/orchestrator/resources/instance/InstanceResourceTest.java | 92 |
6 files changed, 392 insertions, 351 deletions
diff --git a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/resources/ApplicationSuspensionRequestHandlerTest.java b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/resources/ApplicationSuspensionRequestHandlerTest.java new file mode 100644 index 00000000000..176a95d1c04 --- /dev/null +++ b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/resources/ApplicationSuspensionRequestHandlerTest.java @@ -0,0 +1,157 @@ +package com.yahoo.vespa.orchestrator.resources;// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +import com.fasterxml.jackson.core.type.TypeReference; +import com.yahoo.cloud.config.ConfigserverConfig; +import com.yahoo.container.jdisc.HttpResponse; +import com.yahoo.container.jdisc.LoggingRequestHandler; +import com.yahoo.jdisc.core.SystemTimer; +import com.yahoo.jdisc.test.MockMetric; +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.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Set; +import java.util.concurrent.Executors; + +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"; + + ApplicationSuspensionRequestHandler handler; + + @BeforeEach + void createHandler() { + DummyServiceMonitor serviceMonitor = new DummyServiceMonitor(); + Orchestrator orchestrator = new OrchestratorImpl( + new ClusterControllerClientFactoryMock(), + new ZkStatusService(new MockCurator(), new MockMetric(), new SystemTimer(), serviceMonitor), + new OrchestratorConfig(new OrchestratorConfig.Builder()), + serviceMonitor, + new ConfigserverConfig(new ConfigserverConfig.Builder()), + new InMemoryFlagSource()); + var handlerContext = new LoggingRequestHandler.Context(Executors.newSingleThreadExecutor(), new MockMetric()); + this.handler = new ApplicationSuspensionRequestHandler(handlerContext, orchestrator); + } + + + @Test + void get_all_suspended_applications_return_empty_list_initially() throws IOException { + 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() throws IOException { + 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() throws IOException { + // 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() throws IOException { + // 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() throws IOException { + // 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 path, String applicationId) throws IOException { + String uri = "http://localhost/orchestrator/v1/suspensions/applications" + path; + com.yahoo.container.jdisc.HttpRequest request; + if (applicationId != null) { + ByteArrayInputStream requestData = new ByteArrayInputStream(applicationId.getBytes(StandardCharsets.UTF_8)); + request = com.yahoo.container.jdisc.HttpRequest.createTestRequest(uri, method, requestData); + } else { + request = com.yahoo.container.jdisc.HttpRequest.createTestRequest(uri, method); + } + return handler.handle(request); + } + + private <T> T parseResponseContent(HttpResponse response, TypeReference<T> responseEntityType) throws IOException { + assertEquals(200, response.getStatus()); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + response.render(out); + return handler.restApi().jacksonJsonMapper().readValue(out.toByteArray(), responseEntityType); + } +}
\ No newline at end of file diff --git a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/resources/host/HostResourceTest.java b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/resources/HostRequestHandlerTest.java index d056c3730fd..c34775c1910 100644 --- a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/resources/host/HostResourceTest.java +++ b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/resources/HostRequestHandlerTest.java @@ -1,9 +1,15 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.orchestrator.resources.host; +// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.orchestrator.resources; import com.google.common.util.concurrent.UncheckedTimeoutException; +import com.yahoo.container.jdisc.HttpRequest; +import com.yahoo.container.jdisc.HttpResponse; +import com.yahoo.container.jdisc.LoggingRequestHandler; import com.yahoo.jdisc.Metric; +import com.yahoo.jdisc.http.HttpRequest.Method; +import com.yahoo.jdisc.test.MockMetric; import com.yahoo.jdisc.test.TestTimer; +import com.yahoo.test.json.JsonTestHelper; import com.yahoo.vespa.applicationmodel.ApplicationInstance; import com.yahoo.vespa.applicationmodel.ApplicationInstanceId; import com.yahoo.vespa.applicationmodel.ApplicationInstanceReference; @@ -41,24 +47,21 @@ import com.yahoo.vespa.orchestrator.status.StatusService; import com.yahoo.vespa.orchestrator.status.ZkStatusService; import com.yahoo.vespa.service.monitor.ServiceModel; import com.yahoo.vespa.service.monitor.ServiceMonitor; -import org.junit.Before; -import org.junit.Test; - -import javax.ws.rs.BadRequestException; -import javax.ws.rs.InternalServerErrorException; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.UriBuilder; -import javax.ws.rs.core.UriInfo; -import java.net.URI; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.time.Clock; import java.time.Instant; import java.util.Collections; import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.concurrent.Executors; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; @@ -68,8 +71,10 @@ import static org.mockito.Mockito.when; /** * @author hakonhall + * @author bjorncs */ -public class HostResourceTest { +class HostRequestHandlerTest { + private static final Clock clock = mock(Clock.class); private static final int SERVICE_MONITOR_CONVERGENCE_LATENCY_SECONDS = 0; private static final TenantId TENANT_ID = new TenantId("tenantId"); @@ -121,14 +126,14 @@ public class HostResourceTest { private final OrchestratorImpl alwaysAllowOrchestrator = createAlwaysAllowOrchestrator(clock); private final OrchestratorImpl hostNotFoundOrchestrator = createHostNotFoundOrchestrator(clock); - private final UriInfo uriInfo = mock(UriInfo.class); + private final OrchestratorImpl alwaysRejectOrchestrator = createAlwaysRejectResolver(clock); - @Before - public void setUp() { + @BeforeEach + void setUp() { when(clock.instant()).thenReturn(Instant.now()); } - public static OrchestratorImpl createAlwaysAllowOrchestrator(Clock clock) { + static OrchestratorImpl createAlwaysAllowOrchestrator(Clock clock) { return new OrchestratorImpl( new AlwaysAllowPolicy(), new ClusterControllerClientFactoryMock(), @@ -140,7 +145,7 @@ public class HostResourceTest { new InMemoryFlagSource()); } - public static OrchestratorImpl createHostNotFoundOrchestrator(Clock clock) { + static OrchestratorImpl createHostNotFoundOrchestrator(Clock clock) { return new OrchestratorImpl( new AlwaysAllowPolicy(), new ClusterControllerClientFactoryMock(), @@ -152,9 +157,9 @@ public class HostResourceTest { new InMemoryFlagSource()); } - public static OrchestratorImpl createAlwaysRejectResolver(Clock clock) { + static OrchestratorImpl createAlwaysRejectResolver(Clock clock) { return new OrchestratorImpl( - new HostResourceTest.AlwaysFailPolicy(), + new AlwaysFailPolicy(), new ClusterControllerClientFactoryMock(), EVERY_HOST_IS_UP_HOST_STATUS_SERVICE, serviceMonitor, @@ -165,27 +170,20 @@ public class HostResourceTest { } @Test - public void returns_200_on_success() { - HostResource hostResource = - new HostResource(alwaysAllowOrchestrator, uriInfo); - - final String hostName = "hostname"; + void returns_200_on_success() throws IOException { + HostRequestHandler handler = createHandler(alwaysAllowOrchestrator); - UpdateHostResponse response = hostResource.suspend(hostName); - - assertEquals(hostName, response.hostname()); + HttpResponse response = executeRequest(handler, Method.PUT, "/orchestrator/v1/hosts/hostname/suspended", null); + UpdateHostResponse updateHostResponse = parseResponseContent(handler, response, UpdateHostResponse.class); + assertEquals("hostname", updateHostResponse.hostname()); } @Test - public void throws_404_when_host_unknown() { - try { - HostResource hostResource = - new HostResource(hostNotFoundOrchestrator, uriInfo); - hostResource.suspend("hostname"); - fail(); - } catch (WebApplicationException w) { - assertEquals(404, w.getResponse().getStatus()); - } + void throws_404_when_host_unknown() throws IOException { + HostRequestHandler handler = createHandler(hostNotFoundOrchestrator); + + HttpResponse response = executeRequest(handler, Method.PUT, "/orchestrator/v1/hosts/hostname/suspended", null); + assertEquals(404, response.getStatus()); } private static class AlwaysFailPolicy implements Policy { @@ -221,79 +219,61 @@ public class HostResourceTest { } @Test - public void throws_409_when_request_rejected_by_policies() { - final OrchestratorImpl alwaysRejectResolver = new OrchestratorImpl( - new AlwaysFailPolicy(), - new ClusterControllerClientFactoryMock(), - EVERY_HOST_IS_UP_HOST_STATUS_SERVICE, - serviceMonitor, - SERVICE_MONITOR_CONVERGENCE_LATENCY_SECONDS, - clock, - applicationApiFactory, - new InMemoryFlagSource()); + void throws_409_when_request_rejected_by_policies() throws IOException { + HostRequestHandler handler = createHandler(alwaysRejectOrchestrator); - try { - HostResource hostResource = new HostResource(alwaysRejectResolver, uriInfo); - hostResource.suspend("hostname"); - fail(); - } catch (WebApplicationException w) { - assertEquals(409, w.getResponse().getStatus()); - } + HttpResponse response = executeRequest(handler, Method.PUT, "/orchestrator/v1/hosts/hostname/suspended", null); + assertEquals(409, response.getStatus()); } - @Test(expected = BadRequestException.class) - public void patch_state_may_throw_bad_request() { + @Test + void patch_state_may_throw_bad_request() throws IOException { Orchestrator orchestrator = mock(Orchestrator.class); - HostResource hostResource = new HostResource(orchestrator, uriInfo); + HostRequestHandler handler = createHandler(orchestrator); - String hostNameString = "hostname"; PatchHostRequest request = new PatchHostRequest(); request.state = "bad state"; - hostResource.patch(hostNameString, request); + HttpResponse response = executeRequest(handler, Method.PATCH, "/orchestrator/v1/hosts/hostname", request); + assertEquals(400, response.getStatus()); } @Test - public void patch_works() throws OrchestrationException { + void patch_works() throws OrchestrationException, IOException { Orchestrator orchestrator = mock(Orchestrator.class); - HostResource hostResource = new HostResource(orchestrator, uriInfo); + HostRequestHandler handler = createHandler(orchestrator); String hostNameString = "hostname"; PatchHostRequest request = new PatchHostRequest(); request.state = "NO_REMARKS"; - PatchHostResponse response = hostResource.patch(hostNameString, request); + HttpResponse httpResponse = executeRequest(handler, Method.PATCH, "/orchestrator/v1/hosts/hostname", request); + PatchHostResponse response = parseResponseContent(handler, httpResponse, PatchHostResponse.class); assertEquals(response.description, "ok"); verify(orchestrator, times(1)).setNodeStatus(new HostName(hostNameString), HostStatus.NO_REMARKS); } - @Test(expected = InternalServerErrorException.class) - public void patch_handles_exception_in_orchestrator() throws OrchestrationException { + @Test + void patch_handles_exception_in_orchestrator() throws OrchestrationException, IOException { Orchestrator orchestrator = mock(Orchestrator.class); - HostResource hostResource = new HostResource(orchestrator, uriInfo); + HostRequestHandler handler = createHandler(orchestrator); String hostNameString = "hostname"; PatchHostRequest request = new PatchHostRequest(); request.state = "NO_REMARKS"; doThrow(new OrchestrationException("error")).when(orchestrator).setNodeStatus(new HostName(hostNameString), HostStatus.NO_REMARKS); - hostResource.patch(hostNameString, request); + HttpResponse httpResponse = executeRequest(handler, Method.PATCH, "/orchestrator/v1/hosts/hostname", request); + assertEquals(500, httpResponse.getStatus()); } @Test - public void getHost_works() throws Exception { + void getHost_works() throws Exception { Orchestrator orchestrator = mock(Orchestrator.class); - HostResource hostResource = new HostResource(orchestrator, uriInfo); + HostRequestHandler handler = createHandler(orchestrator); HostName hostName = new HostName("hostname"); - UriBuilder baseUriBuilder = mock(UriBuilder.class); - when(uriInfo.getBaseUriBuilder()).thenReturn(baseUriBuilder); - when(baseUriBuilder.path(any(String.class))).thenReturn(baseUriBuilder); - when(baseUriBuilder.path(any(Class.class))).thenReturn(baseUriBuilder); - URI uri = new URI("https://foo.com/bar"); - when(baseUriBuilder.build()).thenReturn(uri); - ServiceInstance serviceInstance = new ServiceInstance( new ConfigId("configId"), hostName, @@ -312,8 +292,11 @@ public class HostResourceTest { new ApplicationInstanceId("applicationId")), Collections.singletonList(serviceInstance)); when(orchestrator.getHost(hostName)).thenReturn(host); - GetHostResponse response = hostResource.getHost(hostName.s()); - assertEquals("https://foo.com/bar", response.applicationUrl()); + + HttpResponse httpResponse = executeRequest(handler, Method.GET, "/orchestrator/v1/hosts/hostname", null); + GetHostResponse response = parseResponseContent(handler, httpResponse, GetHostResponse.class); + + assertEquals("http://localhost/orchestrator/v1/instances/tenantId:applicationId", response.applicationUrl()); assertEquals("hostname", response.hostname()); assertEquals("ALLOWED_TO_BE_DOWN", response.state()); assertEquals("1970-01-01T00:00:00Z", response.suspendedSince()); @@ -325,18 +308,48 @@ public class HostResourceTest { } @Test - public void throws_409_on_timeout() throws HostNameNotFoundException, HostStateChangeDeniedException { + void throws_409_on_timeout() throws HostNameNotFoundException, HostStateChangeDeniedException, IOException { Orchestrator orchestrator = mock(Orchestrator.class); doThrow(new UncheckedTimeoutException("Timeout Message")).when(orchestrator).resume(any(HostName.class)); - try { - HostResource hostResource = new HostResource(orchestrator, uriInfo); - hostResource.resume("hostname"); - fail(); - } catch (WebApplicationException w) { - assertEquals(409, w.getResponse().getStatus()); - assertEquals("resume failed: Timeout Message [deadline]", w.getMessage()); + HostRequestHandler handler = createHandler(orchestrator); + HttpResponse httpResponse = executeRequest(handler, Method.DELETE, "/orchestrator/v1/hosts/hostname/suspended", null); + assertEquals(409, httpResponse.getStatus()); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + httpResponse.render(out); + JsonTestHelper.assertJsonEquals("{\n" + + " \"hostname\" : \"hostname\",\n" + + " \"reason\" : {\n" + + " \"constraint\" : \"deadline\",\n" + + " \"message\" : \"resume failed: Timeout Message\"\n" + + " }\n" + + "}", + out.toString()); + } + + private HostRequestHandler createHandler(Orchestrator orchestrator) { + var handlerContext = new LoggingRequestHandler.Context(Executors.newSingleThreadExecutor(), new MockMetric()); + return new HostRequestHandler(handlerContext, orchestrator); + } + + private HttpResponse executeRequest(HostRequestHandler handler, Method method, String path, Object requestEntity) throws IOException { + String uri = "http://localhost" + path; + HttpRequest request; + if (requestEntity != null) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + handler.restApi().jacksonJsonMapper().writeValue(out, requestEntity); + request = HttpRequest.createTestRequest(uri, method, new ByteArrayInputStream(out.toByteArray())); + } else { + request = HttpRequest.createTestRequest(uri, method); } + return handler.handle(request); + } + + private <T> T parseResponseContent(HostRequestHandler handler, HttpResponse response, Class<T> responseEntityType) throws IOException { + assertEquals(200, response.getStatus()); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + response.render(out); + return handler.restApi().jacksonJsonMapper().readValue(out.toByteArray(), responseEntityType); } } diff --git a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/resources/HostSuspensionHandlerTest.java b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/resources/HostSuspensionHandlerTest.java index 9d413526037..be3cb047967 100644 --- a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/resources/HostSuspensionHandlerTest.java +++ b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/resources/HostSuspensionHandlerTest.java @@ -12,7 +12,6 @@ import com.yahoo.vespa.orchestrator.BatchInternalErrorException; import com.yahoo.vespa.orchestrator.Orchestrator; import com.yahoo.vespa.orchestrator.OrchestratorImpl; import com.yahoo.vespa.orchestrator.policy.BatchHostStateChangeDeniedException; -import com.yahoo.vespa.orchestrator.resources.host.HostResourceTest; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -46,14 +45,14 @@ class HostSuspensionHandlerTest { @Test void returns_200_on_success_batch() throws IOException { - HostSuspensionHandler handler = createHandler(HostResourceTest.createAlwaysAllowOrchestrator(clock)); + HostSuspensionHandler handler = createHandler(HostRequestHandlerTest.createAlwaysAllowOrchestrator(clock)); HttpResponse response = executeSuspendAllRequest(handler, "parentHostname", List.of("hostname1", "hostname2")); assertSuccess(response); } @Test void returns_200_empty_batch() throws IOException { - HostSuspensionHandler handler = createHandler(HostResourceTest.createAlwaysAllowOrchestrator(clock)); + HostSuspensionHandler handler = createHandler(HostRequestHandlerTest.createAlwaysAllowOrchestrator(clock)); HttpResponse response = executeSuspendAllRequest(handler, "parentHostname", List.of()); assertSuccess(response); } @@ -63,14 +62,14 @@ class HostSuspensionHandlerTest { // hostnames are part of the request body for multi-host. @Test void returns_400_when_host_unknown_for_batch() { - HostSuspensionHandler handler = createHandler(HostResourceTest.createHostNotFoundOrchestrator(clock)); + HostSuspensionHandler handler = createHandler(HostRequestHandlerTest.createHostNotFoundOrchestrator(clock)); HttpResponse response = executeSuspendAllRequest(handler, "parentHostname", List.of("hostname1", "hostname2")); assertEquals(400, response.getStatus()); } @Test void returns_409_when_request_rejected_by_policies_for_batch() { - OrchestratorImpl alwaysRejectResolver = HostResourceTest.createAlwaysRejectResolver(clock); + OrchestratorImpl alwaysRejectResolver = HostRequestHandlerTest.createAlwaysRejectResolver(clock); HostSuspensionHandler handler = createHandler(alwaysRejectResolver); HttpResponse response = executeSuspendAllRequest(handler, "parentHostname", List.of("hostname1", "hostname2")); assertEquals(409, response.getStatus()); diff --git a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/resources/InstanceRequestHandlerTest.java b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/resources/InstanceRequestHandlerTest.java new file mode 100644 index 00000000000..bee19a6d6f5 --- /dev/null +++ b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/resources/InstanceRequestHandlerTest.java @@ -0,0 +1,132 @@ +// Copyright Verizon Media. 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.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.yahoo.config.provision.ApplicationId; +import com.yahoo.container.jdisc.HttpRequest; +import com.yahoo.container.jdisc.HttpResponse; +import com.yahoo.container.jdisc.LoggingRequestHandler; +import com.yahoo.jdisc.test.MockMetric; +import com.yahoo.jrt.slobrok.api.Mirror; +import com.yahoo.vespa.applicationmodel.ClusterId; +import com.yahoo.vespa.applicationmodel.ConfigId; +import com.yahoo.vespa.applicationmodel.ServiceStatus; +import com.yahoo.vespa.applicationmodel.ServiceStatusInfo; +import com.yahoo.vespa.applicationmodel.ServiceType; +import com.yahoo.vespa.orchestrator.restapi.wire.SlobrokEntryResponse; +import com.yahoo.vespa.service.manager.UnionMonitorManager; +import com.yahoo.vespa.service.monitor.SlobrokApi; +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.Executors; + +import static com.yahoo.jdisc.http.HttpRequest.Method.GET; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * @author bjorncs + */ +class InstanceRequestHandlerTest { + + private static final String APPLICATION_INSTANCE_REFERENCE = "tenant:app:prod:us-west-1:instance"; + private static final ApplicationId APPLICATION_ID = ApplicationId.from( + "tenant", "app", "instance"); + private static final List<Mirror.Entry> ENTRIES = Arrays.asList( + new Mirror.Entry("name1", "tcp/spec:1"), + new Mirror.Entry("name2", "tcp/spec:2")); + private static final ClusterId CLUSTER_ID = new ClusterId("cluster-id"); + private static final ObjectMapper jsonMapper = new ObjectMapper() + .registerModule(new JavaTimeModule()) + .registerModule(new Jdk8Module()); + + private final SlobrokApi slobrokApi = mock(SlobrokApi.class); + private final UnionMonitorManager rootManager = mock(UnionMonitorManager.class); + private final InstanceRequestHandler handler = new InstanceRequestHandler( + new LoggingRequestHandler.Context(Executors.newSingleThreadExecutor(), new MockMetric()), + null, + null, + slobrokApi, + rootManager); + + + @Test + void testGetSlobrokEntries() throws Exception { + testGetSlobrokEntriesWith("foo", "foo"); + } + + @Test + void testGetSlobrokEntriesWithoutPattern() throws Exception { + testGetSlobrokEntriesWith(null, InstanceRequestHandler.DEFAULT_SLOBROK_PATTERN); + } + + @Test + void testGetServiceStatusInfo() throws IOException { + ServiceType serviceType = new ServiceType("serviceType"); + ConfigId configId = new ConfigId("configId"); + ServiceStatus serviceStatus = ServiceStatus.UP; + when(rootManager.getStatus(APPLICATION_ID, CLUSTER_ID, serviceType, configId)) + .thenReturn(new ServiceStatusInfo(serviceStatus)); + + + String uriPath = String.format( + "/orchestrator/v1/instances/%s/serviceStatusInfo?clusterId=%s&serviceType=%s&configId=%s", + APPLICATION_INSTANCE_REFERENCE, + CLUSTER_ID.s(), + serviceType.s(), + configId.s()); + ServiceStatusInfo serviceStatusInfo = executeRequest(uriPath, new TypeReference<>(){}); + + ServiceStatus actualServiceStatus = serviceStatusInfo.serviceStatus(); + verify(rootManager).getStatus(APPLICATION_ID, CLUSTER_ID, serviceType, configId); + assertEquals(serviceStatus, actualServiceStatus); + } + + @Test + void testBadRequest() { + String uriPath = String.format( + "/orchestrator/v1/instances/%s/serviceStatusInfo?clusterId=%s", + APPLICATION_INSTANCE_REFERENCE, + CLUSTER_ID.s()); + HttpRequest request = HttpRequest.createTestRequest("http://localhost" + uriPath, GET); + HttpResponse response = handler.handle(request); + assertEquals(400, response.getStatus()); + } + + private void testGetSlobrokEntriesWith(String pattern, String expectedLookupPattern) + throws Exception{ + when(slobrokApi.lookup(APPLICATION_ID, expectedLookupPattern)) + .thenReturn(ENTRIES); + + String uriPath = String.format("/orchestrator/v1/instances/%s/slobrok", APPLICATION_INSTANCE_REFERENCE); + if (pattern != null) { + uriPath += "?pattern=" + pattern; + } + List<SlobrokEntryResponse> response = executeRequest(uriPath, new TypeReference<>() {}); + + verify(slobrokApi).lookup(APPLICATION_ID, expectedLookupPattern); + + String actualJson = jsonMapper.writeValueAsString(response); + assertEquals( + "[{\"name\":\"name1\",\"spec\":\"tcp/spec:1\"},{\"name\":\"name2\",\"spec\":\"tcp/spec:2\"}]", + actualJson); + } + + private <T> T executeRequest(String path, TypeReference<T> responseEntityType) throws IOException { + HttpRequest request = HttpRequest.createTestRequest("http://localhost" + path, GET); + HttpResponse response = handler.handle(request); + assertEquals(200, response.getStatus()); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + response.render(out); + return jsonMapper.readValue(out.toByteArray(), responseEntityType); + } +} diff --git a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/resources/appsuspension/ApplicationSuspensionResourceTest.java b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/resources/appsuspension/ApplicationSuspensionResourceTest.java deleted file mode 100644 index a7514de5acd..00000000000 --- a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/resources/appsuspension/ApplicationSuspensionResourceTest.java +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.orchestrator.resources.appsuspension; - -import com.yahoo.application.Application; -import com.yahoo.application.Networking; -import com.yahoo.container.Container; -import com.yahoo.jdisc.http.server.jetty.JettyHttpServer; -import org.junit.After; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; - -import javax.ws.rs.client.Client; -import javax.ws.rs.client.ClientBuilder; -import javax.ws.rs.client.Entity; -import javax.ws.rs.client.WebTarget; -import javax.ws.rs.core.GenericType; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import java.net.URI; -import java.util.Set; - -import static org.junit.Assert.assertEquals; - -/** - * Tests the implementation of the orchestrator Application API. - * - * @author smorgrav - */ -public class ApplicationSuspensionResourceTest { - - private static final String BASE_PATH = "/orchestrator/v1/suspensions/applications"; - 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 Application jdiscApplication; - private WebTarget webTarget; - - @Before - public void setup() throws Exception { - jdiscApplication = Application.fromServicesXml(servicesXml(), Networking.enable); - Client client = ClientBuilder.newClient(); - - JettyHttpServer serverProvider = (JettyHttpServer) Container.get().getServerProviderRegistry().allComponents().get(0); - String url = "http://localhost:" + serverProvider.getListenPort() + BASE_PATH; - webTarget = client.target(new URI(url)); - } - - @After - public void teardown() { - jdiscApplication.close(); - webTarget = null; - } - - @Ignore - @Test - public void run_application_locally_for_manual_browser_testing() throws Exception { - System.out.println(webTarget.getUri()); - Thread.sleep(3600 * 1000); - } - - @Test - public void get_all_suspended_applications_return_empty_list_initially() { - Response reply = webTarget.request().get(); - assertEquals(200, reply.getStatus()); - assertEquals("[]", reply.readEntity(String.class)); - } - - @Test - public void invalid_application_id_throws_http_400() { - Response reply = webTarget.request().post(Entity.entity(INVALID_RESOURCE_NAME, MediaType.APPLICATION_JSON_TYPE)); - assertEquals(400, reply.getStatus()); - } - - @Test - public void get_application_status_returns_404_for_not_suspended_and_204_for_suspended() { - // Get on application that is not suspended - Response reply = webTarget.path(RESOURCE_1).request().get(); - assertEquals(404, reply.getStatus()); - - // Post application - reply = webTarget.request().post(Entity.entity(RESOURCE_1, MediaType.APPLICATION_JSON_TYPE)); - assertEquals(204, reply.getStatus()); - - // Get on the application that now should be in suspended - reply = webTarget.path(RESOURCE_1).request().get(); - assertEquals(204, reply.getStatus()); - } - - @Test - public void delete_works_on_suspended_and_not_suspended_applications() { - // Delete an application that is not suspended - Response reply = webTarget.path(RESOURCE_1).request().delete(); - assertEquals(204, reply.getStatus()); - - // Put application in suspend - reply = webTarget.request().post(Entity.entity(RESOURCE_1, MediaType.APPLICATION_JSON_TYPE)); - assertEquals(204, reply.getStatus()); - - // Check that it is in suspend - reply = webTarget.path(RESOURCE_1).request(MediaType.APPLICATION_JSON).get(); - assertEquals(204, reply.getStatus()); - - // Delete it - reply = webTarget.path(RESOURCE_1).request().delete(); - assertEquals(204, reply.getStatus()); - - // Check that it is not in suspend anymore - reply = webTarget.path(RESOURCE_1).request(MediaType.APPLICATION_JSON).get(); - assertEquals(404, reply.getStatus()); - } - - @Test - public void list_applications_returns_the_correct_list_of_suspended_applications() { - // Test that initially we have the empty set - Response reply = webTarget.request(MediaType.APPLICATION_JSON).get(); - assertEquals(200, reply.getStatus()); - assertEquals("[]", reply.readEntity(String.class)); - - // Add a couple of applications to maintenance - webTarget.request().post(Entity.entity(RESOURCE_1, MediaType.APPLICATION_JSON_TYPE)); - webTarget.request().post(Entity.entity(RESOURCE_2, MediaType.APPLICATION_JSON_TYPE)); - assertEquals(200, reply.getStatus()); - - // Test that we get them back - Set<String> responses = webTarget.request(MediaType.APPLICATION_JSON_TYPE) - .get(new GenericType<Set<String>>() {}); - assertEquals(2, responses.size()); - - // Remove suspend for the first resource - webTarget.path(RESOURCE_1).request().delete(); - - // Test that we are back to the start with the empty set - responses = webTarget.request(MediaType.APPLICATION_JSON_TYPE) - .get(new GenericType<Set<String>>() {}); - assertEquals(1, responses.size()); - assertEquals(RESOURCE_2, responses.iterator().next()); - } - - private String servicesXml() { - return "<services>\n" + - " <container version=\"1.0\" jetty=\"true\">\n" + - " <accesslog type=\"disabled\"/>\n" + - " <config name=\"container.handler.threadpool\">\n" + - " <maxthreads>10</maxthreads>\n" + - " </config>\n" + - " <component id=\"com.yahoo.vespa.flags.InMemoryFlagSource\" bundle=\"flags\" />\n" + - " <component id=\"com.yahoo.vespa.curator.mock.MockCurator\" bundle=\"zkfacade\" />\n" + - " <component id=\"com.yahoo.vespa.orchestrator.status.ZkStatusService\" bundle=\"orchestrator\" />\n" + - " <component id=\"com.yahoo.vespa.orchestrator.DummyServiceMonitor\" bundle=\"orchestrator\" />\n" + - " <component id=\"com.yahoo.vespa.orchestrator.OrchestratorImpl\" bundle=\"orchestrator\" />\n" + - " <component id=\"com.yahoo.vespa.orchestrator.controller.ClusterControllerClientFactoryMock\" bundle=\"orchestrator\" />\n" + - "\n" + - " <rest-api path=\"orchestrator/v1/suspensions/applications\" jersey2=\"true\">\n" + - " <components bundle=\"orchestrator\">\n" + - " <package>com.yahoo.vespa.orchestrator.resources.appsuspension</package>\n" + - " </components>\n" + - " </rest-api>\n" + - "\n" + - " <http>\n" + - " <server id=\"foo\" port=\"0\"/>\n" + - " </http>\n" + - " </container>\n" + - "</services>\n"; - } - -} diff --git a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/resources/instance/InstanceResourceTest.java b/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/resources/instance/InstanceResourceTest.java deleted file mode 100644 index 8e2eeb7410d..00000000000 --- a/orchestrator/src/test/java/com/yahoo/vespa/orchestrator/resources/instance/InstanceResourceTest.java +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.orchestrator.resources.instance; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.yahoo.config.provision.ApplicationId; -import com.yahoo.jrt.slobrok.api.Mirror; -import com.yahoo.vespa.applicationmodel.ClusterId; -import com.yahoo.vespa.applicationmodel.ConfigId; -import com.yahoo.vespa.applicationmodel.ServiceStatus; -import com.yahoo.vespa.applicationmodel.ServiceStatusInfo; -import com.yahoo.vespa.applicationmodel.ServiceType; -import com.yahoo.vespa.orchestrator.resources.instance.InstanceResource; -import com.yahoo.vespa.orchestrator.restapi.wire.SlobrokEntryResponse; -import com.yahoo.vespa.service.manager.UnionMonitorManager; -import com.yahoo.vespa.service.monitor.SlobrokApi; -import org.junit.Test; - -import javax.ws.rs.WebApplicationException; -import java.util.Arrays; -import java.util.List; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -public class InstanceResourceTest { - private static final String APPLICATION_INSTANCE_REFERENCE = "tenant:app:prod:us-west-1:instance"; - private static final ApplicationId APPLICATION_ID = ApplicationId.from( - "tenant", "app", "instance"); - private static final List<Mirror.Entry> ENTRIES = Arrays.asList( - new Mirror.Entry("name1", "tcp/spec:1"), - new Mirror.Entry("name2", "tcp/spec:2")); - private static final ClusterId CLUSTER_ID = new ClusterId("cluster-id"); - - private final SlobrokApi slobrokApi = mock(SlobrokApi.class); - private final UnionMonitorManager rootManager = mock(UnionMonitorManager.class); - private final InstanceResource resource = new InstanceResource( - null, - null, - slobrokApi, - rootManager); - - @Test - public void testGetSlobrokEntries() throws Exception { - testGetSlobrokEntriesWith("foo", "foo"); - } - - @Test - public void testGetSlobrokEntriesWithoutPattern() throws Exception { - testGetSlobrokEntriesWith(null, InstanceResource.DEFAULT_SLOBROK_PATTERN); - } - - @Test - public void testGetServiceStatusInfo() { - ServiceType serviceType = new ServiceType("serviceType"); - ConfigId configId = new ConfigId("configId"); - ServiceStatus serviceStatus = ServiceStatus.UP; - when(rootManager.getStatus(APPLICATION_ID, CLUSTER_ID, serviceType, configId)) - .thenReturn(new ServiceStatusInfo(serviceStatus)); - ServiceStatus actualServiceStatus = resource.getServiceStatus( - APPLICATION_INSTANCE_REFERENCE, - CLUSTER_ID.s(), - serviceType.s(), - configId.s()).serviceStatus(); - verify(rootManager).getStatus(APPLICATION_ID, CLUSTER_ID, serviceType, configId); - assertEquals(serviceStatus, actualServiceStatus); - } - - @Test(expected = WebApplicationException.class) - public void testBadRequest() { - resource.getServiceStatus(APPLICATION_INSTANCE_REFERENCE, CLUSTER_ID.s(), null, null); - } - - private void testGetSlobrokEntriesWith(String pattern, String expectedLookupPattern) - throws Exception{ - when(slobrokApi.lookup(APPLICATION_ID, expectedLookupPattern)) - .thenReturn(ENTRIES); - - List<SlobrokEntryResponse> response = resource.getSlobrokEntries( - APPLICATION_INSTANCE_REFERENCE, - pattern); - - verify(slobrokApi).lookup(APPLICATION_ID, expectedLookupPattern); - - ObjectMapper mapper = new ObjectMapper(); - String actualJson = mapper.writeValueAsString(response); - assertEquals( - "[{\"name\":\"name1\",\"spec\":\"tcp/spec:1\"},{\"name\":\"name2\",\"spec\":\"tcp/spec:2\"}]", - actualJson); - } -}
\ No newline at end of file |