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

User-configurable overrides: add optional check to block API requests that conflict with runtime overrides #3180

Merged
merged 8 commits into from
Jan 26, 2024

Conversation

kvrhdn
Copy link
Member

@kvrhdn kvrhdn commented Nov 27, 2023

What this PR does:

Adds an optional check that refuses setting user-configurable overrides if the tenant already has conflicting settings in runtime overrides. This is a crude check, ti doesn't check field-by-field but instead checks if any of the possible overrides are set in runtime overrides.

This check can be enabled with:

overrides:
  user_configurable_overrides:
    enabled: true
    client:
      # ...
    api:
      check_for_conflicting_runtime_overrides: true

Callers can skip/bypass it by adding the query paramter ?skip-conflicting-runtime-check=true.

Which issue(s) this PR fixes:
Fixes #

Checklist

  • Tests updated
  • Documentation added
  • CHANGELOG.md updated - the order of entries should be [CHANGE], [FEATURE], [ENHANCEMENT], [BUGFIX]

@kvrhdn kvrhdn force-pushed the kvrhdn/overrides-api-block-if-set branch from bad37f0 to 880ed9c Compare December 5, 2023 17:34
Copy link
Contributor

@zalegrala zalegrala left a comment

Choose a reason for hiding this comment

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

Looks like a nice start. Just a few notes.

modules/overrides/userconfigurable/api/http.go Outdated Show resolved Hide resolved
modules/overrides/userconfigurable/api/http.go Outdated Show resolved Hide resolved

MetricsGenerator *LimitsMetricsGenerator `json:"metrics_generator,omitempty"`
MetricsGenerator LimitsMetricsGenerator `yaml:"metrics_generator,omitempty" json:"metrics_generator,omitempty"`
Copy link
Contributor

Choose a reason for hiding this comment

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

What's the change here by becoming a non-pointer on the struct? I'm not sure I follow.

Copy link
Member Author

Choose a reason for hiding this comment

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

I did this change to simplify comparing structs. If each child-struct is a pointer it becomes nearly impossible to compare two empty structs as the pointer values will never match.

For example, the following two structs are both empty/unset:

Limits {
  MetricsGenerator: &LimitsMetricsGenerator{
    DisableCollection: nil,
  },
}
Limits {
  MetricsGenerator: nil,
}

But they are not equal. Having every child-struct behind a pointer creates so many variations of 'empty'.

Making them all values instead of pointers avoids this situation + it doesn't change parsing (I added a test to check).

@@ -18,6 +18,8 @@ type Service interface {
Interface
}

type Level string
Copy link
Contributor

Choose a reason for hiding this comment

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

I didn't see this used, did I miss it?

Copy link
Member Author

Choose a reason for hiding this comment

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

It was used in a previous iteration of the API, nice catch!

@@ -159,6 +181,10 @@ func writeError(w http.ResponseWriter, err error) {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if ok := errors.Is(err, errConflictingRuntimeOverrides); ok {
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this allocate ok or is the compiler smart enough to skip it?

Copy link
Member Author

Choose a reason for hiding this comment

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

Huh, I'm not entirely sure why I created a temporary variable here 😅 I probably used a different function before that returned 2 values instead of juts a boolean 🤷🏻

Again nice catch! Surprised the linter didn't pick this up

func TestOverrides_AssertUserConfigurableOverridesAreASubsetOfRuntimeOverrides(t *testing.T) {
userConfigurableOverrides := client.Limits{}

err := gofakeit.Struct(&userConfigurableOverrides)
Copy link
Contributor

@zalegrala zalegrala Jan 25, 2024

Choose a reason for hiding this comment

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

👍 This is neat.

Copy link
Contributor

@zalegrala zalegrala left a comment

Choose a reason for hiding this comment

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

This looks good to me. Looking forward to it.

@kvrhdn kvrhdn merged commit 7d9defd into grafana:main Jan 26, 2024
14 checks passed
@kvrhdn kvrhdn deleted the kvrhdn/overrides-api-block-if-set branch January 26, 2024 15:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

4 participants