github.com/cs3org/reva/v2@v2.27.7/pkg/plugin/loader.go (about)

     1  // Copyright 2018-2021 CERN
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  // In applying this license, CERN does not waive the privileges and immunities
    16  // granted to it by virtue of its status as an Intergovernmental Organization
    17  // or submit itself to any jurisdiction.
    18  
    19  package plugin
    20  
    21  import (
    22  	"bytes"
    23  	"fmt"
    24  	"os"
    25  	"os/exec"
    26  	"path/filepath"
    27  	"regexp"
    28  
    29  	"github.com/cs3org/reva/v2/pkg/errtypes"
    30  	"github.com/hashicorp/go-hclog"
    31  	"github.com/hashicorp/go-plugin"
    32  )
    33  
    34  // RevaPlugin represents the runtime plugin
    35  type RevaPlugin struct {
    36  	Plugin interface{}
    37  	Client *plugin.Client
    38  }
    39  
    40  const dirname = "/var/tmp/reva"
    41  
    42  var isAlphaNum = regexp.MustCompile(`^[A-Za-z0-9]+$`).MatchString
    43  
    44  // Kill kills the plugin process
    45  func (plug *RevaPlugin) Kill() {
    46  	plug.Client.Kill()
    47  }
    48  
    49  var handshake = plugin.HandshakeConfig{
    50  	ProtocolVersion:  1,
    51  	MagicCookieKey:   "BASIC_PLUGIN",
    52  	MagicCookieValue: "hello",
    53  }
    54  
    55  func compile(pluginType string, path string) (string, error) {
    56  	var errb bytes.Buffer
    57  	binaryPath := filepath.Join(dirname, "bin", pluginType, filepath.Base(path))
    58  	command := fmt.Sprintf("go build -o %s %s", binaryPath, path)
    59  	cmd := exec.Command("bash", "-c", command)
    60  	cmd.Stderr = &errb
    61  	err := cmd.Run()
    62  	if err != nil {
    63  		return "", fmt.Errorf("%v: %w", errb.String(), err)
    64  	}
    65  	return binaryPath, nil
    66  }
    67  
    68  // checkDir checks and compiles plugin if the configuration points to a directory.
    69  func checkDirAndCompile(pluginType, driver string) (string, error) {
    70  	bin := driver
    71  	file, err := os.Stat(driver)
    72  	if err != nil {
    73  		return "", err
    74  	}
    75  	// compile if we point to a package
    76  	if file.IsDir() {
    77  		bin, err = compile(pluginType, driver)
    78  		if err != nil {
    79  			return "", err
    80  		}
    81  	}
    82  	return bin, nil
    83  }
    84  
    85  // Load loads the plugin using the hashicorp go-plugin system
    86  func Load(pluginType, driver string) (*RevaPlugin, error) {
    87  	if isAlphaNum(driver) {
    88  		return nil, errtypes.NotFound(driver)
    89  	}
    90  	bin, err := checkDirAndCompile(pluginType, driver)
    91  	if err != nil {
    92  		return nil, err
    93  	}
    94  
    95  	logger := hclog.New(&hclog.LoggerOptions{
    96  		Name:   "plugin",
    97  		Output: os.Stdout,
    98  		Level:  hclog.Trace,
    99  	})
   100  
   101  	client := plugin.NewClient(&plugin.ClientConfig{
   102  		HandshakeConfig: handshake,
   103  		Plugins:         PluginMap,
   104  		Cmd:             exec.Command(bin),
   105  		AllowedProtocols: []plugin.Protocol{
   106  			plugin.ProtocolNetRPC,
   107  		},
   108  		Logger: logger,
   109  	})
   110  
   111  	rpcClient, err := client.Client()
   112  	if err != nil {
   113  		return nil, err
   114  	}
   115  
   116  	raw, err := rpcClient.Dispense(pluginType)
   117  	if err != nil {
   118  		return nil, err
   119  	}
   120  
   121  	revaPlugin := &RevaPlugin{
   122  		Plugin: raw,
   123  		Client: client,
   124  	}
   125  
   126  	return revaPlugin, nil
   127  }