Skip to content

Conversation

@majiayu000
Copy link

Fixes #2201

Summary

Adds support for configuring datasource correlations as code using the operator.

Changes

  • Add correlations field to GrafanaDatasourceSpec
  • Add GrafanaDatasourceCorrelation, GrafanaDatasourceCorrelationConfig, and GrafanaDatasourceCorrelationTransformation types
  • Implement correlation sync in datasource controller using Grafana correlations API
  • Match correlations by composite key (targetUID + label) for idempotent reconciliation
  • Update CRDs and documentation

Example Usage

apiVersion: grafana.integreatly.org/v1beta1
kind: GrafanaDatasource
metadata:
  name: prometheus
spec:
  datasource:
    name: Prometheus
    type: prometheus
    url: http://prometheus:9090
  correlations:
    - targetUID: loki-uid
      label: "View in Loki"
      description: "Logs correlation"
      config:
        field: "traceID"
        target:
          expr: "{traceID=\"${__value.raw}\"}"

🤖 Generated with Claude Code

Add support for datasource correlations as requested in issue grafana#2201.
This allows users to configure datasource correlations as code using
the operator.

Changes:
- Add GrafanaDatasourceCorrelation, GrafanaDatasourceCorrelationConfig,
  and GrafanaDatasourceCorrelationTransformation types
- Add Correlations field to GrafanaDatasourceSpec
- Update datasource controller to sync correlations with Grafana API
- Correlations are created, updated, and deleted via the Grafana
  datasources correlations API endpoints

The implementation follows the existing patterns used by other resources
in the operator (similar to AlertRuleGroups pattern).

Closes grafana#2201

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Signed-off-by: majiayu000 <[email protected]>
@CLAassistant
Copy link

CLAassistant commented Dec 30, 2025

CLA assistant check
All committers have signed the CLA.

@github-actions github-actions bot added documentation Issues relating to documentation, missing, non-clear etc. bugfix this PR fixes a bug labels Dec 30, 2025
Copy link
Collaborator

@weisdd weisdd left a comment

Choose a reason for hiding this comment

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

Thanks for the PR.

As with any other PR, we'd like to see tests covering the new functionality (e.g. adding, modifying, removing correlations, data conversions, etc), otherwise it'd be hard to conclude that any of those changes work as intended (manual testing is something we'd like to avoid).
For interactions with Grafana, externalGrafanaCr from controllers/suite_test.go can be re-used.

As a side note, LLMs tend to add comments everywhere, even to the blocks of code that are self-explanatory. It's better to remove those comments in places where they don't tell anything new (by new I mean, e.g., why something is implemented in a certain way). Example of a useless comment from this PR:

// Get existing correlations from Grafana
existingCorrelations, err := gClient.Datasources.GetCorrelationsBySourceUID(sourceUID)

@majiayu000
Copy link
Author

Thanks for your reminder. Will try now!

Allow updates by avoiding provisioned correlation creation.
Name: targetName,
Type: "prometheus",
Access: "proxy",
URL: "https://demo.promlabs.com",
Copy link
Collaborator

@weisdd weisdd Dec 31, 2025

Choose a reason for hiding this comment

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

Please, don't introduce any dependencies on external services. If you need to imitate a datasource, you can use httptest.NewServer. We have a helper to sets up a mux with json content (tk8s.GetJSONmux).

@majiayu000
Copy link
Author

@weisdd updated correlations: set Type on update requests and switched correlation tests to use a local httptest datasource URL (no external services). No tests run.

Copy link
Collaborator

@theSuess theSuess left a comment

Choose a reason for hiding this comment

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

It works as expected but I found some code smells I'd like to see fixed before we can merge this.

I'll defer to @weisdd or @Baarsgaard for reviewing the tests as I haven't had the time to get familiar with the recent changes to our test layout

}

existingByKey := make(map[string]*models.Correlation)
if existingCorrelations != nil {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This check is not needed. We can rely upon this being non-nil if the err is nil

Comment on lines 509 to 511
if len(cr.Spec.Correlations) == 0 {
return r.deleteAllCorrelations(ctx, gClient, sourceUID)
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Since this isn't a bulk API, the performance gains over the regular code path are minimal, so IMHO we can get rid of this

key := correlationKey(desired.TargetUID, desired.Label)
if existing, found := existingByKey[key]; found {
if err := r.updateCorrelationByUID(gClient, sourceUID, existing.UID, desired); err != nil {
return fmt.Errorf("updating correlation (targetUID=%s, label=%s): %w", desired.TargetUID, desired.Label, err)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Errors should be static in case we want to match on them at some point. Extra information like the UID/Label should go in a log entry if needed

}
} else {
if err := r.createCorrelation(gClient, sourceUID, desired); err != nil {
return fmt.Errorf("creating correlation (targetUID=%s, label=%s): %w", desired.TargetUID, desired.Label, err)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Errors should be static in case we want to match on them at some point. Extra information like the UID/Label should go in a log entry if needed

Type: models.CorrelationType(config.Type),
}

if config.Target != nil {
Copy link
Collaborator

Choose a reason for hiding this comment

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

No need for this to be conditional as the convertJSONToInterface can handle nil

result.Target = convertJSONToInterface(config.Target)
}

if len(config.Transformations) > 0 {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Again, no need for this to be conditional as an empty loop is skipped

Field: config.Field,
}

if config.Target != nil {
Copy link
Collaborator

Choose a reason for hiding this comment

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

No need for this to be conditional as the convertJSONToInterface can handle nil

result.Target = convertJSONToInterface(config.Target)
}

if len(config.Transformations) > 0 {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Again, no need for this to be conditional as an empty loop is skipped

@majiayu000
Copy link
Author

Thanks for the review @theSuess! I've addressed your feedback:

  • Removed the dedicated deleteAllCorrelations function.
  • Switched to static error messages and added logging for dynamic details.
  • Simplified convertJSONToInterface and loop handling.

One note: I retained the if existingCorrelations != nil check. This is because we ignore GetCorrelationsBySourceUIDNotFound errors (setting err to nil), which results in existingCorrelations being nil. Accessing .Payload without the check would cause a panic in that specific case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bugfix this PR fixes a bug documentation Issues relating to documentation, missing, non-clear etc.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support for correlations on datasources

4 participants