functionbuilder
This commit is contained in:
279
build/app/restapi/operations/direktiv_helper.go
Normal file
279
build/app/restapi/operations/direktiv_helper.go
Normal file
@@ -0,0 +1,279 @@
|
||||
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
|
||||
|
||||
}
|
Reference in New Issue
Block a user