Newer
Older
package plugin
import (
"fmt"
"net"
"path/filepath"
"regexp"
Fabian Seidl
committed
"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
// BUILT state describes a plugin which is built and can be loaded into the
// controller.
BUILT
// LOADED state describes a plugin which is running within the controller.
LOADED
// 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
// in the context of the controller is a basic go plugin. A plugin satisfies
// the Storable interface and can be stored.
type Plugin interface {
ID() uuid.UUID
GetClient() *hcplugin.Client
State() State
Manifest() *Manifest
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 {
Fabian Seidl
committed
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"`
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.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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
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"`
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
}