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