Example #1
0
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)
	}
}
Example #2
0
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())
}
Example #3
0
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)
	}
}
Example #4
0
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)
	}
}
Example #5
0
// 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()
}
Example #6
0
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)
	}
}