HLREST: Add x-pack-info API (#31870)

This is the first x-pack API we're adding to the high level REST client
so there is a lot to talk about here!

= Open source

The *client* for these APIs is open source. We're taking the previously
Elastic licensed files used for the `Request` and `Response` objects and
relicensing them under the Apache 2 license.

The implementation of these features is staying under the Elastic
license. This lines up with how the rest of the Elasticsearch language
clients work.

= Location of the new files

We're moving all of the `Request` and `Response` objects that we're
relicensing to the `x-pack/protocol` directory. We're adding a copy of
the Apache 2 license to the root fo the `x-pack/protocol` directory to
line up with the language in the root `LICENSE.txt` file. All files in
this directory will have the Apache 2 license header as well. We don't
want there to be any confusion. Even though the files are under the
`x-pack` directory, they are Apache 2 licensed.

We chose this particular directory layout because it keeps the X-Pack
stuff together and easier to think about.

= Location of the API in the REST client

We've been following the layout of the rest-api-spec files for other
APIs and we plan to do this for the X-Pack APIs with one exception:
we're dropping the `xpack` from the name of most of the APIs. So
`xpack.graph.explore` will become `graph().explore()` and
`xpack.license.get` will become `license().get()`.

`xpack.info` and `xpack.usage` are special here though because they
don't belong to any proper category. For now I'm just calling
`xpack.info` `xPackInfo()` and intend to call usage `xPackUsage` though
I'm not convinced that this is the final name for them. But it does get
us started.

= Jars, jars everywhere!

This change makes the `xpack:protocol` project a `compile` scoped
dependency of the `x-pack:plugin:core` and `client:rest-high-level`
projects. I intend to keep it a compile scoped dependency of
`x-pack:plugin:core` but I intend to bundle the contents of the protocol
jar into the `client:rest-high-level` jar in a follow up. This change
has grown large enough at this point.

In that followup I'll address javadoc issues as well.

= Breaking-Java

This breaks that transport client by a few classes around. We've
traditionally been ok with doing this to the transport client.
This commit is contained in:
Nik Everett 2018-07-08 11:03:56 -04:00 committed by GitHub
parent 49ba271bd8
commit fb27f3e7f0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 1429 additions and 425 deletions

View File

@ -41,6 +41,7 @@ dependencies {
compile "org.elasticsearch.plugin:aggs-matrix-stats-client:${version}"
compile "org.elasticsearch.plugin:rank-eval-client:${version}"
compile "org.elasticsearch.plugin:lang-mustache-client:${version}"
compile project(':x-pack:protocol') // TODO bundle into the jar
testCompile "org.elasticsearch.client:test:${version}"
testCompile "org.elasticsearch.test:framework:${version}"

View File

@ -104,6 +104,7 @@ import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.VersionType;
import org.elasticsearch.index.rankeval.RankEvalRequest;
import org.elasticsearch.protocol.xpack.XPackInfoRequest;
import org.elasticsearch.rest.action.search.RestSearchAction;
import org.elasticsearch.script.mustache.MultiSearchTemplateRequest;
import org.elasticsearch.script.mustache.SearchTemplateRequest;
@ -115,8 +116,10 @@ import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.util.EnumSet;
import java.util.Locale;
import java.util.StringJoiner;
import java.util.stream.Collectors;
final class RequestConverters {
static final XContentType REQUEST_BODY_CONTENT_TYPE = XContentType.JSON;
@ -1065,6 +1068,19 @@ final class RequestConverters {
return request;
}
static Request xPackInfo(XPackInfoRequest infoRequest) {
Request request = new Request(HttpGet.METHOD_NAME, "/_xpack");
if (false == infoRequest.isVerbose()) {
request.addParameter("human", "false");
}
if (false == infoRequest.getCategories().equals(EnumSet.allOf(XPackInfoRequest.Category.class))) {
request.addParameter("categories", infoRequest.getCategories().stream()
.map(c -> c.toString().toLowerCase(Locale.ROOT))
.collect(Collectors.joining(",")));
}
return request;
}
private static HttpEntity createEntity(ToXContent toXContent, XContentType xContentType) throws IOException {
BytesRef source = XContentHelper.toXContent(toXContent, xContentType, false).toBytesRef();
return new ByteArrayEntity(source.bytes, source.offset, source.length, createContentType(xContentType));

View File

@ -66,6 +66,8 @@ import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.rankeval.RankEvalRequest;
import org.elasticsearch.index.rankeval.RankEvalResponse;
import org.elasticsearch.plugins.spi.NamedXContentProvider;
import org.elasticsearch.protocol.xpack.XPackInfoRequest;
import org.elasticsearch.protocol.xpack.XPackInfoResponse;
import org.elasticsearch.rest.BytesRestResponse;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.script.mustache.MultiSearchTemplateRequest;
@ -668,7 +670,7 @@ public class RestHighLevelClient implements Closeable {
emptySet());
}
/**
* Executes a request using the Multi Search Template API.
*
@ -678,9 +680,9 @@ public class RestHighLevelClient implements Closeable {
public final MultiSearchTemplateResponse multiSearchTemplate(MultiSearchTemplateRequest multiSearchTemplateRequest,
RequestOptions options) throws IOException {
return performRequestAndParseEntity(multiSearchTemplateRequest, RequestConverters::multiSearchTemplate,
options, MultiSearchTemplateResponse::fromXContext, emptySet());
}
options, MultiSearchTemplateResponse::fromXContext, emptySet());
}
/**
* Asynchronously executes a request using the Multi Search Template API
*
@ -692,7 +694,7 @@ public class RestHighLevelClient implements Closeable {
ActionListener<MultiSearchTemplateResponse> listener) {
performRequestAsyncAndParseEntity(multiSearchTemplateRequest, RequestConverters::multiSearchTemplate,
options, MultiSearchTemplateResponse::fromXContext, listener, emptySet());
}
}
/**
* Asynchronously executes a request using the Ranking Evaluation API.
@ -792,6 +794,34 @@ public class RestHighLevelClient implements Closeable {
FieldCapabilitiesResponse::fromXContent, listener, emptySet());
}
/**
* Fetch information about X-Pack from the cluster if it is installed.
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/info-api.html">
* the docs</a> for more.
* @param request the request
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @return the response
* @throws IOException in case there is a problem sending the request or parsing back the response
*/
public XPackInfoResponse xPackInfo(XPackInfoRequest request, RequestOptions options) throws IOException {
return performRequestAndParseEntity(request, RequestConverters::xPackInfo, options,
XPackInfoResponse::fromXContent, emptySet());
}
/**
* Fetch information about X-Pack from the cluster if it is installed.
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/info-api.html">
* the docs</a> for more.
* @param request the request
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @param listener the listener to be notified upon request completion
*/
public void xPackInfoAsync(XPackInfoRequest request, RequestOptions options,
ActionListener<XPackInfoResponse> listener) {
performRequestAsyncAndParseEntity(request, RequestConverters::xPackInfo, options,
XPackInfoResponse::fromXContent, listener, emptySet());
}
protected final <Req extends ActionRequest, Resp> Resp performRequestAndParseEntity(Req request,
CheckedFunction<Req, Request, IOException> requestConverter,
RequestOptions options,

View File

@ -21,8 +21,13 @@ package org.elasticsearch.client;
import org.apache.http.client.methods.HttpGet;
import org.elasticsearch.action.main.MainResponse;
import org.elasticsearch.protocol.license.LicenseStatus;
import org.elasticsearch.protocol.xpack.XPackInfoRequest;
import org.elasticsearch.protocol.xpack.XPackInfoResponse;
import org.elasticsearch.protocol.xpack.XPackInfoResponse.FeatureSetsInfo.FeatureSet;
import java.io.IOException;
import java.util.EnumSet;
import java.util.Map;
public class PingAndInfoIT extends ESRestHighLevelClientTestCase {
@ -31,7 +36,6 @@ public class PingAndInfoIT extends ESRestHighLevelClientTestCase {
assertTrue(highLevelClient().ping(RequestOptions.DEFAULT));
}
@SuppressWarnings("unchecked")
public void testInfo() throws IOException {
MainResponse info = highLevelClient().info(RequestOptions.DEFAULT);
// compare with what the low level client outputs
@ -41,6 +45,7 @@ public class PingAndInfoIT extends ESRestHighLevelClientTestCase {
// only check node name existence, might be a different one from what was hit by low level client in multi-node cluster
assertNotNull(info.getNodeName());
@SuppressWarnings("unchecked")
Map<String, Object> versionMap = (Map<String, Object>) infoAsMap.get("version");
assertEquals(versionMap.get("build_flavor"), info.getBuild().flavor().displayName());
assertEquals(versionMap.get("build_type"), info.getBuild().type().displayName());
@ -51,4 +56,49 @@ public class PingAndInfoIT extends ESRestHighLevelClientTestCase {
assertEquals(versionMap.get("lucene_version"), info.getVersion().luceneVersion.toString());
}
public void testXPackInfo() throws IOException {
XPackInfoRequest request = new XPackInfoRequest();
request.setCategories(EnumSet.allOf(XPackInfoRequest.Category.class));
request.setVerbose(true);
XPackInfoResponse info = highLevelClient().xPackInfo(request, RequestOptions.DEFAULT);
MainResponse mainResponse = highLevelClient().info(RequestOptions.DEFAULT);
assertEquals(mainResponse.getBuild().shortHash(), info.getBuildInfo().getHash());
assertEquals("basic", info.getLicenseInfo().getType());
assertEquals("basic", info.getLicenseInfo().getMode());
assertEquals(LicenseStatus.ACTIVE, info.getLicenseInfo().getStatus());
FeatureSet graph = info.getFeatureSetsInfo().getFeatureSets().get("graph");
assertNotNull(graph.description());
assertFalse(graph.available());
assertTrue(graph.enabled());
assertNull(graph.nativeCodeInfo());
FeatureSet monitoring = info.getFeatureSetsInfo().getFeatureSets().get("monitoring");
assertNotNull(monitoring.description());
assertTrue(monitoring.available());
assertTrue(monitoring.enabled());
assertNull(monitoring.nativeCodeInfo());
FeatureSet ml = info.getFeatureSetsInfo().getFeatureSets().get("ml");
assertNotNull(ml.description());
assertFalse(ml.available());
assertTrue(ml.enabled());
assertEquals(mainResponse.getVersion().toString(),
ml.nativeCodeInfo().get("version").toString().replace("-SNAPSHOT", ""));
}
public void testXPackInfoEmptyRequest() throws IOException {
XPackInfoResponse info = highLevelClient().xPackInfo(new XPackInfoRequest(), RequestOptions.DEFAULT);
/*
* The default in the transport client is non-verbose and returning
* no categories which is the opposite of the default when you use
* the API over REST. We don't want to break the transport client
* even though it doesn't feel like a good default.
*/
assertNull(info.getBuildInfo());
assertNull(info.getLicenseInfo());
assertNull(info.getFeatureSetsInfo());
}
}

View File

@ -123,6 +123,7 @@ import org.elasticsearch.index.rankeval.RankEvalRequest;
import org.elasticsearch.index.rankeval.RankEvalSpec;
import org.elasticsearch.index.rankeval.RatedRequest;
import org.elasticsearch.index.rankeval.RestRankEvalAction;
import org.elasticsearch.protocol.xpack.XPackInfoRequest;
import org.elasticsearch.repositories.fs.FsRepository;
import org.elasticsearch.rest.action.search.RestSearchAction;
import org.elasticsearch.script.ScriptType;
@ -150,6 +151,7 @@ import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@ -2465,6 +2467,37 @@ public class RequestConvertersTests extends ESTestCase {
+ "previous requests have content-type [" + xContentType + "]", exception.getMessage());
}
public void testXPackInfo() {
XPackInfoRequest infoRequest = new XPackInfoRequest();
Map<String, String> expectedParams = new HashMap<>();
infoRequest.setVerbose(randomBoolean());
if (false == infoRequest.isVerbose()) {
expectedParams.put("human", "false");
}
int option = between(0, 2);
switch (option) {
case 0:
infoRequest.setCategories(EnumSet.allOf(XPackInfoRequest.Category.class));
break;
case 1:
infoRequest.setCategories(EnumSet.of(XPackInfoRequest.Category.FEATURES));
expectedParams.put("categories", "features");
break;
case 2:
infoRequest.setCategories(EnumSet.of(XPackInfoRequest.Category.FEATURES, XPackInfoRequest.Category.BUILD));
expectedParams.put("categories", "build,features");
break;
default:
throw new IllegalArgumentException("invalid option [" + option + "]");
}
Request request = RequestConverters.xPackInfo(infoRequest);
assertEquals(HttpGet.METHOD_NAME, request.getMethod());
assertEquals("/_xpack", request.getEndpoint());
assertNull(request.getEntity());
assertEquals(expectedParams, request.getParameters());
}
/**
* Randomize the {@link FetchSourceContext} request parameters.
*/

View File

@ -22,14 +22,24 @@ package org.elasticsearch.client.documentation;
import org.apache.http.HttpHost;
import org.elasticsearch.Build;
import org.elasticsearch.Version;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.LatchedActionListener;
import org.elasticsearch.action.main.MainResponse;
import org.elasticsearch.client.ESRestHighLevelClientTestCase;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.cluster.ClusterName;
import org.elasticsearch.protocol.xpack.XPackInfoRequest;
import org.elasticsearch.protocol.xpack.XPackInfoResponse;
import org.elasticsearch.protocol.xpack.XPackInfoResponse.BuildInfo;
import org.elasticsearch.protocol.xpack.XPackInfoResponse.FeatureSetsInfo;
import org.elasticsearch.protocol.xpack.XPackInfoResponse.LicenseInfo;
import java.io.IOException;
import java.util.EnumSet;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* Documentation for miscellaneous APIs in the high level java client.
@ -66,6 +76,59 @@ public class MiscellaneousDocumentationIT extends ESRestHighLevelClientTestCase
assertTrue(response);
}
public void testXPackInfo() throws Exception {
RestHighLevelClient client = highLevelClient();
{
//tag::x-pack-info-execute
XPackInfoRequest request = new XPackInfoRequest();
request.setVerbose(true); // <1>
request.setCategories(EnumSet.of( // <2>
XPackInfoRequest.Category.BUILD,
XPackInfoRequest.Category.LICENSE,
XPackInfoRequest.Category.FEATURES));
XPackInfoResponse response = client.xPackInfo(request, RequestOptions.DEFAULT);
//end::x-pack-info-execute
//tag::x-pack-info-response
BuildInfo build = response.getBuildInfo(); // <1>
LicenseInfo license = response.getLicenseInfo(); // <2>
assertEquals(XPackInfoResponse.BASIC_SELF_GENERATED_LICENSE_EXPIRATION_MILLIS,
license.getExpiryDate()); // <3>
FeatureSetsInfo features = response.getFeatureSetsInfo(); // <4>
//end::x-pack-info-response
assertNotNull(response.getBuildInfo());
assertNotNull(response.getLicenseInfo());
assertNotNull(response.getFeatureSetsInfo());
}
{
XPackInfoRequest request = new XPackInfoRequest();
// tag::x-pack-info-execute-listener
ActionListener<XPackInfoResponse> listener = new ActionListener<XPackInfoResponse>() {
@Override
public void onResponse(XPackInfoResponse indexResponse) {
// <1>
}
@Override
public void onFailure(Exception e) {
// <2>
}
};
// end::x-pack-info-execute-listener
// Replace the empty listener by a blocking listener in test
final CountDownLatch latch = new CountDownLatch(1);
listener = new LatchedActionListener<>(listener, latch);
// tag::x-pack-info-execute-async
client.xPackInfoAsync(request, RequestOptions.DEFAULT, listener); // <1>
// end::x-pack-info-execute-async
assertTrue(latch.await(30L, TimeUnit.SECONDS));
}
}
public void testInitializationFromClientBuilder() throws IOException {
//tag::rest-high-level-client-init
RestHighLevelClient client = new RestHighLevelClient(

View File

@ -0,0 +1,64 @@
[[java-rest-high-x-pack-info]]
=== X-Pack Info API
[[java-rest-high-x-pack-info-execution]]
==== Execution
General information about the installed {xpack} features can be retrieved
using the `xPackInfo()` method:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/MiscellaneousDocumentationIT.java[x-pack-info-execute]
--------------------------------------------------
<1> Enable verbose mode. The default is `false` but `true` will return
more information.
<2> Set the categories of information to retrieve. The the default is to
return no information which is useful for checking if {xpack} is installed
but not much else.
[[java-rest-high-x-pack-info-response]]
==== Response
The returned `XPackInfoResponse` can contain `BuildInfo`, `LicenseInfo`,
and `FeatureSetsInfo` depending on the categories requested.
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/MiscellaneousDocumentationIT.java[x-pack-info-response]
--------------------------------------------------
<1> `BuildInfo` contains the commit hash from which Elasticsearch was
built and the timestamp that the x-pack module was created.
<2> `LicenseInfo` contains the type of license that the cluster is using
and its expiration date.
<3> Basic licenses do not expire and will return this constant.
<4> `FeatureSetsInfo` contains a `Map` from the name of a feature to
information about a feature like whether or not it is available under
the current license.
[[java-rest-high-x-pack-info-async]]
==== Asynchronous Execution
This request can be executed asynchronously:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/MiscellaneousDocumentationIT.java[x-pack-info-execute-async]
--------------------------------------------------
<1> The `XPackInfoRequest` to execute and the `ActionListener` to use when
the execution completes
The asynchronous method does not block and returns immediately. Once it is
completed the `ActionListener` is called back using the `onResponse` method
if the execution successfully completed or using the `onFailure` method if
it failed.
A typical listener for `XPackInfoResponse` looks like:
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests}/MiscellaneousDocumentationIT.java[x-pack-info-execute-listener]
--------------------------------------------------
<1> Called when the execution is successfully completed. The response is
provided as an argument
<2> Called in case of failure. The raised exception is provided as an argument

View File

@ -53,9 +53,11 @@ The Java High Level REST Client supports the following Miscellaneous APIs:
* <<java-rest-high-main>>
* <<java-rest-high-ping>>
* <<java-rest-high-x-pack-info>>
include::miscellaneous/main.asciidoc[]
include::miscellaneous/ping.asciidoc[]
include::miscellaneous/x-pack-info.asciidoc[]
== Indices APIs
@ -181,4 +183,3 @@ The Java High Level REST Client supports the following Scripts APIs:
include::script/get_script.asciidoc[]
include::script/delete_script.asciidoc[]

View File

@ -411,6 +411,7 @@ public final class ObjectParser<Value, Context> extends AbstractObjectParser<Val
INT_ARRAY(START_ARRAY, VALUE_NUMBER, VALUE_STRING),
BOOLEAN_ARRAY(START_ARRAY, VALUE_BOOLEAN),
OBJECT(START_OBJECT),
OBJECT_OR_NULL(START_OBJECT, VALUE_NULL),
OBJECT_ARRAY(START_OBJECT, START_ARRAY),
OBJECT_OR_BOOLEAN(START_OBJECT, VALUE_BOOLEAN),
OBJECT_OR_STRING(START_OBJECT, VALUE_STRING),

View File

@ -12,16 +12,23 @@ subprojects {
// helper method to find the path to a module
ext.xpackModule = { String moduleName -> xpackProject("plugin:${moduleName}").path }
ext.licenseName = 'Elastic License'
ext.licenseUrl = ext.elasticLicenseUrl
project.ext.licenseFile = rootProject.file('licenses/ELASTIC-LICENSE.txt')
project.ext.noticeFile = xpackRootProject.file('NOTICE.txt')
plugins.withType(PluginBuildPlugin).whenPluginAdded {
project.esplugin.licenseFile = rootProject.file('licenses/ELASTIC-LICENSE.txt')
project.esplugin.noticeFile = xpackRootProject.file('NOTICE.txt')
}
if (project.name != 'protocol') {
tasks.withType(LicenseHeadersTask.class) {
approvedLicenses = ['Elastic License', 'Generated']
additionalLicense 'ELAST', 'Elastic License', 'Licensed under the Elastic License'
}
ext.licenseName = 'Elastic License'
ext.licenseUrl = ext.elasticLicenseUrl
project.ext.licenseFile = rootProject.file('licenses/ELASTIC-LICENSE.txt')
project.ext.noticeFile = xpackRootProject.file('NOTICE.txt')
}
}
File checkstyleSuppressions = file('dev-tools/checkstyle_suppressions.xml')
@ -34,10 +41,6 @@ subprojects {
]
}
tasks.withType(LicenseHeadersTask.class) {
approvedLicenses = ['Elastic License', 'Generated']
additionalLicense 'ELAST', 'Elastic License', 'Licensed under the Elastic License'
}
ext.projectSubstitutions += [ "org.elasticsearch.plugin:x-pack-core:${version}": xpackModule('core')]
ext.projectSubstitutions += [ "org.elasticsearch.plugin:x-pack-deprecation:${version}": xpackModule('deprecation')]
ext.projectSubstitutions += [ "org.elasticsearch.plugin:x-pack-graph:${version}": xpackModule('graph')]

View File

@ -25,6 +25,7 @@ dependencyLicenses {
dependencies {
compileOnly "org.elasticsearch:elasticsearch:${version}"
compile project(':x-pack:protocol')
compile "org.apache.httpcomponents:httpclient:${versions.httpclient}"
compile "org.apache.httpcomponents:httpcore:${versions.httpcore}"
compile "org.apache.httpcomponents:httpcore-nio:${versions.httpcore}"

View File

@ -29,6 +29,7 @@ import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.protocol.license.LicenseStatus;
/**
* Data structure for license. Use {@link Builder} to build a license.
@ -267,14 +268,14 @@ public class License implements ToXContentObject {
/**
* @return the current license's status
*/
public Status status() {
public LicenseStatus status() {
long now = System.currentTimeMillis();
if (issueDate > now) {
return Status.INVALID;
return LicenseStatus.INVALID;
} else if (expiryDate < now) {
return Status.EXPIRED;
return LicenseStatus.EXPIRED;
}
return Status.ACTIVE;
return LicenseStatus.ACTIVE;
}
private void validate() {
@ -767,41 +768,6 @@ public class License implements ToXContentObject {
}
}
public enum Status {
ACTIVE("active"),
INVALID("invalid"),
EXPIRED("expired");
private final String label;
Status(String label) {
this.label = label;
}
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 + "]");
}
}
}
/**
* Returns <code>true</code> iff the license is a production licnese
*/

View File

@ -27,6 +27,7 @@ import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.discovery.DiscoveryModule;
import org.elasticsearch.env.Environment;
import org.elasticsearch.gateway.GatewayService;
import org.elasticsearch.protocol.xpack.XPackInfoResponse;
import org.elasticsearch.watcher.ResourceWatcherService;
import org.elasticsearch.xpack.core.XPackPlugin;
import org.elasticsearch.xpack.core.XPackSettings;
@ -72,7 +73,8 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste
*/
static final TimeValue GRACE_PERIOD_DURATION = days(7);
public static final long BASIC_SELF_GENERATED_LICENSE_EXPIRATION_MILLIS = Long.MAX_VALUE - days(365).millis();
public static final long BASIC_SELF_GENERATED_LICENSE_EXPIRATION_MILLIS =
XPackInfoResponse.BASIC_SELF_GENERATED_LICENSE_EXPIRATION_MILLIS;
private final ClusterService clusterService;

View File

@ -1,298 +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.license;
import org.elasticsearch.Version;
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.io.stream.Writeable;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.xpack.core.XPackBuild;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
public class XPackInfoResponse extends ActionResponse {
@Nullable private BuildInfo buildInfo;
@Nullable private LicenseInfo licenseInfo;
@Nullable private FeatureSetsInfo featureSetsInfo;
public XPackInfoResponse() {}
public XPackInfoResponse(@Nullable BuildInfo buildInfo, @Nullable LicenseInfo licenseInfo, @Nullable FeatureSetsInfo featureSetsInfo) {
this.buildInfo = buildInfo;
this.licenseInfo = licenseInfo;
this.featureSetsInfo = featureSetsInfo;
}
/**
* @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;
}
/**
* @return The current status of the feature sets in X-Pack. Feature sets describe the features available/enabled in X-Pack.
*/
public FeatureSetsInfo getFeatureSetsInfo() {
return featureSetsInfo;
}
@Override
public void writeTo(StreamOutput out) throws IOException {
super.writeTo(out);
out.writeOptionalWriteable(buildInfo);
out.writeOptionalWriteable(licenseInfo);
out.writeOptionalWriteable(featureSetsInfo);
}
@Override
public void readFrom(StreamInput in) throws IOException {
this.buildInfo = in.readOptionalWriteable(BuildInfo::new);
this.licenseInfo = in.readOptionalWriteable(LicenseInfo::new);
this.featureSetsInfo = in.readOptionalWriteable(FeatureSetsInfo::new);
}
public static class LicenseInfo implements ToXContentObject, Writeable {
private final String uid;
private final String type;
private final String mode;
private final long expiryDate;
private final License.Status status;
public LicenseInfo(License license) {
this(license.uid(), license.type(), license.operationMode().name().toLowerCase(Locale.ROOT),
license.status(), license.expiryDate());
}
public LicenseInfo(StreamInput in) throws IOException {
this(in.readString(), in.readString(), in.readString(), License.Status.readFrom(in), in.readLong());
}
public LicenseInfo(String uid, String type, String mode, License.Status status, long expiryDate) {
this.uid = uid;
this.type = type;
this.mode = mode;
this.status = status;
this.expiryDate = expiryDate;
}
public String getUid() {
return uid;
}
public String getType() {
return type;
}
public String getMode() {
return mode;
}
public long getExpiryDate() {
return expiryDate;
}
public License.Status getStatus() {
return status;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject()
.field("uid", uid)
.field("type", type)
.field("mode", mode)
.field("status", status.label());
if (expiryDate != LicenseService.BASIC_SELF_GENERATED_LICENSE_EXPIRATION_MILLIS) {
builder.timeField("expiry_date_in_millis", "expiry_date", expiryDate);
}
return builder.endObject();
}
public void writeTo(StreamOutput out) throws IOException {
out.writeString(uid);
out.writeString(type);
out.writeString(mode);
status.writeTo(out);
out.writeLong(expiryDate);
}
}
public static class BuildInfo implements ToXContentObject, Writeable {
private final String hash;
private final String timestamp;
public BuildInfo(XPackBuild build) {
this(build.shortHash(), build.date());
}
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("date", timestamp)
.endObject();
}
public void writeTo(StreamOutput output) throws IOException {
output.writeString(hash);
output.writeString(timestamp);
}
}
public static class FeatureSetsInfo implements ToXContentObject, Writeable {
private final Map<String, FeatureSet> featureSets;
public FeatureSetsInfo(StreamInput in) throws IOException {
int size = in.readVInt();
Map<String, FeatureSet> featureSets = new HashMap<>(size);
for (int i = 0; i < size; i++) {
FeatureSet featureSet = new FeatureSet(in);
featureSets.put(featureSet.name, featureSet);
}
this.featureSets = Collections.unmodifiableMap(featureSets);
}
public FeatureSetsInfo(Set<FeatureSet> featureSets) {
Map<String, FeatureSet> map = new HashMap<>(featureSets.size());
for (FeatureSet featureSet : featureSets) {
map.put(featureSet.name, featureSet);
}
this.featureSets = Collections.unmodifiableMap(map);
}
public Map<String, FeatureSet> getFeatureSets() {
return featureSets;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
List<String> names = new ArrayList<>(this.featureSets.keySet()).stream().sorted().collect(Collectors.toList());
for (String name : names) {
builder.field(name, featureSets.get(name), params);
}
return builder.endObject();
}
public void writeTo(StreamOutput out) throws IOException {
out.writeVInt(featureSets.size());
for (FeatureSet featureSet : featureSets.values()) {
featureSet.writeTo(out);
}
}
public static class FeatureSet implements ToXContentObject, Writeable {
private final String name;
@Nullable private final String description;
private final boolean available;
private final boolean enabled;
@Nullable private final Map<String, Object> nativeCodeInfo;
public FeatureSet(StreamInput in) throws IOException {
this(in.readString(), in.readOptionalString(), in.readBoolean(), in.readBoolean(),
in.getVersion().onOrAfter(Version.V_5_4_0) ? in.readMap() : null);
}
public FeatureSet(String name, @Nullable String description, boolean available, boolean enabled,
@Nullable Map<String, Object> nativeCodeInfo) {
this.name = name;
this.description = description;
this.available = available;
this.enabled = enabled;
this.nativeCodeInfo = nativeCodeInfo;
}
public String name() {
return name;
}
@Nullable
public String description() {
return description;
}
public boolean available() {
return available;
}
public boolean enabled() {
return enabled;
}
@Nullable
public Map<String, Object> nativeCodeInfo() {
return nativeCodeInfo;
}
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
if (description != null) {
builder.field("description", description);
}
builder.field("available", available);
builder.field("enabled", enabled);
if (nativeCodeInfo != null) {
builder.field("native_code_info", nativeCodeInfo);
}
return builder.endObject();
}
public void writeTo(StreamOutput out) throws IOException {
out.writeString(name);
out.writeOptionalString(description);
out.writeBoolean(available);
out.writeBoolean(enabled);
if (out.getVersion().onOrAfter(Version.V_5_4_0)) {
out.writeMap(nativeCodeInfo);
}
}
}
}
}

View File

@ -9,9 +9,9 @@ import org.elasticsearch.action.ActionListener;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.settings.SecureString;
import org.elasticsearch.license.LicensingClient;
import org.elasticsearch.license.XPackInfoResponse;
import org.elasticsearch.protocol.xpack.XPackInfoRequest;
import org.elasticsearch.protocol.xpack.XPackInfoResponse;
import org.elasticsearch.xpack.core.action.XPackInfoAction;
import org.elasticsearch.xpack.core.action.XPackInfoRequest;
import org.elasticsearch.xpack.core.action.XPackInfoRequestBuilder;
import org.elasticsearch.xpack.core.ml.client.MachineLearningClient;
import org.elasticsearch.xpack.core.monitoring.client.MonitoringClient;

View File

@ -12,14 +12,16 @@ import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.license.License;
import org.elasticsearch.license.LicenseService;
import org.elasticsearch.license.XPackInfoResponse;
import org.elasticsearch.license.XPackInfoResponse.FeatureSetsInfo.FeatureSet;
import org.elasticsearch.license.XPackInfoResponse.LicenseInfo;
import org.elasticsearch.protocol.xpack.XPackInfoRequest;
import org.elasticsearch.protocol.xpack.XPackInfoResponse;
import org.elasticsearch.protocol.xpack.XPackInfoResponse.FeatureSetsInfo.FeatureSet;
import org.elasticsearch.protocol.xpack.XPackInfoResponse.LicenseInfo;
import org.elasticsearch.tasks.Task;
import org.elasticsearch.transport.TransportService;
import org.elasticsearch.xpack.core.XPackBuild;
import org.elasticsearch.xpack.core.XPackFeatureSet;
import java.util.Locale;
import java.util.Set;
import java.util.stream.Collectors;
@ -43,14 +45,15 @@ public class TransportXPackInfoAction extends HandledTransportAction<XPackInfoRe
XPackInfoResponse.BuildInfo buildInfo = null;
if (request.getCategories().contains(XPackInfoRequest.Category.BUILD)) {
buildInfo = new XPackInfoResponse.BuildInfo(XPackBuild.CURRENT);
buildInfo = new XPackInfoResponse.BuildInfo(XPackBuild.CURRENT.shortHash(), XPackBuild.CURRENT.date());
}
LicenseInfo licenseInfo = null;
if (request.getCategories().contains(XPackInfoRequest.Category.LICENSE)) {
License license = licenseService.getLicense();
if (license != null) {
licenseInfo = new LicenseInfo(license);
licenseInfo = new LicenseInfo(license.uid(), license.type(), license.operationMode().name().toLowerCase(Locale.ROOT),
license.status(), license.expiryDate());
}
}

View File

@ -6,7 +6,7 @@
package org.elasticsearch.xpack.core.action;
import org.elasticsearch.action.Action;
import org.elasticsearch.license.XPackInfoResponse;
import org.elasticsearch.protocol.xpack.XPackInfoResponse;
public class XPackInfoAction extends Action<XPackInfoResponse> {

View File

@ -7,7 +7,8 @@ package org.elasticsearch.xpack.core.action;
import org.elasticsearch.action.ActionRequestBuilder;
import org.elasticsearch.client.ElasticsearchClient;
import org.elasticsearch.license.XPackInfoResponse;
import org.elasticsearch.protocol.xpack.XPackInfoRequest;
import org.elasticsearch.protocol.xpack.XPackInfoResponse;
import java.util.EnumSet;

View File

@ -6,15 +6,11 @@
package org.elasticsearch.xpack.core.rest.action;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.license.XPackInfoResponse;
import org.elasticsearch.rest.BytesRestResponse;
import org.elasticsearch.protocol.xpack.XPackInfoRequest;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.rest.RestResponse;
import org.elasticsearch.rest.action.RestBuilderListener;
import org.elasticsearch.rest.action.RestToXContentListener;
import org.elasticsearch.xpack.core.XPackClient;
import org.elasticsearch.xpack.core.action.XPackInfoRequest;
import org.elasticsearch.xpack.core.rest.XPackRestHandler;
import java.io.IOException;
@ -22,7 +18,6 @@ import java.util.EnumSet;
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 {
public RestXPackInfoAction(Settings settings, RestController controller) {
@ -48,36 +43,6 @@ public class RestXPackInfoAction extends XPackRestHandler {
client.prepareInfo()
.setVerbose(verbose)
.setCategories(categories)
.execute(new RestBuilderListener<XPackInfoResponse>(channel) {
@Override
public RestResponse buildResponse(XPackInfoResponse infoResponse, XContentBuilder builder) throws Exception {
builder.startObject();
if (infoResponse.getBuildInfo() != null) {
builder.field("build", infoResponse.getBuildInfo(), request);
}
if (infoResponse.getLicenseInfo() != null) {
builder.field("license", infoResponse.getLicenseInfo(), request);
} else if (categories.contains(XPackInfoRequest.Category.LICENSE)) {
// if the user requested the license info, and there is no license, we should send
// back an explicit null value (indicating there is no license). This is different
// than not adding the license info at all
builder.nullField("license");
}
if (infoResponse.getFeatureSetsInfo() != null) {
builder.field("features", infoResponse.getFeatureSetsInfo(), request);
}
if (verbose) {
builder.field("tagline", "You know, for X");
}
builder.endObject();
return new BytesRestResponse(OK, builder);
}
});
.execute(new RestToXContentListener<>(channel));
}
}

View File

@ -10,8 +10,10 @@ import org.elasticsearch.action.support.ActionFilters;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.license.License;
import org.elasticsearch.license.LicenseService;
import org.elasticsearch.license.XPackInfoResponse;
import org.elasticsearch.license.XPackInfoResponse.FeatureSetsInfo.FeatureSet;
import org.elasticsearch.protocol.license.LicenseStatus;
import org.elasticsearch.protocol.xpack.XPackInfoRequest;
import org.elasticsearch.protocol.xpack.XPackInfoResponse;
import org.elasticsearch.protocol.xpack.XPackInfoResponse.FeatureSetsInfo.FeatureSet;
import org.elasticsearch.tasks.Task;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.transport.Transport;
@ -61,7 +63,7 @@ public class TransportXPackInfoActionTests extends ESTestCase {
License license = mock(License.class);
long expiryDate = randomLong();
when(license.expiryDate()).thenReturn(expiryDate);
License.Status status = randomFrom(License.Status.values());
LicenseStatus status = randomFrom(LicenseStatus.values());
when(license.status()).thenReturn(status);
String type = randomAlphaOfLength(10);
when(license.type()).thenReturn(type);

View File

@ -13,11 +13,12 @@ import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.license.License;
import org.elasticsearch.license.XPackInfoResponse;
import org.elasticsearch.protocol.license.LicenseStatus;
import org.elasticsearch.protocol.xpack.XPackInfoRequest;
import org.elasticsearch.protocol.xpack.XPackInfoResponse;
import org.elasticsearch.transport.ActionNotFoundTransportException;
import org.elasticsearch.transport.RemoteClusterAware;
import org.elasticsearch.xpack.core.action.XPackInfoAction;
import org.elasticsearch.xpack.core.action.XPackInfoRequest;
import java.util.EnumSet;
import java.util.Iterator;
@ -136,7 +137,7 @@ public class MlRemoteLicenseChecker {
static boolean licenseSupportsML(XPackInfoResponse.LicenseInfo licenseInfo) {
License.OperationMode mode = License.OperationMode.resolve(licenseInfo.getMode());
return licenseInfo.getStatus() == License.Status.ACTIVE &&
return licenseInfo.getStatus() == LicenseStatus.ACTIVE &&
(mode == License.OperationMode.PLATINUM || mode == License.OperationMode.TRIAL);
}
@ -173,7 +174,7 @@ public class MlRemoteLicenseChecker {
public static String buildErrorMessage(RemoteClusterLicenseInfo clusterLicenseInfo) {
StringBuilder error = new StringBuilder();
if (clusterLicenseInfo.licenseInfo.getStatus() != License.Status.ACTIVE) {
if (clusterLicenseInfo.licenseInfo.getStatus() != LicenseStatus.ACTIVE) {
error.append("The license on cluster [").append(clusterLicenseInfo.clusterName)
.append("] is not active. ");
} else {

View File

@ -11,8 +11,8 @@ import org.elasticsearch.client.Client;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.license.License;
import org.elasticsearch.license.XPackInfoResponse;
import org.elasticsearch.protocol.license.LicenseStatus;
import org.elasticsearch.protocol.xpack.XPackInfoResponse;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.xpack.core.action.XPackInfoAction;
@ -66,16 +66,16 @@ public class MlRemoteLicenseCheckerTests extends ESTestCase {
public void testLicenseSupportsML() {
XPackInfoResponse.LicenseInfo licenseInfo = new XPackInfoResponse.LicenseInfo("uid", "trial", "trial",
License.Status.ACTIVE, randomNonNegativeLong());
LicenseStatus.ACTIVE, randomNonNegativeLong());
assertTrue(MlRemoteLicenseChecker.licenseSupportsML(licenseInfo));
licenseInfo = new XPackInfoResponse.LicenseInfo("uid", "trial", "trial", License.Status.EXPIRED, randomNonNegativeLong());
licenseInfo = new XPackInfoResponse.LicenseInfo("uid", "trial", "trial", LicenseStatus.EXPIRED, randomNonNegativeLong());
assertFalse(MlRemoteLicenseChecker.licenseSupportsML(licenseInfo));
licenseInfo = new XPackInfoResponse.LicenseInfo("uid", "GOLD", "GOLD", License.Status.ACTIVE, randomNonNegativeLong());
licenseInfo = new XPackInfoResponse.LicenseInfo("uid", "GOLD", "GOLD", LicenseStatus.ACTIVE, randomNonNegativeLong());
assertFalse(MlRemoteLicenseChecker.licenseSupportsML(licenseInfo));
licenseInfo = new XPackInfoResponse.LicenseInfo("uid", "PLATINUM", "PLATINUM", License.Status.ACTIVE, randomNonNegativeLong());
licenseInfo = new XPackInfoResponse.LicenseInfo("uid", "PLATINUM", "PLATINUM", LicenseStatus.ACTIVE, randomNonNegativeLong());
assertTrue(MlRemoteLicenseChecker.licenseSupportsML(licenseInfo));
}
@ -186,14 +186,14 @@ public class MlRemoteLicenseCheckerTests extends ESTestCase {
}
private XPackInfoResponse.LicenseInfo createPlatinumLicenseResponse() {
return new XPackInfoResponse.LicenseInfo("uid", "PLATINUM", "PLATINUM", License.Status.ACTIVE, randomNonNegativeLong());
return new XPackInfoResponse.LicenseInfo("uid", "PLATINUM", "PLATINUM", LicenseStatus.ACTIVE, randomNonNegativeLong());
}
private XPackInfoResponse.LicenseInfo createBasicLicenseResponse() {
return new XPackInfoResponse.LicenseInfo("uid", "BASIC", "BASIC", License.Status.ACTIVE, randomNonNegativeLong());
return new XPackInfoResponse.LicenseInfo("uid", "BASIC", "BASIC", LicenseStatus.ACTIVE, randomNonNegativeLong());
}
private XPackInfoResponse.LicenseInfo createExpiredLicenseResponse() {
return new XPackInfoResponse.LicenseInfo("uid", "PLATINUM", "PLATINUM", License.Status.EXPIRED, randomNonNegativeLong());
return new XPackInfoResponse.LicenseInfo("uid", "PLATINUM", "PLATINUM", LicenseStatus.EXPIRED, randomNonNegativeLong());
}
}

View File

@ -27,9 +27,9 @@ import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.env.Environment;
import org.elasticsearch.env.TestEnvironment;
import org.elasticsearch.license.XPackInfoResponse;
import org.elasticsearch.license.XPackInfoResponse.FeatureSetsInfo;
import org.elasticsearch.license.XPackInfoResponse.FeatureSetsInfo.FeatureSet;
import org.elasticsearch.protocol.xpack.XPackInfoResponse;
import org.elasticsearch.protocol.xpack.XPackInfoResponse.FeatureSetsInfo;
import org.elasticsearch.protocol.xpack.XPackInfoResponse.FeatureSetsInfo.FeatureSet;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.xpack.core.security.support.Validation;
import org.elasticsearch.xpack.core.security.user.ElasticUser;

202
x-pack/protocol/LICENSE.txt Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,29 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
apply plugin: 'elasticsearch.build'
description = 'Request and Response objects for x-pack that are used by the' +
' high level rest client and x-pack itself'
dependencies {
compileOnly "org.elasticsearch:elasticsearch:${version}"
testCompile "org.elasticsearch.test:framework:${version}"
}

View File

@ -0,0 +1,67 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.protocol.license;
import java.io.IOException;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Writeable;
/**
* Status of an X-Pack license.
*/
public enum LicenseStatus implements Writeable {
ACTIVE("active"),
INVALID("invalid"),
EXPIRED("expired");
private final String label;
LicenseStatus(String label) {
this.label = label;
}
public String label() {
return label;
}
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeString(label);
}
public static LicenseStatus readFrom(StreamInput in) throws IOException {
return fromString(in.readString());
}
public static LicenseStatus fromString(String value) {
switch (value) {
case "active":
return ACTIVE;
case "invalid":
return INVALID;
case "expired":
return EXPIRED;
default:
throw new IllegalArgumentException("unknown license status [" + value + "]");
}
}
}

View File

@ -0,0 +1,24 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* Request and Response objects for the default distribution's License
* APIs.
*/
package org.elasticsearch.protocol.license;

View File

@ -0,0 +1,24 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* Request and Response objects for the default distribution's Security
* APIs.
*/
package org.elasticsearch.protocol.security;

View File

@ -0,0 +1,24 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* Request and Response objects for the default distribution's Watcher
* APIs.
*/
package org.elasticsearch.protocol.watcher;

View File

@ -1,9 +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.
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.xpack.core.action;
package org.elasticsearch.protocol.xpack;
import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.ActionRequestValidationException;
@ -14,6 +27,9 @@ import java.io.IOException;
import java.util.EnumSet;
import java.util.Locale;
/**
* Fetch information about X-Pack from the cluster.
*/
public class XPackInfoRequest extends ActionRequest {
public enum Category {

View File

@ -0,0 +1,500 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.protocol.xpack;
import org.elasticsearch.Version;
import org.elasticsearch.action.ActionResponse;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Writeable;
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
import org.elasticsearch.common.xcontent.ObjectParser.ValueType;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.protocol.license.LicenseStatus;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg;
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg;
public class XPackInfoResponse extends ActionResponse implements ToXContentObject {
/**
* Value of the license's expiration time if it should never expire.
*/
public static final long BASIC_SELF_GENERATED_LICENSE_EXPIRATION_MILLIS = Long.MAX_VALUE - TimeUnit.HOURS.toMillis(24 * 365);
// TODO move this constant to License.java once we move License.java to the protocol jar
@Nullable private BuildInfo buildInfo;
@Nullable private LicenseInfo licenseInfo;
@Nullable private FeatureSetsInfo featureSetsInfo;
public XPackInfoResponse() {}
public XPackInfoResponse(@Nullable BuildInfo buildInfo, @Nullable LicenseInfo licenseInfo, @Nullable FeatureSetsInfo featureSetsInfo) {
this.buildInfo = buildInfo;
this.licenseInfo = licenseInfo;
this.featureSetsInfo = featureSetsInfo;
}
/**
* @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;
}
/**
* @return The current status of the feature sets in X-Pack. Feature sets describe the features available/enabled in X-Pack.
*/
public FeatureSetsInfo getFeatureSetsInfo() {
return featureSetsInfo;
}
@Override
public void writeTo(StreamOutput out) throws IOException {
super.writeTo(out);
out.writeOptionalWriteable(buildInfo);
out.writeOptionalWriteable(licenseInfo);
out.writeOptionalWriteable(featureSetsInfo);
}
@Override
public void readFrom(StreamInput in) throws IOException {
this.buildInfo = in.readOptionalWriteable(BuildInfo::new);
this.licenseInfo = in.readOptionalWriteable(LicenseInfo::new);
this.featureSetsInfo = in.readOptionalWriteable(FeatureSetsInfo::new);
}
@Override
public boolean equals(Object other) {
if (other == null || other.getClass() != getClass()) return false;
if (this == other) return true;
XPackInfoResponse rhs = (XPackInfoResponse) other;
return Objects.equals(buildInfo, rhs.buildInfo)
&& Objects.equals(licenseInfo, rhs.licenseInfo)
&& Objects.equals(featureSetsInfo, rhs.featureSetsInfo);
}
@Override
public int hashCode() {
return Objects.hash(buildInfo, licenseInfo, featureSetsInfo);
}
@Override
public String toString() {
return Strings.toString(this, true, false);
}
private static final ConstructingObjectParser<XPackInfoResponse, Void> PARSER = new ConstructingObjectParser<>(
"xpack_info_response", true, (a, v) -> {
BuildInfo buildInfo = (BuildInfo) a[0];
LicenseInfo licenseInfo = (LicenseInfo) a[1];
@SuppressWarnings("unchecked") // This is how constructing object parser works
List<FeatureSetsInfo.FeatureSet> featureSets = (List<FeatureSetsInfo.FeatureSet>) a[2];
FeatureSetsInfo featureSetsInfo = featureSets == null ? null : new FeatureSetsInfo(new HashSet<>(featureSets));
return new XPackInfoResponse(buildInfo, licenseInfo, featureSetsInfo);
});
static {
PARSER.declareObject(optionalConstructorArg(), BuildInfo.PARSER, new ParseField("build"));
/*
* licenseInfo is sort of "double optional" because it is
* optional but it can also be send as `null`.
*/
PARSER.declareField(optionalConstructorArg(), (p, v) -> {
if (p.currentToken() == XContentParser.Token.VALUE_NULL) {
return null;
}
return LicenseInfo.PARSER.parse(p, v);
},
new ParseField("license"), ValueType.OBJECT_OR_NULL);
PARSER.declareNamedObjects(optionalConstructorArg(),
(p, c, name) -> FeatureSetsInfo.FeatureSet.PARSER.parse(p, name),
new ParseField("features"));
}
public static XPackInfoResponse fromXContent(XContentParser parser) throws IOException {
return PARSER.parse(parser, null);
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
if (buildInfo != null) {
builder.field("build", buildInfo, params);
}
EnumSet<XPackInfoRequest.Category> categories = XPackInfoRequest.Category
.toSet(Strings.splitStringByCommaToArray(params.param("categories", "_all")));
if (licenseInfo != null) {
builder.field("license", licenseInfo, params);
} else if (categories.contains(XPackInfoRequest.Category.LICENSE)) {
// if the user requested the license info, and there is no license, we should send
// back an explicit null value (indicating there is no license). This is different
// than not adding the license info at all
builder.nullField("license");
}
if (featureSetsInfo != null) {
builder.field("features", featureSetsInfo, params);
}
if (params.paramAsBoolean("human", true)) {
builder.field("tagline", "You know, for X");
}
return builder.endObject();
}
public static class LicenseInfo implements ToXContentObject, Writeable {
private final String uid;
private final String type;
private final String mode;
private final LicenseStatus status;
private final long expiryDate;
public LicenseInfo(String uid, String type, String mode, LicenseStatus status, long expiryDate) {
this.uid = uid;
this.type = type;
this.mode = mode;
this.status = status;
this.expiryDate = expiryDate;
}
public LicenseInfo(StreamInput in) throws IOException {
this(in.readString(), in.readString(), in.readString(), LicenseStatus.readFrom(in), in.readLong());
}
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeString(uid);
out.writeString(type);
out.writeString(mode);
status.writeTo(out);
out.writeLong(expiryDate);
}
public String getUid() {
return uid;
}
public String getType() {
return type;
}
public String getMode() {
return mode;
}
public long getExpiryDate() {
return expiryDate;
}
public LicenseStatus getStatus() {
return status;
}
@Override
public boolean equals(Object other) {
if (other == null || other.getClass() != getClass()) return false;
if (this == other) return true;
LicenseInfo rhs = (LicenseInfo) other;
return Objects.equals(uid, rhs.uid)
&& Objects.equals(type, rhs.type)
&& Objects.equals(mode, rhs.mode)
&& Objects.equals(status, rhs.status)
&& expiryDate == rhs.expiryDate;
}
@Override
public int hashCode() {
return Objects.hash(uid, type, mode, status, expiryDate);
}
private static final ConstructingObjectParser<LicenseInfo, Void> PARSER = new ConstructingObjectParser<>(
"license_info", true, (a, v) -> {
String uid = (String) a[0];
String type = (String) a[1];
String mode = (String) a[2];
LicenseStatus status = LicenseStatus.fromString((String) a[3]);
Long expiryDate = (Long) a[4];
long primitiveExpiryDate = expiryDate == null ? BASIC_SELF_GENERATED_LICENSE_EXPIRATION_MILLIS : expiryDate;
return new LicenseInfo(uid, type, mode, status, primitiveExpiryDate);
});
static {
PARSER.declareString(constructorArg(), new ParseField("uid"));
PARSER.declareString(constructorArg(), new ParseField("type"));
PARSER.declareString(constructorArg(), new ParseField("mode"));
PARSER.declareString(constructorArg(), new ParseField("status"));
PARSER.declareLong(optionalConstructorArg(), new ParseField("expiry_date_in_millis"));
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject()
.field("uid", uid)
.field("type", type)
.field("mode", mode)
.field("status", status.label());
if (expiryDate != BASIC_SELF_GENERATED_LICENSE_EXPIRATION_MILLIS) {
builder.timeField("expiry_date_in_millis", "expiry_date", expiryDate);
}
return builder.endObject();
}
}
public static class BuildInfo implements ToXContentObject, Writeable {
private final String hash;
private final String timestamp;
public BuildInfo(String hash, String timestamp) {
this.hash = hash;
this.timestamp = timestamp;
}
public BuildInfo(StreamInput input) throws IOException {
this(input.readString(), input.readString());
}
@Override
public void writeTo(StreamOutput output) throws IOException {
output.writeString(hash);
output.writeString(timestamp);
}
public String getHash() {
return hash;
}
public String getTimestamp() {
return timestamp;
}
@Override
public boolean equals(Object other) {
if (other == null || other.getClass() != getClass()) return false;
if (this == other) return true;
BuildInfo rhs = (BuildInfo) other;
return Objects.equals(hash, rhs.hash)
&& Objects.equals(timestamp, rhs.timestamp);
}
@Override
public int hashCode() {
return Objects.hash(hash, timestamp);
}
private static final ConstructingObjectParser<BuildInfo, Void> PARSER = new ConstructingObjectParser<>(
"build_info", true, (a, v) -> new BuildInfo((String) a[0], (String) a[1]));
static {
PARSER.declareString(constructorArg(), new ParseField("hash"));
PARSER.declareString(constructorArg(), new ParseField("date"));
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
return builder.startObject()
.field("hash", hash)
.field("date", timestamp)
.endObject();
}
}
public static class FeatureSetsInfo implements ToXContentObject, Writeable {
private final Map<String, FeatureSet> featureSets;
public FeatureSetsInfo(Set<FeatureSet> featureSets) {
Map<String, FeatureSet> map = new HashMap<>(featureSets.size());
for (FeatureSet featureSet : featureSets) {
map.put(featureSet.name, featureSet);
}
this.featureSets = Collections.unmodifiableMap(map);
}
public FeatureSetsInfo(StreamInput in) throws IOException {
int size = in.readVInt();
Map<String, FeatureSet> featureSets = new HashMap<>(size);
for (int i = 0; i < size; i++) {
FeatureSet featureSet = new FeatureSet(in);
featureSets.put(featureSet.name, featureSet);
}
this.featureSets = Collections.unmodifiableMap(featureSets);
}
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeVInt(featureSets.size());
for (FeatureSet featureSet : featureSets.values()) {
featureSet.writeTo(out);
}
}
public Map<String, FeatureSet> getFeatureSets() {
return featureSets;
}
@Override
public boolean equals(Object other) {
if (other == null || other.getClass() != getClass()) return false;
if (this == other) return true;
FeatureSetsInfo rhs = (FeatureSetsInfo) other;
return Objects.equals(featureSets, rhs.featureSets);
}
@Override
public int hashCode() {
return Objects.hash(featureSets);
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
List<String> names = new ArrayList<>(this.featureSets.keySet()).stream().sorted().collect(Collectors.toList());
for (String name : names) {
builder.field(name, featureSets.get(name), params);
}
return builder.endObject();
}
public static class FeatureSet implements ToXContentObject, Writeable {
private final String name;
@Nullable private final String description;
private final boolean available;
private final boolean enabled;
@Nullable private final Map<String, Object> nativeCodeInfo;
public FeatureSet(String name, @Nullable String description, boolean available, boolean enabled,
@Nullable Map<String, Object> nativeCodeInfo) {
this.name = name;
this.description = description;
this.available = available;
this.enabled = enabled;
this.nativeCodeInfo = nativeCodeInfo;
}
public FeatureSet(StreamInput in) throws IOException {
this(in.readString(), in.readOptionalString(), in.readBoolean(), in.readBoolean(),
in.getVersion().onOrAfter(Version.V_5_4_0) ? in.readMap() : null);
}
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeString(name);
out.writeOptionalString(description);
out.writeBoolean(available);
out.writeBoolean(enabled);
if (out.getVersion().onOrAfter(Version.V_5_4_0)) {
out.writeMap(nativeCodeInfo);
}
}
public String name() {
return name;
}
@Nullable
public String description() {
return description;
}
public boolean available() {
return available;
}
public boolean enabled() {
return enabled;
}
@Nullable
public Map<String, Object> nativeCodeInfo() {
return nativeCodeInfo;
}
@Override
public boolean equals(Object other) {
if (other == null || other.getClass() != getClass()) return false;
if (this == other) return true;
FeatureSet rhs = (FeatureSet) other;
return Objects.equals(name, rhs.name)
&& Objects.equals(description, rhs.description)
&& available == rhs.available
&& enabled == rhs.enabled
&& Objects.equals(nativeCodeInfo, rhs.nativeCodeInfo);
}
@Override
public int hashCode() {
return Objects.hash(name, description, available, enabled, nativeCodeInfo);
}
private static final ConstructingObjectParser<FeatureSet, String> PARSER = new ConstructingObjectParser<>(
"feature_set", true, (a, name) -> {
String description = (String) a[0];
boolean available = (Boolean) a[1];
boolean enabled = (Boolean) a[2];
@SuppressWarnings("unchecked") // Matches up with declaration below
Map<String, Object> nativeCodeInfo = (Map<String, Object>) a[3];
return new FeatureSet(name, description, available, enabled, nativeCodeInfo);
});
static {
PARSER.declareString(optionalConstructorArg(), new ParseField("description"));
PARSER.declareBoolean(constructorArg(), new ParseField("available"));
PARSER.declareBoolean(constructorArg(), new ParseField("enabled"));
PARSER.declareObject(optionalConstructorArg(), (p, name) -> p.map(), new ParseField("native_code_info"));
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
if (description != null) {
builder.field("description", description);
}
builder.field("available", available);
builder.field("enabled", enabled);
if (nativeCodeInfo != null) {
builder.field("native_code_info", nativeCodeInfo);
}
return builder.endObject();
}
}
}
}

View File

@ -0,0 +1,23 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* Request and Response objects for miscellaneous X-Pack APIs.
*/
package org.elasticsearch.protocol.xpack;

View File

@ -0,0 +1,30 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.protocol.license;
import java.io.IOException;
import org.elasticsearch.test.ESTestCase;
public class LicenseStatusTests extends ESTestCase {
public void testSerialization() throws IOException {
LicenseStatus status = randomFrom(LicenseStatus.values());
assertSame(status, copyWriteable(status, writableRegistry(), LicenseStatus::readFrom));
}
}

View File

@ -0,0 +1,160 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.protocol.xpack;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.protocol.license.LicenseStatus;
import org.elasticsearch.protocol.xpack.XPackInfoResponse.BuildInfo;
import org.elasticsearch.protocol.xpack.XPackInfoResponse.LicenseInfo;
import org.elasticsearch.protocol.xpack.XPackInfoResponse.FeatureSetsInfo;
import org.elasticsearch.protocol.xpack.XPackInfoResponse.FeatureSetsInfo.FeatureSet;
import org.elasticsearch.test.AbstractStreamableXContentTestCase;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import java.io.IOException;
public class XPackInfoResponseTests extends AbstractStreamableXContentTestCase<XPackInfoResponse> {
@Override
protected XPackInfoResponse doParseInstance(XContentParser parser) throws IOException {
return XPackInfoResponse.fromXContent(parser);
}
@Override
protected XPackInfoResponse createBlankInstance() {
return new XPackInfoResponse();
}
@Override
protected Predicate<String> getRandomFieldsExcludeFilter() {
return path -> path.equals("features")
|| (path.startsWith("features") && path.endsWith("native_code_info"));
}
@Override
protected ToXContent.Params getToXContentParams() {
Map<String, String> params = new HashMap<>();
if (randomBoolean()) {
params.put("human", randomBoolean() ? "true" : "false");
}
if (randomBoolean()) {
params.put("categories", "_none");
}
return new ToXContent.MapParams(params);
}
@Override
protected XPackInfoResponse createTestInstance() {
return new XPackInfoResponse(
randomBoolean() ? null : randomBuildInfo(),
randomBoolean() ? null : randomLicenseInfo(),
randomBoolean() ? null : randomFeatureSetsInfo());
}
@Override
protected XPackInfoResponse mutateInstance(XPackInfoResponse response) {
@SuppressWarnings("unchecked")
Function<XPackInfoResponse, XPackInfoResponse> mutator = randomFrom(
r -> new XPackInfoResponse(
mutateBuildInfo(r.getBuildInfo()),
r.getLicenseInfo(),
r.getFeatureSetsInfo()),
r -> new XPackInfoResponse(
r.getBuildInfo(),
mutateLicenseInfo(r.getLicenseInfo()),
r.getFeatureSetsInfo()),
r -> new XPackInfoResponse(
r.getBuildInfo(),
r.getLicenseInfo(),
mutateFeatureSetsInfo(r.getFeatureSetsInfo())));
return mutator.apply(response);
}
private BuildInfo randomBuildInfo() {
return new BuildInfo(
randomAlphaOfLength(10),
randomAlphaOfLength(15));
}
private BuildInfo mutateBuildInfo(BuildInfo buildInfo) {
if (buildInfo == null) {
return randomBuildInfo();
}
return null;
}
private LicenseInfo randomLicenseInfo() {
return new LicenseInfo(
randomAlphaOfLength(10),
randomAlphaOfLength(4),
randomAlphaOfLength(5),
randomFrom(LicenseStatus.values()),
randomLong());
}
private LicenseInfo mutateLicenseInfo(LicenseInfo licenseInfo) {
if (licenseInfo == null) {
return randomLicenseInfo();
}
return null;
}
private FeatureSetsInfo randomFeatureSetsInfo() {
int size = between(0, 10);
Set<FeatureSet> featureSets = new HashSet<>(size);
while (featureSets.size() < size) {
featureSets.add(randomFeatureSet());
}
return new FeatureSetsInfo(featureSets);
}
private FeatureSetsInfo mutateFeatureSetsInfo(FeatureSetsInfo featureSetsInfo) {
if (featureSetsInfo == null) {
return randomFeatureSetsInfo();
}
return null;
}
private FeatureSet randomFeatureSet() {
return new FeatureSet(
randomAlphaOfLength(5),
randomBoolean() ? null : randomAlphaOfLength(20),
randomBoolean(),
randomBoolean(),
randomNativeCodeInfo());
}
private Map<String, Object> randomNativeCodeInfo() {
if (randomBoolean()) {
return null;
}
int size = between(0, 10);
Map<String, Object> nativeCodeInfo = new HashMap<>(size);
while (nativeCodeInfo.size() < size) {
nativeCodeInfo.put(randomAlphaOfLength(5), randomAlphaOfLength(5));
}
return nativeCodeInfo;
}
}