github.com/diptanu/nomad@v0.5.7-0.20170516172507-d72e86cbe3d9/client/driver/utils.go (about)

     1  package driver
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  	"os/exec"
     9  	"path/filepath"
    10  	"strings"
    11  	"time"
    12  
    13  	"github.com/hashicorp/go-multierror"
    14  	"github.com/hashicorp/go-plugin"
    15  	"github.com/hashicorp/nomad/client/config"
    16  	"github.com/hashicorp/nomad/client/driver/executor"
    17  	cstructs "github.com/hashicorp/nomad/client/driver/structs"
    18  	"github.com/hashicorp/nomad/helper/discover"
    19  	"github.com/hashicorp/nomad/nomad/structs"
    20  )
    21  
    22  // cgroupsMounted returns true if the cgroups are mounted on a system otherwise
    23  // returns false
    24  func cgroupsMounted(node *structs.Node) bool {
    25  	_, ok := node.Attributes["unique.cgroup.mountpoint"]
    26  	return ok
    27  }
    28  
    29  // createExecutor launches an executor plugin and returns an instance of the
    30  // Executor interface
    31  func createExecutor(w io.Writer, clientConfig *config.Config,
    32  	executorConfig *cstructs.ExecutorConfig) (executor.Executor, *plugin.Client, error) {
    33  
    34  	c, err := json.Marshal(executorConfig)
    35  	if err != nil {
    36  		return nil, nil, fmt.Errorf("unable to create executor config: %v", err)
    37  	}
    38  	bin, err := discover.NomadExecutable()
    39  	if err != nil {
    40  		return nil, nil, fmt.Errorf("unable to find the nomad binary: %v", err)
    41  	}
    42  
    43  	config := &plugin.ClientConfig{
    44  		Cmd: exec.Command(bin, "executor", string(c)),
    45  	}
    46  	config.HandshakeConfig = HandshakeConfig
    47  	config.Plugins = GetPluginMap(w, clientConfig.LogLevel)
    48  	config.MaxPort = clientConfig.ClientMaxPort
    49  	config.MinPort = clientConfig.ClientMinPort
    50  
    51  	// setting the setsid of the plugin process so that it doesn't get signals sent to
    52  	// the nomad client.
    53  	if config.Cmd != nil {
    54  		isolateCommand(config.Cmd)
    55  	}
    56  
    57  	executorClient := plugin.NewClient(config)
    58  	rpcClient, err := executorClient.Client()
    59  	if err != nil {
    60  		return nil, nil, fmt.Errorf("error creating rpc client for executor plugin: %v", err)
    61  	}
    62  
    63  	raw, err := rpcClient.Dispense("executor")
    64  	if err != nil {
    65  		return nil, nil, fmt.Errorf("unable to dispense the executor plugin: %v", err)
    66  	}
    67  	executorPlugin := raw.(executor.Executor)
    68  	return executorPlugin, executorClient, nil
    69  }
    70  
    71  func createExecutorWithConfig(config *plugin.ClientConfig, w io.Writer) (executor.Executor, *plugin.Client, error) {
    72  	config.HandshakeConfig = HandshakeConfig
    73  
    74  	// Setting this to DEBUG since the log level at the executor server process
    75  	// is already set, and this effects only the executor client.
    76  	config.Plugins = GetPluginMap(w, "DEBUG")
    77  
    78  	executorClient := plugin.NewClient(config)
    79  	rpcClient, err := executorClient.Client()
    80  	if err != nil {
    81  		return nil, nil, fmt.Errorf("error creating rpc client for executor plugin: %v", err)
    82  	}
    83  
    84  	raw, err := rpcClient.Dispense("executor")
    85  	if err != nil {
    86  		return nil, nil, fmt.Errorf("unable to dispense the executor plugin: %v", err)
    87  	}
    88  	executorPlugin, ok := raw.(*ExecutorRPC)
    89  	if !ok {
    90  		return nil, nil, fmt.Errorf("unexpected executor rpc type: %T", raw)
    91  	}
    92  	// 0.6 Upgrade path: Deregister services from the executor as the Nomad
    93  	// client agent now handles all Consul interactions. Ignore errors as
    94  	// this shouldn't cause the alloc to fail and there's nothing useful to
    95  	// do with them.
    96  	executorPlugin.DeregisterServices()
    97  	return executorPlugin, executorClient, nil
    98  }
    99  
   100  // killProcess kills a process with the given pid
   101  func killProcess(pid int) error {
   102  	proc, err := os.FindProcess(pid)
   103  	if err != nil {
   104  		return err
   105  	}
   106  	return proc.Kill()
   107  }
   108  
   109  // destroyPlugin kills the plugin with the given pid and also kills the user
   110  // process
   111  func destroyPlugin(pluginPid int, userPid int) error {
   112  	var merr error
   113  	if err := killProcess(pluginPid); err != nil {
   114  		merr = multierror.Append(merr, err)
   115  	}
   116  
   117  	if err := killProcess(userPid); err != nil {
   118  		merr = multierror.Append(merr, err)
   119  	}
   120  	return merr
   121  }
   122  
   123  // validateCommand validates that the command only has a single value and
   124  // returns a user friendly error message telling them to use the passed
   125  // argField.
   126  func validateCommand(command, argField string) error {
   127  	trimmed := strings.TrimSpace(command)
   128  	if len(trimmed) == 0 {
   129  		return fmt.Errorf("command empty: %q", command)
   130  	}
   131  
   132  	if len(trimmed) != len(command) {
   133  		return fmt.Errorf("command contains extra white space: %q", command)
   134  	}
   135  
   136  	return nil
   137  }
   138  
   139  // GetKillTimeout returns the kill timeout to use given the tasks desired kill
   140  // timeout and the operator configured max kill timeout.
   141  func GetKillTimeout(desired, max time.Duration) time.Duration {
   142  	maxNanos := max.Nanoseconds()
   143  	desiredNanos := desired.Nanoseconds()
   144  
   145  	// Make the minimum time between signal and kill, 1 second.
   146  	if desiredNanos <= 0 {
   147  		desiredNanos = (1 * time.Second).Nanoseconds()
   148  	}
   149  
   150  	// Protect against max not being set properly.
   151  	if maxNanos <= 0 {
   152  		maxNanos = (10 * time.Second).Nanoseconds()
   153  	}
   154  
   155  	if desiredNanos < maxNanos {
   156  		return time.Duration(desiredNanos)
   157  	}
   158  
   159  	return max
   160  }
   161  
   162  // GetAbsolutePath returns the absolute path of the passed binary by resolving
   163  // it in the path and following symlinks.
   164  func GetAbsolutePath(bin string) (string, error) {
   165  	lp, err := exec.LookPath(bin)
   166  	if err != nil {
   167  		return "", fmt.Errorf("failed to resolve path to %q executable: %v", bin, err)
   168  	}
   169  
   170  	return filepath.EvalSymlinks(lp)
   171  }
   172  
   173  // getExecutorUser returns the user of the task, defaulting to
   174  // cstructs.DefaultUnprivilegedUser if none was given.
   175  func getExecutorUser(task *structs.Task) string {
   176  	if task.User == "" {
   177  		return cstructs.DefaultUnpriviledgedUser
   178  	}
   179  	return task.User
   180  }