Skip to content

Commit

Permalink
HLRC: Add support for XPack Post Start Basic Licence API (#33606)
Browse files Browse the repository at this point in the history
Relates to #29827
  • Loading branch information
vladimirdolzhenko authored Oct 16, 2018
1 parent 936faba commit 230ad53
Show file tree
Hide file tree
Showing 13 changed files with 693 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
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;
Expand Down Expand Up @@ -121,6 +123,28 @@ public void deleteLicenseAsync(DeleteLicenseRequest request, RequestOptions opti
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
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@

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;
Expand Down Expand Up @@ -61,4 +63,18 @@ static Request deleteLicense(DeleteLicenseRequest deleteLicenseRequest) {
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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -980,9 +980,11 @@ EndpointBuilder addCommaSeparatedPathParts(String[] parts) {
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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@

import org.elasticsearch.common.unit.TimeValue;

import static org.elasticsearch.common.unit.TimeValue.timeValueSeconds;

/**
* A base request for any requests that supply timeouts.
*
Expand All @@ -28,8 +30,11 @@
*/
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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
}

Original file line number Diff line number Diff line change
@@ -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);
}

}
Original file line number Diff line number Diff line change
@@ -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()));
}
}
Loading

0 comments on commit 230ad53

Please sign in to comment.