Add max_resource_units to enterprise license (#50910)

The enterprise license type must have "max_resource_units" and may not
have "max_nodes".

This change adds support for this new field, validation that the field
is present if-and-only-if the license is enterprise and bumps the
license version number to reflect the new field.

Includes a BWC layer to return "max_nodes: ${max_resource_units}" in
the GET license API.

Backport of: #50735
This commit is contained in:
Tim Vernum 2020-01-14 12:37:05 +11:00 committed by GitHub
parent f0924e6d5b
commit c2acb8830a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 314 additions and 91 deletions

View File

@ -113,13 +113,19 @@ public class License implements ToXContentObject {
static boolean isTrial(String typeName) { static boolean isTrial(String typeName) {
return TRIAL.getTypeName().equals(typeName); return TRIAL.getTypeName().equals(typeName);
} }
static boolean isEnterprise(String typeName) {
return ENTERPRISE.getTypeName().equals(typeName);
}
} }
public static final int VERSION_START = 1; public static final int VERSION_START = 1;
public static final int VERSION_NO_FEATURE_TYPE = 2; public static final int VERSION_NO_FEATURE_TYPE = 2;
public static final int VERSION_START_DATE = 3; public static final int VERSION_START_DATE = 3;
public static final int VERSION_CRYPTO_ALGORITHMS = 4; public static final int VERSION_CRYPTO_ALGORITHMS = 4;
public static final int VERSION_CURRENT = VERSION_CRYPTO_ALGORITHMS; public static final int VERSION_ENTERPRISE = 5;
public static final int VERSION_CURRENT = VERSION_ENTERPRISE;
/** /**
* XContent param name to deserialize license(s) with * XContent param name to deserialize license(s) with
@ -159,13 +165,14 @@ public class License implements ToXContentObject {
private final long expiryDate; private final long expiryDate;
private final long startDate; private final long startDate;
private final int maxNodes; private final int maxNodes;
private final int maxResourceUnits;
private final OperationMode operationMode; private final OperationMode operationMode;
/** /**
* Decouples operation mode of a license from the license type value. * Decouples operation mode of a license from the license type value.
* <p> * <p>
* Note: The mode indicates features that should be made available, but it does not indicate whether the license is active! * Note: The mode indicates features that should be made available, but it does not indicate whether the license is active!
* * <p>
* The id byte is used for ordering operation modes * The id byte is used for ordering operation modes
*/ */
public enum OperationMode { public enum OperationMode {
@ -182,13 +189,16 @@ public class License implements ToXContentObject {
this.id = id; this.id = id;
} }
/** Returns non-zero positive number when <code>opMode1</code> is greater than <code>opMode2</code> */ /**
* Returns non-zero positive number when <code>opMode1</code> is greater than <code>opMode2</code>
*/
public static int compare(OperationMode opMode1, OperationMode opMode2) { public static int compare(OperationMode opMode1, OperationMode opMode2) {
return Integer.compare(opMode1.id, opMode2.id); return Integer.compare(opMode1.id, opMode2.id);
} }
/** /**
* Determine the operating mode for a license type * Determine the operating mode for a license type
*
* @see LicenseType#resolve(License) * @see LicenseType#resolve(License)
* @see #parse(String) * @see #parse(String)
*/ */
@ -217,6 +227,7 @@ public class License implements ToXContentObject {
* Parses an {@code OperatingMode} from a String. * Parses an {@code OperatingMode} from a String.
* The string must name an operating mode, and not a licensing level (that is, it cannot parse old style license levels * The string must name an operating mode, and not a licensing level (that is, it cannot parse old style license levels
* such as "dev" or "silver"). * such as "dev" or "silver").
*
* @see #description() * @see #description()
*/ */
public static OperationMode parse(String mode) { public static OperationMode parse(String mode) {
@ -233,8 +244,8 @@ public class License implements ToXContentObject {
} }
} }
private License(int version, String uid, String issuer, String issuedTo, long issueDate, String type, private License(int version, String uid, String issuer, String issuedTo, long issueDate, String type, String subscriptionType,
String subscriptionType, String feature, String signature, long expiryDate, int maxNodes, long startDate) { String feature, String signature, long expiryDate, int maxNodes, int maxResourceUnits, long startDate) {
this.version = version; this.version = version;
this.uid = uid; this.uid = uid;
this.issuer = issuer; this.issuer = issuer;
@ -252,6 +263,7 @@ public class License implements ToXContentObject {
this.expiryDate = expiryDate; this.expiryDate = expiryDate;
} }
this.maxNodes = maxNodes; this.maxNodes = maxNodes;
this.maxResourceUnits = maxResourceUnits;
this.startDate = startDate; this.startDate = startDate;
this.operationMode = OperationMode.resolve(LicenseType.resolve(this)); this.operationMode = OperationMode.resolve(LicenseType.resolve(this));
validate(); validate();
@ -300,12 +312,21 @@ public class License implements ToXContentObject {
} }
/** /**
* @return the maximum number of nodes this license has been issued for * @return the maximum number of nodes this license has been issued for, or {@code -1} if this license is not node based.
*/ */
public int maxNodes() { public int maxNodes() {
return maxNodes; return maxNodes;
} }
/**
* @return the maximum number of "resource units" this license has been issued for, or {@code -1} if this license is not resource based.
* A "resource unit" is a measure of computing power (RAM/CPU), the definition of which is maintained outside of the license format,
* or this class.
*/
public int maxResourceUnits() {
return maxResourceUnits;
}
/** /**
* @return a string representing the entity this licenses has been issued to * @return a string representing the entity this licenses has been issued to
*/ */
@ -392,20 +413,39 @@ public class License implements ToXContentObject {
throw new IllegalStateException("uid can not be null"); throw new IllegalStateException("uid can not be null");
} else if (feature == null && version == VERSION_START) { } else if (feature == null && version == VERSION_START) {
throw new IllegalStateException("feature can not be null"); throw new IllegalStateException("feature can not be null");
} else if (maxNodes == -1) {
throw new IllegalStateException("maxNodes has to be set");
} else if (expiryDate == -1) { } else if (expiryDate == -1) {
throw new IllegalStateException("expiryDate has to be set"); throw new IllegalStateException("expiryDate has to be set");
} else if (expiryDate == LicenseService.BASIC_SELF_GENERATED_LICENSE_EXPIRATION_MILLIS && LicenseType.isBasic(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"); throw new IllegalStateException("only basic licenses are allowed to have no expiration");
} }
if (LicenseType.isEnterprise(type) && version < VERSION_ENTERPRISE) {
throw new IllegalStateException("license type [" + type + "] is not a valid for version [" + version + "] licenses");
}
validateLimits(type, maxNodes, maxResourceUnits);
}
private static void validateLimits(String type, int maxNodes, int maxResourceUnits) {
if (LicenseType.isEnterprise(type)) {
if (maxResourceUnits == -1) {
throw new IllegalStateException("maxResourceUnits must be set for enterprise licenses (type=[" + type + "])");
} else if (maxNodes != -1) {
throw new IllegalStateException("maxNodes may not be set for enterprise licenses (type=[" + type + "])");
}
} else {
if (maxNodes == -1) {
throw new IllegalStateException("maxNodes has to be set");
} else if (maxResourceUnits != -1) {
throw new IllegalStateException("maxResourceUnits may only be set for enterprise licenses (not permitted for type=[" +
type + "])");
}
}
} }
public static License readLicense(StreamInput in) throws IOException { public static License readLicense(StreamInput in) throws IOException {
int version = in.readVInt(); // Version for future extensibility int version = in.readVInt(); // Version for future extensibility
if (version > VERSION_CURRENT) { if (version > VERSION_CURRENT) {
throw new ElasticsearchException("Unknown license version found, please upgrade all nodes to the latest elasticsearch-license" + throw new ElasticsearchException("Unknown license version found, please upgrade all nodes to the latest elasticsearch release");
" plugin");
} }
Builder builder = builder(); Builder builder = builder();
builder.version(version); builder.version(version);
@ -420,6 +460,9 @@ public class License implements ToXContentObject {
} }
builder.expiryDate(in.readLong()); builder.expiryDate(in.readLong());
builder.maxNodes(in.readInt()); builder.maxNodes(in.readInt());
if (version >= VERSION_ENTERPRISE) {
builder.maxResourceUnits(in.readInt());
}
builder.issuedTo(in.readString()); builder.issuedTo(in.readString());
builder.issuer(in.readString()); builder.issuer(in.readString());
builder.signature(in.readOptionalString()); builder.signature(in.readOptionalString());
@ -442,6 +485,9 @@ public class License implements ToXContentObject {
} }
out.writeLong(expiryDate); out.writeLong(expiryDate);
out.writeInt(maxNodes); out.writeInt(maxNodes);
if (version >= VERSION_ENTERPRISE) {
out.writeInt(maxResourceUnits);
}
out.writeString(issuedTo); out.writeString(issuedTo);
out.writeString(issuer); out.writeString(issuer);
out.writeOptionalString(signature); out.writeOptionalString(signature);
@ -506,7 +552,16 @@ public class License implements ToXContentObject {
if (expiryDate != LicenseService.BASIC_SELF_GENERATED_LICENSE_EXPIRATION_MILLIS) { if (expiryDate != LicenseService.BASIC_SELF_GENERATED_LICENSE_EXPIRATION_MILLIS) {
builder.timeField(Fields.EXPIRY_DATE_IN_MILLIS, Fields.EXPIRY_DATE, expiryDate); builder.timeField(Fields.EXPIRY_DATE_IN_MILLIS, Fields.EXPIRY_DATE, expiryDate);
} }
builder.field(Fields.MAX_NODES, maxNodes);
if (version >= VERSION_ENTERPRISE) {
builder.field(Fields.MAX_NODES, maxNodes == -1 ? null : maxNodes);
builder.field(Fields.MAX_RESOURCE_UNITS, maxResourceUnits == -1 ? null : maxResourceUnits);
} else if (hideEnterprise && maxNodes == -1) {
builder.field(Fields.MAX_NODES, maxResourceUnits);
} else {
builder.field(Fields.MAX_NODES, maxNodes);
}
builder.field(Fields.ISSUED_TO, issuedTo); builder.field(Fields.ISSUED_TO, issuedTo);
builder.field(Fields.ISSUER, issuer); builder.field(Fields.ISSUER, issuer);
if (!licenseSpecMode && !restViewMode && signature != null) { if (!licenseSpecMode && !restViewMode && signature != null) {
@ -551,6 +606,8 @@ public class License implements ToXContentObject {
builder.startDate(parser.longValue()); builder.startDate(parser.longValue());
} else if (Fields.MAX_NODES.equals(currentFieldName)) { } else if (Fields.MAX_NODES.equals(currentFieldName)) {
builder.maxNodes(parser.intValue()); builder.maxNodes(parser.intValue());
} else if (Fields.MAX_RESOURCE_UNITS.equals(currentFieldName)) {
builder.maxResourceUnits(parser.intValue());
} else if (Fields.ISSUED_TO.equals(currentFieldName)) { } else if (Fields.ISSUED_TO.equals(currentFieldName)) {
builder.issuedTo(parser.text()); builder.issuedTo(parser.text());
} else if (Fields.ISSUER.equals(currentFieldName)) { } else if (Fields.ISSUER.equals(currentFieldName)) {
@ -593,7 +650,7 @@ public class License implements ToXContentObject {
throw new ElasticsearchException("malformed signature for license [" + builder.uid + "]"); throw new ElasticsearchException("malformed signature for license [" + builder.uid + "]");
} else if (version > VERSION_CURRENT) { } else if (version > VERSION_CURRENT) {
throw new ElasticsearchException("Unknown license version found, please upgrade all nodes to the latest " + throw new ElasticsearchException("Unknown license version found, please upgrade all nodes to the latest " +
"elasticsearch-license plugin"); "elasticsearch-license plugin");
} }
// signature version is the source of truth // signature version is the source of truth
builder.version(version); builder.version(version);
@ -625,8 +682,7 @@ public class License implements ToXContentObject {
// EMPTY is safe here because we don't call namedObject // EMPTY is safe here because we don't call namedObject
try (InputStream byteStream = bytes.streamInput(); try (InputStream byteStream = bytes.streamInput();
XContentParser parser = xContentType.xContent() XContentParser parser = xContentType.xContent()
.createParser(NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE, byteStream)) .createParser(NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE, byteStream)) {
{
License license = null; License license = null;
if (parser.nextToken() == XContentParser.Token.START_OBJECT) { if (parser.nextToken() == XContentParser.Token.START_OBJECT) {
if (parser.nextToken() == XContentParser.Token.FIELD_NAME) { if (parser.nextToken() == XContentParser.Token.FIELD_NAME) {
@ -675,7 +731,7 @@ public class License implements ToXContentObject {
if (issueDate != license.issueDate) return false; if (issueDate != license.issueDate) return false;
if (expiryDate != license.expiryDate) return false; if (expiryDate != license.expiryDate) return false;
if (startDate!= license.startDate) return false; if (startDate != license.startDate) return false;
if (maxNodes != license.maxNodes) return false; if (maxNodes != license.maxNodes) return false;
if (version != license.version) return false; if (version != license.version) return false;
if (uid != null ? !uid.equals(license.uid) : license.uid != null) return false; if (uid != null ? !uid.equals(license.uid) : license.uid != null) return false;
@ -700,7 +756,7 @@ public class License implements ToXContentObject {
result = 31 * result + (feature != null ? feature.hashCode() : 0); result = 31 * result + (feature != null ? feature.hashCode() : 0);
result = 31 * result + (signature != null ? signature.hashCode() : 0); result = 31 * result + (signature != null ? signature.hashCode() : 0);
result = 31 * result + (int) (expiryDate ^ (expiryDate >>> 32)); result = 31 * result + (int) (expiryDate ^ (expiryDate >>> 32));
result = 31 * result + (int) (startDate ^ (startDate>>> 32)); result = 31 * result + (int) (startDate ^ (startDate >>> 32));
result = 31 * result + maxNodes; result = 31 * result + maxNodes;
result = 31 * result + version; result = 31 * result + version;
return result; return result;
@ -719,6 +775,7 @@ public class License implements ToXContentObject {
public static final String START_DATE_IN_MILLIS = "start_date_in_millis"; public static final String START_DATE_IN_MILLIS = "start_date_in_millis";
public static final String START_DATE = "start_date"; public static final String START_DATE = "start_date";
public static final String MAX_NODES = "max_nodes"; public static final String MAX_NODES = "max_nodes";
public static final String MAX_RESOURCE_UNITS = "max_resource_units";
public static final String ISSUED_TO = "issued_to"; public static final String ISSUED_TO = "issued_to";
public static final String ISSUER = "issuer"; public static final String ISSUER = "issuer";
public static final String VERSION = "version"; public static final String VERSION = "version";
@ -762,6 +819,7 @@ public class License implements ToXContentObject {
private long expiryDate = -1; private long expiryDate = -1;
private long startDate = -1; private long startDate = -1;
private int maxNodes = -1; private int maxNodes = -1;
private int maxResourceUnits = -1;
public Builder uid(String uid) { public Builder uid(String uid) {
this.uid = uid; this.uid = uid;
@ -817,6 +875,11 @@ public class License implements ToXContentObject {
return this; return this;
} }
public Builder maxResourceUnits(int maxUnits) {
this.maxResourceUnits = maxUnits;
return this;
}
public Builder signature(String signature) { public Builder signature(String signature) {
if (signature != null) { if (signature != null) {
this.signature = signature; this.signature = signature;
@ -831,17 +894,18 @@ public class License implements ToXContentObject {
public Builder fromLicenseSpec(License license, String signature) { public Builder fromLicenseSpec(License license, String signature) {
return uid(license.uid()) return uid(license.uid())
.version(license.version()) .version(license.version())
.issuedTo(license.issuedTo()) .issuedTo(license.issuedTo())
.issueDate(license.issueDate()) .issueDate(license.issueDate())
.startDate(license.startDate()) .startDate(license.startDate())
.type(license.type()) .type(license.type())
.subscriptionType(license.subscriptionType) .subscriptionType(license.subscriptionType)
.feature(license.feature) .feature(license.feature)
.maxNodes(license.maxNodes()) .maxNodes(license.maxNodes())
.expiryDate(license.expiryDate()) .maxResourceUnits(license.maxResourceUnits())
.issuer(license.issuer()) .expiryDate(license.expiryDate())
.signature(signature); .issuer(license.issuer())
.signature(signature);
} }
/** /**
@ -850,15 +914,15 @@ public class License implements ToXContentObject {
*/ */
public Builder fromPre20LicenseSpec(License pre20License) { public Builder fromPre20LicenseSpec(License pre20License) {
return uid(pre20License.uid()) return uid(pre20License.uid())
.issuedTo(pre20License.issuedTo()) .issuedTo(pre20License.issuedTo())
.issueDate(pre20License.issueDate()) .issueDate(pre20License.issueDate())
.maxNodes(pre20License.maxNodes()) .maxNodes(pre20License.maxNodes())
.expiryDate(pre20License.expiryDate()); .expiryDate(pre20License.expiryDate());
} }
public License build() { public License build() {
return new License(version, uid, issuer, issuedTo, issueDate, type, return new License(version, uid, issuer, issuedTo, issueDate, type,
subscriptionType, feature, signature, expiryDate, maxNodes, startDate); subscriptionType, feature, signature, expiryDate, maxNodes, maxResourceUnits, startDate);
} }
public Builder validate() { public Builder validate() {
@ -874,11 +938,10 @@ public class License implements ToXContentObject {
throw new IllegalStateException("uid can not be null"); throw new IllegalStateException("uid can not be null");
} else if (signature == null) { } else if (signature == null) {
throw new IllegalStateException("signature can not be null"); throw new IllegalStateException("signature can not be null");
} else if (maxNodes == -1) {
throw new IllegalStateException("maxNodes has to be set");
} else if (expiryDate == -1) { } else if (expiryDate == -1) {
throw new IllegalStateException("expiryDate has to be set"); throw new IllegalStateException("expiryDate has to be set");
} }
validateLimits(type, maxNodes, maxResourceUnits);
return this; return this;
} }

View File

@ -122,6 +122,7 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste
* Max number of nodes licensed by generated trial license * Max number of nodes licensed by generated trial license
*/ */
static final int SELF_GENERATED_LICENSE_MAX_NODES = 1000; static final int SELF_GENERATED_LICENSE_MAX_NODES = 1000;
static final int SELF_GENERATED_LICENSE_MAX_RESOURCE_UNITS = SELF_GENERATED_LICENSE_MAX_NODES;
public static final String LICENSE_JOB = "licenseJob"; public static final String LICENSE_JOB = "licenseJob";
@ -292,11 +293,8 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste
} }
private static boolean licenseIsCompatible(License license, Version version) { private static boolean licenseIsCompatible(License license, Version version) {
if (License.LicenseType.ENTERPRISE.getTypeName().equalsIgnoreCase(license.type())) { final int maxVersion = LicenseUtils.getMaxLicenseVersion(version);
return version.onOrAfter(Version.V_7_6_0); return license.version() <= maxVersion;
} else {
return true;
}
} }
private boolean isAllowedLicenseType(License.LicenseType type) { private boolean isAllowedLicenseType(License.LicenseType type) {

View File

@ -11,8 +11,6 @@ import org.elasticsearch.cluster.node.DiscoveryNodes;
import org.elasticsearch.license.License.LicenseType; import org.elasticsearch.license.License.LicenseType;
import org.elasticsearch.rest.RestStatus; import org.elasticsearch.rest.RestStatus;
import java.util.stream.StreamSupport;
public class LicenseUtils { public class LicenseUtils {
public static final String EXPIRED_FEATURE_METADATA = "es.license.expired.feature"; public static final String EXPIRED_FEATURE_METADATA = "es.license.expired.feature";
@ -49,25 +47,30 @@ public class LicenseUtils {
* recreated with the new key * recreated with the new key
*/ */
public static boolean signatureNeedsUpdate(License license, DiscoveryNodes currentNodes) { public static boolean signatureNeedsUpdate(License license, DiscoveryNodes currentNodes) {
assert License.VERSION_CRYPTO_ALGORITHMS == License.VERSION_CURRENT : "update this method when adding a new version"; assert License.VERSION_ENTERPRISE == License.VERSION_CURRENT : "update this method when adding a new version";
String typeName = license.type(); String typeName = license.type();
return (LicenseType.isBasic(typeName) || LicenseType.isTrial(typeName)) && return (LicenseType.isBasic(typeName) || LicenseType.isTrial(typeName)) &&
// only upgrade signature when all nodes are ready to deserialize the new signature // only upgrade signature when all nodes are ready to deserialize the new signature
(license.version() < License.VERSION_CRYPTO_ALGORITHMS && (license.version() < License.VERSION_CRYPTO_ALGORITHMS &&
compatibleLicenseVersion(currentNodes) == License.VERSION_CRYPTO_ALGORITHMS compatibleLicenseVersion(currentNodes) >= License.VERSION_CRYPTO_ALGORITHMS
); );
} }
public static int compatibleLicenseVersion(DiscoveryNodes currentNodes) { public static int compatibleLicenseVersion(DiscoveryNodes currentNodes) {
assert License.VERSION_CRYPTO_ALGORITHMS == License.VERSION_CURRENT : "update this method when adding a new version"; return getMaxLicenseVersion(currentNodes.getMinNodeVersion());
if (StreamSupport.stream(currentNodes.spliterator(), false)
.allMatch(node -> node.getVersion().onOrAfter(Version.V_6_4_0))) {
// License.VERSION_CRYPTO_ALGORITHMS was introduced in 6.4.0
return License.VERSION_CRYPTO_ALGORITHMS;
} else {
return License.VERSION_START_DATE;
}
} }
public static int getMaxLicenseVersion(Version version){
if (version != null) {
if (version.before(Version.V_6_4_0)) {
return License.VERSION_START_DATE;
}
if (version.before(Version.V_7_6_0)) {
return License.VERSION_CRYPTO_ALGORITHMS;
}
}
assert License.VERSION_ENTERPRISE == License.VERSION_CURRENT : "update this method when adding a new version";
return License.VERSION_ENTERPRISE;
}
} }

View File

@ -51,12 +51,13 @@ public class RestGetLicenseAction extends XPackRestHandler {
*/ */
@Override @Override
public RestChannelConsumer doPrepareRequest(final RestRequest request, final XPackClient client) throws IOException { public RestChannelConsumer doPrepareRequest(final RestRequest request, final XPackClient client) throws IOException {
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 // Hide enterprise licenses by default, there is an opt-in flag to show them
final boolean hideEnterprise = request.paramAsBoolean("accept_enterprise", false) == false; final boolean hideEnterprise = request.paramAsBoolean("accept_enterprise", false) == false;
final int licenseVersion = hideEnterprise ? License.VERSION_CRYPTO_ALGORITHMS : License.VERSION_CURRENT;
final Map<String, String> overrideParams = new HashMap<>(3);
overrideParams.put(License.REST_VIEW_MODE, "true");
overrideParams.put(License.LICENSE_VERSION_MODE, String.valueOf(licenseVersion));
overrideParams.put(License.XCONTENT_HIDE_ENTERPRISE, String.valueOf(hideEnterprise)); overrideParams.put(License.XCONTENT_HIDE_ENTERPRISE, String.valueOf(hideEnterprise));
final ToXContent.Params params = new ToXContent.DelegatingMapParams(overrideParams, request); final ToXContent.Params params = new ToXContent.DelegatingMapParams(overrideParams, request);

View File

@ -75,10 +75,14 @@ public class StartTrialClusterTask extends ClusterStateUpdateTask {
License.Builder specBuilder = License.builder() License.Builder specBuilder = License.builder()
.uid(UUID.randomUUID().toString()) .uid(UUID.randomUUID().toString())
.issuedTo(clusterName) .issuedTo(clusterName)
.maxNodes(LicenseService.SELF_GENERATED_LICENSE_MAX_NODES)
.issueDate(issueDate) .issueDate(issueDate)
.type(request.getType()) .type(request.getType())
.expiryDate(expiryDate); .expiryDate(expiryDate);
if (License.LicenseType.isEnterprise(request.getType())) {
specBuilder.maxResourceUnits(LicenseService.SELF_GENERATED_LICENSE_MAX_RESOURCE_UNITS);
} else {
specBuilder.maxNodes(LicenseService.SELF_GENERATED_LICENSE_MAX_NODES);
}
License selfGeneratedLicense = SelfGeneratedLicense.create(specBuilder, currentState.nodes()); License selfGeneratedLicense = SelfGeneratedLicense.create(specBuilder, currentState.nodes());
LicensesMetaData newLicensesMetaData = new LicensesMetaData(selfGeneratedLicense, Version.CURRENT); LicensesMetaData newLicensesMetaData = new LicensesMetaData(selfGeneratedLicense, Version.CURRENT);
mdBuilder.putCustom(LicensesMetaData.TYPE, newLicensesMetaData); mdBuilder.putCustom(LicensesMetaData.TYPE, newLicensesMetaData);

View File

@ -70,6 +70,7 @@ public abstract class AbstractLicenseServiceTestCase extends ESTestCase {
when(discoveryNodes.getMasterNode()).thenReturn(mockNode); when(discoveryNodes.getMasterNode()).thenReturn(mockNode);
when(discoveryNodes.spliterator()).thenReturn(Arrays.asList(mockNode).spliterator()); when(discoveryNodes.spliterator()).thenReturn(Arrays.asList(mockNode).spliterator());
when(discoveryNodes.isLocalNodeElectedMaster()).thenReturn(false); when(discoveryNodes.isLocalNodeElectedMaster()).thenReturn(false);
when(discoveryNodes.getMinNodeVersion()).thenReturn(mockNode.getVersion());
when(state.nodes()).thenReturn(discoveryNodes); when(state.nodes()).thenReturn(discoveryNodes);
when(state.getNodes()).thenReturn(discoveryNodes); // it is really ridiculous we have nodes() and getNodes()... when(state.getNodes()).thenReturn(discoveryNodes); // it is really ridiculous we have nodes() and getNodes()...
when(clusterService.state()).thenReturn(state); when(clusterService.state()).thenReturn(state);

View File

@ -35,15 +35,19 @@ public class LicenseOperationModeUpdateTests extends ESTestCase {
public void testLicenseOperationModeUpdate() throws Exception { public void testLicenseOperationModeUpdate() throws Exception {
License.LicenseType type = randomFrom(License.LicenseType.values()); License.LicenseType type = randomFrom(License.LicenseType.values());
License license = License.builder() final License.Builder licenseBuilder = License.builder()
.uid("id") .uid("id")
.expiryDate(0) .expiryDate(0)
.issueDate(0) .issueDate(0)
.issuedTo("elasticsearch") .issuedTo("elasticsearch")
.issuer("issuer") .issuer("issuer")
.type(type) .type(type);
.maxNodes(1) if (type == License.LicenseType.ENTERPRISE) {
.build(); licenseBuilder.maxResourceUnits(1);
} else {
licenseBuilder.maxNodes(1);
}
License license = licenseBuilder.build();
assertThat(license.operationMode(), equalTo(License.OperationMode.resolve(type))); assertThat(license.operationMode(), equalTo(License.OperationMode.resolve(type)));
OperationModeFileWatcherTests.writeMode("gold", licenseModeFile); OperationModeFileWatcherTests.writeMode("gold", licenseModeFile);

View File

@ -190,7 +190,8 @@ public class LicenseServiceTests extends ESTestCase {
.issuer(randomAlphaOfLengthBetween(5, 60)) .issuer(randomAlphaOfLengthBetween(5, 60))
.issuedTo(randomAlphaOfLengthBetween(5, 60)) .issuedTo(randomAlphaOfLengthBetween(5, 60))
.issueDate(System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(randomLongBetween(1, 5000))) .issueDate(System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(randomLongBetween(1, 5000)))
.maxNodes(randomIntBetween(1, 500)) .maxNodes(type == License.LicenseType.ENTERPRISE ? -1 : randomIntBetween(1, 500))
.maxResourceUnits(type == License.LicenseType.ENTERPRISE ? randomIntBetween(10, 500) : -1)
.signature(null) .signature(null)
.build(); .build();
} }

View File

@ -6,13 +6,18 @@
package org.elasticsearch.license; package org.elasticsearch.license;
import org.elasticsearch.ElasticsearchException; import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.Version;
import org.elasticsearch.common.Strings; import org.elasticsearch.common.Strings;
import org.elasticsearch.common.UUIDs; import org.elasticsearch.common.UUIDs;
import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.io.stream.BytesStreamOutput;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.xcontent.NamedXContentRegistry; import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.TestMatchers;
import org.hamcrest.Matchers;
import java.nio.BufferUnderflowException; import java.nio.BufferUnderflowException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
@ -27,7 +32,7 @@ import static org.hamcrest.Matchers.notNullValue;
public class LicenseTests extends ESTestCase { public class LicenseTests extends ESTestCase {
public void testFromXContent() throws Exception { public void testFromXContentForGoldLicenseWithVersion2Signature() throws Exception {
String licenseString = "{\"license\":" + String licenseString = "{\"license\":" +
"{\"uid\":\"4056779d-b823-4c12-a9cb-efa4a8d8c422\"," + "{\"uid\":\"4056779d-b823-4c12-a9cb-efa4a8d8c422\"," +
"\"type\":\"gold\"," + "\"type\":\"gold\"," +
@ -51,27 +56,107 @@ public class LicenseTests extends ESTestCase {
assertThat(license.issuedTo(), equalTo("customer")); assertThat(license.issuedTo(), equalTo("customer"));
assertThat(license.expiryDate(), equalTo(1546596340459L)); assertThat(license.expiryDate(), equalTo(1546596340459L));
assertThat(license.issueDate(), equalTo(1546589020459L)); assertThat(license.issueDate(), equalTo(1546589020459L));
assertThat(license.maxNodes(), equalTo(5));
assertThat(license.maxResourceUnits(), equalTo(-1));
assertThat(license.version(), equalTo(2));
}
public void testFromXContentForGoldLicenseWithVersion4Signature() throws Exception {
String licenseString = "{\"license\":{" +
"\"uid\":\"4056779d-b823-4c12-a9cb-efa4a8d8c422\"," +
"\"type\":\"gold\"," +
"\"issue_date_in_millis\":1546589020459," +
"\"expiry_date_in_millis\":1546596340459," +
"\"max_nodes\":5," +
"\"issued_to\":\"customer\"," +
"\"issuer\":\"elasticsearch\"," +
"\"signature\":\"AAAABAAAAA22vXffI41oM4jLCwZ6AAAAIAo5/x6hrsGh1GqqrJmy4qgmEC7gK0U4zQ6q5ZEMhm4jAAABAH3oL4weubwYGjLGNZsz90" +
"EerX6yOX3Dh6wswG9EfqCiyv6lcjuC7aeKKuOkqhMRTHZ9vHnfMuakHWVlpuGC14WyGqaMwSmgTZ9jVAzt/W3sIotRxM/3rtlCXUc1rOUXNFcii1i3Kkrc" +
"kTzhENTKjdkOmUN3qZlTEmHkp93eYpx8++iIukHYU9K9Vm2VKgydFfxvYaN/Qr+iPfJSbHJB8+DmS2ywdrmdqW+ScE+1ZNouPNhnP3RKTleNvixXPG9l5B" +
"qZ2So1IlCrxVDByA1E6JH5AvjbOucpcGiWCm7IzvfpkzphKHMyxhUaIByoHl9UAf4AdPLhowWAQk0eHMRDDlo=\"," +
"\"start_date_in_millis\":-1}}\n";
License license = License.fromSource(new BytesArray(licenseString.getBytes(StandardCharsets.UTF_8)),
XContentType.JSON);
assertThat(license.type(), equalTo("gold"));
assertThat(license.uid(), equalTo("4056779d-b823-4c12-a9cb-efa4a8d8c422"));
assertThat(license.issuer(), equalTo("elasticsearch"));
assertThat(license.issuedTo(), equalTo("customer"));
assertThat(license.expiryDate(), equalTo(1546596340459L));
assertThat(license.issueDate(), equalTo(1546589020459L));
assertThat(license.maxNodes(), equalTo(5));
assertThat(license.maxResourceUnits(), equalTo(-1));
assertThat(license.version(), equalTo(4));
}
public void testFromXContentForEnterpriseLicenseWithV5Signature() throws Exception {
String licenseString = "{\"license\":{" +
"\"uid\":\"4056779d-b823-4c12-a9cb-efa4a8d8c422\"," +
"\"type\":\"enterprise\"," +
"\"issue_date_in_millis\":1546589020459," +
"\"expiry_date_in_millis\":1546596340459," +
"\"max_nodes\":null," +
"\"max_resource_units\":15," +
"\"issued_to\":\"customer\"," +
"\"issuer\":\"elasticsearch\"," +
"\"signature\":\"AAAABQAAAA2MUoEqXb9K9Ie5d6JJAAAAIAo5/x6hrsGh1GqqrJmy4qgmEC7gK0U4zQ6q5ZEMhm4jAAABAAAwVZKGAmDELUlS5PScBkhQsZa" +
"DaQTtJ4ZP5EnZ/nLpmCt9Dj7d/FRsgMtHmSJLrr2CdrIo4Vx5VuhmbwzZvXMttLz2lrJzG7770PX3TnC9e7F9GdnE9ec0FP2U0ZlLOBOtPuirX0q+j6GfB+DLyE" +
"5D+Lo1NQ3eLJGvbd3DBYPWJxkb+EBVHczCH2OrIEVWnN/TafmkdZCPX5PcultkNOs3j7d3s7b51EXHKoye8UTcB/RGmzZwMah+E6I/VJkqu7UHL8bB01wJeqo6W" +
"xI4LC/9+f5kpmHrUu3CHe5pHbmMGDk7O6/cwt1pw/hnJXKIFCi36IGaKcHLgORxQdN0uzE=\"," +
"\"start_date_in_millis\":-1}}";
License license = License.fromSource(new BytesArray(licenseString.getBytes(StandardCharsets.UTF_8)),
XContentType.JSON);
assertThat(license.type(), equalTo("enterprise"));
assertThat(license.uid(), equalTo("4056779d-b823-4c12-a9cb-efa4a8d8c422"));
assertThat(license.issuer(), equalTo("elasticsearch"));
assertThat(license.issuedTo(), equalTo("customer"));
assertThat(license.expiryDate(), equalTo(1546596340459L));
assertThat(license.issueDate(), equalTo(1546589020459L));
assertThat(license.maxNodes(), equalTo(-1));
assertThat(license.maxResourceUnits(), equalTo(15));
assertThat(license.version(), equalTo(5));
}
public void testThatEnterpriseLicenseMayNotHaveMaxNodes() throws Exception {
License.Builder builder = randomLicense(License.LicenseType.ENTERPRISE)
.maxNodes(randomIntBetween(1, 50))
.maxResourceUnits(randomIntBetween(10, 500));
final IllegalStateException ex = expectThrows(IllegalStateException.class, builder::build);
assertThat(ex, TestMatchers.throwableWithMessage("maxNodes may not be set for enterprise licenses (type=[enterprise])"));
}
public void testThatEnterpriseLicenseMustHaveMaxResourceUnits() throws Exception {
License.Builder builder = randomLicense(License.LicenseType.ENTERPRISE)
.maxResourceUnits(-1);
final IllegalStateException ex = expectThrows(IllegalStateException.class, builder::build);
assertThat(ex, TestMatchers.throwableWithMessage("maxResourceUnits must be set for enterprise licenses (type=[enterprise])"));
}
public void testThatRegularLicensesMustHaveMaxNodes() throws Exception {
License.LicenseType type = randomValueOtherThan(License.LicenseType.ENTERPRISE, () -> randomFrom(License.LicenseType.values()));
License.Builder builder = randomLicense(type)
.maxNodes(-1);
final IllegalStateException ex = expectThrows(IllegalStateException.class, builder::build);
assertThat(ex, TestMatchers.throwableWithMessage("maxNodes has to be set"));
}
public void testThatRegularLicensesMayNotHaveMaxResourceUnits() throws Exception {
License.LicenseType type = randomValueOtherThan(License.LicenseType.ENTERPRISE, () -> randomFrom(License.LicenseType.values()));
License.Builder builder = randomLicense(type)
.maxResourceUnits(randomIntBetween(10, 500))
.maxNodes(randomIntBetween(1, 50));
final IllegalStateException ex = expectThrows(IllegalStateException.class, builder::build);
assertThat(ex, TestMatchers.throwableWithMessage("maxResourceUnits may only be set for enterprise licenses (not permitted " +
"for type=[" + type.getTypeName() + "])"));
} }
public void testLicenseToAndFromXContentForEveryLicenseType() throws Exception { public void testLicenseToAndFromXContentForEveryLicenseType() throws Exception {
for (License.LicenseType type : License.LicenseType.values()) { for (License.LicenseType type : License.LicenseType.values()) {
final License license1 = License.builder() final License license1 = randomLicense(type)
.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 // We need a signature that parses correctly, but it doesn't need to verify
.signature("AAAAAgAAAA34V2kfTJVtvdL2LttwAAABmFJ6NGRnbEM3WVQrZVQwNkdKQmR1VytlMTMyM1J0dTZ1WGwyY2ZCVFhqMGtJU2gzZ3pnNTVpOW" + .signature("AAAABQAAAA2MUoEqXb9K9Ie5d6JJAAAAIAo5/x6hrsGh1GqqrJmy4qgmEC7gK0U4zQ6q5ZEMhm4jAAABAAAwVZKGAmDELUlS5PScBkhQsZa" +
"F5Y1NaUkwyN2VsTEtCYnlZR2c5WWtjQ0phaDlhRjlDUXViUmUwMWhjSkE2TFcwSGdneTJHbUV4N2RHUWJxV20ybjRsZHRzV2xkN0ZmdDlYblJmNVc" + "DaQTtJ4ZP5EnZ/nLpmCt9Dj7d/FRsgMtHmSJLrr2CdrIo4Vx5VuhmbwzZvXMttLz2lrJzG7770PX3TnC9e7F9GdnE9ec0FP2U0ZlLOBOtPuirX0q+j" +
"xMlBWeU81V1hLUm1EK0V1dmF3cFdlSGZzTU5SZE1qUmFra3JkS1hCanBWVmVTaFFwV3BVZERzeG9Sci9rYnlJK2toODZXY09tNmFHUVNUL3IyUHEx" + "6GfB+DLyE5D+Lo1NQ3eLJGvbd3DBYPWJxkb+EBVHczCH2OrIEVWnN/TafmkdZCPX5PcultkNOs3j7d3s7b51EXHKoye8UTcB/RGmzZwMah+E6I/VJk" +
"V3VSTlBneWNJcFQ0bXl0cmhNNnRwbE1CWE4zWjJ5eGFuWFo0NGhsb3B5WFd1eTdYbFFWQkxFVFFPSlBERlB0eVVJYXVSZ0lsR2JpRS9rN1h4MSsvN" + "qu7UHL8bB01wJeqo6WxI4LC/9+f5kpmHrUu3CHe5pHbmMGDk7O6/cwt1pw/hnJXKIFCi36IGaKcHLgORxQdN0uzE=")
"UpOcGN6cU1NOHN1cHNtSTFIUGN1bWNGNEcxekhrblhNOXZ2VEQvYmRzQUFwbytUZEpRR3l6QU5oS2ZFSFdSbGxxNDZyZ0xvUHIwRjdBL2JqcnJnNG" +
"FlK09Cek9pYlJ5Umc9PQAAAQAth77fQLF7CCEL7wA6Z0/UuRm/weECcsjW/50kBnPLO8yEs+9/bPa5LSU0bF6byEXOVeO0ebUQfztpjulbXh8TrBD" +
"SG+6VdxGtohPo2IYPBaXzGs3LOOor6An/lhptxBWdwYmfbcp0m8mnXZh1vN9rmbTsZXnhBIoPTaRDwUBi3vJ3Ms3iLaEm4S8Slrfmtht2jUjgGZ2v" +
"AeZ9OHU2YsGtrSpz6f")
.build(); .build();
XContentParser parser = XContentType.JSON.xContent().createParser(NamedXContentRegistry.EMPTY, THROW_UNSUPPORTED_OPERATION, XContentParser parser = XContentType.JSON.xContent().createParser(NamedXContentRegistry.EMPTY, THROW_UNSUPPORTED_OPERATION,
Strings.toString(license1)); Strings.toString(license1));
@ -83,6 +168,46 @@ public class LicenseTests extends ESTestCase {
assertThat(license2.issuedTo(), equalTo(license1.issuedTo())); assertThat(license2.issuedTo(), equalTo(license1.issuedTo()));
assertThat(license2.expiryDate(), equalTo(license1.expiryDate())); assertThat(license2.expiryDate(), equalTo(license1.expiryDate()));
assertThat(license2.issueDate(), equalTo(license1.issueDate())); assertThat(license2.issueDate(), equalTo(license1.issueDate()));
assertThat(license2.maxNodes(), equalTo(license1.maxNodes()));
assertThat(license2.maxResourceUnits(), equalTo(license1.maxResourceUnits()));
}
}
public void testSerializationOfLicenseForEveryLicenseType() throws Exception {
for (License.LicenseType type : License.LicenseType.values()) {
final String signature = randomBoolean() ? null : "AAAABQAAAA2MUoEqXb9K9Ie5d6JJAAAAIAo5/x6hrsGh1GqqrJmy4qgmEC7gK0U4zQ6q5ZEM" +
"hm4jAAABAAAwVZKGAmDELUlS5PScBkhQsZaDaQTtJ4ZP5EnZ/nLpmCt9Dj7d/FRsgMtHmSJLrr2CdrIo4Vx5VuhmbwzZvXMttLz2lrJzG7770PX3TnC9e7" +
"F9GdnE9ec0FP2U0ZlLOBOtPuirX0q+j6GfB+DLyE5D+Lo1NQ3eLJGvbd3DBYPWJxkb+EBVHczCH2OrIEVWnN/TafmkdZCPX5PcultkNOs3j7d3s7b51EXH" +
"Koye8UTcB/RGmzZwMah+E6I/VJkqu7UHL8bB01wJeqo6WxI4LC/9+f5kpmHrUu3CHe5pHbmMGDk7O6/cwt1pw/hnJXKIFCi36IGaKcHLgORxQdN0uzE=";
final int version;
if (type == License.LicenseType.ENTERPRISE) {
version = randomIntBetween(License.VERSION_ENTERPRISE, License.VERSION_CURRENT);
} else {
version = randomIntBetween(License.VERSION_NO_FEATURE_TYPE, License.VERSION_CURRENT);
}
final License license1 = randomLicense(type).signature(signature).version(version).build();
final BytesStreamOutput out = new BytesStreamOutput();
out.setVersion(Version.CURRENT);
license1.writeTo(out);
final StreamInput in = out.bytes().streamInput();
in.setVersion(Version.CURRENT);
final License license2 = License.readLicense(in);
assertThat(in.read(), Matchers.equalTo(-1));
assertThat(license2, notNullValue());
assertThat(license2.type(), equalTo(type.getTypeName()));
assertThat(license2.version(), equalTo(version));
assertThat(license2.signature(), equalTo(signature));
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()));
assertThat(license2.maxNodes(), equalTo(license1.maxNodes()));
assertThat(license2.maxResourceUnits(), equalTo(license1.maxResourceUnits()));
} }
} }
@ -158,4 +283,17 @@ public class LicenseTests extends ESTestCase {
assertThat(exception.getMessage(), containsString("malformed signature for license [4056779d-b823-4c12-a9cb-efa4a8d8c422]")); assertThat(exception.getMessage(), containsString("malformed signature for license [4056779d-b823-4c12-a9cb-efa4a8d8c422]"));
assertThat(exception.getCause(), instanceOf(IllegalArgumentException.class)); assertThat(exception.getCause(), instanceOf(IllegalArgumentException.class));
} }
private License.Builder randomLicense(License.LicenseType type) {
return 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(type == License.LicenseType.ENTERPRISE ? -1 : randomIntBetween(1, 100))
.maxResourceUnits(type == License.LicenseType.ENTERPRISE ? randomIntBetween(1, 100) : -1)
.issuedTo(randomAlphaOfLengthBetween(5, 50))
.issuer(randomAlphaOfLengthBetween(5, 50));
}
} }

View File

@ -6,6 +6,7 @@
package org.elasticsearch.license; package org.elasticsearch.license;
import org.elasticsearch.ElasticsearchSecurityException; import org.elasticsearch.ElasticsearchSecurityException;
import org.elasticsearch.cluster.node.DiscoveryNodes;
import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.ESTestCase;
import java.util.Arrays; import java.util.Arrays;
@ -33,4 +34,8 @@ public class LicenseUtilsTests extends ESTestCase {
exception = new ElasticsearchSecurityException("msg"); exception = new ElasticsearchSecurityException("msg");
assertFalse(LicenseUtils.isLicenseExpiredException(exception)); assertFalse(LicenseUtils.isLicenseExpiredException(exception));
} }
public void testVersionsUpToDate() {
assertThat(LicenseUtils.compatibleLicenseVersion(DiscoveryNodes.EMPTY_NODES), equalTo(License.VERSION_CURRENT));
}
} }

View File

@ -366,6 +366,7 @@ public class ClusterStatsMonitoringDocTests extends BaseMonitoringDocTestCase<Cl
+ "\"expiry_date\":\"2017-08-07T12:03:22.133Z\"," + "\"expiry_date\":\"2017-08-07T12:03:22.133Z\","
+ "\"expiry_date_in_millis\":1502107402133," + "\"expiry_date_in_millis\":1502107402133,"
+ "\"max_nodes\":2," + "\"max_nodes\":2,"
+ "\"max_resource_units\":null,"
+ "\"issued_to\":\"customer\"," + "\"issued_to\":\"customer\","
+ "\"issuer\":\"elasticsearch\"," + "\"issuer\":\"elasticsearch\","
+ "\"start_date_in_millis\":-1" + "\"start_date_in_millis\":-1"

View File

@ -20,7 +20,7 @@ teardown:
- do: - do:
license.get: {} license.get: {}
## a license object has 11 attributes ## the rest API defaults to a v4 license output with 11 attributes
- length: { license: 11 } - length: { license: 11 }
## bwc for licenses format ## bwc for licenses format

View File

@ -13,35 +13,39 @@ teardown:
license.post: license.post:
acknowledge: true acknowledge: true
body: | 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}} {"license":{"uid":"6e57906b-a8d1-4c1f-acb7-73a16edc3934","type":"enterprise","issue_date_in_millis":1523456691721,"expiry_date_in_millis":1838816691721,"max_nodes":null,"max_resource_units":50,"issued_to":"rest-test","issuer":"elasticsearch","signature":"AAAABQAAAA0sKPJdf9T6DItbXVJKAAAAIAo5/x6hrsGh1GqqrJmy4qgmEC7gK0U4zQ6q5ZEMhm4jAAABAKFCHrix7w/xPG14+wdhld1RmphDmXmHfL1xeuI33Ahr1mOUYZ30eR6GZuh7CnK8BQhfq+z63lgctJepWlvwDSgkOvXWLHrJun7YSCrzz1bism0ZHWw7Swb9DO7vePomVBo/Hm9+eX0pV4/cFQNMmbFaX11tqJZYBEO6sNASVAFL7A1ZcVoB2evweGU9pUQYvFvmyzzySf99miDo3NH0XYdownEdtoNgFfmqa3+koCP7onmRZ1h9jhsDOi30RX/DTDXQKW+XoREnOHCoOAJFxwip/c1qaQAOqp1H6+P20ZGr2sIPiU97OVEU9kulm+E+jgiVW3LwGheOXsUOd1B8Mp0=","start_date_in_millis":-1}}
- match: { license_status: "valid" } - match: { license_status: "valid" }
- do: - do:
license.get: {} license.get: {}
## a license object has 11 attributes ## a v4 (7.X compatible) license object has 11 attributes
- length: { license: 11 } - length: { license: 11 }
## by default the enterprise license is "platinum" ## by default the enterprise license is "platinum", and return "max_nodes"
- match: { license.type: "platinum" } - match: { license.type: "platinum" }
- match: { license.max_nodes: 50 }
- do: - do:
license.get: license.get:
accept_enterprise: "true" accept_enterprise: "true"
## a license object has 11 attributes ## a v5 license object has 12 attributes
- length: { license: 11 } - length: { license: 12 }
## opt-in to return real enterprise type ## opt-in to return real enterprise type
- match: { license.type: "enterprise" } - match: { license.type: "enterprise" }
- match: { license.max_resource_units: 50 }
- match: { license.max_nodes: null }
- do: - do:
license.get: license.get:
accept_enterprise: "false" accept_enterprise: "false"
## a license object has 11 attributes ## a v4 license object has 11 attributes
- length: { license: 11 } - length: { license: 11 }
## opt-out of real enterprise type ## opt-out of real enterprise type
- match: { license.type: "platinum" } - match: { license.type: "platinum" }
- match: { license.max_nodes: 50 }