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

Make core responsible for reading and merging of config files. Simplify legacy config adapter. #21956

Merged
merged 8 commits into from
Aug 28, 2018

Conversation

azasypkin
Copy link
Member

@azasypkin azasypkin commented Aug 14, 2018

This PR switches "legacy" Kibana to read and merge config files via core functionality. This will be done only by core once we merge #22190.

I've also simplified legacy config adapter and removed support for __newPlatform config node, if we need something like this in the future we'll add it back, but since new platform is in master already I don't see any reason to keep this unused functionality.

Blocked by #21885
Unblocks #19994

@azasypkin azasypkin added blocked Team:Core Core services & architecture: plugins, logging, config, saved objects, http, ES client, i18n, etc Feature:New Platform v6.5.0 labels Aug 14, 2018
@azasypkin azasypkin force-pushed the issue-xxx-core-raw-config branch 2 times, most recently from 583fcf7 to 7843504 Compare August 20, 2018 14:30
@azasypkin azasypkin removed the blocked label Aug 23, 2018
@azasypkin azasypkin changed the title [skip ci] Make core responsible for reading and merging of config files. Simplify legacy config adapter. Make core responsible for reading and merging of config files. Simplify legacy config adapter. Aug 23, 2018
@elasticmachine

This comment has been minimized.

@elasticmachine

This comment has been minimized.

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

* in extreme cases when there is no other better way, e.g. bridging with the
* "legacy" systems that consume and process config in a different way.
*/
toRaw(): Record<string, any>;
Copy link
Member Author

@azasypkin azasypkin Aug 24, 2018

Choose a reason for hiding this comment

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

note: will be needed here, here and here.

constructor(readonly configFile: string) {
constructor(
readonly configFiles: ReadonlyArray<string>,
configAdapter: (rawValue: Record<string, any>) => RawConfig = rawValue =>
Copy link
Member Author

Choose a reason for hiding this comment

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

note: I need this "middleware" to be able to apply cli/keystore overrides to config before it will be published to any subscriber.

throw new Error(`some config paths are not handled: ${JSON.stringify(unhandledConfigPaths)}`);
// We don't throw here since unhandled paths are verified by the "legacy"
// Kibana right now, but this will eventually change.
this.log.trace(
Copy link
Member Author

@azasypkin azasypkin Aug 24, 2018

Choose a reason for hiding this comment

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

note: still left trace here, just as a reminder... I'm not sure what to do with that yet. Currently core uses keys defined in the "legacy" schema, so "legacy" Kibana won't complain about unused keys. But once we start using some config keys in the core exclusively and don't define them in the legacy schema then we'll need to expose something like core.getAppliedConfigKeys() so that "legacy" Kibana doesn't mark these keys as unused/unknown (or just filter out core-only settings from config we send to the "legacy" kibana, that would be even easier).

@azasypkin
Copy link
Member Author

This one is ready for review @spalger :)

Thanks!

@azasypkin azasypkin requested a review from spalger August 24, 2018 12:53
@spalger
Copy link
Contributor

spalger commented Aug 27, 2018

Alright, I'll take a look today (was out Friday)

Copy link
Contributor

@spalger spalger left a comment

Choose a reason for hiding this comment

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

Code looks good, and things run nicely, haven't dug too deep into the tests, but spot checked and things look good. I'm struggling a bit with the naming, as noted in the comments, but I suspect these are more situations where things will make more sense once we get the rest of the big PR in...

});
}

function merge(target: any, value: any, key: string = '') {
Copy link
Contributor

Choose a reason for hiding this comment

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

feels like key should just be optional, not default to an empty string.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, was too lazy to write else if (key !== undefined) { to make lodash.set type signature happy :) But agree, will change that.

readonly configFiles: ReadonlyArray<string>,
configAdapter: (rawValue: Record<string, any>) => RawConfig = rawValue =>
new ObjectToRawConfigAdapter(rawValue)
) {
this.config$ = this.rawConfigFromFile$.pipe(
filter(rawConfig => rawConfig !== notRead),
Copy link
Contributor

Choose a reason for hiding this comment

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

Could this filtering be avoided by using a ReplaySubject(1) instead of a BehaviorSubject(notRead)?

Copy link
Member Author

Choose a reason for hiding this comment

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

Absolutely! We also we'll need to review all other places in the server core where observables are used - kbn/observable didn't have a lot of useful operators/factories that we have now.

import { RawConfig } from './raw_config';

/**
* Allows plain javascript object to behave like `RawConfig` instance.
* @internal
*/
export class ObjectToRawConfigAdapter implements RawConfig {
constructor(private readonly rawValue: { [key: string]: any }) {}
constructor(protected readonly rawValue: Record<string, any>) {}
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like this can stay private.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yep, thanks! Was experimenting with something and likely forgot to revert


import { ConfigPath } from './config_service';
import { ConfigPath } from './';
import { RawConfig } from './raw_config';

/**
* Allows plain javascript object to behave like `RawConfig` instance.
* @internal
*/
export class ObjectToRawConfigAdapter implements RawConfig {
Copy link
Contributor

Choose a reason for hiding this comment

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

What is the ObjectTo... part of this name supposed to represent?

Copy link
Member Author

Choose a reason for hiding this comment

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

Well, I'm bad at naming 🙁

At some point we had "adapter" for the legacy config (src/server/config/config.js) to be able to use it everywhere where RawConfig was expected, so it was called LegacyConfigToRawConfigAdapter (like in Number.toString() or just NumberToString 🙂 ):

LegacyConfig (src/server/config/config.js) + ----> (to) + RawConfig (src/core/server/config/raw_config.ts)

But for the non-legacy world we have a plain JavaScript object (or just Object) instead, that was parsed from yaml config file, so it was called ObjectToRawConfigAdapter (probably PlainObjectTo... would have been better):

Record<string, any> (Object) + ----> (to) + RawConfig (src/core/server/config/raw_config.ts)

We got rid of dependency on the legacy config now and can work with underlying raw config object, but we still need some transformation before core can use it (e.g. logging config format is different), so to bridge it with the legacy world I created LegacyObjectToRawConfigAdapter, Legacy part is just to stress that we do something there to support legacy world, and ObjectToRawConfigAdapter part as explained above.

If it sounds really confusing we can definitely rename this at any point, just let me know what name would sound better?

constructor(readonly configFile: string) {
constructor(
readonly configFiles: ReadonlyArray<string>,
configAdapter: (rawValue: Record<string, any>) => RawConfig = rawValue =>
Copy link
Contributor

Choose a reason for hiding this comment

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

Feels like this is mapping the rawConfig to a config instance, which is why the input observable is called rawConfigFromFile$ and the result observable is config$, but the name configAdapter doesn't seem to communicate that, and the fact that it's supposed to return a RawConfig is even more confusing IMO.

Copy link
Member Author

@azasypkin azasypkin Aug 28, 2018

Choose a reason for hiding this comment

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

Agree, it's a way to confusing now. Let's leave raw to real "raw" plain JavaScript config object parsed from yaml and rename RawConfig to just Config (and remove raw from names of adapters as well). I was previously concerned about RawConfig.toRaw, and having Config.toRaw makes me feel a bit better :)

@elasticmachine

This comment has been minimized.

@elasticmachine

This comment has been minimized.

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

@azasypkin
Copy link
Member Author

6.x/6.5: dc04be0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backported Feature:New Platform Team:Core Core services & architecture: plugins, logging, config, saved objects, http, ES client, i18n, etc v6.5.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants