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 }