mirror of
https://github.com/honeymoose/OpenSearch.git
synced 2025-03-25 01:19:02 +00:00
Support "enterprise" license types (#49474)
This adds "enterprise" as an acceptable type for a license loaded through the PUT _license API. Internally an enterprise license is treated as having a "platinum" operating mode. The handling of License types was refactored to have a new explicit "LicenseType" enum in addition to the existing "OperatingMode" enum. By default (in 7.x) the GET license API will return "platinum" when an enterprise license is active in order to be compatible with existing consumers of that API. A new "accept_enterprise" flag has been introduced to allow clients to opt-in to receive the correct "enterprise" type. Backport of: #49223
This commit is contained in:
parent
54467b5d8b
commit
47e5e34f42
@ -88,6 +88,9 @@ public class LicenseGeneratorTool extends LoggingAwareCommand {
|
||||
ExitCodes.USAGE,
|
||||
"Must specify either --license or --licenseFile");
|
||||
}
|
||||
if (licenseSpec == null) {
|
||||
throw new UserException(ExitCodes.DATA_ERROR, "Could not parse license spec");
|
||||
}
|
||||
|
||||
// sign
|
||||
License license = new LicenseSigner(privateKeyPath, publicKeyPath).sign(licenseSpec);
|
||||
|
@ -5,15 +5,6 @@
|
||||
*/
|
||||
package org.elasticsearch.license;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.apache.lucene.util.CollectionUtil;
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.ElasticsearchParseException;
|
||||
@ -31,11 +22,83 @@ import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.protocol.xpack.license.LicenseStatus;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Data structure for license. Use {@link Builder} to build a license.
|
||||
* Provides serialization/deserialization & validation methods for license object
|
||||
*/
|
||||
public class License implements ToXContentObject {
|
||||
|
||||
public enum LicenseType {
|
||||
BASIC,
|
||||
STANDARD,
|
||||
GOLD,
|
||||
PLATINUM,
|
||||
ENTERPRISE,
|
||||
TRIAL;
|
||||
|
||||
public String getTypeName() {
|
||||
return name().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
public static LicenseType parse(String type) throws IllegalArgumentException {
|
||||
try {
|
||||
return LicenseType.valueOf(type.toUpperCase(Locale.ROOT));
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new IllegalArgumentException("unrecognised license type [ " + type + "], supported license types are ["
|
||||
+ Stream.of(values()).map(LicenseType::getTypeName).collect(Collectors.joining(",")) + "]");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Backward compatible license type parsing for older license models
|
||||
*/
|
||||
public static LicenseType resolve(String name) {
|
||||
switch (name.toLowerCase(Locale.ROOT)) {
|
||||
case "missing":
|
||||
return null;
|
||||
case "trial":
|
||||
case "none": // bwc for 1.x subscription_type field
|
||||
case "dev": // bwc for 1.x subscription_type field
|
||||
case "development": // bwc for 1.x subscription_type field
|
||||
return TRIAL;
|
||||
case "basic":
|
||||
return BASIC;
|
||||
case "standard":
|
||||
return STANDARD;
|
||||
case "silver":
|
||||
case "gold":
|
||||
return GOLD;
|
||||
case "platinum":
|
||||
case "cloud_internal":
|
||||
case "internal": // bwc for 1.x subscription_type field
|
||||
return PLATINUM;
|
||||
case "enterprise":
|
||||
return ENTERPRISE;
|
||||
default:
|
||||
throw new IllegalArgumentException("unknown license type [" + name + "]");
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isBasic(String typeName) {
|
||||
return BASIC.getTypeName().equals(typeName);
|
||||
}
|
||||
|
||||
static boolean isTrial(String typeName) {
|
||||
return TRIAL.getTypeName().equals(typeName);
|
||||
}
|
||||
}
|
||||
|
||||
public static final int VERSION_START = 1;
|
||||
public static final int VERSION_NO_FEATURE_TYPE = 2;
|
||||
public static final int VERSION_START_DATE = 3;
|
||||
@ -49,6 +112,12 @@ public class License implements ToXContentObject {
|
||||
* and in a human readable format
|
||||
*/
|
||||
public static final String REST_VIEW_MODE = "rest_view";
|
||||
/**
|
||||
* XContent param name to map the "enterprise" license type to "platinum"
|
||||
* for backwards compatibility with older clients
|
||||
*/
|
||||
public static final String XCONTENT_HIDE_ENTERPRISE = "hide_enterprise";
|
||||
|
||||
/**
|
||||
* XContent param name to deserialize license(s) with
|
||||
* no signature
|
||||
@ -102,28 +171,25 @@ public class License implements ToXContentObject {
|
||||
return Integer.compare(opMode1.id, opMode2.id);
|
||||
}
|
||||
|
||||
public static OperationMode resolve(String type) {
|
||||
switch (type.toLowerCase(Locale.ROOT)) {
|
||||
case "missing":
|
||||
return MISSING;
|
||||
case "trial":
|
||||
case "none": // bwc for 1.x subscription_type field
|
||||
case "dev": // bwc for 1.x subscription_type field
|
||||
case "development": // bwc for 1.x subscription_type field
|
||||
return TRIAL;
|
||||
case "basic":
|
||||
public static OperationMode resolve(String typeName) {
|
||||
LicenseType type = LicenseType.resolve(typeName);
|
||||
if (type == null) {
|
||||
return MISSING;
|
||||
}
|
||||
switch (type) {
|
||||
case BASIC:
|
||||
return BASIC;
|
||||
case "standard":
|
||||
case STANDARD:
|
||||
return STANDARD;
|
||||
case "silver":
|
||||
case "gold":
|
||||
case GOLD:
|
||||
return GOLD;
|
||||
case "platinum":
|
||||
case "cloud_internal":
|
||||
case "internal": // bwc for 1.x subscription_type field
|
||||
case PLATINUM:
|
||||
case ENTERPRISE: // TODO Add an explicit enterprise operating mode
|
||||
return PLATINUM;
|
||||
case TRIAL:
|
||||
return TRIAL;
|
||||
default:
|
||||
throw new IllegalArgumentException("unknown type [" + type + "]");
|
||||
throw new IllegalArgumentException("unsupported license type [" + type.getTypeName() + "]");
|
||||
}
|
||||
}
|
||||
|
||||
@ -301,7 +367,7 @@ public class License implements ToXContentObject {
|
||||
throw new IllegalStateException("maxNodes has to be set");
|
||||
} else if (expiryDate == -1) {
|
||||
throw new IllegalStateException("expiryDate has to be set");
|
||||
} else if (expiryDate == LicenseService.BASIC_SELF_GENERATED_LICENSE_EXPIRATION_MILLIS && "basic".equals(type) == false) {
|
||||
} else if (expiryDate == LicenseService.BASIC_SELF_GENERATED_LICENSE_EXPIRATION_MILLIS && LicenseType.isBasic(type) == false) {
|
||||
throw new IllegalStateException("only basic licenses are allowed to have no expiration");
|
||||
}
|
||||
}
|
||||
@ -377,6 +443,7 @@ public class License implements ToXContentObject {
|
||||
public XContentBuilder toInnerXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
boolean licenseSpecMode = params.paramAsBoolean(LICENSE_SPEC_VIEW_MODE, false);
|
||||
boolean restViewMode = params.paramAsBoolean(REST_VIEW_MODE, false);
|
||||
boolean hideEnterprise = params.paramAsBoolean(XCONTENT_HIDE_ENTERPRISE, false);
|
||||
boolean previouslyHumanReadable = builder.humanReadable();
|
||||
if (licenseSpecMode && restViewMode) {
|
||||
throw new IllegalArgumentException("can have either " + REST_VIEW_MODE + " or " + LICENSE_SPEC_VIEW_MODE);
|
||||
@ -395,7 +462,10 @@ public class License implements ToXContentObject {
|
||||
builder.field(Fields.STATUS, status().label());
|
||||
}
|
||||
builder.field(Fields.UID, uid);
|
||||
builder.field(Fields.TYPE, type);
|
||||
|
||||
final String bwcType = hideEnterprise && "enterprise".equals(type) ? "platinum" : type;
|
||||
builder.field(Fields.TYPE, bwcType);
|
||||
|
||||
if (version == VERSION_START) {
|
||||
builder.field(Fields.SUBSCRIPTION_TYPE, subscriptionType);
|
||||
}
|
||||
@ -689,6 +759,10 @@ public class License implements ToXContentObject {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder type(LicenseType type) {
|
||||
return type(type.getTypeName());
|
||||
}
|
||||
|
||||
public Builder type(String type) {
|
||||
this.type = type;
|
||||
return this;
|
||||
@ -778,6 +852,7 @@ public class License implements ToXContentObject {
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ import org.elasticsearch.common.settings.Setting;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.time.DateFormatter;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.common.util.set.Sets;
|
||||
import org.elasticsearch.discovery.DiscoveryModule;
|
||||
import org.elasticsearch.env.Environment;
|
||||
import org.elasticsearch.gateway.GatewayService;
|
||||
@ -39,15 +40,14 @@ import org.elasticsearch.xpack.core.scheduler.SchedulerEngine;
|
||||
|
||||
import java.time.Clock;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Service responsible for managing {@link LicensesMetaData}.
|
||||
@ -59,19 +59,17 @@ import java.util.concurrent.atomic.AtomicReference;
|
||||
public class LicenseService extends AbstractLifecycleComponent implements ClusterStateListener, SchedulerEngine.Listener {
|
||||
private static final Logger logger = LogManager.getLogger(LicenseService.class);
|
||||
|
||||
public static final Setting<String> SELF_GENERATED_LICENSE_TYPE = new Setting<>("xpack.license.self_generated.type",
|
||||
(s) -> "basic", (s) -> {
|
||||
if (SelfGeneratedLicense.validSelfGeneratedType(s)) {
|
||||
return s;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Illegal self generated license type [" + s + "]. Must be trial or basic.");
|
||||
}
|
||||
public static final Setting<License.LicenseType> SELF_GENERATED_LICENSE_TYPE = new Setting<>("xpack.license.self_generated.type",
|
||||
(s) -> License.LicenseType.BASIC.getTypeName(), (s) -> {
|
||||
final License.LicenseType type = License.LicenseType.parse(s);
|
||||
return SelfGeneratedLicense.validateSelfGeneratedType(type);
|
||||
}, Setting.Property.NodeScope);
|
||||
|
||||
// pkg private for tests
|
||||
static final TimeValue NON_BASIC_SELF_GENERATED_LICENSE_DURATION = TimeValue.timeValueHours(30 * 24);
|
||||
|
||||
static final Set<String> VALID_TRIAL_TYPES = new HashSet<>(Arrays.asList("trial", "platinum", "gold"));
|
||||
static final Set<License.LicenseType> VALID_TRIAL_TYPES = Collections.unmodifiableSet(Sets.newHashSet(
|
||||
License.LicenseType.GOLD, License.LicenseType.PLATINUM, License.LicenseType.ENTERPRISE, License.LicenseType.TRIAL));
|
||||
|
||||
/**
|
||||
* Duration of grace period after a license has expired
|
||||
@ -79,7 +77,7 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste
|
||||
static final TimeValue GRACE_PERIOD_DURATION = days(7);
|
||||
|
||||
public static final long BASIC_SELF_GENERATED_LICENSE_EXPIRATION_MILLIS =
|
||||
XPackInfoResponse.BASIC_SELF_GENERATED_LICENSE_EXPIRATION_MILLIS;
|
||||
XPackInfoResponse.BASIC_SELF_GENERATED_LICENSE_EXPIRATION_MILLIS;
|
||||
|
||||
private final Settings settings;
|
||||
|
||||
@ -117,7 +115,7 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste
|
||||
private static final DateFormatter DATE_FORMATTER = DateFormatter.forPattern("EEEE, MMMM dd, yyyy");
|
||||
|
||||
private static final String ACKNOWLEDGEMENT_HEADER = "This license update requires acknowledgement. To acknowledge the license, " +
|
||||
"please read the following messages and update the license again, this time with the \"acknowledge=true\" parameter:";
|
||||
"please read the following messages and update the license again, this time with the \"acknowledge=true\" parameter:";
|
||||
|
||||
public LicenseService(Settings settings, ClusterService clusterService, Clock clock, Environment env,
|
||||
ResourceWatcherService resourceWatcherService, XPackLicenseState licenseState) {
|
||||
@ -199,7 +197,7 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste
|
||||
final long now = clock.millis();
|
||||
if (!LicenseVerifier.verifyLicense(newLicense) || newLicense.issueDate() > now || newLicense.startDate() > now) {
|
||||
listener.onResponse(new PutLicenseResponse(true, LicensesStatus.INVALID));
|
||||
} else if (newLicense.type().equals("basic")) {
|
||||
} else if (newLicense.type().equals(License.LicenseType.BASIC.getTypeName())) {
|
||||
listener.onFailure(new IllegalArgumentException("Registering basic licenses is not allowed."));
|
||||
} else if (newLicense.expiryDate() < now) {
|
||||
listener.onResponse(new PutLicenseResponse(true, LicensesStatus.EXPIRED));
|
||||
@ -212,7 +210,7 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste
|
||||
if (acknowledgeMessages.isEmpty() == false) {
|
||||
// needs acknowledgement
|
||||
listener.onResponse(new PutLicenseResponse(false, LicensesStatus.VALID, ACKNOWLEDGEMENT_HEADER,
|
||||
acknowledgeMessages));
|
||||
acknowledgeMessages));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -239,36 +237,49 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste
|
||||
}
|
||||
|
||||
clusterService.submitStateUpdateTask("register license [" + newLicense.uid() + "]", new
|
||||
AckedClusterStateUpdateTask<PutLicenseResponse>(request, listener) {
|
||||
@Override
|
||||
protected PutLicenseResponse newResponse(boolean acknowledged) {
|
||||
return new PutLicenseResponse(acknowledged, LicensesStatus.VALID);
|
||||
}
|
||||
AckedClusterStateUpdateTask<PutLicenseResponse>(request, listener) {
|
||||
@Override
|
||||
protected PutLicenseResponse newResponse(boolean acknowledged) {
|
||||
return new PutLicenseResponse(acknowledged, LicensesStatus.VALID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClusterState execute(ClusterState currentState) throws Exception {
|
||||
XPackPlugin.checkReadyForXPackCustomMetadata(currentState);
|
||||
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();
|
||||
@Override
|
||||
public ClusterState execute(ClusterState currentState) throws Exception {
|
||||
XPackPlugin.checkReadyForXPackCustomMetadata(currentState);
|
||||
final Version oldestNodeVersion = currentState.nodes().getSmallestNonClientNodeVersion();
|
||||
if (licenseIsCompatible(newLicense, oldestNodeVersion) == false) {
|
||||
throw new IllegalStateException("The provided license is not compatible with node version [" +
|
||||
oldestNodeVersion + "]");
|
||||
}
|
||||
});
|
||||
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();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean licenseIsCompatible(License license, Version version) {
|
||||
if (License.LicenseType.ENTERPRISE.getTypeName().equalsIgnoreCase(license.type())) {
|
||||
return version.onOrAfter(Version.V_7_6_0);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static Map<String, String[]> getAckMessages(License newLicense, License currentLicense) {
|
||||
Map<String, String[]> acknowledgeMessages = new HashMap<>();
|
||||
if (!License.isAutoGeneratedLicense(currentLicense.signature()) // current license is not auto-generated
|
||||
&& currentLicense.issueDate() > newLicense.issueDate()) { // and has a later issue date
|
||||
acknowledgeMessages.put("license", new String[]{
|
||||
"The new license is older than the currently installed license. " +
|
||||
"Are you sure you want to override the current license?"});
|
||||
&& currentLicense.issueDate() > newLicense.issueDate()) { // and has a later issue date
|
||||
acknowledgeMessages.put("license", new String[] {
|
||||
"The new license is older than the currently installed license. " +
|
||||
"Are you sure you want to override the current license?" });
|
||||
}
|
||||
XPackLicenseState.ACKNOWLEDGMENT_MESSAGES.forEach((feature, ackMessages) -> {
|
||||
String[] messages = ackMessages.apply(currentLicense.operationMode(), newLicense.operationMode());
|
||||
@ -293,8 +304,8 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste
|
||||
updateLicenseState(license, licensesMetaData.getMostRecentTrialVersion());
|
||||
} else if (event.getJobName().startsWith(ExpirationCallback.EXPIRATION_JOB_PREFIX)) {
|
||||
expirationCallbacks.stream()
|
||||
.filter(expirationCallback -> expirationCallback.getId().equals(event.getJobName()))
|
||||
.forEach(expirationCallback -> expirationCallback.on(license));
|
||||
.filter(expirationCallback -> expirationCallback.getId().equals(event.getJobName()))
|
||||
.forEach(expirationCallback -> expirationCallback.on(license));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -304,27 +315,27 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste
|
||||
*/
|
||||
public void removeLicense(final DeleteLicenseRequest request, final ActionListener<ClusterStateUpdateResponse> listener) {
|
||||
clusterService.submitStateUpdateTask("delete license",
|
||||
new AckedClusterStateUpdateTask<ClusterStateUpdateResponse>(request, listener) {
|
||||
@Override
|
||||
protected ClusterStateUpdateResponse newResponse(boolean acknowledged) {
|
||||
return new ClusterStateUpdateResponse(acknowledged);
|
||||
}
|
||||
new AckedClusterStateUpdateTask<ClusterStateUpdateResponse>(request, listener) {
|
||||
@Override
|
||||
protected ClusterStateUpdateResponse newResponse(boolean acknowledged) {
|
||||
return new ClusterStateUpdateResponse(acknowledged);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClusterState execute(ClusterState currentState) throws Exception {
|
||||
MetaData metaData = currentState.metaData();
|
||||
final LicensesMetaData currentLicenses = metaData.custom(LicensesMetaData.TYPE);
|
||||
if (currentLicenses.getLicense() != LicensesMetaData.LICENSE_TOMBSTONE) {
|
||||
MetaData.Builder mdBuilder = MetaData.builder(currentState.metaData());
|
||||
LicensesMetaData newMetadata = new LicensesMetaData(LicensesMetaData.LICENSE_TOMBSTONE,
|
||||
currentLicenses.getMostRecentTrialVersion());
|
||||
mdBuilder.putCustom(LicensesMetaData.TYPE, newMetadata);
|
||||
return ClusterState.builder(currentState).metaData(mdBuilder).build();
|
||||
} else {
|
||||
return currentState;
|
||||
}
|
||||
@Override
|
||||
public ClusterState execute(ClusterState currentState) throws Exception {
|
||||
MetaData metaData = currentState.metaData();
|
||||
final LicensesMetaData currentLicenses = metaData.custom(LicensesMetaData.TYPE);
|
||||
if (currentLicenses.getLicense() != LicensesMetaData.LICENSE_TOMBSTONE) {
|
||||
MetaData.Builder mdBuilder = MetaData.builder(currentState.metaData());
|
||||
LicensesMetaData newMetadata = new LicensesMetaData(LicensesMetaData.LICENSE_TOMBSTONE,
|
||||
currentLicenses.getMostRecentTrialVersion());
|
||||
mdBuilder.putCustom(LicensesMetaData.TYPE, newMetadata);
|
||||
return ClusterState.builder(currentState).metaData(mdBuilder).build();
|
||||
} else {
|
||||
return currentState;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public License getLicense() {
|
||||
@ -337,9 +348,10 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste
|
||||
}
|
||||
|
||||
void startTrialLicense(PostStartTrialRequest request, final ActionListener<PostStartTrialResponse> listener) {
|
||||
if (VALID_TRIAL_TYPES.contains(request.getType()) == false) {
|
||||
throw new IllegalArgumentException("Cannot start trial of type [" + request.getType() + "]. Valid trial types are "
|
||||
+ VALID_TRIAL_TYPES + ".");
|
||||
License.LicenseType requestedType = License.LicenseType.parse(request.getType());
|
||||
if (VALID_TRIAL_TYPES.contains(requestedType) == false) {
|
||||
throw new IllegalArgumentException("Cannot start trial of type [" + requestedType.getTypeName() + "]. Valid trial types are ["
|
||||
+ VALID_TRIAL_TYPES.stream().map(License.LicenseType::getTypeName).sorted().collect(Collectors.joining(",")) + "]");
|
||||
}
|
||||
StartTrialClusterTask task = new StartTrialClusterTask(logger, clusterService.getClusterName().value(), clock, request, listener);
|
||||
clusterService.submitStateUpdateTask("started trial license", task);
|
||||
@ -358,7 +370,7 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste
|
||||
*/
|
||||
private void registerOrUpdateSelfGeneratedLicense() {
|
||||
clusterService.submitStateUpdateTask("maybe generate license for cluster",
|
||||
new StartupSelfGeneratedLicenseTask(settings, clock, clusterService));
|
||||
new StartupSelfGeneratedLicenseTask(settings, clock, clusterService));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -369,11 +381,11 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste
|
||||
if (clusterService.lifecycleState() == Lifecycle.State.STARTED) {
|
||||
final ClusterState clusterState = clusterService.state();
|
||||
if (clusterState.blocks().hasGlobalBlock(GatewayService.STATE_NOT_RECOVERED_BLOCK) == false &&
|
||||
clusterState.nodes().getMasterNode() != null && XPackPlugin.isReadyForXPackCustomMetadata(clusterState)) {
|
||||
clusterState.nodes().getMasterNode() != null && XPackPlugin.isReadyForXPackCustomMetadata(clusterState)) {
|
||||
final LicensesMetaData currentMetaData = clusterState.metaData().custom(LicensesMetaData.TYPE);
|
||||
boolean noLicense = currentMetaData == null || currentMetaData.getLicense() == null;
|
||||
if (clusterState.getNodes().isLocalNodeElectedMaster() &&
|
||||
(noLicense || LicenseUtils.licenseNeedsExtended(currentMetaData.getLicense()))) {
|
||||
(noLicense || LicenseUtils.licenseNeedsExtended(currentMetaData.getLicense()))) {
|
||||
// triggers a cluster changed event eventually notifying the current licensee
|
||||
registerOrUpdateSelfGeneratedLicense();
|
||||
}
|
||||
@ -416,7 +428,7 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste
|
||||
}
|
||||
// notify all interested plugins
|
||||
if (previousClusterState.blocks().hasGlobalBlock(GatewayService.STATE_NOT_RECOVERED_BLOCK)
|
||||
|| prevLicensesMetaData == null) {
|
||||
|| prevLicensesMetaData == null) {
|
||||
if (currentLicensesMetaData != null) {
|
||||
onUpdate(currentLicensesMetaData);
|
||||
}
|
||||
@ -438,8 +450,8 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste
|
||||
// auto-generate license if no licenses ever existed or if the current license is basic and
|
||||
// needs extended or if the license signature needs to be updated. this will trigger a subsequent cluster changed event
|
||||
if (currentClusterState.getNodes().isLocalNodeElectedMaster() &&
|
||||
(noLicense || LicenseUtils.licenseNeedsExtended(currentLicense) ||
|
||||
LicenseUtils.signatureNeedsUpdate(currentLicense, currentClusterState.nodes()))) {
|
||||
(noLicense || LicenseUtils.licenseNeedsExtended(currentLicense) ||
|
||||
LicenseUtils.signatureNeedsUpdate(currentLicense, currentClusterState.nodes()))) {
|
||||
registerOrUpdateSelfGeneratedLicense();
|
||||
}
|
||||
} else if (logger.isDebugEnabled()) {
|
||||
@ -501,15 +513,15 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste
|
||||
scheduler.add(new SchedulerEngine.Job(LICENSE_JOB, nextLicenseCheck(license)));
|
||||
for (ExpirationCallback expirationCallback : expirationCallbacks) {
|
||||
scheduler.add(new SchedulerEngine.Job(expirationCallback.getId(),
|
||||
(startTime, now) ->
|
||||
expirationCallback.nextScheduledTimeForExpiry(license.expiryDate(), startTime, now)));
|
||||
(startTime, now) ->
|
||||
expirationCallback.nextScheduledTimeForExpiry(license.expiryDate(), startTime, now)));
|
||||
}
|
||||
if (previousLicense != null) {
|
||||
// remove operationModeFileWatcher to gc the old license object
|
||||
previousLicense.removeOperationModeFileWatcher();
|
||||
}
|
||||
logger.info("license [{}] mode [{}] - valid", license.uid(),
|
||||
license.operationMode().name().toLowerCase(Locale.ROOT));
|
||||
license.operationMode().name().toLowerCase(Locale.ROOT));
|
||||
}
|
||||
updateLicenseState(license, currentLicensesMetaData.getMostRecentTrialVersion());
|
||||
}
|
||||
@ -547,7 +559,7 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste
|
||||
} else if (license != null) {
|
||||
boolean autoGeneratedLicense = License.isAutoGeneratedLicense(license.signature());
|
||||
if ((autoGeneratedLicense && SelfGeneratedLicense.verify(license))
|
||||
|| (!autoGeneratedLicense && LicenseVerifier.verifyLicense(license))) {
|
||||
|| (!autoGeneratedLicense && LicenseVerifier.verifyLicense(license))) {
|
||||
return license;
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ package org.elasticsearch.license;
|
||||
import org.elasticsearch.ElasticsearchSecurityException;
|
||||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.cluster.node.DiscoveryNodes;
|
||||
import org.elasticsearch.license.License.LicenseType;
|
||||
import org.elasticsearch.rest.RestStatus;
|
||||
|
||||
import java.util.stream.StreamSupport;
|
||||
@ -39,7 +40,8 @@ public class LicenseUtils {
|
||||
}
|
||||
|
||||
public static boolean licenseNeedsExtended(License license) {
|
||||
return "basic".equals(license.type()) && license.expiryDate() != LicenseService.BASIC_SELF_GENERATED_LICENSE_EXPIRATION_MILLIS;
|
||||
return LicenseType.isBasic(license.type()) &&
|
||||
license.expiryDate() != LicenseService.BASIC_SELF_GENERATED_LICENSE_EXPIRATION_MILLIS;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -49,7 +51,8 @@ public class LicenseUtils {
|
||||
public static boolean signatureNeedsUpdate(License license, DiscoveryNodes currentNodes) {
|
||||
assert License.VERSION_CRYPTO_ALGORITHMS == License.VERSION_CURRENT : "update this method when adding a new version";
|
||||
|
||||
return ("basic".equals(license.type()) || "trial".equals(license.type())) &&
|
||||
String typeName = license.type();
|
||||
return (LicenseType.isBasic(typeName) || LicenseType.isTrial(typeName)) &&
|
||||
// only upgrade signature when all nodes are ready to deserialize the new signature
|
||||
(license.version() < License.VERSION_CRYPTO_ALGORITHMS &&
|
||||
compatibleLicenseVersion(currentNodes) == License.VERSION_CRYPTO_ALGORITHMS
|
||||
|
@ -40,7 +40,7 @@ public class LicensesMetaData extends AbstractNamedDiffable<MetaData.Custom> imp
|
||||
* ever existed in the cluster state
|
||||
*/
|
||||
public static final License LICENSE_TOMBSTONE = License.builder()
|
||||
.type("trial")
|
||||
.type(License.LicenseType.TRIAL)
|
||||
.issuer("elasticsearch")
|
||||
.uid("TOMBSTONE")
|
||||
.issuedTo("")
|
||||
|
@ -51,9 +51,14 @@ public class RestGetLicenseAction extends XPackRestHandler {
|
||||
*/
|
||||
@Override
|
||||
public RestChannelConsumer doPrepareRequest(final RestRequest request, final XPackClient client) throws IOException {
|
||||
final Map<String, String> overrideParams = new HashMap<>(2);
|
||||
final Map<String, String> overrideParams = new HashMap<>(3);
|
||||
overrideParams.put(License.REST_VIEW_MODE, "true");
|
||||
overrideParams.put(License.LICENSE_VERSION_MODE, String.valueOf(License.VERSION_CURRENT));
|
||||
|
||||
// Hide enterprise licenses by default, there is an opt-in flag to show them
|
||||
final boolean hideEnterprise = request.paramAsBoolean("accept_enterprise", false) == false;
|
||||
overrideParams.put(License.XCONTENT_HIDE_ENTERPRISE, String.valueOf(hideEnterprise));
|
||||
|
||||
final ToXContent.Params params = new ToXContent.DelegatingMapParams(overrideParams, request);
|
||||
GetLicenseRequest getLicenseRequest = new GetLicenseRequest();
|
||||
getLicenseRequest.local(request.paramAsBoolean("local", getLicenseRequest.local()));
|
||||
|
@ -36,7 +36,7 @@ public class RestPostStartTrialLicense extends XPackRestHandler {
|
||||
@Override
|
||||
protected RestChannelConsumer doPrepareRequest(RestRequest request, XPackClient client) throws IOException {
|
||||
PostStartTrialRequest startTrialRequest = new PostStartTrialRequest();
|
||||
startTrialRequest.setType(request.param("type", "trial"));
|
||||
startTrialRequest.setType(request.param("type", License.LicenseType.TRIAL.getTypeName()));
|
||||
startTrialRequest.acknowledge(request.paramAsBoolean("acknowledge", false));
|
||||
return channel -> client.licensing().postStartTrial(startTrialRequest,
|
||||
new RestBuilderListener<PostStartTrialResponse>(channel) {
|
||||
|
@ -49,9 +49,9 @@ public class RestPutLicenseAction extends XPackRestHandler {
|
||||
putLicenseRequest.timeout(request.paramAsTime("timeout", putLicenseRequest.timeout()));
|
||||
putLicenseRequest.masterNodeTimeout(request.paramAsTime("master_timeout", putLicenseRequest.masterNodeTimeout()));
|
||||
|
||||
if ("basic".equals(putLicenseRequest.license().type())) {
|
||||
if (License.LicenseType.isBasic(putLicenseRequest.license().type())) {
|
||||
throw new IllegalArgumentException("Installing basic licenses is no longer allowed. Use the POST " +
|
||||
"/_license/start_basic API to install a basic license that does not expire.");
|
||||
"/_license/start_basic API to install a basic license that does not expire.");
|
||||
}
|
||||
|
||||
return channel -> client.es().admin().cluster().execute(PutLicenseAction.INSTANCE, putLicenseRequest,
|
||||
|
@ -20,10 +20,10 @@ import java.nio.ByteBuffer;
|
||||
import java.util.Base64;
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.elasticsearch.license.CryptUtils.encryptV3Format;
|
||||
import static org.elasticsearch.license.CryptUtils.encrypt;
|
||||
import static org.elasticsearch.license.CryptUtils.decryptV3Format;
|
||||
import static org.elasticsearch.license.CryptUtils.decrypt;
|
||||
import static org.elasticsearch.license.CryptUtils.decryptV3Format;
|
||||
import static org.elasticsearch.license.CryptUtils.encrypt;
|
||||
import static org.elasticsearch.license.CryptUtils.encryptV3Format;
|
||||
|
||||
class SelfGeneratedLicense {
|
||||
|
||||
@ -83,7 +83,13 @@ class SelfGeneratedLicense {
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean validSelfGeneratedType(String type) {
|
||||
return "basic".equals(type) || "trial".equals(type);
|
||||
static License.LicenseType validateSelfGeneratedType(License.LicenseType type) {
|
||||
switch (type) {
|
||||
case BASIC:
|
||||
case TRIAL:
|
||||
return type;
|
||||
}
|
||||
throw new IllegalArgumentException("invalid self generated license type [" + type + "], only " +
|
||||
License.LicenseType.BASIC + " and " + License.LicenseType.TRIAL + " are accepted");
|
||||
}
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ public class StartBasicClusterTask extends ClusterStateUpdateTask {
|
||||
if (acknowledgeMessages.isEmpty() == false) {
|
||||
listener.onResponse(new PostStartBasicResponse(PostStartBasicResponse.Status.NEED_ACKNOWLEDGEMENT, acknowledgeMessages,
|
||||
ACKNOWLEDGEMENT_HEADER));
|
||||
} else if (oldLicense != null && oldLicense.type().equals("basic")) {
|
||||
} else if (oldLicense != null && License.LicenseType.isBasic(oldLicense.type())) {
|
||||
listener.onResponse(new PostStartBasicResponse(PostStartBasicResponse.Status.ALREADY_USING_BASIC));
|
||||
} else {
|
||||
listener.onResponse(new PostStartBasicResponse(PostStartBasicResponse.Status.GENERATED_BASIC));
|
||||
@ -63,7 +63,7 @@ public class StartBasicClusterTask extends ClusterStateUpdateTask {
|
||||
XPackPlugin.checkReadyForXPackCustomMetadata(currentState);
|
||||
LicensesMetaData licensesMetaData = currentState.metaData().custom(LicensesMetaData.TYPE);
|
||||
License currentLicense = LicensesMetaData.extractLicense(licensesMetaData);
|
||||
if (currentLicense == null || currentLicense.type().equals("basic") == false) {
|
||||
if (currentLicense == null || License.LicenseType.isBasic(currentLicense.type()) == false) {
|
||||
long issueDate = clock.millis();
|
||||
MetaData.Builder mdBuilder = MetaData.builder(currentState.metaData());
|
||||
License.Builder specBuilder = License.builder()
|
||||
@ -71,7 +71,7 @@ public class StartBasicClusterTask extends ClusterStateUpdateTask {
|
||||
.issuedTo(clusterName)
|
||||
.maxNodes(LicenseService.SELF_GENERATED_LICENSE_MAX_NODES)
|
||||
.issueDate(issueDate)
|
||||
.type("basic")
|
||||
.type(License.LicenseType.BASIC)
|
||||
.expiryDate(LicenseService.BASIC_SELF_GENERATED_LICENSE_EXPIRATION_MILLIS);
|
||||
License selfGeneratedLicense = SelfGeneratedLicense.create(specBuilder, currentState.nodes());
|
||||
if (request.isAcknowledged() == false && currentLicense != null) {
|
||||
|
@ -5,8 +5,8 @@
|
||||
*/
|
||||
package org.elasticsearch.license;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.message.ParameterizedMessage;
|
||||
import org.apache.logging.log4j.util.Supplier;
|
||||
import org.elasticsearch.Version;
|
||||
@ -54,11 +54,8 @@ public class StartupSelfGeneratedLicenseTask extends ClusterStateUpdateTask {
|
||||
final LicensesMetaData currentLicensesMetaData = metaData.custom(LicensesMetaData.TYPE);
|
||||
// do not generate a license if any license is present
|
||||
if (currentLicensesMetaData == null) {
|
||||
String type = LicenseService.SELF_GENERATED_LICENSE_TYPE.get(settings);
|
||||
if (SelfGeneratedLicense.validSelfGeneratedType(type) == false) {
|
||||
throw new IllegalArgumentException("Illegal self generated license type [" + type +
|
||||
"]. Must be trial or basic.");
|
||||
}
|
||||
License.LicenseType type = SelfGeneratedLicense.validateSelfGeneratedType(
|
||||
LicenseService.SELF_GENERATED_LICENSE_TYPE.get(settings));
|
||||
return updateWithLicense(currentState, type);
|
||||
} else if (LicenseUtils.signatureNeedsUpdate(currentLicensesMetaData.getLicense(), currentState.nodes())) {
|
||||
return updateLicenseSignature(currentState, currentLicensesMetaData);
|
||||
@ -76,7 +73,7 @@ public class StartupSelfGeneratedLicenseTask extends ClusterStateUpdateTask {
|
||||
long issueDate = license.issueDate();
|
||||
long expiryDate = license.expiryDate();
|
||||
// extend the basic license expiration date if needed since extendBasic will not be called now
|
||||
if ("basic".equals(type) && expiryDate != LicenseService.BASIC_SELF_GENERATED_LICENSE_EXPIRATION_MILLIS) {
|
||||
if (License.LicenseType.isBasic(type) && expiryDate != LicenseService.BASIC_SELF_GENERATED_LICENSE_EXPIRATION_MILLIS) {
|
||||
expiryDate = LicenseService.BASIC_SELF_GENERATED_LICENSE_EXPIRATION_MILLIS;
|
||||
}
|
||||
License.Builder specBuilder = License.builder()
|
||||
@ -117,18 +114,18 @@ public class StartupSelfGeneratedLicenseTask extends ClusterStateUpdateTask {
|
||||
.issuedTo(currentLicense.issuedTo())
|
||||
.maxNodes(selfGeneratedLicenseMaxNodes)
|
||||
.issueDate(currentLicense.issueDate())
|
||||
.type("basic")
|
||||
.type(License.LicenseType.BASIC)
|
||||
.expiryDate(LicenseService.BASIC_SELF_GENERATED_LICENSE_EXPIRATION_MILLIS);
|
||||
License selfGeneratedLicense = SelfGeneratedLicense.create(specBuilder, currentLicense.version());
|
||||
Version trialVersion = currentLicenseMetadata.getMostRecentTrialVersion();
|
||||
return new LicensesMetaData(selfGeneratedLicense, trialVersion);
|
||||
}
|
||||
|
||||
private ClusterState updateWithLicense(ClusterState currentState, String type) {
|
||||
private ClusterState updateWithLicense(ClusterState currentState, License.LicenseType type) {
|
||||
long issueDate = clock.millis();
|
||||
MetaData.Builder mdBuilder = MetaData.builder(currentState.metaData());
|
||||
long expiryDate;
|
||||
if ("basic".equals(type)) {
|
||||
if (type == License.LicenseType.BASIC) {
|
||||
expiryDate = LicenseService.BASIC_SELF_GENERATED_LICENSE_EXPIRATION_MILLIS;
|
||||
} else {
|
||||
expiryDate = issueDate + LicenseService.NON_BASIC_SELF_GENERATED_LICENSE_DURATION.getMillis();
|
||||
@ -142,7 +139,7 @@ public class StartupSelfGeneratedLicenseTask extends ClusterStateUpdateTask {
|
||||
.expiryDate(expiryDate);
|
||||
License selfGeneratedLicense = SelfGeneratedLicense.create(specBuilder, currentState.nodes());
|
||||
LicensesMetaData licensesMetaData;
|
||||
if ("trial".equals(type)) {
|
||||
if (License.LicenseType.TRIAL.equals(type)) {
|
||||
licensesMetaData = new LicensesMetaData(selfGeneratedLicense, Version.CURRENT);
|
||||
} else {
|
||||
licensesMetaData = new LicensesMetaData(selfGeneratedLicense, null);
|
||||
|
@ -48,7 +48,7 @@ public class TransportGetBasicStatusAction extends TransportMasterNodeReadAction
|
||||
listener.onResponse(new GetBasicStatusResponse(true));
|
||||
} else {
|
||||
License license = licensesMetaData.getLicense();
|
||||
listener.onResponse(new GetBasicStatusResponse(license == null || license.type().equals("basic") == false));
|
||||
listener.onResponse(new GetBasicStatusResponse(license == null || License.LicenseType.isBasic(license.type()) == false));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -47,10 +47,13 @@ public class LicenseOperationModeTests extends ESTestCase {
|
||||
assertResolve(OperationMode.PLATINUM, "PlAtINum", "platinum");
|
||||
}
|
||||
|
||||
public void testResolveEnterpriseAsPlatinum() {
|
||||
assertResolve(OperationMode.PLATINUM, License.LicenseType.ENTERPRISE.getTypeName());
|
||||
assertResolve(OperationMode.PLATINUM, License.LicenseType.ENTERPRISE.name());
|
||||
}
|
||||
|
||||
public void testResolveUnknown() {
|
||||
// 'enterprise' is a type that exists in cloud but should be rejected under normal operation
|
||||
// See https://github.com/elastic/x-plugins/issues/3371
|
||||
String[] types = { "unknown", "fake", "enterprise" };
|
||||
String[] types = { "unknown", "fake", "commercial" };
|
||||
|
||||
for (String type : types) {
|
||||
try {
|
||||
@ -59,7 +62,7 @@ public class LicenseOperationModeTests extends ESTestCase {
|
||||
fail(String.format(Locale.ROOT, "[%s] should not be recognized as an operation mode", type));
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
assertThat(e.getMessage(), equalTo("unknown type [" + type + "]"));
|
||||
assertThat(e.getMessage(), equalTo("unknown license type [" + type + "]"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,21 +6,28 @@
|
||||
package org.elasticsearch.license;
|
||||
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.UUIDs;
|
||||
import org.elasticsearch.common.bytes.BytesArray;
|
||||
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
|
||||
import java.nio.BufferUnderflowException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.elasticsearch.common.xcontent.DeprecationHandler.THROW_UNSUPPORTED_OPERATION;
|
||||
import static org.elasticsearch.test.TestMatchers.throwableWithMessage;
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
|
||||
public class LicenseTests extends ESTestCase {
|
||||
|
||||
public void testFromXContent() throws Exception {
|
||||
|
||||
String licenseString = "{\"license\":" +
|
||||
"{\"uid\":\"4056779d-b823-4c12-a9cb-efa4a8d8c422\"," +
|
||||
"\"type\":\"gold\"," +
|
||||
@ -46,6 +53,39 @@ public class LicenseTests extends ESTestCase {
|
||||
assertThat(license.issueDate(), equalTo(1546589020459L));
|
||||
}
|
||||
|
||||
public void testLicenseToAndFromXContentForEveryLicenseType() throws Exception {
|
||||
for (License.LicenseType type : License.LicenseType.values()) {
|
||||
final License license1 = License.builder()
|
||||
.uid(UUIDs.randomBase64UUID(random()))
|
||||
.type(type)
|
||||
.issueDate(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(randomIntBetween(1, 10)))
|
||||
.expiryDate(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(randomIntBetween(1, 1000)))
|
||||
.maxNodes(randomIntBetween(1, 100))
|
||||
.issuedTo(randomAlphaOfLengthBetween(5, 50))
|
||||
.issuer(randomAlphaOfLengthBetween(5, 50))
|
||||
// We need a signature that parses correctly, but it doesn't need to verify
|
||||
.signature("AAAAAgAAAA34V2kfTJVtvdL2LttwAAABmFJ6NGRnbEM3WVQrZVQwNkdKQmR1VytlMTMyM1J0dTZ1WGwyY2ZCVFhqMGtJU2gzZ3pnNTVpOW" +
|
||||
"F5Y1NaUkwyN2VsTEtCYnlZR2c5WWtjQ0phaDlhRjlDUXViUmUwMWhjSkE2TFcwSGdneTJHbUV4N2RHUWJxV20ybjRsZHRzV2xkN0ZmdDlYblJmNVc" +
|
||||
"xMlBWeU81V1hLUm1EK0V1dmF3cFdlSGZzTU5SZE1qUmFra3JkS1hCanBWVmVTaFFwV3BVZERzeG9Sci9rYnlJK2toODZXY09tNmFHUVNUL3IyUHEx" +
|
||||
"V3VSTlBneWNJcFQ0bXl0cmhNNnRwbE1CWE4zWjJ5eGFuWFo0NGhsb3B5WFd1eTdYbFFWQkxFVFFPSlBERlB0eVVJYXVSZ0lsR2JpRS9rN1h4MSsvN" +
|
||||
"UpOcGN6cU1NOHN1cHNtSTFIUGN1bWNGNEcxekhrblhNOXZ2VEQvYmRzQUFwbytUZEpRR3l6QU5oS2ZFSFdSbGxxNDZyZ0xvUHIwRjdBL2JqcnJnNG" +
|
||||
"FlK09Cek9pYlJ5Umc9PQAAAQAth77fQLF7CCEL7wA6Z0/UuRm/weECcsjW/50kBnPLO8yEs+9/bPa5LSU0bF6byEXOVeO0ebUQfztpjulbXh8TrBD" +
|
||||
"SG+6VdxGtohPo2IYPBaXzGs3LOOor6An/lhptxBWdwYmfbcp0m8mnXZh1vN9rmbTsZXnhBIoPTaRDwUBi3vJ3Ms3iLaEm4S8Slrfmtht2jUjgGZ2v" +
|
||||
"AeZ9OHU2YsGtrSpz6f")
|
||||
.build();
|
||||
XContentParser parser = XContentType.JSON.xContent().createParser(NamedXContentRegistry.EMPTY, THROW_UNSUPPORTED_OPERATION,
|
||||
Strings.toString(license1));
|
||||
License license2 = License.fromXContent(parser);
|
||||
assertThat(license2, notNullValue());
|
||||
assertThat(license2.type(), equalTo(type.getTypeName()));
|
||||
assertThat(license2.uid(), equalTo(license1.uid()));
|
||||
assertThat(license2.issuer(), equalTo(license1.issuer()));
|
||||
assertThat(license2.issuedTo(), equalTo(license1.issuedTo()));
|
||||
assertThat(license2.expiryDate(), equalTo(license1.expiryDate()));
|
||||
assertThat(license2.issueDate(), equalTo(license1.issueDate()));
|
||||
}
|
||||
}
|
||||
|
||||
public void testNotEnoughBytesFromXContent() throws Exception {
|
||||
|
||||
String licenseString = "{\"license\": " +
|
||||
@ -86,6 +126,9 @@ public class LicenseTests extends ESTestCase {
|
||||
License.fromSource(new BytesArray(licenseString.getBytes(StandardCharsets.UTF_8)),
|
||||
XContentType.JSON);
|
||||
});
|
||||
// When parsing a license, we read the signature bytes to verify the _version_.
|
||||
// Random alphabetic sig bytes will generate a bad version
|
||||
assertThat(exception, throwableWithMessage(containsString("Unknown license version found")));
|
||||
}
|
||||
|
||||
public void testUnableToBase64DecodeFromXContent() throws Exception {
|
||||
|
@ -23,6 +23,7 @@ import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
import static org.elasticsearch.test.ESIntegTestCase.Scope.SUITE;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
|
||||
@ESIntegTestCase.ClusterScope(scope = SUITE)
|
||||
public class StartTrialLicenseTests extends AbstractLicensesIntegrationTestCase {
|
||||
@ -73,21 +74,21 @@ public class StartTrialLicenseTests extends AbstractLicensesIntegrationTestCase
|
||||
assertEquals("basic", getLicenseResponse.license().type());
|
||||
});
|
||||
|
||||
String type = randomFrom(LicenseService.VALID_TRIAL_TYPES);
|
||||
License.LicenseType type = randomFrom(LicenseService.VALID_TRIAL_TYPES);
|
||||
|
||||
Request ackRequest = new Request("POST", "/_license/start_trial");
|
||||
ackRequest.addParameter("acknowledge", "true");
|
||||
ackRequest.addParameter("type", type);
|
||||
ackRequest.addParameter("type", type.getTypeName());
|
||||
Response response3 = restClient.performRequest(ackRequest);
|
||||
String body3 = Streams.copyToString(new InputStreamReader(response3.getEntity().getContent(), StandardCharsets.UTF_8));
|
||||
assertEquals(200, response3.getStatusLine().getStatusCode());
|
||||
assertTrue(body3.contains("\"trial_was_started\":true"));
|
||||
assertTrue(body3.contains("\"type\":\"" + type + "\""));
|
||||
assertTrue(body3.contains("\"acknowledged\":true"));
|
||||
assertThat(body3, containsString("\"trial_was_started\":true"));
|
||||
assertThat(body3, containsString("\"type\":\"" + type.getTypeName() + "\""));
|
||||
assertThat(body3, containsString("\"acknowledged\":true"));
|
||||
|
||||
assertBusy(() -> {
|
||||
GetLicenseResponse postTrialLicenseResponse = licensingClient.prepareGetLicense().get();
|
||||
assertEquals(type, postTrialLicenseResponse.license().type());
|
||||
assertEquals(type.getTypeName(), postTrialLicenseResponse.license().type());
|
||||
});
|
||||
|
||||
Response response4 = restClient.performRequest(new Request("GET", "/_license/trial_status"));
|
||||
@ -95,11 +96,11 @@ public class StartTrialLicenseTests extends AbstractLicensesIntegrationTestCase
|
||||
assertEquals(200, response4.getStatusLine().getStatusCode());
|
||||
assertEquals("{\"eligible_to_start_trial\":false}", body4);
|
||||
|
||||
String secondAttemptType = randomFrom(LicenseService.VALID_TRIAL_TYPES);
|
||||
License.LicenseType secondAttemptType = randomFrom(LicenseService.VALID_TRIAL_TYPES);
|
||||
|
||||
Request startTrialWhenStartedRequest = new Request("POST", "/_license/start_trial");
|
||||
startTrialWhenStartedRequest.addParameter("acknowledge", "true");
|
||||
startTrialWhenStartedRequest.addParameter("type", secondAttemptType);
|
||||
startTrialWhenStartedRequest.addParameter("type", secondAttemptType.getTypeName());
|
||||
ResponseException ex = expectThrows(ResponseException.class, () -> restClient.performRequest(startTrialWhenStartedRequest));
|
||||
Response response5 = ex.getResponse();
|
||||
String body5 = Streams.copyToString(new InputStreamReader(response5.getEntity().getContent(), StandardCharsets.UTF_8));
|
||||
@ -117,8 +118,8 @@ public class StartTrialLicenseTests extends AbstractLicensesIntegrationTestCase
|
||||
Response response = ex.getResponse();
|
||||
String body = Streams.copyToString(new InputStreamReader(response.getEntity().getContent(), StandardCharsets.UTF_8));
|
||||
assertEquals(400, response.getStatusLine().getStatusCode());
|
||||
assertTrue(body.contains("\"type\":\"illegal_argument_exception\""));
|
||||
assertTrue(body.contains("\"reason\":\"Cannot start trial of type [basic]. Valid trial types are ["));
|
||||
assertThat(body, containsString("\"type\":\"illegal_argument_exception\""));
|
||||
assertThat(body, containsString("\"reason\":\"Cannot start trial of type [basic]. Valid trial types are ["));
|
||||
}
|
||||
|
||||
private void ensureStartingWithBasic() throws Exception {
|
||||
|
@ -18,6 +18,10 @@
|
||||
"local":{
|
||||
"type":"boolean",
|
||||
"description":"Return local information, do not retrieve the state from master node (default: false)"
|
||||
},
|
||||
"accept_enterprise":{
|
||||
"type":"boolean",
|
||||
"description":"If the active license is an enterprise license, return type as 'enterprise' (default: false)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,47 @@
|
||||
---
|
||||
teardown:
|
||||
- do:
|
||||
license.post:
|
||||
acknowledge: true
|
||||
body: |
|
||||
{"licenses":[{"uid":"3aa62ffe-36e1-4fad-bfdc-9dff8301eb22","type":"trial","issue_date_in_millis":1523456691721,"expiry_date_in_millis":1838816691721,"max_nodes":5,"issued_to":"customer","issuer":"elasticsearch","signature":"AAAABAAAAA2kWNcuc+DT0lrlmYZKAAAAIAo5/x6hrsGh1GqqrJmy4qgmEC7gK0U4zQ6q5ZEMhm4jAAABAEn6fG9y2VxKBu2T3D5hffh56kzOQODCOdhr0y2d17ZSIJMZRqO7ZywPCWNS1aR33GhfIHkTER0ysML0xMH/gXavhyRvMBndJj0UBKzuwpTawSlnxYtcqN8mSBIvJC7Ki+uJ1SpAILC2ZP9fnkRlqwXqBlTwfYn7xnZgu9DKrOWru/ipTPObo7jcePl8VTK6nWFen7/hCFDQTUFZ0jQvd+nq7A1PAcHGNxGfdbMVmAXCXgGWkRfT3clo9/vadgo+isNyh1sPq9mN7gwsvBAKtA1FrpH2EXYYbfOsSpBvUmhYMgErLg1k3/CbS0pCWLKOaX1xTMayosdZOjagU3auZXY=","start_date_in_millis":-1}]}
|
||||
---
|
||||
"Installing enterprise license":
|
||||
|
||||
## current license version
|
||||
- do:
|
||||
license.post:
|
||||
acknowledge: true
|
||||
body: |
|
||||
{"license":{"uid":"6e57906b-a8d1-4c1f-acb7-73a16edc3934","type":"enterprise","issue_date_in_millis":1523456691721,"expiry_date_in_millis":1838816691721,"max_nodes":50,"issued_to":"rest-test","issuer":"elasticsearch","signature":"AAAABAAAAA03e8BZRVXaCV4CpPGRAAAAIAo5/x6hrsGh1GqqrJmy4qgmEC7gK0U4zQ6q5ZEMhm4jAAABAAZNhjABV6PRfa7P7sJgn70XCGoKtAVT75yU13JvKBd/UjD4TPhuZcztqZ/tcLEPxm/TSvGlogWmnw/Rw8xs8jMpBpKsJ+LOXjHhDdvXb2y7JJhCH8nlSEblMDRXysNvWpKe60Z/hb7hS4JynEUt0EBb6ji7BL42O07PNll1EGmkfsHazfs46iV91BG1VxXksI78XgWSaA0F/h7tvrNW9PTgsUaLo06InlQ8jA1dal90AoXp+MVDOHWQjVFZzUnO87/7lEb+VXt0IwchaW17ahihJqkCtGvKpWFwpuhx9xiFvkySN/g5LIVjYCvgBkiWExQ9p0Zzg3VoSlMBnVy0BWo=","start_date_in_millis":-1}}
|
||||
|
||||
- match: { license_status: "valid" }
|
||||
|
||||
- do:
|
||||
license.get: {}
|
||||
|
||||
## a license object has 11 attributes
|
||||
- length: { license: 11 }
|
||||
|
||||
## by default the enterprise license is "platinum"
|
||||
- match: { license.type: "platinum" }
|
||||
|
||||
- do:
|
||||
license.get:
|
||||
accept_enterprise: "true"
|
||||
|
||||
## a license object has 11 attributes
|
||||
- length: { license: 11 }
|
||||
|
||||
## opt-in to return real enterprise type
|
||||
- match: { license.type: "enterprise" }
|
||||
|
||||
- do:
|
||||
license.get:
|
||||
accept_enterprise: "false"
|
||||
|
||||
## a license object has 11 attributes
|
||||
- length: { license: 11 }
|
||||
|
||||
## opt-out of real enterprise type
|
||||
- match: { license.type: "platinum" }
|
Loading…
x
Reference in New Issue
Block a user