Test functions 236 (#355)
A single CLI command to invoke a function, print results, and get logs if the function fails. Meant to be an easy way to do testing from the CLI.
This commit is contained in:
@@ -17,7 +17,9 @@ limitations under the License.
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
@@ -29,6 +31,10 @@ func fatal(msg string) {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func warn(msg string) {
|
||||
os.Stderr.WriteString(msg + "\n")
|
||||
}
|
||||
|
||||
func getClient(serverUrl string) *client.Client {
|
||||
|
||||
if len(serverUrl) == 0 {
|
||||
@@ -50,3 +56,33 @@ func checkErr(err error, msg string) {
|
||||
fatal(fmt.Sprintf("Failed to %v: %v", msg, err))
|
||||
}
|
||||
}
|
||||
|
||||
func httpRequest(method, url, body string, headers []string) *http.Response {
|
||||
if method == "" {
|
||||
method = "GET"
|
||||
}
|
||||
|
||||
if method != http.MethodGet &&
|
||||
method != http.MethodDelete &&
|
||||
method != http.MethodPost &&
|
||||
method != http.MethodPut {
|
||||
fatal(fmt.Sprintf("Invalid HTTP method '%s'.", method))
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(method, url, strings.NewReader(body))
|
||||
checkErr(err, "create HTTP request")
|
||||
|
||||
for _, header := range headers {
|
||||
headerKeyValue := strings.SplitN(header, ":", 2)
|
||||
if len(headerKeyValue) != 2 {
|
||||
checkErr(errors.New(""), "create request without appropriate headers")
|
||||
}
|
||||
req.Header.Set(headerKeyValue[0], headerKeyValue[1])
|
||||
}
|
||||
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
checkErr(err, "execute HTTP request")
|
||||
|
||||
return resp
|
||||
}
|
||||
|
||||
@@ -20,9 +20,12 @@ import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
@@ -134,6 +137,34 @@ func getContents(filePath string) []byte {
|
||||
return code
|
||||
}
|
||||
|
||||
func printPodLogs(c *cli.Context) error {
|
||||
fnName := c.String("name")
|
||||
if len(fnName) == 0 {
|
||||
fatal("Need --name argument.")
|
||||
}
|
||||
|
||||
queryURL, err := url.Parse(c.GlobalString("server"))
|
||||
checkErr(err, "parse the base URL")
|
||||
queryURL.Path = fmt.Sprintf("/proxy/logs/%s", fnName)
|
||||
|
||||
req, err := http.NewRequest("POST", queryURL.String(), nil)
|
||||
checkErr(err, "create logs request")
|
||||
|
||||
httpClient := http.Client{}
|
||||
resp, err := httpClient.Do(req)
|
||||
checkErr(err, "execute get logs request")
|
||||
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return errors.New("get logs from pod directly")
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
checkErr(err, "read the response body")
|
||||
fmt.Println(string(body))
|
||||
return nil
|
||||
}
|
||||
|
||||
func fnCreate(c *cli.Context) error {
|
||||
client := getClient(c.GlobalString("server"))
|
||||
|
||||
@@ -383,6 +414,7 @@ func fnList(c *cli.Context) error {
|
||||
}
|
||||
|
||||
func fnLogs(c *cli.Context) error {
|
||||
|
||||
client := getClient(c.GlobalString("server"))
|
||||
|
||||
fnName := c.String("name")
|
||||
@@ -497,3 +529,33 @@ func fnPods(c *cli.Context) error {
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func fnTest(c *cli.Context) error {
|
||||
fnName := c.String("name")
|
||||
routerURL := os.Getenv("FISSION_ROUTER")
|
||||
if len(routerURL) == 0 {
|
||||
fatal("Need FISSION_ROUTER set to your fission router.")
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("http://%s/fission-function/%s", routerURL, fnName)
|
||||
|
||||
resp := httpRequest(c.String("method"), url, c.String("body"), c.StringSlice("header"))
|
||||
if resp.StatusCode < 400 {
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
checkErr(err, "Function test")
|
||||
fmt.Print(string(body))
|
||||
defer resp.Body.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
checkErr(err, "read log response from pod")
|
||||
fmt.Printf("Error calling function %v: %v %v", fnName, resp.StatusCode, string(body))
|
||||
defer resp.Body.Close()
|
||||
err = printPodLogs(c)
|
||||
if err != nil {
|
||||
fnLogs(c)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -47,8 +47,11 @@ func main() {
|
||||
fnFollowFlag := cli.BoolFlag{Name: "follow, f", Usage: "specify if the logs should be streamed"}
|
||||
fnDetailFlag := cli.BoolFlag{Name: "detail, d", Usage: "display detailed information"}
|
||||
fnLogDBTypeFlag := cli.StringFlag{Name: "dbtype", Usage: "log database type, e.g. influxdb (currently only influxdb is supported)"}
|
||||
fnBodyFlag := cli.StringFlag{Name: "body, b", Usage: "request body"}
|
||||
fnHeaderFlag := cli.StringSliceFlag{Name: "header, H", Usage: "request headers"}
|
||||
fnEntryPointFlag := cli.StringFlag{Name: "entrypoint", Usage: "entry point for environment v2 to load with"}
|
||||
fnBuildCmdFlag := cli.StringFlag{Name: "buildcmd", Usage: "build command for builder to run with"}
|
||||
|
||||
fnSubcommands := []cli.Command{
|
||||
{Name: "create", Usage: "Create new function (and optionally, an HTTP route to it)", Flags: []cli.Flag{fnNameFlag, fnEnvNameFlag, fnCodeFlag, fnPackageFlag, fnSrcArchiveFlag, fnDeployArchiveFlag, fnEntryPointFlag, fnBuildCmdFlag, htUrlFlag, htMethodFlag}, Action: fnCreate},
|
||||
{Name: "get", Usage: "Get function source code", Flags: []cli.Flag{fnNameFlag}, Action: fnGet},
|
||||
@@ -58,6 +61,7 @@ func main() {
|
||||
{Name: "list", Usage: "List all functions", Flags: []cli.Flag{}, Action: fnList},
|
||||
{Name: "logs", Usage: "Display function logs", Flags: []cli.Flag{fnNameFlag, fnPodFlag, fnFollowFlag, fnDetailFlag, fnLogDBTypeFlag}, Action: fnLogs},
|
||||
{Name: "pods", Usage: "Display function pods", Flags: []cli.Flag{fnNameFlag, fnLogDBTypeFlag}, Action: fnPods},
|
||||
{Name: "test", Usage: "Test a function", Flags: []cli.Flag{fnNameFlag, fnEnvNameFlag, fnCodeFlag, fnPackageFlag, fnSrcArchiveFlag, htMethodFlag, fnBodyFlag, fnHeaderFlag}, Action: fnTest},
|
||||
}
|
||||
|
||||
// httptriggers
|
||||
|
||||
Reference in New Issue
Block a user