From f534d8817f9530736b62cc0c3646ed9ef305f709 Mon Sep 17 00:00:00 2001 From: Thorhallur Sverrisson Date: Tue, 22 Sep 2015 04:16:21 +0000 Subject: [PATCH 1/6] Adding caching and merging of configurations Configurations are now cached from each provider separately so that we can merge them together when one provider has changed config. The Web module also returns full config info through the HTML call, but REST API is working on a separate web configuration that is sent in just like any other. --- consul.go | 6 +-- docker.go | 6 +-- file.go | 6 +-- marathon.go | 6 +-- provider.go | 2 +- traefik.go | 147 +++++++++++++++++++++++++++++----------------------- web.go | 39 ++++++++++---- 7 files changed, 124 insertions(+), 88 deletions(-) diff --git a/consul.go b/consul.go index 849fefc97..344f8f1ec 100644 --- a/consul.go +++ b/consul.go @@ -68,7 +68,7 @@ func NewConsulProvider() *ConsulProvider { return consulProvider } -func (provider *ConsulProvider) Provide(configurationChan chan<- *Configuration) { +func (provider *ConsulProvider) Provide(configurationChan chan<- configMessage) { config := &api.Config{ Address: provider.Endpoint, Scheme: "http", @@ -99,7 +99,7 @@ func (provider *ConsulProvider) Provide(configurationChan chan<- *Configuration) waitIndex = meta.LastIndex configuration := provider.loadConsulConfig() if configuration != nil { - configurationChan <- configuration + configurationChan <- configMessage{"consul", configuration} } } } @@ -107,7 +107,7 @@ func (provider *ConsulProvider) Provide(configurationChan chan<- *Configuration) } } configuration := provider.loadConsulConfig() - configurationChan <- configuration + configurationChan <- configMessage{"consul", configuration} } func (provider *ConsulProvider) loadConsulConfig() *Configuration { diff --git a/docker.go b/docker.go index 625e2634b..4ae6c2209 100644 --- a/docker.go +++ b/docker.go @@ -65,7 +65,7 @@ var DockerFuncMap = template.FuncMap{ "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 { log.Fatalf("Failed to create a client for docker, error: %s", err) } else { @@ -90,7 +90,7 @@ func (provider *DockerProvider) Provide(configurationChan chan<- *Configuration) log.Debugf("Docker event receveived %+v", event) configuration := provider.loadDockerConfig(dockerClient) if configuration != nil { - configurationChan <- configuration + configurationChan <- configMessage{"docker", configuration} } } } @@ -106,7 +106,7 @@ func (provider *DockerProvider) Provide(configurationChan chan<- *Configuration) } configuration := provider.loadDockerConfig(dockerClient) - configurationChan <- configuration + configurationChan <- configMessage{"docker", configuration} } } diff --git a/file.go b/file.go index ee2a5509a..4cdd4704a 100644 --- a/file.go +++ b/file.go @@ -23,7 +23,7 @@ func NewFileProvider() *FileProvider { return fileProvider } -func (provider *FileProvider) Provide(configurationChan chan<- *Configuration) { +func (provider *FileProvider) Provide(configurationChan chan<- configMessage) { watcher, err := fsnotify.NewWatcher() if err != nil { log.Error("Error creating file watcher", err) @@ -48,7 +48,7 @@ func (provider *FileProvider) Provide(configurationChan chan<- *Configuration) { log.Debug("File event:", event) configuration := provider.LoadFileConfig(file.Name()) if configuration != nil { - configurationChan <- configuration + configurationChan <- configMessage{"file", configuration} } } case error := <-watcher.Errors: @@ -67,7 +67,7 @@ func (provider *FileProvider) Provide(configurationChan chan<- *Configuration) { } configuration := provider.LoadFileConfig(file.Name()) - configurationChan <- configuration + configurationChan <- configMessage{"file", configuration} <-done } diff --git a/marathon.go b/marathon.go index 029edf864..9cb7fe12e 100644 --- a/marathon.go +++ b/marathon.go @@ -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.URL = provider.Endpoint config.EventsInterface = provider.NetworkInterface @@ -88,7 +88,7 @@ func (provider *MarathonProvider) Provide(configurationChan chan<- *Configuratio log.Debug("Marathon event receveived", event) configuration := provider.loadMarathonConfig() if configuration != nil { - configurationChan <- configuration + configurationChan <- configMessage{"marathon", configuration} } } }() @@ -96,7 +96,7 @@ func (provider *MarathonProvider) Provide(configurationChan chan<- *Configuratio } configuration := provider.loadMarathonConfig() - configurationChan <- configuration + configurationChan <- configMessage{"marathon", configuration} } func (provider *MarathonProvider) loadMarathonConfig() *Configuration { diff --git a/provider.go b/provider.go index 1d08df459..680341827 100644 --- a/provider.go +++ b/provider.go @@ -1,5 +1,5 @@ package main type Provider interface { - Provide(configurationChan chan<- *Configuration) + Provide(configurationChan chan<- configMessage) } diff --git a/traefik.go b/traefik.go index 1a0b8ede7..00a2ae8a4 100644 --- a/traefik.go +++ b/traefik.go @@ -28,24 +28,31 @@ import ( ) var ( - globalConfigFile = kingpin.Arg("conf", "Main configration file.").Default("traefik.toml").String() - currentConfiguration = new(Configuration) - metrics = stats.New() - oxyLogger = &OxyLogger{} - templatesRenderer = render.New(render.Options{ + globalConfigFile = kingpin.Arg("conf", "Main configration file.").Default("traefik.toml").String() + currentConfigurations = make(configs) + metrics = stats.New() + oxyLogger = &OxyLogger{} + templatesRenderer = render.New(render.Options{ Directory: "templates", Asset: Asset, AssetNames: AssetNames, }) ) +type configMessage struct { + providerName string + configuration *Configuration +} + +type configs map[string]*Configuration + func main() { runtime.GOMAXPROCS(runtime.NumCPU()) kingpin.Parse() fmtlog.SetFlags(fmtlog.Lshortfile | fmtlog.LstdFlags) var srv *manners.GracefulServer var configurationRouter *mux.Router - var configurationChan = make(chan *Configuration, 10) + var configurationChan = make(chan configMessage, 10) defer close(configurationChan) var sigs = make(chan os.Signal, 1) defer close(sigs) @@ -84,17 +91,25 @@ func main() { // listen new configurations from providers go func() { + for { - configuration := <-configurationChan - log.Infof("Configuration receveived %+v", configuration) - if configuration == nil { + configMsg := <-configurationChan + log.Infof("Configuration receveived from provider %v: %+v", configMsg.providerName, configMsg.configuration) + if configMsg.configuration == nil { 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") } 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 { - currentConfiguration = configuration + currentConfigurations = newConfigurations configurationRouter = newConfigurationRouter oldServer := srv 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.NotFoundHandler = http.HandlerFunc(notFoundHandler) backends := map[string]http.Handler{} - for frontendName, frontend := range configuration.Frontends { - log.Debugf("Creating frontend %s", frontendName) - fwd, _ := forward.New(forward.Logger(oxyLogger)) - newRoute := router.NewRoute().Name(frontendName) - for routeName, route := range frontend.Routes { - log.Debugf("Creating route %s %s:%s", routeName, route.Rule, route.Value) - 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"} + for _, configuration := range configurations { + for frontendName, frontend := range configuration.Frontends { + log.Debugf("Creating frontend %s", frontendName) + fwd, _ := forward.New(forward.Logger(oxyLogger)) + newRoute := router.NewRoute().Name(frontendName) + for routeName, route := range frontend.Routes { + log.Debugf("Creating route %s %s:%s", routeName, route.Rule, route.Value) + newRouteReflect := Invoke(newRoute, route.Rule, route.Value) + newRoute = newRouteReflect[0].Interface().(*mux.Route) } - switch lbMethod { - case drr: - log.Debugf("Creating load-balancer drr") - rebalancer, _ := roundrobin.NewRebalancer(rr, roundrobin.RebalancerLogger(oxyLogger)) - lb = rebalancer - 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()) - rebalancer.UpsertServer(url, roundrobin.Weight(server.Weight)) + 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"} } - 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 + switch lbMethod { + case drr: + log.Debugf("Creating load-balancer drr") + rebalancer, _ := roundrobin.NewRebalancer(rr, roundrobin.RebalancerLogger(oxyLogger)) + lb = rebalancer + 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()) + 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() - if configuration.Backends[frontend.Backend].CircuitBreaker != nil { - 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))) + var negroni = negroni.New() + if configuration.Backends[frontend.Backend].CircuitBreaker != nil { + 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))) + } else { + negroni.UseHandler(lb) + } + backends[frontend.Backend] = negroni } else { - negroni.UseHandler(lb) + log.Debugf("Reusing backend %s", frontend.Backend) } - backends[frontend.Backend] = negroni - } else { - log.Debugf("Reusing backend %s", frontend.Backend) - } - // stream.New(backends[frontend.Backend], stream.Retry("IsNetworkError() && Attempts() <= " + strconv.Itoa(globalConfiguration.Replay)), stream.Logger(oxyLogger)) + // stream.New(backends[frontend.Backend], stream.Retry("IsNetworkError() && Attempts() <= " + strconv.Itoa(globalConfiguration.Replay)), stream.Logger(oxyLogger)) - newRoute.Handler(backends[frontend.Backend]) - err := newRoute.GetError() - if err != nil { - log.Error("Error building route: %s", err) + newRoute.Handler(backends[frontend.Backend]) + err := newRoute.GetError() + if err != nil { + log.Error("Error building route: %s", err) + } } } return router, nil diff --git a/web.go b/web.go index 1151ceeb3..ff6ad7c34 100644 --- a/web.go +++ b/web.go @@ -11,6 +11,10 @@ import ( "github.com/gorilla/mux" ) +var ( + webConfiguration *Configuration +) + type WebProvider struct { Address string CertFile, KeyFile string @@ -20,7 +24,7 @@ type Page struct { Configuration Configuration } -func (provider *WebProvider) Provide(configurationChan chan<- *Configuration) { +func (provider *WebProvider) Provide(configurationChan chan<- configMessage) { systemRouter := mux.NewRouter() systemRouter.Methods("GET").Path("/").Handler(http.HandlerFunc(GetHTMLConfigHandler)) systemRouter.Methods("GET").Path("/health").Handler(http.HandlerFunc(GetHealthHandler)) @@ -31,7 +35,7 @@ func (provider *WebProvider) Provide(configurationChan chan<- *Configuration) { b, _ := ioutil.ReadAll(r.Body) err := json.Unmarshal(b, configuration) if err == nil { - configurationChan <- configuration + configurationChan <- configMessage{"web", configuration} GetConfigHandler(rw, r) } else { log.Errorf("Error parsing configuration %+v", err) @@ -62,11 +66,26 @@ func (provider *WebProvider) Provide(configurationChan chan<- *Configuration) { } func GetConfigHandler(rw http.ResponseWriter, r *http.Request) { - templatesRenderer.JSON(rw, http.StatusOK, currentConfiguration) + templatesRenderer.JSON(rw, http.StatusOK, webConfiguration) } func GetHTMLConfigHandler(response http.ResponseWriter, request *http.Request) { - templatesRenderer.HTML(response, http.StatusOK, "configuration", Page{Configuration: *currentConfiguration}) + var cfg Configuration + cfg.Backends = make(map[string]*Backend) + cfg.Frontends = make(map[string]*Frontend) + + // Quick and dirty merge of config for display + for _, config := range currentConfigurations { + for name, config := range config.Backends { + cfg.Backends[name] = config + } + + for name, config := range config.Frontends { + cfg.Frontends[name] = config + } + } + + templatesRenderer.HTML(response, http.StatusOK, "configuration", Page{Configuration: cfg}) } func GetHealthHandler(rw http.ResponseWriter, r *http.Request) { @@ -74,13 +93,13 @@ func GetHealthHandler(rw http.ResponseWriter, r *http.Request) { } func GetBackendsHandler(rw http.ResponseWriter, r *http.Request) { - templatesRenderer.JSON(rw, http.StatusOK, currentConfiguration.Backends) + templatesRenderer.JSON(rw, http.StatusOK, webConfiguration.Backends) } func GetBackendHandler(rw http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) id := vars["backend"] - if backend, ok := currentConfiguration.Backends[id]; ok { + if backend, ok := webConfiguration.Backends[id]; ok { templatesRenderer.JSON(rw, http.StatusOK, backend) } else { http.NotFound(rw, r) @@ -88,13 +107,13 @@ func GetBackendHandler(rw http.ResponseWriter, r *http.Request) { } func GetFrontendsHandler(rw http.ResponseWriter, r *http.Request) { - templatesRenderer.JSON(rw, http.StatusOK, currentConfiguration.Frontends) + templatesRenderer.JSON(rw, http.StatusOK, webConfiguration.Frontends) } func GetFrontendHandler(rw http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) id := vars["frontend"] - if frontend, ok := currentConfiguration.Frontends[id]; ok { + if frontend, ok := webConfiguration.Frontends[id]; ok { templatesRenderer.JSON(rw, http.StatusOK, frontend) } else { http.NotFound(rw, r) @@ -104,7 +123,7 @@ func GetFrontendHandler(rw http.ResponseWriter, r *http.Request) { func GetServersHandler(rw http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) backend := vars["backend"] - if backend, ok := currentConfiguration.Backends[backend]; ok { + if backend, ok := webConfiguration.Backends[backend]; ok { templatesRenderer.JSON(rw, http.StatusOK, backend.Servers) } else { http.NotFound(rw, r) @@ -115,7 +134,7 @@ func GetServerHandler(rw http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) backend := vars["backend"] server := vars["server"] - if backend, ok := currentConfiguration.Backends[backend]; ok { + if backend, ok := webConfiguration.Backends[backend]; ok { if server, ok := backend.Servers[server]; ok { templatesRenderer.JSON(rw, http.StatusOK, server) } else { From 55a50c5ab74b99101dfb292f30b149180baa6901 Mon Sep 17 00:00:00 2001 From: Thorhallur Sverrisson Date: Tue, 22 Sep 2015 19:49:42 +0000 Subject: [PATCH 2/6] Setting webConfiguration on REST PUT to /api --- web.go | 1 + 1 file changed, 1 insertion(+) diff --git a/web.go b/web.go index ff6ad7c34..6e8999916 100644 --- a/web.go +++ b/web.go @@ -35,6 +35,7 @@ func (provider *WebProvider) Provide(configurationChan chan<- configMessage) { b, _ := ioutil.ReadAll(r.Body) err := json.Unmarshal(b, configuration) if err == nil { + webConfiguration = configuration configurationChan <- configMessage{"web", configuration} GetConfigHandler(rw, r) } else { From a07e3951814ea60ecafa9d71db4bdf33b47665eb Mon Sep 17 00:00:00 2001 From: Thorhallur Sverrisson Date: Tue, 22 Sep 2015 23:17:21 +0000 Subject: [PATCH 3/6] Refactoring REST API to handle multiple providers. I changed what I think is needed and I have done manual testing on this. I tried to keep the changes to a minimun. The changes are approx: * HTML output now includes the provider name in parenthesis. * I'm not versed in bootstrap, should the output group providers in a * table? * PUT is only enabled on /api/web. * GET on /api returns a map containing all providers configuration * GET on /api/{provider} will return the config as before on that * provider. --- templates/configuration.tmpl | 12 ++++--- web.go | 62 +++++++++++++++--------------------- 2 files changed, 34 insertions(+), 40 deletions(-) diff --git a/templates/configuration.tmpl b/templates/configuration.tmpl index 602c16e38..5112fc12c 100644 --- a/templates/configuration.tmpl +++ b/templates/configuration.tmpl @@ -27,9 +27,10 @@
- {{range $keyFrontends, $valueFrontends := .Configuration.Frontends}} + {{range $keyProviders, $valueProviders := .Configurations}} + {{range $keyFrontends, $valueFrontends := $valueProviders.Frontends}}
-
{{$keyFrontends}}
+
{{$keyFrontends}} - ({{$keyProviders}})
diff --git a/web.go b/web.go index 6e8999916..76cef144e 100644 --- a/web.go +++ b/web.go @@ -11,17 +11,13 @@ import ( "github.com/gorilla/mux" ) -var ( - webConfiguration *Configuration -) - type WebProvider struct { Address string CertFile, KeyFile string } type Page struct { - Configuration Configuration + Configurations configs } func (provider *WebProvider) Provide(configurationChan chan<- configMessage) { @@ -29,13 +25,20 @@ func (provider *WebProvider) Provide(configurationChan chan<- configMessage) { systemRouter.Methods("GET").Path("/").Handler(http.HandlerFunc(GetHTMLConfigHandler)) systemRouter.Methods("GET").Path("/health").Handler(http.HandlerFunc(GetHealthHandler)) 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) { + 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) b, _ := ioutil.ReadAll(r.Body) err := json.Unmarshal(b, configuration) if err == nil { - webConfiguration = configuration configurationChan <- configMessage{"web", configuration} GetConfigHandler(rw, r) } else { @@ -43,12 +46,12 @@ func (provider *WebProvider) Provide(configurationChan chan<- configMessage) { http.Error(rw, fmt.Sprintf("%+v", err), http.StatusBadRequest) } })) - systemRouter.Methods("GET").Path("/api/backends").Handler(http.HandlerFunc(GetBackendsHandler)) - systemRouter.Methods("GET").Path("/api/backends/{backend}").Handler(http.HandlerFunc(GetBackendHandler)) - systemRouter.Methods("GET").Path("/api/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/frontends").Handler(http.HandlerFunc(GetFrontendsHandler)) - systemRouter.Methods("GET").Path("/api/frontends/{frontend}").Handler(http.HandlerFunc(GetFrontendHandler)) + systemRouter.Methods("GET").Path("/api/{provider}/backends").Handler(http.HandlerFunc(GetBackendsHandler)) + systemRouter.Methods("GET").Path("/api/{provider}/backends/{backend}").Handler(http.HandlerFunc(GetBackendHandler)) + systemRouter.Methods("GET").Path("/api/{provider}/backends/{backend}/servers").Handler(http.HandlerFunc(GetServersHandler)) + systemRouter.Methods("GET").Path("/api/{provider}/backends/{backend}/servers/{server}").Handler(http.HandlerFunc(GetServerHandler)) + systemRouter.Methods("GET").Path("/api/{provider}/frontends").Handler(http.HandlerFunc(GetFrontendsHandler)) + 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"}))) go func() { @@ -67,26 +70,11 @@ func (provider *WebProvider) Provide(configurationChan chan<- configMessage) { } func GetConfigHandler(rw http.ResponseWriter, r *http.Request) { - templatesRenderer.JSON(rw, http.StatusOK, webConfiguration) + templatesRenderer.JSON(rw, http.StatusOK, currentConfigurations) } func GetHTMLConfigHandler(response http.ResponseWriter, request *http.Request) { - var cfg Configuration - cfg.Backends = make(map[string]*Backend) - cfg.Frontends = make(map[string]*Frontend) - - // Quick and dirty merge of config for display - for _, config := range currentConfigurations { - for name, config := range config.Backends { - cfg.Backends[name] = config - } - - for name, config := range config.Frontends { - cfg.Frontends[name] = config - } - } - - templatesRenderer.HTML(response, http.StatusOK, "configuration", Page{Configuration: cfg}) + templatesRenderer.HTML(response, http.StatusOK, "configuration", Page{Configurations: currentConfigurations}) } func GetHealthHandler(rw http.ResponseWriter, r *http.Request) { @@ -94,13 +82,14 @@ func GetHealthHandler(rw http.ResponseWriter, r *http.Request) { } func GetBackendsHandler(rw http.ResponseWriter, r *http.Request) { - templatesRenderer.JSON(rw, http.StatusOK, webConfiguration.Backends) + vars := mux.Vars(r) + templatesRenderer.JSON(rw, http.StatusOK, currentConfigurations[vars["provider"]].Backends) } func GetBackendHandler(rw http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) id := vars["backend"] - if backend, ok := webConfiguration.Backends[id]; ok { + if backend, ok := currentConfigurations[vars["provider"]].Backends[id]; ok { templatesRenderer.JSON(rw, http.StatusOK, backend) } else { http.NotFound(rw, r) @@ -108,13 +97,14 @@ func GetBackendHandler(rw http.ResponseWriter, r *http.Request) { } func GetFrontendsHandler(rw http.ResponseWriter, r *http.Request) { - templatesRenderer.JSON(rw, http.StatusOK, webConfiguration.Frontends) + vars := mux.Vars(r) + templatesRenderer.JSON(rw, http.StatusOK, currentConfigurations[vars["provider"]].Frontends) } func GetFrontendHandler(rw http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) id := vars["frontend"] - if frontend, ok := webConfiguration.Frontends[id]; ok { + if frontend, ok := currentConfigurations[vars["provider"]].Frontends[id]; ok { templatesRenderer.JSON(rw, http.StatusOK, frontend) } else { http.NotFound(rw, r) @@ -124,7 +114,7 @@ func GetFrontendHandler(rw http.ResponseWriter, r *http.Request) { func GetServersHandler(rw http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) backend := vars["backend"] - if backend, ok := webConfiguration.Backends[backend]; ok { + if backend, ok := currentConfigurations[vars["provider"]].Backends[backend]; ok { templatesRenderer.JSON(rw, http.StatusOK, backend.Servers) } else { http.NotFound(rw, r) @@ -135,7 +125,7 @@ func GetServerHandler(rw http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) backend := vars["backend"] server := vars["server"] - if backend, ok := webConfiguration.Backends[backend]; ok { + if backend, ok := currentConfigurations[vars["provider"]].Backends[backend]; ok { if server, ok := backend.Servers[server]; ok { templatesRenderer.JSON(rw, http.StatusOK, server) } else { From c2635604011149f70114b85049afffbb3cd512a7 Mon Sep 17 00:00:00 2001 From: Thorhallur Sverrisson Date: Wed, 23 Sep 2015 02:11:46 +0000 Subject: [PATCH 4/6] Documentation improvements --- docs/index.md | 146 +++++++++++++++++++++++++++++--------------------- 1 file changed, 86 insertions(+), 60 deletions(-) diff --git a/docs/index.md b/docs/index.md index b8a0e4868..045f221cf 100644 --- a/docs/index.md +++ b/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 $ curl -s "http://localhost:8082/api" | jq . { - "Frontends": { - "frontend-traefik": { - "Routes": { - "route-host-traefik": { - "Value": "traefik.docker.localhost", - "Rule": "Host" - } + "file": { + "Frontends": { + "frontend-traefik": { + "Routes": { + "route-host-traefik": { + "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": { - "Routes": { - "route-host-test": { - "Value": "test.docker.localhost", - "Rule": "Host" + "Backends": { + "backend-test2": { + "Servers": { + "server-stoic_brattain": { + "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": { - "backend-test2": { - "Servers": { - "server-stoic_brattain": { - "Weight": 0, - "Url": "http://172.17.0.8:80" + "marathon": { + "Frontends": { + "frontend-marathon": { + "Routes": { + "route-host-marathon": { + "Value": "marathon.docker.localhost", + "Rule": "Host" + } }, - "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-marathon" + }, }, - "backend-test1": { - "Servers": { - "server-trusting_wozniak": { - "Weight": 0, - "Url": "http://172.17.0.5:80" + "Backends": { + "backend-marathon": { + "Servers": { + "server-marathon-1": { + "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/backends/{backend}```: ```GET``` a backend -* ```/api/backends/{backend}/servers```: ```GET``` servers in a backend -* ```/api/backends/{backend}/servers/{server}```: ```GET``` a server in a backend -* ```/api/frontends```: ```GET``` frontends -* ```/api/frontends/{frontend}```: ```GET``` a frontend +* ```/api/{provider}```: ```GET``` or ```PUT``` provider +* ```/api/{provider}/backends```: ```GET``` backends +* ```/api/{provider}/backends/{backend}```: ```GET``` a backend +* ```/api/{provider}/backends/{backend}/servers```: ```GET``` servers in a backend +* ```/api/{provider}/backends/{backend}/servers/{server}```: ```GET``` a server in a backend +* ```/api/{provider}/frontends```: ```GET``` frontends +* ```/api/{provider}/frontends/{frontend}```: ```GET``` a frontend ## Docker backend From cfa232741d4bc6bb1c45c03458973b1f933ef803 Mon Sep 17 00:00:00 2001 From: Thorhallur Sverrisson Date: Wed, 23 Sep 2015 05:48:32 +0000 Subject: [PATCH 5/6] Adding error checks to REST calls --- web.go | 76 ++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 50 insertions(+), 26 deletions(-) diff --git a/web.go b/web.go index 76cef144e..4aa1371e4 100644 --- a/web.go +++ b/web.go @@ -83,55 +83,79 @@ func GetHealthHandler(rw http.ResponseWriter, r *http.Request) { func GetBackendsHandler(rw http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) - templatesRenderer.JSON(rw, http.StatusOK, currentConfigurations[vars["provider"]].Backends) + 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) { vars := mux.Vars(r) - id := vars["backend"] - if backend, ok := currentConfigurations[vars["provider"]].Backends[id]; ok { - templatesRenderer.JSON(rw, http.StatusOK, backend) - } else { - http.NotFound(rw, r) + providerId := vars["provider"] + backendId := vars["backend"] + if provider, ok := currentConfigurations[providerId]; ok { + if backend, ok := provider.Backends[backendId]; ok { + templatesRenderer.JSON(rw, http.StatusOK, backend) + return + } } + http.NotFound(rw, r) } func GetFrontendsHandler(rw http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) - templatesRenderer.JSON(rw, http.StatusOK, currentConfigurations[vars["provider"]].Frontends) + 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) { vars := mux.Vars(r) - id := vars["frontend"] - if frontend, ok := currentConfigurations[vars["provider"]].Frontends[id]; ok { - templatesRenderer.JSON(rw, http.StatusOK, frontend) - } else { - http.NotFound(rw, r) + providerId := vars["provider"] + frontendId := vars["frontend"] + if provider, ok := currentConfigurations[providerId]; ok { + if frontend, ok := provider.Frontends[frontendId]; ok { + templatesRenderer.JSON(rw, http.StatusOK, frontend) + return + } } + http.NotFound(rw, r) } func GetServersHandler(rw http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) - backend := vars["backend"] - if backend, ok := currentConfigurations[vars["provider"]].Backends[backend]; ok { - templatesRenderer.JSON(rw, http.StatusOK, backend.Servers) - } else { - http.NotFound(rw, r) + providerId := vars["provider"] + backendId := vars["backend"] + if provider, ok := currentConfigurations[providerId]; ok { + if backend, ok := provider.Backends[backendId]; ok { + templatesRenderer.JSON(rw, http.StatusOK, backend.Servers) + return + } } + http.NotFound(rw, r) } func GetServerHandler(rw http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) - backend := vars["backend"] - server := vars["server"] - if backend, ok := currentConfigurations[vars["provider"]].Backends[backend]; ok { - if server, ok := backend.Servers[server]; ok { - templatesRenderer.JSON(rw, http.StatusOK, server) - } else { - http.NotFound(rw, r) + providerId := vars["provider"] + backendId := vars["backend"] + serverId := vars["server"] + fmt.Printf("%v %v %v\n", providerId, backendId, serverId) + if provider, ok := currentConfigurations[providerId]; ok { + fmt.Printf("provider OK\n") + if backend, ok := provider.Backends[backendId]; ok { + fmt.Printf("backend OK\n") + if server, ok := backend.Servers[serverId]; ok { + fmt.Printf("server OK\n") + templatesRenderer.JSON(rw, http.StatusOK, server) + return + } } - } else { - http.NotFound(rw, r) } + http.NotFound(rw, r) } From cc795b917a0c272ecddaf5a66775337fe3d93f8e Mon Sep 17 00:00:00 2001 From: Thorhallur Sverrisson Date: Wed, 23 Sep 2015 10:40:55 +0000 Subject: [PATCH 6/6] Removing debug Printf statements --- web.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/web.go b/web.go index 4aa1371e4..9e80dfafa 100644 --- a/web.go +++ b/web.go @@ -145,13 +145,9 @@ func GetServerHandler(rw http.ResponseWriter, r *http.Request) { providerId := vars["provider"] backendId := vars["backend"] serverId := vars["server"] - fmt.Printf("%v %v %v\n", providerId, backendId, serverId) if provider, ok := currentConfigurations[providerId]; ok { - fmt.Printf("provider OK\n") if backend, ok := provider.Backends[backendId]; ok { - fmt.Printf("backend OK\n") if server, ok := backend.Servers[serverId]; ok { - fmt.Printf("server OK\n") templatesRenderer.JSON(rw, http.StatusOK, server) return }