234 lines
4.6 KiB
Go
234 lines
4.6 KiB
Go
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
|
|
}
|