You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Currently, various notifiers (Email, WeChat, etc.) implement their own logic to retrieve secrets. When *_file configuration options are used (e.g., smtp_auth_password_file), the standard implementation often uses os.ReadFile directly inside the Notify loop.
This leads to:
Performance overhead: Disk I/O is performed for every single notification.
Code duplication: Similar file-reading logic exists across different packages.
Lack of reactivity: There is no centralized way to handle secret rotation (e.g., Kubernetes Secrets updates) without restarting the process or relying on the OS filesystem cache.
2. Proposed Solution
Introduce a SecretFileProvider (or SecretCache) component. This component will be responsible for reading, caching, and watching files containing secrets.
Location
I recommend placing this in a new package pkg/secrets or util/secrets to avoid circular dependencies with the config package, as config is imported by almost everyone.
3. API Design
We define a strategy enum to handle the different use cases requested:
package secrets
typeRefreshStrategyintconst (
// StrategyReadAlways performs an os.ReadFile every time. // Useful for development or filesystems that don't support watches.StrategyReadAlwaysRefreshStrategy=iota// StrategyReadOnce reads the file on the first request and caches it // until the process restarts or the provider is reset.StrategyReadOnce// StrategyFileWatch reads the file and sets up an fsnotify watcher.// The cache is updated automatically when the file changes.StrategyFileWatch
)
typeProviderstruct {
// ... internal fields (mutex, cache map, fsnotify watcher)
}
// NewProvider creates a new secret provider. // It initializes the fsnotify watcher if needed.funcNewProvider(logger*slog.Logger) (*Provider, error) { ... }
// GetString retrieves the secret content.func (p*Provider) GetString(filenamestring, strategyRefreshStrategy) (string, error) { ... }
// Close cleans up watchers.func (p*Provider) Close() error { ... }
4. Implementation Details
The Get Logic
The GetString method will function as follows:
Check Cache: Acquire a read lock. If the file is in the map and the strategy is ReadOnce or FileWatch, return the cached value.
Cache Miss / ReadAlways:
Acquire write lock.
Read file via os.ReadFile.
Store in cache.
Setup Watch (if StrategyFileWatch):
If the file is not already being watched, add it to the fsnotify watcher.
Kubernetes Note: Special care must be taken to watch the directory or handle the ..data symlink swaps common in Kubernetes Secrets, otherwise fsnotify might lose track of the file after an atomic update.
Thread Safety
Since Notify calls happen concurrently, the Provider must use sync.RWMutex to protect the internal cache map.
5. Usage Example
Here is how the implementation in email.go would change.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
DISCLAIMER this proposal is AI generated
1. Problem Statement
Currently, various notifiers (Email, WeChat, etc.) implement their own logic to retrieve secrets. When
*_fileconfiguration options are used (e.g.,smtp_auth_password_file), the standard implementation often usesos.ReadFiledirectly inside theNotifyloop.This leads to:
2. Proposed Solution
Introduce a
SecretFileProvider(orSecretCache) component. This component will be responsible for reading, caching, and watching files containing secrets.Location
I recommend placing this in a new package
pkg/secretsorutil/secretsto avoid circular dependencies with the config package, as config is imported by almost everyone.3. API Design
We define a strategy enum to handle the different use cases requested:
4. Implementation Details
The
GetLogicThe
GetStringmethod will function as follows:ReadOnceorFileWatch, return the cached value.os.ReadFile.StrategyFileWatch):fsnotifywatcher...datasymlink swaps common in Kubernetes Secrets, otherwisefsnotifymight lose track of the file after an atomic update.Thread Safety
Since
Notifycalls happen concurrently, the Provider must usesync.RWMutexto protect the internal cache map.5. Usage Example
Here is how the implementation in email.go would change.
Before:
After:
Assuming the
Emailstruct now holds a reference to the global or context-injectedSecretProvider:6. Lifecycle Management
The
SecretProvidershould ideally be initialized in main.go or thecoordinatorand passed down to thepipelineand subsequently to the Notifiers.When Alertmanager reloads its configuration:
SecretProvidercould be closed (stopping all watches).All reactions