github.com/maier/nomad@v0.4.1-0.20161110003312-a9e3d0b8549d/client/driver/utils.go (about)

     1  package driver
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"os"
     7  	"os/exec"
     8  	"path/filepath"
     9  	"strings"
    10  	"time"
    11  
    12  	"github.com/hashicorp/go-multierror"
    13  	"github.com/hashicorp/go-plugin"
    14  	"github.com/hashicorp/nomad/client/config"
    15  	"github.com/hashicorp/nomad/client/driver/executor"
    16  	"github.com/hashicorp/nomad/client/driver/logging"
    17  	cstructs "github.com/hashicorp/nomad/client/driver/structs"
    18  	"github.com/hashicorp/nomad/nomad/structs"
    19  )
    20  
    21  // createExecutor launches an executor plugin and returns an instance of the
    22  // Executor interface
    23  func createExecutor(config *plugin.ClientConfig, w io.Writer,
    24  	clientConfig *config.Config) (executor.Executor, *plugin.Client, error) {
    25  	config.HandshakeConfig = HandshakeConfig
    26  	config.Plugins = GetPluginMap(w)
    27  	config.MaxPort = clientConfig.ClientMaxPort
    28  	config.MinPort = clientConfig.ClientMinPort
    29  
    30  	// setting the setsid of the plugin process so that it doesn't get signals sent to
    31  	// the nomad client.
    32  	if config.Cmd != nil {
    33  		isolateCommand(config.Cmd)
    34  	}
    35  
    36  	executorClient := plugin.NewClient(config)
    37  	rpcClient, err := executorClient.Client()
    38  	if err != nil {
    39  		return nil, nil, fmt.Errorf("error creating rpc client for executor plugin: %v", err)
    40  	}
    41  
    42  	raw, err := rpcClient.Dispense("executor")
    43  	if err != nil {
    44  		return nil, nil, fmt.Errorf("unable to dispense the executor plugin: %v", err)
    45  	}
    46  	executorPlugin := raw.(executor.Executor)
    47  	return executorPlugin, executorClient, nil
    48  }
    49  
    50  func createLogCollector(config *plugin.ClientConfig, w io.Writer,
    51  	clientConfig *config.Config) (logging.LogCollector, *plugin.Client, error) {
    52  	config.HandshakeConfig = HandshakeConfig
    53  	config.Plugins = GetPluginMap(w)
    54  	config.MaxPort = clientConfig.ClientMaxPort
    55  	config.MinPort = clientConfig.ClientMinPort
    56  	if config.Cmd != nil {
    57  		isolateCommand(config.Cmd)
    58  	}
    59  
    60  	syslogClient := plugin.NewClient(config)
    61  	rpcCLient, err := syslogClient.Client()
    62  	if err != nil {
    63  		return nil, nil, fmt.Errorf("error creating rpc client for syslog plugin: %v", err)
    64  	}
    65  
    66  	raw, err := rpcCLient.Dispense("syslogcollector")
    67  	if err != nil {
    68  		return nil, nil, fmt.Errorf("unable to dispense the syslog plugin: %v", err)
    69  	}
    70  	logCollector := raw.(logging.LogCollector)
    71  	return logCollector, syslogClient, nil
    72  }
    73  
    74  func consulContext(clientConfig *config.Config, containerID string) *executor.ConsulContext {
    75  	return &executor.ConsulContext{
    76  		ConsulConfig:   clientConfig.ConsulConfig,
    77  		ContainerID:    containerID,
    78  		DockerEndpoint: clientConfig.Read("docker.endpoint"),
    79  		TLSCa:          clientConfig.Read("docker.tls.ca"),
    80  		TLSCert:        clientConfig.Read("docker.tls.cert"),
    81  		TLSKey:         clientConfig.Read("docker.tls.key"),
    82  	}
    83  }
    84  
    85  // killProcess kills a process with the given pid
    86  func killProcess(pid int) error {
    87  	proc, err := os.FindProcess(pid)
    88  	if err != nil {
    89  		return err
    90  	}
    91  	return proc.Kill()
    92  }
    93  
    94  // destroyPlugin kills the plugin with the given pid and also kills the user
    95  // process
    96  func destroyPlugin(pluginPid int, userPid int) error {
    97  	var merr error
    98  	if err := killProcess(pluginPid); err != nil {
    99  		merr = multierror.Append(merr, err)
   100  	}
   101  
   102  	if err := killProcess(userPid); err != nil {
   103  		merr = multierror.Append(merr, err)
   104  	}
   105  	return merr
   106  }
   107  
   108  // validateCommand validates that the command only has a single value and
   109  // returns a user friendly error message telling them to use the passed
   110  // argField.
   111  func validateCommand(command, argField string) error {
   112  	trimmed := strings.TrimSpace(command)
   113  	if len(trimmed) == 0 {
   114  		return fmt.Errorf("command empty: %q", command)
   115  	}
   116  
   117  	if len(trimmed) != len(command) {
   118  		return fmt.Errorf("command contains extra white space: %q", command)
   119  	}
   120  
   121  	split := strings.Split(trimmed, " ")
   122  	if len(split) != 1 {
   123  		return fmt.Errorf("command contained more than one input. Use %q field to pass arguments", argField)
   124  	}
   125  
   126  	return nil
   127  }
   128  
   129  // GetKillTimeout returns the kill timeout to use given the tasks desired kill
   130  // timeout and the operator configured max kill timeout.
   131  func GetKillTimeout(desired, max time.Duration) time.Duration {
   132  	maxNanos := max.Nanoseconds()
   133  	desiredNanos := desired.Nanoseconds()
   134  
   135  	// Make the minimum time between signal and kill, 1 second.
   136  	if desiredNanos <= 0 {
   137  		desiredNanos = (1 * time.Second).Nanoseconds()
   138  	}
   139  
   140  	// Protect against max not being set properly.
   141  	if maxNanos <= 0 {
   142  		maxNanos = (10 * time.Second).Nanoseconds()
   143  	}
   144  
   145  	if desiredNanos < maxNanos {
   146  		return time.Duration(desiredNanos)
   147  	}
   148  
   149  	return max
   150  }
   151  
   152  // GetAbsolutePath returns the absolute path of the passed binary by resolving
   153  // it in the path and following symlinks.
   154  func GetAbsolutePath(bin string) (string, error) {
   155  	lp, err := exec.LookPath(bin)
   156  	if err != nil {
   157  		return "", fmt.Errorf("failed to resolve path to %q executable: %v", bin, err)
   158  	}
   159  
   160  	return filepath.EvalSymlinks(lp)
   161  }
   162  
   163  // getExecutorUser returns the user of the task, defaulting to
   164  // cstructs.DefaultUnprivilegedUser if none was given.
   165  func getExecutorUser(task *structs.Task) string {
   166  	if task.User == "" {
   167  		return cstructs.DefaultUnpriviledgedUser
   168  	}
   169  	return task.User
   170  }