package rules import ( "bytes" "encoding/json" "errors" "fmt" "iwarma.ru/console/correlator/events" "os/exec" "strings" "text/template" log "github.com/sirupsen/logrus" ) const ( ExecActionType = "exec" ) type ExecAction struct { Path string Args string Env string Cwd string templates struct { path *template.Template args *template.Template env *template.Template cwd *template.Template } } func (action *ExecAction) GetType() string { return ExecActionType } func (action ExecAction) ToInterface() (map[string]interface{}, error) { result := make(map[string]interface{}) result["type"] = ExecActionType result["path"] = action.Path result["args"] = action.Args result["env"] = action.Env result["cwd"] = action.Cwd return result, nil } func (action ExecAction) MarshalJSON() ([]byte, error) { data, err := action.ToInterface() if err != nil { return nil, err } return json.Marshal(data) } func (action *ExecAction) UnmarshalJSON(b []byte) error { cur := struct { CurType string `json:"type"` Path string `json:"path"` Args string `json:"args"` Env string `json:"env"` Cwd string `json:"cwd"` }{} err := json.Unmarshal(b, &cur) if err != nil { return err } if cur.CurType != ExecActionType { return fmt.Errorf("bad action type, Expect %v, got %v", ExecActionType, cur.CurType) } action.Path = cur.Path action.Args = cur.Args action.Env = cur.Env action.Cwd = cur.Cwd return nil } func (action *ExecAction) ParseInterface(v interface{}) error { m, ok := v.(map[string]interface{}) if !ok { return fmt.Errorf("can't parse %v from %T", v, v) } t, ok := m["type"].(string) if !ok { return errors.New("no type") } if t != ExecActionType { return fmt.Errorf("bad type, expect %v got %v", ExecActionType, t) } path, ok := m["path"].(string) if !ok { return errors.New("no path") } action.Path = path // Args isn't not required args, ok := m["args"].(string) if ok { action.Args = args } // Env isn't required env, ok := m["env"].(string) if ok { action.Env = env } // Cwd isn't required cwd, ok := m["cwd"].(string) if ok { action.Cwd = cwd } return nil } func (action *ExecAction) Perform(events *[]*events.Event) error { cl := log.WithFields(log.Fields{"type": ExecActionType, "event_count": len(*events)}) cl.Debug("Start action") defer cl.Debug("End action") var err error // Check if we need to prepare templates if action.templates.path == nil { action.templates.path, err = template.New("Path").Parse(action.Path) if err != nil { cl.Errorf("Can't create path template: %v", err) return err } action.templates.args, err = template.New("Args").Parse(action.Args) if err != nil { cl.Errorf("Can't create args template: %v", err) return err } action.templates.env, err = template.New("Env").Parse(action.Env) if err != nil { cl.Errorf("Can't create env template: %v", err) return err } action.templates.cwd, err = template.New("Cwd").Parse(action.Cwd) if err != nil { cl.Errorf("Can't create cwd template: %v", err) return err } } // Actual action for _, event := range *events { // Render templates path, err := renderTemplate(action.templates.path, event) if err != nil { cl.Errorf("Can't render path: %v", err) return err } args, err := renderTemplate(action.templates.args, event) if err != nil { cl.Errorf("Can't render args: %v", err) return err } env, err := renderTemplate(action.templates.env, event) if err != nil { cl.Errorf("Can't render env: %v", err) return err } cwd, err := renderTemplate(action.templates.cwd, event) if err != nil { cl.Errorf("Can't render cwd: %v", err) return err } // Prepare command var buf bytes.Buffer fullArgs := make([]string, 1) fullArgs[0] = path for _, cur := range strings.Split(args, " ") { fullArgs = append(fullArgs, cur) } cmd := &exec.Cmd{ Path: path, Args: fullArgs, Env: strings.Split(env, " "), Dir: cwd, Stdout: &buf, Stderr: &buf, } debLog := log.WithFields(log.Fields{ "type": ExecActionType, "event": event.GetString("event_id"), "path": cmd.Path, "args": cmd.Args, "env": cmd.Env, "cwd": cmd.Dir, }) debLog.Debugf("Starting command") // TODO: Maybe set to goroutine err = cmd.Start() if err != nil { cl.Errorf("Script execution error: %v", err) debLog.Debugf("Executabe output:\n%v", buf.String()) return err } err = cmd.Wait() if err != nil { cl.Errorf("Error while waiting script finish: %v", err) debLog.Debugf("Executabe output:\n%v", buf.String()) return err } debLog.Debugf("Executabe output:\n%v", buf.String()) } return nil }