mirror of https://github.com/jetkvm/kvm.git
166 lines
3.9 KiB
Go
166 lines
3.9 KiB
Go
package plugin
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"os/exec"
|
|
"path"
|
|
"syscall"
|
|
)
|
|
|
|
type PluginInstall struct {
|
|
Enabled bool `json:"enabled"`
|
|
|
|
// Current active version of the plugin
|
|
Version string `json:"version"`
|
|
|
|
// Map of a plugin version to the extracted directory
|
|
ExtractedVersions map[string]string `json:"extracted_versions"`
|
|
|
|
manifest *PluginManifest
|
|
runningVersion string
|
|
processManager *ProcessManager
|
|
rpcServer *PluginRpcServer
|
|
}
|
|
|
|
func (p *PluginInstall) GetManifest() (*PluginManifest, error) {
|
|
if p.manifest != nil {
|
|
return p.manifest, nil
|
|
}
|
|
|
|
manifest, err := readManifest(p.GetExtractedFolder())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
p.manifest = manifest
|
|
return manifest, nil
|
|
}
|
|
|
|
func (p *PluginInstall) GetExtractedFolder() string {
|
|
return p.ExtractedVersions[p.Version]
|
|
}
|
|
|
|
func (p *PluginInstall) GetStatus() (*PluginStatus, error) {
|
|
manifest, err := p.GetManifest()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get plugin manifest: %v", err)
|
|
}
|
|
|
|
status := PluginStatus{
|
|
PluginManifest: *manifest,
|
|
Enabled: p.Enabled,
|
|
}
|
|
|
|
// If the rpc server is connected and the plugin is reporting status, use that
|
|
if p.rpcServer != nil &&
|
|
p.rpcServer.status.Status != "disconnected" &&
|
|
p.rpcServer.status.Status != "unknown" {
|
|
status.Status = p.rpcServer.status.Status
|
|
status.Message = p.rpcServer.status.Message
|
|
|
|
if status.Status == "error" {
|
|
status.Message = p.rpcServer.status.Message
|
|
}
|
|
} else {
|
|
status.Status = "stopped"
|
|
if p.processManager != nil {
|
|
status.Status = "running"
|
|
if p.processManager.LastError != nil {
|
|
status.Status = "error"
|
|
status.Message = p.processManager.LastError.Error()
|
|
}
|
|
}
|
|
log.Printf("Status from process manager: %v", status.Status)
|
|
}
|
|
|
|
return &status, nil
|
|
}
|
|
|
|
func (p *PluginInstall) ReconcileSubprocess() error {
|
|
manifest, err := p.GetManifest()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get plugin manifest: %v", err)
|
|
}
|
|
|
|
versionRunning := p.runningVersion
|
|
|
|
versionShouldBeRunning := p.Version
|
|
if !p.Enabled {
|
|
versionShouldBeRunning = ""
|
|
}
|
|
|
|
log.Printf("Reconciling plugin %s running %v, should be running %v", manifest.Name, versionRunning, versionShouldBeRunning)
|
|
|
|
if versionRunning == versionShouldBeRunning {
|
|
log.Printf("Plugin %s is already running version %s", manifest.Name, versionRunning)
|
|
return nil
|
|
}
|
|
|
|
if p.processManager != nil {
|
|
log.Printf("Stopping plugin %s running version %s", manifest.Name, versionRunning)
|
|
p.processManager.Disable()
|
|
p.processManager = nil
|
|
p.runningVersion = ""
|
|
err = p.rpcServer.Stop()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to stop rpc server: %v", err)
|
|
}
|
|
}
|
|
|
|
if versionShouldBeRunning == "" {
|
|
return nil
|
|
}
|
|
|
|
workingDir := path.Join(pluginsFolder, "working_dirs", p.manifest.Name)
|
|
err = os.MkdirAll(workingDir, 0755)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create working directory: %v", err)
|
|
}
|
|
|
|
p.rpcServer = NewPluginRpcServer(p, workingDir)
|
|
err = p.rpcServer.Start()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to start rpc server: %v", err)
|
|
}
|
|
|
|
p.processManager = NewProcessManager(func() *exec.Cmd {
|
|
cmd := exec.Command(manifest.BinaryPath)
|
|
cmd.Dir = p.GetExtractedFolder()
|
|
cmd.Env = append(cmd.Env,
|
|
"JETKVM_PLUGIN_SOCK="+p.rpcServer.SocketPath(),
|
|
"JETKVM_PLUGIN_WORKING_DIR="+workingDir,
|
|
)
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
// Ensure that the process is killed when the parent dies
|
|
cmd.SysProcAttr = &syscall.SysProcAttr{
|
|
Setpgid: true,
|
|
Pdeathsig: syscall.SIGKILL,
|
|
}
|
|
return cmd
|
|
})
|
|
p.processManager.StartMonitor()
|
|
p.processManager.Enable()
|
|
p.runningVersion = p.Version
|
|
|
|
// Clear out manifest so the new version gets pulled next time
|
|
p.manifest = nil
|
|
|
|
log.Printf("Started plugin %s version %s", manifest.Name, p.Version)
|
|
return nil
|
|
}
|
|
|
|
func (p *PluginInstall) Shutdown() {
|
|
if p.processManager != nil {
|
|
p.processManager.Disable()
|
|
p.processManager = nil
|
|
p.runningVersion = ""
|
|
}
|
|
|
|
if p.rpcServer != nil {
|
|
p.rpcServer.Stop()
|
|
}
|
|
}
|