func getEtcdNode(w http.ResponseWriter, r *http.Request) { etcd := etcd.NewClient([]string{"http://127.0.0.1:4001"}) nodePath := r.FormValue("node") recStr := paramWithDefault(r, "recursive", "false") rec, err := strconv.ParseBool(recStr) if err != nil { w.Write([]byte(err.Error())) return } resp, err := etcd.Get(nodePath, false, rec) w.Write([]byte(fmt.Sprintf("resp: %+v err: %s", resp, err))) }
// This test creates a single node and then set a value to it. // Then this test kills the node and restart it and tries to get the value again. func TestSingleNodeRecovery(t *testing.T) { procAttr := new(os.ProcAttr) procAttr.Files = []*os.File{nil, os.Stdout, os.Stderr} args := []string{"etcd", "-d=/tmp/node1"} process, err := os.StartProcess("etcd", append(args, "-i"), procAttr) if err != nil { t.Fatal("start process failed:" + err.Error()) return } time.Sleep(time.Second) etcd.SyncCluster() // Test Set result, err := etcd.Set("foo", "bar", 100) if err != nil || result.Key != "/foo" || result.Value != "bar" || result.TTL != 99 { if err != nil { t.Fatal(err) } t.Fatalf("Set 1 failed with %s %s %v", result.Key, result.Value, result.TTL) } time.Sleep(time.Second) process.Kill() process, err = os.StartProcess("etcd", args, procAttr) defer process.Kill() if err != nil { t.Fatal("start process failed:" + err.Error()) return } time.Sleep(time.Second) results, err := etcd.Get("foo") if err != nil { t.Fatal("get fail: " + err.Error()) return } result = results[0] if err != nil || result.Key != "/foo" || result.Value != "bar" || result.TTL > 99 { if err != nil { t.Fatal(err) } t.Fatalf("Recovery Get failed with %s %s %v", result.Key, result.Value, result.TTL) } }
func main() { runtime.GOMAXPROCS(1) debugMode = getopt("DEBUG", "") != "" port := getopt("PORT", "8000") endpoint := getopt("DOCKER_HOST", "unix:///var/run/docker.sock") routespath := getopt("ROUTESPATH", "/var/lib/logspout") client, err := docker.NewClient(endpoint) assert(err, "docker") attacher := NewAttachManager(client) router := NewRouteManager(attacher) // HACK: if we are connecting to etcd, get the logger's connection // details from there if etcdHost := os.Getenv("ETCD_HOST"); etcdHost != "" { connectionString := []string{"http://" + etcdHost + ":4001"} debug("etcd:", connectionString[0]) etcd := etcd.NewClient(connectionString) etcd.SetDialTimeout(3 * time.Second) hostResp, err := etcd.Get("/deis/logs/host", false, false) assert(err, "url") portResp, err := etcd.Get("/deis/logs/port", false, false) assert(err, "url") protocol := getEtcdValueOrDefault(etcd, "/deis/logs/protocol", "udp") host := fmt.Sprintf("%s:%s", hostResp.Node.Value, portResp.Node.Value) log.Printf("routing all to %s://%s", protocol, host) router.Add(&Route{Target: Target{Type: "syslog", Addr: host, Protocol: protocol}}) } if len(os.Args) > 1 { u, err := url.Parse(os.Args[1]) assert(err, "url") log.Println("routing all to " + os.Args[1]) router.Add(&Route{Target: Target{Type: u.Scheme, Addr: u.Host}}) } if _, err := os.Stat(routespath); err == nil { log.Println("loading and persisting routes in " + routespath) assert(router.Load(RouteFileStore(routespath)), "persistor") } m := martini.Classic() m.Get("/logs(?:/(?P<predicate>[a-zA-Z]+):(?P<value>.+))?", func(w http.ResponseWriter, req *http.Request, params martini.Params) { source := new(Source) switch { case params["predicate"] == "id" && params["value"] != "": source.ID = params["value"][:12] case params["predicate"] == "name" && params["value"] != "": source.Name = params["value"] case params["predicate"] == "filter" && params["value"] != "": source.Filter = params["value"] } if source.ID != "" && attacher.Get(source.ID) == nil { http.NotFound(w, req) return } logstream := make(chan *Log) defer close(logstream) var closer <-chan bool if req.Header.Get("Upgrade") == "websocket" { closerBi := make(chan bool) go websocketStreamer(w, req, logstream, closerBi) closer = closerBi } else { go httpStreamer(w, req, logstream, source.All() || source.Filter != "") closer = w.(http.CloseNotifier).CloseNotify() } attacher.Listen(source, logstream, closer) }) m.Get("/routes", func(w http.ResponseWriter, req *http.Request) { w.Header().Add("Content-Type", "application/json") routes, _ := router.GetAll() w.Write(append(marshal(routes), '\n')) }) m.Post("/routes", func(w http.ResponseWriter, req *http.Request) (int, string) { route := new(Route) if err := unmarshal(req.Body, route); err != nil { return http.StatusBadRequest, "Bad request: " + err.Error() } // TODO: validate? router.Add(route) w.Header().Add("Content-Type", "application/json") return http.StatusCreated, string(append(marshal(route), '\n')) }) m.Get("/routes/:id", func(w http.ResponseWriter, req *http.Request, params martini.Params) { route, _ := router.Get(params["id"]) if route == nil { http.NotFound(w, req) return } w.Write(append(marshal(route), '\n')) }) m.Delete("/routes/:id", func(w http.ResponseWriter, req *http.Request, params martini.Params) { if ok := router.Remove(params["id"]); !ok { http.NotFound(w, req) } }) log.Println("logspout serving http on :" + port) log.Fatal(http.ListenAndServe(":"+port, m)) }