Add api to upgrade from basic to trial license (elastic/x-pack-elasticsearch#2419)
This is related to elastic/x-pack-elasticsearch#1941. Currently we support self-generating either a basic or trial license at cluster startup. With the addition of the basic option, it is possible that a user would choose to self-generate and eventually register a basic license. This commit allows a user to upgrade to a 30-day trial license if they have not already utilized this 30-day trial license before. Additionally it adds a get route to check if the user is eligible to upgrade. This route will allow kibana to implement a cleaner UI. Original commit: elastic/x-pack-elasticsearch@7f19b33a08
This commit is contained in:
parent
b55ab98914
commit
6b2e7fbed8
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* 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.Action;
|
||||
import org.elasticsearch.client.ElasticsearchClient;
|
||||
|
||||
public class GetTrialStatusAction extends Action<GetTrialStatusRequest, GetTrialStatusResponse, GetTrialStatusRequestBuilder> {
|
||||
|
||||
public static final GetTrialStatusAction INSTANCE = new GetTrialStatusAction();
|
||||
public static final String NAME = "cluster:admin/xpack/license/trial_status";
|
||||
|
||||
private GetTrialStatusAction() {
|
||||
super(NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GetTrialStatusRequestBuilder newRequestBuilder(ElasticsearchClient client) {
|
||||
return new GetTrialStatusRequestBuilder(client, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GetTrialStatusResponse newResponse() {
|
||||
return new GetTrialStatusResponse();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* 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.ActionRequestValidationException;
|
||||
import org.elasticsearch.action.support.master.MasterNodeReadRequest;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class GetTrialStatusRequest extends MasterNodeReadRequest<GetTrialStatusRequest> {
|
||||
|
||||
public GetTrialStatusRequest() {
|
||||
}
|
||||
|
||||
public GetTrialStatusRequest(StreamInput in) throws IOException {
|
||||
super(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionRequestValidationException validate() {
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* 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.ActionRequestBuilder;
|
||||
import org.elasticsearch.client.ElasticsearchClient;
|
||||
|
||||
class GetTrialStatusRequestBuilder extends ActionRequestBuilder<GetTrialStatusRequest,
|
||||
GetTrialStatusResponse, GetTrialStatusRequestBuilder> {
|
||||
|
||||
GetTrialStatusRequestBuilder(ElasticsearchClient client, GetTrialStatusAction action) {
|
||||
super(client, action, new GetTrialStatusRequest());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* 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.ActionResponse;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
class GetTrialStatusResponse extends ActionResponse {
|
||||
|
||||
private boolean eligibleToStartTrial;
|
||||
|
||||
GetTrialStatusResponse() {
|
||||
}
|
||||
|
||||
GetTrialStatusResponse(boolean eligibleToStartTrial) {
|
||||
this.eligibleToStartTrial = eligibleToStartTrial;
|
||||
}
|
||||
|
||||
boolean isEligibleToStartTrial() {
|
||||
return eligibleToStartTrial;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFrom(StreamInput in) throws IOException {
|
||||
eligibleToStartTrial = in.readBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
out.writeBoolean(eligibleToStartTrial);
|
||||
}
|
||||
}
|
|
@ -8,6 +8,7 @@ package org.elasticsearch.license;
|
|||
import org.apache.logging.log4j.message.ParameterizedMessage;
|
||||
import org.apache.logging.log4j.util.Supplier;
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.cluster.AckedClusterStateUpdateTask;
|
||||
import org.elasticsearch.cluster.ClusterChangedEvent;
|
||||
|
@ -227,8 +228,14 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste
|
|||
|
||||
@Override
|
||||
public ClusterState execute(ClusterState currentState) throws Exception {
|
||||
MetaData.Builder mdBuilder = MetaData.builder(currentState.metaData());
|
||||
mdBuilder.putCustom(LicensesMetaData.TYPE, new LicensesMetaData(newLicense));
|
||||
MetaData currentMetadata = currentState.metaData();
|
||||
LicensesMetaData licensesMetaData = currentMetadata.custom(LicensesMetaData.TYPE);
|
||||
Version trialVersion = null;
|
||||
if (licensesMetaData != null) {
|
||||
trialVersion = licensesMetaData.getMostRecentTrialVersion();
|
||||
}
|
||||
MetaData.Builder mdBuilder = MetaData.builder(currentMetadata);
|
||||
mdBuilder.putCustom(LicensesMetaData.TYPE, new LicensesMetaData(newLicense, trialVersion));
|
||||
return ClusterState.builder(currentState).metaData(mdBuilder).build();
|
||||
}
|
||||
});
|
||||
|
@ -237,7 +244,7 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste
|
|||
}
|
||||
|
||||
|
||||
static TimeValue days(int days) {
|
||||
private static TimeValue days(int days) {
|
||||
return TimeValue.timeValueHours(days * 24);
|
||||
}
|
||||
|
||||
|
@ -273,7 +280,9 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste
|
|||
final LicensesMetaData currentLicenses = metaData.custom(LicensesMetaData.TYPE);
|
||||
if (currentLicenses.getLicense() != LicensesMetaData.LICENSE_TOMBSTONE) {
|
||||
MetaData.Builder mdBuilder = MetaData.builder(currentState.metaData());
|
||||
mdBuilder.putCustom(LicensesMetaData.TYPE, new LicensesMetaData(LicensesMetaData.LICENSE_TOMBSTONE));
|
||||
LicensesMetaData newMetadata = new LicensesMetaData(LicensesMetaData.LICENSE_TOMBSTONE,
|
||||
currentLicenses.getMostRecentTrialVersion());
|
||||
mdBuilder.putCustom(LicensesMetaData.TYPE, newMetadata);
|
||||
return ClusterState.builder(currentState).metaData(mdBuilder).build();
|
||||
} else {
|
||||
return currentState;
|
||||
|
@ -287,6 +296,40 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste
|
|||
return license == LicensesMetaData.LICENSE_TOMBSTONE ? null : license;
|
||||
}
|
||||
|
||||
void upgradeSelfGeneratedLicense(final ActionListener<PostStartTrialResponse> listener) {
|
||||
clusterService.submitStateUpdateTask("upgrade self generated license",
|
||||
new ClusterStateUpdateTask() {
|
||||
@Override
|
||||
public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) {
|
||||
LicensesMetaData licensesMetaData = oldState.metaData().custom(LicensesMetaData.TYPE);
|
||||
logger.debug("upgraded self generated license: {}", licensesMetaData);
|
||||
|
||||
if (licensesMetaData == null || licensesMetaData.isEligibleForTrial()) {
|
||||
listener.onResponse(new PostStartTrialResponse(PostStartTrialResponse.STATUS.UPGRADED_TO_TRIAL));
|
||||
} else {
|
||||
listener.onResponse(new PostStartTrialResponse(PostStartTrialResponse.STATUS.TRIAL_ALREADY_ACTIVATED));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClusterState execute(ClusterState currentState) throws Exception {
|
||||
LicensesMetaData licensesMetaData = currentState.metaData().custom(LicensesMetaData.TYPE);
|
||||
|
||||
if (licensesMetaData == null || licensesMetaData.isEligibleForTrial()) {
|
||||
return updateWithLicense(currentState, "trial");
|
||||
} else {
|
||||
return currentState;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(String source, @Nullable Exception e) {
|
||||
logger.error(new ParameterizedMessage("unexpected failure during [{}]", source), e);
|
||||
listener.onFailure(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Master-only operation to generate a one-time global trial license.
|
||||
* The trial license is only generated and stored if the current cluster state metaData
|
||||
|
@ -299,7 +342,7 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste
|
|||
public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) {
|
||||
LicensesMetaData licensesMetaData = newState.metaData().custom(LicensesMetaData.TYPE);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("registered trial license: {}", licensesMetaData);
|
||||
logger.debug("registered self generated license: {}", licensesMetaData);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -307,27 +350,18 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste
|
|||
public ClusterState execute(ClusterState currentState) throws Exception {
|
||||
final MetaData metaData = currentState.metaData();
|
||||
final LicensesMetaData currentLicensesMetaData = metaData.custom(LicensesMetaData.TYPE);
|
||||
MetaData.Builder mdBuilder = MetaData.builder(currentState.metaData());
|
||||
// do not generate a trial license if any license is present
|
||||
if (currentLicensesMetaData == null) {
|
||||
long issueDate = clock.millis();
|
||||
String type = SELF_GENERATED_LICENSE_TYPE.get(settings);
|
||||
if (validSelfGeneratedType(type) == false) {
|
||||
throw new IllegalArgumentException("Illegal self generated license type [" + type +
|
||||
"]. Must be trial or basic.");
|
||||
}
|
||||
License.Builder specBuilder = License.builder()
|
||||
.uid(UUID.randomUUID().toString())
|
||||
.issuedTo(clusterService.getClusterName().value())
|
||||
.maxNodes(selfGeneratedLicenseMaxNodes)
|
||||
.issueDate(issueDate)
|
||||
.type(type)
|
||||
.expiryDate(issueDate + SELF_GENERATED_LICENSE_DURATION.getMillis());
|
||||
License selfGeneratedLicense = SelfGeneratedLicense.create(specBuilder);
|
||||
mdBuilder.putCustom(LicensesMetaData.TYPE, new LicensesMetaData(selfGeneratedLicense));
|
||||
return ClusterState.builder(currentState).metaData(mdBuilder).build();
|
||||
|
||||
return updateWithLicense(currentState, type);
|
||||
} else {
|
||||
return currentState;
|
||||
}
|
||||
return currentState;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -338,6 +372,27 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste
|
|||
});
|
||||
}
|
||||
|
||||
private ClusterState updateWithLicense(ClusterState currentState, String type) {
|
||||
long issueDate = clock.millis();
|
||||
MetaData.Builder mdBuilder = MetaData.builder(currentState.metaData());
|
||||
License.Builder specBuilder = License.builder()
|
||||
.uid(UUID.randomUUID().toString())
|
||||
.issuedTo(clusterService.getClusterName().value())
|
||||
.maxNodes(selfGeneratedLicenseMaxNodes)
|
||||
.issueDate(issueDate)
|
||||
.type(type)
|
||||
.expiryDate(issueDate + SELF_GENERATED_LICENSE_DURATION.getMillis());
|
||||
License selfGeneratedLicense = SelfGeneratedLicense.create(specBuilder);
|
||||
LicensesMetaData licensesMetaData;
|
||||
if ("trial".equals(type)) {
|
||||
licensesMetaData = new LicensesMetaData(selfGeneratedLicense, Version.CURRENT);
|
||||
} else {
|
||||
licensesMetaData = new LicensesMetaData(selfGeneratedLicense, null);
|
||||
}
|
||||
mdBuilder.putCustom(LicensesMetaData.TYPE, licensesMetaData);
|
||||
return ClusterState.builder(currentState).metaData(mdBuilder).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStart() throws ElasticsearchException {
|
||||
clusterService.addListener(this);
|
||||
|
@ -436,7 +491,7 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste
|
|||
* Additionally schedules license expiry notifications and event callbacks
|
||||
* relative to the current license's expiry
|
||||
*/
|
||||
void onUpdate(final LicensesMetaData currentLicensesMetaData) {
|
||||
private void onUpdate(final LicensesMetaData currentLicensesMetaData) {
|
||||
final License license = getLicense(currentLicensesMetaData);
|
||||
// license can be null if the trial license is yet to be auto-generated
|
||||
// in this case, it is a no-op
|
||||
|
|
|
@ -5,15 +5,17 @@
|
|||
*/
|
||||
package org.elasticsearch.license;
|
||||
|
||||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.cluster.AbstractNamedDiffable;
|
||||
import org.elasticsearch.cluster.MergableCustomMetaData;
|
||||
import org.elasticsearch.cluster.NamedDiff;
|
||||
import org.elasticsearch.cluster.metadata.MetaData;
|
||||
import org.elasticsearch.common.inject.internal.Nullable;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.license.License.OperationMode;
|
||||
import org.elasticsearch.cluster.MergableCustomMetaData;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.EnumSet;
|
||||
|
@ -48,33 +50,56 @@ class LicensesMetaData extends AbstractNamedDiffable<MetaData.Custom> implements
|
|||
|
||||
private License license;
|
||||
|
||||
LicensesMetaData(License license) {
|
||||
// This field describes the version of x-pack for which this cluster has exercised a trial. If the field
|
||||
// is null, then no trial has been exercised. We keep the version to leave open the possibility that we
|
||||
// may eventually allow a cluster to exercise a trial every time they upgrade to a new major version.
|
||||
@Nullable
|
||||
private Version trialVersion;
|
||||
|
||||
LicensesMetaData(License license, Version trialVersion) {
|
||||
this.license = license;
|
||||
this.trialVersion = trialVersion;
|
||||
}
|
||||
|
||||
public License getLicense() {
|
||||
return license;
|
||||
}
|
||||
|
||||
boolean isEligibleForTrial() {
|
||||
if (trialVersion == null) {
|
||||
return true;
|
||||
}
|
||||
return Version.CURRENT.major > trialVersion.major;
|
||||
}
|
||||
|
||||
Version getMostRecentTrialVersion() {
|
||||
return trialVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (license != null) {
|
||||
return license.toString();
|
||||
}
|
||||
return "";
|
||||
return "LicensesMetaData{" +
|
||||
"license=" + license +
|
||||
", trialVersion=" + trialVersion +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
LicensesMetaData that = (LicensesMetaData) o;
|
||||
return !(license != null ? !license.equals(that.license) : that.license != null);
|
||||
|
||||
if (license != null ? !license.equals(that.license) : that.license != null) return false;
|
||||
return trialVersion != null ? trialVersion.equals(that.trialVersion) : that.trialVersion == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return license != null ? license.hashCode() : 0;
|
||||
int result = license != null ? license.hashCode() : 0;
|
||||
result = 31 * result + (trialVersion != null ? trialVersion.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -89,6 +114,7 @@ class LicensesMetaData extends AbstractNamedDiffable<MetaData.Custom> implements
|
|||
|
||||
public static LicensesMetaData fromXContent(XContentParser parser) throws IOException {
|
||||
License license = LICENSE_TOMBSTONE;
|
||||
Version trialLicense = null;
|
||||
XContentParser.Token token;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
|
@ -101,11 +127,14 @@ class LicensesMetaData extends AbstractNamedDiffable<MetaData.Custom> implements
|
|||
} else if (token == XContentParser.Token.VALUE_NULL) {
|
||||
license = LICENSE_TOMBSTONE;
|
||||
}
|
||||
} else if (fieldName.equals(Fields.TRIAL_LICENSE)) {
|
||||
parser.nextToken();
|
||||
trialLicense = Version.fromString(parser.text());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return new LicensesMetaData(license);
|
||||
return new LicensesMetaData(license, trialLicense);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -117,6 +146,9 @@ class LicensesMetaData extends AbstractNamedDiffable<MetaData.Custom> implements
|
|||
license.toInnerXContent(builder, params);
|
||||
builder.endObject();
|
||||
}
|
||||
if (trialVersion != null) {
|
||||
builder.field(Fields.TRIAL_LICENSE, trialVersion.toString());
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
|
@ -128,6 +160,15 @@ class LicensesMetaData extends AbstractNamedDiffable<MetaData.Custom> implements
|
|||
streamOutput.writeBoolean(true); // has a license
|
||||
license.writeTo(streamOutput);
|
||||
}
|
||||
// TODO Eventually this should be 6.0. But it is 7.0 temporarily for bwc
|
||||
if (streamOutput.getVersion().onOrAfter(Version.V_7_0_0_alpha1)) {
|
||||
if (trialVersion == null) {
|
||||
streamOutput.writeBoolean(false);
|
||||
} else {
|
||||
streamOutput.writeBoolean(true);
|
||||
Version.writeVersion(trialVersion, streamOutput);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LicensesMetaData(StreamInput streamInput) throws IOException {
|
||||
|
@ -136,6 +177,13 @@ class LicensesMetaData extends AbstractNamedDiffable<MetaData.Custom> implements
|
|||
} else {
|
||||
license = LICENSE_TOMBSTONE;
|
||||
}
|
||||
// TODO Eventually this should be 6.0. But it is 7.0 temporarily for bwc
|
||||
if (streamInput.getVersion().onOrAfter(Version.V_7_0_0_alpha1)) {
|
||||
boolean hasExercisedTrial = streamInput.readBoolean();
|
||||
if (hasExercisedTrial) {
|
||||
this.trialVersion = Version.readVersion(streamInput);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static NamedDiff<MetaData.Custom> readDiffFrom(StreamInput streamInput) throws IOException {
|
||||
|
@ -155,5 +203,6 @@ class LicensesMetaData extends AbstractNamedDiffable<MetaData.Custom> implements
|
|||
|
||||
private static final class Fields {
|
||||
private static final String LICENSE = "license";
|
||||
private static final String TRIAL_LICENSE = "trial_license";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ public class Licensing implements ActionPlugin {
|
|||
|
||||
public static final String NAME = "license";
|
||||
protected final Settings settings;
|
||||
protected final boolean isTransportClient;
|
||||
private final boolean isTransportClient;
|
||||
private final boolean isTribeNode;
|
||||
|
||||
public List<NamedWriteableRegistry.Entry> getNamedWriteables() {
|
||||
|
@ -53,6 +53,7 @@ public class Licensing implements ActionPlugin {
|
|||
LicensesMetaData::fromXContent));
|
||||
return entries;
|
||||
}
|
||||
|
||||
public Licensing(Settings settings) {
|
||||
this.settings = settings;
|
||||
isTransportClient = transportClientMode(settings);
|
||||
|
@ -66,7 +67,9 @@ public class Licensing implements ActionPlugin {
|
|||
}
|
||||
return Arrays.asList(new ActionHandler<>(PutLicenseAction.INSTANCE, TransportPutLicenseAction.class),
|
||||
new ActionHandler<>(GetLicenseAction.INSTANCE, TransportGetLicenseAction.class),
|
||||
new ActionHandler<>(DeleteLicenseAction.INSTANCE, TransportDeleteLicenseAction.class));
|
||||
new ActionHandler<>(DeleteLicenseAction.INSTANCE, TransportDeleteLicenseAction.class),
|
||||
new ActionHandler<>(PostStartTrialAction.INSTANCE, TransportPostStartTrialAction.class),
|
||||
new ActionHandler<>(GetTrialStatusAction.INSTANCE, TransportGetTrialStatusAction.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -78,6 +81,8 @@ public class Licensing implements ActionPlugin {
|
|||
if (false == isTribeNode) {
|
||||
handlers.add(new RestPutLicenseAction(settings, restController));
|
||||
handlers.add(new RestDeleteLicenseAction(settings, restController));
|
||||
handlers.add(new RestGetTrialStatus(settings, restController));
|
||||
handlers.add(new RestPostStartTrialLicense(settings, restController));
|
||||
}
|
||||
return handlers;
|
||||
}
|
||||
|
|
|
@ -39,4 +39,16 @@ public class LicensingClient {
|
|||
public void deleteLicense(DeleteLicenseRequest request, ActionListener<DeleteLicenseResponse> listener) {
|
||||
client.execute(DeleteLicenseAction.INSTANCE, request, listener);
|
||||
}
|
||||
|
||||
public PostStartTrialRequestBuilder preparePutUpgradeToTrial() {
|
||||
return new PostStartTrialRequestBuilder(client, PostStartTrialAction.INSTANCE);
|
||||
}
|
||||
|
||||
public GetTrialStatusRequestBuilder prepareGetUpgradeToTrial() {
|
||||
return new GetTrialStatusRequestBuilder(client, GetTrialStatusAction.INSTANCE);
|
||||
}
|
||||
|
||||
public void putUpgradeToTrial(PostStartTrialRequest request, ActionListener<PostStartTrialResponse> listener) {
|
||||
client.execute(PostStartTrialAction.INSTANCE, request, listener);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* 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.Action;
|
||||
import org.elasticsearch.client.ElasticsearchClient;
|
||||
|
||||
public class PostStartTrialAction extends Action<PostStartTrialRequest, PostStartTrialResponse, PostStartTrialRequestBuilder> {
|
||||
|
||||
public static final PostStartTrialAction INSTANCE = new PostStartTrialAction();
|
||||
public static final String NAME = "cluster:admin/xpack/license/start_trial";
|
||||
|
||||
private PostStartTrialAction() {
|
||||
super(NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PostStartTrialRequestBuilder newRequestBuilder(ElasticsearchClient client) {
|
||||
return new PostStartTrialRequestBuilder(client, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PostStartTrialResponse newResponse() {
|
||||
return new PostStartTrialResponse();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* 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.ActionRequestValidationException;
|
||||
import org.elasticsearch.action.support.master.MasterNodeRequest;
|
||||
|
||||
public class PostStartTrialRequest extends MasterNodeRequest<PostStartTrialRequest> {
|
||||
|
||||
@Override
|
||||
public ActionRequestValidationException validate() {
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* 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.ActionRequestBuilder;
|
||||
import org.elasticsearch.client.ElasticsearchClient;
|
||||
|
||||
class PostStartTrialRequestBuilder extends ActionRequestBuilder<PostStartTrialRequest,
|
||||
PostStartTrialResponse, PostStartTrialRequestBuilder> {
|
||||
|
||||
PostStartTrialRequestBuilder(ElasticsearchClient client, PostStartTrialAction action) {
|
||||
super(client, action, new PostStartTrialRequest());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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.ActionResponse;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
class PostStartTrialResponse extends ActionResponse {
|
||||
|
||||
enum STATUS {
|
||||
UPGRADED_TO_TRIAL,
|
||||
TRIAL_ALREADY_ACTIVATED
|
||||
}
|
||||
|
||||
private STATUS status;
|
||||
|
||||
PostStartTrialResponse() {
|
||||
}
|
||||
|
||||
PostStartTrialResponse(STATUS status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public STATUS getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFrom(StreamInput in) throws IOException {
|
||||
status = in.readEnum(STATUS.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
out.writeEnum(status);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.rest.BytesRestResponse;
|
||||
import org.elasticsearch.rest.RestController;
|
||||
import org.elasticsearch.rest.RestRequest;
|
||||
import org.elasticsearch.rest.RestResponse;
|
||||
import org.elasticsearch.rest.RestStatus;
|
||||
import org.elasticsearch.rest.action.RestBuilderListener;
|
||||
import org.elasticsearch.xpack.XPackClient;
|
||||
import org.elasticsearch.xpack.rest.XPackRestHandler;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.elasticsearch.rest.RestRequest.Method.GET;
|
||||
|
||||
public class RestGetTrialStatus extends XPackRestHandler {
|
||||
|
||||
RestGetTrialStatus(Settings settings, RestController controller) {
|
||||
super(settings);
|
||||
controller.registerHandler(GET, URI_BASE + "/license/trial_status", this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RestChannelConsumer doPrepareRequest(RestRequest request, XPackClient client) throws IOException {
|
||||
return channel -> client.licensing().prepareGetUpgradeToTrial().execute(
|
||||
new RestBuilderListener<GetTrialStatusResponse>(channel) {
|
||||
@Override
|
||||
public RestResponse buildResponse(GetTrialStatusResponse response, XContentBuilder builder) throws Exception {
|
||||
builder.startObject();
|
||||
builder.field("eligible_to_start_trial", response.isEligibleToStartTrial());
|
||||
builder.endObject();
|
||||
return new BytesRestResponse(RestStatus.OK, builder);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "xpack_trial_status_action";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* 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.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.rest.BytesRestResponse;
|
||||
import org.elasticsearch.rest.RestController;
|
||||
import org.elasticsearch.rest.RestRequest;
|
||||
import org.elasticsearch.rest.RestResponse;
|
||||
import org.elasticsearch.rest.RestStatus;
|
||||
import org.elasticsearch.rest.action.RestBuilderListener;
|
||||
import org.elasticsearch.xpack.XPackClient;
|
||||
import org.elasticsearch.xpack.rest.XPackRestHandler;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.elasticsearch.rest.RestRequest.Method.POST;
|
||||
import static org.elasticsearch.rest.RestRequest.Method.PUT;
|
||||
|
||||
public class RestPostStartTrialLicense extends XPackRestHandler {
|
||||
|
||||
RestPostStartTrialLicense(Settings settings, RestController controller) {
|
||||
super(settings);
|
||||
controller.registerHandler(POST, URI_BASE + "/license/start_trial", this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RestChannelConsumer doPrepareRequest(RestRequest request, XPackClient client) throws IOException {
|
||||
return channel -> client.licensing().preparePutUpgradeToTrial().execute(
|
||||
new RestBuilderListener<PostStartTrialResponse>(channel) {
|
||||
@Override
|
||||
public RestResponse buildResponse(PostStartTrialResponse response, XContentBuilder builder) throws Exception {
|
||||
PostStartTrialResponse.STATUS status = response.getStatus();
|
||||
if (status == PostStartTrialResponse.STATUS.TRIAL_ALREADY_ACTIVATED) {
|
||||
builder.startObject()
|
||||
.field("trial_was_started", false)
|
||||
.field("error_message", "Operation failed: Trial was already activated.")
|
||||
.endObject();
|
||||
return new BytesRestResponse(RestStatus.FORBIDDEN, builder);
|
||||
} else if (status == PostStartTrialResponse.STATUS.UPGRADED_TO_TRIAL) {
|
||||
builder.startObject().field("trial_was_started", true).endObject();
|
||||
return new BytesRestResponse(RestStatus.OK, builder);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unexpected status for PostStartTrialResponse: [" + status + "]");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "xpack_upgrade_to_trial_action";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* 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.ActionListener;
|
||||
import org.elasticsearch.action.support.ActionFilters;
|
||||
import org.elasticsearch.action.support.master.TransportMasterNodeReadAction;
|
||||
import org.elasticsearch.cluster.ClusterState;
|
||||
import org.elasticsearch.cluster.block.ClusterBlockException;
|
||||
import org.elasticsearch.cluster.block.ClusterBlockLevel;
|
||||
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
|
||||
import org.elasticsearch.cluster.service.ClusterService;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.transport.TransportService;
|
||||
|
||||
public class TransportGetTrialStatusAction extends TransportMasterNodeReadAction<GetTrialStatusRequest, GetTrialStatusResponse> {
|
||||
|
||||
@Inject
|
||||
public TransportGetTrialStatusAction(Settings settings, TransportService transportService, ClusterService clusterService,
|
||||
ThreadPool threadPool, ActionFilters actionFilters,
|
||||
IndexNameExpressionResolver indexNameExpressionResolver) {
|
||||
super(settings, GetTrialStatusAction.NAME, transportService, clusterService, threadPool, actionFilters,
|
||||
GetTrialStatusRequest::new, indexNameExpressionResolver);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String executor() {
|
||||
return ThreadPool.Names.SAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GetTrialStatusResponse newResponse() {
|
||||
return new GetTrialStatusResponse();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void masterOperation(GetTrialStatusRequest request, ClusterState state,
|
||||
ActionListener<GetTrialStatusResponse> listener) throws Exception {
|
||||
LicensesMetaData licensesMetaData = state.metaData().custom(LicensesMetaData.TYPE);
|
||||
listener.onResponse(new GetTrialStatusResponse(licensesMetaData == null || licensesMetaData.isEligibleForTrial()));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ClusterBlockException checkBlock(GetTrialStatusRequest request, ClusterState state) {
|
||||
return state.blocks().globalBlockedException(ClusterBlockLevel.METADATA_READ);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* 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.ActionListener;
|
||||
import org.elasticsearch.action.support.ActionFilters;
|
||||
import org.elasticsearch.action.support.master.TransportMasterNodeAction;
|
||||
import org.elasticsearch.cluster.ClusterState;
|
||||
import org.elasticsearch.cluster.block.ClusterBlockException;
|
||||
import org.elasticsearch.cluster.block.ClusterBlockLevel;
|
||||
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
|
||||
import org.elasticsearch.cluster.service.ClusterService;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.transport.TransportService;
|
||||
|
||||
public class TransportPostStartTrialAction extends TransportMasterNodeAction<PostStartTrialRequest, PostStartTrialResponse> {
|
||||
|
||||
private final LicenseService licenseService;
|
||||
|
||||
@Inject
|
||||
public TransportPostStartTrialAction(Settings settings, TransportService transportService, ClusterService clusterService,
|
||||
LicenseService licenseService, ThreadPool threadPool, ActionFilters actionFilters,
|
||||
IndexNameExpressionResolver indexNameExpressionResolver) {
|
||||
super(settings, PostStartTrialAction.NAME, transportService, clusterService, threadPool, actionFilters,
|
||||
indexNameExpressionResolver, PostStartTrialRequest::new);
|
||||
this.licenseService = licenseService;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String executor() {
|
||||
return ThreadPool.Names.SAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PostStartTrialResponse newResponse() {
|
||||
return new PostStartTrialResponse();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void masterOperation(PostStartTrialRequest request, ClusterState state,
|
||||
ActionListener<PostStartTrialResponse> listener) throws Exception {
|
||||
licenseService.upgradeSelfGeneratedLicense(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ClusterBlockException checkBlock(PostStartTrialRequest request, ClusterState state) {
|
||||
return state.blocks().globalBlockedException(ClusterBlockLevel.METADATA_WRITE);
|
||||
}
|
||||
}
|
|
@ -49,16 +49,20 @@ public abstract class AbstractLicenseServiceTestCase extends ESTestCase {
|
|||
}
|
||||
|
||||
protected void setInitialState(License license, XPackLicenseState licenseState, Settings settings) {
|
||||
setInitialState(license, licenseState, settings, randomBoolean() ? "trial" : "basic");
|
||||
}
|
||||
|
||||
protected void setInitialState(License license, XPackLicenseState licenseState, Settings settings, String selfGeneratedType) {
|
||||
Path tempDir = createTempDir();
|
||||
when(environment.configFile()).thenReturn(tempDir);
|
||||
licenseType = randomBoolean() ? "trial" : "basic";
|
||||
licenseType = selfGeneratedType;
|
||||
settings = Settings.builder().put(settings).put(LicenseService.SELF_GENERATED_LICENSE_TYPE.getKey(), licenseType).build();
|
||||
licenseService = new LicenseService(settings, clusterService, clock, environment, resourceWatcherService, licenseState);
|
||||
ClusterState state = mock(ClusterState.class);
|
||||
final ClusterBlocks noBlock = ClusterBlocks.builder().build();
|
||||
when(state.blocks()).thenReturn(noBlock);
|
||||
MetaData metaData = mock(MetaData.class);
|
||||
when(metaData.custom(LicensesMetaData.TYPE)).thenReturn(new LicensesMetaData(license));
|
||||
when(metaData.custom(LicensesMetaData.TYPE)).thenReturn(new LicensesMetaData(license, null));
|
||||
when(state.metaData()).thenReturn(metaData);
|
||||
final DiscoveryNode mockNode = new DiscoveryNode("b", buildNewFakeTransportAddress(), emptyMap(), emptySet(), Version.CURRENT);
|
||||
when(discoveryNodes.getMasterNode()).thenReturn(mockNode);
|
||||
|
|
|
@ -65,7 +65,7 @@ public abstract class AbstractLicensesIntegrationTestCase extends ESIntegTestCas
|
|||
@Override
|
||||
public ClusterState execute(ClusterState currentState) throws Exception {
|
||||
MetaData.Builder mdBuilder = MetaData.builder(currentState.metaData());
|
||||
mdBuilder.putCustom(LicensesMetaData.TYPE, new LicensesMetaData(license));
|
||||
mdBuilder.putCustom(LicensesMetaData.TYPE, new LicensesMetaData(license, null));
|
||||
return ClusterState.builder(currentState).metaData(mdBuilder).build();
|
||||
}
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ public class LicenseClusterChangeTests extends AbstractLicenseServiceTestCase {
|
|||
public void testNotificationOnNewLicense() throws Exception {
|
||||
ClusterState oldState = ClusterState.builder(new ClusterName("a")).build();
|
||||
final License license = TestUtils.generateSignedLicense(TimeValue.timeValueHours(24));
|
||||
MetaData metaData = MetaData.builder().putCustom(LicensesMetaData.TYPE, new LicensesMetaData(license)).build();
|
||||
MetaData metaData = MetaData.builder().putCustom(LicensesMetaData.TYPE, new LicensesMetaData(license, null)).build();
|
||||
ClusterState newState = ClusterState.builder(new ClusterName("a")).metaData(metaData).build();
|
||||
licenseService.clusterChanged(new ClusterChangedEvent("simulated", newState, oldState));
|
||||
assertThat(licenseState.activeUpdates.size(), equalTo(1));
|
||||
|
@ -56,7 +56,7 @@ public class LicenseClusterChangeTests extends AbstractLicenseServiceTestCase {
|
|||
|
||||
public void testNoNotificationOnExistingLicense() throws Exception {
|
||||
final License license = TestUtils.generateSignedLicense(TimeValue.timeValueHours(24));
|
||||
MetaData metaData = MetaData.builder().putCustom(LicensesMetaData.TYPE, new LicensesMetaData(license)).build();
|
||||
MetaData metaData = MetaData.builder().putCustom(LicensesMetaData.TYPE, new LicensesMetaData(license, null)).build();
|
||||
ClusterState newState = ClusterState.builder(new ClusterName("a")).metaData(metaData).build();
|
||||
ClusterState oldState = ClusterState.builder(newState).build();
|
||||
licenseService.clusterChanged(new ClusterChangedEvent("simulated", newState, oldState));
|
||||
|
|
|
@ -18,9 +18,9 @@ import static org.mockito.Mockito.when;
|
|||
|
||||
public class LicenseRegistrationTests extends AbstractLicenseServiceTestCase {
|
||||
|
||||
public void testTrialLicenseRequestOnEmptyLicenseState() throws Exception {
|
||||
public void testSelfGeneratedTrialLicense() throws Exception {
|
||||
XPackLicenseState licenseState = new XPackLicenseState();
|
||||
setInitialState(null, licenseState, Settings.EMPTY);
|
||||
setInitialState(null, licenseState, Settings.EMPTY, "trial");
|
||||
when(discoveryNodes.isLocalNodeElectedMaster()).thenReturn(true);
|
||||
licenseService.start();
|
||||
|
||||
|
@ -31,6 +31,26 @@ public class LicenseRegistrationTests extends AbstractLicenseServiceTestCase {
|
|||
LicensesMetaData licenseMetaData = stateWithLicense.metaData().custom(LicensesMetaData.TYPE);
|
||||
assertNotNull(licenseMetaData);
|
||||
assertNotNull(licenseMetaData.getLicense());
|
||||
assertFalse(licenseMetaData.isEligibleForTrial());
|
||||
assertEquals("trial", licenseMetaData.getLicense().type());
|
||||
assertEquals(clock.millis() + LicenseService.SELF_GENERATED_LICENSE_DURATION.millis(), licenseMetaData.getLicense().expiryDate());
|
||||
}
|
||||
|
||||
public void testSelfGeneratedBasicLicense() throws Exception {
|
||||
XPackLicenseState licenseState = new XPackLicenseState();
|
||||
setInitialState(null, licenseState, Settings.EMPTY, "basic");
|
||||
when(discoveryNodes.isLocalNodeElectedMaster()).thenReturn(true);
|
||||
licenseService.start();
|
||||
|
||||
ClusterState state = ClusterState.builder(new ClusterName("a")).build();
|
||||
ArgumentCaptor<ClusterStateUpdateTask> stateUpdater = ArgumentCaptor.forClass(ClusterStateUpdateTask.class);
|
||||
verify(clusterService, Mockito.times(1)).submitStateUpdateTask(any(), stateUpdater.capture());
|
||||
ClusterState stateWithLicense = stateUpdater.getValue().execute(state);
|
||||
LicensesMetaData licenseMetaData = stateWithLicense.metaData().custom(LicensesMetaData.TYPE);
|
||||
assertNotNull(licenseMetaData);
|
||||
assertNotNull(licenseMetaData.getLicense());
|
||||
assertTrue(licenseMetaData.isEligibleForTrial());
|
||||
assertEquals("basic", licenseMetaData.getLicense().type());
|
||||
assertEquals(clock.millis() + LicenseService.SELF_GENERATED_LICENSE_DURATION.millis(), licenseMetaData.getLicense().expiryDate());
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
package org.elasticsearch.license;
|
||||
|
||||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.cluster.ClusterModule;
|
||||
import org.elasticsearch.cluster.metadata.MetaData;
|
||||
import org.elasticsearch.cluster.metadata.RepositoriesMetaData;
|
||||
|
@ -29,9 +30,10 @@ import static org.hamcrest.Matchers.notNullValue;
|
|||
import static org.hamcrest.Matchers.nullValue;
|
||||
|
||||
public class LicensesMetaDataSerializationTests extends ESTestCase {
|
||||
|
||||
public void testXContentSerializationOneSignedLicense() throws Exception {
|
||||
License license = TestUtils.generateSignedLicense(TimeValue.timeValueHours(2));
|
||||
LicensesMetaData licensesMetaData = new LicensesMetaData(license);
|
||||
LicensesMetaData licensesMetaData = new LicensesMetaData(license, null);
|
||||
XContentBuilder builder = XContentFactory.jsonBuilder();
|
||||
builder.startObject();
|
||||
builder.startObject("licenses");
|
||||
|
@ -40,12 +42,27 @@ public class LicensesMetaDataSerializationTests extends ESTestCase {
|
|||
builder.endObject();
|
||||
LicensesMetaData licensesMetaDataFromXContent = getLicensesMetaDataFromXContent(createParser(builder));
|
||||
assertThat(licensesMetaDataFromXContent.getLicense(), equalTo(license));
|
||||
assertNull(licensesMetaDataFromXContent.getMostRecentTrialVersion());
|
||||
}
|
||||
|
||||
public void testXContentSerializationOneSignedLicenseWithUsedTrial() throws Exception {
|
||||
License license = TestUtils.generateSignedLicense(TimeValue.timeValueHours(2));
|
||||
LicensesMetaData licensesMetaData = new LicensesMetaData(license, Version.CURRENT);
|
||||
XContentBuilder builder = XContentFactory.jsonBuilder();
|
||||
builder.startObject();
|
||||
builder.startObject("licenses");
|
||||
licensesMetaData.toXContent(builder, ToXContent.EMPTY_PARAMS);
|
||||
builder.endObject();
|
||||
builder.endObject();
|
||||
LicensesMetaData licensesMetaDataFromXContent = getLicensesMetaDataFromXContent(createParser(builder));
|
||||
assertThat(licensesMetaDataFromXContent.getLicense(), equalTo(license));
|
||||
assertEquals(licensesMetaDataFromXContent.getMostRecentTrialVersion(), Version.CURRENT);
|
||||
}
|
||||
|
||||
public void testLicenseMetadataParsingDoesNotSwallowOtherMetaData() throws Exception {
|
||||
new Licensing(Settings.EMPTY); // makes sure LicensePlugin is registered in Custom MetaData
|
||||
License license = TestUtils.generateSignedLicense(TimeValue.timeValueHours(2));
|
||||
LicensesMetaData licensesMetaData = new LicensesMetaData(license);
|
||||
LicensesMetaData licensesMetaData = new LicensesMetaData(license, Version.CURRENT);
|
||||
RepositoryMetaData repositoryMetaData = new RepositoryMetaData("repo", "fs", Settings.EMPTY);
|
||||
RepositoriesMetaData repositoriesMetaData = new RepositoriesMetaData(repositoryMetaData);
|
||||
final MetaData.Builder metaDataBuilder = MetaData.builder();
|
||||
|
@ -79,7 +96,7 @@ public class LicensesMetaDataSerializationTests extends ESTestCase {
|
|||
.type(randomBoolean() ? "trial" : "basic")
|
||||
.expiryDate(issueDate + TimeValue.timeValueHours(2).getMillis());
|
||||
final License trialLicense = SelfGeneratedLicense.create(specBuilder);
|
||||
LicensesMetaData licensesMetaData = new LicensesMetaData(trialLicense);
|
||||
LicensesMetaData licensesMetaData = new LicensesMetaData(trialLicense, Version.CURRENT);
|
||||
XContentBuilder builder = XContentFactory.jsonBuilder();
|
||||
builder.startObject();
|
||||
builder.startObject("licenses");
|
||||
|
@ -88,6 +105,7 @@ public class LicensesMetaDataSerializationTests extends ESTestCase {
|
|||
builder.endObject();
|
||||
LicensesMetaData licensesMetaDataFromXContent = getLicensesMetaDataFromXContent(createParser(builder));
|
||||
assertThat(licensesMetaDataFromXContent.getLicense(), equalTo(trialLicense));
|
||||
assertEquals(licensesMetaDataFromXContent.getMostRecentTrialVersion(), Version.CURRENT);
|
||||
}
|
||||
|
||||
public void testLicenseTombstoneFromXContext() throws Exception {
|
||||
|
@ -101,6 +119,19 @@ public class LicensesMetaDataSerializationTests extends ESTestCase {
|
|||
assertThat(metaDataFromXContent.getLicense(), equalTo(LicensesMetaData.LICENSE_TOMBSTONE));
|
||||
}
|
||||
|
||||
public void testLicenseTombstoneWithUsedTrialFromXContext() throws Exception {
|
||||
final XContentBuilder builder = XContentFactory.jsonBuilder();
|
||||
builder.startObject();
|
||||
builder.startObject("licenses");
|
||||
builder.nullField("license");
|
||||
builder.field("trial_license", Version.CURRENT.toString());
|
||||
builder.endObject();
|
||||
builder.endObject();
|
||||
LicensesMetaData metaDataFromXContent = getLicensesMetaDataFromXContent(createParser(builder));
|
||||
assertThat(metaDataFromXContent.getLicense(), equalTo(LicensesMetaData.LICENSE_TOMBSTONE));
|
||||
assertEquals(metaDataFromXContent.getMostRecentTrialVersion(), Version.CURRENT);
|
||||
}
|
||||
|
||||
private static LicensesMetaData getLicensesMetaDataFromXContent(XContentParser parser) throws Exception {
|
||||
parser.nextToken(); // consume null
|
||||
parser.nextToken(); // consume "licenses"
|
||||
|
|
|
@ -348,6 +348,6 @@ public class TestUtils {
|
|||
}
|
||||
|
||||
public static void putLicense(MetaData.Builder builder, License license) {
|
||||
builder.putCustom(LicensesMetaData.TYPE, new LicensesMetaData(license));
|
||||
builder.putCustom(LicensesMetaData.TYPE, new LicensesMetaData(license, null));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* 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.client.Response;
|
||||
import org.elasticsearch.client.ResponseException;
|
||||
import org.elasticsearch.client.RestClient;
|
||||
import org.elasticsearch.common.io.Streams;
|
||||
import org.elasticsearch.common.network.NetworkModule;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.plugins.Plugin;
|
||||
import org.elasticsearch.test.ESIntegTestCase;
|
||||
import org.elasticsearch.transport.Netty4Plugin;
|
||||
import org.elasticsearch.xpack.XPackPlugin;
|
||||
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
import static org.elasticsearch.test.ESIntegTestCase.Scope.SUITE;
|
||||
|
||||
@ESIntegTestCase.ClusterScope(scope = SUITE)
|
||||
public class UpgradeToTrialTests extends AbstractLicensesIntegrationTestCase {
|
||||
|
||||
@Override
|
||||
protected Settings nodeSettings(int nodeOrdinal) {
|
||||
return Settings.builder()
|
||||
.put(super.nodeSettings(nodeOrdinal))
|
||||
.put("node.data", true)
|
||||
.put(LicenseService.SELF_GENERATED_LICENSE_TYPE.getKey(), "basic")
|
||||
.put(NetworkModule.HTTP_ENABLED.getKey(), true).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<Class<? extends Plugin>> nodePlugins() {
|
||||
return Arrays.asList(XPackPlugin.class, Netty4Plugin.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<Class<? extends Plugin>> transportClientPlugins() {
|
||||
return nodePlugins();
|
||||
}
|
||||
|
||||
public void testUpgradeToTrial() throws Exception {
|
||||
LicensingClient licensingClient = new LicensingClient(client());
|
||||
GetLicenseResponse getLicenseResponse = licensingClient.prepareGetLicense().get();
|
||||
|
||||
assertEquals("basic", getLicenseResponse.license().type());
|
||||
|
||||
RestClient restClient = getRestClient();
|
||||
Response response = restClient.performRequest("GET", "/_xpack/license/trial_status");
|
||||
String body = Streams.copyToString(new InputStreamReader(response.getEntity().getContent(), StandardCharsets.UTF_8));
|
||||
assertEquals(200, response.getStatusLine().getStatusCode());
|
||||
assertEquals("{\"eligible_to_start_trial\":true}", body);
|
||||
|
||||
Response response2 = restClient.performRequest("POST", "/_xpack/license/start_trial");
|
||||
String body2 = Streams.copyToString(new InputStreamReader(response2.getEntity().getContent(), StandardCharsets.UTF_8));
|
||||
assertEquals(200, response2.getStatusLine().getStatusCode());
|
||||
assertEquals("{\"trial_was_started\":true}", body2);
|
||||
|
||||
Response response3 = restClient.performRequest("GET", "/_xpack/license/trial_status");
|
||||
String body3 = Streams.copyToString(new InputStreamReader(response3.getEntity().getContent(), StandardCharsets.UTF_8));
|
||||
assertEquals(200, response3.getStatusLine().getStatusCode());
|
||||
assertEquals("{\"eligible_to_start_trial\":false}", body3);
|
||||
|
||||
ResponseException ex = expectThrows(ResponseException.class,
|
||||
() -> restClient.performRequest("POST", "/_xpack/license/start_trial"));
|
||||
Response response4 = ex.getResponse();
|
||||
String body4 = Streams.copyToString(new InputStreamReader(response4.getEntity().getContent(), StandardCharsets.UTF_8));
|
||||
assertEquals(403, response4.getStatusLine().getStatusCode());
|
||||
assertTrue(body4.contains("\"trial_was_started\":false"));
|
||||
assertTrue(body4.contains("\"error_message\":\"Operation failed: Trial was already activated.\""));
|
||||
}
|
||||
}
|
|
@ -158,3 +158,5 @@ indices:data/write/delete/byquery
|
|||
indices:data/write/reindex
|
||||
cluster:admin/xpack/deprecation/info
|
||||
cluster:admin/xpack/ml/job/forecast
|
||||
cluster:admin/xpack/license/start_trial
|
||||
cluster:admin/xpack/license/trial_status
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"xpack.license.get_trial_status": {
|
||||
"documentation": "https://www.elastic.co/guide/en/x-pack/current/license-management.html",
|
||||
"methods": ["GET"],
|
||||
"url": {
|
||||
"path": "/_xpack/license/trial_status",
|
||||
"paths": ["/_xpack/license/trial_status"],
|
||||
"parts" : {
|
||||
},
|
||||
"params": {
|
||||
}
|
||||
},
|
||||
"body": null
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"xpack.license.post_start_trial": {
|
||||
"documentation": "https://www.elastic.co/guide/en/x-pack/current/license-management.html",
|
||||
"methods": ["POST"],
|
||||
"url": {
|
||||
"path": "/_xpack/license/start_trial",
|
||||
"paths": ["/_xpack/license/start_trial"],
|
||||
"parts" : {
|
||||
},
|
||||
"params": {
|
||||
}
|
||||
},
|
||||
"body": null
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue