397 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			397 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			Go
		
	
	
	
| package procstat
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"io/ioutil"
 | |
| 	"os"
 | |
| 	"os/exec"
 | |
| 	"path/filepath"
 | |
| 	"runtime"
 | |
| 	"strings"
 | |
| 	"testing"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/influxdata/telegraf/testutil"
 | |
| 	"github.com/shirou/gopsutil/cpu"
 | |
| 	"github.com/shirou/gopsutil/process"
 | |
| 	"github.com/stretchr/testify/assert"
 | |
| 	"github.com/stretchr/testify/require"
 | |
| )
 | |
| 
 | |
| func init() {
 | |
| 	execCommand = mockExecCommand
 | |
| }
 | |
| func mockExecCommand(arg0 string, args ...string) *exec.Cmd {
 | |
| 	args = append([]string{"-test.run=TestMockExecCommand", "--", arg0}, args...)
 | |
| 	cmd := exec.Command(os.Args[0], args...)
 | |
| 	cmd.Stderr = os.Stderr
 | |
| 	return cmd
 | |
| }
 | |
| func TestMockExecCommand(t *testing.T) {
 | |
| 	var cmd []string
 | |
| 	for _, arg := range os.Args {
 | |
| 		if string(arg) == "--" {
 | |
| 			cmd = []string{}
 | |
| 			continue
 | |
| 		}
 | |
| 		if cmd == nil {
 | |
| 			continue
 | |
| 		}
 | |
| 		cmd = append(cmd, string(arg))
 | |
| 	}
 | |
| 	if cmd == nil {
 | |
| 		return
 | |
| 	}
 | |
| 	cmdline := strings.Join(cmd, " ")
 | |
| 
 | |
| 	if cmdline == "systemctl show TestGather_systemdUnitPIDs" {
 | |
| 		fmt.Printf(`PIDFile=
 | |
| GuessMainPID=yes
 | |
| MainPID=11408
 | |
| ControlPID=0
 | |
| ExecMainPID=11408
 | |
| `)
 | |
| 		os.Exit(0)
 | |
| 	}
 | |
| 
 | |
| 	fmt.Printf("command not found\n")
 | |
| 	os.Exit(1)
 | |
| }
 | |
| 
 | |
| type testPgrep struct {
 | |
| 	pids []PID
 | |
| 	err  error
 | |
| }
 | |
| 
 | |
| func pidFinder(pids []PID, err error) func() (PIDFinder, error) {
 | |
| 	return func() (PIDFinder, error) {
 | |
| 		return &testPgrep{
 | |
| 			pids: pids,
 | |
| 			err:  err,
 | |
| 		}, nil
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (pg *testPgrep) PidFile(path string) ([]PID, error) {
 | |
| 	return pg.pids, pg.err
 | |
| }
 | |
| 
 | |
| func (p *testProc) Cmdline() (string, error) {
 | |
| 	return "test_proc", nil
 | |
| }
 | |
| 
 | |
| func (pg *testPgrep) Pattern(pattern string) ([]PID, error) {
 | |
| 	return pg.pids, pg.err
 | |
| }
 | |
| 
 | |
| func (pg *testPgrep) Uid(user string) ([]PID, error) {
 | |
| 	return pg.pids, pg.err
 | |
| }
 | |
| 
 | |
| func (pg *testPgrep) FullPattern(pattern string) ([]PID, error) {
 | |
| 	return pg.pids, pg.err
 | |
| }
 | |
| 
 | |
| type testProc struct {
 | |
| 	pid  PID
 | |
| 	tags map[string]string
 | |
| }
 | |
| 
 | |
| func newTestProc(pid PID) (Process, error) {
 | |
| 	proc := &testProc{
 | |
| 		tags: make(map[string]string),
 | |
| 	}
 | |
| 	return proc, nil
 | |
| }
 | |
| 
 | |
| func (p *testProc) PID() PID {
 | |
| 	return p.pid
 | |
| }
 | |
| 
 | |
| func (p *testProc) Username() (string, error) {
 | |
| 	return "testuser", nil
 | |
| }
 | |
| 
 | |
| func (p *testProc) Tags() map[string]string {
 | |
| 	return p.tags
 | |
| }
 | |
| 
 | |
| func (p *testProc) PageFaults() (*process.PageFaultsStat, error) {
 | |
| 	return &process.PageFaultsStat{}, nil
 | |
| }
 | |
| 
 | |
| func (p *testProc) IOCounters() (*process.IOCountersStat, error) {
 | |
| 	return &process.IOCountersStat{}, nil
 | |
| }
 | |
| 
 | |
| func (p *testProc) MemoryInfo() (*process.MemoryInfoStat, error) {
 | |
| 	return &process.MemoryInfoStat{}, nil
 | |
| }
 | |
| 
 | |
| func (p *testProc) Name() (string, error) {
 | |
| 	return "test_proc", nil
 | |
| }
 | |
| 
 | |
| func (p *testProc) NumCtxSwitches() (*process.NumCtxSwitchesStat, error) {
 | |
| 	return &process.NumCtxSwitchesStat{}, nil
 | |
| }
 | |
| 
 | |
| func (p *testProc) NumFDs() (int32, error) {
 | |
| 	return 0, nil
 | |
| }
 | |
| 
 | |
| func (p *testProc) NumThreads() (int32, error) {
 | |
| 	return 0, nil
 | |
| }
 | |
| 
 | |
| func (p *testProc) Percent(interval time.Duration) (float64, error) {
 | |
| 	return 0, nil
 | |
| }
 | |
| 
 | |
| func (p *testProc) Times() (*cpu.TimesStat, error) {
 | |
| 	return &cpu.TimesStat{}, nil
 | |
| }
 | |
| 
 | |
| func (p *testProc) RlimitUsage(gatherUsage bool) ([]process.RlimitStat, error) {
 | |
| 	return []process.RlimitStat{}, nil
 | |
| }
 | |
| 
 | |
| var pid PID = PID(42)
 | |
| var exe string = "foo"
 | |
| 
 | |
| func TestGather_CreateProcessErrorOk(t *testing.T) {
 | |
| 	var acc testutil.Accumulator
 | |
| 
 | |
| 	p := Procstat{
 | |
| 		Exe:             exe,
 | |
| 		createPIDFinder: pidFinder([]PID{pid}, nil),
 | |
| 		createProcess: func(PID) (Process, error) {
 | |
| 			return nil, fmt.Errorf("createProcess error")
 | |
| 		},
 | |
| 	}
 | |
| 	require.NoError(t, acc.GatherError(p.Gather))
 | |
| }
 | |
| 
 | |
| func TestGather_CreatePIDFinderError(t *testing.T) {
 | |
| 	var acc testutil.Accumulator
 | |
| 
 | |
| 	p := Procstat{
 | |
| 		createPIDFinder: func() (PIDFinder, error) {
 | |
| 			return nil, fmt.Errorf("createPIDFinder error")
 | |
| 		},
 | |
| 		createProcess: newTestProc,
 | |
| 	}
 | |
| 	require.Error(t, acc.GatherError(p.Gather))
 | |
| }
 | |
| 
 | |
| func TestGather_ProcessName(t *testing.T) {
 | |
| 	var acc testutil.Accumulator
 | |
| 
 | |
| 	p := Procstat{
 | |
| 		Exe:             exe,
 | |
| 		ProcessName:     "custom_name",
 | |
| 		createPIDFinder: pidFinder([]PID{pid}, nil),
 | |
| 		createProcess:   newTestProc,
 | |
| 	}
 | |
| 	require.NoError(t, acc.GatherError(p.Gather))
 | |
| 
 | |
| 	assert.Equal(t, "custom_name", acc.TagValue("procstat", "process_name"))
 | |
| }
 | |
| 
 | |
| func TestGather_NoProcessNameUsesReal(t *testing.T) {
 | |
| 	var acc testutil.Accumulator
 | |
| 	pid := PID(os.Getpid())
 | |
| 
 | |
| 	p := Procstat{
 | |
| 		Exe:             exe,
 | |
| 		createPIDFinder: pidFinder([]PID{pid}, nil),
 | |
| 		createProcess:   newTestProc,
 | |
| 	}
 | |
| 	require.NoError(t, acc.GatherError(p.Gather))
 | |
| 
 | |
| 	assert.True(t, acc.HasTag("procstat", "process_name"))
 | |
| }
 | |
| 
 | |
| func TestGather_NoPidTag(t *testing.T) {
 | |
| 	var acc testutil.Accumulator
 | |
| 
 | |
| 	p := Procstat{
 | |
| 		Exe:             exe,
 | |
| 		createPIDFinder: pidFinder([]PID{pid}, nil),
 | |
| 		createProcess:   newTestProc,
 | |
| 	}
 | |
| 	require.NoError(t, acc.GatherError(p.Gather))
 | |
| 	assert.True(t, acc.HasInt32Field("procstat", "pid"))
 | |
| 	assert.False(t, acc.HasTag("procstat", "pid"))
 | |
| }
 | |
| 
 | |
| func TestGather_PidTag(t *testing.T) {
 | |
| 	var acc testutil.Accumulator
 | |
| 
 | |
| 	p := Procstat{
 | |
| 		Exe:             exe,
 | |
| 		PidTag:          true,
 | |
| 		createPIDFinder: pidFinder([]PID{pid}, nil),
 | |
| 		createProcess:   newTestProc,
 | |
| 	}
 | |
| 	require.NoError(t, acc.GatherError(p.Gather))
 | |
| 	assert.Equal(t, "42", acc.TagValue("procstat", "pid"))
 | |
| 	assert.False(t, acc.HasInt32Field("procstat", "pid"))
 | |
| }
 | |
| 
 | |
| func TestGather_Prefix(t *testing.T) {
 | |
| 	var acc testutil.Accumulator
 | |
| 
 | |
| 	p := Procstat{
 | |
| 		Exe:             exe,
 | |
| 		Prefix:          "custom_prefix",
 | |
| 		createPIDFinder: pidFinder([]PID{pid}, nil),
 | |
| 		createProcess:   newTestProc,
 | |
| 	}
 | |
| 	require.NoError(t, acc.GatherError(p.Gather))
 | |
| 	assert.True(t, acc.HasInt32Field("procstat", "custom_prefix_num_fds"))
 | |
| }
 | |
| 
 | |
| func TestGather_Exe(t *testing.T) {
 | |
| 	var acc testutil.Accumulator
 | |
| 
 | |
| 	p := Procstat{
 | |
| 		Exe:             exe,
 | |
| 		createPIDFinder: pidFinder([]PID{pid}, nil),
 | |
| 		createProcess:   newTestProc,
 | |
| 	}
 | |
| 	require.NoError(t, acc.GatherError(p.Gather))
 | |
| 
 | |
| 	assert.Equal(t, exe, acc.TagValue("procstat", "exe"))
 | |
| }
 | |
| 
 | |
| func TestGather_User(t *testing.T) {
 | |
| 	var acc testutil.Accumulator
 | |
| 	user := "ada"
 | |
| 
 | |
| 	p := Procstat{
 | |
| 		User:            user,
 | |
| 		createPIDFinder: pidFinder([]PID{pid}, nil),
 | |
| 		createProcess:   newTestProc,
 | |
| 	}
 | |
| 	require.NoError(t, acc.GatherError(p.Gather))
 | |
| 
 | |
| 	assert.Equal(t, user, acc.TagValue("procstat", "user"))
 | |
| }
 | |
| 
 | |
| func TestGather_Pattern(t *testing.T) {
 | |
| 	var acc testutil.Accumulator
 | |
| 	pattern := "foo"
 | |
| 
 | |
| 	p := Procstat{
 | |
| 		Pattern:         pattern,
 | |
| 		createPIDFinder: pidFinder([]PID{pid}, nil),
 | |
| 		createProcess:   newTestProc,
 | |
| 	}
 | |
| 	require.NoError(t, acc.GatherError(p.Gather))
 | |
| 
 | |
| 	assert.Equal(t, pattern, acc.TagValue("procstat", "pattern"))
 | |
| }
 | |
| 
 | |
| func TestGather_MissingPidMethod(t *testing.T) {
 | |
| 	var acc testutil.Accumulator
 | |
| 
 | |
| 	p := Procstat{
 | |
| 		createPIDFinder: pidFinder([]PID{pid}, nil),
 | |
| 		createProcess:   newTestProc,
 | |
| 	}
 | |
| 	require.Error(t, acc.GatherError(p.Gather))
 | |
| }
 | |
| 
 | |
| func TestGather_PidFile(t *testing.T) {
 | |
| 	var acc testutil.Accumulator
 | |
| 	pidfile := "/path/to/pidfile"
 | |
| 
 | |
| 	p := Procstat{
 | |
| 		PidFile:         pidfile,
 | |
| 		createPIDFinder: pidFinder([]PID{pid}, nil),
 | |
| 		createProcess:   newTestProc,
 | |
| 	}
 | |
| 	require.NoError(t, acc.GatherError(p.Gather))
 | |
| 
 | |
| 	assert.Equal(t, pidfile, acc.TagValue("procstat", "pidfile"))
 | |
| }
 | |
| 
 | |
| func TestGather_PercentFirstPass(t *testing.T) {
 | |
| 	var acc testutil.Accumulator
 | |
| 	pid := PID(os.Getpid())
 | |
| 
 | |
| 	p := Procstat{
 | |
| 		Pattern:         "foo",
 | |
| 		PidTag:          true,
 | |
| 		createPIDFinder: pidFinder([]PID{pid}, nil),
 | |
| 		createProcess:   NewProc,
 | |
| 	}
 | |
| 	require.NoError(t, acc.GatherError(p.Gather))
 | |
| 
 | |
| 	assert.True(t, acc.HasFloatField("procstat", "cpu_time_user"))
 | |
| 	assert.False(t, acc.HasFloatField("procstat", "cpu_usage"))
 | |
| }
 | |
| 
 | |
| func TestGather_PercentSecondPass(t *testing.T) {
 | |
| 	var acc testutil.Accumulator
 | |
| 	pid := PID(os.Getpid())
 | |
| 
 | |
| 	p := Procstat{
 | |
| 		Pattern:         "foo",
 | |
| 		PidTag:          true,
 | |
| 		createPIDFinder: pidFinder([]PID{pid}, nil),
 | |
| 		createProcess:   NewProc,
 | |
| 	}
 | |
| 	require.NoError(t, acc.GatherError(p.Gather))
 | |
| 	require.NoError(t, acc.GatherError(p.Gather))
 | |
| 
 | |
| 	assert.True(t, acc.HasFloatField("procstat", "cpu_time_user"))
 | |
| 	assert.True(t, acc.HasFloatField("procstat", "cpu_usage"))
 | |
| }
 | |
| 
 | |
| func TestGather_systemdUnitPIDs(t *testing.T) {
 | |
| 	p := Procstat{
 | |
| 		createPIDFinder: pidFinder([]PID{}, nil),
 | |
| 		SystemdUnit:     "TestGather_systemdUnitPIDs",
 | |
| 	}
 | |
| 	var acc testutil.Accumulator
 | |
| 	pids, tags, err := p.findPids(&acc)
 | |
| 	require.NoError(t, err)
 | |
| 	assert.Equal(t, []PID{11408}, pids)
 | |
| 	assert.Equal(t, "TestGather_systemdUnitPIDs", tags["systemd_unit"])
 | |
| }
 | |
| 
 | |
| func TestGather_cgroupPIDs(t *testing.T) {
 | |
| 	//no cgroups in windows
 | |
| 	if runtime.GOOS == "windows" {
 | |
| 		t.Skip("no cgroups in windows")
 | |
| 	}
 | |
| 	td, err := ioutil.TempDir("", "")
 | |
| 	require.NoError(t, err)
 | |
| 	defer os.RemoveAll(td)
 | |
| 	err = ioutil.WriteFile(filepath.Join(td, "cgroup.procs"), []byte("1234\n5678\n"), 0644)
 | |
| 	require.NoError(t, err)
 | |
| 
 | |
| 	p := Procstat{
 | |
| 		createPIDFinder: pidFinder([]PID{}, nil),
 | |
| 		CGroup:          td,
 | |
| 	}
 | |
| 	var acc testutil.Accumulator
 | |
| 	pids, tags, err := p.findPids(&acc)
 | |
| 	require.NoError(t, err)
 | |
| 	assert.Equal(t, []PID{1234, 5678}, pids)
 | |
| 	assert.Equal(t, td, tags["cgroup"])
 | |
| }
 | |
| 
 | |
| func TestProcstatLookupMetric(t *testing.T) {
 | |
| 	p := Procstat{
 | |
| 		createPIDFinder: pidFinder([]PID{543}, nil),
 | |
| 		Exe:             "-Gsys",
 | |
| 	}
 | |
| 	var acc testutil.Accumulator
 | |
| 	err := acc.GatherError(p.Gather)
 | |
| 	require.NoError(t, err)
 | |
| 	require.Equal(t, len(p.procs)+1, len(acc.Metrics))
 | |
| }
 |