32
32
from traitlets import Any , Bool , Dict , Integer , Unicode , default , observe , validate
33
33
34
34
from . import traefik_utils
35
+ from .traefik_utils import deep_merge
35
36
36
37
37
38
class TraefikProxy (Proxy ):
@@ -65,6 +66,25 @@ def _concurrency_changed(self, change):
65
66
"traefik.toml" , config = True , help = """traefik's static configuration file"""
66
67
)
67
68
69
+ extra_static_config = Dict (
70
+ config = True ,
71
+ help = """Extra static configuration for treafik.
72
+
73
+ Merged with the default static config before writing to `.static_config_file`.
74
+
75
+ Has no effect if `Proxy.should_start` is False.
76
+ """ ,
77
+ )
78
+ extra_dynamic_config = Dict (
79
+ config = True ,
80
+ help = """Extra dynamic configuration for treafik.
81
+
82
+ Merged with the default dynamic config during startup.
83
+
84
+ Always takes effect.
85
+ """ ,
86
+ )
87
+
68
88
toml_static_config_file = Unicode (
69
89
config = True ,
70
90
help = "Deprecated. Use static_config_file" ,
@@ -188,13 +208,6 @@ def _add_port(self, proposal):
188
208
url = urlunparse (parsed )
189
209
return url
190
210
191
- traefik_cert_resolver = Unicode (
192
- config = True ,
193
- help = """The traefik certificate Resolver to use for requesting certificates""" ,
194
- )
195
-
196
- # FIXME: How best to enable TLS on routers assigned to only select
197
- # entrypoints defined here?
198
211
traefik_entrypoint = Unicode (
199
212
help = """The traefik entrypoint name to use.
200
213
@@ -414,14 +427,16 @@ async def _setup_traefik_static_config(self):
414
427
Subclasses should specify any traefik providers themselves, in
415
428
:attrib:`self.static_config["providers"]`
416
429
"""
417
- self .static_config ["providers" ][
418
- "providersThrottleDuration"
419
- ] = self .traefik_providers_throttle_duration
420
-
430
+ static_config = {
431
+ "api" : {},
432
+ "providers" : {
433
+ "providersThrottleDuration" : self .traefik_providers_throttle_duration ,
434
+ },
435
+ }
421
436
if self .traefik_log_level :
422
- self . static_config ["log" ] = {"level" : self .traefik_log_level }
437
+ static_config ["log" ] = {"level" : self .traefik_log_level }
423
438
424
- entrypoints = {
439
+ entrypoints = static_config [ "entryPoints" ] = {
425
440
self .traefik_entrypoint : {
426
441
"address" : urlparse (self .public_url ).netloc ,
427
442
},
@@ -430,8 +445,20 @@ async def _setup_traefik_static_config(self):
430
445
},
431
446
}
432
447
433
- self .static_config ["entryPoints" ] = entrypoints
434
- self .static_config ["api" ] = {}
448
+ if self .is_https :
449
+ entrypoints [self .traefik_entrypoint ]["http" ] = {
450
+ "tls" : {
451
+ "options" : "default" ,
452
+ }
453
+ }
454
+
455
+ # load what we just defined at _lower_ priority
456
+ # than anything added to self.static_config in a subclass before this
457
+ self .static_config = deep_merge (static_config , self .static_config )
458
+ if self .extra_static_config :
459
+ self .static_config = deep_merge (
460
+ self .static_config , self .extra_static_config
461
+ )
435
462
436
463
self .log .info (f"Writing traefik static config: { self .static_config } " )
437
464
@@ -446,29 +473,46 @@ async def _setup_traefik_dynamic_config(self):
446
473
self .log .debug ("Setting up traefik's dynamic config..." )
447
474
self ._generate_htpassword ()
448
475
api_url = urlparse (self .traefik_api_url )
449
- api_path = api_url .path if api_url .path else '/api'
476
+ api_path = api_url .path if api_url .path . strip ( "/" ) else '/api'
450
477
api_credentials = (
451
478
f"{ self .traefik_api_username } :{ self .traefik_api_hashed_password } "
452
479
)
453
- http = self .dynamic_config .setdefault ("http" , {})
454
- routers = http .setdefault ("routers" , {})
480
+ dynamic_config = {
481
+ "http" : {
482
+ "routers" : {},
483
+ "middlewares" : {},
484
+ }
485
+ }
486
+ dynamic_config ["http" ]
487
+ routers = dynamic_config ["http" ]["routers" ]
488
+ middlewares = dynamic_config ["http" ]["middlewares" ]
455
489
routers ["route_api" ] = {
456
490
"rule" : f"Host(`{ api_url .hostname } `) && PathPrefix(`{ api_path } `)" ,
457
491
"entryPoints" : [self .traefik_api_entrypoint ],
458
492
"service" : "api@internal" ,
459
493
"middlewares" : ["auth_api" ],
460
494
}
461
- middlewares = http .setdefault ("middlewares" , {})
462
495
middlewares ["auth_api" ] = {"basicAuth" : {"users" : [api_credentials ]}}
496
+
497
+ # add default ssl cert/keys
463
498
if self .ssl_cert and self .ssl_key :
464
- tls = self .dynamic_config .setdefault ("tls" , {})
465
- stores = tls .setdefault ("stores" , {})
466
- stores ["default" ] = {
467
- "defaultCertificate" : {
468
- "certFile" : self .ssl_cert ,
469
- "keyFile" : self .ssl_key ,
499
+ dynamic_config ["tls" ] = {
500
+ "stores" : {
501
+ "default" : {
502
+ "defaultCertificate" : {
503
+ "certFile" : self .ssl_cert ,
504
+ "keyFile" : self .ssl_key ,
505
+ }
506
+ }
470
507
}
471
508
}
509
+
510
+ self .dynamic_config = deep_merge (dynamic_config , self .dynamic_config )
511
+ if self .extra_dynamic_config :
512
+ self .dynamic_config = deep_merge (
513
+ self .dynamic_config , self .extra_dynamic_config
514
+ )
515
+
472
516
await self ._apply_dynamic_config (self .dynamic_config , None )
473
517
474
518
def validate_routespec (self , routespec ):
@@ -556,19 +600,6 @@ def _dynamic_config_for_route(self, routespec, target, data):
556
600
"loadBalancer" : {"servers" : [{"url" : target }], "passHostHeader" : True }
557
601
}
558
602
559
- # Enable TLS on this router if globally enabled
560
- if self .is_https :
561
- tls_config = {}
562
- if self .traefik_cert_resolver :
563
- tls_config ["certResolver" ] = self .traefik_cert_resolver
564
- else :
565
- # we need _some_ key to be set
566
- # because key-value stores can't store empty dicts.
567
- # put a default value here
568
- tls_config ["options" ] = "default"
569
-
570
- router ["tls" ] = tls_config
571
-
572
603
# Add the data node to a separate top-level node, so traefik doesn't see it.
573
604
# key needs to be key-value safe (no '/')
574
605
# store original routespec, router, service aliases for easy lookup
0 commit comments