HLRC: Add support for XPack Post Start Basic Licence API (#33606)
Relates to #29827
This commit is contained in:
parent
936faba6c4
commit
230ad5339b
|
@ -22,6 +22,8 @@ package org.elasticsearch.client;
|
|||
import org.apache.http.HttpEntity;
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.action.support.master.AcknowledgedResponse;
|
||||
import org.elasticsearch.client.license.StartBasicRequest;
|
||||
import org.elasticsearch.client.license.StartBasicResponse;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.io.Streams;
|
||||
import org.elasticsearch.common.xcontent.DeprecationHandler;
|
||||
|
@ -121,6 +123,28 @@ public final class LicenseClient {
|
|||
AcknowledgedResponse::fromXContent, listener, emptySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates an indefinite basic license.
|
||||
* @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 StartBasicResponse startBasic(StartBasicRequest request, RequestOptions options) throws IOException {
|
||||
return restHighLevelClient.performRequestAndParseEntity(request, LicenseRequestConverters::startBasic, options,
|
||||
StartBasicResponse::fromXContent, emptySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously initiates an indefinite basic license.
|
||||
* @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 startBasicAsync(StartBasicRequest request, RequestOptions options,
|
||||
ActionListener<StartBasicResponse> listener) {
|
||||
restHighLevelClient.performRequestAsyncAndParseEntity(request, LicenseRequestConverters::startBasic, options,
|
||||
StartBasicResponse::fromXContent, listener, emptySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an entire response into a json string
|
||||
*
|
||||
|
|
|
@ -21,7 +21,9 @@ package org.elasticsearch.client;
|
|||
|
||||
import org.apache.http.client.methods.HttpDelete;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.client.methods.HttpPut;
|
||||
import org.elasticsearch.client.license.StartBasicRequest;
|
||||
import org.elasticsearch.protocol.xpack.license.DeleteLicenseRequest;
|
||||
import org.elasticsearch.protocol.xpack.license.GetLicenseRequest;
|
||||
import org.elasticsearch.protocol.xpack.license.PutLicenseRequest;
|
||||
|
@ -61,4 +63,18 @@ public class LicenseRequestConverters {
|
|||
parameters.withMasterTimeout(deleteLicenseRequest.masterNodeTimeout());
|
||||
return request;
|
||||
}
|
||||
|
||||
static Request startBasic(StartBasicRequest startBasicRequest) {
|
||||
String endpoint = new RequestConverters.EndpointBuilder()
|
||||
.addPathPartAsIs("_xpack", "license", "start_basic")
|
||||
.build();
|
||||
Request request = new Request(HttpPost.METHOD_NAME, endpoint);
|
||||
RequestConverters.Params parameters = new RequestConverters.Params(request);
|
||||
parameters.withTimeout(startBasicRequest.timeout());
|
||||
parameters.withMasterTimeout(startBasicRequest.masterNodeTimeout());
|
||||
if (startBasicRequest.isAcknowledge()) {
|
||||
parameters.putParam("acknowledge", "true");
|
||||
}
|
||||
return request;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -980,9 +980,11 @@ final class RequestConverters {
|
|||
return this;
|
||||
}
|
||||
|
||||
EndpointBuilder addPathPartAsIs(String part) {
|
||||
if (Strings.hasLength(part)) {
|
||||
joiner.add(part);
|
||||
EndpointBuilder addPathPartAsIs(String ... parts) {
|
||||
for (String part : parts) {
|
||||
if (Strings.hasLength(part)) {
|
||||
joiner.add(part);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@ package org.elasticsearch.client;
|
|||
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
|
||||
import static org.elasticsearch.common.unit.TimeValue.timeValueSeconds;
|
||||
|
||||
/**
|
||||
* A base request for any requests that supply timeouts.
|
||||
*
|
||||
|
@ -28,8 +30,11 @@ import org.elasticsearch.common.unit.TimeValue;
|
|||
*/
|
||||
public class TimedRequest implements Validatable {
|
||||
|
||||
private TimeValue timeout;
|
||||
private TimeValue masterTimeout;
|
||||
public static final TimeValue DEFAULT_ACK_TIMEOUT = timeValueSeconds(30);
|
||||
public static final TimeValue DEFAULT_MASTER_NODE_TIMEOUT = TimeValue.timeValueSeconds(30);
|
||||
|
||||
private TimeValue timeout = DEFAULT_ACK_TIMEOUT;
|
||||
private TimeValue masterTimeout = DEFAULT_MASTER_NODE_TIMEOUT;
|
||||
|
||||
public void setTimeout(TimeValue timeout) {
|
||||
this.timeout = timeout;
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* 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.license;
|
||||
|
||||
import org.elasticsearch.client.TimedRequest;
|
||||
|
||||
public class StartBasicRequest extends TimedRequest {
|
||||
private final boolean acknowledge;
|
||||
|
||||
public StartBasicRequest() {
|
||||
this(false);
|
||||
}
|
||||
|
||||
public StartBasicRequest(boolean acknowledge) {
|
||||
this.acknowledge = acknowledge;
|
||||
}
|
||||
|
||||
public boolean isAcknowledge() {
|
||||
return acknowledge;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
* 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.license;
|
||||
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.collect.Tuple;
|
||||
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
|
||||
import org.elasticsearch.common.xcontent.XContentParseException;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.rest.RestStatus;
|
||||
|
||||
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;
|
||||
import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureExpectedToken;
|
||||
|
||||
public class StartBasicResponse {
|
||||
|
||||
private static final ConstructingObjectParser<StartBasicResponse, Void> PARSER = new ConstructingObjectParser<>(
|
||||
"start_basic_response", true, (a, v) -> {
|
||||
boolean basicWasStarted = (Boolean) a[0];
|
||||
String errorMessage = (String) a[1];
|
||||
|
||||
if (basicWasStarted) {
|
||||
return new StartBasicResponse(StartBasicResponse.Status.GENERATED_BASIC);
|
||||
}
|
||||
StartBasicResponse.Status status = StartBasicResponse.Status.fromErrorMessage(errorMessage);
|
||||
@SuppressWarnings("unchecked") Tuple<String, Map<String, String[]>> acknowledgements = (Tuple<String, Map<String, String[]>>) a[2];
|
||||
return new StartBasicResponse(status, acknowledgements.v2(), acknowledgements.v1());
|
||||
});
|
||||
|
||||
static {
|
||||
PARSER.declareBoolean(constructorArg(), new ParseField("basic_was_started"));
|
||||
PARSER.declareString(optionalConstructorArg(), new ParseField("error_message"));
|
||||
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 (new ParseField("message").getPreferredName().equals(currentFieldName)) {
|
||||
ensureExpectedToken(XContentParser.Token.VALUE_STRING, token, parser::getTokenLocation);
|
||||
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) {
|
||||
ensureExpectedToken(XContentParser.Token.VALUE_STRING, token, parser::getTokenLocation);
|
||||
acknowledgeMessagesList.add(parser.text());
|
||||
}
|
||||
acknowledgeMessages.put(currentFieldName, acknowledgeMessagesList.toArray(new String[0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
return new Tuple<>(message, acknowledgeMessages);
|
||||
},
|
||||
new ParseField("acknowledge"));
|
||||
}
|
||||
|
||||
private Map<String, String[]> acknowledgeMessages;
|
||||
private String acknowledgeMessage;
|
||||
|
||||
enum Status {
|
||||
GENERATED_BASIC(true, null, RestStatus.OK),
|
||||
ALREADY_USING_BASIC(false, "Operation failed: Current license is basic.", RestStatus.FORBIDDEN),
|
||||
NEED_ACKNOWLEDGEMENT(false, "Operation failed: Needs acknowledgement.", RestStatus.OK);
|
||||
|
||||
private final boolean isBasicStarted;
|
||||
private final String errorMessage;
|
||||
private final RestStatus restStatus;
|
||||
|
||||
Status(boolean isBasicStarted, String errorMessage, RestStatus restStatus) {
|
||||
this.isBasicStarted = isBasicStarted;
|
||||
this.errorMessage = errorMessage;
|
||||
this.restStatus = restStatus;
|
||||
}
|
||||
|
||||
String getErrorMessage() {
|
||||
return errorMessage;
|
||||
}
|
||||
|
||||
boolean isBasicStarted() {
|
||||
return isBasicStarted;
|
||||
}
|
||||
|
||||
static StartBasicResponse.Status fromErrorMessage(final String errorMessage) {
|
||||
final StartBasicResponse.Status[] values = StartBasicResponse.Status.values();
|
||||
for (StartBasicResponse.Status status : values) {
|
||||
if (Objects.equals(status.errorMessage, errorMessage)) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("No status for error message ['" + errorMessage + "']");
|
||||
}
|
||||
}
|
||||
|
||||
private StartBasicResponse.Status status;
|
||||
|
||||
public StartBasicResponse() {
|
||||
}
|
||||
|
||||
StartBasicResponse(StartBasicResponse.Status status) {
|
||||
this(status, Collections.emptyMap(), null);
|
||||
}
|
||||
|
||||
StartBasicResponse(StartBasicResponse.Status status,
|
||||
Map<String, String[]> acknowledgeMessages, String acknowledgeMessage) {
|
||||
this.status = status;
|
||||
this.acknowledgeMessages = acknowledgeMessages;
|
||||
this.acknowledgeMessage = acknowledgeMessage;
|
||||
}
|
||||
|
||||
public boolean isAcknowledged() {
|
||||
return status != StartBasicResponse.Status.NEED_ACKNOWLEDGEMENT;
|
||||
}
|
||||
|
||||
public boolean isBasicStarted() {
|
||||
return status.isBasicStarted;
|
||||
}
|
||||
|
||||
public String getErrorMessage() {
|
||||
return status.errorMessage;
|
||||
}
|
||||
|
||||
public String getAcknowledgeMessage() {
|
||||
return acknowledgeMessage;
|
||||
}
|
||||
|
||||
public Map<String, String[]> getAcknowledgeMessages() {
|
||||
return acknowledgeMessages;
|
||||
}
|
||||
|
||||
public static StartBasicResponse fromXContent(XContentParser parser) throws IOException {
|
||||
return PARSER.parse(parser, null);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* 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.apache.http.client.methods.HttpPost;
|
||||
import org.elasticsearch.action.support.master.AcknowledgedRequest;
|
||||
import org.elasticsearch.client.license.StartBasicRequest;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.elasticsearch.client.RequestConvertersTests.setRandomMasterTimeout;
|
||||
import static org.elasticsearch.client.RequestConvertersTests.setRandomTimeout;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.nullValue;
|
||||
|
||||
public class LicenseRequestConvertersTests extends ESTestCase {
|
||||
public void testStartBasic() {
|
||||
final boolean acknowledge = randomBoolean();
|
||||
StartBasicRequest startBasicRequest = new StartBasicRequest(acknowledge);
|
||||
Map<String, String> expectedParams = new HashMap<>();
|
||||
if (acknowledge) {
|
||||
expectedParams.put("acknowledge", Boolean.TRUE.toString());
|
||||
}
|
||||
|
||||
setRandomTimeout(startBasicRequest, AcknowledgedRequest.DEFAULT_ACK_TIMEOUT, expectedParams);
|
||||
setRandomMasterTimeout(startBasicRequest, expectedParams);
|
||||
Request request = LicenseRequestConverters.startBasic(startBasicRequest);
|
||||
|
||||
assertThat(request.getMethod(), equalTo(HttpPost.METHOD_NAME));
|
||||
assertThat(request.getEndpoint(), equalTo("/_xpack/license/start_basic"));
|
||||
assertThat(request.getParameters(), equalTo(expectedParams));
|
||||
assertThat(request.getEntity(), is(nullValue()));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* 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.Build;
|
||||
import org.elasticsearch.client.license.StartBasicRequest;
|
||||
import org.elasticsearch.client.license.StartBasicResponse;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.collect.MapBuilder;
|
||||
import org.elasticsearch.protocol.xpack.license.LicensesStatus;
|
||||
import org.elasticsearch.protocol.xpack.license.PutLicenseRequest;
|
||||
import org.elasticsearch.protocol.xpack.license.PutLicenseResponse;
|
||||
import org.junit.After;
|
||||
import org.junit.BeforeClass;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.nullValue;
|
||||
import static org.hamcrest.Matchers.isEmptyOrNullString;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.empty;
|
||||
|
||||
public class LicensingIT extends ESRestHighLevelClientTestCase {
|
||||
|
||||
@BeforeClass
|
||||
public static void checkForSnapshot() {
|
||||
assumeTrue("Trial license used to rollback is only valid when tested against snapshot/test builds",
|
||||
Build.CURRENT.isSnapshot());
|
||||
}
|
||||
|
||||
@After
|
||||
public void rollbackToTrial() throws IOException {
|
||||
putTrialLicense();
|
||||
}
|
||||
|
||||
public static void putTrialLicense() throws IOException {
|
||||
assumeTrue("Trial license is only valid when tested against snapshot/test builds",
|
||||
Build.CURRENT.isSnapshot());
|
||||
|
||||
// use a hard-coded trial license for 20 yrs to be able to roll back from another licenses
|
||||
final String licenseDefinition = Strings.toString(jsonBuilder()
|
||||
.startObject()
|
||||
.field("licenses", Arrays.asList(
|
||||
MapBuilder.<String, Object>newMapBuilder()
|
||||
.put("uid", "96fc37c6-6fc9-43e2-a40d-73143850cd72")
|
||||
.put("type", "trial")
|
||||
// 2018-10-16 07:02:48 UTC
|
||||
.put("issue_date_in_millis", "1539673368158")
|
||||
// 2038-10-11 07:02:48 UTC, 20 yrs later
|
||||
.put("expiry_date_in_millis", "2170393368158")
|
||||
.put("max_nodes", "5")
|
||||
.put("issued_to", "client_rest-high-level_integTestCluster")
|
||||
.put("issuer", "elasticsearch")
|
||||
.put("start_date_in_millis", "-1")
|
||||
.put("signature",
|
||||
"AAAABAAAAA3FXON9kGmNqmH+ASDWAAAAIAo5/x6hrsGh1GqqrJmy4qgmEC7gK0U4zQ6q5ZEMhm4jAAABAAcdKHL0BfM2uqTgT7BDuFxX5lb"
|
||||
+ "t/bHDVJ421Wwgm5p3IMbw/W13iiAHz0hhDziF7acJbc/y65L+BKGtVC1gSSHeLDHaAD66VrjKxfc7VbGyJIAYBOdujf0rheurmaD3IcNo"
|
||||
+ "/tWDjCdtTwrNziFkorsGcPadBP5Yc6csk3/Q74DlfiYweMBxLUfkBERwxwd5OQS6ujGvl/4bb8p5zXvOw8vMSaAXSXXnExP6lam+0934W"
|
||||
+ "0kHvU7IGk+fCUjOaiSWKSoE4TEcAtVNYj/oRoRtfQ1KQGpdCHxTHs1BimdZaG0nBHDsvhYlVVLSvHN6QzqsHWgFDG6JJxhtU872oTRSUHA=")
|
||||
.immutableMap()))
|
||||
.endObject());
|
||||
|
||||
final PutLicenseRequest request = new PutLicenseRequest();
|
||||
request.setAcknowledge(true);
|
||||
request.setLicenseDefinition(licenseDefinition);
|
||||
final PutLicenseResponse response = highLevelClient().license().putLicense(request, RequestOptions.DEFAULT);
|
||||
assertThat(response.isAcknowledged(), equalTo(true));
|
||||
assertThat(response.status(), equalTo(LicensesStatus.VALID));
|
||||
}
|
||||
|
||||
public void testStartBasic() throws Exception {
|
||||
// we don't test the case where we successfully start a basic because the integ test cluster generates one on startup
|
||||
// and we don't have a good way to prevent that / work around it in this test project
|
||||
// case where we don't acknowledge basic license conditions
|
||||
{
|
||||
final StartBasicRequest request = new StartBasicRequest();
|
||||
final StartBasicResponse response = highLevelClient().license().startBasic(request, RequestOptions.DEFAULT);
|
||||
assertThat(response.isAcknowledged(), equalTo(false));
|
||||
assertThat(response.isBasicStarted(), equalTo(false));
|
||||
assertThat(response.getErrorMessage(), equalTo("Operation failed: Needs acknowledgement."));
|
||||
assertThat(response.getAcknowledgeMessage(),
|
||||
containsString("This license update requires acknowledgement. " +
|
||||
"To acknowledge the license, please read the following messages and call /start_basic again"));
|
||||
assertNotEmptyAcknowledgeMessages(response);
|
||||
}
|
||||
// case where we acknowledge and the basic is started successfully
|
||||
{
|
||||
final StartBasicRequest request = new StartBasicRequest(true);
|
||||
final StartBasicResponse response = highLevelClient().license().startBasic(request, RequestOptions.DEFAULT);
|
||||
assertThat(response.isAcknowledged(), equalTo(true));
|
||||
assertThat(response.isBasicStarted(), equalTo(true));
|
||||
assertThat(response.getErrorMessage(), nullValue());
|
||||
assertThat(response.getAcknowledgeMessage(), nullValue());
|
||||
assertThat(response.getAcknowledgeMessages().size(), equalTo(0));
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertNotEmptyAcknowledgeMessages(StartBasicResponse response) {
|
||||
assertThat(response.getAcknowledgeMessages().entrySet(), not(empty()));
|
||||
for (Map.Entry<String, String[]> entry : response.getAcknowledgeMessages().entrySet()) {
|
||||
assertThat(entry.getKey(), not(isEmptyOrNullString()));
|
||||
final List<String> messages = Arrays.asList(entry.getValue());
|
||||
for (String message : messages) {
|
||||
assertThat(message, not(isEmptyOrNullString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1572,6 +1572,12 @@ public class RequestConvertersTests extends ESTestCase {
|
|||
setRandomLocal(request::local, expectedParams);
|
||||
}
|
||||
|
||||
static void setRandomTimeout(TimedRequest request, TimeValue defaultTimeout, Map<String, String> expectedParams) {
|
||||
setRandomTimeout(s ->
|
||||
request.setTimeout(TimeValue.parseTimeValue(s, request.getClass().getName() + ".timeout")),
|
||||
defaultTimeout, expectedParams);
|
||||
}
|
||||
|
||||
static void setRandomTimeout(Consumer<String> setter, TimeValue defaultTimeout, Map<String, String> expectedParams) {
|
||||
if (randomBoolean()) {
|
||||
String timeout = randomTimeValue();
|
||||
|
@ -1583,9 +1589,19 @@ public class RequestConvertersTests extends ESTestCase {
|
|||
}
|
||||
|
||||
static void setRandomMasterTimeout(MasterNodeRequest<?> request, Map<String, String> expectedParams) {
|
||||
setRandomMasterTimeout(request::masterNodeTimeout, expectedParams);
|
||||
}
|
||||
|
||||
static void setRandomMasterTimeout(TimedRequest request, Map<String, String> expectedParams) {
|
||||
setRandomMasterTimeout(s ->
|
||||
request.setMasterTimeout(TimeValue.parseTimeValue(s, request.getClass().getName() + ".masterNodeTimeout")),
|
||||
expectedParams);
|
||||
}
|
||||
|
||||
static void setRandomMasterTimeout(Consumer<String> setter, Map<String, String> expectedParams) {
|
||||
if (randomBoolean()) {
|
||||
String masterTimeout = randomTimeValue();
|
||||
request.masterNodeTimeout(masterTimeout);
|
||||
setter.accept(masterTimeout);
|
||||
expectedParams.put("master_timeout", masterTimeout);
|
||||
} else {
|
||||
expectedParams.put("master_timeout", MasterNodeRequest.DEFAULT_MASTER_NODE_TIMEOUT.getStringRep());
|
||||
|
|
|
@ -26,6 +26,8 @@ import org.elasticsearch.action.support.master.AcknowledgedResponse;
|
|||
import org.elasticsearch.client.ESRestHighLevelClientTestCase;
|
||||
import org.elasticsearch.client.RequestOptions;
|
||||
import org.elasticsearch.client.RestHighLevelClient;
|
||||
import org.elasticsearch.client.license.StartBasicRequest;
|
||||
import org.elasticsearch.client.license.StartBasicResponse;
|
||||
import org.elasticsearch.common.Booleans;
|
||||
import org.elasticsearch.protocol.xpack.license.DeleteLicenseRequest;
|
||||
import org.elasticsearch.protocol.xpack.license.GetLicenseRequest;
|
||||
|
@ -33,11 +35,15 @@ import org.elasticsearch.protocol.xpack.license.GetLicenseResponse;
|
|||
import org.elasticsearch.protocol.xpack.license.LicensesStatus;
|
||||
import org.elasticsearch.protocol.xpack.license.PutLicenseRequest;
|
||||
import org.elasticsearch.protocol.xpack.license.PutLicenseResponse;
|
||||
import org.junit.After;
|
||||
import org.junit.BeforeClass;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.elasticsearch.client.LicensingIT.putTrialLicense;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.endsWith;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
|
@ -50,8 +56,18 @@ import static org.hamcrest.Matchers.startsWith;
|
|||
*/
|
||||
public class LicensingDocumentationIT extends ESRestHighLevelClientTestCase {
|
||||
|
||||
@BeforeClass
|
||||
public static void checkForSnapshot() {
|
||||
assumeTrue("Trial license used to rollback is only valid when tested against snapshot/test builds",
|
||||
Build.CURRENT.isSnapshot());
|
||||
}
|
||||
|
||||
@After
|
||||
public void rollbackToTrial() throws IOException {
|
||||
putTrialLicense();
|
||||
}
|
||||
|
||||
public void testLicense() throws Exception {
|
||||
assumeTrue("License is only valid when tested against snapshot/test builds", Build.CURRENT.isSnapshot());
|
||||
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\"," +
|
||||
|
@ -215,4 +231,50 @@ public class LicensingDocumentationIT extends ESRestHighLevelClientTestCase {
|
|||
assertThat(currentLicense, endsWith("}"));
|
||||
}
|
||||
}
|
||||
|
||||
public void testPostStartBasic() throws Exception {
|
||||
RestHighLevelClient client = highLevelClient();
|
||||
{
|
||||
//tag::start-basic-execute
|
||||
StartBasicRequest request = new StartBasicRequest();
|
||||
|
||||
StartBasicResponse response = client.license().startBasic(request, RequestOptions.DEFAULT);
|
||||
//end::start-basic-execute
|
||||
|
||||
//tag::start-basic-response
|
||||
boolean acknowledged = response.isAcknowledged(); // <1>
|
||||
boolean basicStarted = response.isBasicStarted(); // <2>
|
||||
String errorMessage = response.getErrorMessage(); // <3>
|
||||
String acknowledgeMessage = response.getAcknowledgeMessage(); // <4>
|
||||
Map<String, String[]> acknowledgeMessages = response.getAcknowledgeMessages(); // <5>
|
||||
//end::start-basic-response
|
||||
}
|
||||
{
|
||||
StartBasicRequest request = new StartBasicRequest();
|
||||
// tag::start-basic-listener
|
||||
ActionListener<StartBasicResponse> listener = new ActionListener<StartBasicResponse>() {
|
||||
@Override
|
||||
public void onResponse(StartBasicResponse indexResponse) {
|
||||
// <1>
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Exception e) {
|
||||
// <2>
|
||||
}
|
||||
};
|
||||
// end::start-basic-listener
|
||||
|
||||
// Replace the empty listener by a blocking listener in test
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
listener = new LatchedActionListener<>(listener, latch);
|
||||
|
||||
// tag::start-basic-execute-async
|
||||
client.license().startBasicAsync(
|
||||
request, RequestOptions.DEFAULT, listener); // <1>
|
||||
// end::start-basic-execute-async
|
||||
|
||||
assertTrue(latch.await(30L, TimeUnit.SECONDS));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* 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.license;
|
||||
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.protocol.xpack.common.ProtocolUtils;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
|
||||
public class StartBasicResponseTests extends ESTestCase {
|
||||
|
||||
public void testFromXContent() throws Exception {
|
||||
StartBasicResponse.Status status = randomFrom(StartBasicResponse.Status.values());
|
||||
|
||||
boolean acknowledged = status != StartBasicResponse.Status.NEED_ACKNOWLEDGEMENT;
|
||||
String acknowledgeMessage = null;
|
||||
Map<String, String[]> ackMessages = Collections.emptyMap();
|
||||
if (status != StartBasicResponse.Status.GENERATED_BASIC) {
|
||||
acknowledgeMessage = randomAlphaOfLength(10);
|
||||
ackMessages = randomAckMessages();
|
||||
}
|
||||
|
||||
final StartBasicResponse startBasicResponse = new StartBasicResponse(status, ackMessages, acknowledgeMessage);
|
||||
|
||||
XContentType xContentType = randomFrom(XContentType.values());
|
||||
XContentBuilder builder = XContentFactory.contentBuilder(xContentType);
|
||||
|
||||
toXContent(startBasicResponse, builder);
|
||||
|
||||
final StartBasicResponse response = StartBasicResponse.fromXContent(createParser(builder));
|
||||
assertThat(response.isAcknowledged(), equalTo(acknowledged));
|
||||
assertThat(response.isBasicStarted(), equalTo(status.isBasicStarted()));
|
||||
assertThat(response.getAcknowledgeMessage(), equalTo(acknowledgeMessage));
|
||||
assertThat(ProtocolUtils.equals(response.getAcknowledgeMessages(), ackMessages), equalTo(true));
|
||||
}
|
||||
|
||||
private static void toXContent(StartBasicResponse response, XContentBuilder builder) throws IOException {
|
||||
builder.startObject();
|
||||
builder.field("acknowledged", response.isAcknowledged());
|
||||
if (response.isBasicStarted()) {
|
||||
builder.field("basic_was_started", true);
|
||||
} else {
|
||||
builder.field("basic_was_started", false);
|
||||
builder.field("error_message", response.getErrorMessage());
|
||||
}
|
||||
if (response.getAcknowledgeMessages().isEmpty() == false) {
|
||||
builder.startObject("acknowledge");
|
||||
builder.field("message", response.getAcknowledgeMessage());
|
||||
for (Map.Entry<String, String[]> entry : response.getAcknowledgeMessages().entrySet()) {
|
||||
builder.startArray(entry.getKey());
|
||||
for (String message : entry.getValue()) {
|
||||
builder.value(message);
|
||||
}
|
||||
builder.endArray();
|
||||
}
|
||||
builder.endObject();
|
||||
}
|
||||
builder.endObject();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
[[java-rest-high-start-basic]]
|
||||
=== Start Basic License
|
||||
|
||||
[[java-rest-high-start-basic-execution]]
|
||||
==== Execution
|
||||
|
||||
This API creates and enables a basic license using the `startBasic()` method.
|
||||
|
||||
["source","java",subs="attributes,callouts,macros"]
|
||||
--------------------------------------------------
|
||||
include-tagged::{doc-tests}/LicensingDocumentationIT.java[start-basic-execute]
|
||||
--------------------------------------------------
|
||||
|
||||
[[java-rest-high-start-basic-response]]
|
||||
==== Response
|
||||
|
||||
The returned `StartBasicResponse` returns a field indicating whether the
|
||||
basic was started. If it was started, the response returns a the type of
|
||||
license started. If it was not started, it returns an error message describing
|
||||
why.
|
||||
|
||||
Acknowledgement messages may also be returned if this API was called without
|
||||
the `acknowledge` flag set to `true`. In this case you need to display the
|
||||
messages to the end user and if they agree, resubmit the request with the
|
||||
`acknowledge` flag set to `true`. Please note that the response will still
|
||||
return a 200 return code even if it requires an acknowledgement. So, it is
|
||||
necessary to check the `acknowledged` flag.
|
||||
|
||||
["source","java",subs="attributes,callouts,macros"]
|
||||
--------------------------------------------------
|
||||
include-tagged::{doc-tests}/LicensingDocumentationIT.java[start-basic-response]
|
||||
--------------------------------------------------
|
||||
<1> Whether or not the request had the `acknowledge` flag set
|
||||
<2> Whether or not this request caused a basic to start
|
||||
<3> If this request did not cause a basic to start, a message explaining why
|
||||
<4> If the user's request did not have the `acknowledge` flag set, a summary
|
||||
of the user's acknowledgement required for this API
|
||||
<5> If the user's request did not have the `acknowledge` flag set, contains
|
||||
keys of commercial features and values of messages describing how they will
|
||||
be affected by licensing changes as the result of starting a basic
|
||||
|
||||
[[java-rest-high-start-basic-async]]
|
||||
==== Asynchronous Execution
|
||||
|
||||
This request can be executed asynchronously:
|
||||
|
||||
["source","java",subs="attributes,callouts,macros"]
|
||||
--------------------------------------------------
|
||||
include-tagged::{doc-tests}/LicensingDocumentationIT.java[start-basic-execute-async]
|
||||
--------------------------------------------------
|
||||
<1> The `StartBasicResponse` 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 `StartBasicResponse` looks like:
|
||||
|
||||
["source","java",subs="attributes,callouts,macros"]
|
||||
--------------------------------------------------
|
||||
include-tagged::{doc-tests}/LicensingDocumentationIT.java[start-basic-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
|
|
@ -214,10 +214,12 @@ The Java High Level REST Client supports the following Licensing APIs:
|
|||
* <<java-rest-high-put-license>>
|
||||
* <<java-rest-high-get-license>>
|
||||
* <<java-rest-high-delete-license>>
|
||||
* <<java-rest-high-start-basic>>
|
||||
|
||||
include::licensing/put-license.asciidoc[]
|
||||
include::licensing/get-license.asciidoc[]
|
||||
include::licensing/delete-license.asciidoc[]
|
||||
include::licensing/start-basic.asciidoc[]
|
||||
|
||||
== Machine Learning APIs
|
||||
:upid: {mainid}-x-pack-ml
|
||||
|
|
Loading…
Reference in New Issue