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 }