2015-11-01 16:35:01 +01:00
package provider
2015-11-13 11:50:32 +01:00
import (
"bytes"
2016-12-30 09:21:13 +01:00
"io/ioutil"
"strings"
"text/template"
"unicode"
2016-08-13 12:55:15 -04:00
2015-11-13 11:50:32 +01:00
"github.com/BurntSushi/toml"
2017-08-10 20:42:39 +02:00
"github.com/Masterminds/sprig"
2017-11-20 15:26:03 +01:00
"github.com/containous/traefik/autogen/gentemplates"
2016-09-23 18:27:01 +02:00
"github.com/containous/traefik/log"
2016-04-13 20:36:23 +02:00
"github.com/containous/traefik/safe"
2016-02-24 16:43:39 +01:00
"github.com/containous/traefik/types"
2015-11-13 11:50:32 +01:00
)
2015-11-01 16:35:01 +01:00
2015-11-01 19:29:47 +01:00
// Provider defines methods of a provider.
2015-11-01 16:35:01 +01:00
type Provider interface {
2015-11-01 19:29:47 +01:00
// Provide allows the provider to provide configurations to traefik
// using the given configuration channel.
2018-07-11 09:08:03 +02:00
Provide ( configurationChan chan <- types . ConfigMessage , pool * safe . Pool ) error
Init ( constraints types . Constraints ) error
2015-11-01 16:35:01 +01:00
}
2015-11-13 11:50:32 +01:00
2016-01-13 22:46:44 +01:00
// BaseProvider should be inherited by providers
type BaseProvider struct {
2017-10-02 10:32:02 +02:00
Watch bool ` description:"Watch provider" export:"true" `
Filename string ` description:"Override default configuration template. For advanced users :)" export:"true" `
Constraints types . Constraints ` description:"Filter services by constraint, matching with Traefik tags." export:"true" `
Trace bool ` description:"Display additional provider logs (if available)." export:"true" `
2018-03-23 13:30:03 +01:00
TemplateVersion int ` description:"Template version." export:"true" `
2017-10-02 10:32:02 +02:00
DebugLogGeneratedTemplate bool ` description:"Enable debug logging of generated configuration template." export:"true" `
2016-05-30 15:05:58 +02:00
}
2018-07-11 09:08:03 +02:00
// Init for compatibility reason the BaseProvider implements an empty Init
func ( p * BaseProvider ) Init ( constraints types . Constraints ) error {
p . Constraints = append ( p . Constraints , constraints ... )
return nil
}
2017-12-04 20:04:08 +01:00
// MatchConstraints must match with EVERY single constraint
2016-05-30 15:05:58 +02:00
// returns first constraint that do not match or nil
2016-05-20 17:17:38 +02:00
func ( p * BaseProvider ) MatchConstraints ( tags [ ] string ) ( bool , * types . Constraint ) {
2017-10-10 11:10:02 +02:00
// if there is no tags and no constraints, filtering is disabled
2016-05-30 15:05:58 +02:00
if len ( tags ) == 0 && len ( p . Constraints ) == 0 {
2016-05-20 17:17:38 +02:00
return true , nil
2016-05-30 15:05:58 +02:00
}
for _ , constraint := range p . Constraints {
2016-05-20 17:17:38 +02:00
// xor: if ok and constraint.MustMatch are equal, then no tag is currently matching with the constraint
if ok := constraint . MatchConstraintWithAtLeastOneTag ( tags ) ; ok != constraint . MustMatch {
2016-11-09 19:27:04 +01:00
return false , constraint
2016-05-30 15:05:58 +02:00
}
}
// If no constraint or every constraints matching
2016-05-20 17:17:38 +02:00
return true , nil
2015-11-13 11:50:32 +01:00
}
2018-03-22 11:14:04 -04:00
// GetConfiguration return the provider configuration from default template (file or content) or overrode template file
func ( p * BaseProvider ) GetConfiguration ( defaultTemplate string , funcMap template . FuncMap , templateObjects interface { } ) ( * types . Configuration , error ) {
tmplContent , err := p . getTemplateContent ( defaultTemplate )
if err != nil {
return nil , err
}
return p . CreateConfiguration ( tmplContent , funcMap , templateObjects )
}
// CreateConfiguration create a provider configuration from content using templating
func ( p * BaseProvider ) CreateConfiguration ( tmplContent string , funcMap template . FuncMap , templateObjects interface { } ) ( * types . Configuration , error ) {
2017-08-10 20:42:39 +02:00
var defaultFuncMap = sprig . TxtFuncMap ( )
// tolower is deprecated in favor of sprig's lower function
defaultFuncMap [ "tolower" ] = strings . ToLower
defaultFuncMap [ "normalize" ] = Normalize
defaultFuncMap [ "split" ] = split
2016-12-03 10:12:22 +01:00
for funcID , funcElement := range funcMap {
defaultFuncMap [ funcID ] = funcElement
}
tmpl := template . New ( p . Filename ) . Funcs ( defaultFuncMap )
2017-11-17 10:12:03 +01:00
2018-03-22 11:14:04 -04:00
_ , err := tmpl . Parse ( tmplContent )
2015-11-13 11:50:32 +01:00
if err != nil {
return nil , err
}
var buffer bytes . Buffer
err = tmpl . Execute ( & buffer , templateObjects )
if err != nil {
return nil , err
}
2016-08-13 12:55:15 -04:00
var renderedTemplate = buffer . String ( )
2017-08-21 10:46:03 +02:00
if p . DebugLogGeneratedTemplate {
2018-03-22 11:14:04 -04:00
log . Debugf ( "Template content: %s" , tmplContent )
log . Debugf ( "Rendering results: %s" , renderedTemplate )
2017-08-21 10:46:03 +02:00
}
2018-05-22 12:02:03 +02:00
return p . DecodeConfiguration ( renderedTemplate )
}
// DecodeConfiguration Decode a *types.Configuration from a content
func ( p * BaseProvider ) DecodeConfiguration ( content string ) ( * types . Configuration , error ) {
configuration := new ( types . Configuration )
if _ , err := toml . Decode ( content , configuration ) ; err != nil {
2015-11-13 11:50:32 +01:00
return nil , err
}
return configuration , nil
}
2017-11-17 10:12:03 +01:00
func ( p * BaseProvider ) getTemplateContent ( defaultTemplateFile string ) ( string , error ) {
if len ( p . Filename ) > 0 {
buf , err := ioutil . ReadFile ( p . Filename )
if err != nil {
return "" , err
}
return string ( buf ) , nil
}
if strings . HasSuffix ( defaultTemplateFile , ".tmpl" ) {
2017-11-20 15:26:03 +01:00
buf , err := gentemplates . Asset ( defaultTemplateFile )
2017-11-17 10:12:03 +01:00
if err != nil {
return "" , err
}
return string ( buf ) , nil
}
return defaultTemplateFile , nil
}
2016-12-03 10:12:22 +01:00
func split ( sep , s string ) [ ] string {
return strings . Split ( s , sep )
}
2017-04-15 16:46:44 +02:00
// Normalize transform a string that work with the rest of traefik
2017-12-04 20:04:08 +01:00
// Replace '.' with '-' in quoted keys because of this issue https://github.com/BurntSushi/toml/issues/78
2017-04-15 15:49:53 +02:00
func Normalize ( name string ) string {
2016-03-27 01:05:17 +01:00
fargs := func ( c rune ) bool {
return ! unicode . IsLetter ( c ) && ! unicode . IsNumber ( c )
}
// get function
return strings . Join ( strings . FieldsFunc ( name , fargs ) , "-" )
}
2016-06-27 16:14:56 +02:00
2017-04-17 12:50:02 +02:00
// ReverseStringSlice invert the order of the given slice of string
func ReverseStringSlice ( slice * [ ] string ) {
2016-11-28 08:59:08 -05:00
for i , j := 0 , len ( * slice ) - 1 ; i < j ; i , j = i + 1 , j - 1 {
( * slice ) [ i ] , ( * slice ) [ j ] = ( * slice ) [ j ] , ( * slice ) [ i ]
}
}