From ce93cb9d9c595dfc635f81b3f3ba3addfae3a17d Mon Sep 17 00:00:00 2001 From: Davanum Srinivas Date: Thu, 4 Aug 2016 12:58:04 -0400 Subject: [PATCH] Remove kubelet dependency on pkill Issue #26093 identified pkill as one of the dependencies of kublet which could be worked around. Build on the code introduced for pidof and regexp for the process(es) we need to send a signal to. Related to #26093 --- pkg/kubelet/cm/container_manager_linux.go | 2 +- pkg/kubelet/container_bridge.go | 4 ++- pkg/util/procfs/procfs.go | 43 +++++++++++++++++++++-- pkg/util/procfs/procfs_test.go | 25 ++++++++++++- 4 files changed, 69 insertions(+), 5 deletions(-) diff --git a/pkg/kubelet/cm/container_manager_linux.go b/pkg/kubelet/cm/container_manager_linux.go index 2230db98d69..dcbc72b3fce 100644 --- a/pkg/kubelet/cm/container_manager_linux.go +++ b/pkg/kubelet/cm/container_manager_linux.go @@ -525,7 +525,7 @@ func getPidsForProcess(name, pidFile string) ([]int, error) { runtime.HandleError(err) } } - return procfs.PidOf(name), nil + return procfs.PidOf(name) } // Ensures that the Docker daemon is in the desired container. diff --git a/pkg/kubelet/container_bridge.go b/pkg/kubelet/container_bridge.go index 55a1f4827b0..dc5259979c1 100644 --- a/pkg/kubelet/container_bridge.go +++ b/pkg/kubelet/container_bridge.go @@ -27,6 +27,8 @@ import ( "github.com/golang/glog" "k8s.io/kubernetes/pkg/util" "k8s.io/kubernetes/pkg/util/iptables" + "k8s.io/kubernetes/pkg/util/procfs" + "syscall" ) var cidrRegexp = regexp.MustCompile(`inet ([0-9a-fA-F.:]*/[0-9]*)`) @@ -54,7 +56,7 @@ func createCBR0(wantCIDR *net.IPNet, babysitDaemons bool) error { // For now just log the error. The containerRuntime check will catch docker failures. // TODO (dawnchen) figure out what we should do for rkt here. if babysitDaemons { - if err := exec.Command("pkill", "-KILL", "docker").Run(); err != nil { + if err := procfs.PKill("docker", syscall.SIGKILL); err != nil { glog.Error(err) } } else if util.UsingSystemdInitSystem() { diff --git a/pkg/util/procfs/procfs.go b/pkg/util/procfs/procfs.go index 72d9abbe012..a7d95427e70 100644 --- a/pkg/util/procfs/procfs.go +++ b/pkg/util/procfs/procfs.go @@ -23,11 +23,14 @@ import ( "os" "path" "path/filepath" + "regexp" "strconv" "strings" + "syscall" "unicode" "github.com/golang/glog" + utilerrors "k8s.io/kubernetes/pkg/util/errors" ) type ProcFS struct{} @@ -62,7 +65,43 @@ func (pfs *ProcFS) GetFullContainerName(pid int) (string, error) { return containerNameFromProcCgroup(string(content)) } -func PidOf(name string) []int { +// Find process(es) using a regular expression and send a specified +// signal to each process +func PKill(name string, sig syscall.Signal) error { + if len(name) == 0 { + return fmt.Errorf("name should not be empty") + } + re, err := regexp.Compile(name) + if err != nil { + return err + } + pids := getPids(re) + if len(pids) == 0 { + return fmt.Errorf("unable to fetch pids for process name : %q", name) + } + errList := []error{} + for _, pid := range pids { + if err = syscall.Kill(pid, sig); err != nil { + errList = append(errList, err) + } + } + return utilerrors.NewAggregate(errList) +} + +// Find process(es) with a specified name (exact match) +// and return their pid(s) +func PidOf(name string) ([]int, error) { + if len(name) == 0 { + return []int{}, fmt.Errorf("name should not be empty") + } + re, err := regexp.Compile("(^|/)" + name + "$") + if err != nil { + return []int{}, err + } + return getPids(re), nil +} + +func getPids(re *regexp.Regexp) []int { pids := []int{} filepath.Walk("/proc", func(path string, info os.FileInfo, err error) error { base := filepath.Base(path) @@ -94,7 +133,7 @@ func PidOf(name string) []int { return nil } // Check if the name of the executable is what we are looking for - if filepath.Base(exe[0]) == name { + if re.MatchString(exe[0]) { dirname := filepath.Base(filepath.Dir(path)) // Grab the PID from the directory path pid, _ := strconv.Atoi(dirname) diff --git a/pkg/util/procfs/procfs_test.go b/pkg/util/procfs/procfs_test.go index 848326217b7..380b23e4143 100644 --- a/pkg/util/procfs/procfs_test.go +++ b/pkg/util/procfs/procfs_test.go @@ -19,9 +19,12 @@ package procfs import ( "io/ioutil" "os" + "os/signal" "path/filepath" "runtime" + "syscall" "testing" + "time" "github.com/stretchr/testify/assert" ) @@ -66,7 +69,27 @@ func TestPidOf(t *testing.T) { if runtime.GOOS == "darwin" || runtime.GOOS == "windows" { t.Skipf("not supported on GOOS=%s", runtime.GOOS) } - pids := PidOf(filepath.Base(os.Args[0])) + pids, err := PidOf(filepath.Base(os.Args[0])) + assert.Empty(t, err) assert.NotZero(t, pids) assert.Contains(t, pids, os.Getpid()) } + +func TestPKill(t *testing.T) { + if runtime.GOOS == "darwin" || runtime.GOOS == "windows" { + t.Skipf("not supported on GOOS=%s", runtime.GOOS) + } + sig := syscall.SIGCONT + c := make(chan os.Signal, 1) + signal.Notify(c, sig) + defer signal.Stop(c) + PKill(os.Args[0], sig) + select { + case s := <-c: + if s != sig { + t.Fatalf("signal was %v, want %v", s, sig) + } + case <-time.After(1 * time.Second): + t.Fatalf("timeout waiting for %v", sig) + } +}