github.com/ncodes/nomad@v0.5.7-0.20170403112158-97adf4a74fb3/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/ncodes/nomad/client/config" 16 "github.com/ncodes/nomad/client/driver/executor" 17 cstructs "github.com/ncodes/nomad/client/driver/structs" 18 "github.com/ncodes/nomad/helper/discover" 19 "github.com/ncodes/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 := raw.(executor.Executor) 89 return executorPlugin, executorClient, nil 90 } 91 92 func consulContext(clientConfig *config.Config, containerID string) *executor.ConsulContext { 93 return &executor.ConsulContext{ 94 ConsulConfig: clientConfig.ConsulConfig, 95 ContainerID: containerID, 96 DockerEndpoint: clientConfig.Read("docker.endpoint"), 97 TLSCa: clientConfig.Read("docker.tls.ca"), 98 TLSCert: clientConfig.Read("docker.tls.cert"), 99 TLSKey: clientConfig.Read("docker.tls.key"), 100 } 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 // cstructs.DefaultUnprivilegedUser if none was given. 178 func getExecutorUser(task *structs.Task) string { 179 if task.User == "" { 180 return cstructs.DefaultUnpriviledgedUser 181 } 182 return task.User 183 }