Implements cloud_internal license type
"cloud_internal" license type enables dynamically updating license operation mode via a config file. When the installed license is "cloud_internal", the node level operation mode can be updated by writing a `license_mode` file in the x-pack config directory (config/x-pack/license_mode). The file is expected to have a string representing the desired license mode (e.g. "gold", "basic"). In case of a failure to read a valid license mode from the `license_mode` file, the operation mode for "cloud_internal" license defaults to PLATINUM. This change also ensures that the correct operation mode is reported via the _xpack endpoint. closes elastic/elasticsearch#2042 Original commit: elastic/x-pack-elasticsearch@6a2d788e45
This commit is contained in:
parent
4e81ef42a0
commit
0db0e2f0c9
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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 <code>licenseModePath</code>, use
|
||||
* {@link #getCurrentOperationMode()} to access the latest mode
|
||||
*
|
||||
* In case of failure to read a valid operation mode from <code>licenseModePath</code>,
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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<Object> createComponents(ClusterService clusterService, Clock clock,
|
||||
public Collection<Object> 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);
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 <code>newLicense</code>
|
||||
* when <code>currentLicense</code> 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) {
|
||||
|
|
|
@ -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<Licensee> registeredLicensees) {
|
||||
public LicensesService(Settings settings, ClusterService clusterService, Clock clock, Environment env,
|
||||
ResourceWatcherService resourceWatcherService, List<Licensee> 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 <code>currentLicensesMetaData</code>.
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<ClusterBlock> 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<Licensee.Status> statuses = new CopyOnWriteArrayList<>();
|
||||
public final AtomicInteger expirationMessagesCalled = new AtomicInteger(0);
|
||||
public final List<Tuple<License, License>> acknowledgementRequested = new CopyOnWriteArrayList<>();
|
||||
public final List<Tuple<License.OperationMode, License.OperationMode>> 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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"));
|
||||
}
|
||||
|
||||
@After
|
||||
public void after() {
|
||||
licensesService.stop();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
|
|
|
@ -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.<String, String[]>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.<String, String[]>emptyMap()));
|
||||
verify(clusterService, times(1)).submitStateUpdateTask(any(String.class), any(ClusterStateUpdateTask.class));
|
||||
licensesService.stop();
|
||||
}
|
||||
|
||||
private static class AssertingLicensesUpdateResponse implements ActionListener<PutLicenseResponse> {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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<Object> createComponents(ClusterService clusterService, Clock clock,
|
||||
public Collection<Object> 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<Licensee> licensees) {
|
||||
super(settings, null, null, licensees);
|
||||
public LicenseServiceForCollectors(Settings settings, Environment env,
|
||||
ResourceWatcherService resourceWatcherService, List<Licensee> 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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<Object> createComponents(ClusterService clusterService, Clock clock,
|
||||
public Collection<Object> 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<Licensee> licensees;
|
||||
|
||||
@Inject
|
||||
public MockLicenseService(Settings settings, List<Licensee> licensees) {
|
||||
super(settings, null, null, licensees);
|
||||
public MockLicenseService(Settings settings, Environment environment,
|
||||
ResourceWatcherService resourceWatcherService, List<Licensee> 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<Object> createComponents(ClusterService clusterService, Clock clock,
|
||||
public Collection<Object> 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<Licensee> licensees;
|
||||
|
||||
public TestLicensesService(Settings settings, List<Licensee> licensees) {
|
||||
super(settings, null, null, Collections.emptyList());
|
||||
public TestLicensesService(Settings settings, Environment env, ResourceWatcherService resourceWatcherService,
|
||||
List<Licensee> licensees) {
|
||||
super(settings, null, null, env, resourceWatcherService, Collections.emptyList());
|
||||
this.licensees = licensees;
|
||||
enable(OperationMode.BASIC);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue