@@ -7,6 +7,8 @@ use std::{
7
7
thread,
8
8
} ;
9
9
10
+ use crate :: config:: ComponentConfig ;
11
+
10
12
use notify:: { recommended_watcher, EventKind , RecursiveMode } ;
11
13
12
14
use crate :: Error ;
@@ -67,9 +69,18 @@ pub fn spawn_thread<'a>(
67
69
watcher_conf : WatcherConfig ,
68
70
signal_tx : crate :: signal:: SignalTx ,
69
71
config_paths : impl IntoIterator < Item = & ' a PathBuf > + ' a ,
72
+ component_configs : Vec < ComponentConfig > ,
70
73
delay : impl Into < Option < Duration > > ,
71
74
) -> Result < ( ) , Error > {
72
- let config_paths: Vec < _ > = config_paths. into_iter ( ) . cloned ( ) . collect ( ) ;
75
+ let mut config_paths: Vec < _ > = config_paths. into_iter ( ) . cloned ( ) . collect ( ) ;
76
+ let mut component_config_paths: Vec < _ > = component_configs
77
+ . clone ( )
78
+ . into_iter ( )
79
+ . flat_map ( |p| p. config_paths . clone ( ) )
80
+ . collect ( ) ;
81
+
82
+ config_paths. append ( & mut component_config_paths) ;
83
+
73
84
let delay = delay. into ( ) . unwrap_or ( CONFIG_WATCH_DELAY ) ;
74
85
75
86
// Create watcher now so not to miss any changes happening between
@@ -92,6 +103,12 @@ pub fn spawn_thread<'a>(
92
103
93
104
debug ! ( message = "Consumed file change events for delay." , delay = ?delay) ;
94
105
106
+ let component_keys: Vec < _ > = component_configs
107
+ . clone ( )
108
+ . into_iter ( )
109
+ . flat_map ( |p| p. contains ( & event. paths ) )
110
+ . collect ( ) ;
111
+
95
112
// We need to read paths to resolve any inode changes that may have happened.
96
113
// And we need to do it before raising sighup to avoid missing any change.
97
114
if let Err ( error) = watcher. add_paths ( & config_paths) {
@@ -102,9 +119,17 @@ pub fn spawn_thread<'a>(
102
119
debug ! ( message = "Reloaded paths." ) ;
103
120
104
121
info ! ( "Configuration file changed." ) ;
105
- _ = signal_tx. send ( crate :: signal:: SignalTo :: ReloadFromDisk ) . map_err ( |error| {
106
- error ! ( message = "Unable to reload configuration file. Restart Vector to reload it." , cause = %error)
107
- } ) ;
122
+ if !component_keys. is_empty ( ) {
123
+ info ! ( "Component {:?} configuration changed." , component_keys) ;
124
+ _ = signal_tx. send ( crate :: signal:: SignalTo :: ReloadComponents ( component_keys) ) . map_err ( |error| {
125
+ error ! ( message = "Unable to reload component configuration. Restart Vector to reload it." , cause = %error)
126
+ } ) ;
127
+ } else {
128
+ _ = signal_tx. send ( crate :: signal:: SignalTo :: ReloadFromDisk )
129
+ . map_err ( |error| {
130
+ error ! ( message = "Unable to reload configuration file. Restart Vector to reload it." , cause = %error)
131
+ } ) ;
132
+ }
108
133
} else {
109
134
debug ! ( message = "Ignoring event." , event = ?event)
110
135
}
@@ -158,6 +183,7 @@ fn create_watcher(
158
183
mod tests {
159
184
use super :: * ;
160
185
use crate :: {
186
+ config:: ComponentKey ,
161
187
signal:: SignalRx ,
162
188
test_util:: { temp_dir, temp_file, trace_init} ,
163
189
} ;
@@ -174,6 +200,75 @@ mod tests {
174
200
)
175
201
}
176
202
203
+ async fn test_component_reload (
204
+ file : & mut File ,
205
+ expected_component : & ComponentKey ,
206
+ timeout : Duration ,
207
+ mut receiver : SignalRx ,
208
+ ) -> bool {
209
+ file. write_all ( & [ 0 ] ) . unwrap ( ) ;
210
+ file. sync_all ( ) . unwrap ( ) ;
211
+
212
+ matches ! (
213
+ tokio:: time:: timeout( timeout, receiver. recv( ) ) . await ,
214
+ Ok ( Ok ( crate :: signal:: SignalTo :: ReloadComponents ( components) ) ) if components. contains( expected_component)
215
+ )
216
+ }
217
+
218
+ #[ tokio:: test]
219
+ async fn component_update ( ) {
220
+ trace_init ( ) ;
221
+
222
+ let delay = Duration :: from_secs ( 3 ) ;
223
+ let dir = temp_dir ( ) . to_path_buf ( ) ;
224
+ let watcher_conf = WatcherConfig :: RecommendedWatcher ;
225
+ let component_file_path = vec ! [ dir. join( "tls.cert" ) , dir. join( "tls.key" ) ] ;
226
+ let http_component = ComponentKey :: from ( "http" ) ;
227
+
228
+ std:: fs:: create_dir ( & dir) . unwrap ( ) ;
229
+
230
+ let mut component_files: Vec < std:: fs:: File > = component_file_path
231
+ . iter ( )
232
+ . map ( |file| File :: create ( file) . unwrap ( ) )
233
+ . collect ( ) ;
234
+ let component_config =
235
+ ComponentConfig :: new ( component_file_path. clone ( ) , http_component. clone ( ) ) ;
236
+
237
+ let ( signal_tx, signal_rx) = broadcast:: channel ( 128 ) ;
238
+ spawn_thread (
239
+ watcher_conf,
240
+ signal_tx,
241
+ & [ dir] ,
242
+ vec ! [ component_config] ,
243
+ delay,
244
+ )
245
+ . unwrap ( ) ;
246
+
247
+ let signal_rx = signal_rx. resubscribe ( ) ;
248
+ let signal_rx2 = signal_rx. resubscribe ( ) ;
249
+
250
+ if !test_component_reload (
251
+ & mut component_files[ 0 ] ,
252
+ & http_component,
253
+ delay * 5 ,
254
+ signal_rx,
255
+ )
256
+ . await
257
+ {
258
+ panic ! ( "Test timed out" ) ;
259
+ }
260
+
261
+ if !test_component_reload (
262
+ & mut component_files[ 1 ] ,
263
+ & http_component,
264
+ delay * 5 ,
265
+ signal_rx2,
266
+ )
267
+ . await
268
+ {
269
+ panic ! ( "Test timed out" ) ;
270
+ }
271
+ }
177
272
#[ tokio:: test]
178
273
async fn file_directory_update ( ) {
179
274
trace_init ( ) ;
@@ -187,7 +282,7 @@ mod tests {
187
282
let mut file = File :: create ( & file_path) . unwrap ( ) ;
188
283
189
284
let ( signal_tx, signal_rx) = broadcast:: channel ( 128 ) ;
190
- spawn_thread ( watcher_conf, signal_tx, & [ dir] , delay) . unwrap ( ) ;
285
+ spawn_thread ( watcher_conf, signal_tx, & [ dir] , vec ! [ ] , delay) . unwrap ( ) ;
191
286
192
287
if !test ( & mut file, delay * 5 , signal_rx) . await {
193
288
panic ! ( "Test timed out" ) ;
@@ -204,7 +299,7 @@ mod tests {
204
299
let watcher_conf = WatcherConfig :: RecommendedWatcher ;
205
300
206
301
let ( signal_tx, signal_rx) = broadcast:: channel ( 128 ) ;
207
- spawn_thread ( watcher_conf, signal_tx, & [ file_path] , delay) . unwrap ( ) ;
302
+ spawn_thread ( watcher_conf, signal_tx, & [ file_path] , vec ! [ ] , delay) . unwrap ( ) ;
208
303
209
304
if !test ( & mut file, delay * 5 , signal_rx) . await {
210
305
panic ! ( "Test timed out" ) ;
@@ -225,7 +320,7 @@ mod tests {
225
320
let watcher_conf = WatcherConfig :: RecommendedWatcher ;
226
321
227
322
let ( signal_tx, signal_rx) = broadcast:: channel ( 128 ) ;
228
- spawn_thread ( watcher_conf, signal_tx, & [ sym_file] , delay) . unwrap ( ) ;
323
+ spawn_thread ( watcher_conf, signal_tx, & [ sym_file] , vec ! [ ] , delay) . unwrap ( ) ;
229
324
230
325
if !test ( & mut file, delay * 5 , signal_rx) . await {
231
326
panic ! ( "Test timed out" ) ;
@@ -246,7 +341,7 @@ mod tests {
246
341
let mut file = File :: create ( & file_path) . unwrap ( ) ;
247
342
248
343
let ( signal_tx, signal_rx) = broadcast:: channel ( 128 ) ;
249
- spawn_thread ( watcher_conf, signal_tx, & [ sub_dir] , delay) . unwrap ( ) ;
344
+ spawn_thread ( watcher_conf, signal_tx, & [ sub_dir] , vec ! [ ] , delay) . unwrap ( ) ;
250
345
251
346
if !test ( & mut file, delay * 5 , signal_rx) . await {
252
347
panic ! ( "Test timed out" ) ;
0 commit comments