diff --git a/elasticsearch/license/base/src/main/java/org/elasticsearch/license/core/License.java b/elasticsearch/license/base/src/main/java/org/elasticsearch/license/core/License.java index 403b9870f3c..f07d38edeed 100644 --- a/elasticsearch/license/base/src/main/java/org/elasticsearch/license/core/License.java +++ b/elasticsearch/license/base/src/main/java/org/elasticsearch/license/core/License.java @@ -101,6 +101,7 @@ public class License implements ToXContent { case "gold": return GOLD; case "platinum": + case "cloud_internal": case "internal": // bwc for 1.x subscription_type field return PLATINUM; default: @@ -196,12 +197,42 @@ public class License implements ToXContent { } /** - * @return the operation mode of the license as computed from the license type + * @return the operation mode of the license as computed from the license type or from + * the license mode file */ public OperationMode operationMode() { + synchronized (this) { + if (canReadOperationModeFromFile() && operationModeFileWatcher != null) { + return operationModeFileWatcher.getCurrentOperationMode(); + } + } return operationMode; } + private boolean canReadOperationModeFromFile() { + return type.equals("cloud_internal"); + } + + private volatile OperationModeFileWatcher operationModeFileWatcher; + + /** + * Sets the operation mode file watcher for the license and initializes the + * file watcher when the license type allows to override operation mode from file + */ + public synchronized void setOperationModeFileWatcher(final OperationModeFileWatcher operationModeFileWatcher) { + this.operationModeFileWatcher = operationModeFileWatcher; + if (canReadOperationModeFromFile()) { + this.operationModeFileWatcher.init(); + } + } + + /** + * Removes operation mode file watcher, so unused license objects can be gc'ed + */ + public synchronized void removeOperationModeFileWatcher() { + this.operationModeFileWatcher = null; + } + /** * @return the current license's status */ diff --git a/elasticsearch/license/base/src/main/java/org/elasticsearch/license/core/OperationModeFileWatcher.java b/elasticsearch/license/base/src/main/java/org/elasticsearch/license/core/OperationModeFileWatcher.java new file mode 100644 index 00000000000..3ba63e3cdd3 --- /dev/null +++ b/elasticsearch/license/base/src/main/java/org/elasticsearch/license/core/OperationModeFileWatcher.java @@ -0,0 +1,113 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.license.core; + + +import org.elasticsearch.common.logging.ESLogger; +import org.elasticsearch.license.core.License.OperationMode; +import org.elasticsearch.watcher.FileChangesListener; +import org.elasticsearch.watcher.FileWatcher; +import org.elasticsearch.watcher.ResourceWatcherService; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * File based watcher for license {@link OperationMode} + * Watches for changes in licenseModePath, use + * {@link #getCurrentOperationMode()} to access the latest mode + * + * In case of failure to read a valid operation mode from licenseModePath, + * the operation mode will default to PLATINUM + */ +public final class OperationModeFileWatcher extends FileChangesListener { + private final ResourceWatcherService resourceWatcherService; + private final Path licenseModePath; + private final AtomicBoolean initialized = new AtomicBoolean(); + private final OperationMode defaultOperationMode = OperationMode.PLATINUM; + private volatile OperationMode currentOperationMode = defaultOperationMode; + private final ESLogger logger; + private final Runnable onChange; + + public OperationModeFileWatcher(ResourceWatcherService resourceWatcherService, Path licenseModePath, + ESLogger logger, Runnable onChange) { + this.resourceWatcherService = resourceWatcherService; + this.licenseModePath = licenseModePath; + this.logger = logger; + this.onChange = onChange; + } + + public void init() { + if (initialized.compareAndSet(false, true)) { + final FileWatcher watcher = new FileWatcher(licenseModePath); + watcher.addListener(this); + try { + resourceWatcherService.add(watcher, ResourceWatcherService.Frequency.HIGH); + if (Files.exists(licenseModePath)) { + onChange(licenseModePath); + } + } catch (IOException e) { + logger.error("couldn't initialize watching license mode file", e); + } + } + } + + /** + * Returns the current operation mode based on license mode file. + * Defaults to {@link OperationMode#PLATINUM} + */ + public OperationMode getCurrentOperationMode() { + return currentOperationMode; + } + + @Override + public void onFileInit(Path file) { + onChange(file); + } + + @Override + public void onFileCreated(Path file) { + onChange(file); + } + + @Override + public void onFileDeleted(Path file) { + onChange(file); + } + + @Override + public void onFileChanged(Path file) { + onChange(file); + } + + private synchronized void onChange(Path file) { + if (file.equals(licenseModePath)) { + currentOperationMode = defaultOperationMode; + if (Files.exists(licenseModePath) + && Files.isReadable(licenseModePath)) { + final byte[] content; + try { + content = Files.readAllBytes(licenseModePath); + } catch (IOException e) { + logger.error("couldn't read operation mode from [{}]", e, licenseModePath.toAbsolutePath().toString()); + return; + } + String operationMode = new String(content, StandardCharsets.UTF_8); + try { + currentOperationMode = OperationMode.resolve(operationMode); + } catch (IllegalArgumentException e) { + logger.error("invalid operation mode in [{}]", e, licenseModePath.toAbsolutePath().toString()); + return; + } + } + onChange.run(); + } + } +} + diff --git a/elasticsearch/license/base/src/test/java/org/elasticsearch/license/core/LicenseOperationModeUpdateTests.java b/elasticsearch/license/base/src/test/java/org/elasticsearch/license/core/LicenseOperationModeUpdateTests.java new file mode 100644 index 00000000000..8bd379862cc --- /dev/null +++ b/elasticsearch/license/base/src/test/java/org/elasticsearch/license/core/LicenseOperationModeUpdateTests.java @@ -0,0 +1,73 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.license.core; + +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.watcher.FileWatcher; +import org.elasticsearch.watcher.ResourceWatcherService; +import org.junit.Before; + +import java.nio.file.Path; + +import static org.elasticsearch.license.core.OperationModeFileWatcherTests.writeMode; +import static org.hamcrest.Matchers.equalTo; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; + +public class LicenseOperationModeUpdateTests extends ESTestCase { + + private OperationModeFileWatcher operationModeFileWatcher; + private Path licenseModeFile; + private ResourceWatcherService resourceWatcherService; + + @Before + public void init() throws Exception { + licenseModeFile = createTempFile(); + resourceWatcherService = mock(ResourceWatcherService.class); + operationModeFileWatcher = new OperationModeFileWatcher(resourceWatcherService, licenseModeFile, logger, () -> {}); + } + + public void testLicenseOperationModeUpdate() throws Exception { + String type = randomFrom("trial", "basic", "standard", "gold", "platinum"); + License license = License.builder() + .uid("id") + .expiryDate(0) + .issueDate(0) + .issuedTo("elasticsearch") + .issuer("issuer") + .type(type) + .maxNodes(1) + .build(); + + assertThat(license.operationMode(), equalTo(License.OperationMode.resolve(type))); + writeMode("gold", licenseModeFile); + license.setOperationModeFileWatcher(operationModeFileWatcher); + verifyZeroInteractions(resourceWatcherService); + assertThat(license.operationMode(), equalTo(License.OperationMode.resolve(type))); + } + + public void testCloudInternalLicenseOperationModeUpdate() throws Exception { + License license = License.builder() + .uid("id") + .expiryDate(0) + .issueDate(0) + .issuedTo("elasticsearch") + .issuer("issuer") + .type("cloud_internal") + .maxNodes(1) + .build(); + + assertThat(license.operationMode(), equalTo(License.OperationMode.PLATINUM)); + writeMode("gold", licenseModeFile); + license.setOperationModeFileWatcher(operationModeFileWatcher); + verify(resourceWatcherService, times(1)).add(any(FileWatcher.class), eq(ResourceWatcherService.Frequency.HIGH)); + assertThat(license.operationMode(), equalTo(License.OperationMode.GOLD)); + } +} diff --git a/elasticsearch/license/base/src/test/java/org/elasticsearch/license/core/OperationModeFileWatcherTests.java b/elasticsearch/license/base/src/test/java/org/elasticsearch/license/core/OperationModeFileWatcherTests.java new file mode 100644 index 00000000000..a51ea032c91 --- /dev/null +++ b/elasticsearch/license/base/src/test/java/org/elasticsearch/license/core/OperationModeFileWatcherTests.java @@ -0,0 +1,114 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.license.core; + +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.threadpool.TestThreadPool; +import org.elasticsearch.watcher.ResourceWatcherService; +import org.junit.After; +import org.junit.Before; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.hamcrest.Matchers.equalTo; + +public class OperationModeFileWatcherTests extends ESTestCase { + private ResourceWatcherService watcherService; + private TestThreadPool threadPool; + private Path licenseModePath; + private OperationModeFileWatcher operationModeFileWatcher; + private AtomicInteger onChangeCounter; + + @Before + public void setup() throws Exception { + threadPool = new TestThreadPool("license mode file watcher tests"); + Settings settings = Settings.builder() + .put("resource.reload.interval.high", "10ms") + .build(); + watcherService = new ResourceWatcherService(settings, + threadPool); + watcherService.start(); + licenseModePath = createTempFile(); + onChangeCounter = new AtomicInteger(); + operationModeFileWatcher = new OperationModeFileWatcher(watcherService, licenseModePath, logger, + () -> onChangeCounter.incrementAndGet()); + } + + @After + public void shutdown() throws InterruptedException { + terminate(threadPool); + watcherService.stop(); + } + + public void testInit() throws Exception { + writeMode("gold"); + assertThat(operationModeFileWatcher.getCurrentOperationMode(), equalTo(License.OperationMode.PLATINUM)); + operationModeFileWatcher.init(); + assertThat(onChangeCounter.get(), equalTo(2)); + assertThat(operationModeFileWatcher.getCurrentOperationMode(), equalTo(License.OperationMode.GOLD)); + } + + public void testUpdateModeFromFile() throws Exception { + Files.delete(licenseModePath); + operationModeFileWatcher.init(); + assertThat(operationModeFileWatcher.getCurrentOperationMode(), equalTo(License.OperationMode.PLATINUM)); + writeMode("gold"); + assertBusy(() -> assertThat(operationModeFileWatcher.getCurrentOperationMode(), equalTo(License.OperationMode.GOLD))); + writeMode("basic"); + assertBusy(() -> assertThat(operationModeFileWatcher.getCurrentOperationMode(), equalTo(License.OperationMode.BASIC))); + assertThat(onChangeCounter.get(), equalTo(2)); + } + + public void testDeleteModeFromFile() throws Exception { + Files.delete(licenseModePath); + operationModeFileWatcher.init(); + writeMode("gold"); + assertBusy(() -> assertThat(operationModeFileWatcher.getCurrentOperationMode(), equalTo(License.OperationMode.GOLD))); + Files.delete(licenseModePath); + assertBusy(() -> assertThat(operationModeFileWatcher.getCurrentOperationMode(), equalTo(License.OperationMode.PLATINUM))); + assertThat(onChangeCounter.get(), equalTo(2)); + } + + public void testInvalidModeFromFile() throws Exception { + writeMode("invalid"); + operationModeFileWatcher.init(); + assertThat(operationModeFileWatcher.getCurrentOperationMode(), equalTo(License.OperationMode.PLATINUM)); + operationModeFileWatcher.onFileChanged(licenseModePath); + assertThat(operationModeFileWatcher.getCurrentOperationMode(), equalTo(License.OperationMode.PLATINUM)); + } + + public void testLicenseModeFileIsDirectory() throws Exception { + licenseModePath = createTempDir(); + operationModeFileWatcher.init(); + assertThat(operationModeFileWatcher.getCurrentOperationMode(), equalTo(License.OperationMode.PLATINUM)); + operationModeFileWatcher.onFileChanged(licenseModePath); + assertThat(operationModeFileWatcher.getCurrentOperationMode(), equalTo(License.OperationMode.PLATINUM)); + } + + public void testLicenseModeFileCreatedAfterInit() throws Exception { + Files.delete(licenseModePath); + operationModeFileWatcher.init(); + assertThat(operationModeFileWatcher.getCurrentOperationMode(), equalTo(License.OperationMode.PLATINUM)); + Path tempFile = createTempFile(); + writeMode("gold", tempFile); + licenseModePath = tempFile; + assertBusy(() -> assertThat(operationModeFileWatcher.getCurrentOperationMode(), equalTo(License.OperationMode.GOLD))); + } + + private void writeMode(String mode) throws IOException { + writeMode(mode, licenseModePath); + } + + static void writeMode(String mode, Path file) throws IOException { + Files.write(file, mode.getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE); + } +} diff --git a/elasticsearch/x-pack/graph/src/main/java/org/elasticsearch/xpack/graph/GraphLicensee.java b/elasticsearch/x-pack/graph/src/main/java/org/elasticsearch/xpack/graph/GraphLicensee.java index 90518bc8cee..7d31411597b 100644 --- a/elasticsearch/x-pack/graph/src/main/java/org/elasticsearch/xpack/graph/GraphLicensee.java +++ b/elasticsearch/x-pack/graph/src/main/java/org/elasticsearch/xpack/graph/GraphLicensee.java @@ -7,7 +7,6 @@ package org.elasticsearch.xpack.graph; import org.elasticsearch.common.Strings; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.license.core.License; import org.elasticsearch.license.core.License.OperationMode; import org.elasticsearch.license.plugin.core.AbstractLicenseeComponent; import org.elasticsearch.license.plugin.core.LicenseState; @@ -28,17 +27,15 @@ public class GraphLicensee extends AbstractLicenseeComponent { } @Override - public String[] acknowledgmentMessages(License currentLicense, License newLicense) { - switch (newLicense.operationMode()) { + public String[] acknowledgmentMessages(OperationMode currentMode, OperationMode newMode) { + switch (newMode) { case BASIC: case STANDARD: case GOLD: - if (currentLicense != null) { - switch (currentLicense.operationMode()) { - case TRIAL: - case PLATINUM: - return new String[] { "Graph will be disabled" }; - } + switch (currentMode) { + case TRIAL: + case PLATINUM: + return new String[] { "Graph will be disabled" }; } break; } diff --git a/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/Licensing.java b/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/Licensing.java index 4fcb7ee2fb1..7629a129b26 100644 --- a/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/Licensing.java +++ b/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/Licensing.java @@ -12,6 +12,7 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Module; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.env.Environment; import org.elasticsearch.license.plugin.action.delete.DeleteLicenseAction; import org.elasticsearch.license.plugin.action.delete.TransportDeleteLicenseAction; import org.elasticsearch.license.plugin.action.get.GetLicenseAction; @@ -25,6 +26,7 @@ import org.elasticsearch.license.plugin.rest.RestGetLicenseAction; import org.elasticsearch.license.plugin.rest.RestPutLicenseAction; import org.elasticsearch.plugins.ActionPlugin; import org.elasticsearch.rest.RestHandler; +import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xpack.graph.GraphLicensee; import org.elasticsearch.xpack.monitoring.MonitoringLicensee; import org.elasticsearch.xpack.security.SecurityLicenseState; @@ -83,14 +85,16 @@ public class Licensing implements ActionPlugin { RestDeleteLicenseAction.class); } - public Collection createComponents(ClusterService clusterService, Clock clock, + public Collection createComponents(ClusterService clusterService, Clock clock, Environment environment, + ResourceWatcherService resourceWatcherService, SecurityLicenseState securityLicenseState) { SecurityLicensee securityLicensee = new SecurityLicensee(settings, securityLicenseState); WatcherLicensee watcherLicensee = new WatcherLicensee(settings); MonitoringLicensee monitoringLicensee = new MonitoringLicensee(settings); GraphLicensee graphLicensee = new GraphLicensee(settings); LicensesService licensesService = new LicensesService(settings, clusterService, clock, - Arrays.asList(securityLicensee, watcherLicensee, monitoringLicensee, graphLicensee)); + environment, resourceWatcherService, + Arrays.asList(securityLicensee, watcherLicensee, monitoringLicensee, graphLicensee)); return Arrays.asList(licensesService, securityLicenseState, securityLicensee, watcherLicensee, monitoringLicensee, graphLicensee); } diff --git a/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/LicenseSchedule.java b/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/LicenseSchedule.java index 133ea78220f..6059e340cb6 100644 --- a/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/LicenseSchedule.java +++ b/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/LicenseSchedule.java @@ -8,8 +8,6 @@ package org.elasticsearch.license.plugin.core; import org.elasticsearch.license.core.License; import org.elasticsearch.xpack.scheduler.SchedulerEngine; -import static org.elasticsearch.license.plugin.core.LicensesService.GRACE_PERIOD_DURATION; -import static org.elasticsearch.license.plugin.core.LicensesService.getLicenseState; public class LicenseSchedule implements SchedulerEngine.Schedule { @@ -22,12 +20,12 @@ public class LicenseSchedule implements SchedulerEngine.Schedule { @Override public long nextScheduledTimeAfter(long startTime, long time) { long nextScheduledTime = -1; - switch (getLicenseState(license, time)) { + switch (LicenseState.resolve(license, time)) { case ENABLED: nextScheduledTime = license.expiryDate(); break; case GRACE_PERIOD: - nextScheduledTime = license.expiryDate() + GRACE_PERIOD_DURATION.getMillis(); + nextScheduledTime = license.expiryDate() + LicenseState.GRACE_PERIOD_DURATION.getMillis(); break; case DISABLED: if (license.issueDate() > time) { @@ -42,4 +40,4 @@ public class LicenseSchedule implements SchedulerEngine.Schedule { } return nextScheduledTime; } -} \ No newline at end of file +} diff --git a/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/LicenseState.java b/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/LicenseState.java index a6eba86c532..8db2f996199 100644 --- a/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/LicenseState.java +++ b/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/LicenseState.java @@ -5,6 +5,11 @@ */ package org.elasticsearch.license.plugin.core; +import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.license.core.License; + +import static org.elasticsearch.license.plugin.core.LicensesService.days; + /** * States of a registered licensee * based on the current license @@ -38,5 +43,26 @@ public enum LicenseState { * changes to {@link #ENABLED}, otherwise * remains unchanged */ - DISABLED + DISABLED; + + /** + * Duration of grace period after a license has expired + */ + public static final TimeValue GRACE_PERIOD_DURATION = days(7); + + public static LicenseState resolve(final License license, long time) { + if (license == null) { + return DISABLED; + } + if (license.issueDate() > time) { + return DISABLED; + } + if (license.expiryDate() > time) { + return ENABLED; + } + if ((license.expiryDate() + GRACE_PERIOD_DURATION.getMillis()) > time) { + return GRACE_PERIOD; + } + return DISABLED; + } } diff --git a/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/Licensee.java b/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/Licensee.java index cadeb922568..9928b2ff677 100644 --- a/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/Licensee.java +++ b/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/Licensee.java @@ -5,10 +5,10 @@ */ package org.elasticsearch.license.plugin.core; -import org.elasticsearch.license.core.License; import org.elasticsearch.license.core.License.OperationMode; import java.util.Locale; +import java.util.Objects; public interface Licensee { @@ -26,11 +26,10 @@ public interface Licensee { /** * Messages to be returned when - * installing newLicense - * when currentLicense is - * active + * changing from current operation mode + * to new operation mode */ - String[] acknowledgmentMessages(License currentLicense, License newLicense); + String[] acknowledgmentMessages(OperationMode currentMode, OperationMode newMode); /** * Notifies when a new license is activated @@ -85,6 +84,20 @@ public interface Licensee { return licenseState; } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Status status = (Status) o; + return Objects.equals(mode, status.mode) && Objects.equals(licenseState, status.licenseState); + } + + @Override + public int hashCode() { + return Objects.hash(mode, licenseState); + } + @Override public String toString() { switch (licenseState) { diff --git a/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/LicensesService.java b/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/LicensesService.java index 2c313a5c09e..1a1dd6414a2 100644 --- a/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/LicensesService.java +++ b/elasticsearch/x-pack/license-plugin/src/main/java/org/elasticsearch/license/plugin/core/LicensesService.java @@ -18,18 +18,21 @@ import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.component.AbstractLifecycleComponent; import org.elasticsearch.common.component.Lifecycle; -import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.joda.FormatDateTimeFormatter; import org.elasticsearch.common.joda.Joda; import org.elasticsearch.common.logging.LoggerMessageFormat; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.env.Environment; import org.elasticsearch.gateway.GatewayService; import org.elasticsearch.license.core.License; import org.elasticsearch.license.core.LicenseVerifier; +import org.elasticsearch.license.core.OperationModeFileWatcher; import org.elasticsearch.license.plugin.action.delete.DeleteLicenseRequest; import org.elasticsearch.license.plugin.action.put.PutLicenseRequest; import org.elasticsearch.license.plugin.action.put.PutLicenseResponse; +import org.elasticsearch.watcher.ResourceWatcherService; +import org.elasticsearch.xpack.XPackPlugin; import org.elasticsearch.xpack.scheduler.SchedulerEngine; import org.elasticsearch.xpack.support.clock.Clock; @@ -40,7 +43,6 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.UUID; -import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; @@ -74,6 +76,11 @@ public class LicensesService extends AbstractLifecycleComponent implements Clust private SchedulerEngine scheduler; private final Clock clock; + /** + * File watcher for operation mode changes + */ + private final OperationModeFileWatcher operationModeFileWatcher; + /** * Callbacks to notify relative to license expiry */ @@ -84,27 +91,24 @@ public class LicensesService extends AbstractLifecycleComponent implements Clust */ private int trialLicenseMaxNodes = 1000; - /** - * Duration of grace period after a license has expired - */ - public static final TimeValue GRACE_PERIOD_DURATION = days(7); - - private static final String LICENSE_JOB = "licenseJob"; + public static final String LICENSE_JOB = "licenseJob"; private static final FormatDateTimeFormatter DATE_FORMATTER = Joda.forPattern("EEEE, MMMMM dd, yyyy", Locale.ROOT); 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:"; - public LicensesService(Settings settings, ClusterService clusterService, Clock clock, - List registeredLicensees) { + public LicensesService(Settings settings, ClusterService clusterService, Clock clock, Environment env, + ResourceWatcherService resourceWatcherService, List registeredLicensees) { super(settings); this.clusterService = clusterService; - populateExpirationCallbacks(); this.clock = clock; this.scheduler = new SchedulerEngine(clock); - this.scheduler.register(this); this.registeredLicensees = registeredLicensees.stream().map(InternalLicensee::new).collect(Collectors.toList()); + this.operationModeFileWatcher = new OperationModeFileWatcher(resourceWatcherService, + XPackPlugin.resolveConfigFile(env, "license_mode"), logger, () -> notifyLicensees(getLicense())); + this.scheduler.register(this); + populateExpirationCallbacks(); } private void populateExpirationCallbacks() { @@ -229,7 +233,8 @@ public class LicensesService extends AbstractLifecycleComponent implements Clust "override the current license?"}); } for (InternalLicensee licensee : registeredLicensees) { - String[] listenerAcknowledgeMessages = licensee.acknowledgmentMessages(currentLicense, newLicense); + String[] listenerAcknowledgeMessages = licensee.acknowledgmentMessages( + currentLicense.operationMode(), newLicense.operationMode()); if (listenerAcknowledgeMessages.length > 0) { acknowledgeMessages.put(licensee.id(), listenerAcknowledgeMessages); } @@ -305,13 +310,12 @@ public class LicensesService extends AbstractLifecycleComponent implements Clust }); } - public LicenseState licenseState() { - if (registeredLicensees.size() > 0) { - return registeredLicensees.get(0).currentLicenseState; - } else { - final License license = getLicense(clusterService.state().metaData().custom(LicensesMetaData.TYPE)); - return getLicenseState(license, clock.millis()); + public Licensee.Status licenseeStatus() { + final License license = getLicense(); + if (license == null) { + return Licensee.Status.MISSING; } + return new Licensee.Status(license.operationMode(), LicenseState.resolve(license, clock.millis())); } public License getLicense() { @@ -376,7 +380,6 @@ public class LicensesService extends AbstractLifecycleComponent implements Clust scheduler.stop(); // clear all handlers registeredLicensees.clear(); - // clear current license currentLicense.set(null); } @@ -430,45 +433,22 @@ public class LicensesService extends AbstractLifecycleComponent implements Clust } if (license != null) { logger.debug("notifying [{}] listeners", registeredLicensees.size()); - switch (getLicenseState(license, clock.millis())) { + final LicenseState licenseState = LicenseState.resolve(license, clock.millis()); + Licensee.Status status = new Licensee.Status(license.operationMode(), licenseState); + for (InternalLicensee licensee : registeredLicensees) { + licensee.onChange(status); + } + switch (status.getLicenseState()) { case ENABLED: - for (InternalLicensee licensee : registeredLicensees) { - licensee.onChange(license, LicenseState.ENABLED); - } - logger.debug("license [{}] - valid", license.uid()); - break; + logger.debug("license [{}] - valid", license.uid()); break; case GRACE_PERIOD: - for (InternalLicensee licensee : registeredLicensees) { - licensee.onChange(license, LicenseState.GRACE_PERIOD); - } - logger.warn("license [{}] - grace", license.uid()); - break; + logger.warn("license [{}] - grace", license.uid()); break; case DISABLED: - for (InternalLicensee licensee : registeredLicensees) { - licensee.onChange(license, LicenseState.DISABLED); - } - logger.warn("license [{}] - expired", license.uid()); - break; + logger.warn("license [{}] - expired", license.uid()); break; } } } - static LicenseState getLicenseState(final License license, long time) { - if (license == null) { - return LicenseState.DISABLED; - } - if (license.issueDate() > time) { - return LicenseState.DISABLED; - } - if (license.expiryDate() > time) { - return LicenseState.ENABLED; - } - if ((license.expiryDate() + GRACE_PERIOD_DURATION.getMillis()) > time) { - return LicenseState.GRACE_PERIOD; - } - return LicenseState.DISABLED; - } - /** * Notifies registered licensees of license state change and/or new active license * based on the license in currentLicensesMetaData. @@ -480,16 +460,22 @@ public class LicensesService extends AbstractLifecycleComponent implements Clust // license can be null if the trial license is yet to be auto-generated // in this case, it is a no-op if (license != null) { - notifyLicensees(license); - if (license.equals(currentLicense.get()) == false) { + final License previousLicense = currentLicense.get(); + if (license.equals(previousLicense) == false) { currentLicense.set(license); + license.setOperationModeFileWatcher(operationModeFileWatcher); scheduler.add(new SchedulerEngine.Job(LICENSE_JOB, new LicenseSchedule(license))); for (ExpirationCallback expirationCallback : expirationCallbacks) { scheduler.add(new SchedulerEngine.Job(expirationCallback.getId(), (startTime, now) -> expirationCallback.nextScheduledTimeForExpiry(license.expiryDate(), startTime, now))); } + if (previousLicense != null) { + // remove operationModeFileWatcher to gc the old license object + previousLicense.removeOperationModeFileWatcher(); + } } + notifyLicensees(license); } } @@ -516,7 +502,7 @@ public class LicensesService extends AbstractLifecycleComponent implements Clust License license = metaData.getLicense(); if (license == LicensesMetaData.LICENSE_TOMBSTONE) { return license; - } else { + } else if (license != null) { boolean autoGeneratedLicense = License.isAutoGeneratedLicense(license.signature()); if ((autoGeneratedLicense && TrialLicense.verify(license)) || (!autoGeneratedLicense && LicenseVerifier.verifyLicense(license))) { @@ -531,9 +517,9 @@ public class LicensesService extends AbstractLifecycleComponent implements Clust * Stores acknowledgement, expiration and license notification callbacks * for a registered listener */ - private class InternalLicensee { - volatile License currentLicense = null; - volatile LicenseState currentLicenseState = LicenseState.DISABLED; + private final class InternalLicensee { + volatile Licensee.Status currentStatus = Licensee.Status.MISSING; + private final Licensee licensee; private InternalLicensee(Licensee licensee) { @@ -542,7 +528,7 @@ public class LicensesService extends AbstractLifecycleComponent implements Clust @Override public String toString() { - return "(listener: " + licensee.id() + ", state: " + currentLicenseState.name() + ")"; + return "(listener: " + licensee.id() + ", state: " + currentStatus + ")"; } public String id() { @@ -553,31 +539,21 @@ public class LicensesService extends AbstractLifecycleComponent implements Clust return licensee.expirationMessages(); } - public String[] acknowledgmentMessages(License currentLicense, License newLicense) { - return licensee.acknowledgmentMessages(currentLicense, newLicense); + public String[] acknowledgmentMessages(License.OperationMode currentMode, License.OperationMode newMode) { + return licensee.acknowledgmentMessages(currentMode, newMode); } - public void onChange(License license, LicenseState state) { - synchronized (this) { - if (currentLicense == null // not yet initialized - || !currentLicense.equals(license) // current license has changed - || currentLicenseState != state) { // same license but state has changed - logger.debug("licensee [{}] notified", licensee.id()); - licensee.onChange(new Licensee.Status(license.operationMode(), state)); - currentLicense = license; - currentLicenseState = state; - } + public synchronized void onChange(final Licensee.Status status) { + if (currentStatus == null // not yet initialized + || !currentStatus.equals(status)) { // current license has changed + logger.debug("licensee [{}] notified", licensee.id()); + licensee.onChange(status); + currentStatus = status; } } public void onRemove() { - synchronized (this) { - if (currentLicense != null || currentLicenseState != LicenseState.DISABLED) { - currentLicense = null; - currentLicenseState = LicenseState.DISABLED; - licensee.onChange(Licensee.Status.MISSING); - } - } + onChange(Licensee.Status.MISSING); } } } \ No newline at end of file diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/LicensesServiceClusterTests.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/LicensesServiceClusterTests.java index ae2b4038af7..d9e47d8f9eb 100644 --- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/LicensesServiceClusterTests.java +++ b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/LicensesServiceClusterTests.java @@ -5,37 +5,27 @@ */ package org.elasticsearch.license.plugin; -import org.elasticsearch.client.ClusterAdminClient; import org.elasticsearch.common.network.NetworkModule; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.env.Environment; import org.elasticsearch.license.core.License; -import org.elasticsearch.license.plugin.action.delete.DeleteLicenseAction; -import org.elasticsearch.license.plugin.action.delete.DeleteLicenseRequestBuilder; -import org.elasticsearch.license.plugin.action.delete.DeleteLicenseResponse; -import org.elasticsearch.license.plugin.action.get.GetLicenseAction; -import org.elasticsearch.license.plugin.action.get.GetLicenseRequestBuilder; -import org.elasticsearch.license.plugin.action.get.GetLicenseResponse; -import org.elasticsearch.license.plugin.action.put.PutLicenseAction; -import org.elasticsearch.license.plugin.action.put.PutLicenseRequestBuilder; -import org.elasticsearch.license.plugin.action.put.PutLicenseResponse; import org.elasticsearch.license.plugin.core.LicenseState; -import org.elasticsearch.license.plugin.core.LicensesMetaData; import org.elasticsearch.license.plugin.core.LicensesService; -import org.elasticsearch.license.plugin.core.LicensesStatus; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.test.ESIntegTestCase.ClusterScope; import org.elasticsearch.xpack.MockNetty3Plugin; import org.elasticsearch.xpack.XPackPlugin; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Arrays; import java.util.Collection; import static org.elasticsearch.license.plugin.TestUtils.generateSignedLicense; import static org.elasticsearch.test.ESIntegTestCase.Scope.TEST; import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.not; -import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; @ClusterScope(scope = TEST, numDataNodes = 0, numClientNodes = 0, maxNumDataNodes = 0, transportClientRatio = 0) @@ -55,6 +45,7 @@ public class LicensesServiceClusterTests extends AbstractLicensesIntegrationTest return Settings.builder() .put(super.nodeSettings(nodeOrdinal)) .put("node.data", true) + .put("resource.reload.interval.high", "500ms") // for license mode file watcher .put(NetworkModule.HTTP_ENABLED.getKey(), true); } @@ -79,37 +70,54 @@ public class LicensesServiceClusterTests extends AbstractLicensesIntegrationTest ensureGreen(); logger.info("--> put signed license"); - License license = generateAndPutLicenses(); - getAndCheckLicense(license); + LicensingClient licensingClient = new LicensingClient(client()); + License license = generateSignedLicense(TimeValue.timeValueMinutes(1)); + putLicense(license); + assertThat(licensingClient.prepareGetLicense().get().license(), equalTo(license)); + assertOperationMode(license.operationMode()); + logger.info("--> restart all nodes"); internalCluster().fullRestart(); ensureYellow(); - + licensingClient = new LicensingClient(client()); logger.info("--> get and check signed license"); - getAndCheckLicense(license); - + assertThat(licensingClient.prepareGetLicense().get().license(), equalTo(license)); logger.info("--> remove licenses"); - removeLicense(); - assertNoLicense(); + licensingClient.prepareDeleteLicense().get(); + assertOperationMode(License.OperationMode.MISSING); + logger.info("--> restart all nodes"); internalCluster().fullRestart(); + licensingClient = new LicensingClient(client()); ensureYellow(); - assertNoLicense(); + assertThat(licensingClient.prepareGetLicense().get().license(), nullValue()); + assertOperationMode(License.OperationMode.MISSING); + wipeAllLicenses(); } + public void testCloudInternalLicense() throws Exception { + wipeAllLicenses(); - private void assertLicenseState(LicenseState state) throws InterruptedException { - boolean success = awaitBusy(() -> { - for (LicensesService service : internalCluster().getDataNodeInstances(LicensesService.class)) { - if (service.licenseState() == state) { - return true; - } - } - return false; - }); - assertTrue(success); + int numNodes = randomIntBetween(1, 5); + logger.info("--> starting {} node(s)", numNodes); + for (int i = 0; i < numNodes; i++) { + internalCluster().startNode(); + } + ensureGreen(); + + logger.info("--> put signed license"); + LicensingClient licensingClient = new LicensingClient(client()); + License license = generateSignedLicense("cloud_internal", License.VERSION_CURRENT, System.currentTimeMillis(), + TimeValue.timeValueMinutes(1)); + putLicense(license); + assertThat(licensingClient.prepareGetLicense().get().license(), equalTo(license)); + assertOperationMode(License.OperationMode.PLATINUM); + writeCloudInternalMode("gold"); + assertOperationMode(License.OperationMode.GOLD); + writeCloudInternalMode("basic"); + assertOperationMode(License.OperationMode.BASIC); } public void testClusterRestartWhileEnabled() throws Exception { @@ -143,7 +151,7 @@ public class LicensesServiceClusterTests extends AbstractLicensesIntegrationTest internalCluster().startNode(); ensureGreen(); assertLicenseState(LicenseState.ENABLED); - putLicense(TestUtils.generateExpiredLicense(System.currentTimeMillis() - LicensesService.GRACE_PERIOD_DURATION.getMillis())); + putLicense(TestUtils.generateExpiredLicense(System.currentTimeMillis() - LicenseState.GRACE_PERIOD_DURATION.getMillis())); assertLicenseState(LicenseState.DISABLED); logger.info("--> restart node"); internalCluster().fullRestart(); @@ -160,44 +168,36 @@ public class LicensesServiceClusterTests extends AbstractLicensesIntegrationTest assertLicenseState(LicenseState.ENABLED); } - private void removeLicense() throws Exception { - ClusterAdminClient cluster = internalCluster().client().admin().cluster(); - ensureGreen(); - License putLicenses = generateSignedLicense(TimeValue.timeValueMinutes(1)); - PutLicenseRequestBuilder putLicenseRequestBuilder = new PutLicenseRequestBuilder(cluster, PutLicenseAction.INSTANCE); - putLicenseRequestBuilder.setLicense(putLicenses); - DeleteLicenseResponse response = new DeleteLicenseRequestBuilder(cluster, DeleteLicenseAction.INSTANCE).get(); - assertThat(response.isAcknowledged(), equalTo(true)); + private void assertLicenseState(LicenseState state) throws InterruptedException { + boolean success = awaitBusy(() -> { + for (LicensesService service : internalCluster().getDataNodeInstances(LicensesService.class)) { + if (service.licenseeStatus().getLicenseState() == state) { + return true; + } + } + return false; + }); + assertTrue(success); } - private License generateAndPutLicenses() throws Exception { - ClusterAdminClient cluster = internalCluster().client().admin().cluster(); - ensureGreen(); - License putLicenses = generateSignedLicense(TimeValue.timeValueMinutes(1)); - PutLicenseRequestBuilder putLicenseRequestBuilder = new PutLicenseRequestBuilder(cluster, PutLicenseAction.INSTANCE); - putLicenseRequestBuilder.setLicense(putLicenses); - putLicenseRequestBuilder.setAcknowledge(true); - final PutLicenseResponse putLicenseResponse = putLicenseRequestBuilder.get(); - assertThat(putLicenseResponse.isAcknowledged(), equalTo(true)); - assertThat(putLicenseResponse.status(), equalTo(LicensesStatus.VALID)); - return putLicenses; + private void assertOperationMode(License.OperationMode operationMode) throws InterruptedException { + boolean success = awaitBusy(() -> { + for (LicensesService service : internalCluster().getDataNodeInstances(LicensesService.class)) { + if (service.licenseeStatus().getMode() == operationMode) { + return true; + } + } + return false; + }); + assertTrue(success); } - private void assertNoLicense() { - ClusterAdminClient cluster = internalCluster().client().admin().cluster(); - final GetLicenseResponse response = new GetLicenseRequestBuilder(cluster, GetLicenseAction.INSTANCE).get(); - assertThat(response.license(), nullValue()); - LicensesMetaData licensesMetaData = clusterService().state().metaData().custom(LicensesMetaData.TYPE); - assertThat(licensesMetaData, notNullValue()); - assertThat(licensesMetaData.getLicense(), equalTo(LicensesMetaData.LICENSE_TOMBSTONE)); + private void writeCloudInternalMode(String mode) throws Exception { + for (Environment environment : internalCluster().getDataOrMasterNodeInstances(Environment.class)) { + Path licenseModePath = XPackPlugin.resolveConfigFile(environment, "license_mode"); + Files.createDirectories(licenseModePath.getParent()); + Files.write(licenseModePath, mode.getBytes(StandardCharsets.UTF_8)); + } } - private void getAndCheckLicense(License license) { - ClusterAdminClient cluster = internalCluster().client().admin().cluster(); - final GetLicenseResponse response = new GetLicenseRequestBuilder(cluster, GetLicenseAction.INSTANCE).get(); - assertThat(response.license(), equalTo(license)); - LicensesMetaData licensesMetaData = clusterService().state().metaData().custom(LicensesMetaData.TYPE); - assertThat(licensesMetaData, notNullValue()); - assertThat(licensesMetaData.getLicense(), not(LicensesMetaData.LICENSE_TOMBSTONE)); - } } diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/TestUtils.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/TestUtils.java index 8904b90c086..16b6b6a583e 100644 --- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/TestUtils.java +++ b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/TestUtils.java @@ -6,9 +6,6 @@ package org.elasticsearch.license.plugin; import org.elasticsearch.action.ActionListener; -import org.elasticsearch.client.Client; -import org.elasticsearch.cluster.block.ClusterBlock; -import org.elasticsearch.cluster.block.ClusterBlockLevel; import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.io.PathUtils; import org.elasticsearch.common.joda.DateMathParser; @@ -29,10 +26,8 @@ import org.elasticsearch.license.plugin.core.LicensesService; import org.elasticsearch.license.plugin.core.LicensesStatus; import org.junit.Assert; -import java.io.IOException; import java.nio.file.Path; import java.util.List; -import java.util.Set; import java.util.UUID; import java.util.concurrent.Callable; import java.util.concurrent.CopyOnWriteArrayList; @@ -92,8 +87,11 @@ public class TestUtils { } public static License generateSignedLicense(String type, long issueDate, TimeValue expiryDuration) throws Exception { + return generateSignedLicense(type, randomIntBetween(License.VERSION_START, License.VERSION_CURRENT), issueDate, expiryDuration); + } + + public static License generateSignedLicense(String type, int version, long issueDate, TimeValue expiryDuration) throws Exception { long issue = (issueDate != -1L) ? issueDate : System.currentTimeMillis() - TimeValue.timeValueHours(2).getMillis(); - int version = randomIntBetween(License.VERSION_START, License.VERSION_CURRENT); final String licenseType; if (version < License.VERSION_NO_FEATURE_TYPE) { licenseType = randomFrom("subscription", "internal", "development"); @@ -117,17 +115,26 @@ public class TestUtils { return signer.sign(builder.build()); } - public static License generateExpiredLicense() throws Exception { - return generateExpiredLicense(System.currentTimeMillis() - TimeValue.timeValueHours(randomIntBetween(1, 10)).getMillis()); + public static License generateExpiredLicense(long expiryDate) throws Exception { + return generateExpiredLicense(randomFrom("basic", "silver", "dev", "gold", "platinum"), expiryDate); } - public static License generateExpiredLicense(long expiryDate) throws Exception { + public static License generateExpiredLicense() throws Exception { + return generateExpiredLicense(randomFrom("basic", "silver", "dev", "gold", "platinum")); + } + + public static License generateExpiredLicense(String type) throws Exception { + return generateExpiredLicense(type, + System.currentTimeMillis() - TimeValue.timeValueHours(randomIntBetween(1, 10)).getMillis()); + } + + public static License generateExpiredLicense(String type, long expiryDate) throws Exception { final License.Builder builder = License.builder() .uid(UUID.randomUUID().toString()) .version(License.VERSION_CURRENT) .expiryDate(expiryDate) .issueDate(expiryDate - TimeValue.timeValueMinutes(10).getMillis()) - .type(randomFrom("basic", "silver", "dev", "gold", "platinum")) + .type(type) .issuedTo("customer") .issuer("elasticsearch") .maxNodes(5); @@ -139,20 +146,6 @@ public class TestUtils { return PathUtils.get(TestUtils.class.getResource(resource).toURI()); } - public static void awaitNoBlock(final Client client) throws InterruptedException { - boolean success = awaitBusy(() -> { - Set clusterBlocks = client.admin().cluster().prepareState().setLocal(true).execute().actionGet() - .getState().blocks().global(ClusterBlockLevel.METADATA_WRITE); - return clusterBlocks.isEmpty(); - }); - assertThat("awaiting no block for too long", success, equalTo(true)); - } - - public static void awaitNoPendingTasks(final Client client) throws InterruptedException { - boolean success = awaitBusy(() -> client.admin().cluster().preparePendingClusterTasks().get().getPendingTasks().isEmpty()); - assertThat("awaiting no pending tasks for too long", success, equalTo(true)); - } - public static void registerAndAckSignedLicenses(final LicensesService licensesService, License license, final LicensesStatus expectedStatus) { PutLicenseRequest putLicenseRequest = new PutLicenseRequest().license(license).acknowledge(true); @@ -183,7 +176,7 @@ public class TestUtils { public final String id; public final List statuses = new CopyOnWriteArrayList<>(); public final AtomicInteger expirationMessagesCalled = new AtomicInteger(0); - public final List> acknowledgementRequested = new CopyOnWriteArrayList<>(); + public final List> acknowledgementRequested = new CopyOnWriteArrayList<>(); private String[] acknowledgmentMessages = new String[0]; @@ -207,8 +200,8 @@ public class TestUtils { } @Override - public String[] acknowledgmentMessages(License currentLicense, License newLicense) { - acknowledgementRequested.add(new Tuple<>(currentLicense, newLicense)); + public String[] acknowledgmentMessages(License.OperationMode currentMode, License.OperationMode newMode) { + acknowledgementRequested.add(new Tuple<>(currentMode, newMode)); return acknowledgmentMessages; } @@ -218,4 +211,4 @@ public class TestUtils { statuses.add(status); } } -} \ No newline at end of file +} diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/AbstractLicenseServiceTestCase.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/AbstractLicenseServiceTestCase.java index a602aed030f..9007df3e9f5 100644 --- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/AbstractLicenseServiceTestCase.java +++ b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/AbstractLicenseServiceTestCase.java @@ -6,7 +6,6 @@ package org.elasticsearch.license.plugin.core; import java.util.Arrays; -import java.util.Collections; import org.elasticsearch.Version; import org.elasticsearch.cluster.ClusterName; @@ -19,11 +18,16 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.component.Lifecycle; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.LocalTransportAddress; +import org.elasticsearch.env.Environment; import org.elasticsearch.license.core.License; import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xpack.support.clock.ClockMock; +import org.junit.After; import org.junit.Before; +import java.nio.file.Path; + import static java.util.Collections.emptyMap; import static java.util.Collections.emptySet; import static org.mockito.Mockito.mock; @@ -33,18 +37,25 @@ public abstract class AbstractLicenseServiceTestCase extends ESTestCase { protected LicensesService licensesService; protected ClusterService clusterService; + protected ResourceWatcherService resourceWatcherService; protected ClockMock clock; protected DiscoveryNodes discoveryNodes; + protected Environment environment; @Before public void init() throws Exception { clusterService = mock(ClusterService.class); clock = new ClockMock(); discoveryNodes = mock(DiscoveryNodes.class); + resourceWatcherService = mock(ResourceWatcherService.class); + environment = mock(Environment.class); } protected void setInitialState(License license, Licensee... licensees) { - licensesService = new LicensesService(Settings.EMPTY, clusterService, clock, Arrays.asList(licensees)); + Path tempDir = createTempDir(); + when(environment.configFile()).thenReturn(tempDir); + licensesService = new LicensesService(Settings.EMPTY, clusterService, clock, environment, + resourceWatcherService, Arrays.asList(licensees)); ClusterState state = mock(ClusterState.class); final ClusterBlocks noBlock = ClusterBlocks.builder().build(); when(state.blocks()).thenReturn(noBlock); @@ -60,4 +71,9 @@ public abstract class AbstractLicenseServiceTestCase extends ESTestCase { when(clusterService.lifecycleState()).thenReturn(Lifecycle.State.STARTED); when(clusterService.getClusterName()).thenReturn(new ClusterName("a")); } -} \ No newline at end of file + + @After + public void after() { + licensesService.stop(); + } +} diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/AbstractLicenseeTestCase.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/AbstractLicenseeTestCase.java index 6e7af417af6..85f7d5ba969 100644 --- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/AbstractLicenseeTestCase.java +++ b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/AbstractLicenseeTestCase.java @@ -39,17 +39,8 @@ public abstract class AbstractLicenseeTestCase extends ESTestCase { * @param licensee The licensee to test */ public static void assertEmptyAck(OperationMode fromMode, OperationMode toMode, Licensee licensee) { - License fromLicense = mock(License.class); - when(fromLicense.operationMode()).thenReturn(fromMode); - License toLicense = mock(License.class); - when(toLicense.operationMode()).thenReturn(toMode); - - if (randomBoolean()) { - fromLicense = null; - } - // test it - String[] messages = licensee.acknowledgmentMessages(fromLicense, toLicense); + String[] messages = licensee.acknowledgmentMessages(fromMode, toMode); assertThat(fromToMessage(fromMode, toMode), messages.length, equalTo(0)); } @@ -77,12 +68,7 @@ public abstract class AbstractLicenseeTestCase extends ESTestCase { * @param licensee The licensee to test */ public static String[] ackLicenseChange(OperationMode fromMode, OperationMode toMode, Licensee licensee) { - License fromLicense = mock(License.class); - when(fromLicense.operationMode()).thenReturn(fromMode); - License toLicense = mock(License.class); - when(toLicense.operationMode()).thenReturn(toMode); - - return licensee.acknowledgmentMessages(fromLicense, toLicense); + return licensee.acknowledgmentMessages(fromMode, toMode); } /** diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicenseRegistrationTests.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicenseRegistrationTests.java index cfe023f1b9b..b351d92e228 100644 --- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicenseRegistrationTests.java +++ b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicenseRegistrationTests.java @@ -35,8 +35,6 @@ public class LicenseRegistrationTests extends AbstractLicenseServiceTestCase { assertNotNull(licenseMetaData); assertNotNull(licenseMetaData.getLicense()); assertEquals(clock.millis() + LicensesService.TRIAL_LICENSE_DURATION.millis(), licenseMetaData.getLicense().expiryDate()); - - licensesService.stop(); } public void testNotificationOnRegistration() throws Exception { @@ -47,6 +45,5 @@ public class LicenseRegistrationTests extends AbstractLicenseServiceTestCase { assertThat(licensee.statuses.size(), equalTo(1)); final LicenseState licenseState = licensee.statuses.get(0).getLicenseState(); assertTrue(licenseState == LicenseState.ENABLED); - licensesService.stop(); } } \ No newline at end of file diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicenseScheduleTests.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicenseScheduleTests.java index a8a179bb463..84981c9a7c2 100644 --- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicenseScheduleTests.java +++ b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicenseScheduleTests.java @@ -32,13 +32,13 @@ public class LicenseScheduleTests extends ESTestCase { public void testGraceLicenseSchedule() throws Exception { long triggeredTime = license.expiryDate() + between(1, - ((int) LicensesService.GRACE_PERIOD_DURATION.getMillis())); + ((int) LicenseState.GRACE_PERIOD_DURATION.getMillis())); assertThat(schedule.nextScheduledTimeAfter(license.issueDate(), triggeredTime), - equalTo(license.expiryDate() + LicensesService.GRACE_PERIOD_DURATION.getMillis())); + equalTo(license.expiryDate() + LicenseState.GRACE_PERIOD_DURATION.getMillis())); } public void testExpiredLicenseSchedule() throws Exception { - long triggeredTime = license.expiryDate() + LicensesService.GRACE_PERIOD_DURATION.getMillis() + + long triggeredTime = license.expiryDate() + LicenseState.GRACE_PERIOD_DURATION.getMillis() + randomIntBetween(1, 1000); assertThat(schedule.nextScheduledTimeAfter(license.issueDate(), triggeredTime), equalTo(-1L)); @@ -49,4 +49,4 @@ public class LicenseScheduleTests extends ESTestCase { assertThat(schedule.nextScheduledTimeAfter(triggeredTime, triggeredTime), equalTo(license.issueDate())); } -} \ No newline at end of file +} diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesAcknowledgementTests.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesAcknowledgementTests.java index ee951e4424d..1ac4d815b9e 100644 --- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesAcknowledgementTests.java +++ b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesAcknowledgementTests.java @@ -41,7 +41,7 @@ public class LicensesAcknowledgementTests extends AbstractLicenseServiceTestCase licensesService.registerLicense(putLicenseRequest, new AssertingLicensesUpdateResponse(false, LicensesStatus.VALID, Collections.singletonMap(id, acknowledgeMessages))); assertThat(licensee.acknowledgementRequested.size(), equalTo(1)); - assertThat(licensee.acknowledgementRequested.get(0).v2(), equalTo(signedLicense)); + assertThat(licensee.acknowledgementRequested.get(0).v2(), equalTo(signedLicense.operationMode())); assertThat(licensesService.getLicense(), not(signedLicense)); // try installing a signed license with acknowledgement @@ -52,8 +52,7 @@ public class LicensesAcknowledgementTests extends AbstractLicenseServiceTestCase Collections.emptyMap())); verify(clusterService, times(1)).submitStateUpdateTask(any(String.class), any(ClusterStateUpdateTask.class)); assertThat(licensee.acknowledgementRequested.size(), equalTo(1)); - assertThat(licensee.acknowledgementRequested.get(0).v2(), equalTo(signedLicense)); - licensesService.stop(); + assertThat(licensee.acknowledgementRequested.get(0).v2(), equalTo(signedLicense.operationMode())); } public void testAcknowledgementMultipleLicensee() throws Exception { @@ -78,9 +77,9 @@ public class LicensesAcknowledgementTests extends AbstractLicenseServiceTestCase expectedMessages)); verify(clusterService, times(0)).submitStateUpdateTask(any(String.class), any(ClusterStateUpdateTask.class)); assertThat(licensee2.acknowledgementRequested.size(), equalTo(1)); - assertThat(licensee2.acknowledgementRequested.get(0).v2(), equalTo(signedLicense)); + assertThat(licensee2.acknowledgementRequested.get(0).v2(), equalTo(signedLicense.operationMode())); assertThat(licensee1.acknowledgementRequested.size(), equalTo(1)); - assertThat(licensee1.acknowledgementRequested.get(0).v2(), equalTo(signedLicense)); + assertThat(licensee1.acknowledgementRequested.get(0).v2(), equalTo(signedLicense.operationMode())); assertThat(licensesService.getLicense(), not(signedLicense)); // try installing a signed license with acknowledgement @@ -91,7 +90,6 @@ public class LicensesAcknowledgementTests extends AbstractLicenseServiceTestCase licensesService.registerLicense(putLicenseRequest, new AssertingLicensesUpdateResponse(true, LicensesStatus.VALID, Collections.emptyMap())); verify(clusterService, times(1)).submitStateUpdateTask(any(String.class), any(ClusterStateUpdateTask.class)); - licensesService.stop(); } private static class AssertingLicensesUpdateResponse implements ActionListener { @@ -123,4 +121,4 @@ public class LicensesAcknowledgementTests extends AbstractLicenseServiceTestCase public void onFailure(Exception throwable) { } } -} \ No newline at end of file +} diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesNotificationTests.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesNotificationTests.java index 524059fafdf..21f6a28fe1b 100644 --- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesNotificationTests.java +++ b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/license/plugin/core/LicensesNotificationTests.java @@ -37,7 +37,7 @@ public class LicensesNotificationTests extends AbstractLicenseServiceTestCase { assertLicenseStates(assertingLicensee, LicenseState.ENABLED, LicenseState.GRACE_PERIOD); } clock.fastForward(TimeValue.timeValueMillis((license.expiryDate() + - LicensesService.GRACE_PERIOD_DURATION.getMillis()) - clock.millis())); + LicenseState.GRACE_PERIOD_DURATION.getMillis()) - clock.millis())); licensesService.onUpdate(licensesMetaData); for (AssertingLicensee assertingLicensee : assertingLicensees) { assertLicenseStates(assertingLicensee, LicenseState.ENABLED, LicenseState.GRACE_PERIOD, LicenseState.DISABLED); @@ -45,12 +45,12 @@ public class LicensesNotificationTests extends AbstractLicenseServiceTestCase { clock.setTime(new DateTime(DateTimeZone.UTC)); final License newLicense = TestUtils.generateSignedLicense(TimeValue.timeValueHours(2)); clock.fastForward(TimeValue.timeValueHours(1)); - licensesService.onUpdate(new LicensesMetaData(newLicense)); + LicensesMetaData licensesMetaData1 = new LicensesMetaData(newLicense); + licensesService.onUpdate(licensesMetaData1); for (AssertingLicensee assertingLicensee : assertingLicensees) { assertLicenseStates(assertingLicensee, LicenseState.ENABLED, LicenseState.GRACE_PERIOD, LicenseState.DISABLED, LicenseState.ENABLED); } - licensesService.stop(); } private void assertLicenseStates(AssertingLicensee licensee, LicenseState... states) { @@ -89,4 +89,4 @@ public class LicensesNotificationTests extends AbstractLicenseServiceTestCase { sb.append("]"); return sb.toString(); } -} \ No newline at end of file +} diff --git a/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/MonitoringLicensee.java b/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/MonitoringLicensee.java index 5b5ee9a59d1..4d28924830c 100644 --- a/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/MonitoringLicensee.java +++ b/elasticsearch/x-pack/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/MonitoringLicensee.java @@ -8,7 +8,6 @@ package org.elasticsearch.xpack.monitoring; import org.elasticsearch.common.Strings; import org.elasticsearch.common.logging.LoggerMessageFormat; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.license.core.License; import org.elasticsearch.license.core.License.OperationMode; import org.elasticsearch.license.plugin.core.AbstractLicenseeComponent; import org.elasticsearch.license.plugin.core.LicenseState; @@ -43,27 +42,25 @@ public class MonitoringLicensee extends AbstractLicenseeComponent { } @Override - public String[] acknowledgmentMessages(License currentLicense, License newLicense) { - switch (newLicense.operationMode()) { + public String[] acknowledgmentMessages(OperationMode currentMode, OperationMode newMode) { + switch (newMode) { case BASIC: - if (currentLicense != null) { - switch (currentLicense.operationMode()) { - case TRIAL: - case STANDARD: - case GOLD: - case PLATINUM: - return new String[] { - LoggerMessageFormat.format( - "Multi-cluster support is disabled for clusters with [{}] license. If you are\n" + - "running multiple clusters, users won't be able to access the clusters with\n" + - "[{}] licenses from within a single X-Pack Kibana instance. You will have to deploy a\n" + - "separate and dedicated X-pack Kibana instance for each [{}] cluster you wish to monitor.", - newLicense.type(), newLicense.type(), newLicense.type()), - LoggerMessageFormat.format( - "Automatic index cleanup is locked to {} days for clusters with [{}] license.", - MonitoringSettings.HISTORY_DURATION.getDefault(Settings.EMPTY).days(), newLicense.type()) - }; - } + switch (currentMode) { + case TRIAL: + case STANDARD: + case GOLD: + case PLATINUM: + return new String[] { + LoggerMessageFormat.format( + "Multi-cluster support is disabled for clusters with [{}] license. If you are\n" + + "running multiple clusters, users won't be able to access the clusters with\n" + + "[{}] licenses from within a single X-Pack Kibana instance. You will have to deploy a\n" + + "separate and dedicated X-pack Kibana instance for each [{}] cluster you wish to monitor.", + newMode, newMode, newMode), + LoggerMessageFormat.format( + "Automatic index cleanup is locked to {} days for clusters with [{}] license.", + MonitoringSettings.HISTORY_DURATION.getDefault(Settings.EMPTY).days(), newMode) + }; } break; } diff --git a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/collector/AbstractCollectorTestCase.java b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/collector/AbstractCollectorTestCase.java index 339d071c0e7..59ddc194046 100644 --- a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/collector/AbstractCollectorTestCase.java +++ b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/agent/collector/AbstractCollectorTestCase.java @@ -22,6 +22,7 @@ import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.inject.Module; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.env.Environment; import org.elasticsearch.license.core.License; import org.elasticsearch.license.plugin.Licensing; import org.elasticsearch.license.plugin.core.LicenseState; @@ -31,6 +32,7 @@ import org.elasticsearch.plugins.Plugin; import org.elasticsearch.rest.RestHandler; import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.ESIntegTestCase.ClusterScope; +import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xpack.XPackPlugin; import org.elasticsearch.xpack.graph.GraphLicensee; import org.elasticsearch.xpack.monitoring.MonitoringLicensee; @@ -194,13 +196,14 @@ public abstract class AbstractCollectorTestCase extends MonitoringIntegTestCase } @Override - public Collection createComponents(ClusterService clusterService, Clock clock, + public Collection createComponents(ClusterService clusterService, Clock clock, Environment environment, + ResourceWatcherService resourceWatcherService, SecurityLicenseState securityLicenseState) { WatcherLicensee watcherLicensee = new WatcherLicensee(settings); MonitoringLicensee monitoringLicensee = new MonitoringLicensee(settings); GraphLicensee graphLicensee = new GraphLicensee(settings); - LicensesService licensesService = new LicenseServiceForCollectors(settings, - Arrays.asList(watcherLicensee, monitoringLicensee, graphLicensee)); + LicensesService licensesService = new LicenseServiceForCollectors(settings, environment, + resourceWatcherService, Arrays.asList(watcherLicensee, monitoringLicensee, graphLicensee)); return Arrays.asList(licensesService, watcherLicensee, monitoringLicensee, graphLicensee); } @@ -229,8 +232,9 @@ public abstract class AbstractCollectorTestCase extends MonitoringIntegTestCase private volatile License license; @Inject - public LicenseServiceForCollectors(Settings settings, List licensees) { - super(settings, null, null, licensees); + public LicenseServiceForCollectors(Settings settings, Environment env, + ResourceWatcherService resourceWatcherService, List licensees) { + super(settings, null, null, env, resourceWatcherService, licensees); this.licensees = licensees; } @@ -241,7 +245,7 @@ public abstract class AbstractCollectorTestCase extends MonitoringIntegTestCase } @Override - public LicenseState licenseState() { + public Licensee.Status licenseeStatus() { return null; } diff --git a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/license/LicenseIntegrationTests.java b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/license/LicenseIntegrationTests.java index 745c664a1bf..732016dc92a 100644 --- a/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/license/LicenseIntegrationTests.java +++ b/elasticsearch/x-pack/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/license/LicenseIntegrationTests.java @@ -11,14 +11,17 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.inject.Module; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.env.Environment; import org.elasticsearch.license.core.License; import org.elasticsearch.license.plugin.Licensing; +import org.elasticsearch.license.plugin.core.AbstractLicenseeComponent; import org.elasticsearch.license.plugin.core.LicenseState; import org.elasticsearch.license.plugin.core.Licensee; import org.elasticsearch.license.plugin.core.LicensesService; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.rest.RestHandler; import org.elasticsearch.test.ESIntegTestCase.ClusterScope; +import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xpack.XPackPlugin; import org.elasticsearch.xpack.graph.GraphLicensee; import org.elasticsearch.xpack.monitoring.MonitoringLicensee; @@ -97,13 +100,14 @@ public class LicenseIntegrationTests extends MonitoringIntegTestCase { } @Override - public Collection createComponents(ClusterService clusterService, Clock clock, + public Collection createComponents(ClusterService clusterService, Clock clock, Environment environment, + ResourceWatcherService resourceWatcherService, SecurityLicenseState securityLicenseState) { WatcherLicensee watcherLicensee = new WatcherLicensee(settings); MonitoringLicensee monitoringLicensee = new MonitoringLicensee(settings); GraphLicensee graphLicensee = new GraphLicensee(settings); - LicensesService licensesService = new MockLicenseService(settings, - Arrays.asList(watcherLicensee, monitoringLicensee, graphLicensee)); + LicensesService licensesService = new MockLicenseService(settings, environment, resourceWatcherService, + Arrays.asList(watcherLicensee, monitoringLicensee, graphLicensee)); return Arrays.asList(licensesService, watcherLicensee, monitoringLicensee, graphLicensee); } @@ -123,8 +127,9 @@ public class LicenseIntegrationTests extends MonitoringIntegTestCase { private final List licensees; @Inject - public MockLicenseService(Settings settings, List licensees) { - super(settings, null, null, licensees); + public MockLicenseService(Settings settings, Environment environment, + ResourceWatcherService resourceWatcherService, List licensees) { + super(settings, null, null, environment, resourceWatcherService, licensees); this.licensees = licensees; enable(); } @@ -143,7 +148,7 @@ public class LicenseIntegrationTests extends MonitoringIntegTestCase { } @Override - public LicenseState licenseState() { + public Licensee.Status licenseeStatus() { return null; } diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/SecurityLicensee.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/SecurityLicensee.java index dc25b44c09f..176d08addc1 100644 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/SecurityLicensee.java +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/SecurityLicensee.java @@ -37,54 +37,48 @@ public class SecurityLicensee extends AbstractLicenseeComponent { } @Override - public String[] acknowledgmentMessages(License currentLicense, License newLicense) { - switch (newLicense.operationMode()) { + public String[] acknowledgmentMessages(License.OperationMode currentMode, License.OperationMode newMode) { + switch (newMode) { case BASIC: - if (currentLicense != null) { - switch (currentLicense.operationMode()) { - case TRIAL: - case STANDARD: - case GOLD: - case PLATINUM: - return new String[] { - "The following X-Pack security functionality will be disabled: authentication, authorization, " + - "ip filtering, and auditing. Please restart your node after applying the license.", - "Field and document level access control will be disabled.", - "Custom realms will be ignored." - }; - } + switch (currentMode) { + case TRIAL: + case STANDARD: + case GOLD: + case PLATINUM: + return new String[] { + "The following X-Pack security functionality will be disabled: authentication, authorization, " + + "ip filtering, and auditing. Please restart your node after applying the license.", + "Field and document level access control will be disabled.", + "Custom realms will be ignored." + }; } break; case GOLD: - if (currentLicense != null) { - switch (currentLicense.operationMode()) { - case BASIC: - case STANDARD: - // ^^ though technically it was already disabled, it's not bad to remind them - case TRIAL: - case PLATINUM: - return new String[] { - "Field and document level access control will be disabled.", - "Custom realms will be ignored." - }; - } + switch (currentMode) { + case BASIC: + case STANDARD: + // ^^ though technically it was already disabled, it's not bad to remind them + case TRIAL: + case PLATINUM: + return new String[] { + "Field and document level access control will be disabled.", + "Custom realms will be ignored." + }; } break; case STANDARD: - if (currentLicense != null) { - switch (currentLicense.operationMode()) { - case BASIC: - // ^^ though technically it was already disabled, it's not bad to remind them - case GOLD: - case PLATINUM: - case TRIAL: - return new String[] { - "Authentication will be limited to the native realms.", - "IP filtering and auditing will be disabled.", - "Field and document level access control will be disabled.", - "Custom realms will be ignored." - }; - } + switch (currentMode) { + case BASIC: + // ^^ though technically it was already disabled, it's not bad to remind them + case GOLD: + case PLATINUM: + case TRIAL: + return new String[] { + "Authentication will be limited to the native realms.", + "IP filtering and auditing will be disabled.", + "Field and document level access control will be disabled.", + "Custom realms will be ignored." + }; } } return Strings.EMPTY_ARRAY; diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/integration/LicensingTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/integration/LicensingTests.java index 95c262e2954..ec93c9367c3 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/integration/LicensingTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/integration/LicensingTests.java @@ -31,6 +31,7 @@ import org.elasticsearch.common.inject.Module; import org.elasticsearch.common.network.NetworkModule; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.ThreadContext; +import org.elasticsearch.env.Environment; import org.elasticsearch.license.core.License.OperationMode; import org.elasticsearch.license.plugin.Licensing; import org.elasticsearch.license.plugin.core.LicenseState; @@ -42,6 +43,7 @@ import org.elasticsearch.rest.RestStatus; import org.elasticsearch.test.SecurityIntegTestCase; import org.elasticsearch.test.SecuritySettingsSource; import org.elasticsearch.transport.Transport; +import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xpack.MockNetty3Plugin; import org.elasticsearch.xpack.XPackPlugin; import org.elasticsearch.xpack.graph.GraphLicensee; @@ -258,16 +260,17 @@ public class LicensingTests extends SecurityIntegTestCase { } @Override - public Collection createComponents(ClusterService clusterService, Clock clock, + public Collection createComponents(ClusterService clusterService, Clock clock, Environment environment, + ResourceWatcherService resourceWatcherService, SecurityLicenseState securityLicenseState) { SecurityLicensee securityLicensee = new SecurityLicensee(settings, securityLicenseState); WatcherLicensee watcherLicensee = new WatcherLicensee(settings); MonitoringLicensee monitoringLicensee = new MonitoringLicensee(settings); GraphLicensee graphLicensee = new GraphLicensee(settings); - TestLicensesService licensesService = new TestLicensesService(settings, - Arrays.asList(securityLicensee, watcherLicensee, monitoringLicensee, graphLicensee)); + TestLicensesService licensesService = new TestLicensesService(settings, environment, resourceWatcherService, + Arrays.asList(securityLicensee, watcherLicensee, monitoringLicensee, graphLicensee)); return Arrays.asList(securityLicensee, licensesService, watcherLicensee, monitoringLicensee, - graphLicensee, securityLicenseState); + graphLicensee, securityLicenseState); } public InternalLicensing() { @@ -297,8 +300,9 @@ public class LicensingTests extends SecurityIntegTestCase { private final List licensees; - public TestLicensesService(Settings settings, List licensees) { - super(settings, null, null, Collections.emptyList()); + public TestLicensesService(Settings settings, Environment env, ResourceWatcherService resourceWatcherService, + List licensees) { + super(settings, null, null, env, resourceWatcherService, Collections.emptyList()); this.licensees = licensees; enable(OperationMode.BASIC); } diff --git a/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/XPackPlugin.java b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/XPackPlugin.java index 0ecbf6d309f..63322c8aba3 100644 --- a/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/XPackPlugin.java +++ b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/XPackPlugin.java @@ -191,7 +191,8 @@ public class XPackPlugin extends Plugin implements ScriptPlugin, ActionPlugin { final InternalClient internalClient = new InternalClient(settings, threadPool, client, security.getCryptoService()); components.add(internalClient); - components.addAll(licensing.createComponents(clusterService, getClock(), security.getSecurityLicenseState())); + components.addAll(licensing.createComponents(clusterService, getClock(), env, resourceWatcherService, + security.getSecurityLicenseState())); components.addAll(security.createComponents(resourceWatcherService)); // watcher http stuff diff --git a/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/action/XPackInfoResponse.java b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/action/XPackInfoResponse.java index 3dfb7852a1c..996f22b42ca 100644 --- a/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/action/XPackInfoResponse.java +++ b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/action/XPackInfoResponse.java @@ -82,20 +82,23 @@ public class XPackInfoResponse extends ActionResponse { private final String uid; private final String type; + private final String mode; private final long expiryDate; private final License.Status status; public LicenseInfo(License license) { - this(license.uid(), license.type(), license.status(), license.expiryDate()); + this(license.uid(), license.type(), license.operationMode().name().toLowerCase(Locale.ROOT), + license.status(), license.expiryDate()); } public LicenseInfo(StreamInput in) throws IOException { - this(in.readString(), in.readString(), License.Status.readFrom(in), in.readLong()); + this(in.readString(), in.readString(), in.readString(), License.Status.readFrom(in), in.readLong()); } - public LicenseInfo(String uid, String type, License.Status status, long expiryDate) { + public LicenseInfo(String uid, String type, String mode, License.Status status, long expiryDate) { this.uid = uid; this.type = type; + this.mode = mode; this.status = status; this.expiryDate = expiryDate; } @@ -108,6 +111,10 @@ public class XPackInfoResponse extends ActionResponse { return type; } + public String getMode() { + return mode; + } + public long getExpiryDate() { return expiryDate; } @@ -121,7 +128,7 @@ public class XPackInfoResponse extends ActionResponse { return builder.startObject() .field("uid", uid) .field("type", type) - .field("mode", License.OperationMode.resolve(type).name().toLowerCase(Locale.ROOT)) + .field("mode", mode) .field("status", status.label()) .dateValueField("expiry_date_in_millis", "expiry_date", expiryDate) .endObject(); @@ -130,6 +137,7 @@ public class XPackInfoResponse extends ActionResponse { public void writeTo(StreamOutput out) throws IOException { out.writeString(uid); out.writeString(type); + out.writeString(mode); status.writeTo(out); out.writeLong(expiryDate); } diff --git a/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/action/TransportXPackInfoActionTests.java b/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/action/TransportXPackInfoActionTests.java index fc3b5faf986..4d37968f6fe 100644 --- a/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/action/TransportXPackInfoActionTests.java +++ b/elasticsearch/x-pack/src/test/java/org/elasticsearch/xpack/action/TransportXPackInfoActionTests.java @@ -22,6 +22,7 @@ import org.junit.Before; import java.util.EnumSet; import java.util.HashSet; +import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.concurrent.CountDownLatch; @@ -80,6 +81,8 @@ public class TransportXPackInfoActionTests extends ESTestCase { when(license.status()).thenReturn(status); String type = randomAsciiOfLength(10); when(license.type()).thenReturn(type); + License.OperationMode mode = randomFrom(License.OperationMode.values()); + when(license.operationMode()).thenReturn(mode); String uid = randomAsciiOfLength(30); when(license.uid()).thenReturn(uid); when(licensesService.getLicense()).thenReturn(license); @@ -129,6 +132,7 @@ public class TransportXPackInfoActionTests extends ESTestCase { assertThat(response.get().getLicenseInfo().getExpiryDate(), is(expiryDate)); assertThat(response.get().getLicenseInfo().getStatus(), is(status)); assertThat(response.get().getLicenseInfo().getType(), is(type)); + assertThat(response.get().getLicenseInfo().getMode(), is(mode.name().toLowerCase(Locale.ROOT))); assertThat(response.get().getLicenseInfo().getUid(), is(uid)); } else { assertThat(response.get().getLicenseInfo(), nullValue()); diff --git a/elasticsearch/x-pack/src/test/resources/rest-api-spec/test/xpack/15_basic.yaml b/elasticsearch/x-pack/src/test/resources/rest-api-spec/test/xpack/15_basic.yaml index b65872fee18..6aeb3b87872 100644 --- a/elasticsearch/x-pack/src/test/resources/rest-api-spec/test/xpack/15_basic.yaml +++ b/elasticsearch/x-pack/src/test/resources/rest-api-spec/test/xpack/15_basic.yaml @@ -61,7 +61,7 @@ - is_true: license - match: { license.uid: "893361dc-9749-4997-93cb-802e3dofh7aa" } - match: { license.type: "internal" } - - match: { license.mode: "platinum" } + - match: { license.mode: "trial" } - match: { license.status: "active" } - match: { license.expiry_date_in_millis: 1914278399999 } - is_true: features @@ -133,7 +133,7 @@ - is_true: license - match: { license.uid: "893361dc-9749-4997-93cb-802e3dofh7aa" } - match: { license.type: "internal" } - - match: { license.mode: "platinum" } + - match: { license.mode: "trial" } - match: { license.status: "active" } - match: { license.expiry_date_in_millis: 1914278399999 } @@ -147,7 +147,7 @@ - is_true: license - match: { license.uid: "893361dc-9749-4997-93cb-802e3dofh7aa" } - match: { license.type: "internal" } - - match: { license.mode: "platinum" } + - match: { license.mode: "trial" } - match: { license.status: "active" } - match: { license.expiry_date_in_millis: 1914278399999 } - is_true: features diff --git a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/WatcherLicensee.java b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/WatcherLicensee.java index 3c1c6f10a7a..cc0ede339db 100644 --- a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/WatcherLicensee.java +++ b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/xpack/watcher/WatcherLicensee.java @@ -7,7 +7,6 @@ package org.elasticsearch.xpack.watcher; import org.elasticsearch.common.Strings; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.license.core.License; import org.elasticsearch.license.core.License.OperationMode; import org.elasticsearch.license.plugin.core.AbstractLicenseeComponent; import org.elasticsearch.license.plugin.core.LicenseState; @@ -30,17 +29,15 @@ public class WatcherLicensee extends AbstractLicenseeComponent { } @Override - public String[] acknowledgmentMessages(License currentLicense, License newLicense) { - switch (newLicense.operationMode()) { + public String[] acknowledgmentMessages(OperationMode currentMode, OperationMode newMode) { + switch (newMode) { case BASIC: - if (currentLicense != null) { - switch (currentLicense.operationMode()) { - case TRIAL: - case STANDARD: - case GOLD: - case PLATINUM: - return new String[] { "Watcher will be disabled" }; - } + switch (currentMode) { + case TRIAL: + case STANDARD: + case GOLD: + case PLATINUM: + return new String[] { "Watcher will be disabled" }; } break; }