github.com/hhrutter/nomad@v0.6.0-rc2.0.20170723054333-80c4b03f0705/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/allocdir"
    16  	"github.com/hashicorp/nomad/client/config"
    17  	"github.com/hashicorp/nomad/client/driver/env"
    18  	"github.com/hashicorp/nomad/client/driver/executor"
    19  	dstructs "github.com/hashicorp/nomad/client/driver/structs"
    20  	cstructs "github.com/hashicorp/nomad/client/structs"
    21  	"github.com/hashicorp/nomad/helper/discover"
    22  	"github.com/hashicorp/nomad/nomad/structs"
    23  )
    24  
    25  // cgroupsMounted returns true if the cgroups are mounted on a system otherwise
    26  // returns false
    27  func cgroupsMounted(node *structs.Node) bool {
    28  	_, ok := node.Attributes["unique.cgroup.mountpoint"]
    29  	return ok
    30  }
    31  
    32  // createExecutor launches an executor plugin and returns an instance of the
    33  // Executor interface
    34  func createExecutor(w io.Writer, clientConfig *config.Config,
    35  	executorConfig *dstructs.ExecutorConfig) (executor.Executor, *plugin.Client, error) {
    36  
    37  	c, err := json.Marshal(executorConfig)
    38  	if err != nil {
    39  		return nil, nil, fmt.Errorf("unable to create executor config: %v", err)
    40  	}
    41  	bin, err := discover.NomadExecutable()
    42  	if err != nil {
    43  		return nil, nil, fmt.Errorf("unable to find the nomad binary: %v", err)
    44  	}
    45  
    46  	config := &plugin.ClientConfig{
    47  		Cmd: exec.Command(bin, "executor", string(c)),
    48  	}
    49  	config.HandshakeConfig = HandshakeConfig
    50  	config.Plugins = GetPluginMap(w, clientConfig.LogLevel)
    51  	config.MaxPort = clientConfig.ClientMaxPort
    52  	config.MinPort = clientConfig.ClientMinPort
    53  
    54  	// setting the setsid of the plugin process so that it doesn't get signals sent to
    55  	// the nomad client.
    56  	if config.Cmd != nil {
    57  		isolateCommand(config.Cmd)
    58  	}
    59  
    60  	executorClient := plugin.NewClient(config)
    61  	rpcClient, err := executorClient.Client()
    62  	if err != nil {
    63  		return nil, nil, fmt.Errorf("error creating rpc client for executor plugin: %v", err)
    64  	}
    65  
    66  	raw, err := rpcClient.Dispense("executor")
    67  	if err != nil {
    68  		return nil, nil, fmt.Errorf("unable to dispense the executor plugin: %v", err)
    69  	}
    70  	executorPlugin := raw.(executor.Executor)
    71  	return executorPlugin, executorClient, nil
    72  }
    73  
    74  func createExecutorWithConfig(config *plugin.ClientConfig, w io.Writer) (executor.Executor, *plugin.Client, error) {
    75  	config.HandshakeConfig = HandshakeConfig
    76  
    77  	// Setting this to DEBUG since the log level at the executor server process
    78  	// is already set, and this effects only the executor client.
    79  	config.Plugins = GetPluginMap(w, "DEBUG")
    80  
    81  	executorClient := plugin.NewClient(config)
    82  	rpcClient, err := executorClient.Client()
    83  	if err != nil {
    84  		return nil, nil, fmt.Errorf("error creating rpc client for executor plugin: %v", err)
    85  	}
    86  
    87  	raw, err := rpcClient.Dispense("executor")
    88  	if err != nil {
    89  		return nil, nil, fmt.Errorf("unable to dispense the executor plugin: %v", err)
    90  	}
    91  	executorPlugin, ok := raw.(*ExecutorRPC)
    92  	if !ok {
    93  		return nil, nil, fmt.Errorf("unexpected executor rpc type: %T", raw)
    94  	}
    95  	// 0.6 Upgrade path: Deregister services from the executor as the Nomad
    96  	// client agent now handles all Consul interactions. Ignore errors as
    97  	// this shouldn't cause the alloc to fail and there's nothing useful to
    98  	// do with them.
    99  	executorPlugin.DeregisterServices()
   100  	return executorPlugin, executorClient, nil
   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  // dstructs.DefaultUnprivilegedUser if none was given.
   178  func getExecutorUser(task *structs.Task) string {
   179  	if task.User == "" {
   180  		return dstructs.DefaultUnpriviledgedUser
   181  	}
   182  	return task.User
   183  }
   184  
   185  // SetEnvvars sets path and host env vars depending on the FS isolation used.
   186  func SetEnvvars(envBuilder *env.Builder, fsi cstructs.FSIsolation, taskDir *allocdir.TaskDir, conf *config.Config) {
   187  	// Set driver-specific environment variables
   188  	switch fsi {
   189  	case cstructs.FSIsolationNone:
   190  		// Use host paths
   191  		envBuilder.SetAllocDir(taskDir.SharedAllocDir)
   192  		envBuilder.SetTaskLocalDir(taskDir.LocalDir)
   193  		envBuilder.SetSecretsDir(taskDir.SecretsDir)
   194  	default:
   195  		// filesystem isolation; use container paths
   196  		envBuilder.SetAllocDir(allocdir.SharedAllocContainerPath)
   197  		envBuilder.SetTaskLocalDir(allocdir.TaskLocalContainerPath)
   198  		envBuilder.SetSecretsDir(allocdir.TaskSecretsContainerPath)
   199  	}
   200  
   201  	// Set the host environment variables for non-image based drivers
   202  	if fsi != cstructs.FSIsolationImage {
   203  		filter := strings.Split(conf.ReadDefault("env.blacklist", config.DefaultEnvBlacklist), ",")
   204  		envBuilder.SetHostEnvvars(filter)
   205  	}
   206  }