github.com/ncodes/nomad@v0.5.7-0.20170403112158-97adf4a74fb3/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/ncodes/nomad/client/config"
    16  	"github.com/ncodes/nomad/client/driver/executor"
    17  	cstructs "github.com/ncodes/nomad/client/driver/structs"
    18  	"github.com/ncodes/nomad/helper/discover"
    19  	"github.com/ncodes/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 := raw.(executor.Executor)
    89  	return executorPlugin, executorClient, nil
    90  }
    91  
    92  func consulContext(clientConfig *config.Config, containerID string) *executor.ConsulContext {
    93  	return &executor.ConsulContext{
    94  		ConsulConfig:   clientConfig.ConsulConfig,
    95  		ContainerID:    containerID,
    96  		DockerEndpoint: clientConfig.Read("docker.endpoint"),
    97  		TLSCa:          clientConfig.Read("docker.tls.ca"),
    98  		TLSCert:        clientConfig.Read("docker.tls.cert"),
    99  		TLSKey:         clientConfig.Read("docker.tls.key"),
   100  	}
   101  }
   102  
   103  // killProcess kills a process with the given pid
   104  func killProcess(pid int) error {
   105  	proc, err := os.FindProcess(pid)
   106  	if err != nil {
   107  		return err
   108  	}
   109  	return proc.Kill()
   110  }
   111  
   112  // destroyPlugin kills the plugin with the given pid and also kills the user
   113  // process
   114  func destroyPlugin(pluginPid int, userPid int) error {
   115  	var merr error
   116  	if err := killProcess(pluginPid); err != nil {
   117  		merr = multierror.Append(merr, err)
   118  	}
   119  
   120  	if err := killProcess(userPid); err != nil {
   121  		merr = multierror.Append(merr, err)
   122  	}
   123  	return merr
   124  }
   125  
   126  // validateCommand validates that the command only has a single value and
   127  // returns a user friendly error message telling them to use the passed
   128  // argField.
   129  func validateCommand(command, argField string) error {
   130  	trimmed := strings.TrimSpace(command)
   131  	if len(trimmed) == 0 {
   132  		return fmt.Errorf("command empty: %q", command)
   133  	}
   134  
   135  	if len(trimmed) != len(command) {
   136  		return fmt.Errorf("command contains extra white space: %q", command)
   137  	}
   138  
   139  	return nil
   140  }
   141  
   142  // GetKillTimeout returns the kill timeout to use given the tasks desired kill
   143  // timeout and the operator configured max kill timeout.
   144  func GetKillTimeout(desired, max time.Duration) time.Duration {
   145  	maxNanos := max.Nanoseconds()
   146  	desiredNanos := desired.Nanoseconds()
   147  
   148  	// Make the minimum time between signal and kill, 1 second.
   149  	if desiredNanos <= 0 {
   150  		desiredNanos = (1 * time.Second).Nanoseconds()
   151  	}
   152  
   153  	// Protect against max not being set properly.
   154  	if maxNanos <= 0 {
   155  		maxNanos = (10 * time.Second).Nanoseconds()
   156  	}
   157  
   158  	if desiredNanos < maxNanos {
   159  		return time.Duration(desiredNanos)
   160  	}
   161  
   162  	return max
   163  }
   164  
   165  // GetAbsolutePath returns the absolute path of the passed binary by resolving
   166  // it in the path and following symlinks.
   167  func GetAbsolutePath(bin string) (string, error) {
   168  	lp, err := exec.LookPath(bin)
   169  	if err != nil {
   170  		return "", fmt.Errorf("failed to resolve path to %q executable: %v", bin, err)
   171  	}
   172  
   173  	return filepath.EvalSymlinks(lp)
   174  }
   175  
   176  // getExecutorUser returns the user of the task, defaulting to
   177  // cstructs.DefaultUnprivilegedUser if none was given.
   178  func getExecutorUser(task *structs.Task) string {
   179  	if task.User == "" {
   180  		return cstructs.DefaultUnpriviledgedUser
   181  	}
   182  	return task.User
   183  }