-
Notifications
You must be signed in to change notification settings - Fork 338
feat(secrets): Add new secrets management package #797
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
base: main
Are you sure you want to change the base?
Conversation
cf118e6 to
24ec0f7
Compare
1882b4c to
694bf26
Compare
Signed-off-by: Henrique Spanoudis Matulis <[email protected]>
|
@pintohutch @bernot-dev @bwplotka PTAL, should be ready! |
bwplotka
left a comment
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.
Thanks, amazing work!
Generally, it's great - maybe first big question is if we want SecretField to store so much state or do we want to move some of this state to manager (or and providers). This might be more composable and easier to reason about. Prometheus discovery is doing some of this. I mentioned one idea (A) 1 and 2 in comments.
However, it's not a blocker, even in the current state, I would say we could try this out on Prometheus (and someone might try on AM side!). What matters is that I see a clean YAML surface format, clean code, idiomatic struct reflection to find fields and healthy amount of test, so amazing! With this we can iterate.
No matter if you want to try (A) or skip it for now, I think I would try to check before merging:
- Limit the global variable spread (https://github.com/prometheus/common/pull/797/files#r2414525826)
- If we can skip validator and timeout based validator state per field, if it's YAGNI (see https://github.com/prometheus/common/pull/797/files#r2414536374)
- Use testable examples: https://github.com/prometheus/common/pull/797/files#r2414499436
Then I would like to review in more depth the manager code, but generally... we could start with this! 🎉 Thanks!
| } | ||
|
|
||
| type SecretFieldSettings struct { | ||
| RefreshInterval time.Duration `yaml:"refreshInterval,omitempty"` |
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.
question: Hm, good question if refresh is really relevant to all providers... E.g it's not for inline and.. also not for file based? Refreshing in general is a key concept here so we at least needs a good commentary in the code for this field on how it suppose to be implemented and consumed?
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.
Hmm I guess it is not relevant to inline but could be relevant to file. Although we might want to think about listening to filesystem changes for a file based provider instead, but I found that to complicate the API quite a bit...
|
|
||
| In your configuration struct, use the `secrets.SecretField` type for any fields that should contain secrets. | ||
|
|
||
| ```go |
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.
Sustainability suggestion (non-blocking):
This is well documented, thanks!
However, it might be even better if we would replace the "How to use" section with a single or set of buildable examples as per https://go.dev/blog/examples .. and commentary in the key public code structures.
secrets/field.go
Outdated
| } | ||
| } | ||
|
|
||
| func (s *SecretField) Get() string { |
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.
Nice! This make caller unaware of errors which can be both great and bad, depending what we do. I think it would be great to test out in this state 👍🏽
One alternative to make things leaner on SecretField (decompose responsibilities and state), would be if SecretField would allow storing (atomically) and getting secret value string. This way we could have common package global free and SecretField manager free (manager could be responsible to set correct things in config).
It could be even provider agnostic/free combined with https://github.com/prometheus/common/pull/797/files#r2414589581 idea (and avoid globals in this package)
| return manager, nil | ||
| } | ||
|
|
||
| func (m *Manager) registerMetrics(r prometheus.Registerer) { |
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.
Thanks for metrics, super important!
secrets/manager.go
Outdated
| return secret.secret | ||
| } | ||
|
|
||
| func (m *Manager) triggerRefresh(s *SecretField) { |
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.
note to myself: Manager code needs deeper review, quite many locks 🙈 prone to errors, but perhaps needed.
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.
yeah it was very complicated before with the verify logic, I think it should be a bit simpler now.. Still need some cleanup there, but should be more readable
secrets/field.go
Outdated
| return nil | ||
| } | ||
|
|
||
| func (s *SecretField) UnmarshalYAML(unmarshal func(interface{}) error) error { |
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.
Have we considered doing plainSecret thingy, but then unmarshal to any or map[string]any and let manager.ApplyConfig do the final parsing? This would make SecretField leaner and manager/provider agnostic (together with setter/getter for secret values https://github.com/prometheus/common/pull/797/files#r2414554264)
…terfaces Signed-off-by: Henrique Matulis <[email protected]>
Remove all verifier logic, as it is not needed for now. This removes the interface and all related logic for validating secrets before they are activated. This simplifies the secret management lifecycle by removing the pending/verification state. Fetched secrets will now become active immediately.
Prevent global propagation Remove unused default value
bwplotka
left a comment
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.
Looks solid, thanks!
Still some few comments to address, but overall looks close to be merged!
| type Field struct { | ||
| providerName string | ||
| providerConfig ProviderConfig | ||
| manager *Manager |
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.
idea during 1:1, it would be nice to remove bi-direction and have lazy refresh perhaps
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.
Alternatively just use interface for Refresh()
| * `ProviderConfig`: An interface for configuring a secret provider. It includes methods to create a new `Provider` instance and to clone the configuration. | ||
| * `ProviderConfigId`: An interface for uniquely identifying a `ProviderConfig` instance. | ||
| * `Provider`: An interface for fetching secrets from a specific source (e.g., inline string, file on disk). The package comes with built-in providers, and new ones can be registered. | ||
| * `Manager`: A component that discovers all `SecretField` instances within a configuration struct, manages their lifecycle, and handles periodic refreshing of secrets. |
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.
Can we put those into comments? Especially for library, unlikely anyone will notice README. In go it's either doc.go or code structure for godocs.
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 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.
Signed-off-by: Henrique Matulis <[email protected]>
This PR introduces a new package, secrets, to prometheus/common. This package provides a unified way to handle secrets within configuration files for Prometheus and its ecosystem components. It is designed to be extensible and observable. See the proposal here