@@ -17,6 +17,7 @@ import (
1717 "github.com/prometheus/client_golang/prometheus/promauto"
1818 "github.com/prometheus/prometheus/config"
1919 "github.com/prometheus/prometheus/discovery"
20+ "github.com/prometheus/prometheus/model/labels"
2021 "github.com/prometheus/prometheus/model/rulefmt"
2122 "github.com/prometheus/prometheus/notifier"
2223 promRules "github.com/prometheus/prometheus/rules"
@@ -47,6 +48,9 @@ type DefaultMultiTenantManager struct {
4748 notifiers map [string ]* rulerNotifier
4849 notifiersDiscoveryMetrics map [string ]discovery.DiscovererMetrics
4950
51+ // Per-user externalLabels.
52+ userExternalLabels * userExternalLabels
53+
5054 // rules backup
5155 rulesBackupManager * rulesBackupManager
5256
@@ -62,7 +66,7 @@ type DefaultMultiTenantManager struct {
6266 syncRuleMtx sync.Mutex
6367}
6468
65- func NewDefaultMultiTenantManager (cfg Config , managerFactory ManagerFactory , evalMetrics * RuleEvalMetrics , reg prometheus.Registerer , logger log.Logger ) (* DefaultMultiTenantManager , error ) {
69+ func NewDefaultMultiTenantManager (cfg Config , limits RulesLimits , managerFactory ManagerFactory , evalMetrics * RuleEvalMetrics , reg prometheus.Registerer , logger log.Logger ) (* DefaultMultiTenantManager , error ) {
6670 ncfg , err := buildNotifierConfig (& cfg )
6771 if err != nil {
6872 return nil , err
@@ -92,6 +96,7 @@ func NewDefaultMultiTenantManager(cfg Config, managerFactory ManagerFactory, eva
9296 frontendPool : newFrontendPool (cfg , logger , reg ),
9397 ruleEvalMetrics : evalMetrics ,
9498 notifiers : map [string ]* rulerNotifier {},
99+ userExternalLabels : newUserExternalLabels (cfg .ExternalLabels , limits ),
95100 notifiersDiscoveryMetrics : notifiersDiscoveryMetrics ,
96101 mapper : newMapper (cfg .RulePath , logger ),
97102 userManagers : map [string ]RulesManager {},
@@ -146,6 +151,7 @@ func (r *DefaultMultiTenantManager) SyncRuleGroups(ctx context.Context, ruleGrou
146151
147152 r .removeNotifier (userID )
148153 r .mapper .cleanupUser (userID )
154+ r .userExternalLabels .remove (userID )
149155 r .lastReloadSuccessful .DeleteLabelValues (userID )
150156 r .lastReloadSuccessfulTimestamp .DeleteLabelValues (userID )
151157 r .configUpdatesTotal .DeleteLabelValues (userID )
@@ -183,12 +189,13 @@ func (r *DefaultMultiTenantManager) BackUpRuleGroups(ctx context.Context, ruleGr
183189func (r * DefaultMultiTenantManager ) syncRulesToManager (ctx context.Context , user string , groups rulespb.RuleGroupList ) {
184190 // Map the files to disk and return the file names to be passed to the users manager if they
185191 // have been updated
186- update , files , err := r .mapper .MapRules (user , groups .Formatted ())
192+ rulesUpdated , files , err := r .mapper .MapRules (user , groups .Formatted ())
187193 if err != nil {
188194 r .lastReloadSuccessful .WithLabelValues (user ).Set (0 )
189195 level .Error (r .logger ).Log ("msg" , "unable to map rule files" , "user" , user , "err" , err )
190196 return
191197 }
198+ externalLabels , externalLabelsUpdated := r .userExternalLabels .update (user )
192199
193200 existing := true
194201 manager := r .getRulesManager (user , ctx )
@@ -201,19 +208,26 @@ func (r *DefaultMultiTenantManager) syncRulesToManager(ctx context.Context, user
201208 return
202209 }
203210
204- if ! existing || update {
211+ if ! existing || rulesUpdated || externalLabelsUpdated {
205212 level .Debug (r .logger ).Log ("msg" , "updating rules" , "user" , user )
206213 r .configUpdatesTotal .WithLabelValues (user ).Inc ()
207- if update && existing {
214+ if rulesUpdated && existing {
208215 r .updateRuleCache (user , manager .RuleGroups ())
209216 }
210- err = manager .Update (r .cfg .EvaluationInterval , files , r . cfg . ExternalLabels , r .cfg .ExternalURL .String (), ruleGroupIterationFunc )
217+ err = manager .Update (r .cfg .EvaluationInterval , files , externalLabels , r .cfg .ExternalURL .String (), ruleGroupIterationFunc )
211218 r .deleteRuleCache (user )
212219 if err != nil {
213220 r .lastReloadSuccessful .WithLabelValues (user ).Set (0 )
214221 level .Error (r .logger ).Log ("msg" , "unable to update rule manager" , "user" , user , "err" , err )
215222 return
216223 }
224+ if externalLabelsUpdated {
225+ if err = r .notifierApplyExternalLabels (user , externalLabels ); err != nil {
226+ r .lastReloadSuccessful .WithLabelValues (user ).Set (0 )
227+ level .Error (r .logger ).Log ("msg" , "unable to update notifier" , "user" , user , "err" , err )
228+ return
229+ }
230+ }
217231
218232 r .lastReloadSuccessful .WithLabelValues (user ).Set (1 )
219233 r .lastReloadSuccessfulTimestamp .WithLabelValues (user ).SetToCurrentTime ()
@@ -348,6 +362,19 @@ func (r *DefaultMultiTenantManager) getOrCreateNotifier(userID string, userManag
348362 return n .notifier , nil
349363}
350364
365+ func (r * DefaultMultiTenantManager ) notifierApplyExternalLabels (userID string , externalLabels labels.Labels ) error {
366+ r .notifiersMtx .Lock ()
367+ defer r .notifiersMtx .Unlock ()
368+
369+ n , ok := r .notifiers [userID ]
370+ if ! ok {
371+ return fmt .Errorf ("notifier not found" )
372+ }
373+ cfg := * r .notifierCfg // Copy it
374+ cfg .GlobalConfig .ExternalLabels = externalLabels
375+ return n .applyConfig (& cfg )
376+ }
377+
351378func (r * DefaultMultiTenantManager ) getCachedRules (userID string ) ([]* promRules.Group , bool ) {
352379 r .ruleCacheMtx .RLock ()
353380 defer r .ruleCacheMtx .RUnlock ()
@@ -402,6 +429,7 @@ func (r *DefaultMultiTenantManager) Stop() {
402429
403430 // cleanup user rules directories
404431 r .mapper .cleanup ()
432+ r .userExternalLabels .cleanup ()
405433}
406434
407435func (* DefaultMultiTenantManager ) ValidateRuleGroup (g rulefmt.RuleGroup ) []error {
0 commit comments