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

[Ingest] Support yaml variables in datasource #64459

Merged

Conversation

nchaulet
Copy link
Member

Summary

Resolve #64152

Support yaml value in stream template. (Needed for redis package for example)

This PR allow agent stream to contain yaml variables, and variables to contains ..

Added more tests to cover these scenarios.

The way yaml variables are processed is done in two step:

  1. process handlebar template and replace yaml value by a placeholder
  2. traverse the generated yaml and replace placeholders by yaml values.

Example redis key metrics

Screen Shot 2020-04-24 at 4 39 20 PM

Screen Shot 2020-04-24 at 4 38 55 PM

@nchaulet nchaulet added v8.0.0 v7.8.0 Team:Fleet Team label for Observability Data Collection Fleet team labels Apr 24, 2020
@nchaulet nchaulet requested a review from a team April 24, 2020 20:39
@nchaulet nchaulet self-assigned this Apr 24, 2020
@elasticmachine
Copy link
Contributor

Pinging @elastic/ingest-management (Team:Ingest Management)

@nchaulet nchaulet requested a review from jen-huang April 24, 2020 20:40
@nchaulet nchaulet added the release_note:skip Skip the PR/issue when compiling release notes label Apr 24, 2020
@ruflin
Copy link
Member

ruflin commented Apr 27, 2020

Can you elaborate on what you exactly mean by "traverse the generated yaml and replace placeholders by yaml values."

@nchaulet
Copy link
Member Author

nchaulet commented Apr 27, 2020

@ruflin Yes

let's say we have the following template

host: {{host}}
{{#if key.patterns}}
key.patterns: {{key.patterns}}
{{/if}}

and the following values

{
 host: { value: "http://127.0.0.1"},
  "key.patterns": { value: "\n- limit: 20\n  pattern: .*\n", type: "yaml"}
}
  1. we are going to compile it with handlebar to
host: http://127.0.0.1
key.patterns: "{{key.patterns}}"

load this yaml as json

{ 
  "host": "http://127.0.0.1"
  "key.patterns": "{{key.patterns}}"
}
  1. I am looking for each yaml variables ({{yaml variables key }} ) and replace it by the parsed yaml value
{ 
  "host": "http://127.0.0.1"
  "key.patterns": [
    { limit: 20, pattern: ".*" }
  ]
}

@ruflin
Copy link
Member

ruflin commented Apr 28, 2020

@nchaulet I'm still missing on why in step 1 not all of the handlebar parts are replaced? Is the second step because of the . in the name?

@jfsiii
Copy link
Contributor

jfsiii commented Apr 28, 2020

@nchaulet We chose Handlebars to avoid all the safety/performance/etc issues with rolling our own templating. I'd like to find a way to work within Handlebars if possible. I think we can change either/both a) the object/keys we supply to handlebars b) adding/registering a yaml or other custom helper https://handlebarsjs.com/guide/#custom-helpers

export function createStream(vars: StreamVars, streamTemplate: string) {
const template = Handlebars.compile(streamTemplate);
return template(vars);
export function createStream(variables: DatasourceConfigRecord, streamTemplate: string) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This function went from returning a YAML string out to returning JS object. I'd like to keep some function that accepts a template string & variables and returns YAML. It doesn't need to this createStream function, but I'd like to keep a function which accepts the template string & variables and outputs the YAML. That way we don't lose the YAML output step and can do safeLoad if/when we want to convert it to JS.

processors:
- add_locale: ~
`);
it('should support yaml values', () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

If we changed createStream to accept a map of string -> any or unknown (vs current string -> string) I believe we could do this test like

test('Test nested', () => {
  const streamTemplate = `
host: {{host}}
{{#if key.patterns}}
key.patterns:{{key.patterns}}
{{/if}}
`;
  const vars = {
    host: 'http://127.0.0.1',
    key: {
      patterns: '\n  - limit: 20\n    pattern: .*',
    },
  };

  const output = createStream(vars, streamTemplate);

  expect(output).toBe(`
host: http://127.0.0.1
key.patterns:
  - limit: 20
    pattern: .*
`);
});

Copy link
Member Author

Choose a reason for hiding this comment

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

I could retranslate to a yaml after, but it's going to be stored as a JSON and send to the agent as a JSON, so if we can avoid one other round of conversion to yaml -> to json

@ruflin
Copy link
Member

ruflin commented Apr 28, 2020

@jfsiii @nchaulet Good discussion in this PR, please keep it going. One thing I wonder is if there is an option to get this PR in quickly in some form to unblock the package development and follow up with the proper implementation?

@jfsiii
Copy link
Contributor

jfsiii commented Apr 28, 2020

@ruflin @nchaulet agreed on iterating. I think the example I posted works and will allow us to keep progressing without the safety/other issues.

If we adopt this we really need to test and/or get feedback from security before releasing. We're processing user input into JS. That's a risky operation and something I'd like to run through a system which has guards and tests instead of rolling our own.

@nchaulet
Copy link
Member Author

nchaulet commented Apr 28, 2020

@jfsiii Yes I would like to avoid to write logic that looks like a template engine too, but I think handlebar is very limited here and replacing a yaml value is more complex than just a string interpolation, you need to have the right indentation, (for some value we could consider escaping them too)

I now it's frequent to do it this way in Helm charts for example but I think this make template really hard to write.

- name: xxxxx-volume
   resources:
      {{- .Values.xxxxx.resources | toYaml | nindent 6 }}

@jfsiii
Copy link
Contributor

jfsiii commented Apr 28, 2020

@nchaulet handlebars is intentionally limited but extensible and iiuc still suitable for what we're doing here.

If you can supply some more examples/tests of inputs/outputs I am happy to look into it.

To @ruflin's request I guess we can merge this as-is, but I would like to open a ticket for security to review before any release.

@ruflin
Copy link
Member

ruflin commented Apr 28, 2020

@jfsiii My hope is that we have a follow up PR directly after this one fixing it.

@nchaulet
Copy link
Member Author

@jfsiii @ruflin if we go with a follow up PR we probably need to update the templates after, so it's not really unlocking us, I will prefer to have this figured before

@nchaulet
Copy link
Member Author

@jfsiii for the security perspective, we are already loading yaml coming from user using safeLoad at multiple place.

@ruflin
Copy link
Member

ruflin commented Apr 28, 2020

@nchaulet One comment on the indentation: My assumption would be that the indentation already has to be correct in the package. If it is not, the package has to be fixed. @mtojek Now wondering if the yaml syntax really allows this or magically removes some spaces ...

@nchaulet
Copy link
Member Author

nchaulet commented Apr 28, 2020

@ruflin @jfsiii if we stay with a solution that is purely handlebar the template will change from

host: {{host}}
{{#if key.patterns}}
key.patterns: {{key.patterns}}
{{/if}}

to

{{#if key.patterns}}
key.patterns: 
  {{ toYaml key.patterns 2 }}
{{/if}}

and we need to introduce a custom handlebar helper something like to toYaml(value, indent)

@jfsiii
Copy link
Contributor

jfsiii commented Apr 28, 2020

@nchaulet did you see #64459 (comment)?

Doesn't that keep the same template and use handlebars w/o a custom helper?

@nchaulet
Copy link
Member Author

@jfsiii in you example you need to know the place where the variable is going to use to already have the indentation in the variable

@ruflin
Copy link
Member

ruflin commented Apr 28, 2020

Couldn't it also be:

host: {{host}}
{{#if key.patterns}}
key.patterns:
{{key.patterns}}
{{/if}}

And now it is purely up to the package to make sure the variable in key.patterns has the correct indentation?

@jfsiii
Copy link
Contributor

jfsiii commented Apr 28, 2020

@nchaulet I took that from the example you posted

"​key.patterns​"​:​ { value​:​ ​"​\n​- limit: 20​\n​  pattern: .*​\n​"​, type​:​ ​"​yaml​"​}

If you have other examples, I can adapt.

Let's try to get together soon and talk about this.

@nchaulet
Copy link
Member Author

@ruflin in you example this mean the variable can be used only one time as the variable will contains the indentation, and we need to add new config to the yaml variable to have the indentation inside

@ruflin
Copy link
Member

ruflin commented Apr 28, 2020

Not sure I follow the part that it can be used only one time. But yes, as the user will provide some input here, he would also have to use the correct indentation in the editor.

@jfsiii jfsiii self-requested a review April 28, 2020 19:33
Copy link
Contributor

@jfsiii jfsiii left a comment

Choose a reason for hiding this comment

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

🚀 Thanks for the token changes and restoring the escaping. That addresses my biggest concerns.

Any changes I can think of wouldn't affect the templates or ability to move the input to arbitrary positions in the map. We can do that in a later PR if it comes up.

Thanks again

Co-Authored-By: John Schulz <john.schulz@elastic.co>
@kibanamachine
Copy link
Contributor

💚 Build Succeeded

History

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


interface StreamVars {
[k: string]: string | string[];
function isValidKey(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.

Not a blocker for this PR but future improvement: I think these services belong in /services/datasources.ts too rather than EPM as EPM services are registry & asset interactions. Might be good to make a separate directory for data source services as that single file is getting rather long.

@nchaulet
Copy link
Member Author

Trying to resume discussions we had offline:

One confusion was coming from the fact that even if we dispaly a YAML to the user, we do not store in saved object or send the agent configuration to the agent as a YAML but as a JSON.

This solution allow to have this template working, and we do not need to change existing templates, and because we load yaml using safeLoad it should not be so insecure.

title: "example"
keys.patterns: {{key.patterns}}
key:
  description: "yaml value one level deep"
{{#if keys.patterns}}
  patterns: {{key.patterns}}
{{/if}}

@nchaulet nchaulet merged commit 129cf4f into elastic:master Apr 29, 2020
@nchaulet nchaulet deleted the feature-ingest-datasource-support-yaml-values branch April 29, 2020 01:43
nchaulet added a commit to nchaulet/kibana that referenced this pull request Apr 29, 2020
gmmorris added a commit to gmmorris/kibana that referenced this pull request Apr 29, 2020
* master: (60 commits)
  [SIEM] Create template timeline (elastic#63136)
  load react component lazily in so management section (elastic#64285)
  Cleanup .eslingignore and add target (elastic#64617)
  [Ingest] Support yaml variables in datasource (elastic#64459)
  typescript-ify portions of src/optimize (elastic#64688)
  [ngSanitize] add explicit dependencies to all uses of `ngSanitize` angular module (elastic#64546)
  Consolidate downloading plugin bundles to bootstrap script (elastic#64685)
  [Maps] disable edit layer button when flyout is open for add layer or map settings (elastic#64230)
  chore(NA): add async import into infra plugin to reduce apm bundle size (elastic#63292)
  [Maps] fix edit filter (elastic#64586)
  [SIEM][Detections] Adds large list support using REST endpoints
  Replace a number of any-ed styled(eui*) with accurate types (elastic#64555)
  [Endpoint] Recursive resolver children (elastic#61914)
  [ML] Fix new job wizard with multiple indices (elastic#64567)
  Use short URLs for legacy plugin deprecation warning (elastic#64540)
  [Uptime] Update uptime ml job id to limit to 64 char (elastic#64394)
  [Ingest] Fix GET /enrollment-api-keys/null error (elastic#64595)
  Consolidate cross-cutting concerns between region & coordinate maps in new maps_legacy plugin (elastic#64123)
  ES UI new platform cleanup (elastic#64332)
  [Event Log] use @timestamp field for queries (elastic#64391)
  ...
gmmorris added a commit to gmmorris/kibana that referenced this pull request Apr 29, 2020
* alerting/np-migration: (64 commits)
  [ML] Changes Machine learning overview UI text (elastic#64625)
  [Uptime] Migrate client to New Platform (elastic#55086)
  Slim vis type timeseries (elastic#64631)
  [Telemetry] Fix inconsistent search behaviour in Advanced Settings (elastic#64510)
  removed unneeded dep and file
  [SIEM] Create template timeline (elastic#63136)
  load react component lazily in so management section (elastic#64285)
  Cleanup .eslingignore and add target (elastic#64617)
  [Ingest] Support yaml variables in datasource (elastic#64459)
  typescript-ify portions of src/optimize (elastic#64688)
  [ngSanitize] add explicit dependencies to all uses of `ngSanitize` angular module (elastic#64546)
  Consolidate downloading plugin bundles to bootstrap script (elastic#64685)
  [Maps] disable edit layer button when flyout is open for add layer or map settings (elastic#64230)
  chore(NA): add async import into infra plugin to reduce apm bundle size (elastic#63292)
  [Maps] fix edit filter (elastic#64586)
  [SIEM][Detections] Adds large list support using REST endpoints
  Replace a number of any-ed styled(eui*) with accurate types (elastic#64555)
  [Endpoint] Recursive resolver children (elastic#61914)
  [ML] Fix new job wizard with multiple indices (elastic#64567)
  Use short URLs for legacy plugin deprecation warning (elastic#64540)
  ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
release_note:skip Skip the PR/issue when compiling release notes Team:Fleet Team label for Observability Data Collection Fleet team v7.8.0 v8.0.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Ingest Manager: YAML options not propagated to config
6 participants