From 3602dc71f25c36614f7067ed43d5653c718097dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roberto=20Abdelkader=20Mart=C3=ADnez=20P=C3=A9rez?= Date: Wed, 2 Oct 2019 15:49:16 +0200 Subject: [PATCH] Code imported from @CesarGallego private repo --- client/get.go | 30 ++++++++++++++++++ client/get_test.go | 66 ++++++++++++++++++++++++++++++++++++++++ client/route_add.go | 31 +++++++++++++++++++ client/route_add_test.go | 11 +++++++ command/get.go | 34 +++++++++++++++++++++ command/route.go | 52 +++++++++++++++++++++++++++++++ command/server.go | 38 +++++++++++++++++++++++ command/set.go | 26 ++++++++++++++++ command/validations.go | 25 +++++++++++++++ main.go | 37 +++++++++------------- 10 files changed, 328 insertions(+), 22 deletions(-) create mode 100644 client/get.go create mode 100644 client/get_test.go create mode 100644 client/route_add.go create mode 100644 client/route_add_test.go create mode 100644 command/get.go create mode 100644 command/route.go create mode 100644 command/server.go create mode 100644 command/set.go create mode 100644 command/validations.go diff --git a/client/get.go b/client/get.go new file mode 100644 index 0000000..f89989a --- /dev/null +++ b/client/get.go @@ -0,0 +1,30 @@ +package client + +import ( + "errors" + "io" + "net/http" + "strings" +) + +func getReason(r *http.Response) string { + return strings.Join(strings.Split(r.Status, " ")[1:], " ") +} + +//GetData will perform the request and write the results on the provided writer +func GetData(host, id, path string, wr io.Writer) error { + url := host + "/handlers/" + id + path + + res, err := http.Get(url) + if err != nil { + return err + } + defer res.Body.Close() + + if res.StatusCode < 200 || res.StatusCode >= 300 { + return errors.New(getReason(res)) + } + + _, err = io.Copy(wr, res.Body) + return err +} diff --git a/client/get_test.go b/client/get_test.go new file mode 100644 index 0000000..80be4af --- /dev/null +++ b/client/get_test.go @@ -0,0 +1,66 @@ +package client + +import ( + "bytes" + "net/http" + "os" + "testing" + + gock "gopkg.in/h2non/gock.v1" +) + +func TestGetInvalidUrl(t *testing.T) { + err := GetData("", "", "", os.Stdout) + if err == nil { + t.Error("Expected error with invalid url ''") + } +} + +func TestGetInvalidWriter(t *testing.T) { + err := GetData("http://localhost:8081", "0000", "/", nil) + if err == nil { + t.Error("Expected error with no writer") + } +} + +func TestGetURLNotFoundWithUnknownID(t *testing.T) { + defer gock.Off() + + gock.New("http://localhost:8081"). + Get("/handlers/000/").Reply(http.StatusNotFound) + + err := GetData("http://localhost:8081", "000", "/", os.Stdout) + + if err == nil { + t.Errorf("Expect not found error but get no error") + } + + if gock.IsDone() == false { + t.Error("No expected endpoint called") + } +} + +func TestGetRetrieveRequestMethod(t *testing.T) { + defer gock.Off() + + gock.New("http://localhost:8081"). + Get("/handlers/000/request/method"). + Reply(http.StatusAccepted). + BodyString("POST") + + rw := new(bytes.Buffer) + + err := GetData("http://localhost:8081", "000", "/request/method", rw) + if err != nil { + t.Errorf("Unexpected error %v", err) + } + strRes := rw.String() + + if strRes != "POST" { + t.Errorf("POST string expected but found: '%v'", strRes) + } + + if gock.IsDone() == false { + t.Error("No expected endpoint called") + } +} diff --git a/client/route_add.go b/client/route_add.go new file mode 100644 index 0000000..b9807a4 --- /dev/null +++ b/client/route_add.go @@ -0,0 +1,31 @@ +package client + +import ( + "errors" + "net/http" + "strings" +) + +// AddRoute will add a new route in kapow +func AddRoute(host, path, method, entrypoint, command string) error { + reqData, err := http.NewRequest( + "PUT", + host+"/routes", + strings.NewReader(command), + ) + if err != nil { + return err + } + + var client = new(http.Client) + res, err := client.Do(reqData) + if err != nil { + return err + } + + if res.StatusCode < 200 || res.StatusCode >= 300 { + return errors.New(res.Status) + } + + return nil +} diff --git a/client/route_add_test.go b/client/route_add_test.go new file mode 100644 index 0000000..d691af8 --- /dev/null +++ b/client/route_add_test.go @@ -0,0 +1,11 @@ +package client + +import "testing" + +func TestInvalidURL(t *testing.T) { + err := AddRoute("http://localhost;8080", "/hi", "GET", "bash -c", "echo 'Hi' | kapow set /response/body") + if err == nil { + t.Error("expect to fail due invalid url") + t.Fail() + } +} diff --git a/command/get.go b/command/get.go new file mode 100644 index 0000000..a480e2d --- /dev/null +++ b/command/get.go @@ -0,0 +1,34 @@ +package command + +import ( + "fmt" + "os" + + "github.com/spf13/cobra" + + "github.com/BBVA/kapow/client" +) + +// GetCmd is the command line interface for get kapow data operation +var GetCmd = &cobra.Command{ + Use: "get [flags] resource", + Short: "Retrive a Kapow! resource", + Long: "Retrive a Kapow! resource for the current request", + Args: cobra.ExactArgs(1), + PreRunE: handlerIDRequired, + Run: func(cmd *cobra.Command, args []string) { + url, _ := cmd.Flags().GetString("url") + handler, _ := cmd.Flags().GetString("handler") + + err := client.GetData(url, handler, args[0], os.Stdout) + if err != nil { + os.Stderr.WriteString(fmt.Sprintf("%v\n", err)) + os.Exit(1) + } + }, +} + +func init() { + GetCmd.Flags().String("url", getEnv("KAPOW_URL", "http://localhost:8082"), "Kapow! data interface URL") + GetCmd.Flags().String("handler", getEnv("KAPOW_HANDLER_ID", ""), "Kapow! handler id") +} diff --git a/command/route.go b/command/route.go new file mode 100644 index 0000000..11b1b26 --- /dev/null +++ b/command/route.go @@ -0,0 +1,52 @@ +package command + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +//RouteCmd is the command line interface for kapow route manipulation +var RouteCmd = &cobra.Command{ + Use: "route [action]", +} + +func init() { + var routeListCmd = &cobra.Command{ + Use: "list [flags]", + Short: "List the current Kapow! routes", + Run: func(cmd *cobra.Command, args []string) { + url, _ := cmd.Flags().GetString("url") + fmt.Println("niano: ", url) + }, + } + routeListCmd.Flags().String("url", getEnv("KAPOW_URL", "http://localhost:8082"), "Kapow! data interface URL") + + var routeAddCmd = &cobra.Command{ + Use: "add [flags] url_pattern [command_file]", + Short: "Add a route", + Run: func(cmd *cobra.Command, args []string) { + url, _ := cmd.Flags().GetString("url") + fmt.Println("niano: ", url) + }, + } + routeAddCmd.Flags().String("url", getEnv("KAPOW_URL", "http://localhost:8082"), "Kapow! data interface URL") + routeAddCmd.Flags().StringP("command", "c", "", "Command to pass to the shell") + routeAddCmd.Flags().StringP("entrypoint", "e", "", "Command to execute") + routeAddCmd.Flags().StringP("method", "X", "", "HTTP method to accept") + + var routeRemoveCmd = &cobra.Command{ + Use: "remove [flags] route_id", + Short: "Remove the given route", + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + url, _ := cmd.Flags().GetString("url") + fmt.Println("niano: ", url) + }, + } + routeRemoveCmd.Flags().String("url", getEnv("KAPOW_URL", "http://localhost:8082"), "Kapow! data interface URL") + + RouteCmd.AddCommand(routeListCmd) + RouteCmd.AddCommand(routeAddCmd) + RouteCmd.AddCommand(routeRemoveCmd) +} diff --git a/command/server.go b/command/server.go new file mode 100644 index 0000000..61f8061 --- /dev/null +++ b/command/server.go @@ -0,0 +1,38 @@ +package command + +import ( + "errors" + "fmt" + + "github.com/spf13/cobra" +) + +// ServerCmd is the command line interface for kapow server +var ServerCmd = &cobra.Command{ + Use: "server [optional flags] [optional pow file(s)]", + Short: "Start a kapow server", + Long: `Start a Kapow server with, by default with client interface, data interface + and admin interface`, + PreRunE: validateServerCommandArguments, + Run: func(cmd *cobra.Command, args []string) { + cert, _ := cmd.Flags().GetString("certfile") + key, _ := cmd.Flags().GetString("keyfile") + fmt.Println("waka server feliz :)", cert, key) + }, +} + +func init() { + ServerCmd.Flags().String("certfile", "", "Cert file to serve thru https") + ServerCmd.Flags().String("keyfile", "", "Key file to serve thru https") + ServerCmd.Flags().String("bind", "", "IP address and port to listen to") + ServerCmd.Flags().BoolP("interactive", "i", false, "Boot an empty kapow server with a shell") +} + +func validateServerCommandArguments(cmd *cobra.Command, args []string) error { + cert, _ := cmd.Flags().GetString("certfile") + key, _ := cmd.Flags().GetString("keyfile") + if (cert == "") != (key == "") { + return errors.New("expected both or neither (certfile and keyfile)") + } + return nil +} diff --git a/command/set.go b/command/set.go new file mode 100644 index 0000000..0bade4f --- /dev/null +++ b/command/set.go @@ -0,0 +1,26 @@ +package command + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +//SetCmd is the command line interface for set kapow data operation +var SetCmd = &cobra.Command{ + Use: "set [flags] resource [value]", + Short: "Set a Kapow! resource value", + Long: "Set a Kapow! resource value for the current request", + Args: cobra.RangeArgs(1, 2), + PreRunE: handlerIDRequired, + Run: func(cmd *cobra.Command, args []string) { + url, _ := cmd.Flags().GetString("url") + handler, _ := cmd.Flags().GetString("handler") + fmt.Println("niano: ", url, handler) + }, +} + +func init() { + SetCmd.Flags().String("url", getEnv("KAPOW_URL", "http://localhost:8082"), "Kapow! data interface URL") + SetCmd.Flags().String("handler", getEnv("KAPOW_HANDLER_ID", ""), "Kapow! handler id") +} diff --git a/command/validations.go b/command/validations.go new file mode 100644 index 0000000..353cc52 --- /dev/null +++ b/command/validations.go @@ -0,0 +1,25 @@ +package command + +import ( + "errors" + "os" + + "github.com/spf13/cobra" +) + +func getEnv(key, fallback string) string { + value, exists := os.LookupEnv(key) + if !exists { + return fallback + } + return value +} + +func handlerIDRequired(cmd *cobra.Command, args []string) error { + handler, _ := cmd.Flags().GetString("handler") + if handler == "" { + return errors.New("--handler or KAPOW_HANDLER_ID is mandatory") + } + return nil + +} diff --git a/main.go b/main.go index 80ddc50..eea713f 100644 --- a/main.go +++ b/main.go @@ -2,31 +2,24 @@ package main import ( "fmt" - b "github.com/BBVA/kapow/pkg/banner" - "net/http" + "os" + + "github.com/spf13/cobra" + + "github.com/BBVA/kapow/command" ) func main() { - ban := b.Banner("0.1.0") - fmt.Println(ban) + var kapowCmd = &cobra.Command{Use: "kapow [action]"} - go func() { - http.ListenAndServe(":8080", &userServerHandler{}) - }() - http.ListenAndServe(":8081", &controlAPIHandler{}) + kapowCmd.AddCommand(command.ServerCmd) + kapowCmd.AddCommand(command.GetCmd) + kapowCmd.AddCommand(command.SetCmd) + kapowCmd.AddCommand(command.RouteCmd) -} - -type userServerHandler struct { -} - -func (m *userServerHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusNotImplemented) -} - -type controlAPIHandler struct { -} - -func (m *controlAPIHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusNotImplemented) + err := kapowCmd.Execute() + if err != nil { + fmt.Println(err) + os.Exit(1) + } }