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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Elasticsearch: Handle multiple annotation structures #66762

Merged
merged 1 commit into from Apr 20, 2023

Conversation

gabor
Copy link
Contributor

@gabor gabor commented Apr 18, 2023

since the elastic annotation handling was migrated from Angular to react in #49529 , we have a bug in how they are handled. this PR fixes the problem.

in Grafana versions up to 9.0.9 the annotation editor for elastic was angular based, and this is how it worked:
you entered a lucene query, and it stored in the annotation-structure like this (there are other fields there too, but they are not important here, i'm ignoring them):

{
    "query":"level:error"
}

this is stored in the dashboard-json, and then the datasource's annotationQuery method reads it at:

const queryString = annotation.query;

NOTE: an unrelated thing that will be important here is: we are doing custom annotation-handling here. Grafana is able to do a generic annotation-handling-thing (for other datasources), and in such a case it creates the JSON like this:

{
    "target": {
        "query": "level:error"
    }
}

(it basically stores a whole Query-object at annotation.target.

with Grafana 9.1.0, we switched for elastic-annotations to React, and wrote custom code to handle this. we tried to keep the JSON shape the same, so we still write to the top-level "query" field:

this worked well, but there's a complication. with this being in React, some other generic Grafana code gets activated, that tries to "migrat" from the OLD toplevel-query-format to the NEW inside-target-format:

prepareAnnotation: (json: any) => {
if (isString(json?.query)) {
const { query, ...rest } = json;
return {
...rest,
target: {
refId: 'annotation_query',
query,
},
mappings: {},
};
}
return json as AnnotationQuery;
},

this always happens whenever an annotation-query is opened for editing. and when this happens, the shape of our JSON changes, query becomes target.query. and when this happens, the datasource.annotationQuery function is not able to do it's work, because it expect the value at query, not at target.query.

also, note that the elastic AnnotationQueryEditor, when editing an existing annotation, READS from target.query, but WRITES to query:

value={annotation.target?.query}
onChange={(query) => {
onAnnotationChange({
...annotation,
query,
});
}}

(this works because when an annotation is opened for editing, that generic grafana migration code happens, and the annotation received by the react component is already in the NEW format)

but this causes problems too. imagine that we have this JSON in dashboard-json, that we open for editing:

{
    "query": "level:error"
}

now, in the react annotation-editor we will see level:error in the text-field. we change it to level:info, and save the JSON.
but, the JSON stored to dashboard-json will be this:

{
    "query": "level:info",
   "target": {
        "query":"level:error"
   }
}

(the automatic grafana migration thing happens when the form is opened for editing, not when the content is saved at the end)

so, to handle all this, this pull request does two changes:

  1. we change datasource.annotationQuery to look for both places (query for old annotations, and target.query for new annotations).. when both exist, we take the old-format one, because of the bug i described just above.. i think it's better to prefer the old value in this scenario.
  2. we change the elastic AnnotationQueryEditor to write to target.query

this should cover all the possible cases.

how to test:
(technically we should start with a locally built grafana 9.0.9, but i was not able to build it locally anymore, it complained about some yarn-thing, so i used grafana-in-docker instead)

  1. use grafana 9.0.9 with something like make devenv=grafana grafana_version=9.0.9
  2. create an annotation. if you are using devenv-elastic, an easy-to-use one is:
    • query: counter:42.
    • set the "text" field to "counter"
  3. verify that you see a single line in the dashboard panel, and when you mouseover to the value of the annotation, it says 42.
  4. verify that the structure of the json is top-level-query, not in-target-query
  5. copy the whole dashboard-json out into a file on your computer.
  6. now switch to this PR on your computer
  7. we need to import the dashboard into your computer, but the UID of the elastic-datasource might be different, so do this:
    • find out the UID of your elastic datasource on your computer. for example, go to edit-datasource-config, and the UID is the last part of the URL.
    • choose import dashboard JSON, and afterwards modify the JSON by correcting the datasource-UID
    • at this point the dashboard should work correctly.
  8. refresh the dashboard. verify that you see a single line in the dashboard panel. (so that new code works well with old-format)
  9. switch to grafana main-branch
  10. edit the dashboard-annotation, set the lucene query to counter:43
  11. save the dashboard
  12. check the dashboard-json, verify that it has a top-level counter:43 and an inside-target counter:42.
  13. switch to this PR
  14. refresh the dashboard
  15. verify that you see a single line in the dashboard panel, and when you mouseover to the value of the annotation, it says 43. (so that the new code shows .querywhen both .query and .target.query exists.
  16. edit the annotation, set the lucene query to counter:44
  17. save the dashboard
  18. verify in the dashboard-json that .target.query exists with counter:44, and no .query exists.
  19. refresh the dashboard
  20. verify that you see a single line in the dashboard panel, and when you mouseover to the value of the annotation, it says 44. (so that the new code is able to read .target.query too)
  21. delete the existing annotation, save dashboard, add a new annotation, for example set the query to counter:45. save dashboard, and verify in the JSON that .target.query is set to counter:45, and .query does not exist. refresh dashboard, verify the annotation says 45 (so a newly created annotation works too)
  22. done 馃槃

(fixes #61107)

@github-actions
Copy link
Contributor

Backend code coverage report for PR #66762
No changes

@github-actions
Copy link
Contributor

Frontend code coverage report for PR #66762

Plugin Main PR Difference
elasticsearch 78.5% 78.43% -.07%

@gabor gabor marked this pull request as ready for review April 18, 2023 14:28
@gabor gabor requested a review from a team as a code owner April 18, 2023 14:28
@gabor gabor added add to changelog backport v9.5.x Bot will automatically open backport PR labels Apr 18, 2023
@gabor gabor added this to the 9.5.x milestone Apr 18, 2023
@gabor gabor changed the title elastic: fix annotation handling Elasticsearch: Fix annotation handling Apr 18, 2023
@gabor gabor changed the title Elasticsearch: Fix annotation handling Elasticsearch: Handle multiple annotation structures Apr 18, 2023
@gabor gabor requested a review from ivanahuckova April 18, 2023 14:29
@@ -23,9 +23,15 @@ export function ElasticsearchAnnotationsQueryEditor(props: Props) {
<ElasticSearchQueryField
value={annotation.target?.query}
onChange={(query) => {
const currentTarget = annotation.target ?? { refId: 'annotation_query' };
Copy link
Contributor Author

Choose a reason for hiding this comment

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

using this refId-value is inspired by

@grafanabot
Copy link
Contributor

Hello @gabor!
Backport pull requests need to be either:

  • Pull requests which address bugs,
  • Urgent fixes which need product approval, in order to get merged,
  • Docs changes.

Please, if the current pull request addresses a bug fix, label it with the type/bug label.
If it already has the product approval, please add the product-approved label. For docs changes, please add the type/docs label.
If the pull request modifies CI behaviour, please add the type/ci label.
If none of the above applies, please consider removing the backport label and target the next major/minor release.
Thanks!

Copy link
Member

@ivanahuckova ivanahuckova left a comment

Choose a reason for hiding this comment

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

LGTM! Thank you for the extensive how to test this. Helped a ltop with making sure it works as expeted.

@gabor gabor merged commit 82ac2ba into main Apr 20, 2023
41 checks passed
@gabor gabor deleted the gabor/elastic-annotation-fix branch April 20, 2023 09:42
grafanabot pushed a commit that referenced this pull request Apr 20, 2023
elastic: fix annotation handling
(cherry picked from commit 82ac2ba)
gabor added a commit that referenced this pull request Apr 20, 2023
Elasticsearch: Handle multiple annotation structures (#66762)

elastic: fix annotation handling
(cherry picked from commit 82ac2ba)

Co-authored-by: G谩bor Farkas <gabor.farkas@gmail.com>
mdvictor pushed a commit that referenced this pull request Apr 21, 2023
@guicaulada guicaulada modified the milestones: 9.5.x, 9.5.0 Apr 24, 2023
@zerok zerok modified the milestones: 9.5.0, 10.0.0 May 4, 2023
@zerok zerok modified the milestones: 10.0.0, 10.0.0-preview May 31, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Annotations Break After Viewing Then Saving
5 participants