@@ -113,13 +113,19 @@ static boolean isBasic(String typeName) {
113113 static boolean isTrial (String typeName ) {
114114 return TRIAL .getTypeName ().equals (typeName );
115115 }
116+
117+ static boolean isEnterprise (String typeName ) {
118+ return ENTERPRISE .getTypeName ().equals (typeName );
119+ }
120+
116121 }
117122
118123 public static final int VERSION_START = 1 ;
119124 public static final int VERSION_NO_FEATURE_TYPE = 2 ;
120125 public static final int VERSION_START_DATE = 3 ;
121126 public static final int VERSION_CRYPTO_ALGORITHMS = 4 ;
122- public static final int VERSION_CURRENT = VERSION_CRYPTO_ALGORITHMS ;
127+ public static final int VERSION_ENTERPRISE = 5 ;
128+ public static final int VERSION_CURRENT = VERSION_ENTERPRISE ;
123129
124130 /**
125131 * XContent param name to deserialize license(s) with
@@ -153,13 +159,14 @@ static boolean isTrial(String typeName) {
153159 private final long expiryDate ;
154160 private final long startDate ;
155161 private final int maxNodes ;
162+ private final int maxResourceUnits ;
156163 private final OperationMode operationMode ;
157164
158165 /**
159166 * Decouples operation mode of a license from the license type value.
160167 * <p>
161168 * Note: The mode indicates features that should be made available, but it does not indicate whether the license is active!
162- *
169+ * <p>
163170 * The id byte is used for ordering operation modes
164171 */
165172 public enum OperationMode {
@@ -176,13 +183,16 @@ public enum OperationMode {
176183 this .id = id ;
177184 }
178185
179- /** Returns non-zero positive number when <code>opMode1</code> is greater than <code>opMode2</code> */
186+ /**
187+ * Returns non-zero positive number when <code>opMode1</code> is greater than <code>opMode2</code>
188+ */
180189 public static int compare (OperationMode opMode1 , OperationMode opMode2 ) {
181190 return Integer .compare (opMode1 .id , opMode2 .id );
182191 }
183192
184193 /**
185194 * Determine the operating mode for a license type
195+ *
186196 * @see LicenseType#resolve(License)
187197 * @see #parse(String)
188198 */
@@ -211,6 +221,7 @@ public static OperationMode resolve(LicenseType type) {
211221 * Parses an {@code OperatingMode} from a String.
212222 * The string must name an operating mode, and not a licensing level (that is, it cannot parse old style license levels
213223 * such as "dev" or "silver").
224+ *
214225 * @see #description()
215226 */
216227 public static OperationMode parse (String mode ) {
@@ -227,8 +238,8 @@ public String description() {
227238 }
228239 }
229240
230- private License (int version , String uid , String issuer , String issuedTo , long issueDate , String type ,
231- String subscriptionType , String feature , String signature , long expiryDate , int maxNodes , long startDate ) {
241+ private License (int version , String uid , String issuer , String issuedTo , long issueDate , String type , String subscriptionType ,
242+ String feature , String signature , long expiryDate , int maxNodes , int maxResourceUnits , long startDate ) {
232243 this .version = version ;
233244 this .uid = uid ;
234245 this .issuer = issuer ;
@@ -246,6 +257,7 @@ private License(int version, String uid, String issuer, String issuedTo, long is
246257 this .expiryDate = expiryDate ;
247258 }
248259 this .maxNodes = maxNodes ;
260+ this .maxResourceUnits = maxResourceUnits ;
249261 this .startDate = startDate ;
250262 this .operationMode = OperationMode .resolve (LicenseType .resolve (this ));
251263 validate ();
@@ -294,12 +306,21 @@ public long expiryDate() {
294306 }
295307
296308 /**
297- * @return the maximum number of nodes this license has been issued for
309+ * @return the maximum number of nodes this license has been issued for, or {@code -1} if this license is not node based.
298310 */
299311 public int maxNodes () {
300312 return maxNodes ;
301313 }
302314
315+ /**
316+ * @return the maximum number of "resource units" this license has been issued for, or {@code -1} if this license is not resource based.
317+ * A "resource unit" is a measure of computing power (RAM/CPU), the definition of which is maintained outside of the license format,
318+ * or this class.
319+ */
320+ public int maxResourceUnits () {
321+ return maxResourceUnits ;
322+ }
323+
303324 /**
304325 * @return a string representing the entity this licenses has been issued to
305326 */
@@ -386,20 +407,39 @@ private void validate() {
386407 throw new IllegalStateException ("uid can not be null" );
387408 } else if (feature == null && version == VERSION_START ) {
388409 throw new IllegalStateException ("feature can not be null" );
389- } else if (maxNodes == -1 ) {
390- throw new IllegalStateException ("maxNodes has to be set" );
391410 } else if (expiryDate == -1 ) {
392411 throw new IllegalStateException ("expiryDate has to be set" );
393412 } else if (expiryDate == LicenseService .BASIC_SELF_GENERATED_LICENSE_EXPIRATION_MILLIS && LicenseType .isBasic (type ) == false ) {
394413 throw new IllegalStateException ("only basic licenses are allowed to have no expiration" );
395414 }
415+
416+ if (LicenseType .isEnterprise (type ) && version < VERSION_ENTERPRISE ) {
417+ throw new IllegalStateException ("license type [" + type + "] is not a valid for version [" + version + "] licenses" );
418+ }
419+ validateLimits (type , maxNodes , maxResourceUnits );
420+ }
421+
422+ private static void validateLimits (String type , int maxNodes , int maxResourceUnits ) {
423+ if (LicenseType .isEnterprise (type )) {
424+ if (maxResourceUnits == -1 ) {
425+ throw new IllegalStateException ("maxResourceUnits must be set for enterprise licenses (type=[" + type + "])" );
426+ } else if (maxNodes != -1 ) {
427+ throw new IllegalStateException ("maxNodes may not be set for enterprise licenses (type=[" + type + "])" );
428+ }
429+ } else {
430+ if (maxNodes == -1 ) {
431+ throw new IllegalStateException ("maxNodes has to be set" );
432+ } else if (maxResourceUnits != -1 ) {
433+ throw new IllegalStateException ("maxResourceUnits may only be set for enterprise licenses (not permitted for type=[" +
434+ type + "])" );
435+ }
436+ }
396437 }
397438
398439 public static License readLicense (StreamInput in ) throws IOException {
399440 int version = in .readVInt (); // Version for future extensibility
400441 if (version > VERSION_CURRENT ) {
401- throw new ElasticsearchException ("Unknown license version found, please upgrade all nodes to the latest elasticsearch-license" +
402- " plugin" );
442+ throw new ElasticsearchException ("Unknown license version found, please upgrade all nodes to the latest elasticsearch release" );
403443 }
404444 Builder builder = builder ();
405445 builder .version (version );
@@ -414,6 +454,9 @@ public static License readLicense(StreamInput in) throws IOException {
414454 }
415455 builder .expiryDate (in .readLong ());
416456 builder .maxNodes (in .readInt ());
457+ if (version >= VERSION_ENTERPRISE ) {
458+ builder .maxResourceUnits (in .readInt ());
459+ }
417460 builder .issuedTo (in .readString ());
418461 builder .issuer (in .readString ());
419462 builder .signature (in .readOptionalString ());
@@ -436,6 +479,9 @@ public void writeTo(StreamOutput out) throws IOException {
436479 }
437480 out .writeLong (expiryDate );
438481 out .writeInt (maxNodes );
482+ if (version >= VERSION_ENTERPRISE ) {
483+ out .writeInt (maxResourceUnits );
484+ }
439485 out .writeString (issuedTo );
440486 out .writeString (issuer );
441487 out .writeOptionalString (signature );
@@ -496,7 +542,14 @@ public XContentBuilder toInnerXContent(XContentBuilder builder, Params params) t
496542 if (expiryDate != LicenseService .BASIC_SELF_GENERATED_LICENSE_EXPIRATION_MILLIS ) {
497543 builder .timeField (Fields .EXPIRY_DATE_IN_MILLIS , Fields .EXPIRY_DATE , expiryDate );
498544 }
499- builder .field (Fields .MAX_NODES , maxNodes );
545+
546+ if (version >= VERSION_ENTERPRISE ) {
547+ builder .field (Fields .MAX_NODES , maxNodes == -1 ? null : maxNodes );
548+ builder .field (Fields .MAX_RESOURCE_UNITS , maxResourceUnits == -1 ? null : maxResourceUnits );
549+ } else {
550+ builder .field (Fields .MAX_NODES , maxNodes );
551+ }
552+
500553 builder .field (Fields .ISSUED_TO , issuedTo );
501554 builder .field (Fields .ISSUER , issuer );
502555 if (!licenseSpecMode && !restViewMode && signature != null ) {
@@ -541,6 +594,8 @@ public static License fromXContent(XContentParser parser) throws IOException {
541594 builder .startDate (parser .longValue ());
542595 } else if (Fields .MAX_NODES .equals (currentFieldName )) {
543596 builder .maxNodes (parser .intValue ());
597+ } else if (Fields .MAX_RESOURCE_UNITS .equals (currentFieldName )) {
598+ builder .maxResourceUnits (parser .intValue ());
544599 } else if (Fields .ISSUED_TO .equals (currentFieldName )) {
545600 builder .issuedTo (parser .text ());
546601 } else if (Fields .ISSUER .equals (currentFieldName )) {
@@ -583,7 +638,7 @@ public static License fromXContent(XContentParser parser) throws IOException {
583638 throw new ElasticsearchException ("malformed signature for license [" + builder .uid + "]" );
584639 } else if (version > VERSION_CURRENT ) {
585640 throw new ElasticsearchException ("Unknown license version found, please upgrade all nodes to the latest " +
586- "elasticsearch-license plugin" );
641+ "elasticsearch-license plugin" );
587642 }
588643 // signature version is the source of truth
589644 builder .version (version );
@@ -615,8 +670,7 @@ public static License fromSource(BytesReference bytes, XContentType xContentType
615670 // EMPTY is safe here because we don't call namedObject
616671 try (InputStream byteStream = bytes .streamInput ();
617672 XContentParser parser = xContentType .xContent ()
618- .createParser (NamedXContentRegistry .EMPTY , LoggingDeprecationHandler .INSTANCE , byteStream ))
619- {
673+ .createParser (NamedXContentRegistry .EMPTY , LoggingDeprecationHandler .INSTANCE , byteStream )) {
620674 License license = null ;
621675 if (parser .nextToken () == XContentParser .Token .START_OBJECT ) {
622676 if (parser .nextToken () == XContentParser .Token .FIELD_NAME ) {
@@ -665,7 +719,7 @@ public boolean equals(Object o) {
665719
666720 if (issueDate != license .issueDate ) return false ;
667721 if (expiryDate != license .expiryDate ) return false ;
668- if (startDate != license .startDate ) return false ;
722+ if (startDate != license .startDate ) return false ;
669723 if (maxNodes != license .maxNodes ) return false ;
670724 if (version != license .version ) return false ;
671725 if (uid != null ? !uid .equals (license .uid ) : license .uid != null ) return false ;
@@ -690,7 +744,7 @@ public int hashCode() {
690744 result = 31 * result + (feature != null ? feature .hashCode () : 0 );
691745 result = 31 * result + (signature != null ? signature .hashCode () : 0 );
692746 result = 31 * result + (int ) (expiryDate ^ (expiryDate >>> 32 ));
693- result = 31 * result + (int ) (startDate ^ (startDate >>> 32 ));
747+ result = 31 * result + (int ) (startDate ^ (startDate >>> 32 ));
694748 result = 31 * result + maxNodes ;
695749 result = 31 * result + version ;
696750 return result ;
@@ -709,6 +763,7 @@ public static final class Fields {
709763 public static final String START_DATE_IN_MILLIS = "start_date_in_millis" ;
710764 public static final String START_DATE = "start_date" ;
711765 public static final String MAX_NODES = "max_nodes" ;
766+ public static final String MAX_RESOURCE_UNITS = "max_resource_units" ;
712767 public static final String ISSUED_TO = "issued_to" ;
713768 public static final String ISSUER = "issuer" ;
714769 public static final String VERSION = "version" ;
@@ -752,6 +807,7 @@ public static class Builder {
752807 private long expiryDate = -1 ;
753808 private long startDate = -1 ;
754809 private int maxNodes = -1 ;
810+ private int maxResourceUnits = -1 ;
755811
756812 public Builder uid (String uid ) {
757813 this .uid = uid ;
@@ -807,6 +863,11 @@ public Builder maxNodes(int maxNodes) {
807863 return this ;
808864 }
809865
866+ public Builder maxResourceUnits (int maxUnits ) {
867+ this .maxResourceUnits = maxUnits ;
868+ return this ;
869+ }
870+
810871 public Builder signature (String signature ) {
811872 if (signature != null ) {
812873 this .signature = signature ;
@@ -821,17 +882,18 @@ public Builder startDate(long startDate) {
821882
822883 public Builder fromLicenseSpec (License license , String signature ) {
823884 return uid (license .uid ())
824- .version (license .version ())
825- .issuedTo (license .issuedTo ())
826- .issueDate (license .issueDate ())
827- .startDate (license .startDate ())
828- .type (license .type ())
829- .subscriptionType (license .subscriptionType )
830- .feature (license .feature )
831- .maxNodes (license .maxNodes ())
832- .expiryDate (license .expiryDate ())
833- .issuer (license .issuer ())
834- .signature (signature );
885+ .version (license .version ())
886+ .issuedTo (license .issuedTo ())
887+ .issueDate (license .issueDate ())
888+ .startDate (license .startDate ())
889+ .type (license .type ())
890+ .subscriptionType (license .subscriptionType )
891+ .feature (license .feature )
892+ .maxNodes (license .maxNodes ())
893+ .maxResourceUnits (license .maxResourceUnits ())
894+ .expiryDate (license .expiryDate ())
895+ .issuer (license .issuer ())
896+ .signature (signature );
835897 }
836898
837899 /**
@@ -840,15 +902,15 @@ public Builder fromLicenseSpec(License license, String signature) {
840902 */
841903 public Builder fromPre20LicenseSpec (License pre20License ) {
842904 return uid (pre20License .uid ())
843- .issuedTo (pre20License .issuedTo ())
844- .issueDate (pre20License .issueDate ())
845- .maxNodes (pre20License .maxNodes ())
846- .expiryDate (pre20License .expiryDate ());
905+ .issuedTo (pre20License .issuedTo ())
906+ .issueDate (pre20License .issueDate ())
907+ .maxNodes (pre20License .maxNodes ())
908+ .expiryDate (pre20License .expiryDate ());
847909 }
848910
849911 public License build () {
850912 return new License (version , uid , issuer , issuedTo , issueDate , type ,
851- subscriptionType , feature , signature , expiryDate , maxNodes , startDate );
913+ subscriptionType , feature , signature , expiryDate , maxNodes , maxResourceUnits , startDate );
852914 }
853915
854916 public Builder validate () {
@@ -864,11 +926,10 @@ public Builder validate() {
864926 throw new IllegalStateException ("uid can not be null" );
865927 } else if (signature == null ) {
866928 throw new IllegalStateException ("signature can not be null" );
867- } else if (maxNodes == -1 ) {
868- throw new IllegalStateException ("maxNodes has to be set" );
869929 } else if (expiryDate == -1 ) {
870930 throw new IllegalStateException ("expiryDate has to be set" );
871931 }
932+ validateLimits (type , maxNodes , maxResourceUnits );
872933 return this ;
873934 }
874935
0 commit comments