Add file to storeconfig

This commit is contained in:
Emile Vauge 2017-11-21 10:24:03 +01:00 committed by Traefiker
parent 0f3e42d463
commit 7ddefcef72
4 changed files with 119 additions and 36 deletions

View file

@ -3,7 +3,7 @@ package main
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
fmtlog "log" stdlog "log"
"github.com/containous/flaeg" "github.com/containous/flaeg"
"github.com/containous/staert" "github.com/containous/staert"
@ -29,15 +29,44 @@ func runStoreConfig(kv *staert.KvSource, traefikConfiguration *TraefikConfigurat
if kv == nil { if kv == nil {
return fmt.Errorf("error using command storeconfig, no Key-value store defined") return fmt.Errorf("error using command storeconfig, no Key-value store defined")
} }
fileConfig := traefikConfiguration.GlobalConfiguration.File
if fileConfig != nil {
traefikConfiguration.GlobalConfiguration.File = nil
if len(fileConfig.Filename) == 0 && len(fileConfig.Directory) == 0 {
fileConfig.Filename = traefikConfiguration.ConfigFile
}
}
jsonConf, err := json.Marshal(traefikConfiguration.GlobalConfiguration) jsonConf, err := json.Marshal(traefikConfiguration.GlobalConfiguration)
if err != nil { if err != nil {
return err return err
} }
fmtlog.Printf("Storing configuration: %s\n", jsonConf) stdlog.Printf("Storing configuration: %s\n", jsonConf)
err = kv.StoreConfig(traefikConfiguration.GlobalConfiguration) err = kv.StoreConfig(traefikConfiguration.GlobalConfiguration)
if err != nil { if err != nil {
return err return err
} }
if fileConfig != nil {
jsonConf, err = json.Marshal(fileConfig)
if err != nil {
return err
}
stdlog.Printf("Storing file configuration: %s\n", jsonConf)
config, err := fileConfig.LoadConfig()
if err != nil {
return err
}
stdlog.Print("Writing config to KV")
err = kv.StoreConfig(config)
if err != nil {
return err
}
}
if traefikConfiguration.GlobalConfiguration.ACME != nil && len(traefikConfiguration.GlobalConfiguration.ACME.StorageFile) > 0 { if traefikConfiguration.GlobalConfiguration.ACME != nil && len(traefikConfiguration.GlobalConfiguration.ACME.StorageFile) > 0 {
// convert ACME json file to KV store // convert ACME json file to KV store
localStore := acme.NewLocalStore(traefikConfiguration.GlobalConfiguration.ACME.StorageFile) localStore := acme.NewLocalStore(traefikConfiguration.GlobalConfiguration.ACME.StorageFile)
@ -45,11 +74,13 @@ func runStoreConfig(kv *staert.KvSource, traefikConfiguration *TraefikConfigurat
if err != nil { if err != nil {
return err return err
} }
meta := cluster.NewMetadata(object) meta := cluster.NewMetadata(object)
err = meta.Marshall() err = meta.Marshall()
if err != nil { if err != nil {
return err return err
} }
source := staert.KvSource{ source := staert.KvSource{
Store: kv, Store: kv,
Prefix: traefikConfiguration.GlobalConfiguration.ACME.Storage, Prefix: traefikConfiguration.GlobalConfiguration.ACME.Storage,

View file

@ -378,8 +378,11 @@ traefik storeconfig [flags] ...
``` ```
This command is here only to automate the [process which upload the configuration into the Key-value store](/user-guide/kv-config/#upload-the-configuration-in-the-key-value-store). This command is here only to automate the [process which upload the configuration into the Key-value store](/user-guide/kv-config/#upload-the-configuration-in-the-key-value-store).
Træfik will not start but the [static configuration](/basics/#static-trfk-configuration) will be uploaded into the Key-value store. Træfik will not start but the [static configuration](/basics/#static-trfk-configuration) will be uploaded into the Key-value store.
If you configured ACME (Let's Encrypt), your registration account and your certificates will also be uploaded. If you configured ACME (Let's Encrypt), your registration account and your certificates will also be uploaded.
If you configured a file backend `[file]`, all your dynamic configuration (backends, frontends...) will be uploaded to the Key-value store.
To upload your ACME certificates to the KV store, get your Traefik TOML file and add the new `storage` option in the `acme` section: To upload your ACME certificates to the KV store, get your Traefik TOML file and add the new `storage` option in the `acme` section:
```toml ```toml

View file

@ -332,10 +332,10 @@ func (s *ConsulSuite) TestCommandStoreConfig(c *check.C) {
c.Assert(err, checker.IsNil) c.Assert(err, checker.IsNil)
// wait for traefik finish without error // wait for traefik finish without error
cmd.Wait() err = cmd.Wait()
c.Assert(err, checker.IsNil)
//CHECK expectedData := map[string]string{
checkmap := map[string]string{
"/traefik/loglevel": "DEBUG", "/traefik/loglevel": "DEBUG",
"/traefik/defaultentrypoints/0": "http", "/traefik/defaultentrypoints/0": "http",
"/traefik/entrypoints/http/address": ":8000", "/traefik/entrypoints/http/address": ":8000",
@ -343,7 +343,7 @@ func (s *ConsulSuite) TestCommandStoreConfig(c *check.C) {
"/traefik/consul/endpoint": consulHost + ":8500", "/traefik/consul/endpoint": consulHost + ":8500",
} }
for key, value := range checkmap { for key, value := range expectedData {
var p *store.KVPair var p *store.KVPair
err = try.Do(60*time.Second, func() error { err = try.Do(60*time.Second, func() error {
p, err = s.kv.Get(key, nil) p, err = s.kv.Get(key, nil)
@ -355,6 +355,54 @@ func (s *ConsulSuite) TestCommandStoreConfig(c *check.C) {
} }
} }
func (s *ConsulSuite) TestCommandStoreConfigWithFile(c *check.C) {
s.setupConsul(c)
consulHost := s.composeProject.Container(c, "consul").NetworkSettings.IPAddress
cmd, display := s.traefikCmd(
"storeconfig",
withConfigFile("fixtures/simple_default.toml"),
"--consul.endpoint="+consulHost+":8500",
"--file.filename=fixtures/file/dir/simple1.toml")
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
// wait for traefik finish without error
err = cmd.Wait()
c.Assert(err, checker.IsNil)
expectedData := map[string]string{
"/traefik/backends/backend1/servers/server1/url": "http://172.17.0.2:80",
"/traefik/frontends/frontend1/backend": "backend1",
"/traefik/frontends/frontend1/routes/test_1/rule": "Path:/test1",
}
for key, value := range expectedData {
var p *store.KVPair
err = try.Do(10*time.Second, func() error {
p, err = s.kv.Get(key, nil)
return err
})
c.Assert(err, checker.IsNil)
c.Assert(string(p.Value), checker.Equals, value)
}
checkNotExistsMap := []string{
"/traefik/file",
}
for _, value := range checkNotExistsMap {
err = try.Do(10*time.Second, func() error {
if exists, err := s.kv.Exists(value, nil); err == nil && exists {
return fmt.Errorf("%s key is not suppose to exist in KV", value)
}
return nil
})
c.Assert(err, checker.IsNil)
}
}
type TestStruct struct { type TestStruct struct {
String string String string
Int int Int int

View file

@ -28,7 +28,7 @@ type Provider struct {
// Provide allows the file provider to provide configurations to traefik // Provide allows the file provider to provide configurations to traefik
// using the given configuration channel. // using the given configuration channel.
func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error { func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error {
configuration, err := p.loadConfig() configuration, err := p.LoadConfig()
if err != nil { if err != nil {
return err return err
@ -52,6 +52,15 @@ func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *s
return nil return nil
} }
// LoadConfig loads configuration either from file or a directory specified by 'Filename'/'Directory'
// and returns a 'Configuration' object
func (p *Provider) LoadConfig() (*types.Configuration, error) {
if p.Directory != "" {
return loadFileConfigFromDirectory(p.Directory, nil)
}
return 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 {
watcher, err := fsnotify.NewWatcher() watcher, err := fsnotify.NewWatcher()
if err != nil { if err != nil {
@ -88,6 +97,27 @@ func (p *Provider) addWatcher(pool *safe.Pool, directory string, configurationCh
return nil return nil
} }
func (p *Provider) watcherCallback(configurationChan chan<- types.ConfigMessage, event fsnotify.Event) {
watchItem := p.Filename
if p.Directory != "" {
watchItem = p.Directory
}
if _, err := os.Stat(watchItem); err != nil {
log.Debugf("Unable to watch %s : %v", watchItem, err)
return
}
configuration, err := p.LoadConfig()
if err != nil {
log.Errorf("Error occurred during watcher callback: %s", err)
return
}
sendConfigToChannel(configurationChan, configuration)
}
func sendConfigToChannel(configurationChan chan<- types.ConfigMessage, configuration *types.Configuration) { func sendConfigToChannel(configurationChan chan<- types.ConfigMessage, configuration *types.Configuration) {
configurationChan <- types.ConfigMessage{ configurationChan <- types.ConfigMessage{
ProviderName: "file", ProviderName: "file",
@ -168,32 +198,3 @@ func loadFileConfigFromDirectory(directory string, configuration *types.Configur
} }
return configuration, nil return configuration, nil
} }
func (p *Provider) watcherCallback(configurationChan chan<- types.ConfigMessage, event fsnotify.Event) {
watchItem := p.Filename
if p.Directory != "" {
watchItem = p.Directory
}
if _, err := os.Stat(watchItem); err != nil {
log.Debugf("Unable to watch %s : %v", watchItem, err)
return
}
configuration, err := p.loadConfig()
if err != nil {
log.Errorf("Error occurred during watcher callback: %s", err)
return
}
sendConfigToChannel(configurationChan, configuration)
}
func (p *Provider) loadConfig() (*types.Configuration, error) {
if p.Directory != "" {
return loadFileConfigFromDirectory(p.Directory, nil)
}
return loadFileConfig(p.Filename)
}