Files
export-xlsx/build/app/restapi/operations/direktiv_helper.go
Nathan Coad 54093c9106
All checks were successful
continuous-integration/drone/push Build is passing
functionbuilder
2023-02-10 16:15:36 +11:00

280 lines
5.1 KiB
Go

package operations
import (
"bytes"
"context"
"crypto/tls"
"encoding/base64"
"encoding/json"
"fmt"
"html"
"html/template"
"io"
"net/http"
"net/http/cookiejar"
"net/url"
"os"
"os/exec"
"strconv"
"strings"
"github.com/Masterminds/sprig"
"github.com/direktiv/apps/go/pkg/apps"
"github.com/mattn/go-shellwords"
"golang.org/x/net/publicsuffix"
)
func fileExists(file string) bool {
_, err := os.Open(file)
if err != nil {
return false
}
return true
}
func file64(path string) string {
b, err := os.ReadFile(path)
if err != nil {
return err.Error()
}
return base64.StdEncoding.EncodeToString(b)
}
func deref(dd interface{}) interface{} {
switch p := dd.(type) {
case *string:
return *p
case *int:
return *p
default:
return p
}
}
func templateString(tmplIn string, data interface{}) (string, error) {
tmpl, err := template.New("base").Funcs(sprig.FuncMap()).Funcs(template.FuncMap{
"fileExists": fileExists,
"deref": deref,
"file64": file64,
}).Parse(tmplIn)
if err != nil {
return "", err
}
var b bytes.Buffer
err = tmpl.Execute(&b, data)
if err != nil {
return "", err
}
v := b.String()
if v == "<no value>" {
v = ""
}
return html.UnescapeString(v), nil
}
func convertTemplateToBool(template string, data interface{}, defaultValue bool) bool {
out, err := templateString(template, data)
if err != nil {
return defaultValue
}
ins, err := strconv.ParseBool(out)
if err != nil {
return defaultValue
}
return ins
}
func runCmd(ctx context.Context, cmdString string, envs []string,
output string, silent, print bool, ri *apps.RequestInfo) (map[string]interface{}, error) {
ir := make(map[string]interface{})
ir[successKey] = false
a, err := shellwords.Parse(cmdString)
if err != nil {
ir[resultKey] = err.Error()
return ir, err
}
if len(a) == 0 {
return ir, fmt.Errorf("command '%v' parsed to empty array", cmdString)
}
// get the binary and args
bin := a[0]
argsIn := []string{}
if len(a) > 1 {
argsIn = a[1:]
}
logger := io.Discard
stdo := io.Discard
if !silent {
logger = ri.LogWriter()
stdo = os.Stdout
}
var o bytes.Buffer
var oerr bytes.Buffer
mwStdout := io.MultiWriter(stdo, &o, logger)
mwStdErr := io.MultiWriter(os.Stdout, &oerr, logger)
cmd := exec.CommandContext(ctx, bin, argsIn...)
cmd.Stdout = mwStdout
cmd.Stderr = mwStdErr
cmd.Dir = ri.Dir()
// change HOME
curEnvs := append(os.Environ(), fmt.Sprintf("HOME=%s", ri.Dir()))
cmd.Env = append(curEnvs, envs...)
if print {
ri.Logger().Infof("running command %v", cmd)
}
err = cmd.Run()
if err != nil {
ir[resultKey] = string(oerr.String())
if oerr.String() == "" {
ir[resultKey] = err.Error()
} else {
ri.Logger().Errorf(oerr.String())
err = fmt.Errorf(oerr.String())
}
return ir, err
}
// successful here
ir[successKey] = true
// output check
b := o.Bytes()
if output != "" {
b, err = os.ReadFile(output)
if err != nil {
ir[resultKey] = err.Error()
return ir, err
}
}
var rj interface{}
err = json.Unmarshal(b, &rj)
if err != nil {
rj = apps.ToJSON(string(b))
}
ir[resultKey] = rj
return ir, nil
}
func doHttpRequest(debug bool, method, u, user, pwd string,
headers map[string]string, insecure, errNo200 bool,
data []byte) (map[string]interface{}, error) {
ir := make(map[string]interface{})
ir[successKey] = false
urlParsed, err := url.Parse(u)
if err != nil {
ir[resultKey] = err.Error()
return ir, err
}
v, err := url.ParseQuery(urlParsed.RawQuery)
if err != nil {
ir[resultKey] = err.Error()
return ir, err
}
urlParsed.RawQuery = v.Encode()
req, err := http.NewRequest(strings.ToUpper(method), urlParsed.String(), bytes.NewReader(data))
if err != nil {
ir[resultKey] = err.Error()
return ir, err
}
req.Close = true
for k, v := range headers {
req.Header.Add(k, v)
}
if user != "" {
req.SetBasicAuth(user, pwd)
}
jar, err := cookiejar.New(&cookiejar.Options{
PublicSuffixList: publicsuffix.List,
})
cr := http.DefaultTransport.(*http.Transport).Clone()
cr.TLSClientConfig = &tls.Config{
InsecureSkipVerify: insecure,
}
client := &http.Client{
Jar: jar,
Transport: cr,
}
if debug {
fmt.Printf("method: %s, insecure: %v\n", method, insecure)
fmt.Printf("url: %s\n", req.URL.String())
fmt.Println("Headers:")
for k, v := range headers {
fmt.Printf("%v = %v\n", k, v)
}
}
resp, err := client.Do(req)
if err != nil {
ir[resultKey] = err.Error()
return ir, err
}
defer resp.Body.Close()
b, err := io.ReadAll(resp.Body)
if err != nil {
ir[resultKey] = err.Error()
return ir, err
}
if errNo200 && (resp.StatusCode < 200 || resp.StatusCode > 299) {
err = fmt.Errorf("response status is not between 200-299: %v (%v)", resp.StatusCode, resp.Status)
ir[resultKey] = err.Error()
return ir, err
}
// from here on it is successful
ir[successKey] = true
ir[statusKey] = resp.Status
ir[codeKey] = resp.StatusCode
ir[headersKey] = resp.Header
var rj interface{}
err = json.Unmarshal(b, &rj)
ir[resultKey] = rj
// if the response is not json, base64 the result
if err != nil {
ir[resultKey] = base64.StdEncoding.EncodeToString(b)
}
return ir, nil
}