func (t *tracer) pidsForContainer(id string) ([]int, error) { var container docker.Container t.docker.WalkContainers(func(c docker.Container) { if c.ID() == id { container = c } }) if container == nil { return []int{}, fmt.Errorf("Not Found") } pidTree, err := process.NewTree(process.NewWalker("/proc")) if err != nil { return []int{}, err } return pidTree.GetChildren(container.PID()) }
func TestTree(t *testing.T) { walker := &mockWalker{ processes: []process.Process{ {PID: 1, PPID: 0, Name: "init"}, {PID: 2, PPID: 1, Name: "bash"}, {PID: 3, PPID: 1, Name: "apache", Threads: 2}, {PID: 4, PPID: 2, Name: "ping", Cmdline: "ping foo.bar.local"}, }, } tree, err := process.NewTree(walker) if err != nil { t.Fatalf("newProcessTree error: %v", err) } for pid, want := range map[int]int{2: 1, 3: 1, 4: 2} { have, err := tree.GetParent(pid) if err != nil || !reflect.DeepEqual(want, have) { t.Errorf("%d: want %#v, have %#v (%v)", pid, want, have, err) } } }
func (t *tracer) http(port int) { router := mux.NewRouter() router.Methods("GET").Path("/container").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { pidTree, err := process.NewTree(process.NewWalker("/proc")) if err != nil { respondWith(w, http.StatusBadRequest, err.Error()) return } containers := []Container{} t.docker.WalkContainers(func(container docker.Container) { children, _ := pidTree.GetChildren(container.PID()) out := Container{ Name: strings.TrimPrefix(container.Container().Name, "/"), ID: container.ID(), PIDs: children, } containers = append(containers, out) }) respondWith(w, http.StatusOK, containers) }) router.Methods("POST").Path("/container/{id}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { id := mux.Vars(r)["id"] children, err := t.pidsForContainer(id) if err != nil { respondWith(w, http.StatusBadRequest, err.Error()) return } for _, pid := range children { t.ptrace.TraceProcess(pid) } w.WriteHeader(204) }) router.Methods("DELETE").Path("/container/{id}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { id := mux.Vars(r)["id"] children, err := t.pidsForContainer(id) if err != nil { respondWith(w, http.StatusBadRequest, err.Error()) return } for _, pid := range children { t.ptrace.StopTracing(pid) } w.WriteHeader(204) }) router.Methods("GET").Path("/pid").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { respondWith(w, http.StatusOK, t.ptrace.AttachedPIDs()) }) router.Methods("POST").Path("/pid/{pid:\\d+}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { pid, err := strconv.Atoi(mux.Vars(r)["pid"]) if err != nil { respondWith(w, http.StatusBadRequest, err.Error()) return } t.ptrace.TraceProcess(pid) w.WriteHeader(204) }) router.Methods("DELETE").Path("/pid/{pid:\\d+}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { pid, err := strconv.Atoi(mux.Vars(r)["pid"]) if err != nil { respondWith(w, http.StatusBadRequest, err.Error()) return } t.ptrace.StopTracing(pid) w.WriteHeader(204) }) router.Methods("GET").Path("/traces").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { respondWith(w, http.StatusOK, t.store.Traces()) }) mime.AddExtensionType(".svg", "image/svg+xml") router.Methods("GET").PathPrefix("/").Handler(http.FileServer(FS(false))) // everything else is static log.Printf("Launching HTTP API on port %d", port) srv := &http.Server{ Addr: fmt.Sprintf(":%d", port), Handler: router, } if err := srv.ListenAndServe(); err != nil { log.Printf("Unable to create http listener: %v", err) } }