Skip to content
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

Implements config deprecation in New Platform #52251

Merged
merged 33 commits into from
Dec 13, 2019

Conversation

pgayvallet
Copy link
Contributor

@pgayvallet pgayvallet commented Dec 5, 2019

Summary

Fix #40255

Implement configuration deprecation management in new platform. core and plugins can now register deprecations to be used to mutates the raw configuration (by renaming or removing deprecated properties)

Actual implementation

The legacy config deprecation system was chaotic in the sense that deprecations were actually applied in different points (and time) in the code. However, the underneath implementation and API was pretty clever and NP implementation is strongly inspired by it.

core can now register deprecation providers using configService.addDeprecationProvider. Plugins can register the same providers when declaring their configuration using their PluginConfigDescriptor

 const configSchema = schema.object({
   someNewKey: schema.string({ defaultValue: 'a string' }),
 });
 
 type ConfigType = TypeOf<typeof configSchema>;
 
 export const config: PluginConfigDescriptor<ConfigType> = {
   schema: configSchema,
   deprecations: ({ rename, unused }) => [
     rename('someOldKey', 'someNewKey'),
     unused('deprecatedProperty'),
   ],
 };

By default, deprecations can only access a plugin's configuration schema, meaning that rename('someOldKey', 'someNewKey'), will actually rename myplugin.someOldKey to myplugin.someNewKey. However due to legacy and edge case requirements, we also expose APIs to access the whole config (renameFromRoot and unusedFromRoot)

Deprecations are not limited to rename and unused (even if they cover 99% of the needs) and any custom implementation of ConfigDeprecation can be returned by a provider.

RawConfigService and ConfigService changes

To properly manage and apply deprecations to the configuration values, I had to change these two services responsibilities a little. Now the RawConfigService returns the configuration values in a raw, Record<string, any> format instead of returning an actual Config object. This allows to move all configuration data manipulation logic in a single place (the config service). This is probably more coherent regarding what we could expect of a Raw[..]-type service anyway.

The most important change regarding ConfigService is in the schema validation timing: because of some edge cases of deprecations, it's no longer possible to validates a plugin's schema when registering it. Practical exemple is a property that would have been migrated from one plugin to another (and we got some of these in the legacy deprecations, such as rename('xpack.telemetry.enabled', 'telemetry.enabled'),). For this reason, global config validation is now done at a single point of time, just after plugins discovery.

Legacy deprecations in new platform

As legacy and new deprecation system are pretty close, I was able to create an adapter from legacy deprecations to new ones. the LegacyService now collect every legacy plugin's deprecation providers, adapt them, and register them in the new platform. That way, the NP configuration is properly aware of legacy deprecations and expose up to date values.

What remains to be done

  • Totally removes the legacy deprecation system

UPDATE after discussion, this will be done after NP plugin migration, and is out of the scope of this PR

We are so close and yet so far. This impacts LegacyService and legacy code the most. Currently, the legacy service creates a LegacyConfig from the raw config's value, and calls findPluginSpecs with it. The config then enter in the legacy world/code, where discovered legacy plugins mutates the legacy configuration with both their schema, their value and their deprecations. This mutated legacy config is then considered the source of truth when we instanciate the legacy kibana server.

To be able to totally suppress the legacy deprecation system, we will need to remove the part where the deprecation are applied from legacy findPluginSpecs, get the non-migrated config back in legacyService, and then apply the new platform deprecation to it. However, all this code is strongly coupled atm, not typed (js, no ts), and lack test coverage, so I will need to be extremely careful with that part, and think that we might want to do it in a separate PR.

Checklist

Use strikethroughs to remove checklist items you don't feel are applicable to this PR.

- [ ] This was checked for cross-browser compatibility, including a check against IE11
- [ ] Any text added follows EUI's writing guidelines, uses sentence case text and includes i18n support

For maintainers

- [ ] This was checked for breaking API changes and was labeled appropriately

Dev Docs

New platform plugin's configuration now supports deprecation. Use the deprecations key of a plugin's config descriptor to register them.

 // my_plugin/server/index.ts
 import { schema, TypeOf } from '@kbn/config-schema';
 import { PluginConfigDescriptor } from 'kibana/server';
 
 const configSchema = schema.object({
   someNewKey: schema.string({ defaultValue: 'a string' }),
 });
 
 type ConfigType = TypeOf<typeof configSchema>;
 
 export const config: PluginConfigDescriptor<ConfigType> = {
   schema: configSchema,
   deprecations: ({ rename, unused }) => [
     rename('someOldKey', 'someNewKey'),
     unused('deprecatedProperty'),
   ],
 };

@elasticmachine

This comment has been minimized.

@elasticmachine

This comment has been minimized.

@elasticmachine

This comment has been minimized.

@pgayvallet pgayvallet force-pushed the kbn-40255-NP-config-deprecation branch from 98bcb38 to 31a9c38 Compare December 6, 2019 11:15
@elasticmachine

This comment has been minimized.

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

@pgayvallet pgayvallet added Feature:New Platform release_note:plugin_api_changes Contains a Plugin API changes section for the breaking plugin API changes section. Team:Core Core services & architecture: plugins, logging, config, saved objects, http, ES client, i18n, etc v7.6.0 v8.0.0 labels Dec 6, 2019
@elasticmachine
Copy link
Contributor

Pinging @elastic/kibana-platform (Team:Platform)

src/core/server/config/config_service.ts Show resolved Hide resolved
src/core/server/config/config_service.ts Outdated Show resolved Hide resolved
src/core/server/config/deprecation/utils.ts Outdated Show resolved Hide resolved
Comment on lines +44 to +48
deprecations: ({ rename, unused, renameFromRoot }) => [
rename('securityKey', 'secret'),
renameFromRoot('oldtestbed.uiProp', 'testbed.uiProp'),
unused('deprecatedProperty'),
],
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added an example in testbed. Not sure if I should keep it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could add something similar to the integration tests in LP https://github.com/restrry/kibana/blob/55604f711f2b06ca58235c1aabc45f5281e13642/src/legacy/server/config/__tests__/deprecation_warnings.js#L28
although it tests only logging part

Copy link
Contributor Author

@pgayvallet pgayvallet Dec 9, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want a similar approach to what LP does, by actually spawning a real kibana process? Could not find a NP equivalent to legacy const RUN_KBN_SERVER_STARTUP = require.resolve('./fixtures/run_kbn_server_startup'); ? Seems like more than what we call integration_test in NP?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

run_kbn_server_startup uses test_utils under the hood. So do we in the integrations tests.
https://github.com/restrry/kibana/blob/55604f711f2b06ca58235c1aabc45f5281e13642/src/core/server/http/integration_tests/core_services.test.ts#L23
so yes, I think we can adopt the same approach

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, missed that.

@pgayvallet pgayvallet marked this pull request as ready for review December 6, 2019 13:38
@pgayvallet pgayvallet requested a review from a team as a code owner December 6, 2019 13:38
@joshdover
Copy link
Contributor

Overall this is looking great. Haven't dug deep into the implementation yet, but the interface and design looks pretty good to me.

  • Totally removes the legacy deprecation system

We are so close and yet so far. This impacts LegacyService and legacy code the most. Currently, the legacy service creates a LegacyConfig from the raw config's value, and calls findPluginSpecs with it. The config then enter in the legacy world/code, where discovered legacy plugins mutates the legacy configuration with both their schema, their value and their deprecations. This mutated legacy config is then considered the source of truth when we instanciate the legacy kibana server.

To be able to totally suppress the legacy deprecation system, we will need to remove the part where the deprecation are applied from legacy findPluginSpecs, get the non-migrated config back in legacyService, and then apply the new platform deprecation to it. However, all this code is strongly coupled atm, not typed (js, no ts), and lack test coverage, so I will need to be extremely careful with that part, and think that we might want to do it in a separate PR.

Just so I understand correctly:

As-is in this PR right now, New Platform plugins and Legacy plugins will be reading different configs because the NP deprecations are not applied to the config that legacy reads. However, the legacy deprecations are applied to the config that NP reads. Correct?

If so, are we sure there's a need to apply NP deprecations to the config that LP reads? It seems that if an LP plugin wants to read a renamed config key, it'd be fine if we told them they still need to implement the deprecation in the LP too.

@pgayvallet
Copy link
Contributor Author

As-is in this PR right now, New Platform plugins and Legacy plugins will be reading different configs because the NP deprecations are not applied to the config that legacy reads. However, the legacy deprecations are applied to the config that NP reads. Correct?

That is correct.

If so, are we sure there's a need to apply NP deprecations to the config that LP reads? It seems that if an LP plugin wants to read a renamed config key, it'd be fine if we told them they still need to implement the deprecation in the LP too.

I don't know if there is a functional need. To be honest I'm not even sure we really should do it.

However what I meant what more about technical cleanup: ATM, legacy deprecations are applied to the legacy config (a good thing) but in the legacy world (which is the issue), therefor blocking me from actually remove all the legacy config deprecation system. Now that legacy deprecation can be adapted to NP one, we could apply the adapted legacy deprecations (and maybe the NP ones) to LP config in NP (probably in the legacy service), which would allow us to get rid of the legacy code now without waiting for post 8.0.

But we can also decide that this is not necessary and just keep this legacy code until we actually unplug legacy.

WDYT?

@joshdover
Copy link
Contributor

It's not worth to do it now. Removing it after plugins migration is safer and way less complicated.

👍

@joshdover joshdover mentioned this pull request Dec 11, 2019
7 tasks
@mshustov mshustov mentioned this pull request Dec 11, 2019
3 tasks
* under the License.
*/

import _, { has, get } from 'lodash';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
import _, { has, get } from 'lodash';
import { has, get } from 'lodash';


process.env.kbnWorkerType = 'managr';

export default class ClusterManager {
static create(opts, settings = {}, basePathProxy) {
return new ClusterManager(
opts,
Config.withDefaultSchema(transformDeprecations(settings)),
Config.withDefaultSchema(settings),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if it works as expected. As I understand settings is expected to be POJO here. But we pass LegacyConfig https://github.com/elastic/kibana/pull/52251/files#diff-748376502c51ed8bfc271f4a6c4c26d3R251
Shouldn't we wrap in getLegacyRawConfig or pass this.settings in legacy_service ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you are right. However the PR did not change the arguments we were invoking ClusterManager.create with. So I guess this has always been broken?

The fix is (well, should be) easy

require('../../../cli/cluster/cluster_manager').create(
      this.coreContext.env.cliArgs,
--    config,
++    config.get(),
      await basePathProxy$.toPromise()
    );

However the legacy service tests are a mess, and are not properly mocking with correct object types (the config in test is actually a Record and not a LegacyConfig ). Already had to fix that in #52060, so would be easier to fix in a separate PR once it lands.

Will open a ticket for that if it's alright with you

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#52860 created

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, not a blocker for the current work.

@@ -220,6 +228,8 @@ export class Server {
[uiSettingsConfig.path, uiSettingsConfig.schema],
];

this.configService.addDeprecationProvider(rootConfigPath, coreDeprecationProvider);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't we rename setupConfigSchemas ? for example setupCoreConfig.

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

History

To update your PR or re-run it, just comment with:
@elasticmachine merge upstream

@pgayvallet pgayvallet merged commit eb3d9b1 into elastic:master Dec 13, 2019
pgayvallet added a commit to pgayvallet/kibana that referenced this pull request Dec 13, 2019
* implements 'rename' and 'unset' deprecations

* introduce usage of ConfigDeprecationProvider

* adapt RawConfigService to only returns unmodified raw config

* apply deprecations when accessing config

* register legacy plugin deprecation in new platform

* implements ConfigService#validate

* add exemple config deprecation usage in testbed

* documentation

* export public config deprecation types

* fix new test due to rebase

* name ConfigDeprecationFactory

* update generated doc

* add tests for unset and move it to src/core/utils

* add tests for renameFromRoot and unusedFromRoot

* cast paths as any as get expects a fixed-length string array

* use specific logger for deprecations

* add additional test on renameFromRoot

* update migration guide

* migrate core deprecations to NP

* add integration test

* use same log context as legacy

* remove old deprecation warnings integration tests, now covered in NP

* migrates csp deprecation to NP

* removes deprecationWarningMixin from legacy

* remove legacy core deprecations

* remove unused import

* rename setupConfigSchemas to setupCoreConfig

* update generated doc
pgayvallet added a commit that referenced this pull request Dec 13, 2019
* implements 'rename' and 'unset' deprecations

* introduce usage of ConfigDeprecationProvider

* adapt RawConfigService to only returns unmodified raw config

* apply deprecations when accessing config

* register legacy plugin deprecation in new platform

* implements ConfigService#validate

* add exemple config deprecation usage in testbed

* documentation

* export public config deprecation types

* fix new test due to rebase

* name ConfigDeprecationFactory

* update generated doc

* add tests for unset and move it to src/core/utils

* add tests for renameFromRoot and unusedFromRoot

* cast paths as any as get expects a fixed-length string array

* use specific logger for deprecations

* add additional test on renameFromRoot

* update migration guide

* migrate core deprecations to NP

* add integration test

* use same log context as legacy

* remove old deprecation warnings integration tests, now covered in NP

* migrates csp deprecation to NP

* removes deprecationWarningMixin from legacy

* remove legacy core deprecations

* remove unused import

* rename setupConfigSchemas to setupCoreConfig

* update generated doc
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature:New Platform release_note:plugin_api_changes Contains a Plugin API changes section for the breaking plugin API changes section. Team:Core Core services & architecture: plugins, logging, config, saved objects, http, ES client, i18n, etc v7.6.0 v8.0.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

New Platform Config service should support deprecations
4 participants