Add warning/error for dashboards including visualizations by reference#389
Add warning/error for dashboards including visualizations by reference#389mrodm merged 18 commits intoelastic:mainfrom
Conversation
🌐 Coverage report
|
|
/test |
1 similar comment
|
/test |
| for _, objectFile := range objectFiles { | ||
| filePath := objectFile.Path() | ||
|
|
||
| objectReferences, err := objectFile.Values(`$.references[?(@.type=="visualization")]`) |
There was a problem hiding this comment.
is this the right way to check for visualizations included by reference in a dashboard?
There was a problem hiding this comment.
I guess that the best will be to have some test cases 🙂
There was a problem hiding this comment.
I would want to confirm that this reference key in the dashboard json files is the one defining that a visualization is set as reference.
I've just tested that adding some visualizations from the library in the EPR integration, and it resulted in these changes:
--- packages/elastic_package_registry/kibana/dashboard/elastic_package_registry-313c2700-099b-11ed-91b6-3b1f9c2b2771.json
+++ packages/elastic_package_registry/kibana/dashboard/elastic_package_registry-313c2700-099b-11ed-91b6-3b1f9c2b2771.json
@@ -2215,6 +2215,38 @@
"title": "EPR Indexer - Get duration seconds per indexer - Percentile 95",
"type": "lens",
"version": "8.0.0"
+ },
+ {
+ "embeddableConfig": {
+ "enhancements": {}
+ },
+ "gridData": {
+ "h": 15,
+ "i": "6beb6552-317f-442e-a6d5-397dc849aa98",
+ "w": 24,
+ "x": 0,
+ "y": 82
+ },
+ "panelIndex": "6beb6552-317f-442e-a6d5-397dc849aa98",
+ "panelRefName": "panel_6beb6552-317f-442e-a6d5-397dc849aa98",
+ "type": "lens",
+ "version": "8.0.0"
+ },
+ {
+ "embeddableConfig": {
+ "enhancements": {}
+ },
+ "gridData": {
+ "h": 15,
+ "i": "a3ae1ef0-3e98-4d2c-9b19-8b2ddc54ef4d",
+ "w": 24,
+ "x": 24,
+ "y": 82
+ },
+ "panelIndex": "a3ae1ef0-3e98-4d2c-9b19-8b2ddc54ef4d",
+ "panelRefName": "panel_a3ae1ef0-3e98-4d2c-9b19-8b2ddc54ef4d",
+ "type": "visualization",
+ "version": "8.0.0"
}
],
"refreshInterval": {
@@ -2417,6 +2449,16 @@
"id": "metrics-*",
"name": "d237dd46-1074-47ee-8186-3957b956acd9:indexpattern-datasource-layer-cfdc23c6-0606-45ea-b1d2-782ba8103f82",
"type": "index-pattern"
+ },
+ {
+ "id": "elastic_package_registry-b82baae0-1e06-11ed-92f0-1397dcee01a8",
+ "name": "6beb6552-317f-442e-a6d5-397dc849aa98:panel_6beb6552-317f-442e-a6d5-397dc849aa98",
+ "type": "lens"
+ },
+ {
+ "id": "elastic_package_registry-fd226440-1e06-11ed-92f0-1397dcee01a8",
+ "name": "a3ae1ef0-3e98-4d2c-9b19-8b2ddc54ef4d:panel_a3ae1ef0-3e98-4d2c-9b19-8b2ddc54ef4d",
+ "type": "visualization"
}
],
"type": "dashboard"From this test, I've just realised that it should be checked whether or not there are references from both Lens and Visualization. Should it be checked for other types too ?
I will try to add some test cases also for this part too (getting the reference elements from the json)
| "field services.docker-custom-agent: Must not validate the schema (not)", | ||
| }, | ||
| }, | ||
| "visualizations_by_reference": {}, |
There was a problem hiding this comment.
Adding this new element here, the package will be tested, but no errors can be checked. Should it be moved to another test ?
There was a problem hiding this comment.
Good point 🤔 Maybe we need to add some way to inject loggers so we can capture what is being logged. But we can also ignore this till we have #342.
There was a problem hiding this comment.
Maybe we can have some undocumented environment variable like PACKAGE_SPEC_WARNINGS_AS_ERRORS that produces errors on warnings. We could enable it in tests.
There was a problem hiding this comment.
Added that environment variable with some helpers to be able to do tests on warnings
mrodm@06f27a9
| continue | ||
| } | ||
|
|
||
| anyReference(objectReferences, fsys.Path(filePath)) |
There was a problem hiding this comment.
Do you want to check the returning values of this call? Or this is only to log till we have a better way of reporting warnings?
Consider in any case explictly ignoring the returned values.
| anyReference(objectReferences, fsys.Path(filePath)) | |
| _, _ := anyReference(objectReferences, fsys.Path(filePath)) |
Or move error reporting here, something like this:
| anyReference(objectReferences, fsys.Path(filePath)) | |
| ids, err := anyReference(objectReferences, fsys.Path(filePath)) | |
| if err != nil { | |
| return err | |
| } | |
| if len(ids) > 0 { | |
| log.Printf("Warning: visualization by reference found in %s: %s", filePath, strings.Join(ids, ", ")) | |
| } |
There was a problem hiding this comment.
Updated this function to return references and errors (if any). With this change, ValidateVisualizationsUsedByValue function is the one in charge of showing the warnings.
| for _, objectFile := range objectFiles { | ||
| filePath := objectFile.Path() | ||
|
|
||
| objectReferences, err := objectFile.Values(`$.references[?(@.type=="visualization")]`) |
There was a problem hiding this comment.
I guess that the best will be to have some test cases 🙂
| func anyReference(val interface{}, path string) (bool, error) { | ||
| references, err := toReferenceSlice(val) | ||
| if err != nil { | ||
| return false, fmt.Errorf("unable to convert references in file [%s]", path) |
There was a problem hiding this comment.
This looks like an error that we don't want to ignore when calling anyReference.
|
|
||
| func toReferenceSlice(val interface{}) ([]reference, error) { | ||
| var refs []reference | ||
| jsonbody, err := json.Marshal(val) |
There was a problem hiding this comment.
Nit. Probably we can expect a type like map[string]interface{} for these values, maybe you can convert from there instead of marshalling and unmarshalling.
This library may also help to convert from a generic map to a struct: https://pkg.go.dev/github.com/mitchellh/mapstructure
There was a problem hiding this comment.
I've changed the code to use casting/expected types. For the moment, as it would be just one usage, I've tried to not use that mapstructure library.
| func anyReference(val interface{}, path string) (bool, error) { | ||
| references, err := toReferenceSlice(val) | ||
| if err != nil { | ||
| return false, fmt.Errorf("unable to convert references in file [%s]", path) |
There was a problem hiding this comment.
Wrap the error here.
| return false, fmt.Errorf("unable to convert references in file [%s]", path) | |
| return false, fmt.Errorf("unable to convert references in file [%s]: %w", path, err) |
|
|
||
| err = json.Unmarshal(jsonbody, &refs) | ||
| if err != nil { | ||
| log.Printf("error unmarshaling references: %s", err) |
There was a problem hiding this comment.
Why logging these errors? I think it would be better to return the errors and handle (or ignore) in the callers.
There was a problem hiding this comment.
These conversion errors are now returned and the callers can handle them properly.
| "field services.docker-custom-agent: Must not validate the schema (not)", | ||
| }, | ||
| }, | ||
| "visualizations_by_reference": {}, |
There was a problem hiding this comment.
Good point 🤔 Maybe we need to add some way to inject loggers so we can capture what is being logged. But we can also ignore this till we have #342.
Update also messages shown to indicate both the id and type of the reference found.
|
|
||
| objectReferences, err := objectFile.Values(`$.references`) | ||
| if err != nil { | ||
| // no references key in dashboard json |
There was a problem hiding this comment.
There are test packages that do not have "references" key. To avoid errors in case of this key does not exist, this error is ignored.
| for _, ref := range references[1:] { | ||
| s = fmt.Sprintf("%s, %s (%s)", s, ref.ID, ref.Type) | ||
| } | ||
| log.Printf("Warning: references found in dashboard %s: %s", filePath, s) |
There was a problem hiding this comment.
Example of output here:
2022/08/17 13:01:10 Warning: references found in dashboard kibana/dashboard/visualizations_by_reference-82273ffe-6acc-4f2f-bbee-c1004abba63d.json: visualizations_by_reference-5e1a01ff-6f9a-41c1-b7ad-326472db42b6 (visualization), visualizations_by_reference-8287a5d5-1576-4f3a-83c4-444e9058439b (lens)
| "strconv" | ||
| ) | ||
|
|
||
| // EnvVarWarningsAsErrors is the environment variable name used to use warnings as errors |
There was a problem hiding this comment.
Please add a comment here mentioning that this mechanism will be removed if/when structured errors are supported.
| "testing" | ||
|
|
||
| "github.com/elastic/package-spec/code/go/internal/fspath" | ||
|
|
There was a problem hiding this comment.
Nit. Move package spec imports to the bottom of the import block.
There was a problem hiding this comment.
I thought goimports would reorder it and I didn't check that. Updated.
Thanks!
| "missing_data_stream": {}, | ||
| "custom_logs": {}, | ||
| "httpjson_input": {}, | ||
| "sql_input": {}, |
There was a problem hiding this comment.
Could we keep these packages here if we are not enabling errors as warnings here?
There was a problem hiding this comment.
If I keep those packages here, they are going to be checked twice.
In the new test suite where warnings are considered as errors too, if there is any error that is raised (and not expected), then the test should fail too as they do no expect any error ({}).
For instance, I added a new field foo in the manifest in one of them:
=== RUN TestValidateWarnings/custom_logs
validator_test.go:334:
Error Trace: validator_test.go:334
Error: "found 2 validation errors:
1. Warning: package with non-stable semantic version and active beta features (enabled in [../../../../test/packages/custom_logs]) can't be released as stable version.
2. file "../../../../test/packages/custom_logs/manifest.yml" is invalid: field (root): Additional property foo is not allowed
" should have 1 item(s), but has 2
Test: TestValidateWarnings/custom_logs
I could keep them in this test suite, in case the new test suite gets removed after structured errors are implemented. WDYT ? @jsoriano
There was a problem hiding this comment.
Ok, you are right, it is probably fine as it is. If we add structured errors we probably need to revisit these tests in any case.
| if err := common.DisableWarningsAsErrors(); err != nil { | ||
| require.NoError(t, err) | ||
| } |
There was a problem hiding this comment.
Nit. Run it in a defer after enabling this?
|
/test |
What does this PR do?
Add a new validator to check about dashboards using visualizations by reference instead of value. Example output from tests:
These references are obtained from the dashboard json file:
{ ... "id": "dashboard-id", "references": [ { "id": "visualizations_by_reference-5e1a01ff-6f9a-41c1-b7ad-326472db42b6", "name": "panel_0", "type": "visualization" }, { "id": "visualizations_by_reference-8287a5d5-1576-4f3a-83c4-444e9058439b", "name": "panel_1", "type": "visualization" } ], "type": "dashboard" }Why is it important?
This new check wants to highlight (using warning messages) those dashboards using still visualizations by reference.
As mentioned in #316:
Checklist
test/packagesthat prove my change is effective.versions/N/changelog.yml.Related issues