func ExecuteCommand(command *ExecCmd) (output string, err error) { // Execute the command using a shell var shell, flag string if runtime.GOOS == "windows" { shell = "cmd" flag = "/C" } else { shell = "/bin/sh" flag = "-c" } // Setup the reader that will read the lines from the command pr, pw := io.Pipe() copyDoneCh := make(chan struct{}) go copyOutput(pr, copyDoneCh) // Setup the command cmd := exec.Command(shell, flag, command.Cmd) out, _ := circbuf.NewBuffer(maxBufSize) cmd.Stderr = io.MultiWriter(out, pw) cmd.Stdout = io.MultiWriter(out, pw) // Run the command to completion runErr := cmd.Run() pw.Close() <-copyDoneCh if runErr != nil { return string(out.Bytes()), fmt.Errorf("Error running command '%s': %v. Output: %s", command, runErr, out.Bytes()) } return string(out.Bytes()), nil }
// invokeJob will execute the given job. Depending on the event. func (a *AgentCommand) invokeJob(execution *Execution) error { job := execution.Job output, _ := circbuf.NewBuffer(maxBufSize) // Determine the shell invocation based on OS var shell, flag string if runtime.GOOS == windows { shell = "cmd" flag = "/C" } else { shell = "/bin/sh" flag = "-c" } cmd := exec.Command(shell, flag, job.Command) cmd.Stderr = output cmd.Stdout = output // Start a timer to warn about slow handlers slowTimer := time.AfterFunc(2*time.Hour, func() { log.Warnf("proc: Script '%s' slow, execution exceeding %v", job.Command, 2*time.Hour) }) if err := cmd.Start(); err != nil { return err } // Warn if buffer is overritten if output.TotalWritten() > output.Size() { log.Warnf("proc: Script '%s' generated %d bytes of output, truncated to %d", job.Command, output.TotalWritten(), output.Size()) } var success bool err := cmd.Wait() slowTimer.Stop() log.WithFields(logrus.Fields{ "output": output, }).Debug("proc: Command output") if err != nil { log.WithFields(logrus.Fields{ "err": err, }).Error("proc: command error output") success = false } else { success = true } execution.FinishedAt = time.Now() execution.Success = success execution.Output = output.Bytes() rpcServer, err := a.queryRPCConfig() if err != nil { return err } rc := &RPCClient{ServerAddr: string(rpcServer)} return rc.callExecutionDone(execution) }
func (p *ResourceProvisioner) Apply( o terraform.UIOutput, s *terraform.InstanceState, c *terraform.ResourceConfig) error { // Get the command commandRaw, ok := c.Config["command"] if !ok { return fmt.Errorf("local-exec provisioner missing 'command'") } command, ok := commandRaw.(string) if !ok { return fmt.Errorf("local-exec provisioner command must be a string") } // Execute the command using a shell var shell, flag string if runtime.GOOS == "windows" { shell = "cmd" flag = "/C" } else { shell = "/bin/sh" flag = "-c" } // Setup the reader that will read the lines from the command pr, pw := io.Pipe() copyDoneCh := make(chan struct{}) go p.copyOutput(o, pr, copyDoneCh) // Setup the command cmd := exec.Command(shell, flag, command) output, _ := circbuf.NewBuffer(maxBufSize) cmd.Stderr = io.MultiWriter(output, pw) cmd.Stdout = io.MultiWriter(output, pw) // Output what we're about to run o.Output(fmt.Sprintf( "Executing: %s %s \"%s\"", shell, flag, command)) // Run the command to completion err := cmd.Run() // Close the write-end of the pipe so that the goroutine mirroring output // ends properly. pw.Close() <-copyDoneCh if err != nil { return fmt.Errorf("Error running command '%s': %v. Output: %s", command, err, output.Bytes()) } return nil }
func (u *packerUi) init() { // Allocating the circular buffer. The circular buffer should only // keep track up to the point that there is a \n found so it doesn't // need to be huge, but it also limits the max length of an event. var err error u.buf, err = circbuf.NewBuffer(4096) if err != nil { panic(err) } }
// makeWatchHandler returns a handler for the given watch func makeWatchHandler(logOutput io.Writer, params interface{}, reapLock *sync.RWMutex) watch.HandlerFunc { script := params.(string) logger := log.New(logOutput, "", log.LstdFlags) fn := func(idx uint64, data interface{}) { // Disable child process reaping so that we can get this command's // return value. Note that we take the read lock here since we are // waiting on a specific PID and don't need to serialize all waits. reapLock.RLock() defer reapLock.RUnlock() // Create the command cmd, err := ExecScript(script) if err != nil { logger.Printf("[ERR] agent: Failed to setup watch: %v", err) return } cmd.Env = append(os.Environ(), "CONSUL_INDEX="+strconv.FormatUint(idx, 10), ) // Collect the output output, _ := circbuf.NewBuffer(WatchBufSize) cmd.Stdout = output cmd.Stderr = output // Setup the input var inp bytes.Buffer enc := json.NewEncoder(&inp) if err := enc.Encode(data); err != nil { logger.Printf("[ERR] agent: Failed to encode data for watch '%s': %v", script, err) return } cmd.Stdin = &inp // Run the handler if err := cmd.Run(); err != nil { logger.Printf("[ERR] agent: Failed to invoke watch handler '%s': %v", script, err) } // Get the output, add a message about truncation outputStr := string(output.Bytes()) if output.TotalWritten() > output.Size() { outputStr = fmt.Sprintf("Captured %d of %d bytes\n...\n%s", output.Size(), output.TotalWritten(), outputStr) } // Log the output logger.Printf("[DEBUG] agent: watch handler '%s' output: %s", script, outputStr) } return fn }
// check is invoked periodically to perform the HTTP check func (c *CheckHTTP) check() { req, err := http.NewRequest("GET", c.HTTP, nil) if err != nil { c.Logger.Printf("[WARN] agent: http request failed '%s': %s", c.HTTP, err) c.Notify.UpdateCheck(c.CheckID, structs.HealthCritical, err.Error()) return } req.Header.Set("User-Agent", HttpUserAgent) req.Header.Set("Accept", "text/plain, text/*, */*") resp, err := c.httpClient.Do(req) if err != nil { c.Logger.Printf("[WARN] agent: http request failed '%s': %s", c.HTTP, err) c.Notify.UpdateCheck(c.CheckID, structs.HealthCritical, err.Error()) return } defer resp.Body.Close() // Read the response into a circular buffer to limit the size output, _ := circbuf.NewBuffer(CheckBufSize) if _, err := io.Copy(output, resp.Body); err != nil { c.Logger.Printf("[WARN] agent: check '%v': Get error while reading body: %s", c.CheckID, err) } // Format the response body result := fmt.Sprintf("HTTP GET %s: %s Output: %s", c.HTTP, resp.Status, output.String()) if resp.StatusCode >= 200 && resp.StatusCode <= 299 { // PASSING (2xx) c.Logger.Printf("[DEBUG] agent: check '%v' is passing", c.CheckID) c.Notify.UpdateCheck(c.CheckID, structs.HealthPassing, result) } else if resp.StatusCode == 429 { // WARNING // 429 Too Many Requests (RFC 6585) // The user has sent too many requests in a given amount of time. c.Logger.Printf("[WARN] agent: check '%v' is now warning", c.CheckID) c.Notify.UpdateCheck(c.CheckID, structs.HealthWarning, result) } else { // CRITICAL c.Logger.Printf("[WARN] agent: check '%v' is now critical", c.CheckID) c.Notify.UpdateCheck(c.CheckID, structs.HealthCritical, result) } }
// makeWatchHandler returns a handler for the given watch func makeWatchHandler(logOutput io.Writer, params interface{}) watch.HandlerFunc { script := params.(string) logger := log.New(logOutput, "", log.LstdFlags) fn := func(idx uint64, data interface{}) { // Create the command cmd, err := ExecScript(script) if err != nil { logger.Printf("[ERR] agent: Failed to setup watch: %v", err) return } cmd.Env = append(os.Environ(), "CONSUL_INDEX="+strconv.FormatUint(idx, 10), ) // Collect the output output, _ := circbuf.NewBuffer(WatchBufSize) cmd.Stdout = output cmd.Stderr = output // Setup the input var inp bytes.Buffer enc := json.NewEncoder(&inp) if err := enc.Encode(data); err != nil { logger.Printf("[ERR] agent: Failed to encode data for watch '%s': %v", script, err) return } cmd.Stdin = &inp // Run the handler if err := cmd.Run(); err != nil { logger.Printf("[ERR] agent: Failed to invoke watch handler '%s': %v", script, err) } // Get the output, add a message about truncation outputStr := string(output.Bytes()) if output.TotalWritten() > output.Size() { outputStr = fmt.Sprintf("Captured %d of %d bytes\n...\n%s", output.Size(), output.TotalWritten(), outputStr) } // Log the output logger.Printf("[DEBUG] agent: watch handler '%s' output: %s", script, outputStr) } return fn }
// Run runs a script check inside a docker container func (d *DockerScriptCheck) Run() *cstructs.CheckResult { var ( exec *docker.Exec err error execRes *docker.ExecInspect time = time.Now() ) if client, err = d.dockerClient(); err != nil { return &cstructs.CheckResult{Err: err} } client = client execOpts := docker.CreateExecOptions{ AttachStdin: false, AttachStdout: true, AttachStderr: true, Tty: false, Cmd: append([]string{d.cmd}, d.args...), Container: d.containerID, } if exec, err = client.CreateExec(execOpts); err != nil { return &cstructs.CheckResult{Err: err} } output, _ := circbuf.NewBuffer(int64(cstructs.CheckBufSize)) startOpts := docker.StartExecOptions{ Detach: false, Tty: false, OutputStream: output, ErrorStream: output, } if err = client.StartExec(exec.ID, startOpts); err != nil { return &cstructs.CheckResult{Err: err} } if execRes, err = client.InspectExec(exec.ID); err != nil { return &cstructs.CheckResult{Err: err} } return &cstructs.CheckResult{ ExitCode: execRes.ExitCode, Output: string(output.Bytes()), Timestamp: time, } }
// invokeJob will execute the given job. Depending on the event. func (a *AgentCommand) invokeJob(job *Job, execution *Execution) error { output, _ := circbuf.NewBuffer(maxBufSize) cmd := buildCmd(job) cmd.Stderr = output cmd.Stdout = output // Start a timer to warn about slow handlers slowTimer := time.AfterFunc(2*time.Hour, func() { log.Warnf("proc: Script '%s' slow, execution exceeding %v", job.Command, 2*time.Hour) }) err := cmd.Start() // Warn if buffer is overritten if output.TotalWritten() > output.Size() { log.Warnf("proc: Script '%s' generated %d bytes of output, truncated to %d", job.Command, output.TotalWritten(), output.Size()) } var success bool err = cmd.Wait() slowTimer.Stop() log.WithFields(logrus.Fields{ "output": output, }).Debug("proc: Command output") if err != nil { log.WithError(err).Error("proc: command error output") success = false } else { success = true } execution.FinishedAt = time.Now() execution.Success = success execution.Output = output.Bytes() rpcServer, err := a.queryRPCConfig() if err != nil { return err } rc := &RPCClient{ServerAddr: string(rpcServer)} return rc.callExecutionDone(execution) }
// Run runs an exec script check func (e *ExecScriptCheck) Run() *cstructs.CheckResult { buf, _ := circbuf.NewBuffer(int64(cstructs.CheckBufSize)) cmd := exec.Command(e.cmd, e.args...) cmd.Stdout = buf cmd.Stderr = buf e.setChroot(cmd) ts := time.Now() if err := cmd.Start(); err != nil { return &cstructs.CheckResult{Err: err} } errCh := make(chan error, 2) go func() { errCh <- cmd.Wait() }() for { select { case err := <-errCh: endTime := time.Now() if err == nil { return &cstructs.CheckResult{ ExitCode: 0, Output: string(buf.Bytes()), Timestamp: ts, } } exitCode := 1 if exitErr, ok := err.(*exec.ExitError); ok { if status, ok := exitErr.Sys().(syscall.WaitStatus); ok { exitCode = status.ExitStatus() } } return &cstructs.CheckResult{ ExitCode: exitCode, Output: string(buf.Bytes()), Timestamp: ts, Duration: endTime.Sub(ts), } case <-time.After(e.Timeout()): errCh <- fmt.Errorf("timed out after waiting 30s") } } return nil }
func (p *ResourceProvisioner) Apply( s *terraform.ResourceState, c *terraform.ResourceConfig) error { // Get the command commandRaw, ok := c.Config["command"] if !ok { return fmt.Errorf("local-exec provisioner missing 'command'") } command, ok := commandRaw.(string) if !ok { return fmt.Errorf("local-exec provisioner command must be a string") } // Execute the command using a shell var shell, flag string if runtime.GOOS == "windows" { shell = "cmd" flag = "/C" } else { shell = "/bin/sh" flag = "-c" } // Setup the command cmd := exec.Command(shell, flag, command) output, _ := circbuf.NewBuffer(maxBufSize) cmd.Stderr = output cmd.Stdout = output // Run the command to completion if err := cmd.Run(); err != nil { return fmt.Errorf("Error running command '%s': %v. Output: %s", command, err, output.Bytes()) } return nil }
// check is invoked periodically to perform the script check func (c *CheckMonitor) check() { // Create the command cmd, err := ExecScript(c.Script) if err != nil { c.Logger.Printf("[ERR] agent: failed to setup invoke '%s': %s", c.Script, err) c.Notify.UpdateCheck(c.CheckID, structs.HealthUnknown, err.Error()) return } // Collect the output output, _ := circbuf.NewBuffer(CheckBufSize) cmd.Stdout = output cmd.Stderr = output // Start the check if err := cmd.Start(); err != nil { c.Logger.Printf("[ERR] agent: failed to invoke '%s': %s", c.Script, err) c.Notify.UpdateCheck(c.CheckID, structs.HealthUnknown, err.Error()) return } // Wait for the check to complete errCh := make(chan error, 2) go func() { errCh <- cmd.Wait() }() go func() { time.Sleep(30 * time.Second) errCh <- fmt.Errorf("Timed out running check '%s'", c.Script) }() err = <-errCh // Get the output, add a message about truncation outputStr := string(output.Bytes()) if output.TotalWritten() > output.Size() { outputStr = fmt.Sprintf("Captured %d of %d bytes\n...\n%s", output.Size(), output.TotalWritten(), outputStr) } c.Logger.Printf("[DEBUG] agent: check '%s' script '%s' output: %s", c.CheckID, c.Script, outputStr) // Check if the check passed if err == nil { c.Logger.Printf("[DEBUG] Check '%v' is passing", c.CheckID) c.Notify.UpdateCheck(c.CheckID, structs.HealthPassing, outputStr) return } // If the exit code is 1, set check as warning exitErr, ok := err.(*exec.ExitError) if ok { if status, ok := exitErr.Sys().(syscall.WaitStatus); ok { code := status.ExitStatus() if code == 1 { c.Logger.Printf("[WARN] Check '%v' is now warning", c.CheckID) c.Notify.UpdateCheck(c.CheckID, structs.HealthWarning, outputStr) return } } } // Set the health as critical c.Logger.Printf("[WARN] Check '%v' is now critical", c.CheckID) c.Notify.UpdateCheck(c.CheckID, structs.HealthCritical, outputStr) }
// invokeJob will execute the given job. Depending on the event. func (a *AgentCommand) invokeJob(job *Job, execution *Execution) error { output, _ := circbuf.NewBuffer(maxBufSize) // Determine the shell invocation based on OS var shell, flag string if runtime.GOOS == windows { shell = "cmd" flag = "/C" } else { shell = "/bin/sh" flag = "-c" } cmd := exec.Command(shell, flag, job.Command) cmd.Stderr = output cmd.Stdout = output // Start a timer to warn about slow handlers slowTimer := time.AfterFunc(2*time.Hour, func() { log.Warnf("Script '%s' slow, execution exceeding %v", job.Command, 2*time.Hour) }) if err := cmd.Start(); err != nil { return err } // Warn if buffer is overritten if output.TotalWritten() > output.Size() { log.Warnf("Script '%s' generated %d bytes of output, truncated to %d", job.Command, output.TotalWritten(), output.Size()) } var success bool err := cmd.Wait() slowTimer.Stop() log.Debugf("Command output: %s", output) if err != nil { log.Error(err) success = false } else { success = true } execution.FinishedAt = time.Now() execution.Success = success execution.Output = output.Bytes() executionJson, _ := json.Marshal(execution) params := &serf.QueryParam{ FilterTags: map[string]string{"server": "true"}, RequestAck: true, } qr, err := a.serf.Query(QueryExecutionDone, executionJson, params) if err != nil { log.WithFields(logrus.Fields{ "query": QueryExecutionDone, "error": err, }).Debug("Error sending query") } defer qr.Close() ackCh := qr.AckCh() respCh := qr.ResponseCh() for !qr.Finished() { select { case ack, ok := <-ackCh: if ok { log.WithFields(logrus.Fields{ "query": QueryExecutionDone, "from": ack, }).Debug("Received ack") } case resp, ok := <-respCh: if ok { log.WithFields(logrus.Fields{ "query": QueryExecutionDone, "from": resp.From, "payload": string(resp.Payload), }).Debug("Received response") } } } log.Debugf("Done receiving acks and responses from %s query", QueryExecutionDone) return nil }
func (c *CheckDocker) check() { //Set up the Exec since execOpts := docker.CreateExecOptions{ AttachStdin: false, AttachStdout: true, AttachStderr: true, Tty: false, Cmd: c.cmd, Container: c.DockerContainerID, } var ( exec *docker.Exec err error ) if exec, err = c.dockerClient.CreateExec(execOpts); err != nil { c.Logger.Printf("[DEBUG] agent: Error while creating Exec: %s", err.Error()) c.Notify.UpdateCheck(c.CheckID, structs.HealthCritical, fmt.Sprintf("Unable to create Exec, error: %s", err.Error())) return } // Collect the output output, _ := circbuf.NewBuffer(CheckBufSize) err = c.dockerClient.StartExec(exec.ID, docker.StartExecOptions{Detach: false, Tty: false, OutputStream: output, ErrorStream: output}) if err != nil { c.Logger.Printf("[DEBUG] Error in executing health checks: %s", err.Error()) c.Notify.UpdateCheck(c.CheckID, structs.HealthCritical, fmt.Sprintf("Unable to start Exec: %s", err.Error())) return } // Get the output, add a message about truncation outputStr := string(output.Bytes()) if output.TotalWritten() > output.Size() { outputStr = fmt.Sprintf("Captured %d of %d bytes\n...\n%s", output.Size(), output.TotalWritten(), outputStr) } c.Logger.Printf("[DEBUG] agent: check '%s' script '%s' output: %s", c.CheckID, c.Script, outputStr) execInfo, err := c.dockerClient.InspectExec(exec.ID) if err != nil { c.Logger.Printf("[DEBUG] Error in inspecting check result : %s", err.Error()) c.Notify.UpdateCheck(c.CheckID, structs.HealthCritical, fmt.Sprintf("Unable to inspect Exec: %s", err.Error())) return } // Sets the status of the check to healthy if exit code is 0 if execInfo.ExitCode == 0 { c.Notify.UpdateCheck(c.CheckID, structs.HealthPassing, outputStr) return } // Set the status of the check to Warning if exit code is 1 if execInfo.ExitCode == 1 { c.Logger.Printf("[DEBUG] Check failed with exit code: %d", execInfo.ExitCode) c.Notify.UpdateCheck(c.CheckID, structs.HealthWarning, outputStr) return } // Set the health as critical c.Logger.Printf("[WARN] agent: Check '%v' is now critical", c.CheckID) c.Notify.UpdateCheck(c.CheckID, structs.HealthCritical, outputStr) }
func (p *Processor) Run() { log.Debugf("Run job[%s] by worker", p.Job) //get job script scriptBts, _, _ := p.zk.Get("/swiss/jobscript/" + strconv.Itoa(p.Job.JobId)) script := string(scriptBts) log.Debugf("Get the job script:%s", script) fullFilePath := "/Users/yws/Documents/dev/works/" + strconv.Itoa(p.Job.ExeId) log.Debugf("Job script full path:%s", fullFilePath) //write to file fout, err := os.Create(fullFilePath) if err != nil { log.Errorf("Job script full path,%s,%s", fullFilePath, err) return } fout.Write(scriptBts) fout.Close() defer func() { os.Remove(fullFilePath) }() //end write file //设置超时提醒 slowTimer := time.AfterFunc(2*time.Hour, func() { log.Warningf("proc: Script '%s' slow, execution exceeding %v", script, 2*time.Hour) }) //防止job的log太多,限制为256k, 当超过256k后,最早出现的日志将会逐步被代替 output, _ := circbuf.NewBuffer(256000) //封装成cmd if p.Job.ScriptType == Shell { } var shell, flag string switch p.Job.ScriptType { case Shell: if runtime.GOOS == "windows" { shell = "cmd" flag = "" } else { shell = "/bin/sh" flag = "" } case Hive: shell = "hive" flag = "-f" case Python: shell = "python" flag = "-f" } cmd := exec.Command(shell, fullFilePath) cmd.Stderr = output cmd.Stdout = output //运行job var success, done bool log.Debugf("Start the cmd, Shell:%s, flag:%s", shell, flag) cmd.Start() go func() { err = cmd.Wait() slowTimer.Stop() if err != nil { log.Errorf("proc: command error output: %s", err) success = false } else { success = true } done = true }() for !done { log.Info(output.String()) time.Sleep(1 * time.Second) } log.Info("Job run completed, and success status is -> ", success) //execution.FinishedAt = time.Now() //execution.Success = success //execution.Output = output.Bytes() }
// check is invoked periodically to perform the script check func (c *CheckMonitor) check() { // Disable child process reaping so that we can get this command's // return value. Note that we take the read lock here since we are // waiting on a specific PID and don't need to serialize all waits. c.ReapLock.RLock() defer c.ReapLock.RUnlock() // Create the command cmd, err := ExecScript(c.Script) if err != nil { c.Logger.Printf("[ERR] agent: failed to setup invoke '%s': %s", c.Script, err) c.Notify.UpdateCheck(c.CheckID, structs.HealthCritical, err.Error()) return } // Collect the output output, _ := circbuf.NewBuffer(CheckBufSize) cmd.Stdout = output cmd.Stderr = output // Start the check if err := cmd.Start(); err != nil { c.Logger.Printf("[ERR] agent: failed to invoke '%s': %s", c.Script, err) c.Notify.UpdateCheck(c.CheckID, structs.HealthCritical, err.Error()) return } // Wait for the check to complete errCh := make(chan error, 2) go func() { errCh <- cmd.Wait() }() go func() { if c.Timeout > 0 { time.Sleep(c.Timeout) } else { time.Sleep(30 * time.Second) } errCh <- fmt.Errorf("Timed out running check '%s'", c.Script) }() err = <-errCh // Get the output, add a message about truncation outputStr := string(output.Bytes()) if output.TotalWritten() > output.Size() { outputStr = fmt.Sprintf("Captured %d of %d bytes\n...\n%s", output.Size(), output.TotalWritten(), outputStr) } c.Logger.Printf("[DEBUG] agent: check '%s' script '%s' output: %s", c.CheckID, c.Script, outputStr) // Check if the check passed if err == nil { c.Logger.Printf("[DEBUG] agent: Check '%v' is passing", c.CheckID) c.Notify.UpdateCheck(c.CheckID, structs.HealthPassing, outputStr) return } // If the exit code is 1, set check as warning exitErr, ok := err.(*exec.ExitError) if ok { if status, ok := exitErr.Sys().(syscall.WaitStatus); ok { code := status.ExitStatus() if code == 1 { c.Logger.Printf("[WARN] agent: Check '%v' is now warning", c.CheckID) c.Notify.UpdateCheck(c.CheckID, structs.HealthWarning, outputStr) return } } } // Set the health as critical c.Logger.Printf("[WARN] agent: Check '%v' is now critical", c.CheckID) c.Notify.UpdateCheck(c.CheckID, structs.HealthCritical, outputStr) }
// invokeEventScript will execute the given event script with the given // event. Depending on the event, the semantics of how data are passed // are a bit different. For all events, the SERF_EVENT environmental // variable is the type of the event. For user events, the SERF_USER_EVENT // environmental variable is also set, containing the name of the user // event that was fired. // // In all events, data is passed in via stdin to faciliate piping. See // the various stdin functions below for more information. func invokeEventScript(logger *log.Logger, script string, self serf.Member, event serf.Event) error { defer metrics.MeasureSince([]string{"agent", "invoke", script}, time.Now()) output, _ := circbuf.NewBuffer(maxBufSize) // Determine the shell invocation based on OS var shell, flag string if runtime.GOOS == windows { shell = "cmd" flag = "/C" } else { shell = "/bin/sh" flag = "-c" } cmd := exec.Command(shell, flag, script) cmd.Env = append(os.Environ(), "SERF_EVENT="+event.EventType().String(), "SERF_SELF_NAME="+self.Name, "SERF_SELF_IP="+self.Addr.String(), "SERF_SELF_PORT="+fmt.Sprintf("%v", self.Port), "SERF_SELF_ROLE="+self.Tags["role"], ) cmd.Stderr = output cmd.Stdout = output // Add all the tags for name, val := range self.Tags { //http://stackoverflow.com/questions/2821043/allowed-characters-in-linux-environment-variable-names //(http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap08.html for the long version) //says that env var names must be in [A-Z0-9_] and not start with [0-9]. //we only care about the first part, so convert all chars not in [A-Z0-9_] to _ sanitizedName := sanitizeTagRegexp.ReplaceAllString(strings.ToUpper(name), "_") tag_env := fmt.Sprintf("SERF_TAG_%s=%s", sanitizedName, val) cmd.Env = append(cmd.Env, tag_env) } stdin, err := cmd.StdinPipe() if err != nil { return err } switch e := event.(type) { case serf.MemberEvent: go memberEventStdin(logger, stdin, &e) case serf.UserEvent: cmd.Env = append(cmd.Env, "SERF_USER_EVENT="+e.Name) cmd.Env = append(cmd.Env, fmt.Sprintf("SERF_USER_LTIME=%d", e.LTime)) go streamPayload(logger, stdin, e.Payload) case *serf.Query: cmd.Env = append(cmd.Env, "SERF_QUERY_NAME="+e.Name) cmd.Env = append(cmd.Env, fmt.Sprintf("SERF_QUERY_LTIME=%d", e.LTime)) go streamPayload(logger, stdin, e.Payload) default: return fmt.Errorf("Unknown event type: %s", event.EventType().String()) } // Start a timer to warn about slow handlers slowTimer := time.AfterFunc(warnSlow, func() { logger.Printf("[WARN] agent: Script '%s' slow, execution exceeding %v", script, warnSlow) }) if err := cmd.Start(); err != nil { return err } // Warn if buffer is overritten if output.TotalWritten() > output.Size() { logger.Printf("[WARN] agent: Script '%s' generated %d bytes of output, truncated to %d", script, output.TotalWritten(), output.Size()) } err = cmd.Wait() slowTimer.Stop() logger.Printf("[DEBUG] agent: Event '%s' script output: %s", event.EventType().String(), output.String()) if err != nil { return err } // If this is a query and we have output, respond if query, ok := event.(*serf.Query); ok && output.TotalWritten() > 0 { if err := query.Respond(output.Bytes()); err != nil { logger.Printf("[WARN] agent: Failed to respond to query '%s': %s", event.String(), err) } } return nil }