-
Notifications
You must be signed in to change notification settings - Fork 72
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 warning/error for dashboards including visualizations by reference #389
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this the right way to check for visualizations included by reference in a dashboard?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess that the best will be to have some test cases 🙂
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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)
@@ -90,6 +90,7 @@ func TestValidateFile(t *testing.T) { | |||
"field services.docker-custom-agent: Must not validate the schema (not)", | |||
}, | |||
}, | |||
"visualizations_by_reference": {}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These conversion errors are now returned and the callers can handle them properly.
@@ -90,6 +90,7 @@ func TestValidateFile(t *testing.T) { | |||
"field services.docker-custom-agent: Must not validate the schema (not)", | |||
}, | |||
}, | |||
"visualizations_by_reference": {}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit. Move package spec imports to the bottom of the import block.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought goimports would reorder it and I didn't check that. Updated.
Thanks!
@@ -28,9 +29,6 @@ func TestValidateFile(t *testing.T) { | |||
"deploy_terraform": {}, | |||
"time_series": {}, | |||
"missing_data_stream": {}, | |||
"custom_logs": {}, | |||
"httpjson_input": {}, | |||
"sql_input": {}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we keep these packages here if we are not enabling errors as warnings here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit. Run it in a defer
after enabling this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed using defer
/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:
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/packages
that prove my change is effective.versions/N/changelog.yml
.Related issues