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 warning/error for dashboards including visualizations by reference #389

Merged
merged 18 commits into from
Aug 18, 2022

Conversation

mrodm
Copy link
Contributor

@mrodm mrodm commented Aug 12, 2022

What does this PR do?

Add a new validator to check about dashboards using visualizations by reference instead of value. Example output from tests:

=== RUN   TestValidateFile/visualizations_by_reference
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)

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:

The goal is to move to by value to have dashboards and visualizations directly bundled together as a unit which speeds up the installation of it in Fleet and also makes sure the visualizations do not all show up in the search (visualization library), only the dashboard.

Checklist

Related issues

@mrodm mrodm added the Team:Ecosystem Label for the Packages Ecosystem team label Aug 12, 2022
@mrodm mrodm self-assigned this Aug 12, 2022
@elasticmachine
Copy link

elasticmachine commented Aug 12, 2022

💚 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: 2022-08-18T11:26:10.190+0000

  • Duration: 7 min 59 sec

Test stats 🧪

Test Results
Failed 0
Passed 391
Skipped 2
Total 393

🤖 GitHub comments

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

  • /test : Re-trigger the build.

@elasticmachine
Copy link

elasticmachine commented Aug 12, 2022

🌐 Coverage report

Name Metrics % (covered/total) Diff
Packages 100.0% (7/7) 💚
Files 70.833% (17/24) 👍 2.652
Classes 78.788% (26/33) 👍 1.369
Methods 58.416% (59/101) 👍 0.521
Lines 43.876% (523/1192) 👎 -0.186
Conditionals 100.0% (0/0) 💚

@mrodm
Copy link
Contributor Author

mrodm commented Aug 12, 2022

/test

1 similar comment
@mrodm
Copy link
Contributor Author

mrodm commented Aug 16, 2022

/test

for _, objectFile := range objectFiles {
filePath := objectFile.Path()

objectReferences, err := objectFile.Values(`$.references[?(@.type=="visualization")]`)
Copy link
Contributor Author

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?

Copy link
Member

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 🙂

Copy link
Contributor Author

@mrodm mrodm Aug 17, 2022

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": {},
Copy link
Contributor Author

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 ?

Copy link
Member

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.

Copy link
Member

@jsoriano jsoriano Aug 17, 2022

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.

Copy link
Contributor Author

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

@mrodm mrodm changed the title Add warning/error for dashboards including visualizations by reference - WIP Add warning/error for dashboards including visualizations by reference Aug 16, 2022
@mrodm mrodm marked this pull request as ready for review August 16, 2022 16:42
@mrodm mrodm requested a review from a team as a code owner August 16, 2022 16:42
continue
}

anyReference(objectReferences, fsys.Path(filePath))
Copy link
Member

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.

Suggested change
anyReference(objectReferences, fsys.Path(filePath))
_, _ := anyReference(objectReferences, fsys.Path(filePath))

Or move error reporting here, something like this:

Suggested change
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, ", "))
}

Copy link
Contributor Author

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")]`)
Copy link
Member

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)
Copy link
Member

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)
Copy link
Member

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

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'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)
Copy link
Member

Choose a reason for hiding this comment

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

Wrap the error here.

Suggested change
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)
Copy link
Member

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.

Copy link
Contributor Author

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": {},
Copy link
Member

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.

mrodm added 3 commits August 17, 2022 11:33
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
Copy link
Contributor Author

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)
Copy link
Contributor Author

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)

@mrodm mrodm requested a review from jsoriano August 17, 2022 17:18
"strconv"
)

// EnvVarWarningsAsErrors is the environment variable name used to use warnings as errors
Copy link
Member

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"

Copy link
Member

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.

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 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": {},
Copy link
Member

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?

Copy link
Contributor Author

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

Copy link
Member

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.

Comment on lines 350 to 352
if err := common.DisableWarningsAsErrors(); err != nil {
require.NoError(t, err)
}
Copy link
Member

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed using defer

@mrodm
Copy link
Contributor Author

mrodm commented Aug 18, 2022

/test

jsoriano
jsoriano previously approved these changes Aug 18, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Team:Ecosystem Label for the Packages Ecosystem team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Warning when dashboard with visualisations by reference instead of value exist
3 participants