package nucleus import ( "bytes" "os/exec" "path/filepath" goPlugin "plugin" "code.fbi.h-da.de/danet/gosdn/controller/customerrs" "code.fbi.h-da.de/danet/gosdn/controller/interfaces/plugin" "code.fbi.h-da.de/danet/gosdn/controller/nucleus/util" log "github.com/sirupsen/logrus" ) // BuildPlugin builds a new plugin within the given path. The source file must // be present at the given path. The built plugin is written to a 'plugin.so' // file. func BuildPlugin(path string, sourceFileNames []string, args ...string) error { pPath := filepath.Join(path, util.PluginOutputName) fileNames := make([]string, len(sourceFileNames)) for i, fileName := range sourceFileNames { sPath := filepath.Join(path, fileName) fileNames[i] = sPath } // Provide standard build arguments within a string slice buildCommand := []string{ "go", "build", "-buildmode=plugin", "-trimpath", "-o", pPath, } // Append build arguments buildCommand = append(buildCommand, args...) buildCommand = append(buildCommand, fileNames...) var stderr bytes.Buffer // Create the command to be executed cmd := exec.Command(buildCommand[0], buildCommand[1:]...) cmd.Dir = "./" cmd.Stderr = &stderr // Run the command and build the plugin err := cmd.Run() if err != nil { log.Error(stderr.String()) return err } return nil } // LoadPlugin opens a go plugin binary which must be named 'plugin.so' at the // given path. LoadPlugin checks for the symbol 'PluginSymbol' that has to be // provided within the plugin to be loaded. If the symbol is found, the loaded // plugin is returned. func LoadPlugin(path string) (goPlugin.Symbol, error) { path = filepath.Join(path, util.PluginOutputName) // Open the plugin, which is a binary (named 'plugin.so') within the given // path. gp, err := goPlugin.Open(path) if err != nil { return nil, err } // Search for the specific symbol within the plugin. If it does not contain // the symbol is not usable and an error is thrown. symbol, err := gp.Lookup("PluginSymbol") if err != nil { return nil, err } return symbol, nil } // UpdatePlugin updates a given Plugin. Therefore the version of the // `plugin.yml` manifest file is compared to the version in use. If a new // version is within the plugin folder, the new version of the plugin is built. // NOTE:This should only be used with caution. func UpdatePlugin(p plugin.Plugin) (updated bool, err error) { tmpManifest, err := plugin.ReadManifestFromFile(filepath.Join(p.Path(), util.ManifestFileName)) if err != nil { return false, err } if p.Manifest().Version < tmpManifest.Version { err := BuildPlugin(p.Path(), []string{util.GoStructName, util.GoStructAdditionsName}) if err != nil { return false, err } log.Info("Plugin update executed.") return true, nil } return false, customerrs.PluginVersionError{ PlugID: p.ID().String(), ProvidedVer: tmpManifest.Version, UsedVer: p.Manifest().Version, } }