diff --git a/pkg/kubelet/cm/container_manager_linux.go b/pkg/kubelet/cm/container_manager_linux.go index a4f16b715d1..79accf4b7f8 100644 --- a/pkg/kubelet/cm/container_manager_linux.go +++ b/pkg/kubelet/cm/container_manager_linux.go @@ -527,7 +527,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 25530f86ef6..b60f53933d8 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 { if err != nil { @@ -98,7 +137,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) + } +}