diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/License.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/License.java index 5dddd8169e27b..90b8a35cbaf1c 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/License.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/License.java @@ -15,6 +15,7 @@ import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.xcontent.LoggingDeprecationHandler; import org.elasticsearch.common.xcontent.XContentHelper; +import org.elasticsearch.xcontent.ToXContent; import org.elasticsearch.xcontent.ToXContentObject; import org.elasticsearch.xcontent.XContentBuilder; import org.elasticsearch.xcontent.XContentParser; @@ -518,6 +519,8 @@ public XContentBuilder toInnerXContent(XContentBuilder builder, Params params) t if (expiryDate != LicenseSettings.BASIC_SELF_GENERATED_LICENSE_EXPIRATION_MILLIS) { builder.timestampFieldsFromUnixEpochMillis(Fields.EXPIRY_DATE_IN_MILLIS, Fields.EXPIRY_DATE, expiryDate); + double daysUntilExpiry = LicenseUtils.getDaysUntilExpiry(this, System.currentTimeMillis()); + builder.field(Fields.EXPIRY_IN_DAYS, String.format(Locale.ROOT, "%.1f", daysUntilExpiry)); } if (licenseVersion >= VERSION_ENTERPRISE) { @@ -740,6 +743,7 @@ public static final class Fields { public static final String FEATURE = "feature"; public static final String EXPIRY_DATE_IN_MILLIS = "expiry_date_in_millis"; public static final String EXPIRY_DATE = "expiry_date"; + public static final String EXPIRY_IN_DAYS = "expiry_in_days"; public static final String START_DATE_IN_MILLIS = "start_date_in_millis"; public static final String START_DATE = "start_date"; public static final String MAX_NODES = "max_nodes"; diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/LicenseUtils.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/LicenseUtils.java index 7e67ee892043d..c8ebdbdac367d 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/LicenseUtils.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/LicenseUtils.java @@ -171,4 +171,21 @@ public static String getExpiryWarning(long licenseExpiryDate, long currentTime) } return null; } + + /** + * Calculate the number of days until license expiry + * @param license The license to check + * @param now Current time in milliseconds + * @return Number of days until expiry (can be negative if expired) + */ + public static double getDaysUntilExpiry(License license, long now) { + if (license == null) { + return 0.0; + } + final long expiryDate = getExpiryDate(license); + if (expiryDate == LicenseSettings.BASIC_SELF_GENERATED_LICENSE_EXPIRATION_MILLIS) { + return Double.POSITIVE_INFINITY; + } + return (expiryDate - now) / (24.0 * 60 * 60 * 1000); + } } diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/license/LicenseUtilsTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/license/LicenseUtilsTests.java index 5f7c6761312d1..cbd24091d7938 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/license/LicenseUtilsTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/license/LicenseUtilsTests.java @@ -77,6 +77,49 @@ public void testGetXPackLicenseStatus() { assertThat(status.expiryWarning(), containsStringIgnoringCase("license expired")); } + public void testGetDaysUntilExpiry() { + // Test null license + assertEquals(0.0, LicenseUtils.getDaysUntilExpiry(null, 0), 0.01); + + // Test basic license (should return infinity) + License basicLicense = License.builder() + .uid("test") + .type(License.LicenseType.BASIC) + .issueDate(System.currentTimeMillis()) + .expiryDate(LicenseSettings.BASIC_SELF_GENERATED_LICENSE_EXPIRATION_MILLIS) + .issuedTo("test") + .issuer("test") + .maxNodes(1000) + .build(); + assertEquals(Double.POSITIVE_INFINITY, LicenseUtils.getDaysUntilExpiry(basicLicense, System.currentTimeMillis()), 0.01); + + // Test regular license with 30 days remaining + long now = System.currentTimeMillis(); + long thirtyDaysInMillis = TimeUnit.DAYS.toMillis(30); + License trialLicense = License.builder() + .uid("test") + .type(License.LicenseType.TRIAL) + .issueDate(now) + .expiryDate(now + thirtyDaysInMillis) + .issuedTo("test") + .issuer("test") + .maxNodes(1000) + .build(); + assertEquals(30.0, LicenseUtils.getDaysUntilExpiry(trialLicense, now), 0.01); + + // Test expired license (should return negative days) + License expiredLicense = License.builder() + .uid("test") + .type(License.LicenseType.TRIAL) + .issueDate(now - thirtyDaysInMillis) + .expiryDate(now - thirtyDaysInMillis) + .issuedTo("test") + .issuer("test") + .maxNodes(1000) + .build(); + assertEquals(-30.0, LicenseUtils.getDaysUntilExpiry(expiredLicense, now), 0.01); + } + private License getLicense(License.LicenseType type, long issueDate, long expiryDate) { License.Builder builder = License.builder() .uid(UUIDs.randomBase64UUID(random()))