func TestBasicWalk(t *testing.T) { var ( procRoot = "/proc" procFunc = func(process.Process, process.Process) {} ) if err := process.NewWalker(procRoot).Walk(procFunc); err != nil { t.Fatal(err) } }
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 TestWalker(t *testing.T) { fs_hook.Mock(mockFS) defer fs_hook.Restore() want := map[int]process.Process{ 3: {PID: 3, PPID: 2, Name: "curl", Cmdline: "curl google.com", Threads: 1, RSSBytes: 8192, RSSBytesLimit: 2048, OpenFilesCount: 3, OpenFilesLimit: 32768}, 2: {PID: 2, PPID: 1, Name: "bash", Cmdline: "bash", Threads: 1, OpenFilesCount: 2}, 4: {PID: 4, PPID: 3, Name: "apache", Cmdline: "apache", Threads: 1, OpenFilesCount: 1}, 1: {PID: 1, PPID: 0, Name: "init", Cmdline: "init", Threads: 1, OpenFilesCount: 0}, } have := map[int]process.Process{} walker := process.NewWalker("/proc") err := walker.Walk(func(p, _ process.Process) { have[p.PID] = p }) if err != nil || !reflect.DeepEqual(want, have) { t.Errorf("%v (%v)", test.Diff(want, have), err) } }
func TestWalkProcPid(t *testing.T) { fs_hook.Mock(mockFS) defer fs_hook.Restore() buf := bytes.Buffer{} walker := process.NewWalker(procRoot) ticker := time.NewTicker(time.Millisecond) defer ticker.Stop() pWalker := newPidWalker(walker, ticker.C, 1) have, err := pWalker.walk(&buf) if err != nil { t.Fatal(err) } want := map[uint64]*Proc{ 5107: { PID: 1, Name: "foo", }, } if !reflect.DeepEqual(want, have) { t.Fatalf("%+v", have) } }
// Main runs the probe func probeMain(flags probeFlags) { setLogLevel(flags.logLevel) setLogFormatter(flags.logPrefix) // Setup in memory metrics sink inm := metrics.NewInmemSink(time.Minute, 2*time.Minute) sig := metrics.DefaultInmemSignal(inm) defer sig.Stop() metrics.NewGlobal(metrics.DefaultConfig("scope-probe"), inm) defer log.Info("probe exiting") if flags.spyProcs && os.Getegid() != 0 { log.Warn("--probe.process=true, but that requires root to find everything") } rand.Seed(time.Now().UnixNano()) var ( probeID = strconv.FormatInt(rand.Int63(), 16) hostName = hostname.Get() hostID = hostName // TODO(pb): we should sanitize the hostname ) log.Infof("probe starting, version %s, ID %s", version, probeID) log.Infof("command line: %v", os.Args) checkpointFlags := map[string]string{} if flags.kubernetesEnabled { checkpointFlags["kubernetes_enabled"] = "true" } go check(checkpointFlags) var targets = []string{} if !flags.noApp { targets = append(targets, fmt.Sprintf("localhost:%d", xfer.AppPort)) } if len(flag.Args()) > 0 { targets = append(targets, flag.Args()...) } log.Infof("publishing to: %s", strings.Join(targets, ", ")) probeConfig := appclient.ProbeConfig{ Token: flags.token, ProbeID: probeID, Insecure: flags.insecure, } clients := appclient.NewMultiAppClient(func(hostname, endpoint string) (appclient.AppClient, error) { return appclient.NewAppClient( probeConfig, hostname, endpoint, xfer.ControlHandlerFunc(controls.HandleControlRequest), ) }) defer clients.Stop() dnsLookupFn := net.LookupIP if flags.resolver != "" { dnsLookupFn = appclient.LookupUsing(flags.resolver) } resolver := appclient.NewResolver(targets, dnsLookupFn, clients.Set) defer resolver.Stop() processCache := process.NewCachingWalker(process.NewWalker(flags.procRoot)) scanner := procspy.NewConnectionScanner(processCache) endpointReporter := endpoint.NewReporter(hostID, hostName, flags.spyProcs, flags.useConntrack, scanner) defer endpointReporter.Stop() p := probe.New(flags.spyInterval, flags.publishInterval, clients) p.AddTicker(processCache) hostReporter := host.NewReporter(hostID, hostName, probeID, version, clients) defer hostReporter.Stop() p.AddReporter( endpointReporter, hostReporter, process.NewReporter(processCache, hostID, process.GetDeltaTotalJiffies), ) p.AddTagger(probe.NewTopologyTagger(), host.NewTagger(hostID)) if flags.dockerEnabled { // Don't add the bridge in Kubernetes since container IPs are global and // shouldn't be scoped if !flags.kubernetesEnabled { if err := report.AddLocalBridge(flags.dockerBridge); err != nil { log.Errorf("Docker: problem with bridge %s: %v", flags.dockerBridge, err) } } if registry, err := docker.NewRegistry(flags.dockerInterval, clients, true, hostID); err == nil { defer registry.Stop() p.AddTagger(docker.NewTagger(registry, processCache)) p.AddReporter(docker.NewReporter(registry, hostID, probeID, p)) } else { log.Errorf("Docker: failed to start registry: %v", err) } } if flags.kubernetesEnabled { if client, err := kubernetes.NewClient(flags.kubernetesAPI, flags.kubernetesInterval); err == nil { defer client.Stop() reporter := kubernetes.NewReporter(client, clients, probeID, hostID, p) defer reporter.Stop() p.AddReporter(reporter) p.AddTagger(reporter) } else { log.Errorf("Kubernetes: failed to start client: %v", err) log.Errorf("Kubernetes: make sure to run Scope inside a POD with a service account or provide a valid kubernetes.api url") } } if flags.weaveAddr != "" { client := weave.NewClient(sanitize.URL("http://", 6784, "")(flags.weaveAddr)) weave := overlay.NewWeave(hostID, client) defer weave.Stop() p.AddTagger(weave) p.AddReporter(weave) dockerBridgeIP, err := network.GetFirstAddressOf(flags.dockerBridge) if err != nil { log.Println("Error getting docker bridge ip:", err) } else { weaveDNSLookup := appclient.LookupUsing(dockerBridgeIP + ":53") weaveResolver := appclient.NewResolver([]string{flags.weaveHostname}, weaveDNSLookup, clients.Set) defer weaveResolver.Stop() } } pluginRegistry, err := plugins.NewRegistry( flags.pluginsRoot, pluginAPIVersion, map[string]string{ "probe_id": probeID, "api_version": pluginAPIVersion, }, ) if err != nil { log.Errorf("plugins: problem loading: %v", err) } else { defer pluginRegistry.Close() p.AddReporter(pluginRegistry) } if flags.httpListen != "" { go func() { log.Infof("Profiling data being exported to %s", flags.httpListen) log.Infof("go tool pprof http://%s/debug/pprof/{profile,heap,block}", flags.httpListen) log.Infof("Profiling endpoint %s terminated: %v", flags.httpListen, http.ListenAndServe(flags.httpListen, nil)) }() } p.Start() defer p.Stop() common.SignalHandlerLoop() }
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) } }