6
1
mirror of https://git.mills.io/saltyim/saltyim.git synced 2024-06-25 16:28:20 +00:00
prologic-saltyim/utils.go
2022-03-20 09:35:47 +10:00

147 lines
3.1 KiB
Go

package saltyim
import (
"bytes"
"context"
"fmt"
"html/template"
"io"
"net/http"
"os"
"os/exec"
"syscall"
"time"
"github.com/Masterminds/sprig/v3"
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").Funcs(sprig.FuncMap()).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, args ...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...)
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()}
}
}
log.
WithError(err).
WithField("out", string(out)).
Errorf("error running command")
return err
}
return nil
}