diff --git a/docker.go b/docker.go new file mode 100644 index 000000000..b8bdcee47 --- /dev/null +++ b/docker.go @@ -0,0 +1,65 @@ +package main +import( + "github.com/fsouza/go-dockerclient" + "fmt" + "github.com/leekchan/gtf" + "bytes" + "github.com/BurntSushi/toml" +) + +type DockerProvider struct { + dockerClient *docker.Client +} + +func (provider *DockerProvider) Provide(serviceChan chan<- Service){ + endpoint := "unix:///var/run/docker.sock" + provider.dockerClient, _ = docker.NewClient(endpoint) + dockerEvents := make(chan *docker.APIEvents) + provider.dockerClient.AddEventListener(dockerEvents) + go func() { + for { + event := <-dockerEvents + fmt.Println("Event receveived", event) + service:= provider.loadDockerConfig() + serviceChan <- *service + } + }() + + service:= provider.loadDockerConfig() + fmt.Println("Sending service") + serviceChan <- *service +} + +func (provider *DockerProvider) loadDockerConfig() *Service { + service := new(Service) + containerList, _ := provider.dockerClient.ListContainers(docker.ListContainersOptions{}) + containersInspected := []docker.Container{} + for _, container := range containerList { + containerInspected, _ := provider.dockerClient.InspectContainer(container.ID) + containersInspected = append(containersInspected, *containerInspected) + } + containers := struct { + Containers []docker.Container + }{ + containersInspected, + } + tmpl, err := gtf.New("docker.tmpl").ParseFiles("docker.tmpl") + if err != nil { + panic(err) + } + + var buffer bytes.Buffer + + err = tmpl.Execute(&buffer, containers) + if err != nil { + panic(err) + } + + fmt.Println(buffer.String()) + + if _, err := toml.Decode(buffer.String(), service); err != nil { + fmt.Println(err) + return nil + } + return service +} \ No newline at end of file diff --git a/provider.go b/provider.go new file mode 100644 index 000000000..91d36656f --- /dev/null +++ b/provider.go @@ -0,0 +1,5 @@ +package main + +type Provider interface { + Provide(chan<- Service) +} diff --git a/service.go b/service.go new file mode 100644 index 000000000..c3a90ec0a --- /dev/null +++ b/service.go @@ -0,0 +1,25 @@ +package main + + +type Backend struct { + Servers map[string]Server +} + +type Server struct { + Url string +} + +type Rule struct { + Category string + Value string +} + +type Route struct { + Backends []string + Rules map[string]Rule +} + +type Service struct { + Backends map[string]Backend + Routes map[string]Route +} \ No newline at end of file diff --git a/tortuous.go b/tortuous.go index 690706505..2a7d5214f 100644 --- a/tortuous.go +++ b/tortuous.go @@ -1,64 +1,57 @@ package main + import ( - "github.com/gorilla/mux" - "github.com/tylerb/graceful" - "net/http" + "bytes" "fmt" - "os" + "github.com/BurntSushi/toml" + "github.com/fsouza/go-dockerclient" + "github.com/gorilla/mux" + "github.com/leekchan/gtf" "github.com/mailgun/oxy/forward" "github.com/mailgun/oxy/roundrobin" - "time" - "net" - "os/signal" - "syscall" - "github.com/BurntSushi/toml" - "reflect" - "net/url" - "github.com/fsouza/go-dockerclient" - "github.com/leekchan/gtf" - "bytes" + "github.com/tylerb/graceful" "github.com/unrolled/render" + "net" + "net/http" + "net/url" + "os" + "os/signal" + "reflect" + "syscall" + "time" ) -type Backend struct { - Servers map[string]Server -} - -type Server struct { - Url string -} - -type Rule struct { - Category string - Value string -} - -type Route struct { - Backends []string - Rules map[string]Rule -} - -type Config struct { - Backends map[string]Backend - Routes map[string]Route -} - var srv *graceful.Server var userRouter *mux.Router -var config = new(Config) var renderer = render.New() +var currentService = new(Service) +var serviceChan = make(chan Service) +var providers = []Provider{} func main() { + providers = append(providers, new(DockerProvider)) + sigs := make(chan os.Signal, 1) signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) systemRouter := mux.NewRouter() - systemRouter.Methods("POST").Path("/restart").HandlerFunc(RestartHandler) systemRouter.Methods("POST").Path("/reload").HandlerFunc(ReloadConfigHandler) systemRouter.Methods("GET").Path("/").HandlerFunc(GetConfigHandler) go http.ListenAndServe(":8000", systemRouter) - userRouter = LoadConfig() + go func() { + for { + service := <-serviceChan + fmt.Println("Service receveived", service) + currentService = &service + userRouter = LoadConfig(service) + srv.Stop(10 * time.Second) + } + }() + + for _, provider := range providers { + provider.Provide(serviceChan) + } goAway := false go func() { @@ -68,12 +61,12 @@ func main() { srv.Stop(10 * time.Second) }() - for{ - if (goAway){ + for { + if goAway { break } srv = &graceful.Server{ - Timeout: 10 * time.Second, + Timeout: 10 * time.Second, NoSignalHandling: true, ConnState: func(conn net.Conn, state http.ConnState) { @@ -81,23 +74,21 @@ func main() { }, Server: &http.Server{ - Addr: ":8001", + Addr: ":8001", Handler: userRouter, }, } go srv.ListenAndServe() fmt.Println("Started") - <- srv.StopChan() + <-srv.StopChan() fmt.Println("Stopped") } } -func LoadDockerConfig(){ - endpoint := "unix:///var/run/docker.sock" - client, _ := docker.NewClient(endpoint) +func LoadDockerConfig(client *docker.Client, service Service) { containerList, _ := client.ListContainers(docker.ListContainersOptions{}) - containersInspected := []docker.Container{} + containersInspected := []docker.Container{} for _, container := range containerList { containerInspected, _ := client.InspectContainer(container.ID) containersInspected = append(containersInspected, *containerInspected) @@ -108,39 +99,52 @@ func LoadDockerConfig(){ containersInspected, } tmpl, err := gtf.New("docker.tmpl").ParseFiles("docker.tmpl") - if err != nil { panic(err) } + if err != nil { + panic(err) + } var buffer bytes.Buffer err = tmpl.Execute(&buffer, containers) - if err != nil { panic(err) } + if err != nil { + panic(err) + } fmt.Println(buffer.String()) - if _, err := toml.Decode(buffer.String(), config); err != nil { + if _, err := toml.Decode(buffer.String(), service); err != nil { fmt.Println(err) return } } -func LoadFileConfig(){ - if _, err := toml.DecodeFile("tortuous.toml", config); err != nil { +func LoadFileConfig(service Service) { + if _, err := toml.DecodeFile("tortuous.toml", service); err != nil { fmt.Println(err) return } } - -func LoadConfig() *mux.Router{ - //LoadDockerConfig() - LoadFileConfig() +func LoadConfig(service Service) *mux.Router { + /*endpoint := "unix:///var/run/docker.sock" + client, _ := docker.NewClient(endpoint) + dockerEvents := make(chan *docker.APIEvents) + LoadDockerConfig(client) + client.AddEventListener(dockerEvents) + go func() { + for { + event := <-dockerEvents + fmt.Println("Event receveived", event) + } + }()*/ + //LoadFileConfig() router := mux.NewRouter() - for routeName, route := range config.Routes { + for routeName, route := range service.Routes { fmt.Println("Creating route", routeName) fwd, _ := forward.New() - newRoutes:= []*mux.Route{} - for ruleName, rule := range route.Rules{ + newRoutes := []*mux.Route{} + for ruleName, rule := range route.Rules { fmt.Println("Creating rule", ruleName) newRouteReflect := Invoke(router.NewRoute(), rule.Category, rule.Value) newRoute := newRouteReflect[0].Interface().(*mux.Route) @@ -150,7 +154,7 @@ func LoadConfig() *mux.Router{ fmt.Println("Creating backend", backendName) lb, _ := roundrobin.New(fwd) rb, _ := roundrobin.NewRebalancer(lb) - for serverName, server := range config.Backends[backendName].Servers { + for serverName, server := range service.Backends[backendName].Servers { fmt.Println("Creating server", serverName) url, _ := url.Parse(server.Url) rb.UpsertServer(url) @@ -163,25 +167,28 @@ func LoadConfig() *mux.Router{ return router } +func DeployService() { + userRouter = LoadConfig(*currentService) +} + func ReloadConfigHandler(rw http.ResponseWriter, r *http.Request) { - userRouter = LoadConfig() + DeployService() + srv.Stop(10 * time.Second) renderer.JSON(rw, http.StatusOK, map[string]interface{}{"status": "reloaded"}) } func RestartHandler(rw http.ResponseWriter, r *http.Request) { - srv.Stop(10 * time.Second) renderer.JSON(rw, http.StatusOK, map[string]interface{}{"status": "restarted"}) } func GetConfigHandler(rw http.ResponseWriter, r *http.Request) { - renderer.JSON(rw, http.StatusOK, config) + renderer.JSON(rw, http.StatusOK, currentService) } -func Invoke(any interface{}, name string, args... interface{}) []reflect.Value { +func Invoke(any interface{}, name string, args ...interface{}) []reflect.Value { inputs := make([]reflect.Value, len(args)) for i, _ := range args { inputs[i] = reflect.ValueOf(args[i]) } return reflect.ValueOf(any).MethodByName(name).Call(inputs) } -