Added support for templates to file provider
This commit is contained in:
parent
d2e84a700f
commit
1a411b658b
5 changed files with 59 additions and 28 deletions
|
@ -239,8 +239,8 @@ func (gc *GlobalConfiguration) SetEffectiveConfiguration(configFile string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to fallback to traefik config file in case the file provider is enabled
|
// Try to fallback to traefik config file in case the file provider is enabled
|
||||||
// but has no file name configured.
|
// but has no file name configured and is not in a directory mode.
|
||||||
if gc.File != nil && len(gc.File.Filename) == 0 {
|
if gc.File != nil && len(gc.File.Filename) == 0 && len(gc.File.Directory) == 0 {
|
||||||
if len(configFile) > 0 {
|
if len(configFile) > 0 {
|
||||||
gc.File.Filename = configFile
|
gc.File.Filename = configFile
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -77,6 +77,11 @@ func TestSetEffectiveConfigurationFileProviderFilename(t *testing.T) {
|
||||||
fileProvider: &file.Provider{BaseProvider: provider.BaseProvider{Filename: "other.toml"}},
|
fileProvider: &file.Provider{BaseProvider: provider.BaseProvider{Filename: "other.toml"}},
|
||||||
wantFileProviderFilename: "other.toml",
|
wantFileProviderFilename: "other.toml",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
desc: "directory for file provider given",
|
||||||
|
fileProvider: &file.Provider{Directory: "/"},
|
||||||
|
wantFileProviderFilename: "",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
|
|
|
@ -7,8 +7,8 @@ import (
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
"github.com/BurntSushi/toml"
|
|
||||||
"github.com/containous/traefik/log"
|
"github.com/containous/traefik/log"
|
||||||
"github.com/containous/traefik/provider"
|
"github.com/containous/traefik/provider"
|
||||||
"github.com/containous/traefik/safe"
|
"github.com/containous/traefik/safe"
|
||||||
|
@ -56,9 +56,9 @@ func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *s
|
||||||
// and returns a 'Configuration' object
|
// and returns a 'Configuration' object
|
||||||
func (p *Provider) BuildConfiguration() (*types.Configuration, error) {
|
func (p *Provider) BuildConfiguration() (*types.Configuration, error) {
|
||||||
if p.Directory != "" {
|
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 {
|
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) {
|
func readFile(filename string) (string, error) {
|
||||||
configuration := &types.Configuration{
|
if len(filename) > 0 {
|
||||||
Frontends: make(map[string]*types.Frontend),
|
buf, err := ioutil.ReadFile(filename)
|
||||||
Backends: make(map[string]*types.Backend),
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(buf), nil
|
||||||
}
|
}
|
||||||
if _, err := toml.DecodeFile(filename, configuration); err != nil {
|
return "", fmt.Errorf("invalid filename: %s", filename)
|
||||||
return nil, fmt.Errorf("error reading configuration file: %s", err)
|
|
||||||
}
|
|
||||||
return configuration, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
fileList, err := ioutil.ReadDir(directory)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -154,17 +172,17 @@ func loadFileConfigFromDirectory(directory string, configuration *types.Configur
|
||||||
for _, item := range fileList {
|
for _, item := range fileList {
|
||||||
|
|
||||||
if item.IsDir() {
|
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 {
|
if err != nil {
|
||||||
return configuration, fmt.Errorf("unable to load content configuration from subdirectory %s: %v", item, err)
|
return configuration, fmt.Errorf("unable to load content configuration from subdirectory %s: %v", item, err)
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
} else if !strings.HasSuffix(item.Name(), ".toml") {
|
} else if !strings.HasSuffix(item.Name(), ".toml") && !strings.HasSuffix(item.Name(), ".tmpl") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
var c *types.Configuration
|
var c *types.Configuration
|
||||||
c, err = loadFileConfig(path.Join(directory, item.Name()))
|
c, err = p.loadFileConfig(path.Join(directory, item.Name()))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return configuration, err
|
return configuration, err
|
||||||
|
|
|
@ -276,10 +276,13 @@ func createSubDir(t *testing.T, rootDir, dir string) string {
|
||||||
|
|
||||||
// createFrontendConfiguration Helper
|
// createFrontendConfiguration Helper
|
||||||
func createFrontendConfiguration(n int) string {
|
func createFrontendConfiguration(n int) string {
|
||||||
conf := "[frontends]\n"
|
conf := "{{$home := env \"HOME\"}}\n[frontends]\n"
|
||||||
for i := 1; i <= n; i++ {
|
for i := 1; i <= n; i++ {
|
||||||
conf += fmt.Sprintf(` [frontends.frontend%[1]d]
|
conf += fmt.Sprintf(` [frontends."frontend%[1]d"]
|
||||||
backend = "backend%[1]d"
|
backend = "backend%[1]d"
|
||||||
|
`, i)
|
||||||
|
conf += fmt.Sprintf(` [frontends."frontend%[1]d".headers]
|
||||||
|
"PublicKey" = "{{$home}}/pub.key"
|
||||||
`, i)
|
`, i)
|
||||||
}
|
}
|
||||||
return conf
|
return conf
|
||||||
|
|
|
@ -50,8 +50,17 @@ func (p *BaseProvider) MatchConstraints(tags []string) (bool, *types.Constraint)
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetConfiguration return the provider configuration using templating
|
// GetConfiguration return the provider configuration from default template (file or content) or overrode template file
|
||||||
func (p *BaseProvider) GetConfiguration(defaultTemplateFile string, funcMap template.FuncMap, templateObjects interface{}) (*types.Configuration, error) {
|
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)
|
configuration := new(types.Configuration)
|
||||||
|
|
||||||
var defaultFuncMap = sprig.TxtFuncMap()
|
var defaultFuncMap = sprig.TxtFuncMap()
|
||||||
|
@ -65,12 +74,7 @@ func (p *BaseProvider) GetConfiguration(defaultTemplateFile string, funcMap temp
|
||||||
|
|
||||||
tmpl := template.New(p.Filename).Funcs(defaultFuncMap)
|
tmpl := template.New(p.Filename).Funcs(defaultFuncMap)
|
||||||
|
|
||||||
tmplContent, err := p.getTemplateContent(defaultTemplateFile)
|
_, err := tmpl.Parse(tmplContent)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = tmpl.Parse(tmplContent)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -83,7 +87,8 @@ func (p *BaseProvider) GetConfiguration(defaultTemplateFile string, funcMap temp
|
||||||
|
|
||||||
var renderedTemplate = buffer.String()
|
var renderedTemplate = buffer.String()
|
||||||
if p.DebugLogGeneratedTemplate {
|
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 {
|
if _, err := toml.Decode(renderedTemplate, configuration); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
Loading…
Add table
Reference in a new issue