github.com/hernad/nomad@v1.6.112/drivers/shared/executor/utils.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package executor
     5  
     6  import (
     7  	"encoding/json"
     8  	"fmt"
     9  	"os"
    10  	"os/exec"
    11  
    12  	"github.com/golang/protobuf/ptypes"
    13  	hclog "github.com/hashicorp/go-hclog"
    14  	plugin "github.com/hashicorp/go-plugin"
    15  	"github.com/hernad/nomad/drivers/shared/executor/proto"
    16  	"github.com/hernad/nomad/helper/stats"
    17  	"github.com/hernad/nomad/plugins/base"
    18  )
    19  
    20  const (
    21  	// ExecutorDefaultMaxPort is the default max port used by the executor for
    22  	// searching for an available port
    23  	ExecutorDefaultMaxPort = 14512
    24  
    25  	// ExecutorDefaultMinPort is the default min port used by the executor for
    26  	// searching for an available port
    27  	ExecutorDefaultMinPort = 14000
    28  )
    29  
    30  // CreateExecutor launches an executor plugin and returns an instance of the
    31  // Executor interface
    32  func CreateExecutor(logger hclog.Logger, driverConfig *base.ClientDriverConfig,
    33  	executorConfig *ExecutorConfig) (Executor, *plugin.Client, error) {
    34  
    35  	executorConfig.CpuTotalTicks = stats.CpuTotalTicks()
    36  	c, err := json.Marshal(executorConfig)
    37  	if err != nil {
    38  		return nil, nil, fmt.Errorf("unable to create executor config: %v", err)
    39  	}
    40  	bin, err := os.Executable()
    41  	if err != nil {
    42  		return nil, nil, fmt.Errorf("unable to find the nomad binary: %v", err)
    43  	}
    44  
    45  	p := &ExecutorPlugin{
    46  		logger:        logger,
    47  		fsIsolation:   executorConfig.FSIsolation,
    48  		cpuTotalTicks: executorConfig.CpuTotalTicks,
    49  	}
    50  
    51  	config := &plugin.ClientConfig{
    52  		HandshakeConfig:  base.Handshake,
    53  		Plugins:          map[string]plugin.Plugin{"executor": p},
    54  		Cmd:              exec.Command(bin, "executor", string(c)),
    55  		AllowedProtocols: []plugin.Protocol{plugin.ProtocolGRPC},
    56  		Logger:           logger.Named("executor"),
    57  	}
    58  
    59  	if driverConfig != nil {
    60  		config.MaxPort = driverConfig.ClientMaxPort
    61  		config.MinPort = driverConfig.ClientMinPort
    62  	} else {
    63  		config.MaxPort = ExecutorDefaultMaxPort
    64  		config.MinPort = ExecutorDefaultMinPort
    65  	}
    66  
    67  	// setting the setsid of the plugin process so that it doesn't get signals sent to
    68  	// the nomad client.
    69  	if config.Cmd != nil {
    70  		isolateCommand(config.Cmd)
    71  	}
    72  
    73  	return newExecutorClient(config, logger)
    74  }
    75  
    76  // ReattachToExecutor launches a plugin with a given plugin config
    77  func ReattachToExecutor(reattachConfig *plugin.ReattachConfig, logger hclog.Logger) (Executor, *plugin.Client, error) {
    78  	config := &plugin.ClientConfig{
    79  		HandshakeConfig:  base.Handshake,
    80  		Reattach:         reattachConfig,
    81  		Plugins:          GetPluginMap(logger, false, stats.CpuTotalTicks()),
    82  		AllowedProtocols: []plugin.Protocol{plugin.ProtocolGRPC},
    83  		Logger:           logger.Named("executor"),
    84  	}
    85  
    86  	return newExecutorClient(config, logger)
    87  }
    88  
    89  func newExecutorClient(config *plugin.ClientConfig, logger hclog.Logger) (Executor, *plugin.Client, error) {
    90  	executorClient := plugin.NewClient(config)
    91  	rpcClient, err := executorClient.Client()
    92  	if err != nil {
    93  		return nil, nil, fmt.Errorf("error creating rpc client for executor plugin: %v", err)
    94  	}
    95  
    96  	raw, err := rpcClient.Dispense("executor")
    97  	if err != nil {
    98  		return nil, nil, fmt.Errorf("unable to dispense the executor plugin: %v", err)
    99  	}
   100  	executorPlugin, ok := raw.(Executor)
   101  	if !ok {
   102  		return nil, nil, fmt.Errorf("unexpected executor rpc type: %T", raw)
   103  	}
   104  	return executorPlugin, executorClient, nil
   105  }
   106  
   107  func processStateToProto(ps *ProcessState) (*proto.ProcessState, error) {
   108  	timestamp, err := ptypes.TimestampProto(ps.Time)
   109  	if err != nil {
   110  		return nil, err
   111  	}
   112  	pb := &proto.ProcessState{
   113  		Pid:      int32(ps.Pid),
   114  		ExitCode: int32(ps.ExitCode),
   115  		Signal:   int32(ps.Signal),
   116  		Time:     timestamp,
   117  	}
   118  
   119  	return pb, nil
   120  }
   121  
   122  func processStateFromProto(pb *proto.ProcessState) (*ProcessState, error) {
   123  	timestamp, err := ptypes.Timestamp(pb.Time)
   124  	if err != nil {
   125  		return nil, err
   126  	}
   127  
   128  	return &ProcessState{
   129  		Pid:      int(pb.Pid),
   130  		ExitCode: int(pb.ExitCode),
   131  		Signal:   int(pb.Signal),
   132  		Time:     timestamp,
   133  	}, nil
   134  }
   135  
   136  // IsolationMode returns the namespace isolation mode as determined from agent
   137  // plugin configuration and task driver configuration. The task configuration
   138  // takes precedence, if it is configured.
   139  func IsolationMode(plugin, task string) string {
   140  	if task != "" {
   141  		return task
   142  	}
   143  	return plugin
   144  }