Skip to content

Commit c2186b6

Browse files
andyb-elastickcm
authored andcommitted
add start trial API to HLRC (#33406)
Introduces client-specific request and response classes that do not depend on the server The `type` parameter is named `licenseType` in the response class to be more descriptive. The parts that make up the acknowledged-required response are given slightly different names than their server-response types to be consistent with the naming in the put license API Tests do not cover all cases because the integ test cluster starts up with a trial license - this will be addressed in a future commit
1 parent 572133a commit c2186b6

File tree

10 files changed

+491
-24
lines changed

10 files changed

+491
-24
lines changed

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
import org.apache.http.HttpEntity;
2323
import org.elasticsearch.action.ActionListener;
2424
import org.elasticsearch.action.support.master.AcknowledgedResponse;
25+
import org.elasticsearch.client.license.StartTrialRequest;
26+
import org.elasticsearch.client.license.StartTrialResponse;
2527
import org.elasticsearch.client.license.StartBasicRequest;
2628
import org.elasticsearch.client.license.StartBasicResponse;
2729
import org.elasticsearch.common.Strings;
@@ -44,6 +46,7 @@
4446
import java.nio.charset.StandardCharsets;
4547

4648
import static java.util.Collections.emptySet;
49+
import static java.util.Collections.singleton;
4750

4851
/**
4952
* A wrapper for the {@link RestHighLevelClient} that provides methods for
@@ -123,6 +126,30 @@ public void deleteLicenseAsync(DeleteLicenseRequest request, RequestOptions opti
123126
AcknowledgedResponse::fromXContent, listener, emptySet());
124127
}
125128

129+
/**
130+
* Starts a trial license on the cluster.
131+
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
132+
* @return the response
133+
* @throws IOException in case there is a problem sending the request or parsing back the response
134+
*/
135+
public StartTrialResponse startTrial(StartTrialRequest request, RequestOptions options) throws IOException {
136+
return restHighLevelClient.performRequestAndParseEntity(request, LicenseRequestConverters::startTrial, options,
137+
StartTrialResponse::fromXContent, singleton(403));
138+
}
139+
140+
/**
141+
* Asynchronously starts a trial license on the cluster.
142+
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
143+
* @param listener the listener to be notified upon request completion
144+
*/
145+
public void startTrialAsync(StartTrialRequest request,
146+
RequestOptions options,
147+
ActionListener<StartTrialResponse> listener) {
148+
149+
restHighLevelClient.performRequestAsyncAndParseEntity(request, LicenseRequestConverters::startTrial, options,
150+
StartTrialResponse::fromXContent, listener, singleton(403));
151+
}
152+
126153
/**
127154
* Initiates an indefinite basic license.
128155
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized

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

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,15 @@
2323
import org.apache.http.client.methods.HttpGet;
2424
import org.apache.http.client.methods.HttpPost;
2525
import org.apache.http.client.methods.HttpPut;
26+
import org.elasticsearch.client.license.StartTrialRequest;
2627
import org.elasticsearch.client.license.StartBasicRequest;
2728
import org.elasticsearch.protocol.xpack.license.DeleteLicenseRequest;
2829
import org.elasticsearch.protocol.xpack.license.GetLicenseRequest;
2930
import org.elasticsearch.protocol.xpack.license.PutLicenseRequest;
3031

3132
public class LicenseRequestConverters {
3233
static Request putLicense(PutLicenseRequest putLicenseRequest) {
33-
String endpoint = new RequestConverters.EndpointBuilder()
34-
.addPathPartAsIs("_xpack")
35-
.addPathPartAsIs("license")
36-
.build();
34+
String endpoint = new RequestConverters.EndpointBuilder().addPathPartAsIs("_xpack", "license").build();
3735
Request request = new Request(HttpPut.METHOD_NAME, endpoint);
3836
RequestConverters.Params parameters = new RequestConverters.Params(request);
3937
parameters.withTimeout(putLicenseRequest.timeout());
@@ -46,24 +44,34 @@ static Request putLicense(PutLicenseRequest putLicenseRequest) {
4644
}
4745

4846
static Request getLicense(GetLicenseRequest getLicenseRequest) {
49-
String endpoint = new RequestConverters.EndpointBuilder()
50-
.addPathPartAsIs("_xpack")
51-
.addPathPartAsIs("license")
52-
.build();
47+
String endpoint = new RequestConverters.EndpointBuilder().addPathPartAsIs("_xpack", "license").build();
5348
Request request = new Request(HttpGet.METHOD_NAME, endpoint);
5449
RequestConverters.Params parameters = new RequestConverters.Params(request);
5550
parameters.withLocal(getLicenseRequest.local());
5651
return request;
5752
}
5853

5954
static Request deleteLicense(DeleteLicenseRequest deleteLicenseRequest) {
60-
Request request = new Request(HttpDelete.METHOD_NAME, "/_xpack/license");
55+
String endpoint = new RequestConverters.EndpointBuilder().addPathPartAsIs("_xpack", "license").build();
56+
Request request = new Request(HttpDelete.METHOD_NAME, endpoint);
6157
RequestConverters.Params parameters = new RequestConverters.Params(request);
6258
parameters.withTimeout(deleteLicenseRequest.timeout());
6359
parameters.withMasterTimeout(deleteLicenseRequest.masterNodeTimeout());
6460
return request;
6561
}
6662

63+
static Request startTrial(StartTrialRequest startTrialRequest) {
64+
final String endpoint = new RequestConverters.EndpointBuilder().addPathPartAsIs("_xpack", "license", "start_trial").build();
65+
final Request request = new Request(HttpPost.METHOD_NAME, endpoint);
66+
67+
RequestConverters.Params parameters = new RequestConverters.Params(request);
68+
parameters.putParam("acknowledge", Boolean.toString(startTrialRequest.isAcknowledge()));
69+
if (startTrialRequest.getLicenseType() != null) {
70+
parameters.putParam("type", startTrialRequest.getLicenseType());
71+
}
72+
return request;
73+
}
74+
6775
static Request startBasic(StartBasicRequest startBasicRequest) {
6876
String endpoint = new RequestConverters.EndpointBuilder()
6977
.addPathPartAsIs("_xpack", "license", "start_basic")

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -980,7 +980,7 @@ EndpointBuilder addCommaSeparatedPathParts(String[] parts) {
980980
return this;
981981
}
982982

983-
EndpointBuilder addPathPartAsIs(String ... parts) {
983+
EndpointBuilder addPathPartAsIs(String... parts) {
984984
for (String part : parts) {
985985
if (Strings.hasLength(part)) {
986986
joiner.add(part);
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
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.license;
21+
22+
import org.elasticsearch.client.Validatable;
23+
import org.elasticsearch.common.Nullable;
24+
25+
public class StartTrialRequest implements Validatable {
26+
27+
private final boolean acknowledge;
28+
private final String licenseType;
29+
30+
public StartTrialRequest() {
31+
this(false);
32+
}
33+
34+
public StartTrialRequest(boolean acknowledge) {
35+
this(acknowledge, null);
36+
}
37+
38+
public StartTrialRequest(boolean acknowledge, @Nullable String licenseType) {
39+
this.acknowledge = acknowledge;
40+
this.licenseType = licenseType;
41+
}
42+
43+
public boolean isAcknowledge() {
44+
return acknowledge;
45+
}
46+
47+
public String getLicenseType() {
48+
return licenseType;
49+
}
50+
}
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
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.license;
21+
22+
import org.elasticsearch.common.ParseField;
23+
import org.elasticsearch.common.collect.Tuple;
24+
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
25+
import org.elasticsearch.common.xcontent.XContentParseException;
26+
import org.elasticsearch.common.xcontent.XContentParser;
27+
28+
import java.io.IOException;
29+
import java.util.ArrayList;
30+
import java.util.HashMap;
31+
import java.util.List;
32+
import java.util.Map;
33+
34+
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg;
35+
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg;
36+
37+
public class StartTrialResponse {
38+
39+
private static final ConstructingObjectParser<StartTrialResponse, Void> PARSER = new ConstructingObjectParser<>(
40+
"start_trial_response",
41+
true,
42+
(Object[] arguments, Void aVoid) -> {
43+
final boolean acknowledged = (boolean) arguments[0];
44+
final boolean trialWasStarted = (boolean) arguments[1];
45+
final String licenseType = (String) arguments[2];
46+
final String errorMessage = (String) arguments[3];
47+
48+
@SuppressWarnings("unchecked")
49+
final Tuple<String, Map<String, String[]>> acknowledgeDetails = (Tuple<String, Map<String, String[]>>) arguments[4];
50+
final String acknowledgeHeader;
51+
final Map<String, String[]> acknowledgeMessages;
52+
53+
if (acknowledgeDetails != null) {
54+
acknowledgeHeader = acknowledgeDetails.v1();
55+
acknowledgeMessages = acknowledgeDetails.v2();
56+
} else {
57+
acknowledgeHeader = null;
58+
acknowledgeMessages = null;
59+
}
60+
61+
return new StartTrialResponse(acknowledged, trialWasStarted, licenseType, errorMessage, acknowledgeHeader,
62+
acknowledgeMessages);
63+
}
64+
);
65+
66+
static {
67+
PARSER.declareBoolean(constructorArg(), new ParseField("acknowledged"));
68+
PARSER.declareBoolean(constructorArg(), new ParseField("trial_was_started"));
69+
PARSER.declareString(optionalConstructorArg(), new ParseField("type"));
70+
PARSER.declareString(optionalConstructorArg(), new ParseField("error_message"));
71+
// todo consolidate this parsing with the parsing in PutLicenseResponse
72+
PARSER.declareObject(optionalConstructorArg(), (parser, aVoid) -> {
73+
final Map<String, String[]> acknowledgeMessages = new HashMap<>();
74+
String message = null;
75+
76+
final Map<String, Object> parsedMap = parser.map();
77+
for (Map.Entry<String, Object> entry : parsedMap.entrySet()) {
78+
if (entry.getKey().equals("message")) {
79+
if (entry.getValue() instanceof String) {
80+
message = (String) entry.getValue();
81+
} else {
82+
throw new XContentParseException(parser.getTokenLocation(), "unexpected acknowledgement header type");
83+
}
84+
} else {
85+
if (entry.getValue() instanceof List) {
86+
final List<String> messageStrings = new ArrayList<>();
87+
@SuppressWarnings("unchecked")
88+
final List<Object> messageObjects = (List<Object>) entry.getValue();
89+
for (Object messageObject : messageObjects) {
90+
if (messageObject instanceof String) {
91+
messageStrings.add((String) messageObject);
92+
} else {
93+
throw new XContentParseException(parser.getTokenLocation(), "expected text in acknowledgement message");
94+
}
95+
}
96+
97+
acknowledgeMessages.put(entry.getKey(), messageStrings.toArray(new String[messageStrings.size()]));
98+
} else {
99+
throw new XContentParseException(parser.getTokenLocation(), "unexpected acknowledgement message type");
100+
}
101+
}
102+
}
103+
104+
if (message == null) {
105+
throw new XContentParseException(parser.getTokenLocation(), "expected acknowledgement header");
106+
}
107+
108+
return new Tuple<>(message, acknowledgeMessages);
109+
110+
}, new ParseField("acknowledge"));
111+
}
112+
113+
public static StartTrialResponse fromXContent(XContentParser parser) throws IOException {
114+
return PARSER.apply(parser, null);
115+
}
116+
117+
private final boolean acknowledged;
118+
private final boolean trialWasStarted;
119+
private final String licenseType;
120+
private final String errorMessage;
121+
private final String acknowledgeHeader;
122+
private final Map<String, String[]> acknowledgeMessages;
123+
124+
public StartTrialResponse(boolean acknowledged,
125+
boolean trialWasStarted,
126+
String licenseType,
127+
String errorMessage,
128+
String acknowledgeHeader,
129+
Map<String, String[]> acknowledgeMessages) {
130+
131+
this.acknowledged = acknowledged;
132+
this.trialWasStarted = trialWasStarted;
133+
this.licenseType = licenseType;
134+
this.errorMessage = errorMessage;
135+
this.acknowledgeHeader = acknowledgeHeader;
136+
this.acknowledgeMessages = acknowledgeMessages;
137+
}
138+
139+
/**
140+
* Returns true if the request that corresponds to this response acknowledged license changes that would occur as a result of starting
141+
* a trial license
142+
*/
143+
public boolean isAcknowledged() {
144+
return acknowledged;
145+
}
146+
147+
/**
148+
* Returns true if a trial license was started as a result of the request corresponding to this response. Returns false if the cluster
149+
* did not start a trial, or a trial had already been started before the corresponding request was made
150+
*/
151+
public boolean isTrialWasStarted() {
152+
return trialWasStarted;
153+
}
154+
155+
/**
156+
* If a trial license was started as a result of the request corresponding to this response (see {@link #isTrialWasStarted()}) then
157+
* returns the type of license that was started on the cluster. Returns null otherwise
158+
*/
159+
public String getLicenseType() {
160+
return licenseType;
161+
}
162+
163+
/**
164+
* If a trial license was not started as a result of the request corresponding to this response (see {@link #isTrialWasStarted()} then
165+
* returns a brief message explaining why the trial could not be started. Returns false otherwise
166+
*/
167+
public String getErrorMessage() {
168+
return errorMessage;
169+
}
170+
171+
/**
172+
* If the request corresponding to this response did not acknowledge licensing changes that would result from starting a trial license
173+
* (see {@link #isAcknowledged()}), returns a message describing how the user must acknowledge licensing changes as a result of
174+
* such a request. Returns null otherwise
175+
*/
176+
public String getAcknowledgeHeader() {
177+
return acknowledgeHeader;
178+
}
179+
180+
/**
181+
* If the request corresponding to this response did not acknowledge licensing changes that would result from starting a trial license
182+
* (see {@link #isAcknowledged()}, returns a map. The map's keys are names of commercial Elasticsearch features, and their values are
183+
* messages about how those features will be affected by licensing changes as a result of starting a trial license
184+
*/
185+
public Map<String, String[]> getAcknowledgeMessages() {
186+
return acknowledgeMessages;
187+
}
188+
}

0 commit comments

Comments
 (0)