Skip to content

Commit

Permalink
Support IOC and restrict access with module (#94927)
Browse files Browse the repository at this point in the history
This commit provides some IOC controls for Elastic named modules
and is the final PR for some refactoring work.

releated:
#94171
#94347
#94261
#94566
#94646
  • Loading branch information
jakelandis committed Apr 10, 2023
1 parent e8d4211 commit 22e53a9
Show file tree
Hide file tree
Showing 28 changed files with 486 additions and 173 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public MockPluginsService(Settings settings, Environment environment, Collection
}
pluginsLoaded.add(new LoadedPlugin(pluginInfo, plugin, pluginClass.getClassLoader(), ModuleLayer.boot()));
}

loadExtensions(pluginsLoaded);
this.classpathPlugins = List.copyOf(pluginsLoaded);
}

Expand Down
3 changes: 3 additions & 0 deletions x-pack/plugin/core/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

exports org.elasticsearch.index.engine.frozen;
exports org.elasticsearch.license;
exports org.elasticsearch.license.internal to org.elasticsearch.stateless;
exports org.elasticsearch.protocol.xpack.common;
exports org.elasticsearch.protocol.xpack.frozen;
exports org.elasticsearch.protocol.xpack.graph;
Expand Down Expand Up @@ -210,4 +211,6 @@
exports org.elasticsearch.xpack.core.watcher.trigger;
exports org.elasticsearch.xpack.core.watcher.watch;
exports org.elasticsearch.xpack.core.watcher;

opens org.elasticsearch.license.internal; // spi
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,12 @@
import org.elasticsearch.common.logging.LoggerMessageFormat;
import org.elasticsearch.common.scheduler.SchedulerEngine;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.time.DateFormatter;
import org.elasticsearch.core.Nullable;
import org.elasticsearch.core.SuppressForbidden;
import org.elasticsearch.core.TimeValue;
import org.elasticsearch.gateway.GatewayService;
import org.elasticsearch.license.internal.MutableLicenseService;
import org.elasticsearch.license.internal.XPackLicenseStatus;
import org.elasticsearch.protocol.xpack.license.LicensesStatus;
import org.elasticsearch.protocol.xpack.license.PutLicenseResponse;
import org.elasticsearch.threadpool.ThreadPool;
Expand All @@ -43,7 +44,6 @@
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

Expand All @@ -56,7 +56,7 @@
*/
public class ClusterStateLicenseService extends AbstractLifecycleComponent
implements
LicenseService.MutableLicenseService,
MutableLicenseService,
ClusterStateListener,
SchedulerEngine.Listener {
private static final Logger logger = LogManager.getLogger(ClusterStateLicenseService.class);
Expand Down Expand Up @@ -93,8 +93,6 @@ public class ClusterStateLicenseService extends AbstractLifecycleComponent

public static final String LICENSE_JOB = "licenseJob";

public static final DateFormatter DATE_FORMATTER = DateFormatter.forPattern("EEEE, MMMM dd, yyyy");

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:";

Expand Down Expand Up @@ -137,7 +135,7 @@ CharSequence buildExpirationMessage(long expirationMillis, boolean expired) {
License [{}] on [{}].
# If you have a new license, please update it. Otherwise, please reach out to
# your support contact.
#\s""", expiredMsg, DATE_FORMATTER.formatMillis(expirationMillis));
#\s""", expiredMsg, LicenseUtils.DATE_FORMATTER.formatMillis(expirationMillis));
if (expired) {
general = general.toUpperCase(Locale.ROOT);
}
Expand Down Expand Up @@ -464,49 +462,20 @@ public void clusterChanged(ClusterChangedEvent event) {
}
}

protected String getExpiryWarning(long licenseExpiryDate, long currentTime) {
final long diff = licenseExpiryDate - currentTime;
if (LicenseSettings.LICENSE_EXPIRATION_WARNING_PERIOD.getMillis() > diff) {
final long days = TimeUnit.MILLISECONDS.toDays(diff);
final String expiryMessage = (days == 0 && diff > 0)
? "expires today"
: (diff > 0
? String.format(Locale.ROOT, "will expire in [%d] days", days)
: String.format(
Locale.ROOT,
"expired on [%s]",
ClusterStateLicenseService.DATE_FORMATTER.formatMillis(licenseExpiryDate)
));
return "Your license "
+ expiryMessage
+ ". "
+ "Contact your administrator or update your license for continued use of features";
}
return null;
}

private void updateXPackLicenseState(License license) {
long time = clock.millis();
if (license == LicensesMetadata.LICENSE_TOMBSTONE) {
// implies license has been explicitly deleted
xPacklicenseState.update(License.OperationMode.MISSING, false, getExpiryWarning(LicenseUtils.getExpiryDate(license), time));
xPacklicenseState.update(LicenseUtils.getXPackLicenseStatus(license, clock));
return;
}
checkForExpiredLicense(license);
}

private boolean checkForExpiredLicense(License license) {
long time = clock.millis();
if (license != null) {
final boolean active;
if (LicenseUtils.getExpiryDate(license) == LicenseSettings.BASIC_SELF_GENERATED_LICENSE_EXPIRATION_MILLIS) {
active = true;
} else {
active = time >= license.issueDate() && time < LicenseUtils.getExpiryDate(license);
}
xPacklicenseState.update(license.operationMode(), active, getExpiryWarning(LicenseUtils.getExpiryDate(license), time));

if (active) {
XPackLicenseStatus xPackLicenseStatus = LicenseUtils.getXPackLicenseStatus(license, clock);
xPacklicenseState.update(xPackLicenseStatus);
if (xPackLicenseStatus.active()) {
logger.debug("license [{}] - valid", license.uid());
return false;
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@

package org.elasticsearch.license;

import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.common.component.LifecycleComponent;
import org.elasticsearch.protocol.xpack.license.PutLicenseResponse;

/**
* Interface to read the current license. Consumers should generally not need to read the license directly and should instead
Expand All @@ -26,32 +23,4 @@ public interface LicenseService extends LifecycleComponent {
*/
License getLicense();

/**
* Interface to update the current license.
* <b>This interface is not intended to be implemented by alternative implementations and exists for internal use only.</b>
*/
interface MutableLicenseService extends LicenseService, LifecycleComponent {

/**
* Creates or updates the current license as defined by the request.
*/
void registerLicense(PutLicenseRequest request, ActionListener<PutLicenseResponse> listener);

/**
* Removes the current license. Implementations should remove the current license and ensure that attempts to read returns
* {@link LicensesMetadata#LICENSE_TOMBSTONE} if a license was removed. Additionally the {@link XPackLicenseState} must be updated.
*/
void removeLicense(ActionListener<? extends AcknowledgedResponse> listener);

/**
* Installs a basic license.
*/
void startBasicLicense(PostStartBasicRequest request, ActionListener<PostStartBasicResponse> listener);

/**
* Installs a trial license.
*/
void startTrialLicense(PostStartTrialRequest request, ActionListener<PostStartTrialResponse> listener);

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@

package org.elasticsearch.license;

import org.elasticsearch.license.internal.XPackLicenseStatus;

/**
* Marker interface for callbacks that are invoked when the license state changes.
*/
@FunctionalInterface
public interface LicenseStateListener {

/**
* Callback when the license state changes. See {@link XPackLicenseState#update(License.OperationMode, boolean, String)}.
* Callback when the license state changes. See {@link XPackLicenseState#update(XPackLicenseStatus)}.
*/
void licenseStateChanged();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,24 @@
import org.elasticsearch.Version;
import org.elasticsearch.cluster.node.DiscoveryNodes;
import org.elasticsearch.common.hash.MessageDigests;
import org.elasticsearch.common.time.DateFormatter;
import org.elasticsearch.license.License.LicenseType;
import org.elasticsearch.license.internal.XPackLicenseStatus;
import org.elasticsearch.protocol.xpack.license.LicenseStatus;
import org.elasticsearch.rest.RestStatus;

import java.nio.charset.StandardCharsets;
import java.time.Clock;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

public class LicenseUtils {

public static final String EXPIRED_FEATURE_METADATA = "es.license.expired.feature";
public static final DateFormatter DATE_FORMATTER = DateFormatter.forPattern("EEEE, MMMM dd, yyyy");

/**
* Exception to be thrown when a feature action requires a valid license, but license
Expand Down Expand Up @@ -102,6 +109,29 @@ public static LicenseStatus status(License license) {
return LicenseStatus.ACTIVE;
}

/**
* Derive the status from the {@link License} for use with {@link XPackLicenseState}
* @param license The license used to derive the returned {@link XPackLicenseStatus}
* @param clock The clock used for expiry checks. Will typically be Clock.systemUTC();
* @return The status for use with {@link XPackLicenseState}
*/
public static XPackLicenseStatus getXPackLicenseStatus(License license, Clock clock) {
long now = clock.millis();
Objects.requireNonNull(license, "license must not be null");
Objects.requireNonNull(clock, "clock must not be null");
if (license == LicensesMetadata.LICENSE_TOMBSTONE) {
return new XPackLicenseStatus(License.OperationMode.MISSING, false, getExpiryWarning(LicenseUtils.getExpiryDate(license), now));
} else {
final boolean active;
if (LicenseUtils.getExpiryDate(license) == LicenseSettings.BASIC_SELF_GENERATED_LICENSE_EXPIRATION_MILLIS) {
active = true;
} else {
active = now >= license.issueDate() && now < LicenseUtils.getExpiryDate(license);
}
return new XPackLicenseStatus(license.operationMode(), active, getExpiryWarning(LicenseUtils.getExpiryDate(license), now));
}
}

public static Map<String, String[]> getAckMessages(License newLicense, License currentLicense) {
Map<String, String[]> acknowledgeMessages = new HashMap<>();
if (License.isAutoGeneratedLicense(currentLicense.signature()) == false // current license is not auto-generated
Expand All @@ -121,4 +151,21 @@ public static Map<String, String[]> getAckMessages(License newLicense, License c
});
return acknowledgeMessages;
}

public static String getExpiryWarning(long licenseExpiryDate, long currentTime) {
final long diff = licenseExpiryDate - currentTime;
if (LicenseSettings.LICENSE_EXPIRATION_WARNING_PERIOD.getMillis() > diff) {
final long days = TimeUnit.MILLISECONDS.toDays(diff);
final String expiryMessage = (days == 0 && diff > 0)
? "expires today"
: (diff > 0
? String.format(Locale.ROOT, "will expire in [%d] days", days)
: String.format(Locale.ROOT, "expired on [%s]", LicenseUtils.DATE_FORMATTER.formatMillis(licenseExpiryDate)));
return "Your license "
+ expiryMessage
+ ". "
+ "Contact your administrator or update your license for continued use of features";
}
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,21 @@
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.license.internal.MutableLicenseService;
import org.elasticsearch.protocol.xpack.license.DeleteLicenseRequest;
import org.elasticsearch.tasks.Task;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;

public class TransportDeleteLicenseAction extends AcknowledgedTransportMasterNodeAction<DeleteLicenseRequest> {

private final LicenseService.MutableLicenseService licenseService;
private final MutableLicenseService licenseService;

@Inject
public TransportDeleteLicenseAction(
TransportService transportService,
ClusterService clusterService,
LicenseService.MutableLicenseService licenseService,
MutableLicenseService licenseService,
ThreadPool threadPool,
ActionFilters actionFilters,
IndexNameExpressionResolver indexNameExpressionResolver
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,20 @@
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.license.internal.MutableLicenseService;
import org.elasticsearch.tasks.Task;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;

public class TransportPostStartBasicAction extends TransportMasterNodeAction<PostStartBasicRequest, PostStartBasicResponse> {

private final LicenseService.MutableLicenseService licenseService;
private final MutableLicenseService licenseService;

@Inject
public TransportPostStartBasicAction(
TransportService transportService,
ClusterService clusterService,
LicenseService.MutableLicenseService licenseService,
MutableLicenseService licenseService,
ThreadPool threadPool,
ActionFilters actionFilters,
IndexNameExpressionResolver indexNameExpressionResolver
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,20 @@
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.license.internal.MutableLicenseService;
import org.elasticsearch.tasks.Task;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;

public class TransportPostStartTrialAction extends TransportMasterNodeAction<PostStartTrialRequest, PostStartTrialResponse> {

private final LicenseService.MutableLicenseService licenseService;
private final MutableLicenseService licenseService;

@Inject
public TransportPostStartTrialAction(
TransportService transportService,
ClusterService clusterService,
LicenseService.MutableLicenseService licenseService,
MutableLicenseService licenseService,
ThreadPool threadPool,
ActionFilters actionFilters,
IndexNameExpressionResolver indexNameExpressionResolver
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,21 @@
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.license.internal.MutableLicenseService;
import org.elasticsearch.protocol.xpack.license.PutLicenseResponse;
import org.elasticsearch.tasks.Task;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;

public class TransportPutLicenseAction extends TransportMasterNodeAction<PutLicenseRequest, PutLicenseResponse> {

private final LicenseService.MutableLicenseService licenseService;
private final MutableLicenseService licenseService;

@Inject
public TransportPutLicenseAction(
TransportService transportService,
ClusterService clusterService,
LicenseService.MutableLicenseService licenseService,
MutableLicenseService licenseService,
ThreadPool threadPool,
ActionFilters actionFilters,
IndexNameExpressionResolver indexNameExpressionResolver
Expand Down

0 comments on commit 22e53a9

Please sign in to comment.