Skip to content

Commit

Permalink
Remove cgo and use 'getent passwd' to lookup user names
Browse files Browse the repository at this point in the history
  • Loading branch information
treydock committed May 17, 2024
1 parent 28e9388 commit b1b7278
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 8 deletions.
2 changes: 1 addition & 1 deletion .promu.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
go:
version: 1.20
cgo: true
cgo: false
repository:
path: github.com/treydock/cgroup_exporter
build:
Expand Down
9 changes: 4 additions & 5 deletions collector/cgroupv1.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ package collector

import (
"fmt"
"os/user"
"path/filepath"
"regexp"
"strings"
Expand Down Expand Up @@ -45,11 +44,11 @@ func getInfov1(name string, metric *CgroupMetric, logger log.Logger) {
if len(userSliceMatch) == 2 {
metric.userslice = true
metric.uid = userSliceMatch[1]
user, err := user.LookupId(metric.uid)
user, err := getentPasswd(metric.uid)
if err != nil {
level.Error(logger).Log("msg", "Error looking up user slice uid", "uid", metric.uid, "err", err)
} else {
metric.username = user.Username
metric.username = user
}
return
}
Expand All @@ -59,11 +58,11 @@ func getInfov1(name string, metric *CgroupMetric, logger log.Logger) {
metric.job = true
metric.uid = slurmMatch[1]
metric.jobid = slurmMatch[2]
user, err := user.LookupId(metric.uid)
user, err := getentPasswd(metric.uid)
if err != nil {
level.Error(logger).Log("msg", "Error looking up slurm uid", "uid", metric.uid, "err", err)
} else {
metric.username = user.Username
metric.username = user
}
return
}
Expand Down
4 changes: 2 additions & 2 deletions collector/cgroupv2.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,12 @@ func getInfov2(name string, pids []int, metric *CgroupMetric, logger log.Logger)
// effective UID
uid := procStat.UIDs[1]
metric.uid = uid
user, err := user.LookupId(metric.uid)
user, err := getentPasswd(metric.uid)
if err != nil {
level.Error(logger).Log("msg", "Error looking up slurm uid", "uid", metric.uid, "err", err)
return
}
metric.username = user.Username
metric.username = user
return
}
}
Expand Down
22 changes: 22 additions & 0 deletions collector/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@
package collector

import (
"bytes"
"context"
"fmt"
"os"
"os/exec"
"reflect"
"strconv"
"strings"
Expand All @@ -33,7 +36,9 @@ var (
CgroupRoot = kingpin.Flag("path.cgroup.root", "Root path to cgroup fs").Default(defCgroupRoot).String()
collectProcMaxExec = kingpin.Flag("collect.proc.max-exec", "Max length of process executable to record").Default("100").Int()
ProcRoot = kingpin.Flag("path.proc.root", "Root path to proc fs").Default(defProcRoot).String()
userLookupTimeout = kingpin.Flag("exec.getent.timeout", "Timeout for running 'getent passwd' command").Default("5s").Duration()
metricLock = sync.RWMutex{}
execCommand = exec.CommandContext
)

const (
Expand Down Expand Up @@ -311,3 +316,20 @@ func sliceContains(s interface{}, v interface{}) bool {
}
return false
}

func getentPasswd(uid string) (string, error) {
ctx, cancel := context.WithTimeout(context.Background(), *userLookupTimeout)
defer cancel()
cmd := execCommand(ctx, "getent", "passwd", uid)
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
err := cmd.Run()
if ctx.Err() == context.DeadlineExceeded {
return "", fmt.Errorf("Timeout executing: getent passwd %s", uid)
} else if err != nil {
return "", fmt.Errorf("Error executing 'getent passwd %s': %s %s", uid, stderr.String(), err.Error())
}
username := strings.Split(stdout.String(), ":")[0]
return username, nil
}
40 changes: 40 additions & 0 deletions collector/collector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,25 @@
package collector

import (
"context"
"os"
"os/exec"
"path/filepath"
"reflect"
"runtime"
"strconv"
"testing"
"time"

"github.com/go-kit/log"
)

var (
mockedExitStatus = 0
mockedStdout string
_, cancel = context.WithTimeout(context.Background(), 5*time.Second)
)

func TestMain(m *testing.M) {
_, filename, _, _ := runtime.Caller(0)
dir := filepath.Dir(filename)
Expand Down Expand Up @@ -84,3 +94,33 @@ func TestGetProcInfo(t *testing.T) {
}
}
}

func fakeExecCommand(ctx context.Context, command string, args ...string) *exec.Cmd {
cs := []string{"-test.run=TestExecCommandHelper", "--", command}
cs = append(cs, args...)
defer cancel()
cmd := exec.CommandContext(ctx, os.Args[0], cs...)
es := strconv.Itoa(mockedExitStatus)
tmp, _ := os.MkdirTemp("", "fake")
cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1",
"GOCOVERDIR=" + tmp,
"STDOUT=" + mockedStdout,
"EXIT_STATUS=" + es}
return cmd
}

func TestGetentPasswd(t *testing.T) {
timeout := 5 * time.Second
userLookupTimeout = &timeout
execCommand = fakeExecCommand
mockedExitStatus = 0
mockedStdout = "adm:x:3:4:adm:/var/adm:/sbin/nologin"
defer func() { execCommand = exec.CommandContext }()
user, err := getentPasswd("3")
if err != nil {
t.Errorf("Unexpected error: %s", err.Error())
}
if user != "adm" {
t.Errorf("Unexpected user: %s", user)
}
}

0 comments on commit b1b7278

Please sign in to comment.