mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-01-19 03:20:49 -05:00
169 lines
3.5 KiB
Go
169 lines
3.5 KiB
Go
package controller
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/owncloud/ocis/ocis/pkg/runtime/config"
|
|
"github.com/owncloud/ocis/ocis/pkg/runtime/process"
|
|
"github.com/owncloud/ocis/ocis/pkg/runtime/storage"
|
|
"github.com/owncloud/ocis/ocis/pkg/runtime/watcher"
|
|
"github.com/rs/zerolog"
|
|
|
|
"github.com/olekukonko/tablewriter"
|
|
)
|
|
|
|
// Controller supervises processes.
|
|
type Controller struct {
|
|
m *sync.RWMutex
|
|
options Options
|
|
log zerolog.Logger
|
|
Config *config.Config
|
|
|
|
Store storage.Storage
|
|
|
|
// Bin is the oCIS single binary name.
|
|
Bin string
|
|
|
|
// BinPath is the oCIS single binary path withing the host machine.
|
|
// The Controller needs to know the binary location in order to spawn new extensions.
|
|
BinPath string
|
|
|
|
// Terminated facilitates communication from Watcher <-> Controller. Writes to this
|
|
// channel WILL always attempt to restart the crashed process.
|
|
Terminated chan process.ProcEntry
|
|
}
|
|
|
|
var (
|
|
once = sync.Once{}
|
|
)
|
|
|
|
// NewController initializes a new controller.
|
|
func NewController(o ...Option) Controller {
|
|
opts := &Options{}
|
|
|
|
for _, f := range o {
|
|
f(opts)
|
|
}
|
|
|
|
c := Controller{
|
|
m: &sync.RWMutex{},
|
|
options: *opts,
|
|
log: *opts.Log,
|
|
Bin: "ocis",
|
|
Terminated: make(chan process.ProcEntry),
|
|
Store: storage.NewMapStorage(),
|
|
|
|
Config: opts.Config,
|
|
}
|
|
|
|
if opts.Bin != "" {
|
|
c.Bin = opts.Bin
|
|
}
|
|
|
|
// Get binary location from $PATH lookup. If not present, it uses arg[0] as entry point.
|
|
path, err := exec.LookPath(c.Bin)
|
|
if err != nil {
|
|
c.log.Debug().Msg("oCIS binary not present in PATH, using Args[0]")
|
|
path = os.Args[0]
|
|
}
|
|
c.BinPath = path
|
|
return c
|
|
}
|
|
|
|
// Start and watches a process.
|
|
func (c *Controller) Start(pe process.ProcEntry) error {
|
|
if pid := c.Store.Load(pe.Extension); pid != 0 {
|
|
c.log.Debug().Msg(fmt.Sprintf("extension already running: %s", pe.Extension))
|
|
return nil
|
|
}
|
|
|
|
w := watcher.NewWatcher()
|
|
if err := pe.Start(c.BinPath); err != nil {
|
|
return err
|
|
}
|
|
|
|
// store the spawned child process PID.
|
|
if err := c.Store.Store(pe); err != nil {
|
|
return err
|
|
}
|
|
|
|
w.Follow(pe, c.Terminated, c.options.Config.KeepAlive)
|
|
|
|
once.Do(func() {
|
|
j := janitor{
|
|
time.Second,
|
|
c.Store,
|
|
}
|
|
|
|
go j.run()
|
|
go detach(c)
|
|
})
|
|
return nil
|
|
}
|
|
|
|
// Kill a managed process.
|
|
// Should a process managed by the runtime be allowed to be killed if the runtime is configured not to?
|
|
func (c *Controller) Kill(pe process.ProcEntry) error {
|
|
// load stored PID
|
|
pid := c.Store.Load(pe.Extension)
|
|
|
|
// find process in host by PID
|
|
p, err := os.FindProcess(pid)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := c.Store.Delete(pe); err != nil {
|
|
return err
|
|
}
|
|
c.log.Info().Str("package", "watcher").Msgf("terminating %v", pe.Extension)
|
|
|
|
// terminate child process
|
|
return p.Kill()
|
|
}
|
|
|
|
// Shutdown a running runtime.
|
|
func (c *Controller) Shutdown(ch chan struct{}) error {
|
|
entries := c.Store.LoadAll()
|
|
for cmd, pid := range entries {
|
|
c.log.Info().Str("package", "watcher").Msgf("gracefully terminating %v", cmd)
|
|
p, _ := os.FindProcess(pid)
|
|
if err := p.Kill(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
ch <- struct{}{}
|
|
return nil
|
|
}
|
|
|
|
// List managed processes.
|
|
func (c *Controller) List() string {
|
|
tableString := &strings.Builder{}
|
|
table := tablewriter.NewWriter(tableString)
|
|
table.SetHeader([]string{"Extension", "PID"})
|
|
|
|
entries := c.Store.LoadAll()
|
|
|
|
keys := make([]string, 0, len(entries))
|
|
for k := range entries {
|
|
keys = append(keys, k)
|
|
}
|
|
|
|
sort.Strings(keys)
|
|
|
|
for _, v := range keys {
|
|
table.Append([]string{v, strconv.Itoa(entries[v])})
|
|
}
|
|
|
|
table.Render()
|
|
return tableString.String()
|
|
}
|