diff --git a/configuration/configuration.go b/configuration/configuration.go index 48861e844..4a85f7544 100644 --- a/configuration/configuration.go +++ b/configuration/configuration.go @@ -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 { diff --git a/configuration/configuration_test.go b/configuration/configuration_test.go index fa6a15894..df9bb9bc4 100644 --- a/configuration/configuration_test.go +++ b/configuration/configuration_test.go @@ -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 { diff --git a/provider/file/file.go b/provider/file/file.go index 137b20cf3..ded4ac123 100644 --- a/provider/file/file.go +++ b/provider/file/file.go @@ -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 diff --git a/provider/file/file_test.go b/provider/file/file_test.go index 649fc32ae..8a6caf660 100644 --- a/provider/file/file_test.go +++ b/provider/file/file_test.go @@ -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 diff --git a/provider/provider.go b/provider/provider.go index eec8c7a91..2ea0e2dc4 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -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