-
Notifications
You must be signed in to change notification settings - Fork 25.6k
Add license feature usage api #59342
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
This commit adds a new api to track when gold+ features are used within x-pack. The tracking is done internally whenever a feature is checked against the current license. The output of the api is a list of each used feature, which includes the name, license level, and last time it was used. In addition to a unit test for the tracking, a rest test is added which ensures starting up a default configured node does not result in any features registering as used. There are a couple features which currently do not work well with the tracking, as they are checked in a manner that makes them look always used. Those features will be fixed in followups, and in this PR they are omitted from the feature usage output.
|
Pinging @elastic/es-security (:Security/License) |
| boolean shouldIntercept = licenseState.isSecurityEnabled() && licenseState.checkFeature(Feature.SECURITY_DLS_FLS); | ||
| boolean shouldIntercept = licenseState.isSecurityEnabled(); | ||
| var licenseChecker = new MemoizedSupplier<>(() -> licenseState.checkFeature(Feature.SECURITY_DLS_FLS)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The existing expression of licenseState.isSecurityEnabled() && licenseState.checkFeature(Feature.SECURITY_DLS_FLS) has a potential racing issue between the two license checks. I noticed we have been somewhat inconsistent in handling this. In some places, e.g. ResizeRequestInterceptor, we use licenseState.copyCurrentLicenseState() to ensure checks are always done against the same license. In other places including this one, no special handling is done. The new changes also retain this behaviour. My question is: what is our recommend approach for this subtle issue? I am OK with either approach as long as it is consistently applied.
I am also curious about the benefit of the new MemoizedSupplier. Why do we need to check the license deeper in the loop? Isn't it sufficient and more efficient to check in the outermost if statement?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is our recommend approach for this subtle issue?
It is true there is a race condition, but in practice I don't think we need to worry about it. This is an extreme edge case, where the license changes mid request. AFAIU, the copy method was added to handle the race condition in tests, not for any problem found by users. That said, the long term solution to this is to change all of our checks to use a stashed copy in the ThreadContext that would be inserted when the request is first received. That idea is described in #53909.
I am also curious about the benefit of the new MemoizedSupplier. Why do we need to check the license deeper in the loop?
We don't know if DLS/FLS is actually used until we resolve the each indices' access controls inside the loop. Thus we need to check when resolving each index. It is true this is similar to the problem described above, and could be partially solved by copying the license state. However, it also means needlessly updating the last used time in quick succession. I could go either way, but thought reducing those updates was worthwhile given the simpleness of implementing the MemoizedSupplier.
| boolean dls = indexAccessControl.getDocumentPermissions().hasDocumentLevelPermissions(); | ||
| if (fls || dls) { | ||
| if (bulkItemRequest.request() instanceof UpdateRequest) { | ||
| if (licenseChecker.get() && bulkItemRequest.request() instanceof UpdateRequest) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I see, the license checking is moved here because we need to accurately tracking the usage, i.e. only check it when it is absolutely necessary. In this case, I wonder whether we should check it after the instanceof, i.e. bulkItemRequest.request() instanceof UpdateRequest && licenseChecker.get().
|
|
||
| @Override | ||
| protected void doExecute(Task task, GetFeatureUsageRequest request, ActionListener<GetFeatureUsageResponse> listener) { | ||
| Map<XPackLicenseState.Feature, Long> featureUsage = licenseState.getLastUsed(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIUC, the feature usage is reported for the local node only? So in cloud with coordinating node setup, how can user get the feature usages for nodes other than the coordinating ones?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cloud will need to hit every node in the cluster, not just coordinating nodes. AFAIU they can do this (this would be on their backend infrastructure side, and so would not be going through eg load balancers). It would be similar to eg health checks that hit each node.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok so this API is more geared towards Cloud internal management instead of end-users. Then it is fine.
|
@elasticmachine run elasticsearch-ci/2 |
| public XPackLicenseState copyCurrentLicenseState() { | ||
| return executeAgainstStatus(status -> | ||
| new XPackLicenseState(listeners, isSecurityEnabled, isSecurityExplicitlyEnabled, status)); | ||
| new XPackLicenseState(listeners, isSecurityEnabled, isSecurityExplicitlyEnabled, status, lastUsed, epochMillisProvider)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This worries me a bit since a copy can now mutate the original object. It is not an issue for the time being since no such usage exists. But might be a confusing behaviour in future?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's exactly the intention. The copy is meant for read purposes, but mutation purposes must be unified, otherwise we would would not be able to check what checks have been done across copies.
| final XPackLicenseState frozenLicenseState = licenseState.copyCurrentLicenseState(); | ||
| final AuditTrail auditTrail = auditTrailService.get(); | ||
| if (frozenLicenseState.isSecurityEnabled()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are using both frozenLicenseState here and the original licenseState with MemoizedSupplier below. Given the current change, there is no benefit of having frozenLicenseState anymore. It can probably just be removed.
| ActionListener<Void> listener) { | ||
| if (requestInfo.getRequest() instanceof ResizeRequest) { | ||
| final ResizeRequest request = (ResizeRequest) requestInfo.getRequest(); | ||
| final XPackLicenseState frozenLicenseState = licenseState.copyCurrentLicenseState(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here for using frozenLicenseState and licenseState together.
| @Override | ||
| public boolean available() { | ||
| return licenseState != null && licenseState.isAllowed(Feature.VOTING_ONLY); | ||
| return licenseState != null && licenseState.checkFeature(Feature.VOTING_ONLY); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method does not seem to be used anywhere? But the inner class VotingOnlyNodeFeatureSet#UsageInfoAction also has a availabe() method which internally uses licenseState.checkFeature. But all other XPackInfoFeatureTransportAction uses licenseState.isAllowed. Is this inconsistency intended?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've fixed the checkFeature usage, it was incorrect. I'll address the unused class in a followup.
ywangd
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM.
I left a few comments which would be great if they could be addressed. But none of them is critical enough to delay the approval.
This commit adds a new api to track when gold+ features are used within x-pack. The tracking is done internally whenever a feature is checked against the current license. The output of the api is a list of each used feature, which includes the name, license level, and last time it was used. In addition to a unit test for the tracking, a rest test is added which ensures starting up a default configured node does not result in any features registering as used. There are a couple features which currently do not work well with the tracking, as they are checked in a manner that makes them look always used. Those features will be fixed in followups, and in this PR they are omitted from the feature usage output.
This commit adds a new api to track when gold+ features are used within x-pack. The tracking is done internally whenever a feature is checked against the current license. The output of the api is a list of each used feature, which includes the name, license level, and last time it was used. In addition to a unit test for the tracking, a rest test is added which ensures starting up a default configured node does not result in any features registering as used. There are a couple features which currently do not work well with the tracking, as they are checked in a manner that makes them look always used. Those features will be fixed in followups, and in this PR they are omitted from the feature usage output.
This commit adds a new api to track when gold+ features are used within
x-pack. The tracking is done internally whenever a feature is checked
against the current license. The output of the api is a list of each
used feature, which includes the name, license level, and last time it
was used. In addition to a unit test for the tracking, a rest test is
added which ensures starting up a default configured node does not
result in any features registering as used.
There are a couple features which currently do not work well with the
tracking, as they are checked in a manner that makes them look always
used. Those features will be fixed in followups, and in this PR they are
omitted from the feature usage output.