Merge pull request #12 from thorhs/multi_provider_config_updates

Adding caching and merging of configurations
This commit is contained in:
Vincent Demeester 2015-09-29 11:02:39 +01:00
commit cdcd5a2b68
9 changed files with 257 additions and 180 deletions

View file

@ -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 {

View file

@ -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}
} }
} }

View file

@ -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

View file

@ -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
} }

View file

@ -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 {

View file

@ -1,5 +1,5 @@
package main package main
type Provider interface { type Provider interface {
Provide(configurationChan chan<- *Configuration) Provide(configurationChan chan<- configMessage)
} }

View file

@ -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>

View file

@ -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
View file

@ -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)
} }