@@ -154,6 +154,26 @@ def remove_item_from_config(config, property_path, value):
154
154
return config_copy
155
155
156
156
157
+ def validate_config (config , validate ):
158
+ """
159
+ Validate changes to the config with tljh-config against the schema
160
+ """
161
+ import jsonschema
162
+
163
+ from .config_schema import config_schema
164
+
165
+ try :
166
+ jsonschema .validate (instance = config , schema = config_schema )
167
+ except jsonschema .exceptions .ValidationError as e :
168
+ if validate :
169
+ print (
170
+ f"Config validation error: { e .message } .\n "
171
+ "You can still apply this change without validation by re-running your command with the --no-validate flag.\n "
172
+ "If you think this validation error is incorrect, please report it to https://github.com/jupyterhub/the-littlest-jupyterhub/issues."
173
+ )
174
+ exit ()
175
+
176
+
157
177
def show_config (config_path ):
158
178
"""
159
179
Pretty print config from given config_path
@@ -167,73 +187,73 @@ def show_config(config_path):
167
187
yaml .dump (config , sys .stdout )
168
188
169
189
170
- def set_config_value (config_path , key_path , value ):
190
+ def set_config_value (config_path , key_path , value , validate = True ):
171
191
"""
172
192
Set key at key_path in config_path to value
173
193
"""
174
194
# FIXME: Have a file lock here
175
- # FIXME: Validate schema here
176
195
try :
177
196
with open (config_path ) as f :
178
197
config = yaml .load (f )
179
198
except FileNotFoundError :
180
199
config = {}
181
-
182
200
config = set_item_in_config (config , key_path , value )
183
201
202
+ validate_config (config , validate )
203
+
184
204
with open (config_path , "w" ) as f :
185
205
yaml .dump (config , f )
186
206
187
207
188
- def unset_config_value (config_path , key_path ):
208
+ def unset_config_value (config_path , key_path , validate = True ):
189
209
"""
190
210
Unset key at key_path in config_path
191
211
"""
192
212
# FIXME: Have a file lock here
193
- # FIXME: Validate schema here
194
213
try :
195
214
with open (config_path ) as f :
196
215
config = yaml .load (f )
197
216
except FileNotFoundError :
198
217
config = {}
199
218
200
219
config = unset_item_from_config (config , key_path )
220
+ validate_config (config , validate )
201
221
202
222
with open (config_path , "w" ) as f :
203
223
yaml .dump (config , f )
204
224
205
225
206
- def add_config_value (config_path , key_path , value ):
226
+ def add_config_value (config_path , key_path , value , validate = True ):
207
227
"""
208
228
Add value to list at key_path
209
229
"""
210
230
# FIXME: Have a file lock here
211
- # FIXME: Validate schema here
212
231
try :
213
232
with open (config_path ) as f :
214
233
config = yaml .load (f )
215
234
except FileNotFoundError :
216
235
config = {}
217
236
218
237
config = add_item_to_config (config , key_path , value )
238
+ validate_config (config , validate )
219
239
220
240
with open (config_path , "w" ) as f :
221
241
yaml .dump (config , f )
222
242
223
243
224
- def remove_config_value (config_path , key_path , value ):
244
+ def remove_config_value (config_path , key_path , value , validate = True ):
225
245
"""
226
246
Remove value from list at key_path
227
247
"""
228
248
# FIXME: Have a file lock here
229
- # FIXME: Validate schema here
230
249
try :
231
250
with open (config_path ) as f :
232
251
config = yaml .load (f )
233
252
except FileNotFoundError :
234
253
config = {}
235
254
236
255
config = remove_item_from_config (config , key_path , value )
256
+ validate_config (config , validate )
237
257
238
258
with open (config_path , "w" ) as f :
239
259
yaml .dump (config , f )
@@ -336,6 +356,18 @@ def main(argv=None):
336
356
argparser .add_argument (
337
357
"--config-path" , default = CONFIG_FILE , help = "Path to TLJH config.yaml file"
338
358
)
359
+
360
+ argparser .add_argument (
361
+ "--validate" , action = "store_true" , help = "Validate the TLJH config"
362
+ )
363
+ argparser .add_argument (
364
+ "--no-validate" ,
365
+ dest = "validate" ,
366
+ action = "store_false" ,
367
+ help = "Do not validate the TLJH config" ,
368
+ )
369
+ argparser .set_defaults (validate = True )
370
+
339
371
subparsers = argparser .add_subparsers (dest = "action" )
340
372
341
373
show_parser = subparsers .add_parser ("show" , help = "Show current configuration" )
@@ -383,13 +415,19 @@ def main(argv=None):
383
415
if args .action == "show" :
384
416
show_config (args .config_path )
385
417
elif args .action == "set" :
386
- set_config_value (args .config_path , args .key_path , parse_value (args .value ))
418
+ set_config_value (
419
+ args .config_path , args .key_path , parse_value (args .value ), args .validate
420
+ )
387
421
elif args .action == "unset" :
388
- unset_config_value (args .config_path , args .key_path )
422
+ unset_config_value (args .config_path , args .key_path , args . validate )
389
423
elif args .action == "add-item" :
390
- add_config_value (args .config_path , args .key_path , parse_value (args .value ))
424
+ add_config_value (
425
+ args .config_path , args .key_path , parse_value (args .value ), args .validate
426
+ )
391
427
elif args .action == "remove-item" :
392
- remove_config_value (args .config_path , args .key_path , parse_value (args .value ))
428
+ remove_config_value (
429
+ args .config_path , args .key_path , parse_value (args .value ), args .validate
430
+ )
393
431
elif args .action == "reload" :
394
432
reload_component (args .component )
395
433
else :
0 commit comments