package saltyim import ( "bytes" "context" "fmt" "io" "net/http" "os" "os/exec" "syscall" "text/template" "time" log "github.com/sirupsen/logrus" ) const ( defaultRequestTimeout = time.Second * 5 ) func MustRenderString(s string, ctx interface{}) string { out, err := RenderString(s, ctx) if err != nil { log.WithError(err).Fatal("error rendering string template") } return out } // DirExists returns true if the given directory exists func DirExists(name string) bool { stat, err := os.Stat(name) if err != nil { if os.IsNotExist(err) { return false } return false } return stat.IsDir() } // FileExists returns true if the given file exists func FileExists(name string) bool { if _, err := os.Stat(name); err != nil { if os.IsNotExist(err) { return false } } return true } // RenderString renders the string `s` as a `text/template` using the provided // context `ctx` as input into the template. // Typically used to render the results into a Markdown document. func RenderString(s string, ctx interface{}) (string, error) { t := template.Must(template.New("s").Parse(s)) buf := bytes.NewBuffer([]byte{}) err := t.Execute(buf, ctx) if err != nil { return "", err } return buf.String(), nil } // Request is a generic request handling function for making artbitrary HTPT // requests to Salty endpoints for looking up Salty Addresses, Configs and // publishing encrypted messages. func Request(method, uri string, headers http.Header, body io.Reader) (*http.Response, error) { req, err := http.NewRequest(method, uri, body) if err != nil { return nil, fmt.Errorf("%s: http.NewRequest fail: %s", uri, err) } if headers == nil { headers = make(http.Header) } // Set a default User-Agent (if none set) if headers.Get("User-Agent") == "" { headers.Set("User-Agent", fmt.Sprintf("saltyim/%s", FullVersion())) } req.Header = headers client := http.Client{ Timeout: defaultRequestTimeout, } res, err := client.Do(req) if err != nil { return nil, fmt.Errorf("%s: client.Do fail: %s", uri, err) } if res.StatusCode/100 != 2 { return nil, fmt.Errorf("non-2xx response received: %s", res.Status) } return res, nil } // CmdExists ... func CmdExists(cmd string) bool { _, err := exec.LookPath(cmd) return err == nil } // RunCmd ... func RunCmd(timeout time.Duration, command string, stdin io.Reader, args ...string) (string, error) { var ( ctx context.Context cancel context.CancelFunc ) if timeout > 0 { ctx, cancel = context.WithTimeout(context.Background(), timeout) } else { ctx, cancel = context.WithCancel(context.Background()) } defer cancel() cmd := exec.CommandContext(ctx, command, args...) cmd.Stdin = stdin out, err := cmd.CombinedOutput() if err != nil { if exitError, ok := err.(*exec.ExitError); ok { if ws, ok := exitError.Sys().(syscall.WaitStatus); ok && ws.Signal() == syscall.SIGKILL { err = &ErrCommandKilled{Err: err, Signal: ws.Signal()} } else { err = &ErrCommandFailed{Err: err, Status: exitError.ExitCode()} } } return "", err } return string(out), nil } // SetTerminalTitle sets the Terminal/Console's title func SetTerminalTitle(format string, args ...interface{}) { fmt.Printf("\033]0;%s\007", fmt.Sprintf(format, args...)) }