Rest HL client: Add put license action (#32214)
In the HL REST client we replace the License object with a string, because of complexity of this class. It is also not really needed on the client side since end-users are not interacting with the license besides passing it as a string to the server. Relates #29827
This commit is contained in:
parent
c1cc0cef61
commit
29c802f88e
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* 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.client;
|
||||
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.protocol.xpack.license.PutLicenseRequest;
|
||||
import org.elasticsearch.protocol.xpack.license.PutLicenseResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static java.util.Collections.emptySet;
|
||||
|
||||
/**
|
||||
* A wrapper for the {@link RestHighLevelClient} that provides methods for
|
||||
* accessing the Elastic License-related methods
|
||||
* <p>
|
||||
* See the <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/licensing-apis.html">
|
||||
* X-Pack Licensing APIs on elastic.co</a> for more information.
|
||||
*/
|
||||
public class LicenseClient {
|
||||
|
||||
private final RestHighLevelClient restHighLevelClient;
|
||||
|
||||
LicenseClient(RestHighLevelClient restHighLevelClient) {
|
||||
this.restHighLevelClient = restHighLevelClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates license for the cluster.
|
||||
* @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 PutLicenseResponse putLicense(PutLicenseRequest request, RequestOptions options) throws IOException {
|
||||
return restHighLevelClient.performRequestAndParseEntity(request, RequestConverters::putLicense, options,
|
||||
PutLicenseResponse::fromXContent, emptySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously updates license for the cluster cluster.
|
||||
* @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 putLicenseAsync(PutLicenseRequest request, RequestOptions options, ActionListener<PutLicenseResponse> listener) {
|
||||
restHighLevelClient.performRequestAsyncAndParseEntity(request, RequestConverters::putLicense, options,
|
||||
PutLicenseResponse::fromXContent, listener, emptySet());
|
||||
}
|
||||
|
||||
}
|
|
@ -109,6 +109,7 @@ import org.elasticsearch.index.rankeval.RankEvalRequest;
|
|||
import org.elasticsearch.protocol.xpack.XPackInfoRequest;
|
||||
import org.elasticsearch.protocol.xpack.watcher.PutWatchRequest;
|
||||
import org.elasticsearch.protocol.xpack.XPackUsageRequest;
|
||||
import org.elasticsearch.protocol.xpack.license.PutLicenseRequest;
|
||||
import org.elasticsearch.rest.action.search.RestSearchAction;
|
||||
import org.elasticsearch.script.mustache.MultiSearchTemplateRequest;
|
||||
import org.elasticsearch.script.mustache.SearchTemplateRequest;
|
||||
|
@ -1139,6 +1140,18 @@ final class RequestConverters {
|
|||
return request;
|
||||
}
|
||||
|
||||
static Request putLicense(PutLicenseRequest putLicenseRequest) {
|
||||
Request request = new Request(HttpPut.METHOD_NAME, "/_xpack/license");
|
||||
Params parameters = new Params(request);
|
||||
parameters.withTimeout(putLicenseRequest.timeout());
|
||||
parameters.withMasterTimeout(putLicenseRequest.masterNodeTimeout());
|
||||
if (putLicenseRequest.isAcknowledge()) {
|
||||
parameters.putParam("acknowledge", "true");
|
||||
}
|
||||
request.setJsonEntity(putLicenseRequest.getLicenseDefinition());
|
||||
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));
|
||||
|
|
|
@ -42,10 +42,12 @@ public final class XPackClient {
|
|||
|
||||
private final RestHighLevelClient restHighLevelClient;
|
||||
private final WatcherClient watcherClient;
|
||||
private final LicenseClient licenseClient;
|
||||
|
||||
XPackClient(RestHighLevelClient restHighLevelClient) {
|
||||
this.restHighLevelClient = restHighLevelClient;
|
||||
this.watcherClient = new WatcherClient(restHighLevelClient);
|
||||
this.licenseClient = new LicenseClient(restHighLevelClient);
|
||||
}
|
||||
|
||||
public WatcherClient watcher() {
|
||||
|
@ -100,4 +102,15 @@ public final class XPackClient {
|
|||
restHighLevelClient.performRequestAsyncAndParseEntity(request, RequestConverters::xpackUsage, options,
|
||||
XPackUsageResponse::fromXContent, listener, emptySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* A wrapper for the {@link RestHighLevelClient} that provides methods for
|
||||
* accessing the Elastic Licensing APIs.
|
||||
* <p>
|
||||
* See the <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/licensing-apis.html">
|
||||
* X-Pack APIs on elastic.co</a> for more information.
|
||||
*/
|
||||
public LicenseClient license() {
|
||||
return licenseClient;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* 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.client.documentation;
|
||||
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.action.LatchedActionListener;
|
||||
import org.elasticsearch.client.ESRestHighLevelClientTestCase;
|
||||
import org.elasticsearch.client.RequestOptions;
|
||||
import org.elasticsearch.client.RestHighLevelClient;
|
||||
import org.elasticsearch.protocol.xpack.license.LicensesStatus;
|
||||
import org.elasticsearch.protocol.xpack.license.PutLicenseRequest;
|
||||
import org.elasticsearch.protocol.xpack.license.PutLicenseResponse;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
|
||||
/**
|
||||
* Documentation for Licensing APIs in the high level java client.
|
||||
* Code wrapped in {@code tag} and {@code end} tags is included in the docs.
|
||||
*/
|
||||
public class LicensingDocumentationIT extends ESRestHighLevelClientTestCase {
|
||||
|
||||
public void testPutLicense() throws Exception {
|
||||
RestHighLevelClient client = highLevelClient();
|
||||
String license = "{\"license\": {\"uid\":\"893361dc-9749-4997-93cb-802e3d7fa4a8\",\"type\":\"gold\"," +
|
||||
"\"issue_date_in_millis\":1411948800000,\"expiry_date_in_millis\":1914278399999,\"max_nodes\":1,\"issued_to\":\"issued_to\"," +
|
||||
"\"issuer\":\"issuer\",\"signature\":\"AAAAAgAAAA3U8+YmnvwC+CWsV/mRAAABmC9ZN0hjZDBGYnVyRXpCOW5Bb3FjZDAxOWpSbTVoMVZwUzRxVk1PSm" +
|
||||
"kxakxZdW5IMlhlTHNoN1N2MXMvRFk4d3JTZEx3R3RRZ0pzU3lobWJKZnQvSEFva0ppTHBkWkprZWZSQi9iNmRQNkw1SlpLN0lDalZCS095MXRGN1lIZlpYcVVTTn" +
|
||||
"FrcTE2dzhJZmZrdFQrN3JQeGwxb0U0MXZ0dDJHSERiZTVLOHNzSDByWnpoZEphZHBEZjUrTVBxRENNSXNsWWJjZllaODdzVmEzUjNiWktNWGM5TUhQV2plaUo4Q1" +
|
||||
"JOUml4MXNuL0pSOEhQaVB2azhmUk9QVzhFeTFoM1Q0RnJXSG53MWk2K055c28zSmRnVkF1b2JSQkFLV2VXUmVHNDZ2R3o2VE1qbVNQS2lxOHN5bUErZlNIWkZSVm" +
|
||||
"ZIWEtaSU9wTTJENDVvT1NCYklacUYyK2FwRW9xa0t6dldMbmMzSGtQc3FWOTgzZ3ZUcXMvQkt2RUZwMFJnZzlvL2d2bDRWUzh6UG5pdENGWFRreXNKNkE9PQAAAQ" +
|
||||
"Be8GfzDm6T537Iuuvjetb3xK5dvg0K5NQapv+rczWcQFxgCuzbF8plkgetP1aAGZP4uRESDQPMlOCsx4d0UqqAm9f7GbBQ3l93P+PogInPFeEH9NvOmaAQovmxVM" +
|
||||
"9SE6DsDqlX4cXSO+bgWpXPTd2LmpoQc1fXd6BZ8GeuyYpVHVKp9hVU0tAYjw6HzYOE7+zuO1oJYOxElqy66AnIfkvHrvni+flym3tE7tDTgsDRaz7W3iBhaqiSnt" +
|
||||
"EqabEkvHdPHQdSR99XGaEvnHO1paK01/35iZF6OXHsF7CCj+558GRXiVxzueOe7TsGSSt8g7YjZwV9bRCyU7oB4B/nidgI\"}}";
|
||||
{
|
||||
//tag::put-license-execute
|
||||
PutLicenseRequest request = new PutLicenseRequest();
|
||||
request.setLicenseDefinition(license); // <1>
|
||||
request.setAcknowledge(false); // <2>
|
||||
|
||||
PutLicenseResponse response = client.xpack().license().putLicense(request, RequestOptions.DEFAULT);
|
||||
//end::put-license-execute
|
||||
|
||||
//tag::put-license-response
|
||||
LicensesStatus status = response.status(); // <1>
|
||||
assertEquals(status, LicensesStatus.VALID); // <2>
|
||||
boolean acknowledged = response.isAcknowledged(); // <3>
|
||||
String acknowledgeHeader = response.acknowledgeHeader(); // <4>
|
||||
Map<String, String[]> acknowledgeMessages = response.acknowledgeMessages(); // <5>
|
||||
//end::put-license-response
|
||||
|
||||
assertFalse(acknowledged); // Should fail because we are trying to downgrade from platinum trial to gold
|
||||
assertThat(acknowledgeHeader, startsWith("This license update requires acknowledgement."));
|
||||
assertThat(acknowledgeMessages.keySet(), not(hasSize(0)));
|
||||
}
|
||||
{
|
||||
PutLicenseRequest request = new PutLicenseRequest();
|
||||
// tag::put-license-execute-listener
|
||||
ActionListener<PutLicenseResponse> listener = new ActionListener<PutLicenseResponse>() {
|
||||
@Override
|
||||
public void onResponse(PutLicenseResponse indexResponse) {
|
||||
// <1>
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Exception e) {
|
||||
// <2>
|
||||
}
|
||||
};
|
||||
// end::put-license-execute-listener
|
||||
|
||||
// Replace the empty listener by a blocking listener in test
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
listener = new LatchedActionListener<>(listener, latch);
|
||||
|
||||
// tag::put-license-execute-async
|
||||
client.xpack().license().putLicenseAsync(
|
||||
request, RequestOptions.DEFAULT, listener); // <1>
|
||||
// end::put-license-execute-async
|
||||
|
||||
assertTrue(latch.await(30L, TimeUnit.SECONDS));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
[[java-rest-high-put-license]]
|
||||
=== Update License
|
||||
|
||||
[[java-rest-high-put-license-execution]]
|
||||
==== Execution
|
||||
|
||||
The license can be added or updated using the `putLicense()` method:
|
||||
|
||||
["source","java",subs="attributes,callouts,macros"]
|
||||
--------------------------------------------------
|
||||
include-tagged::{doc-tests}/LicensingDocumentationIT.java[put-license-execute]
|
||||
--------------------------------------------------
|
||||
<1> 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.
|
||||
<2> A JSON document containing the license information.
|
||||
|
||||
[[java-rest-high-put-license-response]]
|
||||
==== Response
|
||||
|
||||
The returned `PutLicenseResponse` contains the `LicensesStatus`,
|
||||
`acknowledged` flag and possible acknowledge messages. The acknowledge messages
|
||||
are present if you previously had a license with more features than one you
|
||||
are trying to update and you didn't set the `acknowledge` flag to `true`. In this case
|
||||
you need to display the messages to the end user and if they agree, resubmit the
|
||||
license with the `acknowledge` flag set to `true`. Please note that the request will
|
||||
still return a 200 return code even if requires an acknowledgement. So, it is
|
||||
necessary to check the `acknowledged` flag.
|
||||
|
||||
["source","java",subs="attributes,callouts,macros"]
|
||||
--------------------------------------------------
|
||||
include-tagged::{doc-tests}/LicensingDocumentationIT.java[put-license-response]
|
||||
--------------------------------------------------
|
||||
<1> The status of the license
|
||||
<2> Make sure that the license is valid.
|
||||
<3> Check the acknowledge flag.
|
||||
<4> It should be true if license is acknowledge.
|
||||
<5> Otherwise we can see the acknowledge messages in `acknowledgeHeader()`
|
||||
<6> and check component-specific messages in `acknowledgeMessages()`.
|
||||
|
||||
[[java-rest-high-put-license-async]]
|
||||
==== Asynchronous Execution
|
||||
|
||||
This request can be executed asynchronously:
|
||||
|
||||
["source","java",subs="attributes,callouts,macros"]
|
||||
--------------------------------------------------
|
||||
include-tagged::{doc-tests}/LicensingDocumentationIT.java[put-license-execute-async]
|
||||
--------------------------------------------------
|
||||
<1> The `PutLicenseRequest` 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 `PutLicenseResponse` looks like:
|
||||
|
||||
["source","java",subs="attributes,callouts,macros"]
|
||||
--------------------------------------------------
|
||||
include-tagged::{doc-tests}/LicensingDocumentationIT.java[put-license-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
|
|
@ -186,3 +186,12 @@ The Java High Level REST Client supports the following Scripts APIs:
|
|||
|
||||
include::script/get_script.asciidoc[]
|
||||
include::script/delete_script.asciidoc[]
|
||||
|
||||
|
||||
== Licensing APIs
|
||||
|
||||
The Java High Level REST Client supports the following Licensing APIs:
|
||||
|
||||
* <<java-rest-high-put-license>>
|
||||
|
||||
include::licensing/put-license.asciidoc[]
|
||||
|
|
|
@ -28,6 +28,8 @@ import org.elasticsearch.discovery.DiscoveryModule;
|
|||
import org.elasticsearch.env.Environment;
|
||||
import org.elasticsearch.gateway.GatewayService;
|
||||
import org.elasticsearch.protocol.xpack.XPackInfoResponse;
|
||||
import org.elasticsearch.protocol.xpack.license.LicensesStatus;
|
||||
import org.elasticsearch.protocol.xpack.license.PutLicenseResponse;
|
||||
import org.elasticsearch.watcher.ResourceWatcherService;
|
||||
import org.elasticsearch.xpack.core.XPackPlugin;
|
||||
import org.elasticsearch.xpack.core.XPackSettings;
|
||||
|
|
|
@ -1,34 +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;
|
||||
|
||||
public enum LicensesStatus {
|
||||
VALID((byte) 0),
|
||||
INVALID((byte) 1),
|
||||
EXPIRED((byte) 2);
|
||||
|
||||
private final byte id;
|
||||
|
||||
LicensesStatus(byte id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public int id() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public static LicensesStatus fromId(int id) {
|
||||
if (id == 0) {
|
||||
return VALID;
|
||||
} else if (id == 1) {
|
||||
return INVALID;
|
||||
} else if (id == 2) {
|
||||
return EXPIRED;
|
||||
} else {
|
||||
throw new IllegalStateException("no valid LicensesStatus for id=" + id);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ package org.elasticsearch.license;
|
|||
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.client.ElasticsearchClient;
|
||||
import org.elasticsearch.protocol.xpack.license.PutLicenseResponse;
|
||||
|
||||
public class LicensingClient {
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
package org.elasticsearch.license;
|
||||
|
||||
import org.elasticsearch.action.Action;
|
||||
import org.elasticsearch.protocol.xpack.license.PutLicenseResponse;
|
||||
|
||||
public class PutLicenseAction extends Action<PutLicenseResponse> {
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import org.elasticsearch.action.support.master.AcknowledgedRequestBuilder;
|
|||
import org.elasticsearch.client.ElasticsearchClient;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.protocol.xpack.license.PutLicenseResponse;
|
||||
|
||||
/**
|
||||
* Register license request builder
|
||||
|
|
|
@ -1,119 +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.action.support.master.AcknowledgedResponse;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class PutLicenseResponse extends AcknowledgedResponse {
|
||||
|
||||
private LicensesStatus status;
|
||||
private Map<String, String[]> acknowledgeMessages;
|
||||
private String acknowledgeHeader;
|
||||
|
||||
PutLicenseResponse() {
|
||||
}
|
||||
|
||||
public PutLicenseResponse(boolean acknowledged, LicensesStatus status) {
|
||||
this(acknowledged, status, null, Collections.<String, String[]>emptyMap());
|
||||
}
|
||||
|
||||
public PutLicenseResponse(boolean acknowledged, LicensesStatus status, String acknowledgeHeader,
|
||||
Map<String, String[]> acknowledgeMessages) {
|
||||
super(acknowledged);
|
||||
this.status = status;
|
||||
this.acknowledgeHeader = acknowledgeHeader;
|
||||
this.acknowledgeMessages = acknowledgeMessages;
|
||||
}
|
||||
|
||||
public LicensesStatus status() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public Map<String, String[]> acknowledgeMessages() {
|
||||
return acknowledgeMessages;
|
||||
}
|
||||
|
||||
public String acknowledgeHeader() {
|
||||
return acknowledgeHeader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFrom(StreamInput in) throws IOException {
|
||||
super.readFrom(in);
|
||||
status = LicensesStatus.fromId(in.readVInt());
|
||||
acknowledgeHeader = in.readOptionalString();
|
||||
int size = in.readVInt();
|
||||
Map<String, String[]> acknowledgeMessages = new HashMap<>(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
String feature = in.readString();
|
||||
int nMessages = in.readVInt();
|
||||
String[] messages = new String[nMessages];
|
||||
for (int j = 0; j < nMessages; j++) {
|
||||
messages[j] = in.readString();
|
||||
}
|
||||
acknowledgeMessages.put(feature, messages);
|
||||
}
|
||||
this.acknowledgeMessages = acknowledgeMessages;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
super.writeTo(out);
|
||||
out.writeVInt(status.id());
|
||||
out.writeOptionalString(acknowledgeHeader);
|
||||
out.writeVInt(acknowledgeMessages.size());
|
||||
for (Map.Entry<String, String[]> entry : acknowledgeMessages.entrySet()) {
|
||||
out.writeString(entry.getKey());
|
||||
out.writeVInt(entry.getValue().length);
|
||||
for (String message : entry.getValue()) {
|
||||
out.writeString(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addCustomFields(XContentBuilder builder, Params params) throws IOException {
|
||||
switch (status) {
|
||||
case VALID:
|
||||
builder.field("license_status", "valid");
|
||||
break;
|
||||
case INVALID:
|
||||
builder.field("license_status", "invalid");
|
||||
break;
|
||||
case EXPIRED:
|
||||
builder.field("license_status", "expired");
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("unknown status [" + status + "] found");
|
||||
}
|
||||
if (!acknowledgeMessages.isEmpty()) {
|
||||
builder.startObject("acknowledge");
|
||||
builder.field("message", acknowledgeHeader);
|
||||
for (Map.Entry<String, String[]> entry : acknowledgeMessages.entrySet()) {
|
||||
builder.startArray(entry.getKey());
|
||||
for (String message : entry.getValue()) {
|
||||
builder.value(message);
|
||||
}
|
||||
builder.endArray();
|
||||
}
|
||||
builder.endObject();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Strings.toString(this, true, true);
|
||||
}
|
||||
}
|
|
@ -16,6 +16,7 @@ import org.elasticsearch.cluster.block.ClusterBlockLevel;
|
|||
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.protocol.xpack.license.PutLicenseResponse;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.transport.TransportService;
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ import org.elasticsearch.cluster.node.DiscoveryNode;
|
|||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.transport.TransportAddress;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.protocol.xpack.license.PutLicenseResponse;
|
||||
|
||||
import java.net.InetAddress;
|
||||
|
||||
|
|
|
@ -9,6 +9,8 @@ import org.elasticsearch.action.ActionListener;
|
|||
import org.elasticsearch.cluster.ClusterStateUpdateTask;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.protocol.xpack.license.LicensesStatus;
|
||||
import org.elasticsearch.protocol.xpack.license.PutLicenseResponse;
|
||||
|
||||
import static org.elasticsearch.common.unit.TimeValue.timeValueHours;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
|
|
@ -11,6 +11,7 @@ import org.elasticsearch.cluster.service.ClusterService;
|
|||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.plugins.Plugin;
|
||||
import org.elasticsearch.protocol.xpack.license.LicensesStatus;
|
||||
import org.elasticsearch.test.ESSingleNodeTestCase;
|
||||
import org.elasticsearch.xpack.core.LocalStateCompositeXPackPlugin;
|
||||
import org.elasticsearch.xpack.core.XPackSettings;
|
||||
|
@ -145,4 +146,4 @@ public class LicensesManagerServiceTests extends ESSingleNodeTestCase {
|
|||
}
|
||||
assertThat("remove license(s) failed", success.get(), equalTo(true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,8 @@ import org.elasticsearch.common.unit.TimeValue;
|
|||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.node.Node;
|
||||
import org.elasticsearch.plugins.Plugin;
|
||||
import org.elasticsearch.protocol.xpack.license.LicensesStatus;
|
||||
import org.elasticsearch.protocol.xpack.license.PutLicenseResponse;
|
||||
import org.elasticsearch.test.ESSingleNodeTestCase;
|
||||
import org.elasticsearch.xpack.core.LocalStateCompositeXPackPlugin;
|
||||
import org.elasticsearch.xpack.core.XPackSettings;
|
||||
|
@ -230,4 +232,4 @@ public class LicensesTransportTests extends ESSingleNodeTestCase {
|
|||
assertThat(putLicenseResponse.isAcknowledged(), equalTo(true));
|
||||
assertThat(putLicenseResponse.status(), equalTo(LicensesStatus.VALID));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,103 +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.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.io.stream.BytesStreamOutput;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentHelper;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.sameInstance;
|
||||
|
||||
public class PutLicenseResponseTests extends ESTestCase {
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testSerialization() throws Exception {
|
||||
boolean acknowledged = randomBoolean();
|
||||
LicensesStatus status = randomFrom(LicensesStatus.VALID, LicensesStatus.INVALID, LicensesStatus.EXPIRED);
|
||||
Map<String, String[]> ackMessages = randomAckMessages();
|
||||
|
||||
PutLicenseResponse response = new PutLicenseResponse(acknowledged, status, "", ackMessages);
|
||||
|
||||
XContentBuilder contentBuilder = XContentFactory.jsonBuilder();
|
||||
response.toXContent(contentBuilder, ToXContent.EMPTY_PARAMS);
|
||||
|
||||
Map<String, Object> map = XContentHelper.convertToMap(BytesReference.bytes(contentBuilder), false,
|
||||
contentBuilder.contentType()).v2();
|
||||
assertThat(map.containsKey("acknowledged"), equalTo(true));
|
||||
boolean actualAcknowledged = (boolean) map.get("acknowledged");
|
||||
assertThat(actualAcknowledged, equalTo(acknowledged));
|
||||
|
||||
assertThat(map.containsKey("license_status"), equalTo(true));
|
||||
String actualStatus = (String) map.get("license_status");
|
||||
assertThat(actualStatus, equalTo(status.name().toLowerCase(Locale.ROOT)));
|
||||
|
||||
assertThat(map.containsKey("acknowledge"), equalTo(true));
|
||||
Map<String, List<String>> actualAckMessages = (Map<String, List<String>>) map.get("acknowledge");
|
||||
assertTrue(actualAckMessages.containsKey("message"));
|
||||
actualAckMessages.remove("message");
|
||||
assertThat(actualAckMessages.keySet(), equalTo(ackMessages.keySet()));
|
||||
for (Map.Entry<String, List<String>> entry : actualAckMessages.entrySet()) {
|
||||
assertArrayEquals(entry.getValue().toArray(), ackMessages.get(entry.getKey()));
|
||||
}
|
||||
}
|
||||
|
||||
public void testStreamSerialization() throws IOException {
|
||||
boolean acknowledged = randomBoolean();
|
||||
LicensesStatus status = randomFrom(LicensesStatus.VALID, LicensesStatus.INVALID, LicensesStatus.EXPIRED);
|
||||
Map<String, String[]> ackMessages = randomAckMessages();
|
||||
|
||||
// write the steam so that we can attempt to read it back
|
||||
BytesStreamOutput output = new BytesStreamOutput();
|
||||
|
||||
PutLicenseResponse response = new PutLicenseResponse(acknowledged, status, "", ackMessages);
|
||||
// write it out
|
||||
response.writeTo(output);
|
||||
|
||||
StreamInput input = output.bytes().streamInput();
|
||||
|
||||
// read it back in
|
||||
response.readFrom(input);
|
||||
|
||||
assertThat(response.isAcknowledged(), equalTo(acknowledged));
|
||||
assertThat(response.status(), equalTo(status));
|
||||
assertThat(response.acknowledgeMessages(), not(sameInstance(ackMessages)));
|
||||
assertThat(response.acknowledgeMessages().size(), equalTo(ackMessages.size()));
|
||||
|
||||
for (String key : ackMessages.keySet()) {
|
||||
assertArrayEquals(ackMessages.get(key), response.acknowledgeMessages().get(key));
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<String, String[]> randomAckMessages() {
|
||||
int nFeatures = randomIntBetween(1, 5);
|
||||
|
||||
Map<String, String[]> ackMessages = new HashMap<>();
|
||||
|
||||
for (int i = 0; i < nFeatures; i++) {
|
||||
String feature = randomAlphaOfLengthBetween(9, 15);
|
||||
int nMessages = randomIntBetween(1, 5);
|
||||
String[] messages = new String[nMessages];
|
||||
for (int j = 0; j < nMessages; j++) {
|
||||
messages[j] = randomAlphaOfLengthBetween(10, 30);
|
||||
}
|
||||
ackMessages.put(feature, messages);
|
||||
}
|
||||
|
||||
return ackMessages;
|
||||
}
|
||||
}
|
|
@ -20,6 +20,8 @@ import org.elasticsearch.common.xcontent.XContentBuilder;
|
|||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.license.licensor.LicenseSigner;
|
||||
import org.elasticsearch.protocol.xpack.license.LicensesStatus;
|
||||
import org.elasticsearch.protocol.xpack.license.PutLicenseResponse;
|
||||
import org.hamcrest.MatcherAssert;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
import org.junit.Assert;
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* 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.common;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Common utilities used for XPack protocol classes
|
||||
*/
|
||||
public final class ProtocolUtils {
|
||||
|
||||
/**
|
||||
* Implements equals for a map of string arrays
|
||||
*
|
||||
* The map of string arrays is used in some XPack protocol classes but does't work with equal.
|
||||
*/
|
||||
public static boolean equals(Map<String, String[]> a, Map<String, String[]> b) {
|
||||
if (a == null) {
|
||||
return b == null;
|
||||
}
|
||||
if (b == null) {
|
||||
return false;
|
||||
}
|
||||
if (a.size() != b.size()) {
|
||||
return false;
|
||||
}
|
||||
for (Map.Entry<String, String[]> entry : a.entrySet()) {
|
||||
String[] val = entry.getValue();
|
||||
String key = entry.getKey();
|
||||
if (val == null) {
|
||||
if (b.get(key) != null || b.containsKey(key) == false) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (Arrays.equals(val, b.get(key)) == false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hashCode for map of string arrays
|
||||
*
|
||||
* The map of string arrays does't work with hashCode.
|
||||
*/
|
||||
public static int hashCode(Map<String, String[]> a) {
|
||||
int hash = 0;
|
||||
for (Map.Entry<String, String[]> entry : a.entrySet())
|
||||
hash += Arrays.hashCode(entry.getValue());
|
||||
return hash;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* 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.license;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public enum LicensesStatus {
|
||||
VALID((byte) 0),
|
||||
INVALID((byte) 1),
|
||||
EXPIRED((byte) 2);
|
||||
|
||||
private final byte id;
|
||||
|
||||
LicensesStatus(byte id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public int id() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public static LicensesStatus fromId(int id) {
|
||||
if (id == 0) {
|
||||
return VALID;
|
||||
} else if (id == 1) {
|
||||
return INVALID;
|
||||
} else if (id == 2) {
|
||||
return EXPIRED;
|
||||
} else {
|
||||
throw new IllegalStateException("no valid LicensesStatus for id=" + id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.name().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
public static LicensesStatus fromString(String value) {
|
||||
switch (value) {
|
||||
case "valid":
|
||||
return VALID;
|
||||
case "invalid":
|
||||
return INVALID;
|
||||
case "expired":
|
||||
return EXPIRED;
|
||||
default:
|
||||
throw new IllegalArgumentException("unknown licenses status [" + value + "]");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* 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.license;
|
||||
|
||||
import org.elasticsearch.action.ActionRequestValidationException;
|
||||
import org.elasticsearch.action.support.master.AcknowledgedRequest;
|
||||
|
||||
public class PutLicenseRequest extends AcknowledgedRequest<PutLicenseRequest> {
|
||||
|
||||
private String licenseDefinition;
|
||||
private boolean acknowledge = false;
|
||||
|
||||
public PutLicenseRequest() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionRequestValidationException validate() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setLicenseDefinition(String licenseDefinition) {
|
||||
this.licenseDefinition = licenseDefinition;
|
||||
}
|
||||
|
||||
public String getLicenseDefinition() {
|
||||
return licenseDefinition;
|
||||
}
|
||||
|
||||
public void setAcknowledge(boolean acknowledge) {
|
||||
this.acknowledge = acknowledge;
|
||||
}
|
||||
|
||||
public boolean isAcknowledge() {
|
||||
return acknowledge;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,209 @@
|
|||
/*
|
||||
* 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.license;
|
||||
|
||||
import org.elasticsearch.action.support.master.AcknowledgedResponse;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.collect.Tuple;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParseException;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.protocol.xpack.common.ProtocolUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg;
|
||||
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg;
|
||||
|
||||
public class PutLicenseResponse extends AcknowledgedResponse {
|
||||
|
||||
private static final ConstructingObjectParser<PutLicenseResponse, Void> PARSER = new ConstructingObjectParser<>(
|
||||
"put_license_response", true, (a, v) -> {
|
||||
boolean acknowledged = (Boolean) a[0];
|
||||
LicensesStatus licensesStatus = LicensesStatus.fromString((String) a[1]);
|
||||
@SuppressWarnings("unchecked") Tuple<String, Map<String, String[]>> acknowledgements = (Tuple<String, Map<String, String[]>>) a[2];
|
||||
if (acknowledgements == null) {
|
||||
return new PutLicenseResponse(acknowledged, licensesStatus);
|
||||
} else {
|
||||
return new PutLicenseResponse(acknowledged, licensesStatus, acknowledgements.v1(), acknowledgements.v2());
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
static {
|
||||
PARSER.declareBoolean(constructorArg(), new ParseField("acknowledged"));
|
||||
PARSER.declareString(constructorArg(), new ParseField("license_status"));
|
||||
PARSER.declareObject(optionalConstructorArg(), (parser, v) -> {
|
||||
Map<String, String[]> acknowledgeMessages = new HashMap<>();
|
||||
String message = null;
|
||||
XContentParser.Token token;
|
||||
String currentFieldName = null;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else {
|
||||
if (currentFieldName == null) {
|
||||
throw new XContentParseException(parser.getTokenLocation(), "expected message header or acknowledgement");
|
||||
}
|
||||
if ("message".equals(currentFieldName)) {
|
||||
if (token != XContentParser.Token.VALUE_STRING) {
|
||||
throw new XContentParseException(parser.getTokenLocation(), "unexpected message header type");
|
||||
}
|
||||
message = parser.text();
|
||||
} else {
|
||||
if (token != XContentParser.Token.START_ARRAY) {
|
||||
throw new XContentParseException(parser.getTokenLocation(), "unexpected acknowledgement type");
|
||||
}
|
||||
List<String> acknowledgeMessagesList = new ArrayList<>();
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
|
||||
if (token != XContentParser.Token.VALUE_STRING) {
|
||||
throw new XContentParseException(parser.getTokenLocation(), "unexpected acknowledgement text");
|
||||
}
|
||||
acknowledgeMessagesList.add(parser.text());
|
||||
}
|
||||
acknowledgeMessages.put(currentFieldName, acknowledgeMessagesList.toArray(new String[0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
return new Tuple<>(message, acknowledgeMessages);
|
||||
},
|
||||
new ParseField("acknowledge"));
|
||||
}
|
||||
|
||||
private LicensesStatus status;
|
||||
private Map<String, String[]> acknowledgeMessages;
|
||||
private String acknowledgeHeader;
|
||||
|
||||
public PutLicenseResponse() {
|
||||
}
|
||||
|
||||
public PutLicenseResponse(boolean acknowledged, LicensesStatus status) {
|
||||
this(acknowledged, status, null, Collections.<String, String[]>emptyMap());
|
||||
}
|
||||
|
||||
public PutLicenseResponse(boolean acknowledged, LicensesStatus status, String acknowledgeHeader,
|
||||
Map<String, String[]> acknowledgeMessages) {
|
||||
super(acknowledged);
|
||||
this.status = status;
|
||||
this.acknowledgeHeader = acknowledgeHeader;
|
||||
this.acknowledgeMessages = acknowledgeMessages;
|
||||
}
|
||||
|
||||
public LicensesStatus status() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public Map<String, String[]> acknowledgeMessages() {
|
||||
return acknowledgeMessages;
|
||||
}
|
||||
|
||||
public String acknowledgeHeader() {
|
||||
return acknowledgeHeader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFrom(StreamInput in) throws IOException {
|
||||
super.readFrom(in);
|
||||
status = LicensesStatus.fromId(in.readVInt());
|
||||
acknowledgeHeader = in.readOptionalString();
|
||||
int size = in.readVInt();
|
||||
Map<String, String[]> acknowledgeMessages = new HashMap<>(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
String feature = in.readString();
|
||||
int nMessages = in.readVInt();
|
||||
String[] messages = new String[nMessages];
|
||||
for (int j = 0; j < nMessages; j++) {
|
||||
messages[j] = in.readString();
|
||||
}
|
||||
acknowledgeMessages.put(feature, messages);
|
||||
}
|
||||
this.acknowledgeMessages = acknowledgeMessages;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
super.writeTo(out);
|
||||
out.writeVInt(status.id());
|
||||
out.writeOptionalString(acknowledgeHeader);
|
||||
out.writeVInt(acknowledgeMessages.size());
|
||||
for (Map.Entry<String, String[]> entry : acknowledgeMessages.entrySet()) {
|
||||
out.writeString(entry.getKey());
|
||||
out.writeVInt(entry.getValue().length);
|
||||
for (String message : entry.getValue()) {
|
||||
out.writeString(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addCustomFields(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.field("license_status", status.toString());
|
||||
if (!acknowledgeMessages.isEmpty()) {
|
||||
builder.startObject("acknowledge");
|
||||
builder.field("message", acknowledgeHeader);
|
||||
for (Map.Entry<String, String[]> entry : acknowledgeMessages.entrySet()) {
|
||||
builder.startArray(entry.getKey());
|
||||
for (String message : entry.getValue()) {
|
||||
builder.value(message);
|
||||
}
|
||||
builder.endArray();
|
||||
}
|
||||
builder.endObject();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Strings.toString(this, true, true);
|
||||
}
|
||||
|
||||
public static PutLicenseResponse fromXContent(XContentParser parser) throws IOException {
|
||||
return PARSER.parse(parser, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
if (!super.equals(o)) return false;
|
||||
PutLicenseResponse that = (PutLicenseResponse) o;
|
||||
|
||||
return status == that.status &&
|
||||
ProtocolUtils.equals(acknowledgeMessages, that.acknowledgeMessages) &&
|
||||
Objects.equals(acknowledgeHeader, that.acknowledgeHeader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(super.hashCode(), status, ProtocolUtils.hashCode(acknowledgeMessages), acknowledgeHeader);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* 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.common;
|
||||
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ProtocolUtilsTests extends ESTestCase {
|
||||
|
||||
public void testMapStringEqualsAndHash() {
|
||||
assertTrue(ProtocolUtils.equals(null, null));
|
||||
assertFalse(ProtocolUtils.equals(null, new HashMap<>()));
|
||||
assertFalse(ProtocolUtils.equals(new HashMap<>(), null));
|
||||
|
||||
Map<String, String[]> a = new HashMap<>();
|
||||
a.put("foo", new String[] { "a", "b" });
|
||||
a.put("bar", new String[] { "b", "c" });
|
||||
|
||||
Map<String, String[]> b = new HashMap<>();
|
||||
b.put("foo", new String[] { "a", "b" });
|
||||
|
||||
assertFalse(ProtocolUtils.equals(a, b));
|
||||
assertFalse(ProtocolUtils.equals(b, a));
|
||||
|
||||
b.put("bar", new String[] { "c", "b" });
|
||||
|
||||
assertFalse(ProtocolUtils.equals(a, b));
|
||||
assertFalse(ProtocolUtils.equals(b, a));
|
||||
|
||||
b.put("bar", new String[] { "b", "c" });
|
||||
|
||||
assertTrue(ProtocolUtils.equals(a, b));
|
||||
assertTrue(ProtocolUtils.equals(b, a));
|
||||
assertEquals(ProtocolUtils.hashCode(a), ProtocolUtils.hashCode(b));
|
||||
|
||||
b.put("baz", new String[] { "b", "c" });
|
||||
|
||||
assertFalse(ProtocolUtils.equals(a, b));
|
||||
assertFalse(ProtocolUtils.equals(b, a));
|
||||
|
||||
a.put("non", null);
|
||||
|
||||
assertFalse(ProtocolUtils.equals(a, b));
|
||||
assertFalse(ProtocolUtils.equals(b, a));
|
||||
|
||||
b.put("non", null);
|
||||
b.remove("baz");
|
||||
|
||||
assertTrue(ProtocolUtils.equals(a, b));
|
||||
assertTrue(ProtocolUtils.equals(b, a));
|
||||
assertEquals(ProtocolUtils.hashCode(a), ProtocolUtils.hashCode(b));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* 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.license;
|
||||
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.test.AbstractStreamableXContentTestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class PutLicenseResponseTests extends AbstractStreamableXContentTestCase<PutLicenseResponse> {
|
||||
|
||||
@Override
|
||||
protected boolean supportsUnknownFields() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Predicate<String> getRandomFieldsExcludeFilter() {
|
||||
// The structure of the response is such that unknown fields inside acknowledge cannot be supported since they
|
||||
// are treated as messages from new services
|
||||
return p -> p.startsWith("acknowledge");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PutLicenseResponse createTestInstance() {
|
||||
boolean acknowledged = randomBoolean();
|
||||
LicensesStatus status = randomFrom(LicensesStatus.VALID, LicensesStatus.INVALID, LicensesStatus.EXPIRED);
|
||||
String messageHeader;
|
||||
Map<String, String[]> ackMessages;
|
||||
if (randomBoolean()) {
|
||||
messageHeader = randomAlphaOfLength(10);
|
||||
ackMessages = randomAckMessages();
|
||||
} else {
|
||||
messageHeader = null;
|
||||
ackMessages = Collections.emptyMap();
|
||||
}
|
||||
|
||||
return new PutLicenseResponse(acknowledged, status, messageHeader, ackMessages);
|
||||
}
|
||||
|
||||
private static Map<String, String[]> randomAckMessages() {
|
||||
int nFeatures = randomIntBetween(1, 5);
|
||||
|
||||
Map<String, String[]> ackMessages = new HashMap<>();
|
||||
|
||||
for (int i = 0; i < nFeatures; i++) {
|
||||
String feature = randomAlphaOfLengthBetween(9, 15);
|
||||
int nMessages = randomIntBetween(1, 5);
|
||||
String[] messages = new String[nMessages];
|
||||
for (int j = 0; j < nMessages; j++) {
|
||||
messages[j] = randomAlphaOfLengthBetween(10, 30);
|
||||
}
|
||||
ackMessages.put(feature, messages);
|
||||
}
|
||||
|
||||
return ackMessages;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PutLicenseResponse doParseInstance(XContentParser parser) throws IOException {
|
||||
return PutLicenseResponse.fromXContent(parser);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PutLicenseResponse createBlankInstance() {
|
||||
return new PutLicenseResponse();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PutLicenseResponse mutateInstance(PutLicenseResponse response) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Function<PutLicenseResponse, PutLicenseResponse> mutator = randomFrom(
|
||||
r -> new PutLicenseResponse(
|
||||
r.isAcknowledged() == false,
|
||||
r.status(),
|
||||
r.acknowledgeHeader(),
|
||||
r.acknowledgeMessages()),
|
||||
r -> new PutLicenseResponse(
|
||||
r.isAcknowledged(),
|
||||
mutateStatus(r.status()),
|
||||
r.acknowledgeHeader(),
|
||||
r.acknowledgeMessages()),
|
||||
r -> {
|
||||
if (r.acknowledgeMessages().isEmpty()) {
|
||||
return new PutLicenseResponse(
|
||||
r.isAcknowledged(),
|
||||
r.status(),
|
||||
randomAlphaOfLength(10),
|
||||
randomAckMessages()
|
||||
);
|
||||
} else {
|
||||
return new PutLicenseResponse(r.isAcknowledged(), r.status());
|
||||
}
|
||||
}
|
||||
|
||||
);
|
||||
return mutator.apply(response);
|
||||
}
|
||||
|
||||
private LicensesStatus mutateStatus(LicensesStatus status) {
|
||||
return randomValueOtherThan(status, () -> randomFrom(LicensesStatus.values()));
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue