Replace godep with dep

This commit is contained in:
Manuel de Brito Fontes 2017-10-06 17:26:14 -03:00
parent 1e7489927c
commit bf5616c65b
14883 changed files with 3937406 additions and 361781 deletions

View file

@ -1 +1 @@
0.1.0
0.0.2

View file

@ -0,0 +1,13 @@
package config
import (
. "gopkg.in/check.v1"
"testing"
)
// Hook up gocheck into the "go test" runner.
func Test(t *testing.T) { TestingT(t) }
type MySuite struct{}
var _ = Suite(&MySuite{})

View file

@ -0,0 +1,268 @@
package config
import (
"bytes"
"fmt"
"io/ioutil"
"path/filepath"
"regexp"
"strings"
"text/template"
common "github.com/ncabatoff/process-exporter"
"gopkg.in/yaml.v2"
)
type (
Matcher interface {
// Match returns empty string for no match, or the group name on success.
Match(common.NameAndCmdline) bool
}
FirstMatcher []common.MatchNamer
Config struct {
MatchNamers FirstMatcher
}
commMatcher struct {
comms map[string]struct{}
}
exeMatcher struct {
exes map[string]string
}
cmdlineMatcher struct {
regexes []*regexp.Regexp
captures map[string]string
}
andMatcher []Matcher
templateNamer struct {
template *template.Template
}
matchNamer struct {
andMatcher
templateNamer
}
templateParams struct {
Comm string
ExeBase string
ExeFull string
Matches map[string]string
}
)
func (f FirstMatcher) MatchAndName(nacl common.NameAndCmdline) (bool, string) {
for _, m := range f {
if matched, name := m.MatchAndName(nacl); matched {
return true, name
}
}
return false, ""
}
func (m *matchNamer) MatchAndName(nacl common.NameAndCmdline) (bool, string) {
if !m.Match(nacl) {
return false, ""
}
matches := make(map[string]string)
for _, m := range m.andMatcher {
if mc, ok := m.(*cmdlineMatcher); ok {
for k, v := range mc.captures {
matches[k] = v
}
}
}
exebase, exefull := nacl.Name, nacl.Name
if len(nacl.Cmdline) > 0 {
exefull = nacl.Cmdline[0]
exebase = filepath.Base(exefull)
}
var buf bytes.Buffer
m.template.Execute(&buf, &templateParams{
Comm: nacl.Name,
ExeBase: exebase,
ExeFull: exefull,
Matches: matches,
})
return true, buf.String()
}
func (m *commMatcher) Match(nacl common.NameAndCmdline) bool {
_, found := m.comms[nacl.Name]
return found
}
func (m *exeMatcher) Match(nacl common.NameAndCmdline) bool {
if len(nacl.Cmdline) == 0 {
return false
}
thisbase := filepath.Base(nacl.Cmdline[0])
fqpath, found := m.exes[thisbase]
if !found {
return false
}
if fqpath == "" {
return true
}
return fqpath == nacl.Cmdline[0]
}
func (m *cmdlineMatcher) Match(nacl common.NameAndCmdline) bool {
for _, regex := range m.regexes {
captures := regex.FindStringSubmatch(strings.Join(nacl.Cmdline, " "))
if m.captures == nil {
return false
}
subexpNames := regex.SubexpNames()
if len(subexpNames) != len(captures) {
return false
}
for i, name := range subexpNames {
m.captures[name] = captures[i]
}
}
return true
}
func (m andMatcher) Match(nacl common.NameAndCmdline) bool {
for _, matcher := range m {
if !matcher.Match(nacl) {
return false
}
}
return true
}
// ReadRecipesFile opens the named file and extracts recipes from it.
func ReadFile(cfgpath string) (*Config, error) {
content, err := ioutil.ReadFile(cfgpath)
if err != nil {
return nil, err
}
return GetConfig(string(content))
}
// GetConfig extracts Config from content by parsing it as YAML.
func GetConfig(content string) (*Config, error) {
var yamldata map[string]interface{}
err := yaml.Unmarshal([]byte(content), &yamldata)
if err != nil {
return nil, err
}
yamlProcnames, ok := yamldata["process_names"]
if !ok {
return nil, fmt.Errorf("error parsing YAML config: no top-level 'process_names' key")
}
procnames, ok := yamlProcnames.([]interface{})
if !ok {
return nil, fmt.Errorf("error parsing YAML config: 'process_names' is not a list")
}
var cfg Config
for i, procname := range procnames {
mn, err := getMatchNamer(procname)
if err != nil {
return nil, fmt.Errorf("unable to parse process_name entry %d: %v", i, err)
}
cfg.MatchNamers = append(cfg.MatchNamers, mn)
}
return &cfg, nil
}
func getMatchNamer(yamlmn interface{}) (common.MatchNamer, error) {
nm, ok := yamlmn.(map[interface{}]interface{})
if !ok {
return nil, fmt.Errorf("not a map")
}
var smap = make(map[string][]string)
var nametmpl string
for k, v := range nm {
key, ok := k.(string)
if !ok {
return nil, fmt.Errorf("non-string key %v", k)
}
if key == "name" {
value, ok := v.(string)
if !ok {
return nil, fmt.Errorf("non-string value %v for key %q", v, key)
}
nametmpl = value
} else {
vals, ok := v.([]interface{})
if !ok {
return nil, fmt.Errorf("non-string array value %v for key %q", v, key)
}
var strs []string
for i, si := range vals {
s, ok := si.(string)
if !ok {
return nil, fmt.Errorf("non-string value %v in list[%d] for key %q", v, i, key)
}
strs = append(strs, s)
}
smap[key] = strs
}
}
var matchers andMatcher
if comm, ok := smap["comm"]; ok {
comms := make(map[string]struct{})
for _, c := range comm {
comms[c] = struct{}{}
}
matchers = append(matchers, &commMatcher{comms})
}
if exe, ok := smap["exe"]; ok {
exes := make(map[string]string)
for _, e := range exe {
if strings.Contains(e, "/") {
exes[filepath.Base(e)] = e
} else {
exes[e] = ""
}
}
matchers = append(matchers, &exeMatcher{exes})
}
if cmdline, ok := smap["cmdline"]; ok {
var rs []*regexp.Regexp
for _, c := range cmdline {
r, err := regexp.Compile(c)
if err != nil {
return nil, fmt.Errorf("bad cmdline regex %q: %v", c, err)
}
rs = append(rs, r)
}
matchers = append(matchers, &cmdlineMatcher{
regexes: rs,
captures: make(map[string]string),
})
}
if len(matchers) == 0 {
return nil, fmt.Errorf("no matchers provided")
}
if nametmpl == "" {
nametmpl = "{{.ExeBase}}"
}
tmpl := template.New("cmdname")
tmpl, err := tmpl.Parse(nametmpl)
if err != nil {
return nil, fmt.Errorf("bad name template %q: %v", nametmpl, err)
}
return &matchNamer{matchers, templateNamer{tmpl}}, nil
}

View file

@ -0,0 +1,77 @@
package config
import (
// "github.com/kylelemons/godebug/pretty"
common "github.com/ncabatoff/process-exporter"
. "gopkg.in/check.v1"
)
func (s MySuite) TestConfigBasic(c *C) {
yml := `
process_names:
- exe:
- bash
- exe:
- sh
- exe:
- /bin/ksh
`
cfg, err := GetConfig(yml)
c.Assert(err, IsNil)
c.Check(cfg.MatchNamers, HasLen, 3)
bash := common.NameAndCmdline{"bash", []string{"/bin/bash"}}
sh := common.NameAndCmdline{"sh", []string{"sh"}}
ksh := common.NameAndCmdline{"ksh", []string{"/bin/ksh"}}
found, name := cfg.MatchNamers[0].MatchAndName(bash)
c.Check(found, Equals, true)
c.Check(name, Equals, "bash")
found, name = cfg.MatchNamers[0].MatchAndName(sh)
c.Check(found, Equals, false)
found, name = cfg.MatchNamers[0].MatchAndName(ksh)
c.Check(found, Equals, false)
found, name = cfg.MatchNamers[1].MatchAndName(bash)
c.Check(found, Equals, false)
found, name = cfg.MatchNamers[1].MatchAndName(sh)
c.Check(found, Equals, true)
c.Check(name, Equals, "sh")
found, name = cfg.MatchNamers[1].MatchAndName(ksh)
c.Check(found, Equals, false)
found, name = cfg.MatchNamers[2].MatchAndName(bash)
c.Check(found, Equals, false)
found, name = cfg.MatchNamers[2].MatchAndName(sh)
c.Check(found, Equals, false)
found, name = cfg.MatchNamers[2].MatchAndName(ksh)
c.Check(found, Equals, true)
c.Check(name, Equals, "ksh")
}
func (s MySuite) TestConfigTemplates(c *C) {
yml := `
process_names:
- exe:
- postmaster
cmdline:
- "-D\\s+.+?(?P<Path>[^/]+)(?:$|\\s)"
name: "{{.ExeBase}}:{{.Matches.Path}}"
- exe:
- prometheus
name: "{{.ExeFull}}"
`
cfg, err := GetConfig(yml)
c.Assert(err, IsNil)
c.Check(cfg.MatchNamers, HasLen, 2)
postgres := common.NameAndCmdline{"postmaster", []string{"/usr/bin/postmaster", "-D", "/data/pg"}}
found, name := cfg.MatchNamers[0].MatchAndName(postgres)
c.Check(found, Equals, true)
c.Check(name, Equals, "postmaster:pg")
pm := common.NameAndCmdline{"prometheus", []string{"/usr/local/bin/prometheus"}}
found, name = cfg.MatchNamers[1].MatchAndName(pm)
c.Check(found, Equals, true)
c.Check(name, Equals, "/usr/local/bin/prometheus")
}

View file

@ -0,0 +1,26 @@
package proc
import (
. "gopkg.in/check.v1"
"testing"
)
// Hook up gocheck into the "go test" runner.
func Test(t *testing.T) { TestingT(t) }
type MySuite struct{}
var _ = Suite(&MySuite{})
// read everything in the iterator
func consumeIter(pi ProcIter) ([]ProcIdInfo, error) {
infos := []ProcIdInfo{}
for pi.Next() {
info, err := Info(pi)
if err != nil {
return nil, err
}
infos = append(infos, info)
}
return infos, nil
}

View file

@ -0,0 +1,267 @@
package proc
import (
"github.com/kylelemons/godebug/pretty"
common "github.com/ncabatoff/process-exporter"
. "gopkg.in/check.v1"
"time"
)
type namer map[string]struct{}
func newNamer(names ...string) namer {
nr := make(namer, len(names))
for _, name := range names {
nr[name] = struct{}{}
}
return nr
}
func (n namer) MatchAndName(nacl common.NameAndCmdline) (bool, string) {
if _, ok := n[nacl.Name]; ok {
return true, nacl.Name
}
return false, ""
}
// Test core group() functionality, i.e things not related to namers or parents
// or processes that have exited.
func (s MySuite) TestGrouperBasic(c *C) {
newProc := func(pid int, name string, m ProcMetrics) ProcIdInfo {
pis := newProcIdStatic(pid, 0, 0, name, nil)
return ProcIdInfo{
ProcId: pis.ProcId,
ProcStatic: pis.ProcStatic,
ProcMetrics: m,
}
}
gr := NewGrouper(false, newNamer("g1", "g2"))
p1 := newProc(1, "g1", ProcMetrics{1, 2, 3, 4, 5})
p2 := newProc(2, "g2", ProcMetrics{2, 3, 4, 5, 6})
p3 := newProc(3, "g3", ProcMetrics{})
_, err := gr.Update(procInfoIter(p1, p2, p3))
c.Assert(err, IsNil)
got1 := gr.groups()
want1 := GroupCountMap{
"g1": GroupCounts{Counts{0, 0, 0}, 1, 4, 5, time.Time{}},
"g2": GroupCounts{Counts{0, 0, 0}, 1, 5, 6, time.Time{}},
}
c.Check(got1, DeepEquals, want1)
// Now increment counts and memory and make sure group counts updated.
p1.ProcMetrics = ProcMetrics{2, 3, 4, 5, 6}
p2.ProcMetrics = ProcMetrics{4, 5, 6, 7, 8}
_, err = gr.Update(procInfoIter(p1, p2, p3))
c.Assert(err, IsNil)
got2 := gr.groups()
want2 := GroupCountMap{
"g1": GroupCounts{Counts{1, 1, 1}, 1, 5, 6, time.Time{}},
"g2": GroupCounts{Counts{2, 2, 2}, 1, 7, 8, time.Time{}},
}
c.Check(got2, DeepEquals, want2)
// Now add a new proc and update p2's metrics. The
// counts for p4 won't be factored into the total yet
// because we only add to counts starting with the
// second time we see a proc. Memory is affected though.
p4 := newProc(4, "g2", ProcMetrics{1, 1, 1, 1, 1})
p2.ProcMetrics = ProcMetrics{5, 6, 7, 8, 9}
_, err = gr.Update(procInfoIter(p1, p2, p3, p4))
c.Assert(err, IsNil)
got3 := gr.groups()
want3 := GroupCountMap{
"g1": GroupCounts{Counts{0, 0, 0}, 1, 5, 6, time.Time{}},
"g2": GroupCounts{Counts{1, 1, 1}, 2, 9, 10, time.Time{}},
}
c.Check(got3, DeepEquals, want3, Commentf("diff %s", pretty.Compare(got3, want3)))
p4.ProcMetrics = ProcMetrics{2, 2, 2, 2, 2}
p2.ProcMetrics = ProcMetrics{6, 7, 8, 8, 9}
_, err = gr.Update(procInfoIter(p1, p2, p3, p4))
c.Assert(err, IsNil)
got4 := gr.groups()
want4 := GroupCountMap{
"g1": GroupCounts{Counts{0, 0, 0}, 1, 5, 6, time.Time{}},
"g2": GroupCounts{Counts{2, 2, 2}, 2, 10, 11, time.Time{}},
}
c.Check(got4, DeepEquals, want4, Commentf("diff %s", pretty.Compare(got4, want4)))
}
// Test that if a proc is tracked, we track its descendants, and if not as
// before it gets ignored. We won't bother testing metric accumulation since
// that should be covered by TestGrouperBasic.
func (s MySuite) TestGrouperParents(c *C) {
newProc := func(pid, ppid int, name string) ProcIdInfo {
pis := newProcIdStatic(pid, ppid, 0, name, nil)
return ProcIdInfo{
ProcId: pis.ProcId,
ProcStatic: pis.ProcStatic,
ProcMetrics: ProcMetrics{},
}
}
gr := NewGrouper(true, newNamer("g1", "g2"))
p1 := newProc(1, 0, "g1")
p2 := newProc(2, 0, "g2")
p3 := newProc(3, 0, "g3")
_, err := gr.Update(procInfoIter(p1, p2, p3))
c.Assert(err, IsNil)
got1 := gr.groups()
want1 := GroupCountMap{
"g1": GroupCounts{Counts{}, 1, 0, 0, time.Time{}},
"g2": GroupCounts{Counts{}, 1, 0, 0, time.Time{}},
}
c.Check(got1, DeepEquals, want1, Commentf("diff %s", pretty.Compare(got1, want1)))
// Now we'll give each of the procs a child and test that the count of procs
// in each group is incremented.
p4 := newProc(4, p1.Pid, "")
p5 := newProc(5, p2.Pid, "")
p6 := newProc(6, p3.Pid, "")
_, err = gr.Update(procInfoIter(p1, p2, p3, p4, p5, p6))
c.Assert(err, IsNil)
got2 := gr.groups()
want2 := GroupCountMap{
"g1": GroupCounts{Counts{}, 2, 0, 0, time.Time{}},
"g2": GroupCounts{Counts{}, 2, 0, 0, time.Time{}},
}
c.Check(got2, DeepEquals, want2, Commentf("diff %s", pretty.Compare(got2, want2)))
// Now we'll let p4 die, and give p5 a child and grandchild and great-grandchild.
p7 := newProc(7, p5.Pid, "")
p8 := newProc(8, p7.Pid, "")
p9 := newProc(9, p8.Pid, "")
_, err = gr.Update(procInfoIter(p1, p2, p3, p5, p6, p7, p8, p9))
c.Assert(err, IsNil)
got3 := gr.groups()
want3 := GroupCountMap{
"g1": GroupCounts{Counts{}, 1, 0, 0, time.Time{}},
"g2": GroupCounts{Counts{}, 5, 0, 0, time.Time{}},
}
c.Check(got3, DeepEquals, want3, Commentf("diff %s", pretty.Compare(got3, want3)))
}
// Test that Groups() reports on new CPU/IO activity, even if some processes in the
// group have gone away.
func (s MySuite) TestGrouperGroup(c *C) {
newProc := func(pid int, name string, m ProcMetrics) ProcIdInfo {
pis := newProcIdStatic(pid, 0, 0, name, nil)
return ProcIdInfo{
ProcId: pis.ProcId,
ProcStatic: pis.ProcStatic,
ProcMetrics: m,
}
}
gr := NewGrouper(false, newNamer("g1"))
// First call should return zero CPU/IO.
p1 := newProc(1, "g1", ProcMetrics{1, 2, 3, 4, 5})
_, err := gr.Update(procInfoIter(p1))
c.Assert(err, IsNil)
got1 := gr.Groups()
want1 := GroupCountMap{
"g1": GroupCounts{Counts{0, 0, 0}, 1, 4, 5, time.Time{}},
}
c.Check(got1, DeepEquals, want1)
// Second call should return the delta CPU/IO from first observance,
// as well as latest memory/proccount.
p1.ProcMetrics = ProcMetrics{2, 3, 4, 5, 6}
_, err = gr.Update(procInfoIter(p1))
c.Assert(err, IsNil)
got2 := gr.Groups()
want2 := GroupCountMap{
"g1": GroupCounts{Counts{1, 1, 1}, 1, 5, 6, time.Time{}},
}
c.Check(got2, DeepEquals, want2)
// Third call: process hasn't changed, nor should our group stats.
_, err = gr.Update(procInfoIter(p1))
c.Assert(err, IsNil)
got3 := gr.Groups()
want3 := GroupCountMap{
"g1": GroupCounts{Counts{1, 1, 1}, 1, 5, 6, time.Time{}},
}
c.Check(got3, DeepEquals, want3, Commentf("diff %s", pretty.Compare(got3, want3)))
}
// Test that Groups() reports on new CPU/IO activity, even if some processes in the
// group have gone away.
func (s MySuite) TestGrouperNonDecreasing(c *C) {
newProc := func(pid int, name string, m ProcMetrics) ProcIdInfo {
pis := newProcIdStatic(pid, 0, 0, name, nil)
return ProcIdInfo{
ProcId: pis.ProcId,
ProcStatic: pis.ProcStatic,
ProcMetrics: m,
}
}
gr := NewGrouper(false, newNamer("g1", "g2"))
p1 := newProc(1, "g1", ProcMetrics{1, 2, 3, 4, 5})
p2 := newProc(2, "g2", ProcMetrics{2, 3, 4, 5, 6})
_, err := gr.Update(procInfoIter(p1, p2))
c.Assert(err, IsNil)
got1 := gr.Groups()
want1 := GroupCountMap{
"g1": GroupCounts{Counts{0, 0, 0}, 1, 4, 5, time.Time{}},
"g2": GroupCounts{Counts{0, 0, 0}, 1, 5, 6, time.Time{}},
}
c.Check(got1, DeepEquals, want1)
// Now add a new proc p3 to g2, and increment p1/p2's metrics.
p1.ProcMetrics = ProcMetrics{2, 3, 4, 5, 6}
p2.ProcMetrics = ProcMetrics{4, 5, 6, 7, 8}
p3 := newProc(3, "g2", ProcMetrics{1, 1, 1, 1, 1})
_, err = gr.Update(procInfoIter(p1, p2, p3))
c.Assert(err, IsNil)
got2 := gr.Groups()
want2 := GroupCountMap{
"g1": GroupCounts{Counts{1, 1, 1}, 1, 5, 6, time.Time{}},
"g2": GroupCounts{Counts{2, 2, 2}, 2, 8, 9, time.Time{}},
}
c.Check(got2, DeepEquals, want2)
// Now update p3's metrics and kill p2.
p3.ProcMetrics = ProcMetrics{2, 3, 4, 5, 6}
_, err = gr.Update(procInfoIter(p1, p3))
c.Assert(err, IsNil)
got3 := gr.Groups()
want3 := GroupCountMap{
"g1": GroupCounts{Counts{1, 1, 1}, 1, 5, 6, time.Time{}},
"g2": GroupCounts{Counts{3, 4, 5}, 1, 5, 6, time.Time{}},
}
c.Check(got3, DeepEquals, want3, Commentf("diff %s", pretty.Compare(got3, want3)))
// Now update p3's metrics and kill p1.
p3.ProcMetrics = ProcMetrics{4, 4, 4, 2, 1}
_, err = gr.Update(procInfoIter(p3))
c.Assert(err, IsNil)
got4 := gr.Groups()
want4 := GroupCountMap{
"g1": GroupCounts{Counts{1, 1, 1}, 0, 0, 0, time.Time{}},
"g2": GroupCounts{Counts{5, 5, 5}, 1, 2, 1, time.Time{}},
}
c.Check(got4, DeepEquals, want4, Commentf("diff %s\n%s", pretty.Compare(got4, want4), pretty.Sprint(gr)))
}

View file

@ -0,0 +1,120 @@
package proc
import (
. "gopkg.in/check.v1"
"os"
"os/exec"
)
var fs *FS
func init() {
fs, _ = NewFS("/proc")
}
// Basic test of proc reading: does AllProcs return at least two procs, one of which is us.
func (s MySuite) TestAllProcs(c *C) {
procs := fs.AllProcs()
count := 0
for procs.Next() {
count++
if procs.GetPid() != os.Getpid() {
continue
}
procid, err := procs.GetProcId()
c.Assert(err, IsNil)
c.Check(procid.Pid, Equals, os.Getpid())
static, err := procs.GetStatic()
c.Assert(err, IsNil)
c.Check(static.ParentPid, Equals, os.Getppid())
}
err := procs.Close()
c.Assert(err, IsNil)
c.Check(count, Not(Equals), 0)
}
// Test that we can observe the absence of a child process before it spawns and after it exits,
// and its presence during its lifetime.
func (s MySuite) TestAllProcsSpawn(c *C) {
childprocs := func() ([]ProcIdStatic, error) {
found := []ProcIdStatic{}
procs := fs.AllProcs()
mypid := os.Getpid()
for procs.Next() {
procid, err := procs.GetProcId()
if err != nil {
continue
}
static, err := procs.GetStatic()
if err != nil {
continue
}
if static.ParentPid == mypid {
found = append(found, ProcIdStatic{procid, static})
}
}
err := procs.Close()
if err != nil {
return nil, err
}
return found, nil
}
children1, err := childprocs()
c.Assert(err, IsNil)
cmd := exec.Command("/bin/cat")
wc, err := cmd.StdinPipe()
c.Assert(err, IsNil)
err = cmd.Start()
c.Assert(err, IsNil)
children2, err := childprocs()
c.Assert(err, IsNil)
err = wc.Close()
c.Assert(err, IsNil)
err = cmd.Wait()
c.Assert(err, IsNil)
children3, err := childprocs()
c.Assert(err, IsNil)
foundcat := func(procs []ProcIdStatic) bool {
for _, proc := range procs {
if proc.Name == "cat" {
return true
}
}
return false
}
c.Check(foundcat(children1), Equals, false)
c.Check(foundcat(children2), Equals, true)
c.Check(foundcat(children3), Equals, false)
}
func (s MySuite) TestIterator(c *C) {
// create a new proc with zero metrics, cmdline, starttime, ppid
newProc := func(pid int, name string) ProcIdInfo {
pis := newProcIdStatic(pid, 0, 0, name, nil)
return ProcIdInfo{
ProcId: pis.ProcId,
ProcStatic: pis.ProcStatic,
ProcMetrics: ProcMetrics{},
}
}
p1 := newProc(1, "p1")
want1 := []ProcIdInfo{p1}
pi1 := procInfoIter(want1...)
got, err := consumeIter(pi1)
c.Assert(err, IsNil)
c.Check(got, DeepEquals, want1)
p2 := newProc(2, "p2")
want2 := []ProcIdInfo{p1, p2}
pi2 := procInfoIter(want2...)
got2, err := consumeIter(pi2)
c.Assert(err, IsNil)
c.Check(got2, DeepEquals, want2)
}

View file

@ -0,0 +1,94 @@
package proc
import (
. "gopkg.in/check.v1"
)
// Verify that the tracker accurately reports new procs that aren't ignored or tracked.
func (s MySuite) TestTrackerBasic(c *C) {
// create a new proc with zero metrics, cmdline, starttime, ppid
newProc := func(pid int, startTime uint64, name string) ProcIdInfo {
pis := newProcIdStatic(pid, 0, 0, name, nil)
return ProcIdInfo{
ProcId: pis.ProcId,
ProcStatic: pis.ProcStatic,
ProcMetrics: ProcMetrics{},
}
}
tr := NewTracker()
// Test that p1 is seen as new
p1 := newProc(1, 1, "p1")
want1 := []ProcIdInfo{p1}
got1, _, err := tr.Update(procInfoIter(want1...))
c.Assert(err, IsNil)
c.Check(got1, DeepEquals, want1)
// Test that p1 is no longer seen as new once tracked
tr.Track("g1", p1)
got2, _, err := tr.Update(procInfoIter(want1...))
c.Assert(err, IsNil)
c.Check(got2, DeepEquals, []ProcIdInfo(nil))
// Test that p2 is new now, but p1 is still not
p2 := newProc(2, 2, "p2")
want2 := []ProcIdInfo{p2}
got3, _, err := tr.Update(procInfoIter(p1, p2))
c.Assert(err, IsNil)
c.Check(got3, DeepEquals, want2)
// Test that p2 stops being new once ignored
tr.Ignore(p2.ProcId)
got4, _, err := tr.Update(procInfoIter(p1, p2))
c.Assert(err, IsNil)
c.Check(got4, DeepEquals, []ProcIdInfo(nil))
// TODO test that starttime is taken into account, i.e. pid recycling is handled.
}
// Verify that the tracker accurately reports metric changes.
func (s MySuite) TestTrackerCounts(c *C) {
// create a new proc with cmdline, starttime, ppid
newProc := func(pid int, startTime uint64, name string, m ProcMetrics) ProcIdInfo {
pis := newProcIdStatic(pid, 0, 0, name, nil)
return ProcIdInfo{
ProcId: pis.ProcId,
ProcStatic: pis.ProcStatic,
ProcMetrics: m,
}
}
tr := NewTracker()
// Test that p1 is seen as new
p1 := newProc(1, 1, "p1", ProcMetrics{1, 2, 3, 4, 5})
want1 := []ProcIdInfo{p1}
got1, _, err := tr.Update(procInfoIter(p1))
c.Assert(err, IsNil)
c.Check(got1, DeepEquals, want1)
// Test that p1 is no longer seen as new once tracked
tr.Track("g1", p1)
got2, _, err := tr.Update(procInfoIter(p1))
c.Assert(err, IsNil)
c.Check(got2, DeepEquals, []ProcIdInfo(nil))
// Now update p1's metrics
p1.ProcMetrics = ProcMetrics{2, 3, 4, 5, 6}
got3, _, err := tr.Update(procInfoIter(p1))
c.Assert(err, IsNil)
c.Check(got3, DeepEquals, []ProcIdInfo(nil))
// Test that counts are correct
c.Check(tr.Tracked[p1.ProcId].accum, Equals, Counts{1, 1, 1})
c.Check(tr.Tracked[p1.ProcId].info, DeepEquals, ProcInfo{p1.ProcStatic, p1.ProcMetrics})
// Now update p1's metrics again
p1.ProcMetrics = ProcMetrics{4, 6, 8, 9, 10}
got4, _, err := tr.Update(procInfoIter(p1))
c.Assert(err, IsNil)
c.Check(got4, DeepEquals, []ProcIdInfo(nil))
// Test that counts are correct
c.Check(tr.Tracked[p1.ProcId].accum, Equals, Counts{3, 4, 5})
c.Check(tr.Tracked[p1.ProcId].info, DeepEquals, ProcInfo{p1.ProcStatic, p1.ProcMetrics})
}