2017-04-17 12:50:02 +02:00
package file
2015-09-07 15:25:13 +02:00
import (
2017-05-26 14:32:03 +01:00
"fmt"
"io/ioutil"
"path"
2015-09-07 17:39:22 +02:00
"strings"
2015-09-24 17:16:13 +02:00
"github.com/BurntSushi/toml"
2016-08-18 14:20:11 +02:00
"github.com/containous/traefik/log"
2017-04-17 12:50:02 +02:00
"github.com/containous/traefik/provider"
2016-03-31 18:57:08 +02:00
"github.com/containous/traefik/safe"
2016-02-24 16:43:39 +01:00
"github.com/containous/traefik/types"
2015-09-24 17:16:13 +02:00
"gopkg.in/fsnotify.v1"
2015-09-07 15:25:13 +02:00
)
2017-04-17 12:50:02 +02:00
var _ provider . Provider = ( * Provider ) ( nil )
2016-08-16 19:13:18 +02:00
2017-04-17 12:50:02 +02:00
// Provider holds configurations of the provider.
type Provider struct {
provider . BaseProvider ` mapstructure:",squash" `
2017-05-26 14:32:03 +01:00
Directory string ` description:"Load configuration from one or more .toml files in a directory" `
2015-09-07 15:25:13 +02:00
}
2017-04-17 12:50:02 +02:00
// Provide allows the file provider to provide configurations to traefik
2015-11-01 19:29:47 +01:00
// using the given configuration channel.
2017-04-17 12:50:02 +02:00
func ( p * Provider ) Provide ( configurationChan chan <- types . ConfigMessage , pool * safe . Pool , constraints types . Constraints ) error {
2017-05-26 14:32:03 +01:00
configuration , err := p . loadConfig ( )
2015-09-07 15:25:13 +02:00
if err != nil {
2015-10-01 12:04:25 +02:00
return err
2015-09-07 15:25:13 +02:00
}
2017-05-26 14:32:03 +01:00
if p . Watch {
var watchItem string
if p . Directory != "" {
watchItem = p . Directory
} else {
watchItem = p . Filename
}
if err := p . addWatcher ( pool , watchItem , configurationChan , p . watcherCallback ) ; err != nil {
return err
}
}
sendConfigToChannel ( configurationChan , configuration )
return nil
}
func ( p * Provider ) addWatcher ( pool * safe . Pool , directory string , configurationChan chan <- types . ConfigMessage , callback func ( chan <- types . ConfigMessage , fsnotify . Event ) ) error {
watcher , err := fsnotify . NewWatcher ( )
2015-09-07 15:25:13 +02:00
if err != nil {
2017-05-26 14:32:03 +01:00
return fmt . Errorf ( "error creating file watcher: %s" , err )
2015-09-07 15:25:13 +02:00
}
2017-05-26 14:32:03 +01:00
// Process events
pool . Go ( func ( stop chan bool ) {
defer watcher . Close ( )
for {
select {
case <- stop :
return
case evt := <- watcher . Events :
callback ( configurationChan , evt )
case err := <- watcher . Errors :
log . Errorf ( "Watcher event error: %s" , err )
2015-09-07 15:25:13 +02:00
}
2015-10-03 16:50:53 +02:00
}
2017-05-26 14:32:03 +01:00
} )
err = watcher . Add ( directory )
if err != nil {
return fmt . Errorf ( "error adding file watcher: %s" , err )
2015-09-07 17:39:22 +02:00
}
2015-09-07 15:25:13 +02:00
2017-05-26 14:32:03 +01:00
return nil
}
func sendConfigToChannel ( configurationChan chan <- types . ConfigMessage , configuration * types . Configuration ) {
2015-11-13 11:50:32 +01:00
configurationChan <- types . ConfigMessage {
ProviderName : "file" ,
Configuration : configuration ,
}
2015-09-07 15:25:13 +02:00
}
2017-05-26 14:32:03 +01:00
func loadFileConfig ( filename string ) ( * types . Configuration , error ) {
2015-11-01 16:35:01 +01:00
configuration := new ( types . Configuration )
2015-09-08 00:15:14 +02:00
if _ , err := toml . DecodeFile ( filename , configuration ) ; err != nil {
2017-05-26 14:32:03 +01:00
return nil , fmt . Errorf ( "error reading configuration file: %s" , err )
}
return configuration , nil
}
func loadFileConfigFromDirectory ( directory string ) ( * types . Configuration , error ) {
fileList , err := ioutil . ReadDir ( directory )
if err != nil {
return nil , fmt . Errorf ( "unable to read directory %s: %v" , directory , err )
}
configuration := & types . Configuration {
Frontends : make ( map [ string ] * types . Frontend ) ,
Backends : make ( map [ string ] * types . Backend ) ,
2015-09-07 15:25:13 +02:00
}
2017-05-26 14:32:03 +01:00
for _ , file := range fileList {
if ! strings . HasSuffix ( file . Name ( ) , ".toml" ) {
continue
}
var c * types . Configuration
c , err = loadFileConfig ( path . Join ( directory , file . Name ( ) ) )
if err != nil {
return nil , err
}
for backendName , backend := range c . Backends {
if _ , exists := configuration . Backends [ backendName ] ; exists {
log . Warnf ( "Backend %s already configured, skipping" , backendName )
} else {
configuration . Backends [ backendName ] = backend
}
}
for frontendName , frontend := range c . Frontends {
if _ , exists := configuration . Frontends [ frontendName ] ; exists {
log . Warnf ( "Frontend %s already configured, skipping" , frontendName )
} else {
configuration . Frontends [ frontendName ] = frontend
}
}
}
return configuration , nil
}
func ( p * Provider ) watcherCallback ( configurationChan chan <- types . ConfigMessage , event fsnotify . Event ) {
configuration , err := p . loadConfig ( )
if err != nil {
log . Errorf ( "Error occurred during watcher callback: %s" , err )
return
}
sendConfigToChannel ( configurationChan , configuration )
}
func ( p * Provider ) loadConfig ( ) ( * types . Configuration , error ) {
if p . Directory != "" {
return loadFileConfigFromDirectory ( p . Directory )
}
return loadFileConfig ( p . Filename )
2015-09-12 15:10:03 +02:00
}