diff options
author | Frode Lundgren <frodelu@yahoo-inc.com> | 2019-12-06 23:22:01 -0800 |
---|---|---|
committer | Frode Lundgren <frodelu@yahoo-inc.com> | 2019-12-06 23:22:01 -0800 |
commit | 22e0baee45c3d39b95dc4fdbdfb5f99e863aec69 (patch) | |
tree | 7c07262e1f24455c1c969a1a372dbcf019915ca3 | |
parent | 70d634fe5462dfce43e4549750774d7f3dde069a (diff) |
Remove Statuspage.io proxy
6 files changed, 1 insertions, 327 deletions
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/PathGroup.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/PathGroup.java index 26bf189dd3d..f0b86cdb0e8 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/PathGroup.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/PathGroup.java @@ -176,8 +176,7 @@ enum PathGroup { classifiedInfo("/cost/v1/{*}", "/deployment/v1/{*}", "/", - "/d/{*}", - "/statuspage/v1/{*}"), + "/d/{*}"), /** Same as classifiedInfo, but with optional /api prefix */ classifiedApiInfo(Optional.of("/api"), diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/statuspage/StatusPageClient.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/statuspage/StatusPageClient.java deleted file mode 100644 index 93213172048..00000000000 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/statuspage/StatusPageClient.java +++ /dev/null @@ -1,117 +0,0 @@ -// 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.controller.restapi.statuspage; - -import com.yahoo.slime.Slime; -import com.yahoo.vespa.config.SlimeUtils; -import org.apache.http.HttpStatus; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.utils.URIBuilder; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.client.HttpClients; -import org.apache.http.util.EntityUtils; - -import java.io.IOException; -import java.io.UncheckedIOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.time.Duration; -import java.util.Objects; -import java.util.Optional; - -/** - * A basic client for the StatusPage API. - * - * @author mpolden - */ -public class StatusPageClient { - - private static final Duration requestTimeout = Duration.ofSeconds(30); - - private final URI url; - private final String key; - - private StatusPageClient(URI url, String key) { - this.url = Objects.requireNonNull(url, "url cannot be null"); - this.key = Objects.requireNonNull(key, "key cannot be null"); - } - - /** GET given page and return response body as slime */ - public Slime get(String page, Optional<String> since) { - HttpGet get = new HttpGet(pageUrl(page, since)); - try (CloseableHttpClient client = client()) { - try (CloseableHttpResponse response = client.execute(get)) { - if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { - throw new IllegalArgumentException("Received status " + response.getStatusLine().getStatusCode() + - " from StatusPage"); - } - byte[] body = EntityUtils.toByteArray(response.getEntity()); - return SlimeUtils.jsonToSlime(body); - } - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - - URI pageUrl(String page, Optional<String> since) { - if (!allowAccess(page)) { - throw new IllegalArgumentException("Invalid resource: '" + page + "'"); - } - URIBuilder builder = new URIBuilder(url) - .setPath("/api/v2/" + page + ".json") - .setParameter("api_key", key); - since.ifPresent(s -> builder.setParameter("since", s)); - try { - return builder.build(); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - } - - public static StatusPageClient create(URI url, String secret) { - String[] parts = secret.split(":"); - if (parts.length != 2) { - throw new IllegalArgumentException("Invalid secret"); - } - String pageId = parts[0]; - String apiKey = parts[1]; - if (isDefault(url)) { - // Rewrite URL to include page ID - try { - url = new URIBuilder(url).setHost(pageId + "." + url.getHost()).build(); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - } - return new StatusPageClient(url, apiKey); - } - - private static CloseableHttpClient client() { - HttpClientBuilder builder = HttpClients.custom(); - RequestConfig requestConfig = RequestConfig.custom() - .setConnectionRequestTimeout((int) requestTimeout.toMillis()) - .setConnectTimeout((int) requestTimeout.toMillis()) - .setSocketTimeout((int) requestTimeout.toMillis()) - .build(); - return builder.setDefaultRequestConfig(requestConfig) - .setUserAgent("vespa-statuspage-client") - .build(); - } - - /** Returns whether given page is allowed to be accessed */ - private static boolean allowAccess(String page) { - switch (page) { - case "incidents": - case "scheduled-maintenances": - return true; - } - return false; - } - - private static boolean isDefault(URI url) { - return "statuspage.io".equals(url.getHost()); - } - -} diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/statuspage/StatusPageProxyHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/statuspage/StatusPageProxyHandler.java deleted file mode 100644 index 8c445fe3a0a..00000000000 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/statuspage/StatusPageProxyHandler.java +++ /dev/null @@ -1,67 +0,0 @@ -// 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.controller.restapi.statuspage; - -import com.google.inject.Inject; -import com.yahoo.container.jdisc.HttpRequest; -import com.yahoo.container.jdisc.HttpResponse; -import com.yahoo.container.jdisc.LoggingRequestHandler; -import com.yahoo.container.jdisc.secretstore.SecretStore; -import com.yahoo.slime.Slime; -import com.yahoo.restapi.ErrorResponse; -import com.yahoo.restapi.Path; -import com.yahoo.restapi.SlimeJsonResponse; -import com.yahoo.vespa.hosted.controller.statuspage.config.StatuspageConfig; -import com.yahoo.yolean.Exceptions; - -import java.net.URI; -import java.util.Optional; -import java.util.logging.Level; - -/** - * Proxies requests from the controller to StatusPage. - * - * @author andreer - * @author mpolden - */ -@SuppressWarnings("unused") // Handler -public class StatusPageProxyHandler extends LoggingRequestHandler { - - private static final String secretKey = "vespa_hosted.controller.statuspage_api_key"; - - private final SecretStore secretStore; - private final URI apiUrl; - - @Inject - public StatusPageProxyHandler(Context parentCtx, SecretStore secretStore, StatuspageConfig config) { - super(parentCtx); - this.secretStore = secretStore; - this.apiUrl = URI.create(config.apiUrl()); - } - - @Override - public HttpResponse handle(HttpRequest request) { - try { - switch (request.getMethod()) { - case GET: return handleGET(request); - default: return ErrorResponse.methodNotAllowed("Method '" + request.getMethod() + "' is not supported"); - } - } catch (IllegalArgumentException e) { - return ErrorResponse.badRequest(Exceptions.toMessageString(e)); - } catch (RuntimeException e) { - log.log(Level.WARNING, "Unexpected error handling '" + request.getUri() + "'", e); - return ErrorResponse.internalServerError(Exceptions.toMessageString(e)); - } - } - - private HttpResponse handleGET(HttpRequest request) { - Path path = new Path(request.getUri()); - if (!path.matches("/statuspage/v1/{page}")) { - return ErrorResponse.notFoundError("Nothing at " + path); - } - StatusPageClient client = StatusPageClient.create(apiUrl, secretStore.getSecret(secretKey)); - Optional<String> since = Optional.ofNullable(request.getProperty("since")); - Slime statusPageResponse = client.get(path.get("page"), since); - return new SlimeJsonResponse(statusPageResponse); - } - -} diff --git a/controller-server/src/main/resources/configdefinitions/statuspage.def b/controller-server/src/main/resources/configdefinitions/statuspage.def deleted file mode 100644 index db8b034f99b..00000000000 --- a/controller-server/src/main/resources/configdefinitions/statuspage.def +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -namespace=vespa.hosted.controller.statuspage.config - -apiUrl string default=https://statuspage.io diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/statuspage/StatusPageClientTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/statuspage/StatusPageClientTest.java deleted file mode 100644 index 378e4298552..00000000000 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/statuspage/StatusPageClientTest.java +++ /dev/null @@ -1,44 +0,0 @@ -// 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.controller.restapi.statuspage; - -import org.junit.Test; - -import java.net.URI; -import java.util.Optional; - -import static org.junit.Assert.*; - -/** - * @author mpolden - */ -public class StatusPageClientTest { - - @Test - public void test_url_building() { - { - URI apiUrl = URI.create("https://statuspage.io"); - String secret = "testpage:testkey"; - assertEquals("https://testpage.statuspage.io/api/v2/incidents.json?api_key=testkey", - StatusPageClient.create(apiUrl, secret).pageUrl("incidents", Optional.empty()).toString()); - assertEquals("https://testpage.statuspage.io/api/v2/scheduled-maintenances.json?api_key=testkey&since=2015-01-01T00%3A00%2B00%3A00", - StatusPageClient.create(apiUrl, secret).pageUrl("scheduled-maintenances", - Optional.of("2015-01-01T00:00+00:00")).toString()); - } - - { - URI apiUrl = URI.create("http://foo.bar"); - assertEquals("http://foo.bar/api/v2/incidents.json?api_key=testkey", - StatusPageClient.create(apiUrl, "testpage:testkey").pageUrl("incidents", Optional.empty()).toString()); - } - - { - try { - URI apiUrl = URI.create("http://foo.bar"); - assertEquals("http://foo.bar/api/v2/incidents.json?api_key=testkey", - StatusPageClient.create(apiUrl, "").pageUrl("incidents", Optional.empty()).toString()); - fail("Expected exception"); - } catch (IllegalArgumentException ignored) {} - } - } - -} diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/statuspage/StatusPageProxyApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/statuspage/StatusPageProxyApiTest.java deleted file mode 100644 index f39914fd8fb..00000000000 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/statuspage/StatusPageProxyApiTest.java +++ /dev/null @@ -1,93 +0,0 @@ -// 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.controller.restapi.statuspage; - -import com.github.tomakehurst.wiremock.junit.WireMockRule; -import com.yahoo.application.Networking; -import com.yahoo.application.container.JDisc; -import com.yahoo.application.container.handler.Request; -import com.yahoo.application.container.handler.Response; -import com.yahoo.vespa.hosted.controller.integration.SecretStoreMock; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; - -import java.io.IOException; - -import static com.github.tomakehurst.wiremock.client.WireMock.get; -import static com.github.tomakehurst.wiremock.client.WireMock.okJson; -import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; -import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; -import static org.junit.Assert.assertEquals; - -/** - * @author mpolden - */ -public class StatusPageProxyApiTest { - - private JDisc container; - - @Before - public void startContainer() { - container = JDisc.fromServicesXml(servicesXml(), Networking.disable); - secretStore().setSecret("vespa_hosted.controller.statuspage_api_key", "page-id:secret"); - } - - @After - public void stopContainer() { - container.close(); - secretStore().clear(); - } - - @Rule - public final WireMockRule wireMock = new WireMockRule(options().dynamicPort(), true); - - @Test - public void test_proxy() throws Exception { - // Invalid requests - assertResponse("/statuspage/v1/", "{\"error-code\":\"NOT_FOUND\",\"message\":\"Nothing at path '/statuspage/v1'\"}", 404); - assertResponse("/statuspage/v1/invalid", "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Invalid resource: 'invalid'\"}", 400); - assertResponse(Request.Method.POST, "/statuspage/v1/invalid", "{\"error-code\":\"METHOD_NOT_ALLOWED\",\"message\":\"Method 'POST' is not supported\"}", 405); - - // Mock responses from StatusPage - wireMock.stubFor(get(urlEqualTo("/api/v2/incidents.json?api_key=secret")) - .willReturn(okJson("{\"incidents\":[]}"))); - wireMock.stubFor(get(urlEqualTo("/api/v2/scheduled-maintenances.json?api_key=secret")) - .willReturn(okJson("{\"scheduled_maintenances\":[]}"))); - - assertResponse("/statuspage/v1/incidents", "{\"incidents\":[]}", 200); - assertResponse("/statuspage/v1/scheduled-maintenances", "{\"scheduled_maintenances\":[]}", 200); - } - - private void assertResponse(String path, String expectedResponse, int expectedStatusCode) throws IOException { - assertResponse(Request.Method.GET, path, expectedResponse, expectedStatusCode); - } - - private void assertResponse(Request.Method method, String path, String expectedResponse, int expectedStatusCode) throws IOException { - Response response = container.handleRequest(new Request("http://localhost:8080" + path, new byte[0], method)); - assertEquals(expectedResponse, response.getBodyAsString()); - assertEquals("Status code", expectedStatusCode, response.getStatus()); - assertEquals("application/json; charset=UTF-8", response.getHeaders().getFirst("Content-Type")); - } - - private SecretStoreMock secretStore() { - return (SecretStoreMock) container.components().getComponent(SecretStoreMock.class.getName()); - } - - private String servicesXml() { - String statusPageApiUrl = "http://127.0.0.1:" + wireMock.port(); - return "<container version='1.0'>\n" + - " <config name='vespa.hosted.controller.statuspage.config.statuspage'>\n" + - " <apiUrl>" + statusPageApiUrl + "</apiUrl>\n" + - " </config>\n" + - " <component id='com.yahoo.vespa.hosted.controller.integration.SecretStoreMock'/>\n" + - " <handler id='com.yahoo.vespa.hosted.controller.restapi.statuspage.StatusPageProxyHandler'>\n" + - " <binding>http://*/statuspage/v1/*</binding>\n" + - " </handler>\n" + - " <http>\n" + - " <server id='default' port='8080'/>\n" + - " </http>\n" + - "</container>"; - } - -} |