2015-11-01 16:35:01 +01:00
package provider
2015-11-13 11:50:32 +01:00
import (
"bytes"
2016-06-27 16:14:56 +02:00
"crypto/tls"
"crypto/x509"
"fmt"
2016-12-30 09:21:13 +01:00
"io/ioutil"
2016-08-13 12:55:15 -04:00
"os"
2016-12-30 09:21:13 +01:00
"strings"
"text/template"
"unicode"
2016-08-13 12:55:15 -04:00
2015-11-13 11:50:32 +01:00
"github.com/BurntSushi/toml"
2016-02-24 16:43:39 +01:00
"github.com/containous/traefik/autogen"
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.
2016-11-09 19:27:04 +01:00
Provide ( configurationChan chan <- types . ConfigMessage , pool * safe . Pool , 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 {
2016-06-02 15:17:04 +02:00
Watch bool ` description:"Watch provider" `
Filename string ` description:"Override default configuration template. For advanced users :)" `
Constraints types . Constraints ` description:"Filter services by constraint, matching with Traefik tags." `
2016-05-30 15:05:58 +02:00
}
// MatchConstraints must match with EVERY single contraint
// 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 ) {
2016-05-30 15:05:58 +02:00
// if there is no tags and no contraints, filtering is disabled
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
}
2017-04-15 16:46:44 +02:00
// GetConfiguration return the provider configuration using templating
2017-04-15 15:49:53 +02:00
func ( p * BaseProvider ) GetConfiguration ( defaultTemplateFile string , funcMap template . FuncMap , templateObjects interface { } ) ( * types . Configuration , error ) {
2015-11-13 11:50:32 +01:00
var (
buf [ ] byte
err error
)
configuration := new ( types . Configuration )
2016-12-03 10:12:22 +01:00
var defaultFuncMap = template . FuncMap {
2017-04-17 12:50:02 +02:00
"replace" : Replace ,
2016-12-03 10:12:22 +01:00
"tolower" : strings . ToLower ,
2017-04-15 16:46:44 +02:00
"normalize" : Normalize ,
2016-12-03 10:12:22 +01:00
"split" : split ,
"contains" : contains ,
}
for funcID , funcElement := range funcMap {
defaultFuncMap [ funcID ] = funcElement
}
tmpl := template . New ( p . Filename ) . Funcs ( defaultFuncMap )
2015-11-13 11:50:32 +01:00
if len ( p . Filename ) > 0 {
buf , err = ioutil . ReadFile ( p . Filename )
if err != nil {
return nil , err
}
} else {
buf , err = autogen . Asset ( defaultTemplateFile )
if err != nil {
return nil , err
}
}
_ , err = tmpl . Parse ( string ( buf ) )
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 ( )
// log.Debugf("Rendering results of %s:\n%s", defaultTemplateFile, renderedTemplate)
if _ , err := toml . Decode ( renderedTemplate , configuration ) ; err != nil {
2015-11-13 11:50:32 +01:00
return nil , err
}
return configuration , nil
}
2017-04-17 12:50:02 +02:00
// Replace is an alias for strings.Replace
func Replace ( s1 string , s2 string , s3 string ) string {
2015-11-13 11:50:32 +01:00
return strings . Replace ( s3 , s1 , s2 , - 1 )
}
2016-12-03 10:12:22 +01:00
func contains ( substr , s string ) bool {
return strings . Contains ( s , substr )
}
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-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 ]
}
}
2016-06-27 16:14:56 +02:00
// ClientTLS holds TLS specific configurations as client
// CA, Cert and Key can be either path or file contents
type ClientTLS struct {
CA string ` description:"TLS CA" `
Cert string ` description:"TLS cert" `
Key string ` description:"TLS key" `
InsecureSkipVerify bool ` description:"TLS insecure skip verify" `
}
// CreateTLSConfig creates a TLS config from ClientTLS structures
func ( clientTLS * ClientTLS ) CreateTLSConfig ( ) ( * tls . Config , error ) {
var err error
2016-08-13 22:05:43 -04:00
if clientTLS == nil {
log . Warnf ( "clientTLS is nil" )
return nil , nil
}
2016-06-27 16:14:56 +02:00
caPool := x509 . NewCertPool ( )
if clientTLS . CA != "" {
var ca [ ] byte
if _ , errCA := os . Stat ( clientTLS . CA ) ; errCA == nil {
ca , err = ioutil . ReadFile ( clientTLS . CA )
if err != nil {
return nil , fmt . Errorf ( "Failed to read CA. %s" , err )
}
} else {
ca = [ ] byte ( clientTLS . CA )
}
caPool . AppendCertsFromPEM ( ca )
}
cert := tls . Certificate { }
_ , errKeyIsFile := os . Stat ( clientTLS . Key )
if _ , errCertIsFile := os . Stat ( clientTLS . Cert ) ; errCertIsFile == nil {
if errKeyIsFile == nil {
cert , err = tls . LoadX509KeyPair ( clientTLS . Cert , clientTLS . Key )
if err != nil {
return nil , fmt . Errorf ( "Failed to load TLS keypair: %v" , err )
}
} else {
return nil , fmt . Errorf ( "tls cert is a file, but tls key is not" )
}
} else {
if errKeyIsFile != nil {
cert , err = tls . X509KeyPair ( [ ] byte ( clientTLS . Cert ) , [ ] byte ( clientTLS . Key ) )
if err != nil {
return nil , fmt . Errorf ( "Failed to load TLS keypair: %v" , err )
}
} else {
return nil , fmt . Errorf ( "tls key is a file, but tls cert is not" )
}
}
TLSConfig := & tls . Config {
Certificates : [ ] tls . Certificate { cert } ,
RootCAs : caPool ,
InsecureSkipVerify : clientTLS . InsecureSkipVerify ,
}
return TLSConfig , nil
}