From 8aa48ffaff8f9d61ea9faa30cd8b6cb9b6c8f8dc Mon Sep 17 00:00:00 2001 From: uboness Date: Fri, 15 Apr 2016 13:28:27 +0200 Subject: [PATCH] Introduced the X-Pack Info API - Removed Shield's Info API - Removed Watcher's Info API Closes elastic/elasticsearch#2014 Original commit: elastic/x-pack-elasticsearch@6910cb1d6ea1927387f9dfe858c935949f346a8e --- .../elasticsearch/license/core/License.java | 19 ++ .../10_insufficient_shield_privs.yaml | 8 +- .../license/plugin/LicensingClient.java | 58 +++++++ .../delete/DeleteLicenseRequestBuilder.java | 4 + .../action/get/GetLicenseRequestBuilder.java | 4 + .../action/put/PutLicenseRequestBuilder.java | 4 + .../org/elasticsearch/shield/Security.java | 4 - .../rest/action/RestShieldInfoAction.java | 106 ------------ .../ShieldPluginEnabledDisabledTests.java | 46 ----- .../shield/ShieldPluginTests.java | 13 +- .../transport/KnownActionsTests.java | 7 +- .../org/elasticsearch/transport/actions | 1 + .../org/elasticsearch/xpack/XPackBuild.java} | 21 +-- .../org/elasticsearch/xpack/XPackClient.java | 26 ++- .../org/elasticsearch/xpack/XPackPlugin.java | 11 ++ .../action/TransportXPackInfoAction.java | 44 +++++ .../xpack/action/XPackInfoAction.java | 32 ++++ .../xpack/action/XPackInfoRequest.java | 22 +++ .../xpack/action/XPackInfoRequestBuilder.java | 23 +++ .../xpack/action/XPackInfoResponse.java | 163 ++++++++++++++++++ .../xpack/rest/RestXPackInfoAction.java | 59 +++++++ .../xpack/rest/XPackRestHandler.java | 32 ++++ .../main/resources/xpack-build.properties} | 0 .../action/TransportXPackInfoActionTests.java | 101 +++++++++++ .../rest-api-spec/api/xpack.info.json | 13 ++ .../rest-api-spec/test/xpack/15_basic.yaml | 54 ++++++ .../org/elasticsearch/watcher/Watcher.java | 2 - .../elasticsearch/watcher/WatcherModule.java | 2 +- .../rest/action/RestWatcherInfoAction.java | 55 ------ .../main/resources/watcher-build.properties | 3 - .../rest-api-spec/api/watcher.info.json | 15 -- .../test/watcher/watch_info/10_basic.yaml | 5 - 32 files changed, 697 insertions(+), 260 deletions(-) create mode 100644 elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/LicensingClient.java delete mode 100644 elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/rest/action/RestShieldInfoAction.java rename elasticsearch/x-pack/{shield/src/main/java/org/elasticsearch/shield/ShieldBuild.java => src/main/java/org/elasticsearch/xpack/XPackBuild.java} (76%) create mode 100644 elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/action/TransportXPackInfoAction.java create mode 100644 elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/action/XPackInfoAction.java create mode 100644 elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/action/XPackInfoRequest.java create mode 100644 elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/action/XPackInfoRequestBuilder.java create mode 100644 elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/action/XPackInfoResponse.java create mode 100644 elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/rest/RestXPackInfoAction.java create mode 100644 elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/rest/XPackRestHandler.java rename elasticsearch/x-pack/{shield/src/main/resources/shield-build.properties => src/main/resources/xpack-build.properties} (100%) create mode 100644 elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/action/TransportXPackInfoActionTests.java create mode 100644 elasticsearch/x-pack/src/test/resources/rest-api-spec/api/xpack.info.json create mode 100644 elasticsearch/x-pack/src/test/resources/rest-api-spec/test/xpack/15_basic.yaml delete mode 100644 elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/watcher/rest/action/RestWatcherInfoAction.java delete mode 100644 elasticsearch/x-pack/watcher/src/main/resources/watcher-build.properties delete mode 100644 elasticsearch/x-pack/watcher/src/test/resources/rest-api-spec/api/watcher.info.json delete mode 100644 elasticsearch/x-pack/watcher/src/test/resources/rest-api-spec/test/watcher/watch_info/10_basic.yaml diff --git a/elasticsearch/license/base/src/main/java/org/elasticsearch/license/core/License.java b/elasticsearch/license/base/src/main/java/org/elasticsearch/license/core/License.java index 1b37a55166c..a736463e5ba 100644 --- a/elasticsearch/license/base/src/main/java/org/elasticsearch/license/core/License.java +++ b/elasticsearch/license/base/src/main/java/org/elasticsearch/license/core/License.java @@ -685,6 +685,7 @@ public class License implements ToXContent { } public enum Status { + ACTIVE("active"), INVALID("invalid"), EXPIRED("expired"); @@ -698,5 +699,23 @@ public class License implements ToXContent { public String label() { return label; } + + public void writeTo(StreamOutput out) throws IOException { + out.writeString(label); + } + + public static Status readFrom(StreamInput in) throws IOException { + String value = in.readString(); + switch (value) { + case "active": + return ACTIVE; + case "invalid": + return INVALID; + case "expired": + return EXPIRED; + default: + throw new IllegalArgumentException("unknown license status [" + value + "]"); + } + } } } diff --git a/elasticsearch/qa/smoke-test-watcher-with-shield/src/test/resources/rest-api-spec/test/watcher/watcher_and_shield/10_insufficient_shield_privs.yaml b/elasticsearch/qa/smoke-test-watcher-with-shield/src/test/resources/rest-api-spec/test/watcher/watcher_and_shield/10_insufficient_shield_privs.yaml index 04422f654f4..668900466e1 100644 --- a/elasticsearch/qa/smoke-test-watcher-with-shield/src/test/resources/rest-api-spec/test/watcher/watcher_and_shield/10_insufficient_shield_privs.yaml +++ b/elasticsearch/qa/smoke-test-watcher-with-shield/src/test/resources/rest-api-spec/test/watcher/watcher_and_shield/10_insufficient_shield_privs.yaml @@ -1,12 +1,12 @@ --- "Test watcher is protected by shield": - do: - headers: {es-shield-runas-user: powerless_user} + headers: { es-shield-runas-user: powerless_user } catch: forbidden - watcher.info: {} + watcher.stats: {} # there seems to be a bug in the yaml parser we use, where a single element list # has the END_LIST token skipped...so here we just rerun the same request without # the impersonation to show it works - do: - watcher.info: {} - - is_true: version.build_hash + watcher.stats: {} + - match: { watcher_state: started } diff --git a/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/LicensingClient.java b/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/LicensingClient.java new file mode 100644 index 00000000000..203f1fb3e0d --- /dev/null +++ b/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/LicensingClient.java @@ -0,0 +1,58 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.license.plugin; + +import org.elasticsearch.action.ActionListener; +import org.elasticsearch.client.ElasticsearchClient; +import org.elasticsearch.license.core.License; +import org.elasticsearch.license.plugin.action.delete.DeleteLicenseAction; +import org.elasticsearch.license.plugin.action.delete.DeleteLicenseRequest; +import org.elasticsearch.license.plugin.action.delete.DeleteLicenseRequestBuilder; +import org.elasticsearch.license.plugin.action.delete.DeleteLicenseResponse; +import org.elasticsearch.license.plugin.action.get.GetLicenseAction; +import org.elasticsearch.license.plugin.action.get.GetLicenseRequest; +import org.elasticsearch.license.plugin.action.get.GetLicenseRequestBuilder; +import org.elasticsearch.license.plugin.action.get.GetLicenseResponse; +import org.elasticsearch.license.plugin.action.put.PutLicenseAction; +import org.elasticsearch.license.plugin.action.put.PutLicenseRequest; +import org.elasticsearch.license.plugin.action.put.PutLicenseRequestBuilder; +import org.elasticsearch.license.plugin.action.put.PutLicenseResponse; + +/** + * + */ +public class LicensingClient { + + private final ElasticsearchClient client; + + public LicensingClient(ElasticsearchClient client) { + this.client = client; + } + + public PutLicenseRequestBuilder preparePutLicense(License license) { + return new PutLicenseRequestBuilder(client).setLicense(license); + } + + public void putLicense(PutLicenseRequest request, ActionListener listener) { + client.execute(PutLicenseAction.INSTANCE, request, listener); + } + + public GetLicenseRequestBuilder prepareGetLicense() { + return new GetLicenseRequestBuilder(client); + } + + public void getLicense(GetLicenseRequest request, ActionListener listener) { + client.execute(GetLicenseAction.INSTANCE, request, listener); + } + + public DeleteLicenseRequestBuilder prepareDeleteLicense() { + return new DeleteLicenseRequestBuilder(client); + } + + public void deleteLicense(DeleteLicenseRequest request, ActionListener listener) { + client.execute(DeleteLicenseAction.INSTANCE, request, listener); + } +} diff --git a/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/action/delete/DeleteLicenseRequestBuilder.java b/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/action/delete/DeleteLicenseRequestBuilder.java index e1539603cc7..7753234a23a 100644 --- a/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/action/delete/DeleteLicenseRequestBuilder.java +++ b/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/action/delete/DeleteLicenseRequestBuilder.java @@ -11,6 +11,10 @@ import org.elasticsearch.client.ElasticsearchClient; public class DeleteLicenseRequestBuilder extends AcknowledgedRequestBuilder { + public DeleteLicenseRequestBuilder(ElasticsearchClient client) { + this(client, DeleteLicenseAction.INSTANCE); + } + /** * Creates new get licenses request builder * diff --git a/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/action/get/GetLicenseRequestBuilder.java b/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/action/get/GetLicenseRequestBuilder.java index 6d078ef3139..1574bbb638e 100644 --- a/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/action/get/GetLicenseRequestBuilder.java +++ b/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/action/get/GetLicenseRequestBuilder.java @@ -11,6 +11,10 @@ import org.elasticsearch.client.ElasticsearchClient; public class GetLicenseRequestBuilder extends MasterNodeReadOperationRequestBuilder { + public GetLicenseRequestBuilder(ElasticsearchClient client) { + this(client, GetLicenseAction.INSTANCE); + } + /** * Creates new get licenses request builder * diff --git a/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/action/put/PutLicenseRequestBuilder.java b/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/action/put/PutLicenseRequestBuilder.java index b4a3a5e8499..5759cc18f18 100644 --- a/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/action/put/PutLicenseRequestBuilder.java +++ b/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/action/put/PutLicenseRequestBuilder.java @@ -14,6 +14,10 @@ import org.elasticsearch.license.core.License; */ public class PutLicenseRequestBuilder extends AcknowledgedRequestBuilder { + public PutLicenseRequestBuilder(ElasticsearchClient client) { + this(client, PutLicenseAction.INSTANCE); + } + /** * Constructs register license request * diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/Security.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/Security.java index d17086b0ac2..641e779b672 100644 --- a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/Security.java +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/Security.java @@ -67,7 +67,6 @@ import org.elasticsearch.shield.license.ShieldLicenseState; import org.elasticsearch.shield.license.ShieldLicensee; import org.elasticsearch.shield.rest.ShieldRestModule; import org.elasticsearch.shield.rest.action.RestAuthenticateAction; -import org.elasticsearch.shield.rest.action.RestShieldInfoAction; import org.elasticsearch.shield.rest.action.realm.RestClearRealmCacheAction; import org.elasticsearch.shield.rest.action.role.RestPutRoleAction; import org.elasticsearch.shield.rest.action.role.RestClearRolesCacheAction; @@ -298,9 +297,6 @@ public class Security { return; } - // we want to expose the shield rest action even when the plugin is disabled - module.registerRestHandler(RestShieldInfoAction.class); - if (enabled) { module.registerTransport(Security.NAME, ShieldNettyTransport.class); module.registerTransportService(Security.NAME, ShieldServerTransportService.class); diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/rest/action/RestShieldInfoAction.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/rest/action/RestShieldInfoAction.java deleted file mode 100644 index b79fbb9e51a..00000000000 --- a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/rest/action/RestShieldInfoAction.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -package org.elasticsearch.shield.rest.action; - -import org.elasticsearch.Build; -import org.elasticsearch.Version; -import org.elasticsearch.client.Client; -import org.elasticsearch.cluster.ClusterName; -import org.elasticsearch.common.inject.Inject; -import org.elasticsearch.common.inject.internal.Nullable; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.rest.BaseRestHandler; -import org.elasticsearch.rest.BytesRestResponse; -import org.elasticsearch.rest.RestChannel; -import org.elasticsearch.rest.RestController; -import org.elasticsearch.rest.RestRequest; -import org.elasticsearch.rest.RestStatus; -import org.elasticsearch.shield.ShieldBuild; -import org.elasticsearch.shield.Security; -import org.elasticsearch.shield.license.ShieldLicenseState; - -import static org.elasticsearch.rest.RestRequest.Method.GET; -import static org.elasticsearch.rest.RestRequest.Method.HEAD; - -public class RestShieldInfoAction extends BaseRestHandler { - - private final ClusterName clusterName; - private final ShieldLicenseState shieldLicenseState; - private final boolean shieldEnabled; - - @Inject - public RestShieldInfoAction(Settings settings, RestController controller, Client client, ClusterName clusterName, - @Nullable ShieldLicenseState licenseState) { - super(settings, client); - this.clusterName = clusterName; - this.shieldLicenseState = licenseState; - this.shieldEnabled = Security.enabled(settings); - controller.registerHandler(GET, "/_shield", this); - controller.registerHandler(HEAD, "/_shield", this); - } - - @Override - protected void handleRequest(RestRequest request, RestChannel channel, Client client) throws Exception { - if (request.method() == RestRequest.Method.HEAD) { - channel.sendResponse(new BytesRestResponse(RestStatus.OK)); - return; - } - - XContentBuilder builder = channel.newBuilder(); - - // Default to pretty printing, but allow ?pretty=false to disable - if (!request.hasParam("pretty")) { - builder.prettyPrint().lfAtEnd(); - } - - builder.startObject(); - - builder.field("status", resolveStatus()); - if (settings.get("name") != null) { - builder.field("name", settings.get("name")); - } - builder.field("cluster_name", clusterName.value()); - builder.startObject("version") - .field("number", Version.CURRENT.toString()) - .field("build_hash", ShieldBuild.CURRENT.hash()) - .field("build_timestamp", ShieldBuild.CURRENT.timestamp()) - .field("build_snapshot", Build.CURRENT.isSnapshot()) - .endObject(); - builder.field("tagline", "You Know, for Security"); - builder.endObject(); - - channel.sendResponse(new BytesRestResponse(RestStatus.OK, builder)); - } - - private Status resolveStatus() { - if (shieldEnabled) { - assert shieldLicenseState != null; - // TODO this is error prone since the state could change between checks. We can also make this status better - // but we may remove this endpoint since it no longer serves much purpose - if (shieldLicenseState.securityEnabled() && shieldLicenseState.statsAndHealthEnabled()) { - return Status.ENABLED; - } - return Status.UNLICENSED; - } - return Status.DISABLED; - } - - private static enum Status { - ENABLED("enabled"), DISABLED("disabled"), UNLICENSED("unlicensed"); - - private final String status; - - Status(String status) { - this.status = status; - } - - @Override - public String toString() { - return status; - } - } -} diff --git a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/ShieldPluginEnabledDisabledTests.java b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/ShieldPluginEnabledDisabledTests.java index 01dfbf85b72..fbb4b49145e 100644 --- a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/ShieldPluginEnabledDisabledTests.java +++ b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/ShieldPluginEnabledDisabledTests.java @@ -95,50 +95,4 @@ public class ShieldPluginEnabledDisabledTests extends ShieldIntegTestCase { assertThat(transport, matcher); } } - - public void testShieldInfoStatus() throws IOException { - HttpServerTransport httpServerTransport = internalCluster().getDataNodeInstance(HttpServerTransport.class); - OperationMode mode; - if (enabled) { - mode = randomFrom(OperationMode.values()); - LicensingTests.enableLicensing(mode); - } else { - // this is the default right now - mode = OperationMode.BASIC; - } - - try (CloseableHttpClient httpClient = HttpClients.createDefault()) { - HttpResponse response = new HttpRequestBuilder(httpClient).httpTransport(httpServerTransport) - .method("GET") - .path("/_shield") - .addHeader(UsernamePasswordToken.BASIC_AUTH_HEADER, - basicAuthHeaderValue(ShieldSettingsSource.DEFAULT_USER_NAME, - new SecuredString(ShieldSettingsSource.DEFAULT_PASSWORD.toCharArray()))).execute(); - assertThat(response.getStatusCode(), is(OK.getStatus())); - - String expectedValue; - if (enabled) { - if (mode == OperationMode.BASIC) { - expectedValue = "unlicensed"; - } else { - expectedValue = "enabled"; - } - } else { - expectedValue = "disabled"; - } - assertThat(new JsonPath(response.getBody()).evaluate("status").toString(), equalTo(expectedValue)); - - if (enabled) { - LicensingTests.disableLicensing(); - response = new HttpRequestBuilder(httpClient).httpTransport(httpServerTransport) - .method("GET") - .path("/_shield") - .addHeader(UsernamePasswordToken.BASIC_AUTH_HEADER, - basicAuthHeaderValue(ShieldSettingsSource.DEFAULT_USER_NAME, - new SecuredString(ShieldSettingsSource.DEFAULT_PASSWORD.toCharArray()))).execute(); - assertThat(response.getStatusCode(), is(OK.getStatus())); - assertThat(new JsonPath(response.getBody()).evaluate("status").toString(), equalTo("unlicensed")); - } - } - } } diff --git a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/ShieldPluginTests.java b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/ShieldPluginTests.java index a0316f24304..9fd7c5febe2 100644 --- a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/ShieldPluginTests.java +++ b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/ShieldPluginTests.java @@ -37,24 +37,25 @@ public class ShieldPluginTests extends ShieldIntegTestCase { public void testThatPluginIsLoaded() throws IOException { HttpServerTransport httpServerTransport = internalCluster().getDataNodeInstance(HttpServerTransport.class); try (CloseableHttpClient httpClient = HttpClients.createDefault()) { - logger.info("executing unauthorized request to /_shield infos"); + logger.info("executing unauthorized request to /_xpack info"); HttpResponse response = new HttpRequestBuilder(httpClient).httpTransport(httpServerTransport) .method("GET") - .path("/_shield") + .path("/_xpack") .execute(); assertThat(response.getStatusCode(), is(UNAUTHORIZED.getStatus())); - logger.info("executing authorized request to /_shield infos"); + logger.info("executing authorized request to /_xpack infos"); response = new HttpRequestBuilder(httpClient).httpTransport(httpServerTransport) .method("GET") - .path("/_shield") + .path("/_xpack") .addHeader(UsernamePasswordToken.BASIC_AUTH_HEADER, basicAuthHeaderValue(ShieldSettingsSource.DEFAULT_USER_NAME, new SecuredString(ShieldSettingsSource.DEFAULT_PASSWORD.toCharArray()))) .execute(); assertThat(response.getStatusCode(), is(OK.getStatus())); - assertThat(response.getBody(), allOf(containsString("status"), containsString("cluster_name"), containsString("number"), - containsString("build_hash"), containsString("build_timestamp"), containsString("build_snapshot"))); + assertThat(response.getBody(), allOf(containsString("status"), containsString("hash"), + containsString("timestamp"), containsString("uid"), + containsString("type"), containsString("status"))); } } } diff --git a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/transport/KnownActionsTests.java b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/transport/KnownActionsTests.java index f685cf46ed6..81b8d1ec058 100644 --- a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/transport/KnownActionsTests.java +++ b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/transport/KnownActionsTests.java @@ -14,6 +14,7 @@ import org.elasticsearch.graph.Graph; import org.elasticsearch.shield.action.ShieldActionModule; import org.elasticsearch.test.ESIntegTestCase.ClusterScope; import org.elasticsearch.test.ShieldIntegTestCase; +import org.elasticsearch.xpack.XPackPlugin; import org.junit.BeforeClass; import java.io.IOException; @@ -107,6 +108,9 @@ public class KnownActionsTests extends ShieldIntegTestCase { // loading es core actions loadActions(collectSubClasses(Action.class, Action.class), actions); + // loading all xpack top level actions + loadActions(collectSubClasses(Action.class, XPackPlugin.class), actions); + // loading shield actions loadActions(collectSubClasses(Action.class, ShieldActionModule.class), actions); @@ -115,8 +119,7 @@ public class KnownActionsTests extends ShieldIntegTestCase { // also loading all actions from the graph plugin loadActions(collectSubClasses(Action.class, Graph.class), actions); - - + return unmodifiableSet(actions); } diff --git a/elasticsearch/x-pack/shield/src/test/resources/org/elasticsearch/transport/actions b/elasticsearch/x-pack/shield/src/test/resources/org/elasticsearch/transport/actions index e7e92274312..376da2fd067 100644 --- a/elasticsearch/x-pack/shield/src/test/resources/org/elasticsearch/transport/actions +++ b/elasticsearch/x-pack/shield/src/test/resources/org/elasticsearch/transport/actions @@ -73,6 +73,7 @@ indices:data/write/index indices:data/write/script/delete indices:data/write/script/put indices:data/write/update +cluster:monitor/xpack/info cluster:monitor/xpack/license/get cluster:admin/xpack/license/delete cluster:admin/xpack/license/put diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/ShieldBuild.java b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/XPackBuild.java similarity index 76% rename from elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/ShieldBuild.java rename to elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/XPackBuild.java index 0cc704608a9..6570bffb54e 100644 --- a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/ShieldBuild.java +++ b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/XPackBuild.java @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -package org.elasticsearch.shield; +package org.elasticsearch.xpack; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; @@ -17,16 +17,17 @@ import java.util.Properties; /** * */ -public class ShieldBuild { +public class XPackBuild { - public static final ShieldBuild CURRENT; + + public static final XPackBuild CURRENT; static { String hash = "NA"; String hashShort = "NA"; String timestamp = "NA"; - try (InputStream is = ShieldBuild.class.getResourceAsStream("/shield-build.properties")) { + try (InputStream is = XPackBuild.class.getResourceAsStream("/xpack-build.properties")) { Properties props = new Properties(); props.load(is); hash = props.getProperty("hash", hash); @@ -41,14 +42,14 @@ public class ShieldBuild { // just ignore... } - CURRENT = new ShieldBuild(hash, hashShort, timestamp); + CURRENT = new XPackBuild(hash, hashShort, timestamp); } private String hash; private String hashShort; private String timestamp; - ShieldBuild(String hash, String hashShort, String timestamp) { + XPackBuild(String hash, String hashShort, String timestamp) { this.hash = hash; this.hashShort = hashShort; this.timestamp = timestamp; @@ -66,16 +67,16 @@ public class ShieldBuild { return timestamp; } - public static ShieldBuild readBuild(StreamInput in) throws IOException { + public static XPackBuild readBuild(StreamInput in) throws IOException { String hash = in.readString(); String hashShort = in.readString(); String timestamp = in.readString(); - return new ShieldBuild(hash, hashShort, timestamp); + return new XPackBuild(hash, hashShort, timestamp); } - public static void writeBuild(ShieldBuild build, StreamOutput out) throws IOException { + public static void writeBuild(XPackBuild build, StreamOutput out) throws IOException { out.writeString(build.hash()); out.writeString(build.hashShort()); out.writeString(build.timestamp()); } -} +} \ No newline at end of file diff --git a/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/XPackClient.java b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/XPackClient.java index bf281eda1d8..60708212110 100644 --- a/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/XPackClient.java +++ b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/XPackClient.java @@ -5,11 +5,17 @@ */ package org.elasticsearch.xpack; +import org.elasticsearch.action.ActionListener; import org.elasticsearch.client.Client; +import org.elasticsearch.license.plugin.LicensingClient; import org.elasticsearch.marvel.client.MonitoringClient; import org.elasticsearch.shield.authc.support.SecuredString; import org.elasticsearch.shield.client.SecurityClient; import org.elasticsearch.watcher.client.WatcherClient; +import org.elasticsearch.xpack.action.XPackInfoAction; +import org.elasticsearch.xpack.action.XPackInfoRequest; +import org.elasticsearch.xpack.action.XPackInfoRequestBuilder; +import org.elasticsearch.xpack.action.XPackInfoResponse; import java.util.Collections; import java.util.Map; @@ -24,17 +30,27 @@ public class XPackClient { private final Client client; + private final LicensingClient licensingClient; private final MonitoringClient monitoringClient; private final SecurityClient securityClient; private final WatcherClient watcherClient; public XPackClient(Client client) { this.client = client; + this.licensingClient = new LicensingClient(client); this.monitoringClient = new MonitoringClient(client); this.securityClient = new SecurityClient(client); this.watcherClient = new WatcherClient(client); } + public Client es() { + return client; + } + + public LicensingClient licensing() { + return licensingClient; + } + public MonitoringClient monitoring() { return monitoringClient; } @@ -58,6 +74,14 @@ public class XPackClient { * @param passwd The password of the user. This char array can be cleared after calling this method. */ public XPackClient withAuth(String username, char[] passwd) { - return withHeaders(Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue(username, new SecuredString(passwd)))); + return withHeaders(Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue(username, new SecuredString(passwd)))); + } + + public XPackInfoRequestBuilder prepareInfo() { + return new XPackInfoRequestBuilder(client); + } + + public void info(XPackInfoRequest request, ActionListener listener) { + client.execute(XPackInfoAction.INSTANCE, request, listener); } } diff --git a/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/XPackPlugin.java b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/XPackPlugin.java index c1ffacccdd3..266cf949009 100644 --- a/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/XPackPlugin.java +++ b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/XPackPlugin.java @@ -25,10 +25,13 @@ import org.elasticsearch.script.ScriptModule; import org.elasticsearch.shield.authc.AuthenticationModule; import org.elasticsearch.shield.Security; import org.elasticsearch.watcher.Watcher; +import org.elasticsearch.xpack.action.TransportXPackInfoAction; +import org.elasticsearch.xpack.action.XPackInfoAction; import org.elasticsearch.xpack.common.init.LazyInitializationModule; import org.elasticsearch.xpack.common.init.LazyInitializationService; import org.elasticsearch.xpack.extensions.XPackExtension; import org.elasticsearch.xpack.extensions.XPackExtensionsService; +import org.elasticsearch.xpack.rest.RestXPackInfoAction; import java.nio.file.Path; import java.security.AccessController; @@ -75,6 +78,7 @@ public class XPackPlugin extends Plugin { } protected final Settings settings; + protected boolean transportClientMode; protected final XPackExtensionsService extensionsService; protected Licensing licensing; @@ -85,6 +89,7 @@ public class XPackPlugin extends Plugin { public XPackPlugin(Settings settings) { this.settings = settings; + transportClientMode = transportClientMode(settings); this.licensing = new Licensing(settings); this.security = new Security(settings); this.marvel = new Marvel(settings); @@ -166,6 +171,9 @@ public class XPackPlugin extends Plugin { } public void onModule(NetworkModule module) { + if (!transportClientMode) { + module.registerRestHandler(RestXPackInfoAction.class); + } licensing.onModule(module); marvel.onModule(module); security.onModule(module); @@ -174,6 +182,9 @@ public class XPackPlugin extends Plugin { } public void onModule(ActionModule module) { + if (!transportClientMode) { + module.registerAction(XPackInfoAction.INSTANCE, TransportXPackInfoAction.class); + } licensing.onModule(module); marvel.onModule(module); security.onModule(module); diff --git a/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/action/TransportXPackInfoAction.java b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/action/TransportXPackInfoAction.java new file mode 100644 index 00000000000..a80e49759c1 --- /dev/null +++ b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/action/TransportXPackInfoAction.java @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.xpack.action; + +import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.support.ActionFilters; +import org.elasticsearch.action.support.HandledTransportAction; +import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.license.core.License; +import org.elasticsearch.license.plugin.core.LicensesService; +import org.elasticsearch.threadpool.ThreadPool; +import org.elasticsearch.transport.TransportService; +import org.elasticsearch.xpack.XPackBuild; +import org.elasticsearch.xpack.action.XPackInfoResponse.LicenseInfo; + +/** + */ +public class TransportXPackInfoAction extends HandledTransportAction { + + private final LicensesService licensesService; + + @Inject + public TransportXPackInfoAction(Settings settings, ThreadPool threadPool, TransportService transportService, + ActionFilters actionFilters, IndexNameExpressionResolver indexNameExpressionResolver, + LicensesService licensesService) { + super(settings, XPackInfoAction.NAME, threadPool, transportService, actionFilters, indexNameExpressionResolver, + XPackInfoRequest::new); + this.licensesService = licensesService; + } + + @Override + protected void doExecute(XPackInfoRequest request, ActionListener listener) { + XPackInfoResponse.BuildInfo buildInfo = new XPackInfoResponse.BuildInfo(XPackBuild.CURRENT); + License license = licensesService.getLicense(); + LicenseInfo licenseInfo = license != null ? new LicenseInfo(license) : null; + XPackInfoResponse response = new XPackInfoResponse(buildInfo, licenseInfo); + listener.onResponse(response); + } +} diff --git a/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/action/XPackInfoAction.java b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/action/XPackInfoAction.java new file mode 100644 index 00000000000..9adf597024d --- /dev/null +++ b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/action/XPackInfoAction.java @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.xpack.action; + +import org.elasticsearch.action.Action; +import org.elasticsearch.client.ElasticsearchClient; + +/** + * + */ +public class XPackInfoAction extends Action { + + public static final String NAME = "cluster:monitor/xpack/info"; + public static final XPackInfoAction INSTANCE = new XPackInfoAction(); + + public XPackInfoAction() { + super(NAME); + } + + @Override + public XPackInfoRequestBuilder newRequestBuilder(ElasticsearchClient client) { + return new XPackInfoRequestBuilder(client); + } + + @Override + public XPackInfoResponse newResponse() { + return new XPackInfoResponse(); + } +} diff --git a/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/action/XPackInfoRequest.java b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/action/XPackInfoRequest.java new file mode 100644 index 00000000000..e3364c6a140 --- /dev/null +++ b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/action/XPackInfoRequest.java @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.xpack.action; + +import org.elasticsearch.action.ActionRequest; +import org.elasticsearch.action.ActionRequestValidationException; + +/** + * + */ +public class XPackInfoRequest extends ActionRequest { + + public XPackInfoRequest() {} + + @Override + public ActionRequestValidationException validate() { + return null; + } +} diff --git a/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/action/XPackInfoRequestBuilder.java b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/action/XPackInfoRequestBuilder.java new file mode 100644 index 00000000000..b5e3bc9c8e0 --- /dev/null +++ b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/action/XPackInfoRequestBuilder.java @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.xpack.action; + +import org.elasticsearch.action.ActionRequestBuilder; +import org.elasticsearch.client.ElasticsearchClient; + +/** + */ +public class XPackInfoRequestBuilder extends ActionRequestBuilder { + + public XPackInfoRequestBuilder(ElasticsearchClient client) { + this(client, XPackInfoAction.INSTANCE); + } + + public XPackInfoRequestBuilder(ElasticsearchClient client, XPackInfoAction action) { + super(client, action, new XPackInfoRequest()); + } + +} diff --git a/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/action/XPackInfoResponse.java b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/action/XPackInfoResponse.java new file mode 100644 index 00000000000..478b41a20ea --- /dev/null +++ b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/action/XPackInfoResponse.java @@ -0,0 +1,163 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.xpack.action; + +import org.elasticsearch.action.ActionResponse; +import org.elasticsearch.common.Nullable; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.license.core.License; +import org.elasticsearch.xpack.XPackBuild; + +import java.io.IOException; +import java.util.Locale; + +/** + */ +public class XPackInfoResponse extends ActionResponse { + + private BuildInfo buildInfo; + private @Nullable LicenseInfo licenseInfo; + + public XPackInfoResponse() {} + + public XPackInfoResponse(BuildInfo buildInfo, @Nullable LicenseInfo licenseInfo) { + this.buildInfo = buildInfo; + this.licenseInfo = licenseInfo; + } + + /** + * @return The build info (incl. build hash and timestamp) + */ + public BuildInfo getBuildInfo() { + return buildInfo; + } + + /** + * @return The current license info (incl. UID, type/mode. status and expiry date). May return {@code null} when no + * license is currently installed. + */ + public LicenseInfo getLicenseInfo() { + return licenseInfo; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + buildInfo.writeTo(out); + if (licenseInfo != null) { + out.writeBoolean(true); + licenseInfo.writeTo(out); + } else { + out.writeBoolean(false); + } + } + + @Override + public void readFrom(StreamInput in) throws IOException { + this.buildInfo = new BuildInfo(in); + this.licenseInfo = in.readBoolean() ? new LicenseInfo(in) : null; + } + + public static class LicenseInfo implements ToXContent { + + private final String uid; + private final String type; + private final long expiryDate; + private final License.Status status; + + public LicenseInfo(License license) { + this(license.uid(), license.type(), license.status(), license.expiryDate()); + } + + public LicenseInfo(StreamInput in) throws IOException { + this(in.readString(), in.readString(), License.Status.readFrom(in), in.readLong()); + } + + public LicenseInfo(String uid, String type, License.Status status, long expiryDate) { + this.uid = uid; + this.type = type; + this.status = status; + this.expiryDate = expiryDate; + } + + public String getUid() { + return uid; + } + + public String getType() { + return type; + } + + public long getExpiryDate() { + return expiryDate; + } + + public License.Status getStatus() { + return status; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + return builder.startObject() + .field("uid", uid) + .field("type", type) + .field("mode", License.OperationMode.resolve(type).name().toLowerCase(Locale.ROOT)) + .field("status", status.label()) + .dateValueField("expiry_date_in_millis", "expiry_date", expiryDate) + .endObject(); + } + + public void writeTo(StreamOutput out) throws IOException { + out.writeString(uid); + out.writeString(type); + status.writeTo(out); + out.writeLong(expiryDate); + } + } + + public static class BuildInfo implements ToXContent { + + private final String hash; + private final String timestamp; + + public BuildInfo(XPackBuild build) { + this(build.hash(), build.timestamp()); + } + + public BuildInfo(StreamInput input) throws IOException { + this(input.readString(), input.readString()); + } + + public BuildInfo(String hash, String timestamp) { + this.hash = hash; + this.timestamp = timestamp; + } + + public String getHash() { + return hash; + } + + public String getTimestamp() { + return timestamp; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + return builder.startObject() + .field("hash", hash) + .field("timestamp", timestamp) + .endObject(); + } + + public void writeTo(StreamOutput output) throws IOException { + output.writeString(hash); + output.writeString(timestamp); + } + } +} diff --git a/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/rest/RestXPackInfoAction.java b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/rest/RestXPackInfoAction.java new file mode 100644 index 00000000000..6d7a4a4809a --- /dev/null +++ b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/rest/RestXPackInfoAction.java @@ -0,0 +1,59 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.xpack.rest; + +import org.elasticsearch.client.Client; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.rest.BytesRestResponse; +import org.elasticsearch.rest.RestChannel; +import org.elasticsearch.rest.RestController; +import org.elasticsearch.rest.RestRequest; +import org.elasticsearch.rest.RestResponse; +import org.elasticsearch.rest.action.support.RestBuilderListener; +import org.elasticsearch.xpack.XPackClient; +import org.elasticsearch.xpack.action.XPackInfoResponse; + +import static org.elasticsearch.rest.RestRequest.Method.GET; +import static org.elasticsearch.rest.RestRequest.Method.HEAD; +import static org.elasticsearch.rest.RestStatus.OK; + +public class RestXPackInfoAction extends XPackRestHandler { + + @Inject + public RestXPackInfoAction(Settings settings, RestController controller, Client client) { + super(settings, client); + controller.registerHandler(HEAD, URI_BASE, this); + controller.registerHandler(GET, URI_BASE, this); + } + + @Override + protected void handleRequest(RestRequest request, RestChannel restChannel, XPackClient client) throws Exception { + client.prepareInfo().execute(new RestBuilderListener(restChannel) { + @Override + public RestResponse buildResponse(XPackInfoResponse infoResponse, XContentBuilder builder) throws Exception { + + // we treat HEAD requests as simple pings to ensure that X-Pack is installed + // we still execute the action as we want this request to be authorized + if (request.method() == RestRequest.Method.HEAD) { + return new BytesRestResponse(OK); + } + + builder.startObject(); + builder.field("build", infoResponse.getBuildInfo(), request); + if (infoResponse.getLicenseInfo() != null) { + builder.field("license", infoResponse.getLicenseInfo(), request); + } else { + builder.nullField("license"); + } + builder.field("tagline", "You know, for X"); + builder.endObject(); + return new BytesRestResponse(OK, builder); + } + }); + } +} diff --git a/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/rest/XPackRestHandler.java b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/rest/XPackRestHandler.java new file mode 100644 index 00000000000..76ce0f4a458 --- /dev/null +++ b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/rest/XPackRestHandler.java @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.xpack.rest; + +import org.elasticsearch.client.Client; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.rest.BaseRestHandler; +import org.elasticsearch.rest.RestChannel; +import org.elasticsearch.rest.RestRequest; +import org.elasticsearch.xpack.XPackClient; + +/** + * + */ +public abstract class XPackRestHandler extends BaseRestHandler { + + protected static String URI_BASE = "_xpack"; + + public XPackRestHandler(Settings settings, Client client) { + super(settings, client); + } + + @Override + protected final void handleRequest(RestRequest request, RestChannel channel, Client client) throws Exception { + handleRequest(request, channel, new XPackClient(client)); + } + + protected abstract void handleRequest(RestRequest request, RestChannel channel, XPackClient client) throws Exception; +} diff --git a/elasticsearch/x-pack/shield/src/main/resources/shield-build.properties b/elasticsearch/x-pack/src/main/resources/xpack-build.properties similarity index 100% rename from elasticsearch/x-pack/shield/src/main/resources/shield-build.properties rename to elasticsearch/x-pack/src/main/resources/xpack-build.properties diff --git a/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/action/TransportXPackInfoActionTests.java b/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/action/TransportXPackInfoActionTests.java new file mode 100644 index 00000000000..ac4a1bfe4cd --- /dev/null +++ b/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/action/TransportXPackInfoActionTests.java @@ -0,0 +1,101 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.xpack.action; + +import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.support.ActionFilters; +import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.license.core.License; +import org.elasticsearch.license.plugin.core.LicensesService; +import org.elasticsearch.shield.user.AnonymousUser; +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.threadpool.ThreadPool; +import org.elasticsearch.transport.TransportService; +import org.junit.After; +import org.junit.Before; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +import static org.hamcrest.Matchers.is; +import static org.hamcrest.core.IsNull.notNullValue; +import static org.hamcrest.core.IsNull.nullValue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class TransportXPackInfoActionTests extends ESTestCase { + + private boolean anonymousEnabled; + + @Before + public void maybeEnableAnonymous() { + anonymousEnabled = randomBoolean(); + if (anonymousEnabled) { + Settings settings = Settings.builder().put(AnonymousUser.ROLES_SETTING.getKey(), "superuser").build(); + AnonymousUser.initialize(settings); + } + } + + @After + public void resetAnonymous() { + AnonymousUser.initialize(Settings.EMPTY); + } + + public void testDoExecute() throws Exception { + + LicensesService licensesService = mock(LicensesService.class); + + TransportXPackInfoAction action = new TransportXPackInfoAction(Settings.EMPTY, mock(ThreadPool.class), + mock(TransportService.class), mock(ActionFilters.class), mock(IndexNameExpressionResolver.class), licensesService); + + License license = mock(License.class); + long expiryDate = randomLong(); + when(license.expiryDate()).thenReturn(expiryDate); + License.Status status = randomFrom(License.Status.values()); + when(license.status()).thenReturn(status); + String type = randomAsciiOfLength(10); + when(license.type()).thenReturn(type); + String uid = randomAsciiOfLength(30); + when(license.uid()).thenReturn(uid); + when(licensesService.getLicense()).thenReturn(license); + + XPackInfoRequest request = new XPackInfoRequest(); + + final CountDownLatch latch = new CountDownLatch(1); + final AtomicReference response = new AtomicReference<>(); + final AtomicReference error = new AtomicReference<>(); + action.doExecute(request, new ActionListener() { + @Override + public void onResponse(XPackInfoResponse infoResponse) { + response.set(infoResponse); + latch.countDown(); + } + + @Override + public void onFailure(Throwable e) { + error.set(e); + latch.countDown(); + } + }); + + if (!latch.await(5, TimeUnit.SECONDS)) { + fail("waiting too long for "); + } + + assertThat(error.get(), nullValue()); + assertThat(response.get(), notNullValue()); + + assertThat(response.get().getBuildInfo(), notNullValue()); + assertThat(response.get().getLicenseInfo(), notNullValue()); + assertThat(response.get().getLicenseInfo().getExpiryDate(), is(expiryDate)); + assertThat(response.get().getLicenseInfo().getStatus(), is(status)); + assertThat(response.get().getLicenseInfo().getType(), is(type)); + assertThat(response.get().getLicenseInfo().getUid(), is(uid)); + } + +} diff --git a/elasticsearch/x-pack/src/test/resources/rest-api-spec/api/xpack.info.json b/elasticsearch/x-pack/src/test/resources/rest-api-spec/api/xpack.info.json new file mode 100644 index 00000000000..232266b8b7a --- /dev/null +++ b/elasticsearch/x-pack/src/test/resources/rest-api-spec/api/xpack.info.json @@ -0,0 +1,13 @@ +{ + "xpack.info": { + "documentation": "Retrieve information about xpack, including buid number/timestamp and license status", + "methods": [ "GET", "HEAD" ], + "url": { + "path": "/_xpack", + "paths": [ "/_xpack" ], + "parts": {}, + "params": {} + }, + "body": null + } +} diff --git a/elasticsearch/x-pack/src/test/resources/rest-api-spec/test/xpack/15_basic.yaml b/elasticsearch/x-pack/src/test/resources/rest-api-spec/test/xpack/15_basic.yaml new file mode 100644 index 00000000000..8e62c2ee28c --- /dev/null +++ b/elasticsearch/x-pack/src/test/resources/rest-api-spec/test/xpack/15_basic.yaml @@ -0,0 +1,54 @@ +# Integration tests xpack info API +# +"X-Pack Info": + - do: + cluster.health: + wait_for_status: yellow + + - do: + license.delete: {} + - match: { acknowledged: true } + + # we don't have a license now + - do: + xpack.info: {} + - is_true: build.hash + - is_true: build.timestamp + - is_false: license + + # lets put an active license + - do: + license.post: + body: > + { + "license": { + "uid": "893361dc-9749-4997-93cb-802e3dofh7aa", + "type": "internal", + "subscription_type": "none", + "issue_date_in_millis": 1443484800000, + "feature": "watcher", + "expiry_date_in_millis": 1914278399999, + "max_nodes": 1, + "issued_to": "issuedTo", + "issuer": "issuer", + "signature": "AAAAAQAAAA0Sc90guRIaQEmgLvMnAAABmC9ZN0hjZDBGYnVyRXpCOW5Bb3FjZDAxOWpSbTVoMVZwUzRxVk1PSmkxakxZdW5IMlhlTHNoN1N2MXMvRFk4d3JTZEx3R3RRZ0pzU3lobWJKZnQvSEFva0ppTHBkWkprZWZSQi9iNmRQNkw1SlpLN0lDalZCS095MXRGN1lIZlpYcVVTTnFrcTE2dzhJZmZrdFQrN3JQeGwxb0U0MXZ0dDJHSERiZTVLOHNzSDByWnpoZEphZHBEZjUrTVBxRENNSXNsWWJjZllaODdzVmEzUjNiWktNWGM5TUhQV2plaUo4Q1JOUml4MXNuL0pSOEhQaVB2azhmUk9QVzhFeTFoM1Q0RnJXSG53MWk2K055c28zSmRnVkF1b2JSQkFLV2VXUmVHNDZ2R3o2VE1qbVNQS2lxOHN5bUErZlNIWkZSVmZIWEtaSU9wTTJENDVvT1NCYklacUYyK2FwRW9xa0t6dldMbmMzSGtQc3FWOTgzZ3ZUcXMvQkt2RUZwMFJnZzlvL2d2bDRWUzh6UG5pdENGWFRreXNKNkE9PQAAAQCQ94dju0pnDZR3Uuypi0ic3aQJ+nvVqe+U8u79Dga5n1qIjcHDh7HvIBJEkF+tnVPlo/PXV/x7BZSwVY1PVErit+6rYix1yuHEgqwxmx/VdRICjCaZM6tk0Ob4dZCPv6Ebn2Mmk89KHC/PwiLPqF6QfwV/Pkpa8k2A3ORJmvYSDvXhe6tCs8dqc4ebrsFxqrZjwWh5CZSpzqqZBFXlngDv2N0hHhpGlueRszD0JJ5dfEL5ZA1DDOrgO9OJVejSHyRqe1L5QRUNdXPVfS+EAG0Dd1cNdJ/sMpYCPnVjbw6iq2/YgM3cuztsXVBY7ij4WnoP3ce7Zjs9TwHn+IqzftC6" + } + } + - match: { license_status: "valid" } + + - do: + license.get: {} + - match: { license.uid: "893361dc-9749-4997-93cb-802e3dofh7aa" } + - match: { license.type: "internal" } + - match: { license.status: "active" } + + - do: + xpack.info: {} + - is_true: build.hash + - is_true: build.timestamp + - is_true: license + - match: { license.uid: "893361dc-9749-4997-93cb-802e3dofh7aa" } + - match: { license.type: "internal" } + - match: { license.mode: "platinum" } + - match: { license.status: "active" } + - match: { license.expiry_date_in_millis: 1914278399999 } diff --git a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/watcher/Watcher.java b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/watcher/Watcher.java index 9ec2cee89de..2a7c4abae67 100644 --- a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/watcher/Watcher.java +++ b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/watcher/Watcher.java @@ -48,7 +48,6 @@ import org.elasticsearch.watcher.rest.action.RestGetWatchAction; import org.elasticsearch.watcher.rest.action.RestHijackOperationAction; import org.elasticsearch.watcher.rest.action.RestPutWatchAction; import org.elasticsearch.watcher.rest.action.RestWatchServiceAction; -import org.elasticsearch.watcher.rest.action.RestWatcherInfoAction; import org.elasticsearch.watcher.rest.action.RestWatcherStatsAction; import org.elasticsearch.watcher.support.WatcherIndexTemplateRegistry.TemplateConfig; import org.elasticsearch.watcher.support.clock.ClockModule; @@ -218,7 +217,6 @@ public class Watcher { module.registerRestHandler(RestPutWatchAction.class); module.registerRestHandler(RestDeleteWatchAction.class); module.registerRestHandler(RestWatcherStatsAction.class); - module.registerRestHandler(RestWatcherInfoAction.class); module.registerRestHandler(RestGetWatchAction.class); module.registerRestHandler(RestWatchServiceAction.class); module.registerRestHandler(RestAckWatchAction.class); diff --git a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/watcher/WatcherModule.java b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/watcher/WatcherModule.java index 07b8691a7cf..46cbad6864f 100644 --- a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/watcher/WatcherModule.java +++ b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/watcher/WatcherModule.java @@ -61,7 +61,7 @@ public class WatcherModule extends AbstractModule { } } - public static final Integer getHistoryIndexTemplateVersion() { + public static Integer getHistoryIndexTemplateVersion() { try (InputStream is = WatcherModule.class.getResourceAsStream(PROPERTIES_FILE)) { Properties properties = new Properties(); properties.load(is); diff --git a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/watcher/rest/action/RestWatcherInfoAction.java b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/watcher/rest/action/RestWatcherInfoAction.java deleted file mode 100644 index c23f5814af7..00000000000 --- a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/watcher/rest/action/RestWatcherInfoAction.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -package org.elasticsearch.watcher.rest.action; - -import org.elasticsearch.Build; -import org.elasticsearch.Version; -import org.elasticsearch.client.Client; -import org.elasticsearch.common.inject.Inject; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.rest.BytesRestResponse; -import org.elasticsearch.rest.RestChannel; -import org.elasticsearch.rest.RestController; -import org.elasticsearch.rest.RestRequest; -import org.elasticsearch.rest.RestResponse; -import org.elasticsearch.rest.action.support.RestBuilderListener; -import org.elasticsearch.watcher.client.WatcherClient; -import org.elasticsearch.watcher.rest.WatcherRestHandler; -import org.elasticsearch.watcher.transport.actions.stats.WatcherStatsRequest; -import org.elasticsearch.watcher.transport.actions.stats.WatcherStatsResponse; - -import static org.elasticsearch.rest.RestRequest.Method.GET; -import static org.elasticsearch.rest.RestStatus.OK; - -public class RestWatcherInfoAction extends WatcherRestHandler { - - @Inject - public RestWatcherInfoAction(Settings settings, RestController controller, Client client) { - super(settings, client); - controller.registerHandler(GET, URI_BASE, this); - } - - @Override - protected void handleRequest(RestRequest request, RestChannel restChannel, WatcherClient client) throws Exception { - client.watcherStats(new WatcherStatsRequest(), new RestBuilderListener(restChannel) { - @Override - public RestResponse buildResponse(WatcherStatsResponse watcherStatsResponse, XContentBuilder builder) throws Exception { - builder.startObject() - .startObject("version") - .field("number", Version.CURRENT.toString()) - .field("build_hash", watcherStatsResponse.getBuild().hash()) - .field("build_timestamp", watcherStatsResponse.getBuild().timestamp()) - .field("build_snapshot", Build.CURRENT.isSnapshot()) - .endObject() - .field("tagline", "You Know, for Alerts & Automation") - .endObject(); - return new BytesRestResponse(OK, builder); - - } - }); - } -} diff --git a/elasticsearch/x-pack/watcher/src/main/resources/watcher-build.properties b/elasticsearch/x-pack/watcher/src/main/resources/watcher-build.properties deleted file mode 100644 index f3ad519cedb..00000000000 --- a/elasticsearch/x-pack/watcher/src/main/resources/watcher-build.properties +++ /dev/null @@ -1,3 +0,0 @@ -version=${project.version} -hash=${buildNumber} -timestamp=${timestamp} \ No newline at end of file diff --git a/elasticsearch/x-pack/watcher/src/test/resources/rest-api-spec/api/watcher.info.json b/elasticsearch/x-pack/watcher/src/test/resources/rest-api-spec/api/watcher.info.json deleted file mode 100644 index e8c3d2ff837..00000000000 --- a/elasticsearch/x-pack/watcher/src/test/resources/rest-api-spec/api/watcher.info.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "watcher.info": { - "documentation": "http://www.elastic.co/guide/en/watcher/current/appendix-api-info.html", - "methods": [ "GET" ], - "url": { - "path": "/_watcher/", - "paths": [ "/_watcher/" ], - "parts": { - }, - "params": { - } - }, - "body": null - } -} diff --git a/elasticsearch/x-pack/watcher/src/test/resources/rest-api-spec/test/watcher/watch_info/10_basic.yaml b/elasticsearch/x-pack/watcher/src/test/resources/rest-api-spec/test/watcher/watch_info/10_basic.yaml deleted file mode 100644 index 9c24d70de76..00000000000 --- a/elasticsearch/x-pack/watcher/src/test/resources/rest-api-spec/test/watcher/watch_info/10_basic.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -"Test watcher info api": - - do: {watcher.info: {}} - - is_true: version.build_hash - - is_true: version.build_timestamp