github.com/taylorchu/nomad@v0.5.3-rc1.0.20170407200202-db11e7dd7b55/client/driver/executor/checks.go (about) 1 package executor 2 3 import ( 4 "fmt" 5 "log" 6 "os/exec" 7 "sync" 8 "syscall" 9 "time" 10 11 "github.com/armon/circbuf" 12 docker "github.com/fsouza/go-dockerclient" 13 cstructs "github.com/hashicorp/nomad/client/driver/structs" 14 ) 15 16 var ( 17 // We store the client globally to cache the connection to the docker daemon. 18 createClient sync.Once 19 client *docker.Client 20 ) 21 22 const ( 23 // The default check timeout 24 defaultCheckTimeout = 30 * time.Second 25 ) 26 27 // DockerScriptCheck runs nagios compatible scripts in a docker container and 28 // provides the check result 29 type DockerScriptCheck struct { 30 id string // id of the check 31 interval time.Duration // interval of the check 32 timeout time.Duration // timeout of the check 33 containerID string // container id in which the check will be invoked 34 logger *log.Logger 35 cmd string // check command 36 args []string // check command arguments 37 38 dockerEndpoint string // docker endpoint 39 tlsCert string // path to tls certificate 40 tlsCa string // path to tls ca 41 tlsKey string // path to tls key 42 } 43 44 // dockerClient creates the client to interact with the docker daemon 45 func (d *DockerScriptCheck) dockerClient() (*docker.Client, error) { 46 if client != nil { 47 return client, nil 48 } 49 50 var err error 51 createClient.Do(func() { 52 if d.dockerEndpoint != "" { 53 if d.tlsCert+d.tlsKey+d.tlsCa != "" { 54 d.logger.Printf("[DEBUG] executor.checks: using TLS client connection to %s", d.dockerEndpoint) 55 client, err = docker.NewTLSClient(d.dockerEndpoint, d.tlsCert, d.tlsKey, d.tlsCa) 56 } else { 57 d.logger.Printf("[DEBUG] executor.checks: using standard client connection to %s", d.dockerEndpoint) 58 client, err = docker.NewClient(d.dockerEndpoint) 59 } 60 return 61 } 62 63 d.logger.Println("[DEBUG] executor.checks: using client connection initialized from environment") 64 client, err = docker.NewClientFromEnv() 65 }) 66 return client, err 67 } 68 69 // Run runs a script check inside a docker container 70 func (d *DockerScriptCheck) Run() *cstructs.CheckResult { 71 var ( 72 exec *docker.Exec 73 err error 74 execRes *docker.ExecInspect 75 time = time.Now() 76 ) 77 78 if client, err = d.dockerClient(); err != nil { 79 return &cstructs.CheckResult{Err: err} 80 } 81 execOpts := docker.CreateExecOptions{ 82 AttachStdin: false, 83 AttachStdout: true, 84 AttachStderr: true, 85 Tty: false, 86 Cmd: append([]string{d.cmd}, d.args...), 87 Container: d.containerID, 88 } 89 if exec, err = client.CreateExec(execOpts); err != nil { 90 return &cstructs.CheckResult{Err: err} 91 } 92 93 output, _ := circbuf.NewBuffer(int64(cstructs.CheckBufSize)) 94 startOpts := docker.StartExecOptions{ 95 Detach: false, 96 Tty: false, 97 OutputStream: output, 98 ErrorStream: output, 99 } 100 101 if err = client.StartExec(exec.ID, startOpts); err != nil { 102 return &cstructs.CheckResult{Err: err} 103 } 104 if execRes, err = client.InspectExec(exec.ID); err != nil { 105 return &cstructs.CheckResult{Err: err} 106 } 107 return &cstructs.CheckResult{ 108 ExitCode: execRes.ExitCode, 109 Output: string(output.Bytes()), 110 Timestamp: time, 111 } 112 } 113 114 // ID returns the check id 115 func (d *DockerScriptCheck) ID() string { 116 return d.id 117 } 118 119 // Interval returns the interval at which the check has to run 120 func (d *DockerScriptCheck) Interval() time.Duration { 121 return d.interval 122 } 123 124 // Timeout returns the duration after which a check is timed out. 125 func (d *DockerScriptCheck) Timeout() time.Duration { 126 if d.timeout == 0 { 127 return defaultCheckTimeout 128 } 129 return d.timeout 130 } 131 132 // ExecScriptCheck runs a nagios compatible script and returns the check result 133 type ExecScriptCheck struct { 134 id string // id of the script check 135 interval time.Duration // interval at which the check is invoked 136 timeout time.Duration // timeout duration of the check 137 cmd string // command of the check 138 args []string // args passed to the check 139 taskDir string // the root directory of the check 140 141 FSIsolation bool // indicates whether the check has to be run within a chroot 142 } 143 144 // Run runs an exec script check 145 func (e *ExecScriptCheck) Run() *cstructs.CheckResult { 146 buf, _ := circbuf.NewBuffer(int64(cstructs.CheckBufSize)) 147 cmd := exec.Command(e.cmd, e.args...) 148 cmd.Stdout = buf 149 cmd.Stderr = buf 150 e.setChroot(cmd) 151 ts := time.Now() 152 if err := cmd.Start(); err != nil { 153 return &cstructs.CheckResult{Err: err} 154 } 155 errCh := make(chan error, 2) 156 go func() { 157 errCh <- cmd.Wait() 158 }() 159 160 select { 161 case err := <-errCh: 162 endTime := time.Now() 163 if err == nil { 164 return &cstructs.CheckResult{ 165 ExitCode: 0, 166 Output: string(buf.Bytes()), 167 Timestamp: ts, 168 } 169 } 170 exitCode := 1 171 if exitErr, ok := err.(*exec.ExitError); ok { 172 if status, ok := exitErr.Sys().(syscall.WaitStatus); ok { 173 exitCode = status.ExitStatus() 174 } 175 } 176 return &cstructs.CheckResult{ 177 ExitCode: exitCode, 178 Output: string(buf.Bytes()), 179 Timestamp: ts, 180 Duration: endTime.Sub(ts), 181 } 182 case <-time.After(e.Timeout()): 183 errCh <- fmt.Errorf("timed out after waiting 30s") 184 } 185 186 return nil 187 } 188 189 // ID returns the check id 190 func (e *ExecScriptCheck) ID() string { 191 return e.id 192 } 193 194 // Interval returns the interval at which the check has to run 195 func (e *ExecScriptCheck) Interval() time.Duration { 196 return e.interval 197 } 198 199 // Timeout returns the duration after which a check is timed out. 200 func (e *ExecScriptCheck) Timeout() time.Duration { 201 if e.timeout == 0 { 202 return defaultCheckTimeout 203 } 204 return e.timeout 205 }