2
2
from pathlib import Path
3
3
from threading import Timer
4
4
from typing import Any , Dict , List , Optional
5
+ from urllib .parse import quote
5
6
6
7
from knot_resolver .constants import WATCHDOG_LIB
7
8
from knot_resolver .controller .registered_workers import command_registered_workers
8
9
from knot_resolver .datamodel import KresConfig
9
10
from knot_resolver .manager .config_store import ConfigStore , only_on_real_changes_update
10
11
from knot_resolver .utils import compat
12
+ from knot_resolver .utils .requests import SocketDesc , request
11
13
12
14
logger = logging .getLogger (__name__ )
13
15
16
+ FilesToWatch = Dict [Path , Optional [str ]]
14
17
15
- def tls_cert_files_config (config : KresConfig ) -> List [Any ]:
18
+
19
+ def watched_files_config (config : KresConfig ) -> List [Any ]:
16
20
return [
17
21
config .network .tls .files_watchdog ,
18
22
config .network .tls .cert_file ,
19
23
config .network .tls .key_file ,
24
+ config .local_data .rpz ,
20
25
]
21
26
22
27
23
- FilesToWatch = Dict [Path , str ]
24
-
25
-
26
28
if WATCHDOG_LIB :
27
29
from watchdog .events import (
28
30
FileSystemEvent ,
@@ -31,58 +33,96 @@ def tls_cert_files_config(config: KresConfig) -> List[Any]:
31
33
from watchdog .observers import Observer
32
34
33
35
class FilesWatchdogEventHandler (FileSystemEventHandler ):
34
- def __init__ (self , files : FilesToWatch ) -> None :
36
+ def __init__ (self , files : FilesToWatch , config : KresConfig ) -> None :
35
37
self ._files = files
36
- self ._timer : Optional [Timer ] = None
38
+ self ._config = config
39
+ self ._policy_timer : Optional [Timer ] = None
40
+ self ._timers : Dict [str , Timer ] = {}
41
+
42
+ def _trigger (self , cmd : Optional [str ]) -> None :
43
+ def policy_reload () -> None :
44
+ management = self ._config .management
45
+ socket = SocketDesc (
46
+ f'http+unix://{ quote (str (management .unix_socket ), safe = "" )} /' ,
47
+ 'Key "/management/unix-socket" in validated configuration' ,
48
+ )
49
+ if management .interface :
50
+ socket = SocketDesc (
51
+ f"http://{ management .interface .addr } :{ management .interface .port } " ,
52
+ 'Key "/management/interface" in validated configuration' ,
53
+ )
54
+
55
+ response = request (socket , "POST" , "renew" )
56
+ if response .status != 200 :
57
+ logger .error (f"Failed to reload policy rules: { response .body } " )
58
+ logger .info ("Reloading policy rules has finished" )
59
+
60
+ if not cmd :
61
+ # skipping if reload was already triggered
62
+ if self ._policy_timer and self ._policy_timer .is_alive ():
63
+ logger .info ("Skipping reloading policy rules, it was already triggered" )
64
+ return
65
+ # start a 5sec timer
66
+ logger .info ("Delayed policy rules reload has started" )
67
+ self ._policy_timer = Timer (5 , policy_reload )
68
+ self ._policy_timer .start ()
69
+ return
37
70
38
- def _reload (self , cmd : str ) -> None :
39
71
def command () -> None :
40
72
if compat .asyncio .is_event_loop_running ():
41
73
compat .asyncio .create_task (command_registered_workers (cmd ))
42
74
else :
43
75
compat .asyncio .run (command_registered_workers (cmd ))
44
- logger .info ("Reloading of TLS certificate files has finished" )
76
+ logger .info (f"Sending ' { cmd } ' command to reload watched files has finished" )
45
77
46
- # skipping if reload was already triggered
47
- if self ._timer and self ._timer .is_alive ():
48
- logger .info ("Skipping TLS certificate files reloading, reload command was already triggered" )
78
+ # skipping if command was already triggered
79
+ if cmd in self ._timers and self ._timers [ cmd ] .is_alive ():
80
+ logger .info (f "Skipping sending ' { cmd } ' command, it was already triggered" )
49
81
return
50
82
# start a 5sec timer
51
- logger .info ("Delayed reload of TLS certificate files has started" )
52
- self ._timer = Timer (5 , command )
53
- self ._timer .start ()
83
+ logger .info (f "Delayed send of ' { cmd } ' command has started" )
84
+ self ._timers [ cmd ] = Timer (5 , command )
85
+ self ._timers [ cmd ] .start ()
54
86
55
87
def on_created (self , event : FileSystemEvent ) -> None :
56
88
src_path = Path (str (event .src_path ))
57
89
if src_path in self ._files .keys ():
58
90
logger .info (f"Watched file '{ src_path } ' has been created" )
59
- self ._reload (self ._files [src_path ])
91
+ self ._trigger (self ._files [src_path ])
60
92
61
93
def on_deleted (self , event : FileSystemEvent ) -> None :
62
94
src_path = Path (str (event .src_path ))
63
95
if src_path in self ._files .keys ():
64
96
logger .warning (f"Watched file '{ src_path } ' has been deleted" )
65
- if self ._timer :
66
- self ._timer .cancel ()
97
+ cmd = self ._files [src_path ]
98
+ if cmd in self ._timers :
99
+ self ._timers [cmd ].cancel ()
67
100
for file in self ._files .keys ():
68
101
if file .parent == src_path :
69
102
logger .warning (f"Watched directory '{ src_path } ' has been deleted" )
70
- if self ._timer :
71
- self ._timer .cancel ()
103
+ cmd = self ._files [file ]
104
+ if cmd in self ._timers :
105
+ self ._timers [cmd ].cancel ()
106
+
107
+ def on_moved (self , event : FileSystemEvent ) -> None :
108
+ src_path = Path (str (event .src_path ))
109
+ if src_path in self ._files .keys ():
110
+ logger .info (f"Watched file '{ src_path } ' has been moved" )
111
+ self ._trigger (self ._files [src_path ])
72
112
73
113
def on_modified (self , event : FileSystemEvent ) -> None :
74
114
src_path = Path (str (event .src_path ))
75
115
if src_path in self ._files .keys ():
76
116
logger .info (f"Watched file '{ src_path } ' has been modified" )
77
- self ._reload (self ._files [src_path ])
117
+ self ._trigger (self ._files [src_path ])
78
118
79
119
_files_watchdog : Optional ["FilesWatchdog" ] = None
80
120
81
121
class FilesWatchdog :
82
- def __init__ (self , files_to_watch : FilesToWatch ) -> None :
122
+ def __init__ (self , files_to_watch : FilesToWatch , config : KresConfig ) -> None :
83
123
self ._observer = Observer ()
84
124
85
- event_handler = FilesWatchdogEventHandler (files_to_watch )
125
+ event_handler = FilesWatchdogEventHandler (files_to_watch , config )
86
126
dirs_to_watch : List [Path ] = []
87
127
for file in files_to_watch .keys ():
88
128
if file .parent not in dirs_to_watch :
@@ -104,7 +144,7 @@ def stop(self) -> None:
104
144
self ._observer .join ()
105
145
106
146
107
- @only_on_real_changes_update (tls_cert_files_config )
147
+ @only_on_real_changes_update (watched_files_config )
108
148
async def _init_files_watchdog (config : KresConfig ) -> None :
109
149
if WATCHDOG_LIB :
110
150
global _files_watchdog
@@ -119,9 +159,15 @@ async def _init_files_watchdog(config: KresConfig) -> None:
119
159
files_to_watch [config .network .tls .cert_file .to_path ()] = net_tls
120
160
files_to_watch [config .network .tls .key_file .to_path ()] = net_tls
121
161
162
+ # local-data.rpz
163
+ if config .local_data .rpz :
164
+ for rpz in config .local_data .rpz :
165
+ if rpz .watchdog :
166
+ files_to_watch [rpz .file .to_path ()] = None
167
+
122
168
if files_to_watch :
123
169
logger .info ("Initializing files watchdog" )
124
- _files_watchdog = FilesWatchdog (files_to_watch )
170
+ _files_watchdog = FilesWatchdog (files_to_watch , config )
125
171
_files_watchdog .start ()
126
172
127
173
0 commit comments