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

Add support to define runtime fields #505

Merged
merged 17 commits into from
May 4, 2023
Merged

Conversation

mrodm
Copy link
Contributor

@mrodm mrodm commented Apr 19, 2023

What does this PR do?

Add support to define runtime fields.

In Kibana versions that do not support runtime fields, it is created a mapping with the given type. Because of that, as there is no failure when installing a package that contains a runtime field in those kibana versions, a new validation rule has been introduced to ensure that the packages must have at least a given kibana version.

Here it is the issue to add this support in Kibana elastic/kibana#155255

Checklist

  • I have added test packages to test/packages that prove my change is effective.
  • I have added an entry in spec/changelog.yml.
  • Add validation rule to allow this new runtime key to be used from kibana.version 8.9?

Related issues

@mrodm mrodm self-assigned this Apr 19, 2023
@elasticmachine
Copy link

elasticmachine commented Apr 19, 2023

💚 Build Succeeded

the below badges are clickable and redirect to their specific view in the CI or DOCS
Pipeline View Test View Changes Artifacts preview preview

Expand to view the summary

Build stats

  • Start Time: 2023-05-03T10:04:15.206+0000

  • Duration: 8 min 31 sec

Test stats 🧪

Test Results
Failed 0
Passed 834
Skipped 0
Total 834

🤖 GitHub comments

Expand to view the GitHub comments

To re-run your PR in the CI, just comment with:

  • /test : Re-trigger the build.

@elasticmachine
Copy link

elasticmachine commented Apr 19, 2023

🌐 Coverage report

Name Metrics % (covered/total) Diff
Packages 100.0% (8/8) 💚
Files 68.966% (20/29) 👍
Classes 73.171% (30/41) 👍 0.671
Methods 56.154% (73/130) 👍 2.495
Lines 43.029% (679/1578) 👍 1.472
Conditionals 100.0% (0/0) 💚

Comment on lines +406 to +409
"bad_runtime_kibana_version": []string{
"conditions.kibana.version must be ^8.9.0 or greater to include runtime fields",
},
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Created a new test function to check minimum kibana versions.

return nil
}

return errors.New("Warning: conditions.kibana.version must be ^8.8.0 or greater for non experimental input packages (version > 1.0.0)")
return errors.New("conditions.kibana.version must be ^8.8.0 or greater for non experimental input packages (version > 1.0.0)")
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed Warning: prefix since this error is always raised and fails the validation.

return nil
}

if kibanaVersionConditionIsGreaterThanOrEqualTo(kibanaVersionCondition, "8.9.0") {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

As there is no failure if a package that contains a runtime field is installed in kibana versions that do not support those kind of fields, this validation rule ensures that the packages must have at least a given kibana version.

What version should I set here @kpollich ? I was thinking that maybe it is 8.9 ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Talked offline with @kpollich , Elastic stack / Kibana 8.9.0 version is going to be set as minimum version for runtime fields.

@mrodm mrodm marked this pull request as ready for review April 21, 2023 14:28
@mrodm mrodm requested a review from a team as a code owner April 21, 2023 14:28
jsoriano
jsoriano previously approved these changes Apr 21, 2023
Copy link
Member

@jsoriano jsoriano left a comment

Choose a reason for hiding this comment

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

A couple of nitpicks, but looks good 👍

code/go/internal/validator/semantic/types.go Outdated Show resolved Hide resolved
Comment on lines +77 to +83
case string:
r.enabled = true
r.script = v
default:
// JSONSchema already checks about the type of this field (e.g. int or float)
r.enabled = true
r.script = string(data)
Copy link
Member

Choose a reason for hiding this comment

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

I think we should be on the safe side and return an error here for unexpected types, without assuming that this has always passed first through the json schema validator.

If we want to support numbers, I would add a case for them.

Suggested change
case string:
r.enabled = true
r.script = v
default:
// JSONSchema already checks about the type of this field (e.g. int or float)
r.enabled = true
r.script = string(data)
case string:
r.enabled = true
r.script = v
case float64:
r.enabled = true
r.script = string(data)
default:
return fmt.Errorf("unexpected type....

(json package always uses float64 for numbers, except when UseNumber() is used).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

TBH not sure if we need to support numbers (e.g. float64), just added as a test case for completeness.

Returning that error in the default case makes the two implementations for unmarshaling YAML and JSON different, at least as they are now. UnmarshalYAML should be able to differentiate between strings and float64, int... etc, but it only receives a value.Value that is a string.

It could be added to the UnmarshalYAML something like (or viceversa allowing it):

       if _, err := strconv.ParseFloat(text, 8); err == nil {
               return fmt.Errorf("unexpected type found float64")
       }

And other should also be checked I think.

A side-effect of introducing that error, it also adds an error for each JSON schema error too:

    validator_test.go:173: 
        	Error Trace:	/home/mariorodriguez/Coding/work/package-spec/code/go/pkg/validator/validator_test.go:173
        	Error:      	"found 10 validation errors:
        	            	   1. file "../../../../test/packages/bad_runtime_fields/data_stream/foo/fields/fields.yml" is invalid: field 0: Must not be present
        	            	   2. file "../../../../test/packages/bad_runtime_fields/data_stream/foo/fields/fields.yml" is invalid: field 1.runtime: Invalid type. Expected: string, given: integer
        	            	   3. file "../../../../test/packages/bad_runtime_fields/data_stream/foo/fields/fields.yml" is invalid: field 2: Must not be present
        	            	   4. file "../../../../test/packages/bad_runtime_fields/data_stream/foo/fields/fields.yml" is invalid: field 3: Must not be present
        	            	   5. file "../../../../test/packages/bad_runtime_fields/data_stream/foo/fields/fields.yml" is invalid: can't unmarshal fields: yaml.Unmarshal failed (path: data_stream/foo/fields/fields.yml): unexpected type found float64
        	            	   6. file "../../../../test/packages/bad_runtime_fields/data_stream/foo/fields/fields.yml" is invalid: can't unmarshal fields: yaml.Unmarshal failed (path: data_stream/foo/fields/fields.yml): unexpected type found float64
        	            	   7. file "../../../../test/packages/bad_runtime_fields/data_stream/foo/fields/fields.yml" is invalid: can't unmarshal fields: yaml.Unmarshal failed (path: data_stream/foo/fields/fields.yml): unexpected type found float64
        	            	   8. file "../../../../test/packages/bad_runtime_fields/data_stream/foo/fields/fields.yml" is invalid: can't unmarshal fields: yaml.Unmarshal failed (path: data_stream/foo/fields/fields.yml): unexpected type found float64
        	            	   9. file "../../../../test/packages/bad_runtime_fields/data_stream/foo/fields/fields.yml" is invalid: can't unmarshal fields: yaml.Unmarshal failed (path: data_stream/foo/fields/fields.yml): unexpected type found float64
        	            	  10. file "../../../../test/packages/bad_runtime_fields/data_stream/foo/fields/fields.yml" is invalid: can't unmarshal fields: yaml.Unmarshal failed (path: data_stream/foo/fields/fields.yml): unexpected type found float64
        	            	" should have 4 item(s), but has 10
        	Test:       	TestValidateFile/bad_runtime_fields

It looks that it would be too many errors to be shown to the user/developer for the same field wrong type:

  • There are 4 wrong fields in the bad_runtime_fields package: data_stream/foo/fields/fields.yml.
  • The error is shown for every time that YAML file is parsed.

An option for that would be check for duplicated errors and just show once that error:

--- code/go/internal/validator/spec.go
+++ code/go/internal/validator/spec.go
@@ -93,16 +93,24 @@ func processErrors(errs ve.ValidationErrors) ve.ValidationErrors {
                "Must validate all the schemas (allOf)",
                "Must validate at least one schema (anyOf)",
        }
+
+       duplicated := make(map[string]struct{})
        for _, e := range errs {
                for _, msg := range msgTransforms {
+                       if _, ok := duplicated[e.Error()]; ok {
+                               continue
+                       }
                        if strings.Contains(e.Error(), msg.original) {
-                               processedErrs = append(processedErrs, errors.New(strings.Replace(e.Error(), msg.original, msg.new, 1)))
+                               transformedErr := errors.New(strings.Replace(e.Error(), msg.original, msg.new, 1))
+                               processedErrs = append(processedErrs, transformedErr)
+                               duplicated[transformedErr.Error()] = struct{}{}
                                continue
                        }
                        if substringInSlice(e.Error(), redundant) {
                                continue
                        }
                        processedErrs = append(processedErrs, e)
+                       duplicated[e.Error()] = struct{}{}
                }
        }

Given that, I have some doubts @jsoriano :

  • should we just accept strings for now?
  • if other types are not allowed, in unmarshalYAML should we check for int, unsigned, ... too ?
  • If that error is raised, WDYT about removing duplicate error messages?

Copy link
Member

Choose a reason for hiding this comment

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

Ok, I wouldn't add complexity here to handle special cases. I would be fine with accepting only strings and bools. There would be any valid cases for numbers here?

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 that the scripts defined in runtime fields usually won't set just a number. Adding just a number for a field it could be also done in the ingest pipeline too.

Just tested adding runtime field with just a number in the script field using this index_template:

elasticsearch:
  index_template:
    mappings:
      runtime:
        foo:
          type: keyword
          script:
            source: "42"

And there is an error when trying to install the package:

2023/04/25 16:35:49 DEBUG POST https://127.0.0.1:5601/api/fleet/epm/packages
Error: can't install the package: can't install the package: could not zip-install package; API status code = 500; response body = {"statusCode":500,"error":"Internal Server Error","message":"mapper_parsing_exception\n\tCaused by:\n\t\tscript_exception: compile error\n\tRoot causes:\n\t\tscript_exception: compile error"}

Tested with the Elastic stack versions 8.8.0-SNAPSHOT and 8.0.0

As there will be compile errors, I guess we could just check bools or everything else being a string, couldn't we?

Copy link
Member

Choose a reason for hiding this comment

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

Yes, let's consider that this is a bool or a script, and rely on script compilation for the rest.

Comment on lines +437 to +443
- boolean
- date
- double
- geo_point
- ip
- keyword
- long
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This list is not the complete list available.
According to the docs, the complete list is:

  • boolean
  • composite
  • date
  • double
  • geo_point
  • ip
  • keyword
  • long
  • lookup

In this condition, just the types that we support in the type field are added. Missing types:

  • composite
  • lookup

jsoriano
jsoriano previously approved these changes Apr 25, 2023
@mrodm mrodm merged commit 5258244 into elastic:main May 4, 2023
@mrodm mrodm deleted the runtime_fields branch May 4, 2023 15:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants