Rework directory structure

Co-authored-by: Héctor Hurtado <hector.hurtado@bbva.com>
This commit is contained in:
pancho horrillo
2019-10-03 12:05:53 +02:00
parent 8b7d1d69e7
commit 23128026c7
12 changed files with 12 additions and 12 deletions
+30
View File
@@ -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
}
+66
View File
@@ -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")
}
}
+31
View File
@@ -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
}
+11
View File
@@ -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()
}
}
+36
View File
@@ -0,0 +1,36 @@
package client
import (
"fmt"
"io"
"net/http"
)
const (
errMandatoryParam = "Mandatory parameter %s missing"
errInvalidURL = "kapowURL, handlerId or path has invalid format"
errNotFound = "Resource Item Not Found"
errNotValidResource = "Invalid Resource Path"
serverURLTemplate = "%s/%s%s"
)
func SetData(kapowURL, handlerId, path string, r io.Reader) error {
req, err := http.NewRequest("PUT", fmt.Sprintf(serverURLTemplate, kapowURL, handlerId, path), r)
if err != nil {
return err
}
kpowClient := &http.Client{}
if resp, err := kpowClient.Do(req); err != nil {
return err
} else if resp.StatusCode == http.StatusNoContent {
return fmt.Errorf(errNotFound)
} else if resp.StatusCode == http.StatusBadRequest {
return fmt.Errorf(errNotValidResource)
} else if resp.StatusCode >= http.StatusNotFound {
return fmt.Errorf(resp.Status[4:])
}
return nil
}
+123
View File
@@ -0,0 +1,123 @@
package client_test
import (
"net/http"
"strings"
"testing"
"github.com/BBVA/kapow/internal/client"
gock "gopkg.in/h2non/gock.v1"
)
// Test that no content errors are detected as non-existent resource
func TestNoContent(t *testing.T) {
expectedErr := "Resource Item Not Found"
host := "http://localhost:8080"
hid := "xxxxxxxxxxxxxx"
path := "/unpath"
reader := strings.NewReader("Esto es un peacho de dato pa repartir")
defer gock.Off()
gock.New(host).Put("/" + hid + path).Reply(http.StatusNoContent)
if err := client.SetData(host, hid, path, reader); err == nil {
t.Error("Expected error not present")
} else if err.Error() != expectedErr {
t.Errorf("Error don't match: expected \"%s\", got \"%s\"", expectedErr, err.Error())
}
if !gock.IsDone() {
t.Errorf("No endpoint called")
}
}
// Test that bad request errors are detected as invalid resource
func TestBadRequest(t *testing.T) {
expectedErr := "Invalid Resource Path"
host := "http://localhost:8080"
hid := "xxxxxxxxxxxxxx"
path := "/unpath"
reader := strings.NewReader("Esto es un peacho de dato pa repartir")
defer gock.Off()
gock.New(host).Put("/" + hid + path).Reply(http.StatusBadRequest)
if err := client.SetData(host, hid, path, reader); err == nil {
t.Error("Expected error not present")
} else if err.Error() != expectedErr {
t.Errorf("Error don't match: expected \"%s\", got \"%s\"", expectedErr, err.Error())
}
if !gock.IsDone() {
t.Errorf("No endpoint called")
}
}
// Test that not found errors are detected as invalid handler id
func TestNotFound(t *testing.T) {
expectedErr := "Not Found"
host := "http://localhost:8080"
hid := "xxxxxxxxxxxxxx"
path := "/unpath"
reader := strings.NewReader("Esto es un peacho de dato pa repartir")
defer gock.Off()
gock.New(host).Put("/" + hid + path).Reply(http.StatusNotFound)
if err := client.SetData(host, hid, path, reader); err == nil {
t.Error("Expected error not present")
} else if err.Error() != expectedErr {
t.Errorf("Error don't match: expected \"%s\", got \"%s\"", expectedErr, err.Error())
}
if !gock.IsDone() {
t.Errorf("No endpoint called")
}
}
// Test that internal server errors are detected correctly
func TestInternalServerError(t *testing.T) {
expectedErr := "Internal Server Error"
host := "http://localhost:8080"
hid := "xxxxxxxxxxxxxx"
path := "/unpath"
reader := strings.NewReader("Esto es un peacho de dato pa repartir")
defer gock.Off()
gock.New(host).Put("/" + hid + path).Reply(http.StatusInternalServerError)
if err := client.SetData(host, hid, path, reader); err == nil {
t.Error("Expected error not present")
} else if err.Error() != expectedErr {
t.Errorf("Error don't match: expected \"%s\", got \"%s\"", expectedErr, err.Error())
}
if !gock.IsDone() {
t.Errorf("No endpoint called")
}
}
// Test a http ok request
func TestOkRequest(t *testing.T) {
host := "http://localhost:8080"
hid := "xxxxxxxxxxxxxx"
path := "/response/status/code"
reader := strings.NewReader("200")
defer gock.Off()
gock.New(host).Put("/" + hid + path).Reply(http.StatusOK)
if err := client.SetData(host, hid, path, reader); err != nil {
t.Error("Unexpected error")
}
if !gock.IsDone() {
t.Errorf("No endpoint called")
}
}
+34
View File
@@ -0,0 +1,34 @@
package cmd
import (
"fmt"
"os"
"github.com/spf13/cobra"
"github.com/BBVA/kapow/internal/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")
}
+52
View File
@@ -0,0 +1,52 @@
package cmd
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)
}
+38
View File
@@ -0,0 +1,38 @@
package cmd
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
}
+26
View File
@@ -0,0 +1,26 @@
package cmd
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")
}
+25
View File
@@ -0,0 +1,25 @@
package cmd
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
}