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()
 | 
						|
	}
 | 
						|
}
 |