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