Extract License service from CCR and Watcher into license_api_guard plugin in x-pack #95973
Conversation
|
Pinging @elastic/es-ui (Team:Elasticsearch UI) |
alisonelizabeth
left a comment
There was a problem hiding this comment.
Thanks for working on this @cjcenizal! I’m happy with this approach and also verified the changes locally.
Next steps: If reviewers approve of this direction, I'll extract this approach into a reusable service and then apply it to similar code in Watcher.
👍 I can re-review once this is complete. Can this service be used more broadly across our other plugins as well (perhaps addressed in a separate PR)?
There was a problem hiding this comment.
not directly related to this PR - but I noticed while looking at the code in my IDE that LicensingPluginSetup.license$ is deprecated, and we should be using license$ provided by the start contract.
361d501 to
4ba7a29
Compare
5e573a1 to
87c0a21
Compare
| RequestHandlerContext, | ||
| } from 'src/core/server'; | ||
|
|
||
| /* eslint-disable @kbn/eslint/no-restricted-paths */ |
There was a problem hiding this comment.
@elastic/kibana-core Do we still need this linting rule to disallow importing x-pack modules into non-x-pack?
There was a problem hiding this comment.
For concrete imports, we definitely do, as it would plainly breaks on OSS distrib.
For types, it's not a 'technical' requirement, but until the basic plugins are moved out of xpack, we should stick with that rule.
There was a problem hiding this comment.
AFAIK we don't release OSS distributable starting from v7.11 https://www.elastic.co/downloads/past-releases#kibana-oss
But I'd rather plugins didn't import from x-pack. Note that the code in src/ and x-pack have different licenses, so it's easy to leak commercial code outside by importing runtime code accidentally.
Regarding the current use case: why licensing code lives in src/ at all? Licensing compatible types can be declared in the current file, but maybe an x-pack plugin is a better place?
937ed8a to
3c6acd7
Compare
3c6acd7 to
7c34572
Compare
| start({ pluginId, minimumLicenseType, licensing }: StartSettings) { | ||
| licensing.license$.subscribe((license) => { | ||
| this.licenseType = license.type; | ||
| this.licenseCheckState = license.check(pluginId, minimumLicenseType!).state; |
There was a problem hiding this comment.
To testers: you can hardcode a value here, e.g. 'invalid', to test the behavior under various conditions.
|
@alisonelizabeth @jloleysens This is ready for your 👀 ! |
pgayvallet
left a comment
There was a problem hiding this comment.
I know this may change soon, but I'm not at ease with the fact a xpack-dependent utility class importing types from licensing is exposed from an OSS plugin.
@mshustov wdyt?
| RequestHandlerContext, | ||
| } from 'src/core/server'; | ||
|
|
||
| /* eslint-disable @kbn/eslint/no-restricted-paths */ |
There was a problem hiding this comment.
For concrete imports, we definitely do, as it would plainly breaks on OSS distrib.
For types, it's not a 'technical' requirement, but until the basic plugins are moved out of xpack, we should stick with that rule.
|
@alisonelizabeth @jloleysens @pgayvallet @mshustov Thanks for your reviews! I've addressed your feedback. I agree that we should sequester @alisonelizabeth To your question about using this in our other plugins -- I just checked and I believe all of our other plugins are licensed under Basic, so there's no need to guard their API routes this way. Reviewers, would you mind taking another look at sharing your thoughts? Thanks! |
| import { of } from 'rxjs'; | ||
| import type { KibanaRequest, RequestHandlerContext } from 'src/core/server'; | ||
| // eslint-disable-next-line @kbn/eslint/no-restricted-paths | ||
| import { httpServerMock } from 'src/core/server/http/http_server.mocks'; |
There was a problem hiding this comment.
@mshustov Do you think we could update src/core/server to export these helpers publicly?
There was a problem hiding this comment.
They are available from src/core/server/mocks
There was a problem hiding this comment.
LGTM!
At some point in the future I think we could discuss either moving it into the licensing plugin if we think non-ESUI plugins would benefit from it, or into an x-pack-specific version of es_ui_shared if not.
We are planning to add something similar #56926
| } | ||
|
|
||
| start({ pluginId, minimumLicenseType, licensing }: StartSettings) { | ||
| licensing.license$.subscribe((license: ILicense) => { |
There was a problem hiding this comment.
shouldn't we unsubscribe on the stop lifecycle?
There was a problem hiding this comment.
@mshustov I was under the impression that stop is never called in server-side plugins -- plugins just "live" until the Kibana server process is killed. Is that not the case?
There was a problem hiding this comment.
plugins just "live" until the Kibana server process is killed.
@cjcenizal Nope. It's always called during the normal teardown process (unless Kibana crashes).
There was a problem hiding this comment.
When does teardown occur? How can I trigger it?
| const licenseErrorMessage = this.getLicenseErrorMessage(this.licenseCheckState); | ||
| this.logger?.warn(licenseErrorMessage); | ||
|
|
||
| return response.customError({ |
There was a problem hiding this comment.
why not response.forbidden?
return response.forbidden({
body: {
message: licenseErrorMessage,
},
});
alisonelizabeth
left a comment
There was a problem hiding this comment.
Changes LGTM. Thanks @cjcenizal!
@alisonelizabeth To your question about using this in our other plugins -- I just checked and I believe all of our other plugins are licensed under Basic, so there's no need to guard their API routes this way.
I think most - if not all - of our plugins have a similar license check (for example, Ingest Node Pipelines, Index Management and ILM). Can we remove this guard now?
| }, | ||
| ].forEach(({ licenseState, expectedMessage }) => { | ||
| describe(`${licenseState} license`, () => { | ||
| it('replies with 403 and message and logs the message', () => { |
There was a problem hiding this comment.
| it('replies with 403 and message and logs the message', () => { | |
| it('replies with 403 status and logs the message', () => { |
jloleysens
left a comment
There was a problem hiding this comment.
Thanks for addressing that feedback @cjcenizal. Happy with the current changes!
Co-authored-by: Alison Goryachev <alisonmllr20@gmail.com>
|
@alisonelizabeth Great call, I created #97314 to track this work. @mshustov I'm going to go ahead and merge once CI is green. We can follow up on your comment about unsubscribing from the license service in another PR if this needs to be addressed. |
💚 Build Succeeded
Metrics [docs]
History
To update your PR or re-run it, just comment with: |
Release note
Cross-cluster Replication and Watcher used to log misleading warnings about the basic license on Kibana startup. These warnings have been removed.
Summary
Fixes #95032
When you're on the Basic license, Watcher and CCR log disconcerting license failure messages to the command line on startup. These messages are phrased in a way that makes users think something is wrong, when in reality nothing is wrong. This PR makes two fundamental changes:
I also refactored our internal license service to leverage the types exposed by the core license service.
Here's how license failures are now surfaced to the user in the UI and CLI for CCR:
Watcher is not quite as helpful but we can address that separately:
Steps to test
license.tsservice to fake the license status.Checklist