Merge pull request #12 from thorhs/multi_provider_config_updates
Adding caching and merging of configurations
This commit is contained in:
commit
cdcd5a2b68
9 changed files with 257 additions and 180 deletions
|
@ -68,7 +68,7 @@ func NewConsulProvider() *ConsulProvider {
|
||||||
return consulProvider
|
return consulProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *ConsulProvider) Provide(configurationChan chan<- *Configuration) {
|
func (provider *ConsulProvider) Provide(configurationChan chan<- configMessage) {
|
||||||
config := &api.Config{
|
config := &api.Config{
|
||||||
Address: provider.Endpoint,
|
Address: provider.Endpoint,
|
||||||
Scheme: "http",
|
Scheme: "http",
|
||||||
|
@ -99,7 +99,7 @@ func (provider *ConsulProvider) Provide(configurationChan chan<- *Configuration)
|
||||||
waitIndex = meta.LastIndex
|
waitIndex = meta.LastIndex
|
||||||
configuration := provider.loadConsulConfig()
|
configuration := provider.loadConsulConfig()
|
||||||
if configuration != nil {
|
if configuration != nil {
|
||||||
configurationChan <- configuration
|
configurationChan <- configMessage{"consul", configuration}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -107,7 +107,7 @@ func (provider *ConsulProvider) Provide(configurationChan chan<- *Configuration)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
configuration := provider.loadConsulConfig()
|
configuration := provider.loadConsulConfig()
|
||||||
configurationChan <- configuration
|
configurationChan <- configMessage{"consul", configuration}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *ConsulProvider) loadConsulConfig() *Configuration {
|
func (provider *ConsulProvider) loadConsulConfig() *Configuration {
|
||||||
|
|
|
@ -65,7 +65,7 @@ var DockerFuncMap = template.FuncMap{
|
||||||
"getHost": getHost,
|
"getHost": getHost,
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *DockerProvider) Provide(configurationChan chan<- *Configuration) {
|
func (provider *DockerProvider) Provide(configurationChan chan<- configMessage) {
|
||||||
if dockerClient, err := docker.NewClient(provider.Endpoint); err != nil {
|
if dockerClient, err := docker.NewClient(provider.Endpoint); err != nil {
|
||||||
log.Fatalf("Failed to create a client for docker, error: %s", err)
|
log.Fatalf("Failed to create a client for docker, error: %s", err)
|
||||||
} else {
|
} else {
|
||||||
|
@ -90,7 +90,7 @@ func (provider *DockerProvider) Provide(configurationChan chan<- *Configuration)
|
||||||
log.Debugf("Docker event receveived %+v", event)
|
log.Debugf("Docker event receveived %+v", event)
|
||||||
configuration := provider.loadDockerConfig(dockerClient)
|
configuration := provider.loadDockerConfig(dockerClient)
|
||||||
if configuration != nil {
|
if configuration != nil {
|
||||||
configurationChan <- configuration
|
configurationChan <- configMessage{"docker", configuration}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -106,7 +106,7 @@ func (provider *DockerProvider) Provide(configurationChan chan<- *Configuration)
|
||||||
}
|
}
|
||||||
|
|
||||||
configuration := provider.loadDockerConfig(dockerClient)
|
configuration := provider.loadDockerConfig(dockerClient)
|
||||||
configurationChan <- configuration
|
configurationChan <- configMessage{"docker", configuration}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
146
docs/index.md
146
docs/index.md
|
@ -254,83 +254,109 @@ $ curl -s "http://localhost:8080/health" | jq .
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
* ```/api```: ```GET``` or ```PUT``` a configuration
|
* ```/api```: ```GET``` configuration for all providers
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
$ curl -s "http://localhost:8082/api" | jq .
|
$ curl -s "http://localhost:8082/api" | jq .
|
||||||
{
|
{
|
||||||
"Frontends": {
|
"file": {
|
||||||
"frontend-traefik": {
|
"Frontends": {
|
||||||
"Routes": {
|
"frontend-traefik": {
|
||||||
"route-host-traefik": {
|
"Routes": {
|
||||||
"Value": "traefik.docker.localhost",
|
"route-host-traefik": {
|
||||||
"Rule": "Host"
|
"Value": "traefik.docker.localhost",
|
||||||
}
|
"Rule": "Host"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Backend": "backend-test2"
|
||||||
},
|
},
|
||||||
"Backend": "backend-test2"
|
"frontend-test": {
|
||||||
|
"Routes": {
|
||||||
|
"route-host-test": {
|
||||||
|
"Value": "test.docker.localhost",
|
||||||
|
"Rule": "Host"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Backend": "backend-test1"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"frontend-test": {
|
"Backends": {
|
||||||
"Routes": {
|
"backend-test2": {
|
||||||
"route-host-test": {
|
"Servers": {
|
||||||
"Value": "test.docker.localhost",
|
"server-stoic_brattain": {
|
||||||
"Rule": "Host"
|
"Weight": 0,
|
||||||
|
"Url": "http://172.17.0.8:80"
|
||||||
|
},
|
||||||
|
"server-jovial_khorana": {
|
||||||
|
"Weight": 0,
|
||||||
|
"Url": "http://172.17.0.12:80"
|
||||||
|
},
|
||||||
|
"server-jovial_franklin": {
|
||||||
|
"Weight": 0,
|
||||||
|
"Url": "http://172.17.0.11:80"
|
||||||
|
},
|
||||||
|
"server-elegant_panini": {
|
||||||
|
"Weight": 0,
|
||||||
|
"Url": "http://172.17.0.9:80"
|
||||||
|
},
|
||||||
|
"server-adoring_elion": {
|
||||||
|
"Weight": 0,
|
||||||
|
"Url": "http://172.17.0.10:80"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Backend": "backend-test1"
|
"backend-test1": {
|
||||||
|
"Servers": {
|
||||||
|
"server-trusting_wozniak": {
|
||||||
|
"Weight": 0,
|
||||||
|
"Url": "http://172.17.0.5:80"
|
||||||
|
},
|
||||||
|
"server-sharp_jang": {
|
||||||
|
"Weight": 0,
|
||||||
|
"Url": "http://172.17.0.7:80"
|
||||||
|
},
|
||||||
|
"server-dreamy_feynman": {
|
||||||
|
"Weight": 0,
|
||||||
|
"Url": "http://172.17.0.6:80"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Backends": {
|
"marathon": {
|
||||||
"backend-test2": {
|
"Frontends": {
|
||||||
"Servers": {
|
"frontend-marathon": {
|
||||||
"server-stoic_brattain": {
|
"Routes": {
|
||||||
"Weight": 0,
|
"route-host-marathon": {
|
||||||
"Url": "http://172.17.0.8:80"
|
"Value": "marathon.docker.localhost",
|
||||||
|
"Rule": "Host"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"server-jovial_khorana": {
|
"Backend": "backend-marathon"
|
||||||
"Weight": 0,
|
},
|
||||||
"Url": "http://172.17.0.12:80"
|
|
||||||
},
|
|
||||||
"server-jovial_franklin": {
|
|
||||||
"Weight": 0,
|
|
||||||
"Url": "http://172.17.0.11:80"
|
|
||||||
},
|
|
||||||
"server-elegant_panini": {
|
|
||||||
"Weight": 0,
|
|
||||||
"Url": "http://172.17.0.9:80"
|
|
||||||
},
|
|
||||||
"server-adoring_elion": {
|
|
||||||
"Weight": 0,
|
|
||||||
"Url": "http://172.17.0.10:80"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"backend-test1": {
|
"Backends": {
|
||||||
"Servers": {
|
"backend-marathon": {
|
||||||
"server-trusting_wozniak": {
|
"Servers": {
|
||||||
"Weight": 0,
|
"server-marathon-1": {
|
||||||
"Url": "http://172.17.0.5:80"
|
"Weight": 0,
|
||||||
|
"Url": "http://172.17.0.8:802"
|
||||||
|
},
|
||||||
},
|
},
|
||||||
"server-sharp_jang": {
|
},
|
||||||
"Weight": 0,
|
},
|
||||||
"Url": "http://172.17.0.7:80"
|
},
|
||||||
},
|
|
||||||
"server-dreamy_feynman": {
|
|
||||||
"Weight": 0,
|
|
||||||
"Url": "http://172.17.0.6:80"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
* ```/api/backends```: ```GET``` backends
|
* ```/api/{provider}```: ```GET``` or ```PUT``` provider
|
||||||
* ```/api/backends/{backend}```: ```GET``` a backend
|
* ```/api/{provider}/backends```: ```GET``` backends
|
||||||
* ```/api/backends/{backend}/servers```: ```GET``` servers in a backend
|
* ```/api/{provider}/backends/{backend}```: ```GET``` a backend
|
||||||
* ```/api/backends/{backend}/servers/{server}```: ```GET``` a server in a backend
|
* ```/api/{provider}/backends/{backend}/servers```: ```GET``` servers in a backend
|
||||||
* ```/api/frontends```: ```GET``` frontends
|
* ```/api/{provider}/backends/{backend}/servers/{server}```: ```GET``` a server in a backend
|
||||||
* ```/api/frontends/{frontend}```: ```GET``` a frontend
|
* ```/api/{provider}/frontends```: ```GET``` frontends
|
||||||
|
* ```/api/{provider}/frontends/{frontend}```: ```GET``` a frontend
|
||||||
|
|
||||||
|
|
||||||
## <a id="docker"></a> Docker backend
|
## <a id="docker"></a> Docker backend
|
||||||
|
|
6
file.go
6
file.go
|
@ -23,7 +23,7 @@ func NewFileProvider() *FileProvider {
|
||||||
return fileProvider
|
return fileProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *FileProvider) Provide(configurationChan chan<- *Configuration) {
|
func (provider *FileProvider) Provide(configurationChan chan<- configMessage) {
|
||||||
watcher, err := fsnotify.NewWatcher()
|
watcher, err := fsnotify.NewWatcher()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Error creating file watcher", err)
|
log.Error("Error creating file watcher", err)
|
||||||
|
@ -48,7 +48,7 @@ func (provider *FileProvider) Provide(configurationChan chan<- *Configuration) {
|
||||||
log.Debug("File event:", event)
|
log.Debug("File event:", event)
|
||||||
configuration := provider.LoadFileConfig(file.Name())
|
configuration := provider.LoadFileConfig(file.Name())
|
||||||
if configuration != nil {
|
if configuration != nil {
|
||||||
configurationChan <- configuration
|
configurationChan <- configMessage{"file", configuration}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case error := <-watcher.Errors:
|
case error := <-watcher.Errors:
|
||||||
|
@ -67,7 +67,7 @@ func (provider *FileProvider) Provide(configurationChan chan<- *Configuration) {
|
||||||
}
|
}
|
||||||
|
|
||||||
configuration := provider.LoadFileConfig(file.Name())
|
configuration := provider.LoadFileConfig(file.Name())
|
||||||
configurationChan <- configuration
|
configurationChan <- configMessage{"file", configuration}
|
||||||
<-done
|
<-done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -67,7 +67,7 @@ var MarathonFuncMap = template.FuncMap{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *MarathonProvider) Provide(configurationChan chan<- *Configuration) {
|
func (provider *MarathonProvider) Provide(configurationChan chan<- configMessage) {
|
||||||
config := marathon.NewDefaultConfig()
|
config := marathon.NewDefaultConfig()
|
||||||
config.URL = provider.Endpoint
|
config.URL = provider.Endpoint
|
||||||
config.EventsInterface = provider.NetworkInterface
|
config.EventsInterface = provider.NetworkInterface
|
||||||
|
@ -88,7 +88,7 @@ func (provider *MarathonProvider) Provide(configurationChan chan<- *Configuratio
|
||||||
log.Debug("Marathon event receveived", event)
|
log.Debug("Marathon event receveived", event)
|
||||||
configuration := provider.loadMarathonConfig()
|
configuration := provider.loadMarathonConfig()
|
||||||
if configuration != nil {
|
if configuration != nil {
|
||||||
configurationChan <- configuration
|
configurationChan <- configMessage{"marathon", configuration}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
@ -96,7 +96,7 @@ func (provider *MarathonProvider) Provide(configurationChan chan<- *Configuratio
|
||||||
}
|
}
|
||||||
|
|
||||||
configuration := provider.loadMarathonConfig()
|
configuration := provider.loadMarathonConfig()
|
||||||
configurationChan <- configuration
|
configurationChan <- configMessage{"marathon", configuration}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *MarathonProvider) loadMarathonConfig() *Configuration {
|
func (provider *MarathonProvider) loadMarathonConfig() *Configuration {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
type Provider interface {
|
type Provider interface {
|
||||||
Provide(configurationChan chan<- *Configuration)
|
Provide(configurationChan chan<- configMessage)
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,9 +27,10 @@
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<!-- <div class="panel-heading">Frontends</div>
|
<!-- <div class="panel-heading">Frontends</div>
|
||||||
<div class="panel-body"> -->
|
<div class="panel-body"> -->
|
||||||
{{range $keyFrontends, $valueFrontends := .Configuration.Frontends}}
|
{{range $keyProviders, $valueProviders := .Configurations}}
|
||||||
|
{{range $keyFrontends, $valueFrontends := $valueProviders.Frontends}}
|
||||||
<div class="panel panel-primary">
|
<div class="panel panel-primary">
|
||||||
<div class="panel-heading">{{$keyFrontends}}</div>
|
<div class="panel-heading">{{$keyFrontends}} - ({{$keyProviders}})</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<a class="btn btn-info" role="button" data-toggle="collapse" href="#{{$valueFrontends.Backend}}" aria-expanded="false">
|
<a class="btn btn-info" role="button" data-toggle="collapse" href="#{{$valueFrontends.Backend}}" aria-expanded="false">
|
||||||
{{$valueFrontends.Backend}}
|
{{$valueFrontends.Backend}}
|
||||||
|
@ -51,14 +52,16 @@
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
{{end}}
|
||||||
<!-- </div> -->
|
<!-- </div> -->
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<!-- <div class="panel-heading">Backends</div>
|
<!-- <div class="panel-heading">Backends</div>
|
||||||
<div class="panel-body"> -->
|
<div class="panel-body"> -->
|
||||||
{{range $keyBackends, $valueBackends := .Configuration.Backends}}
|
{{range $keyProviders, $valueProviders := .Configurations}}
|
||||||
|
{{range $keyBackends, $valueBackends := $valueProviders.Backends}}
|
||||||
<div class="panel panel-primary" id="{{$keyBackends}}">
|
<div class="panel panel-primary" id="{{$keyBackends}}">
|
||||||
<div class="panel-heading">{{$keyBackends}}</div>
|
<div class="panel-heading">{{$keyBackends}}({{$keyProviders}})</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
{{with $valueBackends.LoadBalancer}}
|
{{with $valueBackends.LoadBalancer}}
|
||||||
<a class="btn btn-info" role="button">
|
<a class="btn btn-info" role="button">
|
||||||
|
@ -87,6 +90,7 @@
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
{{end}}
|
||||||
<!-- </div> -->
|
<!-- </div> -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
147
traefik.go
147
traefik.go
|
@ -28,24 +28,31 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
globalConfigFile = kingpin.Arg("conf", "Main configration file.").Default("traefik.toml").String()
|
globalConfigFile = kingpin.Arg("conf", "Main configration file.").Default("traefik.toml").String()
|
||||||
currentConfiguration = new(Configuration)
|
currentConfigurations = make(configs)
|
||||||
metrics = stats.New()
|
metrics = stats.New()
|
||||||
oxyLogger = &OxyLogger{}
|
oxyLogger = &OxyLogger{}
|
||||||
templatesRenderer = render.New(render.Options{
|
templatesRenderer = render.New(render.Options{
|
||||||
Directory: "templates",
|
Directory: "templates",
|
||||||
Asset: Asset,
|
Asset: Asset,
|
||||||
AssetNames: AssetNames,
|
AssetNames: AssetNames,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type configMessage struct {
|
||||||
|
providerName string
|
||||||
|
configuration *Configuration
|
||||||
|
}
|
||||||
|
|
||||||
|
type configs map[string]*Configuration
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||||
kingpin.Parse()
|
kingpin.Parse()
|
||||||
fmtlog.SetFlags(fmtlog.Lshortfile | fmtlog.LstdFlags)
|
fmtlog.SetFlags(fmtlog.Lshortfile | fmtlog.LstdFlags)
|
||||||
var srv *manners.GracefulServer
|
var srv *manners.GracefulServer
|
||||||
var configurationRouter *mux.Router
|
var configurationRouter *mux.Router
|
||||||
var configurationChan = make(chan *Configuration, 10)
|
var configurationChan = make(chan configMessage, 10)
|
||||||
defer close(configurationChan)
|
defer close(configurationChan)
|
||||||
var sigs = make(chan os.Signal, 1)
|
var sigs = make(chan os.Signal, 1)
|
||||||
defer close(sigs)
|
defer close(sigs)
|
||||||
|
@ -84,17 +91,25 @@ func main() {
|
||||||
|
|
||||||
// listen new configurations from providers
|
// listen new configurations from providers
|
||||||
go func() {
|
go func() {
|
||||||
|
|
||||||
for {
|
for {
|
||||||
configuration := <-configurationChan
|
configMsg := <-configurationChan
|
||||||
log.Infof("Configuration receveived %+v", configuration)
|
log.Infof("Configuration receveived from provider %v: %+v", configMsg.providerName, configMsg.configuration)
|
||||||
if configuration == nil {
|
if configMsg.configuration == nil {
|
||||||
log.Info("Skipping empty configuration")
|
log.Info("Skipping empty configuration")
|
||||||
} else if reflect.DeepEqual(currentConfiguration, configuration) {
|
} else if reflect.DeepEqual(currentConfigurations[configMsg.providerName], configMsg.configuration) {
|
||||||
log.Info("Skipping same configuration")
|
log.Info("Skipping same configuration")
|
||||||
} else {
|
} else {
|
||||||
newConfigurationRouter, err := LoadConfig(configuration, globalConfiguration)
|
// Copy configurations to new map so we don't change current if LoadConfig fails
|
||||||
|
newConfigurations := make(configs)
|
||||||
|
for k, v := range currentConfigurations {
|
||||||
|
newConfigurations[k] = v
|
||||||
|
}
|
||||||
|
newConfigurations[configMsg.providerName] = configMsg.configuration
|
||||||
|
|
||||||
|
newConfigurationRouter, err := LoadConfig(newConfigurations, globalConfiguration)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
currentConfiguration = configuration
|
currentConfigurations = newConfigurations
|
||||||
configurationRouter = newConfigurationRouter
|
configurationRouter = newConfigurationRouter
|
||||||
oldServer := srv
|
oldServer := srv
|
||||||
newsrv := prepareServer(configurationRouter, globalConfiguration, oldServer, loggerMiddleware, metrics)
|
newsrv := prepareServer(configurationRouter, globalConfiguration, oldServer, loggerMiddleware, metrics)
|
||||||
|
@ -210,69 +225,71 @@ func prepareServer(router *mux.Router, globalConfiguration *GlobalConfiguration,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadConfig(configuration *Configuration, globalConfiguration *GlobalConfiguration) (*mux.Router, error) {
|
func LoadConfig(configurations configs, globalConfiguration *GlobalConfiguration) (*mux.Router, error) {
|
||||||
router := mux.NewRouter()
|
router := mux.NewRouter()
|
||||||
router.NotFoundHandler = http.HandlerFunc(notFoundHandler)
|
router.NotFoundHandler = http.HandlerFunc(notFoundHandler)
|
||||||
backends := map[string]http.Handler{}
|
backends := map[string]http.Handler{}
|
||||||
for frontendName, frontend := range configuration.Frontends {
|
for _, configuration := range configurations {
|
||||||
log.Debugf("Creating frontend %s", frontendName)
|
for frontendName, frontend := range configuration.Frontends {
|
||||||
fwd, _ := forward.New(forward.Logger(oxyLogger))
|
log.Debugf("Creating frontend %s", frontendName)
|
||||||
newRoute := router.NewRoute().Name(frontendName)
|
fwd, _ := forward.New(forward.Logger(oxyLogger))
|
||||||
for routeName, route := range frontend.Routes {
|
newRoute := router.NewRoute().Name(frontendName)
|
||||||
log.Debugf("Creating route %s %s:%s", routeName, route.Rule, route.Value)
|
for routeName, route := range frontend.Routes {
|
||||||
newRouteReflect := Invoke(newRoute, route.Rule, route.Value)
|
log.Debugf("Creating route %s %s:%s", routeName, route.Rule, route.Value)
|
||||||
newRoute = newRouteReflect[0].Interface().(*mux.Route)
|
newRouteReflect := Invoke(newRoute, route.Rule, route.Value)
|
||||||
}
|
newRoute = newRouteReflect[0].Interface().(*mux.Route)
|
||||||
if backends[frontend.Backend] == nil {
|
|
||||||
log.Debugf("Creating backend %s", frontend.Backend)
|
|
||||||
var lb http.Handler
|
|
||||||
rr, _ := roundrobin.New(fwd)
|
|
||||||
lbMethod, err := NewLoadBalancerMethod(configuration.Backends[frontend.Backend].LoadBalancer)
|
|
||||||
if err != nil {
|
|
||||||
configuration.Backends[frontend.Backend].LoadBalancer = &LoadBalancer{Method: "wrr"}
|
|
||||||
}
|
}
|
||||||
switch lbMethod {
|
if backends[frontend.Backend] == nil {
|
||||||
case drr:
|
log.Debugf("Creating backend %s", frontend.Backend)
|
||||||
log.Debugf("Creating load-balancer drr")
|
var lb http.Handler
|
||||||
rebalancer, _ := roundrobin.NewRebalancer(rr, roundrobin.RebalancerLogger(oxyLogger))
|
rr, _ := roundrobin.New(fwd)
|
||||||
lb = rebalancer
|
lbMethod, err := NewLoadBalancerMethod(configuration.Backends[frontend.Backend].LoadBalancer)
|
||||||
for serverName, server := range configuration.Backends[frontend.Backend].Servers {
|
if err != nil {
|
||||||
url, err := url.Parse(server.URL)
|
configuration.Backends[frontend.Backend].LoadBalancer = &LoadBalancer{Method: "wrr"}
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
log.Debugf("Creating server %s %s", serverName, url.String())
|
|
||||||
rebalancer.UpsertServer(url, roundrobin.Weight(server.Weight))
|
|
||||||
}
|
}
|
||||||
case wrr:
|
switch lbMethod {
|
||||||
log.Debugf("Creating load-balancer wrr")
|
case drr:
|
||||||
lb = rr
|
log.Debugf("Creating load-balancer drr")
|
||||||
for serverName, server := range configuration.Backends[frontend.Backend].Servers {
|
rebalancer, _ := roundrobin.NewRebalancer(rr, roundrobin.RebalancerLogger(oxyLogger))
|
||||||
url, err := url.Parse(server.URL)
|
lb = rebalancer
|
||||||
if err != nil {
|
for serverName, server := range configuration.Backends[frontend.Backend].Servers {
|
||||||
return nil, err
|
url, err := url.Parse(server.URL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
log.Debugf("Creating server %s %s", serverName, url.String())
|
||||||
|
rebalancer.UpsertServer(url, roundrobin.Weight(server.Weight))
|
||||||
|
}
|
||||||
|
case wrr:
|
||||||
|
log.Debugf("Creating load-balancer wrr")
|
||||||
|
lb = rr
|
||||||
|
for serverName, server := range configuration.Backends[frontend.Backend].Servers {
|
||||||
|
url, err := url.Parse(server.URL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
log.Debugf("Creating server %s %s", serverName, url.String())
|
||||||
|
rr.UpsertServer(url, roundrobin.Weight(server.Weight))
|
||||||
}
|
}
|
||||||
log.Debugf("Creating server %s %s", serverName, url.String())
|
|
||||||
rr.UpsertServer(url, roundrobin.Weight(server.Weight))
|
|
||||||
}
|
}
|
||||||
}
|
var negroni = negroni.New()
|
||||||
var negroni = negroni.New()
|
if configuration.Backends[frontend.Backend].CircuitBreaker != nil {
|
||||||
if configuration.Backends[frontend.Backend].CircuitBreaker != nil {
|
log.Debugf("Creating circuit breaker %s", configuration.Backends[frontend.Backend].CircuitBreaker.Expression)
|
||||||
log.Debugf("Creating circuit breaker %s", configuration.Backends[frontend.Backend].CircuitBreaker.Expression)
|
negroni.Use(middlewares.NewCircuitBreaker(lb, configuration.Backends[frontend.Backend].CircuitBreaker.Expression, cbreaker.Logger(oxyLogger)))
|
||||||
negroni.Use(middlewares.NewCircuitBreaker(lb, configuration.Backends[frontend.Backend].CircuitBreaker.Expression, cbreaker.Logger(oxyLogger)))
|
} else {
|
||||||
|
negroni.UseHandler(lb)
|
||||||
|
}
|
||||||
|
backends[frontend.Backend] = negroni
|
||||||
} else {
|
} else {
|
||||||
negroni.UseHandler(lb)
|
log.Debugf("Reusing backend %s", frontend.Backend)
|
||||||
}
|
}
|
||||||
backends[frontend.Backend] = negroni
|
// stream.New(backends[frontend.Backend], stream.Retry("IsNetworkError() && Attempts() <= " + strconv.Itoa(globalConfiguration.Replay)), stream.Logger(oxyLogger))
|
||||||
} else {
|
|
||||||
log.Debugf("Reusing backend %s", frontend.Backend)
|
|
||||||
}
|
|
||||||
// stream.New(backends[frontend.Backend], stream.Retry("IsNetworkError() && Attempts() <= " + strconv.Itoa(globalConfiguration.Replay)), stream.Logger(oxyLogger))
|
|
||||||
|
|
||||||
newRoute.Handler(backends[frontend.Backend])
|
newRoute.Handler(backends[frontend.Backend])
|
||||||
err := newRoute.GetError()
|
err := newRoute.GetError()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Error building route: %s", err)
|
log.Error("Error building route: %s", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return router, nil
|
return router, nil
|
||||||
|
|
106
web.go
106
web.go
|
@ -17,33 +17,41 @@ type WebProvider struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Page struct {
|
type Page struct {
|
||||||
Configuration Configuration
|
Configurations configs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *WebProvider) Provide(configurationChan chan<- *Configuration) {
|
func (provider *WebProvider) Provide(configurationChan chan<- configMessage) {
|
||||||
systemRouter := mux.NewRouter()
|
systemRouter := mux.NewRouter()
|
||||||
systemRouter.Methods("GET").Path("/").Handler(http.HandlerFunc(GetHTMLConfigHandler))
|
systemRouter.Methods("GET").Path("/").Handler(http.HandlerFunc(GetHTMLConfigHandler))
|
||||||
systemRouter.Methods("GET").Path("/health").Handler(http.HandlerFunc(GetHealthHandler))
|
systemRouter.Methods("GET").Path("/health").Handler(http.HandlerFunc(GetHealthHandler))
|
||||||
systemRouter.Methods("GET").Path("/api").Handler(http.HandlerFunc(GetConfigHandler))
|
systemRouter.Methods("GET").Path("/api").Handler(http.HandlerFunc(GetConfigHandler))
|
||||||
systemRouter.Methods("PUT").Path("/api").Handler(http.HandlerFunc(
|
systemRouter.Methods("GET").Path("/api/{provider}").Handler(http.HandlerFunc(GetConfigHandler))
|
||||||
|
systemRouter.Methods("PUT").Path("/api/{provider}").Handler(http.HandlerFunc(
|
||||||
func(rw http.ResponseWriter, r *http.Request) {
|
func(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
if vars["provider"] != "web" {
|
||||||
|
rw.WriteHeader(http.StatusBadRequest)
|
||||||
|
fmt.Fprintf(rw, "Only 'web' provider can be updated through the REST API")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
configuration := new(Configuration)
|
configuration := new(Configuration)
|
||||||
b, _ := ioutil.ReadAll(r.Body)
|
b, _ := ioutil.ReadAll(r.Body)
|
||||||
err := json.Unmarshal(b, configuration)
|
err := json.Unmarshal(b, configuration)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
configurationChan <- configuration
|
configurationChan <- configMessage{"web", configuration}
|
||||||
GetConfigHandler(rw, r)
|
GetConfigHandler(rw, r)
|
||||||
} else {
|
} else {
|
||||||
log.Errorf("Error parsing configuration %+v", err)
|
log.Errorf("Error parsing configuration %+v", err)
|
||||||
http.Error(rw, fmt.Sprintf("%+v", err), http.StatusBadRequest)
|
http.Error(rw, fmt.Sprintf("%+v", err), http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
systemRouter.Methods("GET").Path("/api/backends").Handler(http.HandlerFunc(GetBackendsHandler))
|
systemRouter.Methods("GET").Path("/api/{provider}/backends").Handler(http.HandlerFunc(GetBackendsHandler))
|
||||||
systemRouter.Methods("GET").Path("/api/backends/{backend}").Handler(http.HandlerFunc(GetBackendHandler))
|
systemRouter.Methods("GET").Path("/api/{provider}/backends/{backend}").Handler(http.HandlerFunc(GetBackendHandler))
|
||||||
systemRouter.Methods("GET").Path("/api/backends/{backend}/servers").Handler(http.HandlerFunc(GetServersHandler))
|
systemRouter.Methods("GET").Path("/api/{provider}/backends/{backend}/servers").Handler(http.HandlerFunc(GetServersHandler))
|
||||||
systemRouter.Methods("GET").Path("/api/backends/{backend}/servers/{server}").Handler(http.HandlerFunc(GetServerHandler))
|
systemRouter.Methods("GET").Path("/api/{provider}/backends/{backend}/servers/{server}").Handler(http.HandlerFunc(GetServerHandler))
|
||||||
systemRouter.Methods("GET").Path("/api/frontends").Handler(http.HandlerFunc(GetFrontendsHandler))
|
systemRouter.Methods("GET").Path("/api/{provider}/frontends").Handler(http.HandlerFunc(GetFrontendsHandler))
|
||||||
systemRouter.Methods("GET").Path("/api/frontends/{frontend}").Handler(http.HandlerFunc(GetFrontendHandler))
|
systemRouter.Methods("GET").Path("/api/{provider}/frontends/{frontend}").Handler(http.HandlerFunc(GetFrontendHandler))
|
||||||
systemRouter.Methods("GET").PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(&assetfs.AssetFS{Asset: Asset, AssetDir: AssetDir, Prefix: "static"})))
|
systemRouter.Methods("GET").PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(&assetfs.AssetFS{Asset: Asset, AssetDir: AssetDir, Prefix: "static"})))
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
|
@ -62,11 +70,11 @@ func (provider *WebProvider) Provide(configurationChan chan<- *Configuration) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetConfigHandler(rw http.ResponseWriter, r *http.Request) {
|
func GetConfigHandler(rw http.ResponseWriter, r *http.Request) {
|
||||||
templatesRenderer.JSON(rw, http.StatusOK, currentConfiguration)
|
templatesRenderer.JSON(rw, http.StatusOK, currentConfigurations)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetHTMLConfigHandler(response http.ResponseWriter, request *http.Request) {
|
func GetHTMLConfigHandler(response http.ResponseWriter, request *http.Request) {
|
||||||
templatesRenderer.HTML(response, http.StatusOK, "configuration", Page{Configuration: *currentConfiguration})
|
templatesRenderer.HTML(response, http.StatusOK, "configuration", Page{Configurations: currentConfigurations})
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetHealthHandler(rw http.ResponseWriter, r *http.Request) {
|
func GetHealthHandler(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -74,54 +82,76 @@ func GetHealthHandler(rw http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetBackendsHandler(rw http.ResponseWriter, r *http.Request) {
|
func GetBackendsHandler(rw http.ResponseWriter, r *http.Request) {
|
||||||
templatesRenderer.JSON(rw, http.StatusOK, currentConfiguration.Backends)
|
vars := mux.Vars(r)
|
||||||
|
providerId := vars["provider"]
|
||||||
|
if provider, ok := currentConfigurations[providerId]; ok {
|
||||||
|
templatesRenderer.JSON(rw, http.StatusOK, provider.Backends)
|
||||||
|
} else {
|
||||||
|
http.NotFound(rw, r)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetBackendHandler(rw http.ResponseWriter, r *http.Request) {
|
func GetBackendHandler(rw http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
id := vars["backend"]
|
providerId := vars["provider"]
|
||||||
if backend, ok := currentConfiguration.Backends[id]; ok {
|
backendId := vars["backend"]
|
||||||
templatesRenderer.JSON(rw, http.StatusOK, backend)
|
if provider, ok := currentConfigurations[providerId]; ok {
|
||||||
} else {
|
if backend, ok := provider.Backends[backendId]; ok {
|
||||||
http.NotFound(rw, r)
|
templatesRenderer.JSON(rw, http.StatusOK, backend)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
http.NotFound(rw, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetFrontendsHandler(rw http.ResponseWriter, r *http.Request) {
|
func GetFrontendsHandler(rw http.ResponseWriter, r *http.Request) {
|
||||||
templatesRenderer.JSON(rw, http.StatusOK, currentConfiguration.Frontends)
|
vars := mux.Vars(r)
|
||||||
|
providerId := vars["provider"]
|
||||||
|
if provider, ok := currentConfigurations[providerId]; ok {
|
||||||
|
templatesRenderer.JSON(rw, http.StatusOK, provider.Frontends)
|
||||||
|
} else {
|
||||||
|
http.NotFound(rw, r)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetFrontendHandler(rw http.ResponseWriter, r *http.Request) {
|
func GetFrontendHandler(rw http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
id := vars["frontend"]
|
providerId := vars["provider"]
|
||||||
if frontend, ok := currentConfiguration.Frontends[id]; ok {
|
frontendId := vars["frontend"]
|
||||||
templatesRenderer.JSON(rw, http.StatusOK, frontend)
|
if provider, ok := currentConfigurations[providerId]; ok {
|
||||||
} else {
|
if frontend, ok := provider.Frontends[frontendId]; ok {
|
||||||
http.NotFound(rw, r)
|
templatesRenderer.JSON(rw, http.StatusOK, frontend)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
http.NotFound(rw, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetServersHandler(rw http.ResponseWriter, r *http.Request) {
|
func GetServersHandler(rw http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
backend := vars["backend"]
|
providerId := vars["provider"]
|
||||||
if backend, ok := currentConfiguration.Backends[backend]; ok {
|
backendId := vars["backend"]
|
||||||
templatesRenderer.JSON(rw, http.StatusOK, backend.Servers)
|
if provider, ok := currentConfigurations[providerId]; ok {
|
||||||
} else {
|
if backend, ok := provider.Backends[backendId]; ok {
|
||||||
http.NotFound(rw, r)
|
templatesRenderer.JSON(rw, http.StatusOK, backend.Servers)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
http.NotFound(rw, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetServerHandler(rw http.ResponseWriter, r *http.Request) {
|
func GetServerHandler(rw http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
backend := vars["backend"]
|
providerId := vars["provider"]
|
||||||
server := vars["server"]
|
backendId := vars["backend"]
|
||||||
if backend, ok := currentConfiguration.Backends[backend]; ok {
|
serverId := vars["server"]
|
||||||
if server, ok := backend.Servers[server]; ok {
|
if provider, ok := currentConfigurations[providerId]; ok {
|
||||||
templatesRenderer.JSON(rw, http.StatusOK, server)
|
if backend, ok := provider.Backends[backendId]; ok {
|
||||||
} else {
|
if server, ok := backend.Servers[serverId]; ok {
|
||||||
http.NotFound(rw, r)
|
templatesRenderer.JSON(rw, http.StatusOK, server)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
http.NotFound(rw, r)
|
|
||||||
}
|
}
|
||||||
|
http.NotFound(rw, r)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue