@@ -15,6 +15,7 @@ import (
15
15
"sort"
16
16
"time"
17
17
18
+ "github.com/fsnotify/fsnotify"
18
19
commonconfig "github.com/prometheus/common/config"
19
20
"github.com/prometheus/common/model"
20
21
promconfig "github.com/prometheus/prometheus/config"
@@ -36,6 +37,8 @@ type Manager struct {
36
37
scrapeManager * scrape.Manager
37
38
discoveryManager * discovery.Manager
38
39
enableNativeHistograms bool
40
+ watcher * fsnotify.Watcher
41
+ host component.Host
39
42
}
40
43
41
44
func NewManager (set receiver.Settings , cfg * Config , promCfg * promconfig.Config , enableNativeHistograms bool ) * Manager {
@@ -52,6 +55,8 @@ func NewManager(set receiver.Settings, cfg *Config, promCfg *promconfig.Config,
52
55
func (m * Manager ) Start (ctx context.Context , host component.Host , sm * scrape.Manager , dm * discovery.Manager ) error {
53
56
m .scrapeManager = sm
54
57
m .discoveryManager = dm
58
+ m .host = host
59
+
55
60
err := m .applyCfg ()
56
61
if err != nil {
57
62
m .settings .Logger .Error ("Failed to apply new scrape configuration" , zap .Error (err ))
@@ -61,23 +66,24 @@ func (m *Manager) Start(ctx context.Context, host component.Host, sm *scrape.Man
61
66
// the target allocator is disabled
62
67
return nil
63
68
}
64
- httpClient , err := m .cfg .ClientConfig .ToClient (ctx , host , m .settings .TelemetrySettings )
65
- if err != nil {
66
- m .settings .Logger .Error ("Failed to create http client" , zap .Error (err ))
67
- return err
68
- }
69
69
m .settings .Logger .Info ("Starting target allocator discovery" )
70
70
// immediately sync jobs, not waiting for the first tick
71
- savedHash , err := m .sync (uint64 (0 ), httpClient )
71
+ savedHash , err := m .sync (ctx , uint64 (0 ))
72
72
if err != nil {
73
73
m .settings .Logger .Error ("Failed to sync target allocator" , zap .Error (err ))
74
74
}
75
+
76
+ // Setup fsnotify watchers for TLS files
77
+ if err := m .setupTLSWatchers (ctx ); err != nil {
78
+ m .settings .Logger .Error ("Error setting up TLS watchers" , zap .Error (err ))
79
+ }
80
+
75
81
go func () {
76
82
targetAllocatorIntervalTicker := time .NewTicker (m .cfg .Interval )
77
83
for {
78
84
select {
79
85
case <- targetAllocatorIntervalTicker .C :
80
- hash , newErr := m .sync (savedHash , httpClient )
86
+ hash , newErr := m .sync (ctx , savedHash )
81
87
if newErr != nil {
82
88
m .settings .Logger .Error (newErr .Error ())
83
89
continue
@@ -95,14 +101,73 @@ func (m *Manager) Start(ctx context.Context, host component.Host, sm *scrape.Man
95
101
96
102
func (m * Manager ) Shutdown () {
97
103
close (m .shutdown )
104
+ if err := m .watcher .Close (); err != nil {
105
+ m .settings .Logger .Warn ("Error closing fsnotify watcher" , zap .Error (err ))
106
+ }
107
+ }
108
+
109
+ // setupTLSWatchers creates one fsnotify watcher and adds CAFile, CertFile, KeyFile
110
+ // so that we automatically re‐sync whenever they change.
111
+ func (m * Manager ) setupTLSWatchers (ctx context.Context ) error {
112
+ watcher , err := fsnotify .NewWatcher ()
113
+ if err != nil {
114
+ return fmt .Errorf ("failed to create fsnotify watcher: %w" , err )
115
+ }
116
+ m .watcher = watcher
117
+
118
+ addFile := func (path string ) {
119
+ if path == "" {
120
+ return
121
+ }
122
+ if err := watcher .Add (path ); err != nil {
123
+ m .settings .Logger .Error ("Failed to watch TLS file" , zap .String ("file" , path ), zap .Error (err ))
124
+ }
125
+ }
126
+
127
+ addFile (m .cfg .TLSSetting .CAFile )
128
+ addFile (m .cfg .TLSSetting .CertFile )
129
+ addFile (m .cfg .TLSSetting .KeyFile )
130
+
131
+ go func () {
132
+ for {
133
+ select {
134
+ case event , ok := <- watcher .Events :
135
+ if ! ok {
136
+ return
137
+ }
138
+ if event .Op & (fsnotify .Write | fsnotify .Create | fsnotify .Rename | fsnotify .Remove ) != 0 {
139
+ m .settings .Logger .Info ("TLS file changed; re-syncing" ,
140
+ zap .String ("file" , event .Name ), zap .String ("op" , event .Op .String ()))
141
+ if _ , err := m .sync (ctx , uint64 (0 )); err != nil {
142
+ m .settings .Logger .Error ("Failed to sync after TLS file change" , zap .Error (err ))
143
+ }
144
+ }
145
+ case err , ok := <- watcher .Errors :
146
+ if ! ok {
147
+ return
148
+ }
149
+ m .settings .Logger .Error ("fsnotify watcher error" , zap .Error (err ))
150
+ case <- m .shutdown :
151
+ return
152
+ }
153
+ }
154
+ }()
155
+
156
+ return nil
98
157
}
99
158
100
159
// sync request jobs from targetAllocator and update underlying receiver, if the response does not match the provided compareHash.
101
160
// baseDiscoveryCfg can be used to provide additional ScrapeConfigs which will be added to the retrieved jobs.
102
- func (m * Manager ) sync (compareHash uint64 , httpClient * http. Client ) (uint64 , error ) {
161
+ func (m * Manager ) sync (ctx context. Context , compareHash uint64 ) (uint64 , error ) {
103
162
m .settings .Logger .Debug ("Syncing target allocator jobs" )
104
163
m .settings .Logger .Debug ("endpoint" , zap .String ("endpoint" , m .cfg .Endpoint ))
105
164
165
+ httpClient , err := m .cfg .ClientConfig .ToClient (ctx , m .host , m .settings .TelemetrySettings )
166
+ if err != nil {
167
+ m .settings .Logger .Error ("Failed to create http client" , zap .Error (err ))
168
+ return 0 , err
169
+ }
170
+
106
171
scrapeConfigsResponse , err := getScrapeConfigsResponse (httpClient , m .cfg .Endpoint )
107
172
if err != nil {
108
173
m .settings .Logger .Error ("Failed to retrieve job list" , zap .Error (err ))
0 commit comments