Skip to content
Snippets Groups Projects
plugin.go 7.24 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	"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"
    
    	"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"
    
    type pluginConnection struct {
    	client *hcplugin.Client
    	model  shared.DeviceModel
    }
    
    var pluginClients = make(map[uuid.UUID]pluginConnection, 0)
    
    
    // Plugin is the controllers internal representation of a plugin.
    
    type Plugin struct {
    	UUID     uuid.UUID
    	state    plugin.State
    
    	manifest *plugin.Manifest
    	client   *hcplugin.Client
    	shared.DeviceModel
    }
    
    
    // NewPlugin creates a new Plugin.
    
    func NewPlugin(id uuid.UUID, execPath string) (*Plugin, error) {
    
    	client := hcplugin.NewClient(&hcplugin.ClientConfig{
    		HandshakeConfig:  shared.Handshake,
    		Plugins:          shared.PluginMap,
    
    		Cmd:              exec.Command(filepath.Join(execPath, util.PluginExecutableName)),
    
    		AllowedProtocols: []hcplugin.Protocol{hcplugin.ProtocolGRPC},
    	})
    
    
    	manifest, err := plugin.ReadManifestFromFile(execPath)
    
    	// create a client that is within the AllowedProtocols. In this case this
    	// returns a gRPCClient. Allows to connect through gRPC.
    
    	// Request the plugin. This returns the gRPC client from the
    	// DeviceModelPlugin. This can then be casted to the interface that we are
    	// exposing through the plugin (in this case "DeviceModel").
    
    	raw, err := gRPCClient.Dispense("deviceModel")
    
    	// cast the raw plugin to the DeviceModel interface. This allows to call
    	// methods on the plugin as if it were a normal DeviceModel instance but
    	// actually they are executed on the plugin sent through gRPC.
    
    	model, ok := raw.(shared.DeviceModel)
    	if !ok {
    		return nil, customerrs.InvalidTypeAssertionError{
    			Value: raw,
    			Type:  (*shared.DeviceModel)(nil),
    		}
    	}
    
    
    	pluginClients[id] = pluginConnection{
    		client: client,
    		model:  model,
    	}
    
    
    		DeviceModel: model,
    		manifest:    manifest,
    		state:       plugin.CREATED,
    	}, nil
    
    // NewPluginThroughReattachConfig creates a new Plugin through a reattach config.
    
    func NewPluginThroughReattachConfig(loadedPlugin plugin.LoadedPlugin) (plugin.Plugin, error) {
    
    	//client := hcplugin.NewClient(&hcplugin.ClientConfig{
    	//	HandshakeConfig:  shared.Handshake,
    	//	Plugins:          shared.PluginMap,
    	//	Reattach:         &loadedPlugin.ReattachConfig,
    	//	AllowedProtocols: []hcplugin.Protocol{hcplugin.ProtocolGRPC},
    	//})
    
    	//// create a client that is within the AllowedProtocols. In this case this
    	//// returns a gRPCClient. Allows to connect through gRPC.
    	//gRPCClient, err := client.Client()
    	//if err != nil {
    	//	return nil, err
    	//}
    
    	//// Request the plugin. This returns the gRPC client from the
    	//// DeviceModelPlugin. This can then be casted to the interface that we are
    	//// exposing through the plugin (in this case "DeviceModel").
    	//raw, err := gRPCClient.Dispense("deviceModel")
    	//if err != nil {
    	//	return nil, err
    	//}
    
    	//// cast the raw plugin to the DeviceModel interface. This allows to call
    	//// methods on the plugin as if it were a normal DeviceModel instance but
    	//// actually they are executed on the plugin sent through gRPC.
    	//model, ok := raw.(shared.DeviceModel)
    	//if !ok {
    	//	return nil, customerrs.InvalidTypeAssertionError{
    	//		Value: model,
    	//		Type:  (*shared.DeviceModel)(nil),
    	//	}
    	//}
    	pluginId, err := uuid.Parse(loadedPlugin.ID)
    
    	pc, ok := pluginClients[pluginId]
    
    		return nil, fmt.Errorf("plugin not found")
    
    
    	return &Plugin{
    		UUID:        uuid.MustParse(loadedPlugin.ID),
    
    		client:      pc.client,
    		DeviceModel: pc.model,
    
    	}, nil
    }
    
    // ID returns the ID of the plugin.
    func (p *Plugin) ID() uuid.UUID {
    	return p.UUID
    }
    
    // ID returns the ID of the plugin.
    func (p *Plugin) ReattachConfig() *hcplugin.ReattachConfig {
    	return p.client.ReattachConfig()
    }
    
    
    // Remove ensures that the Plugin is killed and the corresponding files are
    // removed.
    func (p *Plugin) Remove() error {
    	// stop the running plugins process
    	p.Close()
    	// remove the plugins folder
    	return os.RemoveAll(p.ExecPath())
    }
    
    
    // State returns the current state of the plugin.
    // Different states of the plugin can be:
    
    //   - faulty
    func (p *Plugin) State() plugin.State {
    	return p.state
    }
    
    
    // ExecPath returns the path to the executable of the plugin.
    
    func (p *Plugin) ExecPath() string {
    	return p.execPath
    
    }
    
    // GetClient returns the client of the plugin.
    func (p *Plugin) GetClient() *hcplugin.Client {
    	return p.client
    }
    
    // Manifest returns the manifest of the plugin.
    func (p *Plugin) Manifest() *plugin.Manifest {
    	return p.manifest
    }
    
    
    // Update updates the plugin to the latest available version.
    
    func (p *Plugin) Update() error {
    	return fmt.Errorf("not implemented yet")
    }
    
    
    // Restart restarts the plugin.
    
    func (p *Plugin) Restart() error {
    	return fmt.Errorf("not implemented yet")
    }
    
    // Close ends the execution of the plugin.
    func (p *Plugin) Close() {
    
    	p.client.Kill()
    }
    
    // Ping checks if the client connection is healthy.
    func (p *Plugin) Ping() error {
    	protocolClient, err := p.client.Client()
    	if err != nil {
    		return err
    
    	return protocolClient.Ping()
    }
    
    // TODO: update for the new way of handling plugins
    // 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.
    func UpdatePlugin(p plugin.Plugin) (updated bool, err error) {
    	return false, fmt.Errorf("not implemented yet")
    }
    
    func (p *Plugin) MarshalJSON() ([]byte, error) {
    	return json.Marshal(&struct {
    		ID             uuid.UUID                `json:"id,omitempty"`
    		Manifest       *plugin.Manifest         `json:"manifest" bson:"manifest"`
    		State          plugin.State             `json:"state,omitempty" bson:"state"`
    
    		ExecPath       string                   `json:"exec_path,omitempty" bson:"exec_path"`
    
    		ReattachConfig *hcplugin.ReattachConfig `json:"reattatch_config,omitempty" bson:"reattatch_config"`
    	}{
    		ID:             p.ID(),
    		Manifest:       p.Manifest(),
    		State:          p.State(),
    
    		ReattachConfig: p.ReattachConfig(),
    	})
    }
    
    func (p *Plugin) MarshalBSON() ([]byte, error) {
    	return bson.Marshal(&struct {
    		ID             string                   `bson:"_id,omitempty"`
    		Manifest       *plugin.Manifest         `json:"manifest" bson:"manifest"`
    		State          plugin.State             `json:"state,omitempty" bson:"state"`
    
    		ExecPath       string                   `json:"exec_path,omitempty" bson:"exec_path"`
    
    		ReattachConfig *hcplugin.ReattachConfig `json:"reattatch_config,omitempty" bson:"reattatch_config"`
    	}{
    		ID:             p.ID().String(),
    		Manifest:       p.Manifest(),
    		State:          p.State(),