diff --git a/cmd/cli/plugin.go b/cmd/cli/plugin.go index fc5f9da70..30f01e4b6 100644 --- a/cmd/cli/plugin.go +++ b/cmd/cli/plugin.go @@ -2,8 +2,13 @@ package main import ( "fmt" + "io/ioutil" + sys_os "os" + "strconv" "sync" + "syscall" + log "github.com/Sirupsen/logrus" "github.com/docker/infrakit/pkg/discovery" "github.com/docker/infrakit/pkg/launch" "github.com/docker/infrakit/pkg/launch/os" @@ -128,7 +133,64 @@ func pluginCommand(plugins func() discovery.Plugins) *cobra.Command { return nil } - cmd.AddCommand(ls, start) + stop := &cobra.Command{ + Use: "stop", + Short: "Stop named plugins. Args are a list of plugin names. This assumes plugins are local processes and not containers managed by another daemon, like Docker or runc.", + } + + stop.RunE = func(c *cobra.Command, args []string) error { + + allPlugins, err := plugins().List() + if err != nil { + return err + } + + for _, n := range args { + + p, has := allPlugins[n] + if !has { + log.Warningf("Plugin %s not running", n) + continue + } + + if p.Protocol != "unix" { + log.Warningf("Plugin is not a local process", n) + continue + } + + // TODO(chungers) -- here we + pidFile := p.Address + ".pid" + + buff, err := ioutil.ReadFile(pidFile) + if err != nil { + log.Warningf("Cannot read PID file for %s: %s", n, pidFile) + continue + } + + pid, err := strconv.Atoi(string(buff)) + if err != nil { + log.Warningf("Cannot determine PID for %s from file: %s", n, pidFile) + continue + } + + process, err := sys_os.FindProcess(pid) + if err != nil { + log.Warningf("Error finding process of plugin %s", n) + continue + } + + log.Infoln("Stopping", n, "at PID=", pid) + if err := process.Signal(syscall.SIGTERM); err == nil { + process.Wait() + log.Infoln("Process for", n, "exited") + } + + } + + return nil + } + + cmd.AddCommand(ls, start, stop) return cmd } diff --git a/pkg/cli/serverutil.go b/pkg/cli/serverutil.go index ee63fd18e..fbcf2a0d7 100644 --- a/pkg/cli/serverutil.go +++ b/pkg/cli/serverutil.go @@ -1,6 +1,8 @@ package cli import ( + "fmt" + "io/ioutil" "os" "path" @@ -21,11 +23,25 @@ func RunPlugin(name string, plugin server.VersionedInterface) { dir := discovery.Dir() EnsureDirExists(dir) - stoppable, err := server.StartPluginAtPath(path.Join(dir, name), plugin) + socketPath := path.Join(dir, name) + pidPath := path.Join(dir, name+".pid") + + stoppable, err := server.StartPluginAtPath(socketPath, plugin) + if err != nil { + log.Error(err) + } + + // write PID file + err = ioutil.WriteFile(pidPath, []byte(fmt.Sprintf("%v", os.Getpid())), 0644) if err != nil { log.Error(err) } + log.Infoln("PID file at", pidPath) if stoppable != nil { stoppable.AwaitStopped() } + + // clean up + os.Remove(pidPath) + log.Infoln("Removed PID file at", pidPath) } diff --git a/pkg/discovery/dir.go b/pkg/discovery/dir.go index ce3d7b4cc..e8ffddbec 100644 --- a/pkg/discovery/dir.go +++ b/pkg/discovery/dir.go @@ -11,6 +11,18 @@ import ( "github.com/docker/infrakit/pkg/plugin" ) +type errNotUnixSocket string + +func (e errNotUnixSocket) Error() string { + return string(e) +} + +// IsErrNotUnixSocket returns true if the error is due to the file not being a valid unix socket. +func IsErrNotUnixSocket(e error) bool { + _, is := e.(errNotUnixSocket) + return is +} + type dirPluginDiscovery struct { dir string lock sync.Mutex @@ -51,7 +63,7 @@ func (r *dirPluginDiscovery) dirLookup(entry os.FileInfo) (*plugin.Endpoint, err }, nil } - return nil, fmt.Errorf("File is not a socket: %s", entry) + return nil, errNotUnixSocket(fmt.Sprintf("File is not a socket: %s", entry)) } // List returns a list of plugins known, keyed by the name @@ -72,8 +84,16 @@ func (r *dirPluginDiscovery) List() (map[string]*plugin.Endpoint, error) { if !entry.IsDir() { instance, err := r.dirLookup(entry) - if err != nil || instance == nil { - log.Warningln("Loading plugin err=", err) + + if err != nil { + if !IsErrNotUnixSocket(err) { + log.Warningln("Loading plugin err=", err) + } + continue + } + + if instance == nil { + log.Warningln("Plugin in nil=") continue } diff --git a/pkg/discovery/dir_test.go b/pkg/discovery/dir_test.go index f857dbf40..cb1cecc9a 100644 --- a/pkg/discovery/dir_test.go +++ b/pkg/discovery/dir_test.go @@ -13,6 +13,12 @@ import ( "github.com/stretchr/testify/require" ) +func TestErrNotUnixSocket(t *testing.T) { + err := errNotUnixSocket("no socket!") + require.Error(t, err) + require.True(t, IsErrNotUnixSocket(err)) +} + func blockWhileFileExists(name string) { for { _, err := os.Stat(name) diff --git a/scripts/e2e-test.sh b/scripts/e2e-test.sh index 0ed86a583..b79e6ca2c 100755 --- a/scripts/e2e-test.sh +++ b/scripts/e2e-test.sh @@ -142,3 +142,6 @@ infrakit group destroy cattle expect_output_lines "0 instances should exist" "infrakit instance describe -q --name instance-file" "0" echo 'ALL TESTS PASSED' + +echo "Stopping plugins" +infrakit plugin stop group-default instance-file flavor-vanilla