Added support for templates to file provider

This commit is contained in:
Alex Antonov 2018-03-22 11:14:04 -04:00 committed by Traefiker Bot
parent d2e84a700f
commit 1a411b658b
5 changed files with 59 additions and 28 deletions

View file

@ -239,8 +239,8 @@ func (gc *GlobalConfiguration) SetEffectiveConfiguration(configFile string) {
}
// Try to fallback to traefik config file in case the file provider is enabled
// but has no file name configured.
if gc.File != nil && len(gc.File.Filename) == 0 {
// but has no file name configured and is not in a directory mode.
if gc.File != nil && len(gc.File.Filename) == 0 && len(gc.File.Directory) == 0 {
if len(configFile) > 0 {
gc.File.Filename = configFile
} else {

View file

@ -77,6 +77,11 @@ func TestSetEffectiveConfigurationFileProviderFilename(t *testing.T) {
fileProvider: &file.Provider{BaseProvider: provider.BaseProvider{Filename: "other.toml"}},
wantFileProviderFilename: "other.toml",
},
{
desc: "directory for file provider given",
fileProvider: &file.Provider{Directory: "/"},
wantFileProviderFilename: "",
},
}
for _, test := range tests {

View file

@ -7,8 +7,8 @@ import (
"path"
"path/filepath"
"strings"
"text/template"
"github.com/BurntSushi/toml"
"github.com/containous/traefik/log"
"github.com/containous/traefik/provider"
"github.com/containous/traefik/safe"
@ -56,9 +56,9 @@ func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *s
// and returns a 'Configuration' object
func (p *Provider) BuildConfiguration() (*types.Configuration, error) {
if p.Directory != "" {
return loadFileConfigFromDirectory(p.Directory, nil)
return p.loadFileConfigFromDirectory(p.Directory, nil)
}
return loadFileConfig(p.Filename)
return p.loadFileConfig(p.Filename)
}
func (p *Provider) addWatcher(pool *safe.Pool, directory string, configurationChan chan<- types.ConfigMessage, callback func(chan<- types.ConfigMessage, fsnotify.Event)) error {
@ -125,18 +125,36 @@ func sendConfigToChannel(configurationChan chan<- types.ConfigMessage, configura
}
}
func loadFileConfig(filename string) (*types.Configuration, error) {
configuration := &types.Configuration{
Frontends: make(map[string]*types.Frontend),
Backends: make(map[string]*types.Backend),
func readFile(filename string) (string, error) {
if len(filename) > 0 {
buf, err := ioutil.ReadFile(filename)
if err != nil {
return "", err
}
return string(buf), nil
}
if _, err := toml.DecodeFile(filename, configuration); err != nil {
return nil, fmt.Errorf("error reading configuration file: %s", err)
}
return configuration, nil
return "", fmt.Errorf("invalid filename: %s", filename)
}
func loadFileConfigFromDirectory(directory string, configuration *types.Configuration) (*types.Configuration, error) {
func (p *Provider) loadFileConfig(filename string) (*types.Configuration, error) {
fileContent, err := readFile(filename)
if err != nil {
return nil, fmt.Errorf("error reading configuration file: %s - %s", filename, err)
}
configuration, err := p.CreateConfiguration(fileContent, template.FuncMap{}, false)
if err != nil {
return nil, err
}
if configuration == nil || configuration.Backends == nil && configuration.Frontends == nil && configuration.TLS == nil {
configuration = &types.Configuration{
Frontends: make(map[string]*types.Frontend),
Backends: make(map[string]*types.Backend),
}
}
return configuration, err
}
func (p *Provider) loadFileConfigFromDirectory(directory string, configuration *types.Configuration) (*types.Configuration, error) {
fileList, err := ioutil.ReadDir(directory)
if err != nil {
@ -154,17 +172,17 @@ func loadFileConfigFromDirectory(directory string, configuration *types.Configur
for _, item := range fileList {
if item.IsDir() {
configuration, err = loadFileConfigFromDirectory(filepath.Join(directory, item.Name()), configuration)
configuration, err = p.loadFileConfigFromDirectory(filepath.Join(directory, item.Name()), configuration)
if err != nil {
return configuration, fmt.Errorf("unable to load content configuration from subdirectory %s: %v", item, err)
}
continue
} else if !strings.HasSuffix(item.Name(), ".toml") {
} else if !strings.HasSuffix(item.Name(), ".toml") && !strings.HasSuffix(item.Name(), ".tmpl") {
continue
}
var c *types.Configuration
c, err = loadFileConfig(path.Join(directory, item.Name()))
c, err = p.loadFileConfig(path.Join(directory, item.Name()))
if err != nil {
return configuration, err

View file

@ -276,10 +276,13 @@ func createSubDir(t *testing.T, rootDir, dir string) string {
// createFrontendConfiguration Helper
func createFrontendConfiguration(n int) string {
conf := "[frontends]\n"
conf := "{{$home := env \"HOME\"}}\n[frontends]\n"
for i := 1; i <= n; i++ {
conf += fmt.Sprintf(` [frontends.frontend%[1]d]
conf += fmt.Sprintf(` [frontends."frontend%[1]d"]
backend = "backend%[1]d"
`, i)
conf += fmt.Sprintf(` [frontends."frontend%[1]d".headers]
"PublicKey" = "{{$home}}/pub.key"
`, i)
}
return conf

View file

@ -50,8 +50,17 @@ func (p *BaseProvider) MatchConstraints(tags []string) (bool, *types.Constraint)
return true, nil
}
// GetConfiguration return the provider configuration using templating
func (p *BaseProvider) GetConfiguration(defaultTemplateFile string, funcMap template.FuncMap, templateObjects interface{}) (*types.Configuration, error) {
// 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) {
configuration := new(types.Configuration)
var defaultFuncMap = sprig.TxtFuncMap()
@ -65,12 +74,7 @@ func (p *BaseProvider) GetConfiguration(defaultTemplateFile string, funcMap temp
tmpl := template.New(p.Filename).Funcs(defaultFuncMap)
tmplContent, err := p.getTemplateContent(defaultTemplateFile)
if err != nil {
return nil, err
}
_, err = tmpl.Parse(tmplContent)
_, err := tmpl.Parse(tmplContent)
if err != nil {
return nil, err
}
@ -83,7 +87,8 @@ func (p *BaseProvider) GetConfiguration(defaultTemplateFile string, funcMap temp
var renderedTemplate = buffer.String()
if p.DebugLogGeneratedTemplate {
log.Debugf("Rendering results of %s:\n%s", defaultTemplateFile, renderedTemplate)
log.Debugf("Template content: %s", tmplContent)
log.Debugf("Rendering results: %s", renderedTemplate)
}
if _, err := toml.Decode(renderedTemplate, configuration); err != nil {
return nil, err