-
Malte Bauch authored
This commit makes sure that the stored model data is only unmarshalled if the plugin is newly created. Otherwise we assume that the plugin represents the intended state (this is only temporary and will be changed in the future). Requesting configuration data directly from the device will now only pass that information through. Only changes triggered through the controller itself will be persisted. Additionally the handling of a subscribe response has been changed so the data is not unmarshalled into the plugins model data.
Malte Bauch authoredThis commit makes sure that the stored model data is only unmarshalled if the plugin is newly created. Otherwise we assume that the plugin represents the intended state (this is only temporary and will be changed in the future). Requesting configuration data directly from the device will now only pass that information through. Only changes triggered through the controller itself will be persisted. Additionally the handling of a subscribe response has been changed so the data is not unmarshalled into the plugins model data.
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
plugin.go 5.86 KiB
package plugin
import (
"encoding/json"
"fmt"
"net"
"os"
"path/filepath"
"regexp"
"code.fbi.h-da.de/danet/gosdn/controller/customerrs"
"code.fbi.h-da.de/danet/gosdn/controller/nucleus/util"
"code.fbi.h-da.de/danet/gosdn/controller/plugin/shared"
"github.com/google/uuid"
hcplugin "github.com/hashicorp/go-plugin"
"go.mongodb.org/mongo-driver/bson"
"gopkg.in/yaml.v3"
)
// State represents the current state of a plugin within the controller. Since
// the plugins used within the controller are basic go plugins, they can be
// CREATED, BUILT, LOADED or FAULTY. A plugin can not be unloaded (this is a
// limitation of go plugins in general).
type State int64
const (
//CREATED state describes a plugin which has been created but is not yet
//built.
CREATED State = iota
// INITIALIZED state describes a plugin which is running and has been
// initialized with the model data of the associated network element.
INITIALIZED
// FAULTY state describes a plugin which couldn't be built or loaded.
FAULTY
)
// Plugin describes an interface for a plugin within the controller. A plugin
// is based on hashicorp's `go plugin`.
type Plugin interface {
ID() uuid.UUID
GetClient() *hcplugin.Client
State() State
Manifest() *Manifest
ExecPath() string
Update() error
Ping() error
Restart() error
Close()
shared.DeviceModel
}
// Manifest represents the manifest of a plugin.
type Manifest struct {
// Name of the plugin
Name string `yaml:"name" json:"name" bson:"name"`
// Name of the plugin
Firmware string `yaml:"firmware" json:"firmware" bson:"firmware"`
// Author of the plugin
Author string `yaml:"author" json:"author" bson:"author"`
// Version of the plugin
Version string `yaml:"version" json:"version" bson:"version"`
}
// Validate is a method to check if the manifest is valid and is compliant with
// the requirements.
func (m *Manifest) Validate() error {
errs := []error{}
if m.Name == "" {
errs = append(errs, fmt.Errorf("Name is required"))
}
if m.Firmware == "" {
errs = append(errs, fmt.Errorf("Firmware is required"))
}
if m.Author == "" {
errs = append(errs, fmt.Errorf("Author is required"))
}
if m.Version == "" {
errs = append(errs, fmt.Errorf("Version is required"))
}
// regex from: https://stackoverflow.com/a/68921827
validVersion, err := regexp.MatchString(`^([1-9]\d*|0)(\.(([1-9]\d*)|0)){2}$`,
m.Version)
if err != nil {
errs = append(errs, err)
}
if !validVersion {
errs = append(errs, fmt.Errorf("Version has to be of form: X.X.X"))
}
if len(errs) != 0 {
return customerrs.CombinedErrListError{Errors: errs}
}
return nil
}
// ReadManifestFromFile reads a manifest file and returns a pointer to a newly
// created Manifest.
func ReadManifestFromFile(path string) (*Manifest, error) {
manifest := &Manifest{}
manifestFile, err := os.ReadFile(filepath.Join(path, util.ManifestFileName))
if err != nil {
return nil, err
}
err = yaml.Unmarshal(manifestFile, manifest)
if err != nil {
return nil, err
}
// validate the loaded manifest
if err := manifest.Validate(); err != nil {
return nil, err
}
return manifest, nil
}
type LoadedPlugin struct {
// ID represents the UUID of the LoadedPlugin.
ID string `json:"id" bson:"_id"`
// Manifest represents the manifest of the LoadedPlugin.
Manifest Manifest `json:"manifest" bson:"manifest"`
// State represents the state of the LoadedPlugin.
State State `json:"state,omitempty" bson:"state"`
// ExecPath represents the path to the executable of the plugin.
ExecPath string `json:"exec_path,omitempty" bson:"exec_path"`
// ReattachConfig represents the configuration to reattach to a already
// running plugin.
ReattachConfig hcplugin.ReattachConfig `json:"reattatch_config,omitempty" bson:"reattatch_config"`
}
func (lp *LoadedPlugin) UnmarshalBSON(data []byte) error {
loadedPluginHelper := new(LoadedPluginHelper)
if err := bson.Unmarshal(data, loadedPluginHelper); err != nil {
return err
}
lp.ID = loadedPluginHelper.ID
lp.Manifest = loadedPluginHelper.Manifest
lp.State = loadedPluginHelper.State
lp.ExecPath = loadedPluginHelper.ExecPath
lp.ReattachConfig = hcplugin.ReattachConfig{
Protocol: hcplugin.Protocol(loadedPluginHelper.ReattachConfig.Protocol),
ProtocolVersion: loadedPluginHelper.ReattachConfig.ProtocolVersion,
Addr: &net.UnixAddr{
Name: loadedPluginHelper.ReattachConfig.Addr.Name,
Net: loadedPluginHelper.ReattachConfig.Addr.Net,
},
Pid: loadedPluginHelper.ReattachConfig.Pid,
Test: loadedPluginHelper.ReattachConfig.Test,
}
return nil
}
func (lp *LoadedPlugin) UnmarshalJSON(data []byte) error {
loadedPluginHelper := new(LoadedPluginHelper)
if err := json.Unmarshal(data, loadedPluginHelper); err != nil {
return err
}
lp.ID = loadedPluginHelper.ID
lp.Manifest = loadedPluginHelper.Manifest
lp.State = loadedPluginHelper.State
lp.ExecPath = loadedPluginHelper.ExecPath
lp.ReattachConfig = hcplugin.ReattachConfig{
Protocol: hcplugin.Protocol(loadedPluginHelper.ReattachConfig.Protocol),
ProtocolVersion: loadedPluginHelper.ReattachConfig.ProtocolVersion,
Addr: &net.UnixAddr{
Name: loadedPluginHelper.ReattachConfig.Addr.Name,
Net: loadedPluginHelper.ReattachConfig.Addr.Net,
},
Pid: loadedPluginHelper.ReattachConfig.Pid,
Test: loadedPluginHelper.ReattachConfig.Test,
}
return nil
}
type LoadedPluginHelper struct {
ID string `json:"id" bson:"_id"`
Manifest Manifest `json:"manifest" bson:"manifest"`
State State `json:"state,omitempty" bson:"state"`
ExecPath string `json:"exec_path,omitempty" bson:"exec_path"`
ReattachConfig LoadedReattachConfig `json:"reattatch_config,omitempty" bson:"reattatch_config"`
}
type LoadedReattachConfig struct {
Protocol string
ProtocolVersion int
Addr LoadedAddress
Pid int
Test bool
}
type LoadedAddress struct {
Name string
Net string
}