Skip to content

Commit fded154

Browse files
committed
Convert remote license checker to use LicensedFeature (elastic#79876)
The RemoteClusterLicenseChecker pulls the license level of a remote cluster and checks it allows a local feature to communicate with that cluster. It does the check with a lambda, but these methods could be out of sync with the actual licensed feature. This commit converts the remote license checker to take in the feature object that should be checked.
1 parent e6d4fad commit fded154

File tree

8 files changed

+65
-112
lines changed

8 files changed

+65
-112
lines changed

x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/CcrLicenseChecker.java

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
import org.elasticsearch.index.shard.ShardId;
3939
import org.elasticsearch.indices.IndexClosedException;
4040
import org.elasticsearch.license.RemoteClusterLicenseChecker;
41-
import org.elasticsearch.license.XPackLicenseState;
4241
import org.elasticsearch.rest.RestStatus;
4342
import org.elasticsearch.xpack.ccr.action.ShardChangesAction;
4443
import org.elasticsearch.xpack.core.ClientHelper;
@@ -242,7 +241,7 @@ private void checkRemoteClusterLicenseAndFetchClusterState(
242241
final Function<Exception, ElasticsearchStatusException> unknownLicense
243242
) {
244243
// we have to check the license on the remote cluster
245-
new RemoteClusterLicenseChecker(client, XPackLicenseState::isCcrAllowedForOperationMode).checkRemoteClusterLicenses(
244+
new RemoteClusterLicenseChecker(client, CcrConstants.CCR_FEATURE).checkRemoteClusterLicenses(
246245
Collections.singletonList(clusterAlias),
247246
new ActionListener<RemoteClusterLicenseChecker.LicenseCheck>() {
248247

@@ -450,11 +449,7 @@ private static ElasticsearchStatusException indexMetadataNonCompliantRemoteLicen
450449
clusterAlias,
451450
leaderIndex,
452451
clusterAlias,
453-
RemoteClusterLicenseChecker.buildErrorMessage(
454-
"ccr",
455-
licenseCheck.remoteClusterLicenseInfo(),
456-
RemoteClusterLicenseChecker::isAllowedByLicense
457-
)
452+
RemoteClusterLicenseChecker.buildErrorMessage(CcrConstants.CCR_FEATURE, licenseCheck.remoteClusterLicenseInfo())
458453
);
459454
return new ElasticsearchStatusException(message, RestStatus.BAD_REQUEST);
460455
}
@@ -467,11 +462,7 @@ private static ElasticsearchStatusException clusterStateNonCompliantRemoteLicens
467462
Locale.ROOT,
468463
"can not fetch remote cluster state as the remote cluster [%s] is not licensed for [ccr]; %s",
469464
clusterAlias,
470-
RemoteClusterLicenseChecker.buildErrorMessage(
471-
"ccr",
472-
licenseCheck.remoteClusterLicenseInfo(),
473-
RemoteClusterLicenseChecker::isAllowedByLicense
474-
)
465+
RemoteClusterLicenseChecker.buildErrorMessage(CcrConstants.CCR_FEATURE, licenseCheck.remoteClusterLicenseInfo())
475466
);
476467
return new ElasticsearchStatusException(message, RestStatus.BAD_REQUEST);
477468
}

x-pack/plugin/core/src/main/java/org/elasticsearch/license/RemoteClusterLicenseChecker.java

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import org.elasticsearch.client.Client;
1515
import org.elasticsearch.cluster.metadata.ClusterNameExpressionResolver;
1616
import org.elasticsearch.common.util.concurrent.ThreadContext;
17+
import org.elasticsearch.core.Nullable;
1718
import org.elasticsearch.protocol.xpack.XPackInfoRequest;
1819
import org.elasticsearch.protocol.xpack.XPackInfoResponse;
1920
import org.elasticsearch.protocol.xpack.license.LicenseStatus;
@@ -27,11 +28,12 @@
2728
import java.util.Locale;
2829
import java.util.Set;
2930
import java.util.concurrent.atomic.AtomicReference;
30-
import java.util.function.Predicate;
3131
import java.util.stream.Collectors;
3232

33+
import static org.elasticsearch.license.XPackLicenseState.isAllowedByOperationMode;
34+
3335
/**
34-
* Checks remote clusters for license compatibility with a specified license predicate.
36+
* Checks remote clusters for license compatibility with a specified licensed feature.
3537
*/
3638
public final class RemoteClusterLicenseChecker {
3739

@@ -125,23 +127,19 @@ private LicenseCheck(final RemoteClusterLicenseInfo remoteClusterLicenseInfo) {
125127

126128
private static final ClusterNameExpressionResolver clusterNameExpressionResolver = new ClusterNameExpressionResolver();
127129
private final Client client;
128-
private final Predicate<License.OperationMode> predicate;
130+
private final LicensedFeature feature;
129131

130132
/**
131-
* Constructs a remote cluster license checker with the specified license predicate for checking license compatibility. The predicate
132-
* does not need to check for the active license state as this is handled by the remote cluster license checker.
133+
* Constructs a remote cluster license checker with the specified licensed feature for checking license compatibility. The feature
134+
* does not need to check for the active license state as this is handled by the remote cluster license checker. If the feature
135+
* is {@code null} a check is only performed on whether the license is active.
133136
*
134137
* @param client the client
135-
* @param predicate the license predicate
138+
* @param feature the licensed feature
136139
*/
137-
public RemoteClusterLicenseChecker(final Client client, final Predicate<License.OperationMode> predicate) {
140+
public RemoteClusterLicenseChecker(final Client client, @Nullable final LicensedFeature feature) {
138141
this.client = client;
139-
this.predicate = predicate;
140-
}
141-
142-
public static boolean isAllowedByLicense(final XPackInfoResponse.LicenseInfo licenseInfo) {
143-
final License.OperationMode mode = License.OperationMode.parse(licenseInfo.getMode());
144-
return XPackLicenseState.isAllowedByOperationMode(mode, License.OperationMode.PLATINUM);
142+
this.feature = feature;
145143
}
146144

147145
/**
@@ -169,8 +167,8 @@ public void onResponse(final XPackInfoResponse xPackInfoResponse) {
169167
listener.onFailure(new ResourceNotFoundException("license info is missing for cluster [" + clusterAlias.get() + "]"));
170168
return;
171169
}
172-
if ((licenseInfo.getStatus() == LicenseStatus.ACTIVE) == false
173-
|| predicate.test(License.OperationMode.parse(licenseInfo.getMode())) == false) {
170+
171+
if (isActive(feature, licenseInfo) == false || isAllowed(feature, licenseInfo) == false) {
174172
listener.onResponse(LicenseCheck.failure(new RemoteClusterLicenseInfo(clusterAlias.get(), licenseInfo)));
175173
return;
176174
}
@@ -197,6 +195,15 @@ public void onFailure(final Exception e) {
197195
remoteClusterLicense(clusterAlias.get(), infoListener);
198196
}
199197

198+
private static boolean isActive(LicensedFeature feature, XPackInfoResponse.LicenseInfo licenseInfo) {
199+
return feature != null && feature.isNeedsActive() == false || licenseInfo.getStatus() == LicenseStatus.ACTIVE;
200+
}
201+
202+
private static boolean isAllowed(LicensedFeature feature, XPackInfoResponse.LicenseInfo licenseInfo) {
203+
License.OperationMode mode = License.OperationMode.parse(licenseInfo.getMode());
204+
return feature == null || isAllowedByOperationMode(mode, feature.getMinimumOperationMode());
205+
}
206+
200207
private void remoteClusterLicense(final String clusterAlias, final ActionListener<XPackInfoResponse> listener) {
201208
final ThreadContext threadContext = client.threadPool().getThreadContext();
202209
final ContextPreservingActionListener<XPackInfoResponse> contextPreservingActionListener = new ContextPreservingActionListener<>(
@@ -274,22 +281,19 @@ public static List<String> remoteClusterAliases(final Set<String> remoteClusters
274281
* @param remoteClusterLicenseInfo the remote cluster license info of the cluster that failed the license check
275282
* @return an error message representing license incompatibility
276283
*/
277-
public static String buildErrorMessage(
278-
final String feature,
279-
final RemoteClusterLicenseInfo remoteClusterLicenseInfo,
280-
final Predicate<XPackInfoResponse.LicenseInfo> predicate
281-
) {
284+
public static String buildErrorMessage(final LicensedFeature feature, final RemoteClusterLicenseInfo remoteClusterLicenseInfo) {
282285
final StringBuilder error = new StringBuilder();
283-
if (remoteClusterLicenseInfo.licenseInfo().getStatus() != LicenseStatus.ACTIVE) {
286+
if (isActive(feature, remoteClusterLicenseInfo.licenseInfo()) == false) {
284287
error.append(String.format(Locale.ROOT, "the license on cluster [%s] is not active", remoteClusterLicenseInfo.clusterAlias()));
285288
} else {
286-
assert predicate.test(remoteClusterLicenseInfo.licenseInfo()) == false : "license must be incompatible to build error message";
289+
assert isAllowed(feature, remoteClusterLicenseInfo.licenseInfo()) == false
290+
: "license must be incompatible to build error message";
287291
final String message = String.format(
288292
Locale.ROOT,
289293
"the license mode [%s] on cluster [%s] does not enable [%s]",
290294
License.OperationMode.parse(remoteClusterLicenseInfo.licenseInfo().getMode()),
291295
remoteClusterLicenseInfo.clusterAlias(),
292-
feature
296+
feature.getName()
293297
);
294298
error.append(message);
295299
}

x-pack/plugin/core/src/main/java/org/elasticsearch/license/XPackLicenseState.java

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -440,19 +440,11 @@ public Map<FeatureUsage, Long> getLastUsed() {
440440
return usage.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> timeConverter.apply(e.getValue())));
441441
}
442442

443-
public static boolean isMachineLearningAllowedForOperationMode(final OperationMode operationMode) {
444-
return isAllowedByOperationMode(operationMode, OperationMode.PLATINUM);
445-
}
446-
447443
public static boolean isFipsAllowedForOperationMode(final OperationMode operationMode) {
448444
return isAllowedByOperationMode(operationMode, OperationMode.PLATINUM);
449445
}
450446

451-
public static boolean isCcrAllowedForOperationMode(final OperationMode operationMode) {
452-
return isAllowedByOperationMode(operationMode, OperationMode.PLATINUM);
453-
}
454-
455-
public static boolean isAllowedByOperationMode(final OperationMode operationMode, final OperationMode minimumMode) {
447+
static boolean isAllowedByOperationMode(final OperationMode operationMode, final OperationMode minimumMode) {
456448
if (OperationMode.TRIAL == operationMode) {
457449
return true;
458450
}

x-pack/plugin/core/src/test/java/org/elasticsearch/license/RemoteClusterLicenseCheckerTests.java

Lines changed: 19 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -157,10 +157,8 @@ public void testCheckRemoteClusterLicensesGivenCompatibleLicenses() {
157157
responses.add(new XPackInfoResponse(null, createPlatinumLicenseResponse(), null));
158158
responses.add(new XPackInfoResponse(null, createPlatinumLicenseResponse(), null));
159159

160-
final RemoteClusterLicenseChecker licenseChecker = new RemoteClusterLicenseChecker(
161-
client,
162-
operationMode -> XPackLicenseState.isAllowedByOperationMode(operationMode, License.OperationMode.PLATINUM)
163-
);
160+
LicensedFeature.Momentary feature = LicensedFeature.momentary(null, "feature", License.OperationMode.PLATINUM);
161+
final RemoteClusterLicenseChecker licenseChecker = new RemoteClusterLicenseChecker(client, feature);
164162
final AtomicReference<RemoteClusterLicenseChecker.LicenseCheck> licenseCheck = new AtomicReference<>();
165163

166164
licenseChecker.checkRemoteClusterLicenses(
@@ -202,10 +200,8 @@ public void testCheckRemoteClusterLicensesGivenIncompatibleLicense() {
202200
return null;
203201
}).when(client).execute(same(XPackInfoAction.INSTANCE), any(), any());
204202

205-
final RemoteClusterLicenseChecker licenseChecker = new RemoteClusterLicenseChecker(
206-
client,
207-
operationMode -> XPackLicenseState.isAllowedByOperationMode(operationMode, License.OperationMode.PLATINUM)
208-
);
203+
LicensedFeature.Momentary feature = LicensedFeature.momentary(null, "feature", License.OperationMode.PLATINUM);
204+
final RemoteClusterLicenseChecker licenseChecker = new RemoteClusterLicenseChecker(client, feature);
209205
final AtomicReference<RemoteClusterLicenseChecker.LicenseCheck> licenseCheck = new AtomicReference<>();
210206

211207
licenseChecker.checkRemoteClusterLicenses(
@@ -251,10 +247,8 @@ public void testCheckRemoteClusterLicencesGivenNonExistentCluster() {
251247
responses.add(new XPackInfoResponse(null, createPlatinumLicenseResponse(), null));
252248
responses.add(new XPackInfoResponse(null, createPlatinumLicenseResponse(), null));
253249

254-
final RemoteClusterLicenseChecker licenseChecker = new RemoteClusterLicenseChecker(
255-
client,
256-
operationMode -> XPackLicenseState.isAllowedByOperationMode(operationMode, License.OperationMode.PLATINUM)
257-
);
250+
LicensedFeature.Momentary feature = LicensedFeature.momentary(null, "feature", License.OperationMode.PLATINUM);
251+
final RemoteClusterLicenseChecker licenseChecker = new RemoteClusterLicenseChecker(client, feature);
258252
final AtomicReference<Exception> exception = new AtomicReference<>();
259253

260254
licenseChecker.checkRemoteClusterLicenses(
@@ -294,10 +288,8 @@ public void testRemoteClusterLicenseCallUsesSystemContext() throws InterruptedEx
294288
return null;
295289
}).when(client).execute(same(XPackInfoAction.INSTANCE), any(), any());
296290

297-
final RemoteClusterLicenseChecker licenseChecker = new RemoteClusterLicenseChecker(
298-
client,
299-
operationMode -> XPackLicenseState.isAllowedByOperationMode(operationMode, License.OperationMode.PLATINUM)
300-
);
291+
LicensedFeature.Momentary feature = LicensedFeature.momentary(null, "feature", License.OperationMode.PLATINUM);
292+
final RemoteClusterLicenseChecker licenseChecker = new RemoteClusterLicenseChecker(client, feature);
301293

302294
final List<String> remoteClusterAliases = Collections.singletonList("valid");
303295
licenseChecker.checkRemoteClusterLicenses(
@@ -337,10 +329,8 @@ public void testListenerIsExecutedWithCallingContext() throws InterruptedExcepti
337329
responses.add(new XPackInfoResponse(null, createPlatinumLicenseResponse(), null));
338330
responses.add(new XPackInfoResponse(null, createPlatinumLicenseResponse(), null));
339331

340-
final RemoteClusterLicenseChecker licenseChecker = new RemoteClusterLicenseChecker(
341-
client,
342-
operationMode -> XPackLicenseState.isAllowedByOperationMode(operationMode, License.OperationMode.PLATINUM)
343-
);
332+
LicensedFeature.Momentary feature = LicensedFeature.momentary(null, "feature", License.OperationMode.PLATINUM);
333+
final RemoteClusterLicenseChecker licenseChecker = new RemoteClusterLicenseChecker(client, feature);
344334

345335
final AtomicBoolean listenerInvoked = new AtomicBoolean();
346336
threadPool.getThreadContext().putHeader("key", "value");
@@ -383,10 +373,8 @@ public void testBuildErrorMessageForActiveCompatibleLicense() {
383373
"platinum-cluster",
384374
platinumLicence
385375
);
386-
final AssertionError e = expectThrows(
387-
AssertionError.class,
388-
() -> RemoteClusterLicenseChecker.buildErrorMessage("", info, RemoteClusterLicenseChecker::isAllowedByLicense)
389-
);
376+
LicensedFeature.Momentary feature = LicensedFeature.momentary(null, "foo", License.OperationMode.PLATINUM);
377+
final AssertionError e = expectThrows(AssertionError.class, () -> RemoteClusterLicenseChecker.buildErrorMessage(feature, info));
390378
assertThat(e, hasToString(containsString("license must be incompatible to build error message")));
391379
}
392380

@@ -396,9 +384,10 @@ public void testBuildErrorMessageForIncompatibleLicense() {
396384
"basic-cluster",
397385
basicLicense
398386
);
387+
LicensedFeature.Momentary feature = LicensedFeature.momentary(null, "feature", License.OperationMode.PLATINUM);
399388
assertThat(
400-
RemoteClusterLicenseChecker.buildErrorMessage("Feature", info, RemoteClusterLicenseChecker::isAllowedByLicense),
401-
equalTo("the license mode [BASIC] on cluster [basic-cluster] does not enable [Feature]")
389+
RemoteClusterLicenseChecker.buildErrorMessage(feature, info),
390+
equalTo("the license mode [BASIC] on cluster [basic-cluster] does not enable [feature]")
402391
);
403392
}
404393

@@ -408,8 +397,9 @@ public void testBuildErrorMessageForInactiveLicense() {
408397
"expired-cluster",
409398
expiredLicense
410399
);
400+
LicensedFeature.Momentary feature = LicensedFeature.momentary(null, "foo", License.OperationMode.PLATINUM);
411401
assertThat(
412-
RemoteClusterLicenseChecker.buildErrorMessage("Feature", info, RemoteClusterLicenseChecker::isAllowedByLicense),
402+
RemoteClusterLicenseChecker.buildErrorMessage(feature, info),
413403
equalTo("the license on cluster [expired-cluster] is not active")
414404
);
415405
}
@@ -424,10 +414,8 @@ public void testCheckRemoteClusterLicencesNoLicenseMetadata() {
424414
return null;
425415
}).when(client).execute(same(XPackInfoAction.INSTANCE), any(), any());
426416

427-
final RemoteClusterLicenseChecker licenseChecker = new RemoteClusterLicenseChecker(
428-
client,
429-
operationMode -> XPackLicenseState.isAllowedByOperationMode(operationMode, License.OperationMode.PLATINUM)
430-
);
417+
LicensedFeature.Momentary feature = LicensedFeature.momentary(null, "feature", License.OperationMode.PLATINUM);
418+
final RemoteClusterLicenseChecker licenseChecker = new RemoteClusterLicenseChecker(client, feature);
431419
final AtomicReference<Exception> exception = new AtomicReference<>();
432420

433421
licenseChecker.checkRemoteClusterLicenses(

0 commit comments

Comments
 (0)