Skip to content

Commit 29c802f

Browse files
authored
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
1 parent c1cc0ce commit 29c802f

File tree

25 files changed

+889
-258
lines changed

25 files changed

+889
-258
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.elasticsearch.client;
21+
22+
import org.elasticsearch.action.ActionListener;
23+
import org.elasticsearch.protocol.xpack.license.PutLicenseRequest;
24+
import org.elasticsearch.protocol.xpack.license.PutLicenseResponse;
25+
26+
import java.io.IOException;
27+
28+
import static java.util.Collections.emptySet;
29+
30+
/**
31+
* A wrapper for the {@link RestHighLevelClient} that provides methods for
32+
* accessing the Elastic License-related methods
33+
* <p>
34+
* See the <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/licensing-apis.html">
35+
* X-Pack Licensing APIs on elastic.co</a> for more information.
36+
*/
37+
public class LicenseClient {
38+
39+
private final RestHighLevelClient restHighLevelClient;
40+
41+
LicenseClient(RestHighLevelClient restHighLevelClient) {
42+
this.restHighLevelClient = restHighLevelClient;
43+
}
44+
45+
/**
46+
* Updates license for the cluster.
47+
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
48+
* @return the response
49+
* @throws IOException in case there is a problem sending the request or parsing back the response
50+
*/
51+
public PutLicenseResponse putLicense(PutLicenseRequest request, RequestOptions options) throws IOException {
52+
return restHighLevelClient.performRequestAndParseEntity(request, RequestConverters::putLicense, options,
53+
PutLicenseResponse::fromXContent, emptySet());
54+
}
55+
56+
/**
57+
* Asynchronously updates license for the cluster cluster.
58+
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
59+
* @param listener the listener to be notified upon request completion
60+
*/
61+
public void putLicenseAsync(PutLicenseRequest request, RequestOptions options, ActionListener<PutLicenseResponse> listener) {
62+
restHighLevelClient.performRequestAsyncAndParseEntity(request, RequestConverters::putLicense, options,
63+
PutLicenseResponse::fromXContent, listener, emptySet());
64+
}
65+
66+
}

client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java

+13
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@
109109
import org.elasticsearch.protocol.xpack.XPackInfoRequest;
110110
import org.elasticsearch.protocol.xpack.watcher.PutWatchRequest;
111111
import org.elasticsearch.protocol.xpack.XPackUsageRequest;
112+
import org.elasticsearch.protocol.xpack.license.PutLicenseRequest;
112113
import org.elasticsearch.rest.action.search.RestSearchAction;
113114
import org.elasticsearch.script.mustache.MultiSearchTemplateRequest;
114115
import org.elasticsearch.script.mustache.SearchTemplateRequest;
@@ -1139,6 +1140,18 @@ static Request xpackUsage(XPackUsageRequest usageRequest) {
11391140
return request;
11401141
}
11411142

1143+
static Request putLicense(PutLicenseRequest putLicenseRequest) {
1144+
Request request = new Request(HttpPut.METHOD_NAME, "/_xpack/license");
1145+
Params parameters = new Params(request);
1146+
parameters.withTimeout(putLicenseRequest.timeout());
1147+
parameters.withMasterTimeout(putLicenseRequest.masterNodeTimeout());
1148+
if (putLicenseRequest.isAcknowledge()) {
1149+
parameters.putParam("acknowledge", "true");
1150+
}
1151+
request.setJsonEntity(putLicenseRequest.getLicenseDefinition());
1152+
return request;
1153+
}
1154+
11421155
private static HttpEntity createEntity(ToXContent toXContent, XContentType xContentType) throws IOException {
11431156
BytesRef source = XContentHelper.toXContent(toXContent, xContentType, false).toBytesRef();
11441157
return new ByteArrayEntity(source.bytes, source.offset, source.length, createContentType(xContentType));

client/rest-high-level/src/main/java/org/elasticsearch/client/XPackClient.java

+13
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,12 @@ public final class XPackClient {
4242

4343
private final RestHighLevelClient restHighLevelClient;
4444
private final WatcherClient watcherClient;
45+
private final LicenseClient licenseClient;
4546

4647
XPackClient(RestHighLevelClient restHighLevelClient) {
4748
this.restHighLevelClient = restHighLevelClient;
4849
this.watcherClient = new WatcherClient(restHighLevelClient);
50+
this.licenseClient = new LicenseClient(restHighLevelClient);
4951
}
5052

5153
public WatcherClient watcher() {
@@ -100,4 +102,15 @@ public void usageAsync(XPackUsageRequest request, RequestOptions options, Action
100102
restHighLevelClient.performRequestAsyncAndParseEntity(request, RequestConverters::xpackUsage, options,
101103
XPackUsageResponse::fromXContent, listener, emptySet());
102104
}
105+
106+
/**
107+
* A wrapper for the {@link RestHighLevelClient} that provides methods for
108+
* accessing the Elastic Licensing APIs.
109+
* <p>
110+
* See the <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/licensing-apis.html">
111+
* X-Pack APIs on elastic.co</a> for more information.
112+
*/
113+
public LicenseClient license() {
114+
return licenseClient;
115+
}
103116
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.elasticsearch.client.documentation;
21+
22+
import org.elasticsearch.action.ActionListener;
23+
import org.elasticsearch.action.LatchedActionListener;
24+
import org.elasticsearch.client.ESRestHighLevelClientTestCase;
25+
import org.elasticsearch.client.RequestOptions;
26+
import org.elasticsearch.client.RestHighLevelClient;
27+
import org.elasticsearch.protocol.xpack.license.LicensesStatus;
28+
import org.elasticsearch.protocol.xpack.license.PutLicenseRequest;
29+
import org.elasticsearch.protocol.xpack.license.PutLicenseResponse;
30+
31+
import java.util.Map;
32+
import java.util.concurrent.CountDownLatch;
33+
import java.util.concurrent.TimeUnit;
34+
35+
import static org.hamcrest.Matchers.hasSize;
36+
import static org.hamcrest.Matchers.not;
37+
import static org.hamcrest.Matchers.startsWith;
38+
39+
/**
40+
* Documentation for Licensing APIs in the high level java client.
41+
* Code wrapped in {@code tag} and {@code end} tags is included in the docs.
42+
*/
43+
public class LicensingDocumentationIT extends ESRestHighLevelClientTestCase {
44+
45+
public void testPutLicense() throws Exception {
46+
RestHighLevelClient client = highLevelClient();
47+
String license = "{\"license\": {\"uid\":\"893361dc-9749-4997-93cb-802e3d7fa4a8\",\"type\":\"gold\"," +
48+
"\"issue_date_in_millis\":1411948800000,\"expiry_date_in_millis\":1914278399999,\"max_nodes\":1,\"issued_to\":\"issued_to\"," +
49+
"\"issuer\":\"issuer\",\"signature\":\"AAAAAgAAAA3U8+YmnvwC+CWsV/mRAAABmC9ZN0hjZDBGYnVyRXpCOW5Bb3FjZDAxOWpSbTVoMVZwUzRxVk1PSm" +
50+
"kxakxZdW5IMlhlTHNoN1N2MXMvRFk4d3JTZEx3R3RRZ0pzU3lobWJKZnQvSEFva0ppTHBkWkprZWZSQi9iNmRQNkw1SlpLN0lDalZCS095MXRGN1lIZlpYcVVTTn" +
51+
"FrcTE2dzhJZmZrdFQrN3JQeGwxb0U0MXZ0dDJHSERiZTVLOHNzSDByWnpoZEphZHBEZjUrTVBxRENNSXNsWWJjZllaODdzVmEzUjNiWktNWGM5TUhQV2plaUo4Q1" +
52+
"JOUml4MXNuL0pSOEhQaVB2azhmUk9QVzhFeTFoM1Q0RnJXSG53MWk2K055c28zSmRnVkF1b2JSQkFLV2VXUmVHNDZ2R3o2VE1qbVNQS2lxOHN5bUErZlNIWkZSVm" +
53+
"ZIWEtaSU9wTTJENDVvT1NCYklacUYyK2FwRW9xa0t6dldMbmMzSGtQc3FWOTgzZ3ZUcXMvQkt2RUZwMFJnZzlvL2d2bDRWUzh6UG5pdENGWFRreXNKNkE9PQAAAQ" +
54+
"Be8GfzDm6T537Iuuvjetb3xK5dvg0K5NQapv+rczWcQFxgCuzbF8plkgetP1aAGZP4uRESDQPMlOCsx4d0UqqAm9f7GbBQ3l93P+PogInPFeEH9NvOmaAQovmxVM" +
55+
"9SE6DsDqlX4cXSO+bgWpXPTd2LmpoQc1fXd6BZ8GeuyYpVHVKp9hVU0tAYjw6HzYOE7+zuO1oJYOxElqy66AnIfkvHrvni+flym3tE7tDTgsDRaz7W3iBhaqiSnt" +
56+
"EqabEkvHdPHQdSR99XGaEvnHO1paK01/35iZF6OXHsF7CCj+558GRXiVxzueOe7TsGSSt8g7YjZwV9bRCyU7oB4B/nidgI\"}}";
57+
{
58+
//tag::put-license-execute
59+
PutLicenseRequest request = new PutLicenseRequest();
60+
request.setLicenseDefinition(license); // <1>
61+
request.setAcknowledge(false); // <2>
62+
63+
PutLicenseResponse response = client.xpack().license().putLicense(request, RequestOptions.DEFAULT);
64+
//end::put-license-execute
65+
66+
//tag::put-license-response
67+
LicensesStatus status = response.status(); // <1>
68+
assertEquals(status, LicensesStatus.VALID); // <2>
69+
boolean acknowledged = response.isAcknowledged(); // <3>
70+
String acknowledgeHeader = response.acknowledgeHeader(); // <4>
71+
Map<String, String[]> acknowledgeMessages = response.acknowledgeMessages(); // <5>
72+
//end::put-license-response
73+
74+
assertFalse(acknowledged); // Should fail because we are trying to downgrade from platinum trial to gold
75+
assertThat(acknowledgeHeader, startsWith("This license update requires acknowledgement."));
76+
assertThat(acknowledgeMessages.keySet(), not(hasSize(0)));
77+
}
78+
{
79+
PutLicenseRequest request = new PutLicenseRequest();
80+
// tag::put-license-execute-listener
81+
ActionListener<PutLicenseResponse> listener = new ActionListener<PutLicenseResponse>() {
82+
@Override
83+
public void onResponse(PutLicenseResponse indexResponse) {
84+
// <1>
85+
}
86+
87+
@Override
88+
public void onFailure(Exception e) {
89+
// <2>
90+
}
91+
};
92+
// end::put-license-execute-listener
93+
94+
// Replace the empty listener by a blocking listener in test
95+
final CountDownLatch latch = new CountDownLatch(1);
96+
listener = new LatchedActionListener<>(listener, latch);
97+
98+
// tag::put-license-execute-async
99+
client.xpack().license().putLicenseAsync(
100+
request, RequestOptions.DEFAULT, listener); // <1>
101+
// end::put-license-execute-async
102+
103+
assertTrue(latch.await(30L, TimeUnit.SECONDS));
104+
}
105+
}
106+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
[[java-rest-high-put-license]]
2+
=== Update License
3+
4+
[[java-rest-high-put-license-execution]]
5+
==== Execution
6+
7+
The license can be added or updated using the `putLicense()` method:
8+
9+
["source","java",subs="attributes,callouts,macros"]
10+
--------------------------------------------------
11+
include-tagged::{doc-tests}/LicensingDocumentationIT.java[put-license-execute]
12+
--------------------------------------------------
13+
<1> Set the categories of information to retrieve. The the default is to
14+
return no information which is useful for checking if {xpack} is installed
15+
but not much else.
16+
<2> A JSON document containing the license information.
17+
18+
[[java-rest-high-put-license-response]]
19+
==== Response
20+
21+
The returned `PutLicenseResponse` contains the `LicensesStatus`,
22+
`acknowledged` flag and possible acknowledge messages. The acknowledge messages
23+
are present if you previously had a license with more features than one you
24+
are trying to update and you didn't set the `acknowledge` flag to `true`. In this case
25+
you need to display the messages to the end user and if they agree, resubmit the
26+
license with the `acknowledge` flag set to `true`. Please note that the request will
27+
still return a 200 return code even if requires an acknowledgement. So, it is
28+
necessary to check the `acknowledged` flag.
29+
30+
["source","java",subs="attributes,callouts,macros"]
31+
--------------------------------------------------
32+
include-tagged::{doc-tests}/LicensingDocumentationIT.java[put-license-response]
33+
--------------------------------------------------
34+
<1> The status of the license
35+
<2> Make sure that the license is valid.
36+
<3> Check the acknowledge flag.
37+
<4> It should be true if license is acknowledge.
38+
<5> Otherwise we can see the acknowledge messages in `acknowledgeHeader()`
39+
<6> and check component-specific messages in `acknowledgeMessages()`.
40+
41+
[[java-rest-high-put-license-async]]
42+
==== Asynchronous Execution
43+
44+
This request can be executed asynchronously:
45+
46+
["source","java",subs="attributes,callouts,macros"]
47+
--------------------------------------------------
48+
include-tagged::{doc-tests}/LicensingDocumentationIT.java[put-license-execute-async]
49+
--------------------------------------------------
50+
<1> The `PutLicenseRequest` to execute and the `ActionListener` to use when
51+
the execution completes
52+
53+
The asynchronous method does not block and returns immediately. Once it is
54+
completed the `ActionListener` is called back using the `onResponse` method
55+
if the execution successfully completed or using the `onFailure` method if
56+
it failed.
57+
58+
A typical listener for `PutLicenseResponse` looks like:
59+
60+
["source","java",subs="attributes,callouts,macros"]
61+
--------------------------------------------------
62+
include-tagged::{doc-tests}/LicensingDocumentationIT.java[put-license-execute-listener]
63+
--------------------------------------------------
64+
<1> Called when the execution is successfully completed. The response is
65+
provided as an argument
66+
<2> Called in case of failure. The raised exception is provided as an argument

docs/java-rest/high-level/supported-apis.asciidoc

+9
Original file line numberDiff line numberDiff line change
@@ -186,3 +186,12 @@ The Java High Level REST Client supports the following Scripts APIs:
186186

187187
include::script/get_script.asciidoc[]
188188
include::script/delete_script.asciidoc[]
189+
190+
191+
== Licensing APIs
192+
193+
The Java High Level REST Client supports the following Licensing APIs:
194+
195+
* <<java-rest-high-put-license>>
196+
197+
include::licensing/put-license.asciidoc[]

x-pack/plugin/core/src/main/java/org/elasticsearch/license/LicenseService.java

+2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
import org.elasticsearch.env.Environment;
2929
import org.elasticsearch.gateway.GatewayService;
3030
import org.elasticsearch.protocol.xpack.XPackInfoResponse;
31+
import org.elasticsearch.protocol.xpack.license.LicensesStatus;
32+
import org.elasticsearch.protocol.xpack.license.PutLicenseResponse;
3133
import org.elasticsearch.watcher.ResourceWatcherService;
3234
import org.elasticsearch.xpack.core.XPackPlugin;
3335
import org.elasticsearch.xpack.core.XPackSettings;

x-pack/plugin/core/src/main/java/org/elasticsearch/license/LicensesStatus.java

-34
This file was deleted.

x-pack/plugin/core/src/main/java/org/elasticsearch/license/LicensingClient.java

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import org.elasticsearch.action.ActionListener;
99
import org.elasticsearch.client.ElasticsearchClient;
10+
import org.elasticsearch.protocol.xpack.license.PutLicenseResponse;
1011

1112
public class LicensingClient {
1213

x-pack/plugin/core/src/main/java/org/elasticsearch/license/PutLicenseAction.java

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
package org.elasticsearch.license;
77

88
import org.elasticsearch.action.Action;
9+
import org.elasticsearch.protocol.xpack.license.PutLicenseResponse;
910

1011
public class PutLicenseAction extends Action<PutLicenseResponse> {
1112

x-pack/plugin/core/src/main/java/org/elasticsearch/license/PutLicenseRequestBuilder.java

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import org.elasticsearch.client.ElasticsearchClient;
1010
import org.elasticsearch.common.bytes.BytesReference;
1111
import org.elasticsearch.common.xcontent.XContentType;
12+
import org.elasticsearch.protocol.xpack.license.PutLicenseResponse;
1213

1314
/**
1415
* Register license request builder

0 commit comments

Comments
 (0)