2017-04-17 10:50:02 +00:00
package file
2015-09-07 13:25:13 +00:00
import (
2019-03-27 14:02:06 +00:00
"bytes"
2018-11-14 09:18:03 +00:00
"context"
2019-04-01 13:30:07 +00:00
"errors"
2017-05-26 13:32:03 +00:00
"fmt"
2017-11-09 11:16:03 +00:00
"os"
"path/filepath"
2015-09-07 15:39:22 +00:00
"strings"
2018-03-22 15:14:04 +00:00
"text/template"
2015-09-24 15:16:13 +00:00
2021-01-20 14:10:04 +00:00
"github.com/Masterminds/sprig/v3"
2024-01-02 19:58:06 +00:00
"github.com/fsnotify/fsnotify"
2022-11-21 17:36:05 +00:00
"github.com/rs/zerolog/log"
2020-08-17 16:04:03 +00:00
"github.com/traefik/paerser/file"
2023-02-03 14:24:05 +00:00
"github.com/traefik/traefik/v3/pkg/config/dynamic"
"github.com/traefik/traefik/v3/pkg/logs"
"github.com/traefik/traefik/v3/pkg/provider"
"github.com/traefik/traefik/v3/pkg/safe"
"github.com/traefik/traefik/v3/pkg/tls"
2024-01-11 16:06:06 +00:00
"github.com/traefik/traefik/v3/pkg/types"
2015-09-07 13:25:13 +00:00
)
2018-11-14 09:18:03 +00:00
const providerName = "file"
2017-04-17 10:50:02 +00:00
var _ provider . Provider = ( * Provider ) ( nil )
2016-08-16 17:13:18 +00:00
2017-04-17 10:50:02 +00:00
// Provider holds configurations of the provider.
type Provider struct {
2021-06-18 22:08:08 +00:00
Directory string ` description:"Load dynamic configuration from one or more .yml or .toml files in a directory." json:"directory,omitempty" toml:"directory,omitempty" yaml:"directory,omitempty" export:"true" `
2019-07-01 09:30:05 +00:00
Watch bool ` description:"Watch provider." json:"watch,omitempty" toml:"watch,omitempty" yaml:"watch,omitempty" export:"true" `
2019-09-26 07:24:04 +00:00
Filename string ` description:"Load dynamic configuration from a file." json:"filename,omitempty" toml:"filename,omitempty" yaml:"filename,omitempty" export:"true" `
2019-07-01 09:30:05 +00:00
DebugLogGeneratedTemplate bool ` description:"Enable debug logging of generated configuration template." json:"debugLogGeneratedTemplate,omitempty" toml:"debugLogGeneratedTemplate,omitempty" yaml:"debugLogGeneratedTemplate,omitempty" export:"true" `
2019-06-17 09:48:05 +00:00
}
// SetDefaults sets the default values.
func ( p * Provider ) SetDefaults ( ) {
p . Watch = true
p . Filename = ""
2015-09-07 13:25:13 +00:00
}
2020-05-11 10:06:07 +00:00
// Init the provider.
2018-11-27 16:42:04 +00:00
func ( p * Provider ) Init ( ) error {
2019-03-27 14:02:06 +00:00
return nil
2018-07-11 07:08:03 +00:00
}
2017-04-17 10:50:02 +00:00
// Provide allows the file provider to provide configurations to traefik
2015-11-01 18:29:47 +00:00
// using the given configuration channel.
2019-07-10 07:26:04 +00:00
func ( p * Provider ) Provide ( configurationChan chan <- dynamic . Message , pool * safe . Pool ) error {
2017-05-26 13:32:03 +00:00
if p . Watch {
var watchItem string
2019-02-05 16:10:03 +00:00
switch {
case len ( p . Directory ) > 0 :
2017-05-26 13:32:03 +00:00
watchItem = p . Directory
2019-02-05 16:10:03 +00:00
case len ( p . Filename ) > 0 :
2017-11-09 11:16:03 +00:00
watchItem = filepath . Dir ( p . Filename )
2019-02-05 16:10:03 +00:00
default :
2019-07-15 08:22:03 +00:00
return errors . New ( "error using file configuration provider, neither filename or directory defined" )
2017-05-26 13:32:03 +00:00
}
if err := p . addWatcher ( pool , watchItem , configurationChan , p . watcherCallback ) ; err != nil {
return err
}
}
2022-12-09 08:48:04 +00:00
configuration , err := p . BuildConfiguration ( )
if err != nil {
if p . Watch {
2023-09-25 07:10:05 +00:00
log . Error ( ) .
2023-01-02 13:55:45 +00:00
Str ( logs . ProviderName , providerName ) .
Err ( err ) .
Msg ( "Error while building configuration (for the first time)" )
2022-12-09 08:48:04 +00:00
return nil
}
return err
}
2017-05-26 13:32:03 +00:00
sendConfigToChannel ( configurationChan , configuration )
return nil
}
2020-05-11 10:06:07 +00:00
// BuildConfiguration loads configuration either from file or a directory
// specified by 'Filename'/'Directory' and returns a 'Configuration' object.
2019-07-10 07:26:04 +00:00
func ( p * Provider ) BuildConfiguration ( ) ( * dynamic . Configuration , error ) {
2022-11-21 17:36:05 +00:00
ctx := log . With ( ) . Str ( logs . ProviderName , providerName ) . Logger ( ) . WithContext ( context . Background ( ) )
2018-11-14 09:18:03 +00:00
2018-05-22 10:02:03 +00:00
if len ( p . Directory ) > 0 {
2018-11-14 09:18:03 +00:00
return p . loadFileConfigFromDirectory ( ctx , p . Directory , nil )
2017-11-21 09:24:03 +00:00
}
2018-05-22 10:02:03 +00:00
if len ( p . Filename ) > 0 {
2019-09-13 17:28:04 +00:00
return p . loadFileConfig ( ctx , p . Filename , true )
2018-05-22 10:02:03 +00:00
}
2019-07-15 08:22:03 +00:00
return nil , errors . New ( "error using file configuration provider, neither filename or directory defined" )
2017-11-21 09:24:03 +00:00
}
2019-07-10 07:26:04 +00:00
func ( p * Provider ) addWatcher ( pool * safe . Pool , directory string , configurationChan chan <- dynamic . Message , callback func ( chan <- dynamic . Message , fsnotify . Event ) ) error {
2017-05-26 13:32:03 +00:00
watcher , err := fsnotify . NewWatcher ( )
2015-09-07 13:25:13 +00:00
if err != nil {
2020-05-11 10:06:07 +00:00
return fmt . Errorf ( "error creating file watcher: %w" , err )
2015-09-07 13:25:13 +00:00
}
2018-05-22 10:02:03 +00:00
err = watcher . Add ( directory )
if err != nil {
2020-05-11 10:06:07 +00:00
return fmt . Errorf ( "error adding file watcher: %w" , err )
2018-05-22 10:02:03 +00:00
}
2017-05-26 13:32:03 +00:00
// Process events
2020-02-03 16:56:04 +00:00
pool . GoCtx ( func ( ctx context . Context ) {
2017-05-26 13:32:03 +00:00
defer watcher . Close ( )
for {
select {
2020-02-03 16:56:04 +00:00
case <- ctx . Done ( ) :
2017-05-26 13:32:03 +00:00
return
case evt := <- watcher . Events :
2017-11-09 11:16:03 +00:00
if p . Directory == "" {
_ , evtFileName := filepath . Split ( evt . Name )
2019-07-15 08:22:03 +00:00
_ , confFileName := filepath . Split ( p . Filename )
2017-11-09 11:16:03 +00:00
if evtFileName == confFileName {
callback ( configurationChan , evt )
}
} else {
callback ( configurationChan , evt )
}
2017-05-26 13:32:03 +00:00
case err := <- watcher . Errors :
2022-11-21 17:36:05 +00:00
log . Error ( ) . Str ( logs . ProviderName , providerName ) . Err ( err ) . Msg ( "Watcher event error" )
2015-09-07 13:25:13 +00:00
}
2015-10-03 14:50:53 +00:00
}
2017-05-26 13:32:03 +00:00
} )
return nil
}
2019-07-10 07:26:04 +00:00
func ( p * Provider ) watcherCallback ( configurationChan chan <- dynamic . Message , event fsnotify . Event ) {
2019-07-15 08:22:03 +00:00
watchItem := p . Filename
2018-05-22 10:02:03 +00:00
if len ( p . Directory ) > 0 {
2017-11-21 09:24:03 +00:00
watchItem = p . Directory
}
2022-11-21 17:36:05 +00:00
logger := log . With ( ) . Str ( logs . ProviderName , providerName ) . Logger ( )
2018-11-14 09:18:03 +00:00
2017-11-21 09:24:03 +00:00
if _ , err := os . Stat ( watchItem ) ; err != nil {
2022-11-21 17:36:05 +00:00
logger . Error ( ) . Err ( err ) . Msgf ( "Unable to watch %s" , watchItem )
2017-11-21 09:24:03 +00:00
return
}
2017-12-02 18:25:29 +00:00
configuration , err := p . BuildConfiguration ( )
2017-11-21 09:24:03 +00:00
if err != nil {
2022-11-21 17:36:05 +00:00
logger . Error ( ) . Err ( err ) . Msg ( "Error occurred during watcher callback" )
2017-11-21 09:24:03 +00:00
return
}
sendConfigToChannel ( configurationChan , configuration )
}
2019-07-10 07:26:04 +00:00
func sendConfigToChannel ( configurationChan chan <- dynamic . Message , configuration * dynamic . Configuration ) {
configurationChan <- dynamic . Message {
2015-11-13 10:50:32 +00:00
ProviderName : "file" ,
Configuration : configuration ,
}
2015-09-07 13:25:13 +00:00
}
2019-09-13 17:28:04 +00:00
func ( p * Provider ) loadFileConfig ( ctx context . Context , filename string , parseTemplate bool ) ( * dynamic . Configuration , error ) {
2019-06-26 16:18:04 +00:00
var err error
2019-07-10 07:26:04 +00:00
var configuration * dynamic . Configuration
2018-05-22 10:02:03 +00:00
if parseTemplate {
2019-09-13 17:28:04 +00:00
configuration , err = p . CreateConfiguration ( ctx , filename , template . FuncMap { } , false )
2018-05-22 10:02:03 +00:00
} else {
2019-06-26 16:18:04 +00:00
configuration , err = p . DecodeConfiguration ( filename )
2018-05-22 10:02:03 +00:00
}
2018-03-22 15:14:04 +00:00
if err != nil {
return nil , err
}
2018-11-14 09:18:03 +00:00
2019-06-27 21:58:03 +00:00
if configuration . TLS != nil {
2019-09-13 17:28:04 +00:00
configuration . TLS . Certificates = flattenCertificates ( ctx , configuration . TLS )
2021-07-13 12:14:35 +00:00
// TLS Options
if configuration . TLS . Options != nil {
for name , options := range configuration . TLS . Options {
2024-01-11 16:06:06 +00:00
var caCerts [ ] types . FileOrContent
2021-07-13 12:14:35 +00:00
for _ , caFile := range options . ClientAuth . CAFiles {
content , err := caFile . Read ( )
if err != nil {
2022-11-21 17:36:05 +00:00
log . Ctx ( ctx ) . Error ( ) . Err ( err ) . Send ( )
2021-07-13 12:14:35 +00:00
continue
}
2024-01-11 16:06:06 +00:00
caCerts = append ( caCerts , types . FileOrContent ( content ) )
2021-07-13 12:14:35 +00:00
}
options . ClientAuth . CAFiles = caCerts
configuration . TLS . Options [ name ] = options
}
}
// TLS stores
if len ( configuration . TLS . Stores ) > 0 {
for name , store := range configuration . TLS . Stores {
2021-07-19 07:58:14 +00:00
if store . DefaultCertificate == nil {
continue
}
2021-07-13 12:14:35 +00:00
content , err := store . DefaultCertificate . CertFile . Read ( )
if err != nil {
2022-11-21 17:36:05 +00:00
log . Ctx ( ctx ) . Error ( ) . Err ( err ) . Send ( )
2021-07-13 12:14:35 +00:00
continue
}
2024-01-11 16:06:06 +00:00
store . DefaultCertificate . CertFile = types . FileOrContent ( content )
2021-07-13 12:14:35 +00:00
content , err = store . DefaultCertificate . KeyFile . Read ( )
if err != nil {
2022-11-21 17:36:05 +00:00
log . Ctx ( ctx ) . Error ( ) . Err ( err ) . Send ( )
2021-07-13 12:14:35 +00:00
continue
}
2024-01-11 16:06:06 +00:00
store . DefaultCertificate . KeyFile = types . FileOrContent ( content )
2021-07-13 12:14:35 +00:00
configuration . TLS . Stores [ name ] = store
}
}
}
2022-12-09 08:58:05 +00:00
// HTTP ServersTransport
2021-07-13 12:14:35 +00:00
if configuration . HTTP != nil && len ( configuration . HTTP . ServersTransports ) > 0 {
for name , st := range configuration . HTTP . ServersTransports {
var certificates [ ] tls . Certificate
for _ , cert := range st . Certificates {
content , err := cert . CertFile . Read ( )
if err != nil {
2022-11-21 17:36:05 +00:00
log . Ctx ( ctx ) . Error ( ) . Err ( err ) . Send ( )
2021-07-13 12:14:35 +00:00
continue
}
2024-01-11 16:06:06 +00:00
cert . CertFile = types . FileOrContent ( content )
2021-07-13 12:14:35 +00:00
content , err = cert . KeyFile . Read ( )
if err != nil {
2022-11-21 17:36:05 +00:00
log . Ctx ( ctx ) . Error ( ) . Err ( err ) . Send ( )
2021-07-13 12:14:35 +00:00
continue
}
2024-01-11 16:06:06 +00:00
cert . KeyFile = types . FileOrContent ( content )
2021-07-13 12:14:35 +00:00
certificates = append ( certificates , cert )
}
configuration . HTTP . ServersTransports [ name ] . Certificates = certificates
2024-01-11 16:06:06 +00:00
var rootCAs [ ] types . FileOrContent
2021-07-13 12:14:35 +00:00
for _ , rootCA := range st . RootCAs {
content , err := rootCA . Read ( )
if err != nil {
2022-11-21 17:36:05 +00:00
log . Ctx ( ctx ) . Error ( ) . Err ( err ) . Send ( )
2021-07-13 12:14:35 +00:00
continue
}
2024-01-11 16:06:06 +00:00
rootCAs = append ( rootCAs , types . FileOrContent ( content ) )
2021-07-13 12:14:35 +00:00
}
st . RootCAs = rootCAs
}
2019-06-27 21:58:03 +00:00
}
2022-12-09 08:58:05 +00:00
// TCP ServersTransport
if configuration . TCP != nil && len ( configuration . TCP . ServersTransports ) > 0 {
for name , st := range configuration . TCP . ServersTransports {
var certificates [ ] tls . Certificate
if st . TLS == nil {
continue
}
for _ , cert := range st . TLS . Certificates {
content , err := cert . CertFile . Read ( )
if err != nil {
log . Ctx ( ctx ) . Error ( ) . Err ( err ) . Send ( )
continue
}
2024-01-11 16:06:06 +00:00
cert . CertFile = types . FileOrContent ( content )
2022-12-09 08:58:05 +00:00
content , err = cert . KeyFile . Read ( )
if err != nil {
log . Ctx ( ctx ) . Error ( ) . Err ( err ) . Send ( )
continue
}
2024-01-11 16:06:06 +00:00
cert . KeyFile = types . FileOrContent ( content )
2022-12-09 08:58:05 +00:00
certificates = append ( certificates , cert )
}
configuration . TCP . ServersTransports [ name ] . TLS . Certificates = certificates
2024-01-11 16:06:06 +00:00
var rootCAs [ ] types . FileOrContent
2022-12-09 08:58:05 +00:00
for _ , rootCA := range st . TLS . RootCAs {
content , err := rootCA . Read ( )
if err != nil {
log . Ctx ( ctx ) . Error ( ) . Err ( err ) . Send ( )
continue
}
2024-01-11 16:06:06 +00:00
rootCAs = append ( rootCAs , types . FileOrContent ( content ) )
2022-12-09 08:58:05 +00:00
}
st . TLS . RootCAs = rootCAs
}
}
2019-06-27 21:58:03 +00:00
return configuration , nil
}
2019-09-13 17:28:04 +00:00
func flattenCertificates ( ctx context . Context , tlsConfig * dynamic . TLSConfiguration ) [ ] * tls . CertAndStores {
2019-06-27 21:58:03 +00:00
var certs [ ] * tls . CertAndStores
for _ , cert := range tlsConfig . Certificates {
content , err := cert . Certificate . CertFile . Read ( )
2019-01-29 15:46:09 +00:00
if err != nil {
2022-11-21 17:36:05 +00:00
log . Ctx ( ctx ) . Error ( ) . Err ( err ) . Send ( )
2019-01-29 15:46:09 +00:00
continue
}
2024-01-11 16:06:06 +00:00
cert . Certificate . CertFile = types . FileOrContent ( string ( content ) )
2019-01-29 15:46:09 +00:00
2019-06-27 21:58:03 +00:00
content , err = cert . Certificate . KeyFile . Read ( )
2019-01-29 15:46:09 +00:00
if err != nil {
2022-11-21 17:36:05 +00:00
log . Ctx ( ctx ) . Error ( ) . Err ( err ) . Send ( )
2019-01-29 15:46:09 +00:00
continue
}
2024-01-11 16:06:06 +00:00
cert . Certificate . KeyFile = types . FileOrContent ( string ( content ) )
2019-06-27 21:58:03 +00:00
certs = append ( certs , cert )
2019-01-29 15:46:09 +00:00
}
2019-06-27 21:58:03 +00:00
return certs
2017-05-26 13:32:03 +00:00
}
2019-07-10 07:26:04 +00:00
func ( p * Provider ) loadFileConfigFromDirectory ( ctx context . Context , directory string , configuration * dynamic . Configuration ) ( * dynamic . Configuration , error ) {
2021-03-04 19:08:03 +00:00
fileList , err := os . ReadDir ( directory )
2017-05-26 13:32:03 +00:00
if err != nil {
2020-05-11 10:06:07 +00:00
return configuration , fmt . Errorf ( "unable to read directory %s: %w" , directory , err )
2017-05-26 13:32:03 +00:00
}
2017-11-09 11:16:03 +00:00
if configuration == nil {
2019-07-10 07:26:04 +00:00
configuration = & dynamic . Configuration {
HTTP : & dynamic . HTTPConfiguration {
2020-09-11 13:40:03 +00:00
Routers : make ( map [ string ] * dynamic . Router ) ,
Middlewares : make ( map [ string ] * dynamic . Middleware ) ,
Services : make ( map [ string ] * dynamic . Service ) ,
ServersTransports : make ( map [ string ] * dynamic . ServersTransport ) ,
2019-03-14 08:30:04 +00:00
} ,
2019-07-10 07:26:04 +00:00
TCP : & dynamic . TCPConfiguration {
2022-12-09 08:58:05 +00:00
Routers : make ( map [ string ] * dynamic . TCPRouter ) ,
Services : make ( map [ string ] * dynamic . TCPService ) ,
Middlewares : make ( map [ string ] * dynamic . TCPMiddleware ) ,
ServersTransports : make ( map [ string ] * dynamic . TCPServersTransport ) ,
2019-03-14 08:30:04 +00:00
} ,
2019-07-10 07:26:04 +00:00
TLS : & dynamic . TLSConfiguration {
2019-06-27 21:58:03 +00:00
Stores : make ( map [ string ] tls . Store ) ,
Options : make ( map [ string ] tls . Options ) ,
} ,
2020-02-11 00:26:04 +00:00
UDP : & dynamic . UDPConfiguration {
Routers : make ( map [ string ] * dynamic . UDPRouter ) ,
Services : make ( map [ string ] * dynamic . UDPService ) ,
} ,
2017-11-09 11:16:03 +00:00
}
2015-09-07 13:25:13 +00:00
}
2017-05-26 13:32:03 +00:00
2019-06-27 21:58:03 +00:00
configTLSMaps := make ( map [ * tls . CertAndStores ] struct { } )
2019-06-26 16:18:04 +00:00
2017-11-09 11:16:03 +00:00
for _ , item := range fileList {
2022-11-21 17:36:05 +00:00
logger := log . Ctx ( ctx ) . With ( ) . Str ( "filename" , item . Name ( ) ) . Logger ( )
2019-10-11 15:20:05 +00:00
2017-11-09 11:16:03 +00:00
if item . IsDir ( ) {
2022-11-21 17:36:05 +00:00
configuration , err = p . loadFileConfigFromDirectory ( logger . WithContext ( ctx ) , filepath . Join ( directory , item . Name ( ) ) , configuration )
2017-11-09 11:16:03 +00:00
if err != nil {
2020-05-11 10:06:07 +00:00
return configuration , fmt . Errorf ( "unable to load content configuration from subdirectory %s: %w" , item , err )
2017-11-09 11:16:03 +00:00
}
continue
2019-06-26 16:18:04 +00:00
}
switch strings . ToLower ( filepath . Ext ( item . Name ( ) ) ) {
case ".toml" , ".yaml" , ".yml" :
// noop
default :
2017-05-26 13:32:03 +00:00
continue
}
2019-07-10 07:26:04 +00:00
var c * dynamic . Configuration
2022-11-21 17:36:05 +00:00
c , err = p . loadFileConfig ( logger . WithContext ( ctx ) , filepath . Join ( directory , item . Name ( ) ) , true )
2017-05-26 13:32:03 +00:00
if err != nil {
2020-05-11 10:06:07 +00:00
return configuration , fmt . Errorf ( "%s: %w" , filepath . Join ( directory , item . Name ( ) ) , err )
2017-05-26 13:32:03 +00:00
}
2019-03-14 08:30:04 +00:00
for name , conf := range c . HTTP . Routers {
if _ , exists := configuration . HTTP . Routers [ name ] ; exists {
2022-11-21 17:36:05 +00:00
logger . Warn ( ) . Str ( logs . RouterName , name ) . Msg ( "HTTP router already configured, skipping" )
2019-03-14 08:30:04 +00:00
} else {
configuration . HTTP . Routers [ name ] = conf
}
}
for name , conf := range c . HTTP . Middlewares {
if _ , exists := configuration . HTTP . Middlewares [ name ] ; exists {
2022-11-21 17:36:05 +00:00
logger . Warn ( ) . Str ( logs . MiddlewareName , name ) . Msg ( "HTTP middleware already configured, skipping" )
2019-03-14 08:30:04 +00:00
} else {
configuration . HTTP . Middlewares [ name ] = conf
}
}
for name , conf := range c . HTTP . Services {
if _ , exists := configuration . HTTP . Services [ name ] ; exists {
2022-11-21 17:36:05 +00:00
logger . Warn ( ) . Str ( logs . ServiceName , name ) . Msg ( "HTTP service already configured, skipping" )
2017-05-26 13:32:03 +00:00
} else {
2019-03-14 08:30:04 +00:00
configuration . HTTP . Services [ name ] = conf
2017-05-26 13:32:03 +00:00
}
}
2020-09-11 13:40:03 +00:00
for name , conf := range c . HTTP . ServersTransports {
if _ , exists := configuration . HTTP . ServersTransports [ name ] ; exists {
2022-11-21 17:36:05 +00:00
logger . Warn ( ) . Str ( logs . ServersTransportName , name ) . Msg ( "HTTP servers transport already configured, skipping" )
2020-09-11 13:40:03 +00:00
} else {
configuration . HTTP . ServersTransports [ name ] = conf
}
}
2019-03-14 08:30:04 +00:00
for name , conf := range c . TCP . Routers {
if _ , exists := configuration . TCP . Routers [ name ] ; exists {
2022-11-21 17:36:05 +00:00
logger . Warn ( ) . Str ( logs . RouterName , name ) . Msg ( "TCP router already configured, skipping" )
2017-05-26 13:32:03 +00:00
} else {
2019-03-14 08:30:04 +00:00
configuration . TCP . Routers [ name ] = conf
2018-11-14 09:18:03 +00:00
}
}
2021-06-11 13:30:05 +00:00
for name , conf := range c . TCP . Middlewares {
if _ , exists := configuration . TCP . Middlewares [ name ] ; exists {
2022-11-21 17:36:05 +00:00
logger . Warn ( ) . Str ( logs . MiddlewareName , name ) . Msg ( "TCP middleware already configured, skipping" )
2021-06-11 13:30:05 +00:00
} else {
configuration . TCP . Middlewares [ name ] = conf
}
}
2019-03-14 08:30:04 +00:00
for name , conf := range c . TCP . Services {
if _ , exists := configuration . TCP . Services [ name ] ; exists {
2022-11-21 17:36:05 +00:00
logger . Warn ( ) . Str ( logs . ServiceName , name ) . Msg ( "TCP service already configured, skipping" )
2018-11-14 09:18:03 +00:00
} else {
2019-03-14 08:30:04 +00:00
configuration . TCP . Services [ name ] = conf
2017-05-26 13:32:03 +00:00
}
}
2022-12-09 08:58:05 +00:00
for name , conf := range c . TCP . ServersTransports {
if _ , exists := configuration . TCP . ServersTransports [ name ] ; exists {
logger . Warn ( ) . Str ( logs . ServersTransportName , name ) . Msg ( "TCP servers transport already configured, skipping" )
} else {
configuration . TCP . ServersTransports [ name ] = conf
}
}
2020-02-11 00:26:04 +00:00
for name , conf := range c . UDP . Routers {
if _ , exists := configuration . UDP . Routers [ name ] ; exists {
2022-11-21 17:36:05 +00:00
logger . Warn ( ) . Str ( logs . RouterName , name ) . Msg ( "UDP router already configured, skipping" )
2020-02-11 00:26:04 +00:00
} else {
configuration . UDP . Routers [ name ] = conf
}
}
for name , conf := range c . UDP . Services {
if _ , exists := configuration . UDP . Services [ name ] ; exists {
2022-11-21 17:36:05 +00:00
logger . Warn ( ) . Str ( logs . ServiceName , name ) . Msg ( "UDP service already configured, skipping" )
2020-02-11 00:26:04 +00:00
} else {
configuration . UDP . Services [ name ] = conf
}
}
2019-06-27 21:58:03 +00:00
for _ , conf := range c . TLS . Certificates {
2017-11-09 11:16:03 +00:00
if _ , exists := configTLSMaps [ conf ] ; exists {
2022-11-21 17:36:05 +00:00
logger . Warn ( ) . Msgf ( "TLS configuration %v already configured, skipping" , conf )
2017-11-09 11:16:03 +00:00
} else {
configTLSMaps [ conf ] = struct { } { }
}
}
2019-07-18 14:26:05 +00:00
for name , conf := range c . TLS . Options {
if _ , exists := configuration . TLS . Options [ name ] ; exists {
2022-11-21 17:36:05 +00:00
logger . Warn ( ) . Msgf ( "TLS options %v already configured, skipping" , name )
2019-07-18 14:26:05 +00:00
} else {
if configuration . TLS . Options == nil {
configuration . TLS . Options = map [ string ] tls . Options { }
}
configuration . TLS . Options [ name ] = conf
}
}
for name , conf := range c . TLS . Stores {
if _ , exists := configuration . TLS . Stores [ name ] ; exists {
2022-11-21 17:36:05 +00:00
logger . Warn ( ) . Msgf ( "TLS store %v already configured, skipping" , name )
2019-07-18 14:26:05 +00:00
} else {
if configuration . TLS . Stores == nil {
configuration . TLS . Stores = map [ string ] tls . Store { }
}
configuration . TLS . Stores [ name ] = conf
}
}
2017-11-09 11:16:03 +00:00
}
2018-11-14 09:18:03 +00:00
2019-07-18 14:26:05 +00:00
if len ( configTLSMaps ) > 0 && configuration . TLS == nil {
2019-07-10 07:26:04 +00:00
configuration . TLS = & dynamic . TLSConfiguration { }
2019-06-27 21:58:03 +00:00
}
2017-11-09 11:16:03 +00:00
for conf := range configTLSMaps {
2019-06-27 21:58:03 +00:00
configuration . TLS . Certificates = append ( configuration . TLS . Certificates , conf )
2017-11-09 11:16:03 +00:00
}
2019-06-27 21:58:03 +00:00
2017-05-26 13:32:03 +00:00
return configuration , nil
}
2019-03-27 14:02:06 +00:00
// CreateConfiguration creates a provider configuration from content using templating.
2019-09-13 17:28:04 +00:00
func ( p * Provider ) CreateConfiguration ( ctx context . Context , filename string , funcMap template . FuncMap , templateObjects interface { } ) ( * dynamic . Configuration , error ) {
2019-06-26 16:18:04 +00:00
tmplContent , err := readFile ( filename )
if err != nil {
2020-05-11 10:06:07 +00:00
return nil , fmt . Errorf ( "error reading configuration file: %s - %w" , filename , err )
2019-06-26 16:18:04 +00:00
}
2020-07-07 12:42:03 +00:00
defaultFuncMap := sprig . TxtFuncMap ( )
2019-03-27 14:02:06 +00:00
defaultFuncMap [ "normalize" ] = provider . Normalize
defaultFuncMap [ "split" ] = strings . Split
for funcID , funcElement := range funcMap {
defaultFuncMap [ funcID ] = funcElement
}
tmpl := template . New ( p . Filename ) . Funcs ( defaultFuncMap )
2019-06-26 16:18:04 +00:00
_ , err = tmpl . Parse ( tmplContent )
2019-03-27 14:02:06 +00:00
if err != nil {
return nil , err
}
var buffer bytes . Buffer
err = tmpl . Execute ( & buffer , templateObjects )
if err != nil {
return nil , err
}
2020-07-07 12:42:03 +00:00
renderedTemplate := buffer . String ( )
2019-03-27 14:02:06 +00:00
if p . DebugLogGeneratedTemplate {
2022-11-21 17:36:05 +00:00
logger := log . Ctx ( ctx )
logger . Debug ( ) . Msgf ( "Template content: %s" , tmplContent )
logger . Debug ( ) . Msgf ( "Rendering results: %s" , renderedTemplate )
2019-03-27 14:02:06 +00:00
}
2019-06-26 16:18:04 +00:00
return p . decodeConfiguration ( filename , renderedTemplate )
2019-03-27 14:02:06 +00:00
}
// DecodeConfiguration Decodes a *types.Configuration from a content.
2019-07-10 07:26:04 +00:00
func ( p * Provider ) DecodeConfiguration ( filename string ) ( * dynamic . Configuration , error ) {
2019-06-26 16:18:04 +00:00
content , err := readFile ( filename )
if err != nil {
2020-05-11 10:06:07 +00:00
return nil , fmt . Errorf ( "error reading configuration file: %s - %w" , filename , err )
2019-06-26 16:18:04 +00:00
}
return p . decodeConfiguration ( filename , content )
}
2020-07-07 12:42:03 +00:00
func ( p * Provider ) decodeConfiguration ( filePath , content string ) ( * dynamic . Configuration , error ) {
2019-07-10 07:26:04 +00:00
configuration := & dynamic . Configuration {
HTTP : & dynamic . HTTPConfiguration {
2020-09-11 13:40:03 +00:00
Routers : make ( map [ string ] * dynamic . Router ) ,
Middlewares : make ( map [ string ] * dynamic . Middleware ) ,
Services : make ( map [ string ] * dynamic . Service ) ,
ServersTransports : make ( map [ string ] * dynamic . ServersTransport ) ,
2019-03-27 14:02:06 +00:00
} ,
2019-07-10 07:26:04 +00:00
TCP : & dynamic . TCPConfiguration {
2022-12-09 08:58:05 +00:00
Routers : make ( map [ string ] * dynamic . TCPRouter ) ,
Services : make ( map [ string ] * dynamic . TCPService ) ,
Middlewares : make ( map [ string ] * dynamic . TCPMiddleware ) ,
ServersTransports : make ( map [ string ] * dynamic . TCPServersTransport ) ,
2019-03-27 14:02:06 +00:00
} ,
2019-07-10 07:26:04 +00:00
TLS : & dynamic . TLSConfiguration {
2019-06-27 21:58:03 +00:00
Stores : make ( map [ string ] tls . Store ) ,
Options : make ( map [ string ] tls . Options ) ,
} ,
2020-02-11 00:26:04 +00:00
UDP : & dynamic . UDPConfiguration {
Routers : make ( map [ string ] * dynamic . UDPRouter ) ,
Services : make ( map [ string ] * dynamic . UDPService ) ,
} ,
2019-03-27 14:02:06 +00:00
}
2019-06-26 16:18:04 +00:00
2020-06-17 14:48:04 +00:00
err := file . DecodeContent ( content , strings . ToLower ( filepath . Ext ( filePath ) ) , configuration )
if err != nil {
return nil , err
2019-03-27 14:02:06 +00:00
}
2019-06-26 16:18:04 +00:00
2019-03-27 14:02:06 +00:00
return configuration , nil
}
2019-06-26 16:18:04 +00:00
func readFile ( filename string ) ( string , error ) {
if len ( filename ) > 0 {
2021-03-04 19:08:03 +00:00
buf , err := os . ReadFile ( filename )
2019-06-26 16:18:04 +00:00
if err != nil {
return "" , err
}
return string ( buf ) , nil
}
return "" , fmt . Errorf ( "invalid filename: %s" , filename )
}