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