From 37721e6e2575df578ea7f223b89e25144f966641 Mon Sep 17 00:00:00 2001 From: Gowtham Sai Date: Tue, 5 Mar 2019 00:32:54 +0530 Subject: [PATCH 01/26] WIP: list acls cli option --- cmd/weaver-server/main.go | 77 ++++++++++++++++++++++++++++++++++ cmd/weaver-server/main_test.go | 8 ++++ etcd/routeloader.go | 28 +++++++++---- 3 files changed, 106 insertions(+), 7 deletions(-) create mode 100644 cmd/weaver-server/main_test.go diff --git a/cmd/weaver-server/main.go b/cmd/weaver-server/main.go index 2d7c3ef..89bc1e9 100644 --- a/cmd/weaver-server/main.go +++ b/cmd/weaver-server/main.go @@ -29,11 +29,88 @@ func main() { Description: "Start weaver server", Action: startWeaver, }, + { + Name: "acls", + Aliases: []string{"a"}, + Description: "List, Create, Delete, Update ACLs", + Usage: "Perform list, create, update, delete acls", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "etcd-host, etcd", + Value: "http://localhost:2379", + Usage: "Host address of ETCD", + EnvVar: "ETCD_ENDPOINTS", + }, + cli.StringFlag{ + Name: "namespace, ns", + Value: "weaver", + Usage: "Namespace of Weaver ACLS", + EnvVar: "ETCD_KEY_PREFIX", + }, + }, + Subcommands: []cli.Command{ + { + Name: "list", + Usage: "List ACLS", + Aliases: []string{"l"}, + Action: func(c *cli.Context) error { + setupEnv(c) + config.Load() + rl, err := etcd.NewRouteLoader() + if err != nil { + return err + } + acls, err := rl.ListAll() + if err != nil { + return err + } + fmt.Println(acls) + return nil + }, + }, + { + Name: "create", + Usage: "Create ACL", + Aliases: []string{"c"}, + Action: func(c *cli.Context) error { + setupEnv(c) + fmt.Println("new task template: ", c.Args().First()) + return nil + }, + }, + { + Name: "update", + Usage: "Update ACL", + Aliases: []string{"u"}, + Action: func(c *cli.Context) error { + setupEnv(c) + fmt.Println("new task template: ", c.Args().First()) + return nil + }, + }, + { + Name: "delete", + Usage: "Delete ACL", + Aliases: []string{"d"}, + Action: func(c *cli.Context) error { + setupEnv(c) + fmt.Println("new task template: ", c.Args().First()) + return nil + }, + }, + }, + }, } app.Run(os.Args) } +func setupEnv(c *cli.Context) { + os.Setenv("ETCD_ENDPOINTS", c.GlobalString("etcd-host")) + os.Setenv("ETCD_KEY_PREFIX", c.GlobalString("namespace")) + config.Load() +} + func startWeaver(_ *cli.Context) error { sigC := make(chan os.Signal, 1) signal.Notify(sigC, syscall.SIGINT, syscall.SIGKILL, syscall.SIGTERM) diff --git a/cmd/weaver-server/main_test.go b/cmd/weaver-server/main_test.go new file mode 100644 index 0000000..a101378 --- /dev/null +++ b/cmd/weaver-server/main_test.go @@ -0,0 +1,8 @@ +package main + +import ( + "testing" +) + +func testShouldListAllAcls(t *testing.T) { +} diff --git a/etcd/routeloader.go b/etcd/routeloader.go index b828437..fb1060a 100644 --- a/etcd/routeloader.go +++ b/etcd/routeloader.go @@ -47,6 +47,20 @@ func (routeLoader *RouteLoader) PutACL(acl *weaver.ACL) (ACLKey, error) { return key, nil } +func (routeLoader *RouteLoader) ListAll() ([]ACLKey, error) { + keysAPI, key := initEtcd(routeLoader) + resp, err := keysAPI.Get(context.Background(), key, nil) + if err != nil { + return nil, fmt.Errorf("fail to LIST %s with %s", key, err) + } + acls := []ACLKey{} + for _, n := range resp.Node.Nodes { + acls = append(acls, GenACLKey(n.Key)) + fmt.Printf("Key: %q, Value: %q\n", n.Key, n.Value) + } + return acls, nil +} + // GetACL - Fetches an ACL given an ACLKey func (routeLoader *RouteLoader) GetACL(key ACLKey) (*weaver.ACL, error) { res, err := etcd.NewKeysAPI(routeLoader.etcdClient).Get(context.Background(), string(key), nil) @@ -81,8 +95,8 @@ func (routeLoader *RouteLoader) DelACL(key ACLKey) error { } func (routeLoader *RouteLoader) WatchRoutes(ctx context.Context, upsertRouteFunc server.UpsertRouteFunc, deleteRouteFunc server.DeleteRouteFunc) { - etc, key := initEtcd(routeLoader) - watcher := etc.Watcher(key, &etcd.WatcherOptions{Recursive: true}) + keysAPI, key := initEtcd(routeLoader) + watcher := keysAPI.Watcher(key, &etcd.WatcherOptions{Recursive: true}) logger.Infof("starting etcd watcher on %s", key) for { @@ -130,12 +144,12 @@ func (routeLoader *RouteLoader) WatchRoutes(ctx context.Context, upsertRouteFunc func (routeLoader *RouteLoader) BootstrapRoutes(ctx context.Context, upsertRouteFunc server.UpsertRouteFunc) error { // TODO: Consider error scenarios and return an error when it makes sense - etc, key := initEtcd(routeLoader) + keysAPI, key := initEtcd(routeLoader) logger.Infof("bootstrapping router using etcd on %s", key) - res, err := etc.Get(ctx, key, nil) + res, err := keysAPI.Get(ctx, key, nil) if err != nil { logger.Infof("creating router namespace on etcd using %s", key) - _, _ = etc.Set(ctx, key, "", &etcd.SetOptions{ + _, _ = keysAPI.Set(ctx, key, "", &etcd.SetOptions{ Dir: true, }) } @@ -164,7 +178,7 @@ func (routeLoader *RouteLoader) BootstrapRoutes(ctx context.Context, upsertRoute func initEtcd(routeLoader *RouteLoader) (etcd.KeysAPI, string) { key := fmt.Sprintf("/%s/acls/", routeLoader.namespace) - etc := etcd.NewKeysAPI(routeLoader.etcdClient) + keysAPI := etcd.NewKeysAPI(routeLoader.etcdClient) - return etc, key + return keysAPI, key } From 785ea71c5e8da903e53f437bccaed9416a6ec016 Mon Sep 17 00:00:00 2001 From: Gowtham Sai Date: Thu, 14 Mar 2019 02:39:42 +0530 Subject: [PATCH 02/26] Refactor to move cli packages to internal --- Makefile | 4 +- cmd/weaver-server/main.go | 154 --------------------------------- cmd/weaver-server/main_test.go | 8 -- cmd/weaver/main.go | 24 +++++ etcd/routeloader.go | 20 +++-- go.mod | 1 + go.sum | 2 + internal/cli/acl.go | 97 +++++++++++++++++++++ internal/cli/cli.go | 19 ++++ internal/cli/commands.go | 69 +++++++++++++++ internal/cli/list.go | 52 +++++++++++ internal/views/acls/list.go | 17 ++++ internal/views/acls/show.go | 40 +++++++++ internal/views/views.go | 1 + 14 files changed, 338 insertions(+), 170 deletions(-) delete mode 100644 cmd/weaver-server/main.go delete mode 100644 cmd/weaver-server/main_test.go create mode 100644 cmd/weaver/main.go create mode 100644 internal/cli/acl.go create mode 100644 internal/cli/cli.go create mode 100644 internal/cli/commands.go create mode 100644 internal/cli/list.go create mode 100644 internal/views/acls/list.go create mode 100644 internal/views/acls/show.go create mode 100644 internal/views/views.go diff --git a/Makefile b/Makefile index 635266c..38dc5e9 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ all: build fmt vet lint test coverage default: build fmt vet lint test ALL_PACKAGES=$(shell go list ./... | grep -v "vendor") -APP_EXECUTABLE="out/weaver-server" +APP_EXECUTABLE="out/weaver" COMMIT_HASH=$(shell git rev-parse --verify head | cut -c-1-8) BUILD_DATE=$(shell date +%Y-%m-%dT%H:%M:%S%z) @@ -14,7 +14,7 @@ setup: compile: mkdir -p out/ - GO111MODULE=on go build -o $(APP_EXECUTABLE) -ldflags "-X main.BuildDate=$(BUILD_DATE) -X main.Commit=$(COMMIT_HASH) -s -w" ./cmd/weaver-server + GO111MODULE=on go build -o $(APP_EXECUTABLE) -ldflags "-X main.BuildDate=$(BUILD_DATE) -X main.Commit=$(COMMIT_HASH) -s -w" ./cmd/weaver build: deps compile fmt vet lint diff --git a/cmd/weaver-server/main.go b/cmd/weaver-server/main.go deleted file mode 100644 index 89bc1e9..0000000 --- a/cmd/weaver-server/main.go +++ /dev/null @@ -1,154 +0,0 @@ -package main - -import ( - "context" - "fmt" - "log" - "os" - "os/signal" - "syscall" - - raven "github.com/getsentry/raven-go" - "github.com/gojektech/weaver/config" - "github.com/gojektech/weaver/etcd" - "github.com/gojektech/weaver/pkg/instrumentation" - "github.com/gojektech/weaver/pkg/logger" - "github.com/gojektech/weaver/server" - cli "gopkg.in/urfave/cli.v1" -) - -func main() { - app := cli.NewApp() - app.Name = "weaver" - app.Usage = "run weaver-server" - app.Version = fmt.Sprintf("%s built on %s (commit: %s)", Version, BuildDate, Commit) - app.Description = "An Advanced HTTP Reverse Proxy with Dynamic Sharding Strategies" - app.Commands = []cli.Command{ - { - Name: "start", - Description: "Start weaver server", - Action: startWeaver, - }, - { - Name: "acls", - Aliases: []string{"a"}, - Description: "List, Create, Delete, Update ACLs", - Usage: "Perform list, create, update, delete acls", - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "etcd-host, etcd", - Value: "http://localhost:2379", - Usage: "Host address of ETCD", - EnvVar: "ETCD_ENDPOINTS", - }, - cli.StringFlag{ - Name: "namespace, ns", - Value: "weaver", - Usage: "Namespace of Weaver ACLS", - EnvVar: "ETCD_KEY_PREFIX", - }, - }, - Subcommands: []cli.Command{ - { - Name: "list", - Usage: "List ACLS", - Aliases: []string{"l"}, - Action: func(c *cli.Context) error { - setupEnv(c) - config.Load() - rl, err := etcd.NewRouteLoader() - if err != nil { - return err - } - acls, err := rl.ListAll() - if err != nil { - return err - } - fmt.Println(acls) - return nil - }, - }, - { - Name: "create", - Usage: "Create ACL", - Aliases: []string{"c"}, - Action: func(c *cli.Context) error { - setupEnv(c) - fmt.Println("new task template: ", c.Args().First()) - return nil - }, - }, - { - Name: "update", - Usage: "Update ACL", - Aliases: []string{"u"}, - Action: func(c *cli.Context) error { - setupEnv(c) - fmt.Println("new task template: ", c.Args().First()) - return nil - }, - }, - { - Name: "delete", - Usage: "Delete ACL", - Aliases: []string{"d"}, - Action: func(c *cli.Context) error { - setupEnv(c) - fmt.Println("new task template: ", c.Args().First()) - return nil - }, - }, - }, - }, - } - - app.Run(os.Args) -} - -func setupEnv(c *cli.Context) { - os.Setenv("ETCD_ENDPOINTS", c.GlobalString("etcd-host")) - os.Setenv("ETCD_KEY_PREFIX", c.GlobalString("namespace")) - config.Load() -} - -func startWeaver(_ *cli.Context) error { - sigC := make(chan os.Signal, 1) - signal.Notify(sigC, syscall.SIGINT, syscall.SIGKILL, syscall.SIGTERM) - - config.Load() - - raven.SetDSN(config.SentryDSN()) - logger.SetupLogger() - - err := instrumentation.InitiateStatsDMetrics() - if err != nil { - log.Printf("StatsD: Error initiating client %s", err) - } - defer instrumentation.CloseStatsDClient() - - instrumentation.InitNewRelic() - defer instrumentation.ShutdownNewRelic() - - routeLoader, err := etcd.NewRouteLoader() - if err != nil { - log.Printf("StartServer: failed to initialise etcd route loader: %s", err) - } - - ctx, cancel := context.WithCancel(context.Background()) - go server.StartServer(ctx, routeLoader) - - sig := <-sigC - log.Printf("Received %d, shutting down", sig) - - defer cancel() - server.ShutdownServer(ctx) - - return nil -} - -// Build information (will be injected during build) -var ( - Version = "1.0.0" - Commit = "n/a" - BuildDate = "n/a" -) diff --git a/cmd/weaver-server/main_test.go b/cmd/weaver-server/main_test.go deleted file mode 100644 index a101378..0000000 --- a/cmd/weaver-server/main_test.go +++ /dev/null @@ -1,8 +0,0 @@ -package main - -import ( - "testing" -) - -func testShouldListAllAcls(t *testing.T) { -} diff --git a/cmd/weaver/main.go b/cmd/weaver/main.go new file mode 100644 index 0000000..93d3b17 --- /dev/null +++ b/cmd/weaver/main.go @@ -0,0 +1,24 @@ +package main + +import ( + "fmt" + "github.com/gojektech/weaver/internal/cli" + "os" +) + +func main() { + app := cli.NewApp() + app.Name = "Weaver" + app.Version = fmt.Sprintf("%s built on %s (commit: %s)", Version, BuildDate, Commit) + app.Description = "An Advanced HTTP Reverse Proxy with Dynamic Sharding Strategies" + app.Usage = "Multi Utility CLI Tool" + app.Commands = cli.GetCommands() + app.Run(os.Args) +} + +// Build information (will be injected during build) +var ( + Version = "1.0.0" + Commit = "n/a" + BuildDate = "n/a" +) diff --git a/etcd/routeloader.go b/etcd/routeloader.go index fb1060a..4d08a92 100644 --- a/etcd/routeloader.go +++ b/etcd/routeloader.go @@ -47,16 +47,24 @@ func (routeLoader *RouteLoader) PutACL(acl *weaver.ACL) (ACLKey, error) { return key, nil } -func (routeLoader *RouteLoader) ListAll() ([]ACLKey, error) { +func (routeLoader *RouteLoader) ListAll() ([]*weaver.ACL, error) { keysAPI, key := initEtcd(routeLoader) - resp, err := keysAPI.Get(context.Background(), key, nil) + res, err := keysAPI.Get(context.Background(), key, nil) if err != nil { return nil, fmt.Errorf("fail to LIST %s with %s", key, err) } - acls := []ACLKey{} - for _, n := range resp.Node.Nodes { - acls = append(acls, GenACLKey(n.Key)) - fmt.Printf("Key: %q, Value: %q\n", n.Key, n.Value) + acls := []*weaver.ACL{} + sort.Sort(res.Node.Nodes) + for _, nd := range res.Node.Nodes { + logger.Debugf("fetching node key %s", nd.Key) + aclKey := GenACLKey(nd.Key) + acl, err := routeLoader.GetACL(aclKey) + if err != nil { + logger.Errorf("error in fetching %s: %v", nd.Key, err) + } else { + acls = append(acls, acl) + + } } return acls, nil } diff --git a/go.mod b/go.mod index 90fdae9..8afaa5a 100644 --- a/go.mod +++ b/go.mod @@ -21,6 +21,7 @@ require ( github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect github.com/grpc-ecosystem/grpc-gateway v1.7.0 // indirect github.com/hashicorp/hcl v0.0.0-20170217164738-630949a3c5fa // indirect + github.com/jedib0t/go-pretty v4.0.2+incompatible github.com/jonboulle/clockwork v0.1.0 // indirect github.com/kr/pretty v0.1.0 // indirect github.com/magiconair/properties v0.0.0-20170113111004-b3b15ef068fd // indirect diff --git a/go.sum b/go.sum index 181fafa..bc97f52 100644 --- a/go.sum +++ b/go.sum @@ -51,6 +51,8 @@ github.com/hashicorp/hcl v0.0.0-20170217164738-630949a3c5fa h1:10wM7X2JKPrmcvtI9 github.com/hashicorp/hcl v0.0.0-20170217164738-630949a3c5fa/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/jedib0t/go-pretty v4.0.2+incompatible h1:bU7rhIKsGljSXgWOdElmiciqy+5AWbDpdmBJwl55PPg= +github.com/jedib0t/go-pretty v4.0.2+incompatible/go.mod h1:XemHduiw8R651AF9Pt4FwCTKeG3oo7hrHJAoznj9nag= github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= diff --git a/internal/cli/acl.go b/internal/cli/acl.go new file mode 100644 index 0000000..94fee74 --- /dev/null +++ b/internal/cli/acl.go @@ -0,0 +1,97 @@ +package cli + +import ( + "fmt" + "github.com/gojektech/weaver/config" + "github.com/gojektech/weaver/pkg/logger" + cliParser "gopkg.in/urfave/cli.v1" + "os" +) + +const ( + aclCliName = "acls" + aclDescription = "List, Create, Delete, Update ACLs" + aclUsage = "Perform list, create, update, delete acls" +) + +var ( + aclCommandMap = make(map[string]Command) + aclAliases = []string{"a"} + aclFlags = []cliParser.Flag{ + cliParser.StringFlag{ + Name: "etcd-host, etcd", + Value: "http://localhost:2379", + Usage: "Host address of ETCD", + EnvVar: "ETCD_ENDPOINTS", + }, + cliParser.StringFlag{ + Name: "namespace, ns", + Value: "weaver", + Usage: "Namespace of Weaver ACLS", + EnvVar: "ETCD_KEY_PREFIX", + }, + } + + aclCommand = &struct { + flags []cliParser.Flag + *command + }{ + command: &command{ + cliName: aclCliName, + description: aclDescription, + usage: aclUsage, + aliases: aclAliases, + getCommand: func() cliParser.Command { + subCommands := populateSubCommands(&aclCommandMap) + cliParserCmd := cliParser.Command{ + Name: string(aclCliName), + Description: aclDescription, + Usage: aclUsage, + Aliases: aclAliases, + Flags: aclFlags, + } + if len(subCommands) > 0 { + cliParserCmd.Subcommands = subCommands + } else { + cliParserCmd.Action = ExecCommand + } + return cliParserCmd + }, + action: func(c *cliParser.Context) error { + preSetupForACLCli(c) + if cmdToExecute := aclCommandMap[c.Command.Name]; cmdToExecute != nil { + return cmdToExecute.Exec(c) + } + return fmt.Errorf("Error executing command. Command not registered: %s", c.Command.Name) + }, + }, + flags: aclFlags, + } +) + +func registerACLCommand(cliName string, cmd Command) { + if cmdToExecute := aclCommandMap[cliName]; cmdToExecute != nil { + panic(fmt.Sprintf("Command for cli %s already registered by: %s", cliName, cmdToExecute)) + } + aclCommandMap[cliName] = cmd +} + +func populateSubCommands(cmdMap *map[string]Command) []cliParser.Command { + subCommands := []cliParser.Command{} + for _, registerCmd := range *cmdMap { + subCommand := registerCmd.GetCommand() + subCommands = append(subCommands, subCommand) + } + return subCommands +} + +func preSetupForACLCli(c *cliParser.Context) { + os.Setenv("ETCD_ENDPOINTS", c.GlobalString("etcd-host")) + os.Setenv("ETCD_KEY_PREFIX", c.GlobalString("namespace")) + config.Load() + logger.SetupLogger() +} + +func init() { + registerCommand(aclCliName, aclCommand) +} diff --git a/internal/cli/cli.go b/internal/cli/cli.go new file mode 100644 index 0000000..c69e9f7 --- /dev/null +++ b/internal/cli/cli.go @@ -0,0 +1,19 @@ +package cli + +import ( + cliParser "gopkg.in/urfave/cli.v1" +) + +func NewApp() *cliParser.App { + app := cliParser.NewApp() + app.Flags = []cliParser.Flag{ + cliParser.StringFlag{ + Name: "verbose", + Value: "Error", + Usage: "Verbosity of log level, ex: debug, info, warn, error, fatal, panic", + EnvVar: "LOGGER_LEVEL", + }, + } + + return app +} diff --git a/internal/cli/commands.go b/internal/cli/commands.go new file mode 100644 index 0000000..d15243b --- /dev/null +++ b/internal/cli/commands.go @@ -0,0 +1,69 @@ +package cli + +import ( + "fmt" + "github.com/gojektech/weaver/config" + "github.com/gojektech/weaver/pkg/logger" + cliParser "gopkg.in/urfave/cli.v1" + "os" + "strings" +) + +var commandMap = make(map[string]Command) + +type command struct { + cliName string + description string + usage string + aliases []string + getCommand func() cliParser.Command + action func(c *cliParser.Context) error +} + +type Command interface { + GetCommand() cliParser.Command + Exec(c *cliParser.Context) error +} + +func (cmd command) GetCommand() cliParser.Command { + return cmd.getCommand() +} + +func (cmd command) Exec(c *cliParser.Context) error { + return cmd.action(c) +} + +func registerCommand(cliName string, cmd Command) { + if cmdToExecute := commandMap[cliName]; cmdToExecute != nil { + panic(fmt.Sprintf("Command for cli %s already registered by: %s", cliName, cmdToExecute)) + } + commandMap[cliName] = cmd +} + +func GetCommands() []cliParser.Command { + cmds := []cliParser.Command{} + for _, registerCmd := range commandMap { + cmds = append(cmds, registerCmd.GetCommand()) + } + return cmds +} + +func ExecCommand(c *cliParser.Context) error { + cliName := getCliName(c.Command) + + if cmdToExecute := commandMap[cliName]; cmdToExecute != nil { + setUp(c) + return cmdToExecute.Exec(c) + } + return fmt.Errorf("Error executing command. Command not registered: %s", c.Command.Name) +} + +func getCliName(cmd cliParser.Command) string { + return strings.Split(cmd.FullName(), " ")[0] +} + +func setUp(c *cliParser.Context) { + os.Setenv("LOGGER_LEVEL", c.GlobalString("verbose")) + config.Load() + logger.SetupLogger() +} diff --git a/internal/cli/list.go b/internal/cli/list.go new file mode 100644 index 0000000..cf4eb58 --- /dev/null +++ b/internal/cli/list.go @@ -0,0 +1,52 @@ +package cli + +import ( + "github.com/gojektech/weaver/etcd" + views "github.com/gojektech/weaver/internal/views/acls" + cli "gopkg.in/urfave/cli.v1" +) + +const ( + aclListCliName = "list" + aclListDescription = "List ALL Weaver ACL in a Namespace" + aclListUsage = "List ALL Weaver ACL in a Namespace" +) + +var ( + aclListAlises = []string{"l"} + + aclList = command{ + cliName: aclListCliName, + description: aclListDescription, + usage: aclListUsage, + aliases: aclListAlises, + + getCommand: func() cli.Command { + return cli.Command{ + Name: aclListCliName, + Description: aclListDescription, + Usage: aclListUsage, + Aliases: aclListAlises, + Action: ExecCommand, + } + }, + + action: func(c *cli.Context) error { + rl, err := etcd.NewRouteLoader() + if err != nil { + return err + } + acls, err := rl.ListAll() + if err != nil { + return err + } + views.RenderList(acls) + return nil + + }, + } +) + +func init() { + registerACLCommand(aclListCliName, aclList) +} diff --git a/internal/views/acls/list.go b/internal/views/acls/list.go new file mode 100644 index 0000000..9315afd --- /dev/null +++ b/internal/views/acls/list.go @@ -0,0 +1,17 @@ +package acls + +import ( + "github.com/gojektech/weaver" + "github.com/jedib0t/go-pretty/table" + "os" +) + +func RenderList(acls []*weaver.ACL) { + t := table.NewWriter() + t.SetOutputMirror(os.Stdout) + t.AppendHeader(table.Row{"#", "ID", "Criterion"}) + for id, acl := range acls { + t.AppendRow([]interface{}{id, acl.ID, acl.Criterion}) + } + t.Render() +} diff --git a/internal/views/acls/show.go b/internal/views/acls/show.go new file mode 100644 index 0000000..1a99c44 --- /dev/null +++ b/internal/views/acls/show.go @@ -0,0 +1,40 @@ +package acls + +import ( + "encoding/json" + "github.com/gojektech/weaver" + "github.com/gojektech/weaver/pkg/logger" + "github.com/gojektech/weaver/pkg/shard" + "github.com/jedib0t/go-pretty/table" + "os" +) + +func RenderShow(acl *weaver.ACL) { + t := table.NewWriter() + t.SetStyle(table.StyleRounded) + t.SetOutputMirror(os.Stdout) + t.AppendRow([]interface{}{"ID", acl.ID}) + t.AppendRow([]interface{}{"Criterion", acl.Criterion}) + t.AppendRow([]interface{}{"-"}) + t.AppendRow([]interface{}{"EndPoint Config"}) + t.AppendRow([]interface{}{"", "Matcher", acl.EndpointConfig.Matcher}) + t.AppendRow([]interface{}{"", "Shard Expression", acl.EndpointConfig.ShardExpr}) + t.AppendRow([]interface{}{"", "Shard Function", acl.EndpointConfig.ShardFunc}) + + shardConfig := map[string]shard.BackendDefinition{} + if err := json.Unmarshal(acl.EndpointConfig.ShardConfig, &shardConfig); err == nil { + t.AppendRow([]interface{}{"-"}) + t.AppendRow([]interface{}{"Backends"}) + for key, backend := range shardConfig { + t.AppendRow([]interface{}{"", key}) + t.AppendRow([]interface{}{"", "", backend.BackendName}) + t.AppendRow([]interface{}{"", "", backend.BackendURL}) + if backend.Timeout != nil { + t.AppendRow([]interface{}{"", "", backend.Timeout}) + } + } + } else { + logger.Debugf("Error %s", err) + } + t.Render() +} diff --git a/internal/views/views.go b/internal/views/views.go new file mode 100644 index 0000000..e29e57d --- /dev/null +++ b/internal/views/views.go @@ -0,0 +1 @@ +package views From 4c8c7ffe5a1cc2593a859e4bf122c5c5f05f7d87 Mon Sep 17 00:00:00 2001 From: Gowtham Sai Date: Mon, 18 Mar 2019 03:56:36 +0530 Subject: [PATCH 03/26] Initial command setup --- cmd/weaver/main.go | 1 - go.mod | 5 +- go.sum | 12 ++++- internal/cli/acl.go | 97 ------------------------------------ internal/cli/cli.go | 15 +++--- internal/cli/command.go | 20 ++++++++ internal/cli/command_test.go | 36 +++++++++++++ internal/cli/commands.go | 69 ------------------------- internal/cli/list.go | 52 ------------------- 9 files changed, 79 insertions(+), 228 deletions(-) delete mode 100644 internal/cli/acl.go create mode 100644 internal/cli/command.go create mode 100644 internal/cli/command_test.go delete mode 100644 internal/cli/commands.go delete mode 100644 internal/cli/list.go diff --git a/cmd/weaver/main.go b/cmd/weaver/main.go index 93d3b17..b85ae06 100644 --- a/cmd/weaver/main.go +++ b/cmd/weaver/main.go @@ -12,7 +12,6 @@ func main() { app.Version = fmt.Sprintf("%s built on %s (commit: %s)", Version, BuildDate, Commit) app.Description = "An Advanced HTTP Reverse Proxy with Dynamic Sharding Strategies" app.Usage = "Multi Utility CLI Tool" - app.Commands = cli.GetCommands() app.Run(os.Args) } diff --git a/go.mod b/go.mod index 8afaa5a..7c9226d 100644 --- a/go.mod +++ b/go.mod @@ -52,11 +52,14 @@ require ( github.com/tinylib/msgp v1.1.0 // indirect github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 // indirect github.com/ugorji/go v0.0.0-20171019201919-bdcc60b419d1 // indirect + github.com/urfave/cli v1.20.0 github.com/vulcand/predicate v1.0.0 // indirect github.com/vulcand/route v0.0.0-20160805191529-61904570391b github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect go.etcd.io/bbolt v1.3.2 // indirect - golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613 // indirect + golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a // indirect + golang.org/x/net v0.0.0-20190313220215-9f648a60d977 // indirect + golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f // indirect golang.org/x/time v0.0.0-20181108054448-85acf8d2951c // indirect google.golang.org/grpc v1.18.0 // indirect gopkg.in/airbrake/gobrake.v2 v2.0.9 // indirect diff --git a/go.sum b/go.sum index bc97f52..1d74dae 100644 --- a/go.sum +++ b/go.sum @@ -125,6 +125,8 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go v0.0.0-20171019201919-bdcc60b419d1 h1:UvhxfNjNqlZ/x3cDyqxMhoiUpemd3zXkVQApN6bM/lg= github.com/ugorji/go v0.0.0-20171019201919-bdcc60b419d1/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ= +github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/vulcand/predicate v1.0.0 h1:c5lVsC9SKrQjdWNwTTG3RkADPKhSw1SrUZWq6LJL21k= github.com/vulcand/predicate v1.0.0/go.mod h1:mlccC5IRBoc2cIFmCB8ZM62I3VDb6p2GXESMHa3CnZg= github.com/vulcand/route v0.0.0-20160805191529-61904570391b h1:eCB3pa/SYYqLuajbklwy+mJAYNU1U8JQLv3M9gwKSeg= @@ -133,13 +135,16 @@ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5 github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= go.etcd.io/bbolt v1.3.2 h1:Z/90sZLPOeCy2PwprqkFa25PdkusRzaj9P8zm/KNyvk= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613 h1:MQ/ZZiDsUapFFiMS+vzwXkCTeEKaum+Do5rINYJDmxc= -golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a h1:YX8ljsm6wXlHZO+aRz9Exqr0evNhKRNe5K/gi+zKh4U= +golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc h1:a3CU5tJYVj92DY2LaA1kUkrsqD5/3mLDhx2NcNqyW+0= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190313220215-9f648a60d977 h1:actzWV6iWn3GLqN8dZjzsB+CLt+gaV2+wsxroxiQI8I= +golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ= @@ -147,6 +152,9 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e h1:o3PsSEY8E4eXWkXrIP9YJALUkVZqzHJT5DOasTyn8Vs= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f h1:yCrMx/EeIue0+Qca57bWZS7VX6ymEoypmhWyPhz0NHM= +golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c h1:fqgJT0MGcGpPgpWU7VRdRjuArfcOvC4AoJmILihzhDg= diff --git a/internal/cli/acl.go b/internal/cli/acl.go deleted file mode 100644 index 94fee74..0000000 --- a/internal/cli/acl.go +++ /dev/null @@ -1,97 +0,0 @@ -package cli - -import ( - "fmt" - "github.com/gojektech/weaver/config" - "github.com/gojektech/weaver/pkg/logger" - cliParser "gopkg.in/urfave/cli.v1" - "os" -) - -const ( - aclCliName = "acls" - aclDescription = "List, Create, Delete, Update ACLs" - aclUsage = "Perform list, create, update, delete acls" -) - -var ( - aclCommandMap = make(map[string]Command) - aclAliases = []string{"a"} - aclFlags = []cliParser.Flag{ - cliParser.StringFlag{ - Name: "etcd-host, etcd", - Value: "http://localhost:2379", - Usage: "Host address of ETCD", - EnvVar: "ETCD_ENDPOINTS", - }, - cliParser.StringFlag{ - Name: "namespace, ns", - Value: "weaver", - Usage: "Namespace of Weaver ACLS", - EnvVar: "ETCD_KEY_PREFIX", - }, - } - - aclCommand = &struct { - flags []cliParser.Flag - *command - }{ - command: &command{ - cliName: aclCliName, - description: aclDescription, - usage: aclUsage, - aliases: aclAliases, - getCommand: func() cliParser.Command { - subCommands := populateSubCommands(&aclCommandMap) - cliParserCmd := cliParser.Command{ - Name: string(aclCliName), - Description: aclDescription, - Usage: aclUsage, - Aliases: aclAliases, - Flags: aclFlags, - } - if len(subCommands) > 0 { - cliParserCmd.Subcommands = subCommands - } else { - cliParserCmd.Action = ExecCommand - } - return cliParserCmd - }, - action: func(c *cliParser.Context) error { - preSetupForACLCli(c) - if cmdToExecute := aclCommandMap[c.Command.Name]; cmdToExecute != nil { - return cmdToExecute.Exec(c) - } - return fmt.Errorf("Error executing command. Command not registered: %s", c.Command.Name) - }, - }, - flags: aclFlags, - } -) - -func registerACLCommand(cliName string, cmd Command) { - if cmdToExecute := aclCommandMap[cliName]; cmdToExecute != nil { - panic(fmt.Sprintf("Command for cli %s already registered by: %s", cliName, cmdToExecute)) - } - aclCommandMap[cliName] = cmd -} - -func populateSubCommands(cmdMap *map[string]Command) []cliParser.Command { - subCommands := []cliParser.Command{} - for _, registerCmd := range *cmdMap { - subCommand := registerCmd.GetCommand() - subCommands = append(subCommands, subCommand) - } - return subCommands -} - -func preSetupForACLCli(c *cliParser.Context) { - os.Setenv("ETCD_ENDPOINTS", c.GlobalString("etcd-host")) - os.Setenv("ETCD_KEY_PREFIX", c.GlobalString("namespace")) - config.Load() - logger.SetupLogger() -} - -func init() { - registerCommand(aclCliName, aclCommand) -} diff --git a/internal/cli/cli.go b/internal/cli/cli.go index c69e9f7..4f285ce 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -1,19 +1,22 @@ package cli import ( - cliParser "gopkg.in/urfave/cli.v1" + baseCli "github.com/urfave/cli" ) -func NewApp() *cliParser.App { - app := cliParser.NewApp() - app.Flags = []cliParser.Flag{ - cliParser.StringFlag{ +type Context struct { + *baseCli.Context +} + +func NewApp() *baseCli.App { + app := baseCli.NewApp() + app.Flags = []baseCli.Flag{ + baseCli.StringFlag{ Name: "verbose", Value: "Error", Usage: "Verbosity of log level, ex: debug, info, warn, error, fatal, panic", EnvVar: "LOGGER_LEVEL", }, } - return app } diff --git a/internal/cli/command.go b/internal/cli/command.go new file mode 100644 index 0000000..fa04bee --- /dev/null +++ b/internal/cli/command.go @@ -0,0 +1,20 @@ +package cli + +type cmdAction func(c *Context) error + +type Command struct { + name string + action cmdAction +} + +func (cmd *Command) CliHandler() string { + return cmd.name +} + +func (cmd *Command) Exec(c *Context) error { + return cmd.action(c) +} + +func NewDefaultCommand(name, usage, description string, action cmdAction) *Command { + return &Command{name, action} +} diff --git a/internal/cli/command_test.go b/internal/cli/command_test.go new file mode 100644 index 0000000..dbd270a --- /dev/null +++ b/internal/cli/command_test.go @@ -0,0 +1,36 @@ +package cli_test + +import ( + "github.com/gojektech/weaver/internal/cli" + "github.com/stretchr/testify/assert" + "testing" +) + +type testSetup struct { + name, usage, description string + isCalled bool + cmd *cli.Command +} + +func TestDefaultCommandInitialization(t *testing.T) { + ts := setup() + assert.NotNil(t, ts.cmd) +} + +func TestCommandShouldHaveCliHandlerName(t *testing.T) { + ts := setup() + assert.Equal(t, ts.cmd.CliHandler(), ts.name) +} + +func TestCommandShouldExecuteSpecifiedAction(t *testing.T) { + ts := setup() + ts.cmd.Exec(&cli.Context{}) + assert.True(t, ts.isCalled) +} + +func setup() *testSetup { + setup := &testSetup{name: "test", usage: "usage", description: "description", isCalled: false} + action := func(c *cli.Context) error { setup.isCalled = true; return nil } + setup.cmd = cli.NewDefaultCommand(setup.name, setup.usage, setup.description, action) + return setup +} diff --git a/internal/cli/commands.go b/internal/cli/commands.go deleted file mode 100644 index d15243b..0000000 --- a/internal/cli/commands.go +++ /dev/null @@ -1,69 +0,0 @@ -package cli - -import ( - "fmt" - "github.com/gojektech/weaver/config" - "github.com/gojektech/weaver/pkg/logger" - cliParser "gopkg.in/urfave/cli.v1" - "os" - "strings" -) - -var commandMap = make(map[string]Command) - -type command struct { - cliName string - description string - usage string - aliases []string - getCommand func() cliParser.Command - action func(c *cliParser.Context) error -} - -type Command interface { - GetCommand() cliParser.Command - Exec(c *cliParser.Context) error -} - -func (cmd command) GetCommand() cliParser.Command { - return cmd.getCommand() -} - -func (cmd command) Exec(c *cliParser.Context) error { - return cmd.action(c) -} - -func registerCommand(cliName string, cmd Command) { - if cmdToExecute := commandMap[cliName]; cmdToExecute != nil { - panic(fmt.Sprintf("Command for cli %s already registered by: %s", cliName, cmdToExecute)) - } - commandMap[cliName] = cmd -} - -func GetCommands() []cliParser.Command { - cmds := []cliParser.Command{} - for _, registerCmd := range commandMap { - cmds = append(cmds, registerCmd.GetCommand()) - } - return cmds -} - -func ExecCommand(c *cliParser.Context) error { - cliName := getCliName(c.Command) - - if cmdToExecute := commandMap[cliName]; cmdToExecute != nil { - setUp(c) - return cmdToExecute.Exec(c) - } - return fmt.Errorf("Error executing command. Command not registered: %s", c.Command.Name) -} - -func getCliName(cmd cliParser.Command) string { - return strings.Split(cmd.FullName(), " ")[0] -} - -func setUp(c *cliParser.Context) { - os.Setenv("LOGGER_LEVEL", c.GlobalString("verbose")) - config.Load() - logger.SetupLogger() -} diff --git a/internal/cli/list.go b/internal/cli/list.go deleted file mode 100644 index cf4eb58..0000000 --- a/internal/cli/list.go +++ /dev/null @@ -1,52 +0,0 @@ -package cli - -import ( - "github.com/gojektech/weaver/etcd" - views "github.com/gojektech/weaver/internal/views/acls" - cli "gopkg.in/urfave/cli.v1" -) - -const ( - aclListCliName = "list" - aclListDescription = "List ALL Weaver ACL in a Namespace" - aclListUsage = "List ALL Weaver ACL in a Namespace" -) - -var ( - aclListAlises = []string{"l"} - - aclList = command{ - cliName: aclListCliName, - description: aclListDescription, - usage: aclListUsage, - aliases: aclListAlises, - - getCommand: func() cli.Command { - return cli.Command{ - Name: aclListCliName, - Description: aclListDescription, - Usage: aclListUsage, - Aliases: aclListAlises, - Action: ExecCommand, - } - }, - - action: func(c *cli.Context) error { - rl, err := etcd.NewRouteLoader() - if err != nil { - return err - } - acls, err := rl.ListAll() - if err != nil { - return err - } - views.RenderList(acls) - return nil - - }, - } -) - -func init() { - registerACLCommand(aclListCliName, aclList) -} From 864bce54803e7b43f8235b9c2021c24b639cc4f9 Mon Sep 17 00:00:00 2001 From: Gowtham Sai Date: Mon, 18 Mar 2019 04:44:45 +0530 Subject: [PATCH 04/26] Introduce basic parent command --- go.mod | 2 -- go.sum | 4 ---- internal/cli/cli.go | 7 ++++++- internal/cli/command_test.go | 12 ++++++------ internal/cli/parent_command.go | 10 ++++++++++ internal/cli/parent_command_test.go | 23 +++++++++++++++++++++++ 6 files changed, 45 insertions(+), 13 deletions(-) create mode 100644 internal/cli/parent_command.go create mode 100644 internal/cli/parent_command_test.go diff --git a/go.mod b/go.mod index 7c9226d..db824d2 100644 --- a/go.mod +++ b/go.mod @@ -52,7 +52,6 @@ require ( github.com/tinylib/msgp v1.1.0 // indirect github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 // indirect github.com/ugorji/go v0.0.0-20171019201919-bdcc60b419d1 // indirect - github.com/urfave/cli v1.20.0 github.com/vulcand/predicate v1.0.0 // indirect github.com/vulcand/route v0.0.0-20160805191529-61904570391b github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect @@ -66,5 +65,4 @@ require ( gopkg.in/alexcesaro/statsd.v2 v2.0.0 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 // indirect - gopkg.in/urfave/cli.v1 v1.20.0 ) diff --git a/go.sum b/go.sum index 1d74dae..399b1a2 100644 --- a/go.sum +++ b/go.sum @@ -125,8 +125,6 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go v0.0.0-20171019201919-bdcc60b419d1 h1:UvhxfNjNqlZ/x3cDyqxMhoiUpemd3zXkVQApN6bM/lg= github.com/ugorji/go v0.0.0-20171019201919-bdcc60b419d1/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ= -github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw= -github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/vulcand/predicate v1.0.0 h1:c5lVsC9SKrQjdWNwTTG3RkADPKhSw1SrUZWq6LJL21k= github.com/vulcand/predicate v1.0.0/go.mod h1:mlccC5IRBoc2cIFmCB8ZM62I3VDb6p2GXESMHa3CnZg= github.com/vulcand/route v0.0.0-20160805191529-61904570391b h1:eCB3pa/SYYqLuajbklwy+mJAYNU1U8JQLv3M9gwKSeg= @@ -178,8 +176,6 @@ gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 h1:OAj3g0cR6Dx/R07QgQe8wkA9RNj gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0= -gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0= gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/internal/cli/cli.go b/internal/cli/cli.go index 4f285ce..0fb30ec 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -1,13 +1,18 @@ package cli import ( - baseCli "github.com/urfave/cli" + baseCli "gopkg.in/urfave/cli.v1" ) type Context struct { *baseCli.Context } +type CLIHandler interface { + GetCommand() *baseCli.Command + Exec(c *Context) error +} + func NewApp() *baseCli.App { app := baseCli.NewApp() app.Flags = []baseCli.Flag{ diff --git a/internal/cli/command_test.go b/internal/cli/command_test.go index dbd270a..993babb 100644 --- a/internal/cli/command_test.go +++ b/internal/cli/command_test.go @@ -6,30 +6,30 @@ import ( "testing" ) -type testSetup struct { +type testCommandSetup struct { name, usage, description string isCalled bool cmd *cli.Command } func TestDefaultCommandInitialization(t *testing.T) { - ts := setup() + ts := setupTestCommand() assert.NotNil(t, ts.cmd) } func TestCommandShouldHaveCliHandlerName(t *testing.T) { - ts := setup() + ts := setupTestCommand() assert.Equal(t, ts.cmd.CliHandler(), ts.name) } func TestCommandShouldExecuteSpecifiedAction(t *testing.T) { - ts := setup() + ts := setupTestCommand() ts.cmd.Exec(&cli.Context{}) assert.True(t, ts.isCalled) } -func setup() *testSetup { - setup := &testSetup{name: "test", usage: "usage", description: "description", isCalled: false} +func setupTestCommand() *testCommandSetup { + setup := &testCommandSetup{name: "test", usage: "usage", description: "description", isCalled: false} action := func(c *cli.Context) error { setup.isCalled = true; return nil } setup.cmd = cli.NewDefaultCommand(setup.name, setup.usage, setup.description, action) return setup diff --git a/internal/cli/parent_command.go b/internal/cli/parent_command.go new file mode 100644 index 0000000..89e53ab --- /dev/null +++ b/internal/cli/parent_command.go @@ -0,0 +1,10 @@ +package cli + +type ParentCommand struct { + cmdRegistery map[string]*Command + *Command +} + +func NewParentCommand(name, usage, description string) *ParentCommand { + return &ParentCommand{Command: &Command{name: name}} +} diff --git a/internal/cli/parent_command_test.go b/internal/cli/parent_command_test.go new file mode 100644 index 0000000..82e38c0 --- /dev/null +++ b/internal/cli/parent_command_test.go @@ -0,0 +1,23 @@ +package cli_test + +import ( + "github.com/gojektech/weaver/internal/cli" + "github.com/stretchr/testify/assert" + "testing" +) + +type testParentCommandSetup struct { + name, usage, description string + cmd *cli.ParentCommand +} + +func TestParentCommandInitialization(t *testing.T) { + ts := setupTestParentCommand() + assert.NotNil(t, ts.cmd) +} + +func setupTestParentCommand() *testParentCommandSetup { + ts := &testParentCommandSetup{name: "parent", usage: "parent usage", description: "parent description"} + ts.cmd = cli.NewParentCommand(ts.name, ts.usage, ts.description) + return ts +} From 839e2fd0ed7c1d12b07fdab6eb0dd273b07431ac Mon Sep 17 00:00:00 2001 From: Gowtham Sai Date: Mon, 18 Mar 2019 23:01:08 +0530 Subject: [PATCH 05/26] Parent Command can register sub commands --- go.mod | 1 + go.sum | 2 ++ internal/cli/parent_command.go | 9 ++++++++- internal/cli/parent_command_test.go | 6 ++++++ 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index db824d2..495000f 100644 --- a/go.mod +++ b/go.mod @@ -65,4 +65,5 @@ require ( gopkg.in/alexcesaro/statsd.v2 v2.0.0 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 // indirect + gopkg.in/urfave/cli.v1 v1.20.0 ) diff --git a/go.sum b/go.sum index 399b1a2..c9ec799 100644 --- a/go.sum +++ b/go.sum @@ -176,6 +176,8 @@ gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 h1:OAj3g0cR6Dx/R07QgQe8wkA9RNj gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0= +gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0= gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/internal/cli/parent_command.go b/internal/cli/parent_command.go index 89e53ab..28669d4 100644 --- a/internal/cli/parent_command.go +++ b/internal/cli/parent_command.go @@ -1,10 +1,17 @@ package cli +import () + type ParentCommand struct { cmdRegistery map[string]*Command *Command } +func (pc *ParentCommand) RegisterCommand(cmd *Command) error { + pc.cmdRegistery[cmd.CliHandler()] = cmd + return nil +} + func NewParentCommand(name, usage, description string) *ParentCommand { - return &ParentCommand{Command: &Command{name: name}} + return &ParentCommand{cmdRegistery: make(map[string]*Command), Command: &Command{name: name}} } diff --git a/internal/cli/parent_command_test.go b/internal/cli/parent_command_test.go index 82e38c0..aeb1ba4 100644 --- a/internal/cli/parent_command_test.go +++ b/internal/cli/parent_command_test.go @@ -16,6 +16,12 @@ func TestParentCommandInitialization(t *testing.T) { assert.NotNil(t, ts.cmd) } +func TestParentCommandShouldRegisterCommand(t *testing.T) { + ts := setupTestParentCommand() + cmd := cli.NewDefaultCommand("test", "usage", "description", func(c *cli.Context) error { return nil }) + assert.NoError(t, ts.cmd.RegisterCommand(cmd)) +} + func setupTestParentCommand() *testParentCommandSetup { ts := &testParentCommandSetup{name: "parent", usage: "parent usage", description: "parent description"} ts.cmd = cli.NewParentCommand(ts.name, ts.usage, ts.description) From 98216007b1223528a5dc2e79082c24601e81c503 Mon Sep 17 00:00:00 2001 From: Gowtham Sai Date: Mon, 18 Mar 2019 23:35:23 +0530 Subject: [PATCH 06/26] Parent command registers only one cmd for cli handelr --- internal/cli/parent_command.go | 11 +++++++++-- internal/cli/parent_command_test.go | 11 +++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/internal/cli/parent_command.go b/internal/cli/parent_command.go index 28669d4..0cac51a 100644 --- a/internal/cli/parent_command.go +++ b/internal/cli/parent_command.go @@ -1,6 +1,8 @@ package cli -import () +import ( + "fmt" +) type ParentCommand struct { cmdRegistery map[string]*Command @@ -8,7 +10,12 @@ type ParentCommand struct { } func (pc *ParentCommand) RegisterCommand(cmd *Command) error { - pc.cmdRegistery[cmd.CliHandler()] = cmd + cliHandler := cmd.CliHandler() + if _, cmdFound := pc.cmdRegistery[cliHandler]; cmdFound { + return fmt.Errorf("Another Command Regsitered for Cli Handler: %s", cliHandler) + } + + pc.cmdRegistery[cliHandler] = cmd return nil } diff --git a/internal/cli/parent_command_test.go b/internal/cli/parent_command_test.go index aeb1ba4..eef8ca5 100644 --- a/internal/cli/parent_command_test.go +++ b/internal/cli/parent_command_test.go @@ -1,6 +1,7 @@ package cli_test import ( + "fmt" "github.com/gojektech/weaver/internal/cli" "github.com/stretchr/testify/assert" "testing" @@ -22,6 +23,16 @@ func TestParentCommandShouldRegisterCommand(t *testing.T) { assert.NoError(t, ts.cmd.RegisterCommand(cmd)) } +func TestParentCommandShouldNotAllowMoreThanOneCommandPerCliHandler(t *testing.T) { + cliHandler := "test" + ts := setupTestParentCommand() + cmd := cli.NewDefaultCommand(cliHandler, "usage", "description", func(c *cli.Context) error { return nil }) + assert.NoError(t, ts.cmd.RegisterCommand(cmd)) + err := ts.cmd.RegisterCommand(cmd) + assert.Error(t, err) + assert.Equal(t, err, fmt.Errorf("Another Command Regsitered for Cli Handler: %s", cliHandler)) +} + func setupTestParentCommand() *testParentCommandSetup { ts := &testParentCommandSetup{name: "parent", usage: "parent usage", description: "parent description"} ts.cmd = cli.NewParentCommand(ts.name, ts.usage, ts.description) From 96002363b9218f40b3572dd9be2185dc89ae656f Mon Sep 17 00:00:00 2001 From: Gowtham Sai Date: Tue, 19 Mar 2019 08:07:06 +0530 Subject: [PATCH 07/26] Parent CMD executs responsible subcommand --- internal/cli/cli.go | 5 ----- internal/cli/parent_command.go | 10 ++++++++++ internal/cli/parent_command_test.go | 18 ++++++++++++++++++ 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/internal/cli/cli.go b/internal/cli/cli.go index 0fb30ec..429436b 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -8,11 +8,6 @@ type Context struct { *baseCli.Context } -type CLIHandler interface { - GetCommand() *baseCli.Command - Exec(c *Context) error -} - func NewApp() *baseCli.App { app := baseCli.NewApp() app.Flags = []baseCli.Flag{ diff --git a/internal/cli/parent_command.go b/internal/cli/parent_command.go index 0cac51a..6ab3ca6 100644 --- a/internal/cli/parent_command.go +++ b/internal/cli/parent_command.go @@ -2,6 +2,7 @@ package cli import ( "fmt" + "strings" ) type ParentCommand struct { @@ -19,6 +20,15 @@ func (pc *ParentCommand) RegisterCommand(cmd *Command) error { return nil } +func (pc *ParentCommand) Exec(c *Context) error { + fmt.Println(c.Command.FullName()) + cliHandler := strings.Split(c.Command.FullName(), " ")[0] + if cmd, cmdFound := pc.cmdRegistery[cliHandler]; cmdFound { + return cmd.Exec(c) + } + return fmt.Errorf("No Command not registered for :%s", c.Command.FullName()) +} + func NewParentCommand(name, usage, description string) *ParentCommand { return &ParentCommand{cmdRegistery: make(map[string]*Command), Command: &Command{name: name}} } diff --git a/internal/cli/parent_command_test.go b/internal/cli/parent_command_test.go index eef8ca5..8da0a99 100644 --- a/internal/cli/parent_command_test.go +++ b/internal/cli/parent_command_test.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/gojektech/weaver/internal/cli" "github.com/stretchr/testify/assert" + baseCli "gopkg.in/urfave/cli.v1" "testing" ) @@ -33,6 +34,23 @@ func TestParentCommandShouldNotAllowMoreThanOneCommandPerCliHandler(t *testing.T assert.Equal(t, err, fmt.Errorf("Another Command Regsitered for Cli Handler: %s", cliHandler)) } +func TestParentCommandShouldExecuteSubCommand(t *testing.T) { + ts := setupTestParentCommand() + isCmdOneCalled := false + isCmdTwoCalled := false + cmdOne := cli.NewDefaultCommand("test-one", "usage", "description", func(c *cli.Context) error { isCmdOneCalled = true; return nil }) + cmdTwo := cli.NewDefaultCommand("test-two", "usage", "description", func(c *cli.Context) error { isCmdTwoCalled = true; return nil }) + errFromCmdOne := ts.cmd.RegisterCommand(cmdOne) + errFromCmdTwo := ts.cmd.RegisterCommand(cmdTwo) + ctx := &cli.Context{Context: &baseCli.Context{Command: baseCli.Command{Name: "test-one"}}} + ts.cmd.Exec(ctx) + + assert.NoError(t, errFromCmdOne) + assert.NoError(t, errFromCmdTwo) + assert.True(t, isCmdOneCalled) + assert.False(t, isCmdTwoCalled) +} + func setupTestParentCommand() *testParentCommandSetup { ts := &testParentCommandSetup{name: "parent", usage: "parent usage", description: "parent description"} ts.cmd = cli.NewParentCommand(ts.name, ts.usage, ts.description) From 89f70354e6a4c46f1cd2cc369567793aa2ace944 Mon Sep 17 00:00:00 2001 From: Gowtham Sai Date: Sun, 24 Mar 2019 01:13:31 +0530 Subject: [PATCH 08/26] Refactor by merging parent command to command --- internal/cli/command.go | 45 ++++++++++++++++++++++++++--- internal/cli/parent_command.go | 34 ---------------------- internal/cli/parent_command_test.go | 2 +- 3 files changed, 42 insertions(+), 39 deletions(-) delete mode 100644 internal/cli/parent_command.go diff --git a/internal/cli/command.go b/internal/cli/command.go index fa04bee..03a7254 100644 --- a/internal/cli/command.go +++ b/internal/cli/command.go @@ -1,20 +1,57 @@ package cli +import ( + "fmt" + "strings" +) + type cmdAction func(c *Context) error type Command struct { - name string - action cmdAction + name string + action cmdAction + subCommands Commands } +type Commands []*Command + func (cmd *Command) CliHandler() string { return cmd.name } func (cmd *Command) Exec(c *Context) error { - return cmd.action(c) + if cmd.action != nil { + return cmd.action(c) + } else { + cliHandler := strings.Split(c.Command.FullName(), " ")[0] + for _, eachCmd := range cmd.subCommands { + if eachCmd.CliHandler() == cliHandler { + return eachCmd.Exec(c) + } + } + } + return fmt.Errorf("No Command not registered for :%s", c.Command.FullName()) +} + +func (pc *Command) RegisterCommand(cmd *Command) error { + if pc.action == nil { + cliHandler := cmd.CliHandler() + for _, eachCmd := range pc.subCommands { + if eachCmd.CliHandler() == cliHandler { + return fmt.Errorf("Another Command Regsitered for Cli Handler: %s", cliHandler) + } + } + pc.subCommands = append(pc.subCommands, cmd) + return nil + } else { + return fmt.Errorf("Command Does Not Allow SubCommand Registration") + } } func NewDefaultCommand(name, usage, description string, action cmdAction) *Command { - return &Command{name, action} + return &Command{name: name, action: action} +} + +func NewParentCommand(name, usage, description string) *Command { + return &Command{name: name, subCommands: []*Command{}} } diff --git a/internal/cli/parent_command.go b/internal/cli/parent_command.go deleted file mode 100644 index 6ab3ca6..0000000 --- a/internal/cli/parent_command.go +++ /dev/null @@ -1,34 +0,0 @@ -package cli - -import ( - "fmt" - "strings" -) - -type ParentCommand struct { - cmdRegistery map[string]*Command - *Command -} - -func (pc *ParentCommand) RegisterCommand(cmd *Command) error { - cliHandler := cmd.CliHandler() - if _, cmdFound := pc.cmdRegistery[cliHandler]; cmdFound { - return fmt.Errorf("Another Command Regsitered for Cli Handler: %s", cliHandler) - } - - pc.cmdRegistery[cliHandler] = cmd - return nil -} - -func (pc *ParentCommand) Exec(c *Context) error { - fmt.Println(c.Command.FullName()) - cliHandler := strings.Split(c.Command.FullName(), " ")[0] - if cmd, cmdFound := pc.cmdRegistery[cliHandler]; cmdFound { - return cmd.Exec(c) - } - return fmt.Errorf("No Command not registered for :%s", c.Command.FullName()) -} - -func NewParentCommand(name, usage, description string) *ParentCommand { - return &ParentCommand{cmdRegistery: make(map[string]*Command), Command: &Command{name: name}} -} diff --git a/internal/cli/parent_command_test.go b/internal/cli/parent_command_test.go index 8da0a99..7870117 100644 --- a/internal/cli/parent_command_test.go +++ b/internal/cli/parent_command_test.go @@ -10,7 +10,7 @@ import ( type testParentCommandSetup struct { name, usage, description string - cmd *cli.ParentCommand + cmd *cli.Command } func TestParentCommandInitialization(t *testing.T) { From 065b9e8c5e224db29db33f408236550dde364177 Mon Sep 17 00:00:00 2001 From: Gowtham Sai Date: Sun, 24 Mar 2019 14:37:31 +0530 Subject: [PATCH 09/26] Parent command can execute before action --- internal/cli/cli.go | 2 ++ internal/cli/command.go | 19 +++++++++++++------ internal/cli/parent_command_test.go | 26 ++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/internal/cli/cli.go b/internal/cli/cli.go index 429436b..b6cc6cb 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -1,10 +1,12 @@ package cli import ( + "github.com/gojektech/weaver/etcd" baseCli "gopkg.in/urfave/cli.v1" ) type Context struct { + RouteLoader *etcd.RouteLoader *baseCli.Context } diff --git a/internal/cli/command.go b/internal/cli/command.go index 03a7254..ae4f2a9 100644 --- a/internal/cli/command.go +++ b/internal/cli/command.go @@ -8,9 +8,10 @@ import ( type cmdAction func(c *Context) error type Command struct { - name string - action cmdAction - subCommands Commands + name string + action cmdAction + subCommands Commands + isParentCommand bool } type Commands []*Command @@ -20,9 +21,11 @@ func (cmd *Command) CliHandler() string { } func (cmd *Command) Exec(c *Context) error { - if cmd.action != nil { + if !cmd.isParentCommand { return cmd.action(c) } else { + cmd.action(c) + cliHandler := strings.Split(c.Command.FullName(), " ")[0] for _, eachCmd := range cmd.subCommands { if eachCmd.CliHandler() == cliHandler { @@ -34,7 +37,7 @@ func (cmd *Command) Exec(c *Context) error { } func (pc *Command) RegisterCommand(cmd *Command) error { - if pc.action == nil { + if pc.isParentCommand { cliHandler := cmd.CliHandler() for _, eachCmd := range pc.subCommands { if eachCmd.CliHandler() == cliHandler { @@ -53,5 +56,9 @@ func NewDefaultCommand(name, usage, description string, action cmdAction) *Comma } func NewParentCommand(name, usage, description string) *Command { - return &Command{name: name, subCommands: []*Command{}} + return &Command{name: name, subCommands: []*Command{}, isParentCommand: true, action: func(c *Context) error { return nil }} +} + +func NewParentCommandWithAction(name, usage, description string, action cmdAction) *Command { + return &Command{name: name, subCommands: []*Command{}, isParentCommand: true, action: action} } diff --git a/internal/cli/parent_command_test.go b/internal/cli/parent_command_test.go index 7870117..2c0df91 100644 --- a/internal/cli/parent_command_test.go +++ b/internal/cli/parent_command_test.go @@ -18,6 +18,11 @@ func TestParentCommandInitialization(t *testing.T) { assert.NotNil(t, ts.cmd) } +func TestParentCommandShouldBeAbleToInitializeWithAction(t *testing.T) { + ts := setupTestParentCommandWithAction() + assert.NotNil(t, ts.cmd) +} + func TestParentCommandShouldRegisterCommand(t *testing.T) { ts := setupTestParentCommand() cmd := cli.NewDefaultCommand("test", "usage", "description", func(c *cli.Context) error { return nil }) @@ -42,6 +47,7 @@ func TestParentCommandShouldExecuteSubCommand(t *testing.T) { cmdTwo := cli.NewDefaultCommand("test-two", "usage", "description", func(c *cli.Context) error { isCmdTwoCalled = true; return nil }) errFromCmdOne := ts.cmd.RegisterCommand(cmdOne) errFromCmdTwo := ts.cmd.RegisterCommand(cmdTwo) + ctx := &cli.Context{Context: &baseCli.Context{Command: baseCli.Command{Name: "test-one"}}} ts.cmd.Exec(ctx) @@ -51,8 +57,28 @@ func TestParentCommandShouldExecuteSubCommand(t *testing.T) { assert.False(t, isCmdTwoCalled) } +func TestParentCommandBeforeActionShouldBeCalledFirst(t *testing.T) { + ts := setupTestParentCommandWithAction() + orderOfExecution := []string{} + ts.cmd = cli.NewParentCommandWithAction(ts.name, ts.usage, ts.description, func(c *cli.Context) error { orderOfExecution = append(orderOfExecution, "parent"); return nil }) + cmdOne := cli.NewDefaultCommand("test-one", "usage", "description", func(c *cli.Context) error { orderOfExecution = append(orderOfExecution, "child"); return nil }) + errFromCmdOne := ts.cmd.RegisterCommand(cmdOne) + + ctx := &cli.Context{Context: &baseCli.Context{Command: baseCli.Command{Name: "test-one"}}} + ts.cmd.Exec(ctx) + + assert.NoError(t, errFromCmdOne) + assert.Equal(t, orderOfExecution, []string{"parent", "child"}) +} + func setupTestParentCommand() *testParentCommandSetup { ts := &testParentCommandSetup{name: "parent", usage: "parent usage", description: "parent description"} ts.cmd = cli.NewParentCommand(ts.name, ts.usage, ts.description) return ts } + +func setupTestParentCommandWithAction() *testParentCommandSetup { + ts := &testParentCommandSetup{name: "parent", usage: "parent usage", description: "parent description"} + ts.cmd = cli.NewParentCommandWithAction(ts.name, ts.usage, ts.description, func(c *cli.Context) error { return nil }) + return ts +} From 3c346402a38e9a290ed4da6cec811e874e800a41 Mon Sep 17 00:00:00 2001 From: Gowtham Sai Date: Mon, 25 Mar 2019 02:02:51 +0530 Subject: [PATCH 10/26] Cli can register base commands --- internal/cli/cli.go | 14 ++++++++++++++ internal/cli/cli_test.go | 34 ++++++++++++++++++++++++++++++++++ internal/cli/command.go | 4 ++-- 3 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 internal/cli/cli_test.go diff --git a/internal/cli/cli.go b/internal/cli/cli.go index b6cc6cb..c9dde7c 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -1,15 +1,29 @@ package cli import ( + "fmt" "github.com/gojektech/weaver/etcd" baseCli "gopkg.in/urfave/cli.v1" ) +var registeredCommands = Commands{} + type Context struct { RouteLoader *etcd.RouteLoader *baseCli.Context } +func RegisterAsBaseCommand(cmd *Command) error { + cliHandler := cmd.CliHandler() + for _, eachCmd := range registeredCommands { + if eachCmd.CliHandler() == cliHandler { + return fmt.Errorf("Another Command Regsitered for Cli Handler: %s", cliHandler) + } + } + registeredCommands = append(registeredCommands, cmd) + return nil +} + func NewApp() *baseCli.App { app := baseCli.NewApp() app.Flags = []baseCli.Flag{ diff --git a/internal/cli/cli_test.go b/internal/cli/cli_test.go new file mode 100644 index 0000000..13748e7 --- /dev/null +++ b/internal/cli/cli_test.go @@ -0,0 +1,34 @@ +package cli_test + +import ( + "fmt" + "github.com/gojektech/weaver/internal/cli" + "github.com/stretchr/testify/assert" + "testing" +) + +type testAppSetup struct { + name, usage, description string + cmd *cli.Command +} + +func TestAppShouldRegisterACommand(t *testing.T) { + ts := setupTestApp() + err := cli.RegisterAsBaseCommand(ts.cmd) + assert.NoError(t, err) +} + +func TestAppShouldReturnErrorOnDuplicateRegistration(t *testing.T) { + // This will throw error as previous command is also registered with same cli handler + ts := setupTestApp() + err := cli.RegisterAsBaseCommand(ts.cmd) + assert.Error(t, err) + assert.Equal(t, err, fmt.Errorf("Another Command Regsitered for Cli Handler: %s", ts.name)) +} + +func setupTestApp() *testAppSetup { + setup := &testAppSetup{name: "test", usage: "usage", description: "description"} + action := func(c *cli.Context) error { return nil } + setup.cmd = cli.NewDefaultCommand(setup.name, setup.usage, setup.description, action) + return setup +} diff --git a/internal/cli/command.go b/internal/cli/command.go index ae4f2a9..a6e8379 100644 --- a/internal/cli/command.go +++ b/internal/cli/command.go @@ -56,9 +56,9 @@ func NewDefaultCommand(name, usage, description string, action cmdAction) *Comma } func NewParentCommand(name, usage, description string) *Command { - return &Command{name: name, subCommands: []*Command{}, isParentCommand: true, action: func(c *Context) error { return nil }} + return &Command{name: name, subCommands: Commands{}, isParentCommand: true, action: func(c *Context) error { return nil }} } func NewParentCommandWithAction(name, usage, description string, action cmdAction) *Command { - return &Command{name: name, subCommands: []*Command{}, isParentCommand: true, action: action} + return &Command{name: name, subCommands: Commands{}, isParentCommand: true, action: action} } From 3adef86b02110587c3e04fe91acba27460c737d3 Mon Sep 17 00:00:00 2001 From: Gowtham Sai Date: Mon, 25 Mar 2019 02:38:57 +0530 Subject: [PATCH 11/26] Cli converts commands to base commands --- internal/cli/cli.go | 27 +++++++++++++++++++++++++++ internal/cli/cli_test.go | 10 ++++++++++ internal/cli/command.go | 8 +++++--- 3 files changed, 42 insertions(+), 3 deletions(-) diff --git a/internal/cli/cli.go b/internal/cli/cli.go index c9dde7c..44c6968 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -2,8 +2,11 @@ package cli import ( "fmt" + "github.com/gojektech/weaver/config" "github.com/gojektech/weaver/etcd" + "github.com/gojektech/weaver/pkg/logger" baseCli "gopkg.in/urfave/cli.v1" + "os" ) var registeredCommands = Commands{} @@ -24,6 +27,30 @@ func RegisterAsBaseCommand(cmd *Command) error { return nil } +func GetBaseCommands() []*baseCli.Command { + baseCliCommands := []*baseCli.Command{} + for _, eachCmd := range registeredCommands { + baseCmd := &baseCli.Command{ + Name: eachCmd.name, + Usage: eachCmd.usage, + Description: eachCmd.description, + Action: func(ctx *baseCli.Context) error { + c := &Context{Context: ctx} + setup(c) + return eachCmd.Exec(c) + }, + } + baseCliCommands = append(baseCliCommands, baseCmd) + } + return baseCliCommands +} + +func setup(c *Context) { + os.Setenv("LOGGER_LEVEL", c.GlobalString("verbose")) + config.Load() + logger.SetupLogger() +} + func NewApp() *baseCli.App { app := baseCli.NewApp() app.Flags = []baseCli.Flag{ diff --git a/internal/cli/cli_test.go b/internal/cli/cli_test.go index 13748e7..96bc2a2 100644 --- a/internal/cli/cli_test.go +++ b/internal/cli/cli_test.go @@ -26,6 +26,16 @@ func TestAppShouldReturnErrorOnDuplicateRegistration(t *testing.T) { assert.Equal(t, err, fmt.Errorf("Another Command Regsitered for Cli Handler: %s", ts.name)) } +func TestCliGetCommandsShouldGiveCobraCommands(t *testing.T) { + ts := setupTestApp() + baseCliCommands := cli.GetBaseCommands() + + assert.Equal(t, len(baseCliCommands), 1) + assert.Equal(t, baseCliCommands[0].Name, ts.name) + assert.Equal(t, baseCliCommands[0].Usage, ts.usage) + assert.Equal(t, baseCliCommands[0].Description, ts.description) +} + func setupTestApp() *testAppSetup { setup := &testAppSetup{name: "test", usage: "usage", description: "description"} action := func(c *cli.Context) error { return nil } diff --git a/internal/cli/command.go b/internal/cli/command.go index a6e8379..5030e71 100644 --- a/internal/cli/command.go +++ b/internal/cli/command.go @@ -10,6 +10,8 @@ type cmdAction func(c *Context) error type Command struct { name string action cmdAction + usage string + description string subCommands Commands isParentCommand bool } @@ -52,13 +54,13 @@ func (pc *Command) RegisterCommand(cmd *Command) error { } func NewDefaultCommand(name, usage, description string, action cmdAction) *Command { - return &Command{name: name, action: action} + return &Command{name: name, usage: usage, description: description, action: action} } func NewParentCommand(name, usage, description string) *Command { - return &Command{name: name, subCommands: Commands{}, isParentCommand: true, action: func(c *Context) error { return nil }} + return &Command{name: name, usage: usage, description: description, subCommands: Commands{}, isParentCommand: true, action: func(c *Context) error { return nil }} } func NewParentCommandWithAction(name, usage, description string, action cmdAction) *Command { - return &Command{name: name, subCommands: Commands{}, isParentCommand: true, action: action} + return &Command{name: name, usage: usage, description: description, subCommands: Commands{}, isParentCommand: true, action: action} } From 6bc5329d4bba7478ff11e4d88d25f35739c77e1e Mon Sep 17 00:00:00 2001 From: Gowtham Sai Date: Tue, 26 Mar 2019 01:53:05 +0530 Subject: [PATCH 12/26] Cli setup logger and config before passing control to commands --- cmd/weaver/main.go | 2 +- internal/cli/cli.go | 6 +++--- internal/cli/cli_test.go | 18 ++++++++++++++++++ 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/cmd/weaver/main.go b/cmd/weaver/main.go index b85ae06..b0b6de9 100644 --- a/cmd/weaver/main.go +++ b/cmd/weaver/main.go @@ -11,7 +11,7 @@ func main() { app.Name = "Weaver" app.Version = fmt.Sprintf("%s built on %s (commit: %s)", Version, BuildDate, Commit) app.Description = "An Advanced HTTP Reverse Proxy with Dynamic Sharding Strategies" - app.Usage = "Multi Utility CLI Tool" + app.Commands = cli.GetBaseCommands() app.Run(os.Args) } diff --git a/internal/cli/cli.go b/internal/cli/cli.go index 44c6968..79ea69b 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -27,10 +27,10 @@ func RegisterAsBaseCommand(cmd *Command) error { return nil } -func GetBaseCommands() []*baseCli.Command { - baseCliCommands := []*baseCli.Command{} +func GetBaseCommands() []baseCli.Command { + baseCliCommands := []baseCli.Command{} for _, eachCmd := range registeredCommands { - baseCmd := &baseCli.Command{ + baseCmd := baseCli.Command{ Name: eachCmd.name, Usage: eachCmd.usage, Description: eachCmd.description, diff --git a/internal/cli/cli_test.go b/internal/cli/cli_test.go index 96bc2a2..97db094 100644 --- a/internal/cli/cli_test.go +++ b/internal/cli/cli_test.go @@ -2,7 +2,9 @@ package cli_test import ( "fmt" + "github.com/gojektech/weaver/config" "github.com/gojektech/weaver/internal/cli" + "github.com/gojektech/weaver/pkg/logger" "github.com/stretchr/testify/assert" "testing" ) @@ -36,6 +38,22 @@ func TestCliGetCommandsShouldGiveCobraCommands(t *testing.T) { assert.Equal(t, baseCliCommands[0].Description, ts.description) } +func TestCliGetCommandsExecutionSHouldSetupConfigAndLogger(t *testing.T) { + ts := setupTestApp() + err := cli.RegisterAsBaseCommand(ts.cmd) + assert.Error(t, err) + + baseCliCommands := cli.GetBaseCommands() + app := cli.NewApp() + app.Commands = baseCliCommands + app.Run([]string{"binary", "--verbose", "debug", ts.name}) + + // config is supposed to have logger level set + // If it setup logger, logging shouldn't panic + assert.NotPanics(t, func() { logger.Info("Should not panic if logger is setup") }) + assert.Equal(t, config.LogLevel(), "debug") +} + func setupTestApp() *testAppSetup { setup := &testAppSetup{name: "test", usage: "usage", description: "description"} action := func(c *cli.Context) error { return nil } From 02dfcfbb01666aa54cfa95332a470953cad06ccf Mon Sep 17 00:00:00 2001 From: Gowtham Sai Date: Tue, 26 Mar 2019 03:06:22 +0530 Subject: [PATCH 13/26] Introduce flag entity --- internal/cli/flag.go | 9 +++++++++ internal/cli/flag_test.go | 26 ++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 internal/cli/flag.go create mode 100644 internal/cli/flag_test.go diff --git a/internal/cli/flag.go b/internal/cli/flag.go new file mode 100644 index 0000000..1cbe63f --- /dev/null +++ b/internal/cli/flag.go @@ -0,0 +1,9 @@ +package cli + +import ( + baseCli "gopkg.in/urfave/cli.v1" +) + +func NewStringFlag(name, value, usage, env string) baseCli.Flag { + return baseCli.StringFlag{Name: name, Value: value, Usage: usage, EnvVar: env} +} diff --git a/internal/cli/flag_test.go b/internal/cli/flag_test.go new file mode 100644 index 0000000..f9fa044 --- /dev/null +++ b/internal/cli/flag_test.go @@ -0,0 +1,26 @@ +package cli_test + +import ( + "fmt" + "github.com/gojektech/weaver/internal/cli" + "github.com/stretchr/testify/assert" + baseCli "gopkg.in/urfave/cli.v1" + "testing" +) + +type testFlagSetup struct { + name, value, usage, env string + flag baseCli.Flag +} + +func TestInitializationOfStringFlag(t *testing.T) { + ts := setupTestFlag() + + assert.Equal(t, ts.flag.String(), fmt.Sprintf("--%s value\t%s (default: \"%s\") [$%s]", ts.name, ts.usage, ts.value, ts.env)) +} + +func setupTestFlag() *testFlagSetup { + setup := &testFlagSetup{name: "test", value: "default", usage: "use to set value for test", env: "TEST"} + setup.flag = cli.NewStringFlag(setup.name, setup.value, setup.usage, setup.env) + return setup +} From 278dcd88731204256c1ecfb8186136de084c0a3b Mon Sep 17 00:00:00 2001 From: Gowtham Sai Date: Tue, 26 Mar 2019 03:06:44 +0530 Subject: [PATCH 14/26] Command can set flag for a command --- internal/cli/command.go | 7 +++++++ internal/cli/command_test.go | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/internal/cli/command.go b/internal/cli/command.go index 5030e71..b60f432 100644 --- a/internal/cli/command.go +++ b/internal/cli/command.go @@ -2,6 +2,7 @@ package cli import ( "fmt" + baseCli "gopkg.in/urfave/cli.v1" "strings" ) @@ -13,6 +14,7 @@ type Command struct { usage string description string subCommands Commands + flags []baseCli.Flag isParentCommand bool } @@ -22,6 +24,11 @@ func (cmd *Command) CliHandler() string { return cmd.name } +func (cmd *Command) SetFlag(flag baseCli.Flag) error { + cmd.flags = append(cmd.flags, flag) + return nil +} + func (cmd *Command) Exec(c *Context) error { if !cmd.isParentCommand { return cmd.action(c) diff --git a/internal/cli/command_test.go b/internal/cli/command_test.go index 993babb..5b225aa 100644 --- a/internal/cli/command_test.go +++ b/internal/cli/command_test.go @@ -28,6 +28,12 @@ func TestCommandShouldExecuteSpecifiedAction(t *testing.T) { assert.True(t, ts.isCalled) } +func TestShouldBeAbleToSetFlags(t *testing.T) { + ts := setupTestCommand() + flag := cli.NewStringFlag("test", "value", "usage", "env") + assert.NotPanics(t, func() { ts.cmd.SetFlag(flag) }, "Setting a flag panics") +} + func setupTestCommand() *testCommandSetup { setup := &testCommandSetup{name: "test", usage: "usage", description: "description", isCalled: false} action := func(c *cli.Context) error { setup.isCalled = true; return nil } From b5e0c1bde36e3fdc5826221d7a61fecc0f65a576 Mon Sep 17 00:00:00 2001 From: Gowtham Sai Date: Tue, 26 Mar 2019 03:07:11 +0530 Subject: [PATCH 15/26] cli considers command flags while constructing app --- internal/cli/cli.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/internal/cli/cli.go b/internal/cli/cli.go index 79ea69b..e3a5285 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -34,6 +34,7 @@ func GetBaseCommands() []baseCli.Command { Name: eachCmd.name, Usage: eachCmd.usage, Description: eachCmd.description, + Flags: eachCmd.flags, Action: func(ctx *baseCli.Context) error { c := &Context{Context: ctx} setup(c) @@ -54,12 +55,7 @@ func setup(c *Context) { func NewApp() *baseCli.App { app := baseCli.NewApp() app.Flags = []baseCli.Flag{ - baseCli.StringFlag{ - Name: "verbose", - Value: "Error", - Usage: "Verbosity of log level, ex: debug, info, warn, error, fatal, panic", - EnvVar: "LOGGER_LEVEL", - }, + NewStringFlag("verbose", "Error", "Verbosity of log level, ex: debug, info, warn, error, fatal, panic", "LOGGER_LEVEL"), } return app } From a64e9524e9c97b590f55cfb4781ddddcb4927ff9 Mon Sep 17 00:00:00 2001 From: Gowtham Sai Date: Wed, 27 Mar 2019 03:19:30 +0530 Subject: [PATCH 16/26] App run with strings args gurantees execution of base command --- internal/cli/cli_test.go | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/internal/cli/cli_test.go b/internal/cli/cli_test.go index 97db094..510be6b 100644 --- a/internal/cli/cli_test.go +++ b/internal/cli/cli_test.go @@ -24,11 +24,12 @@ func TestAppShouldReturnErrorOnDuplicateRegistration(t *testing.T) { // This will throw error as previous command is also registered with same cli handler ts := setupTestApp() err := cli.RegisterAsBaseCommand(ts.cmd) + assert.Error(t, err) assert.Equal(t, err, fmt.Errorf("Another Command Regsitered for Cli Handler: %s", ts.name)) } -func TestCliGetCommandsShouldGiveCobraCommands(t *testing.T) { +func TestCliGetCommandsShouldGiveBaseCommands(t *testing.T) { ts := setupTestApp() baseCliCommands := cli.GetBaseCommands() @@ -40,8 +41,6 @@ func TestCliGetCommandsShouldGiveCobraCommands(t *testing.T) { func TestCliGetCommandsExecutionSHouldSetupConfigAndLogger(t *testing.T) { ts := setupTestApp() - err := cli.RegisterAsBaseCommand(ts.cmd) - assert.Error(t, err) baseCliCommands := cli.GetBaseCommands() app := cli.NewApp() @@ -54,6 +53,22 @@ func TestCliGetCommandsExecutionSHouldSetupConfigAndLogger(t *testing.T) { assert.Equal(t, config.LogLevel(), "debug") } +func TestAppRunWithOSArgsShouldExecuteBaseCommandAction(t *testing.T) { + isCmdActionExecuted := false + ts := setupTestApp() + ts.name = "exec-test" + ts.cmd = cli.NewDefaultCommand(ts.name, ts.usage, ts.description, func(c *cli.Context) error { isCmdActionExecuted = true; return nil }) + err := cli.RegisterAsBaseCommand(ts.cmd) + assert.NoError(t, err) + + baseCliCommands := cli.GetBaseCommands() + app := cli.NewApp() + app.Commands = baseCliCommands + app.Run([]string{"binary", "--verbose", "debug", ts.name}) + + assert.True(t, isCmdActionExecuted) +} + func setupTestApp() *testAppSetup { setup := &testAppSetup{name: "test", usage: "usage", description: "description"} action := func(c *cli.Context) error { return nil } From a788ab84e7dff4d0c820a7894ae9b2aa9a19d4c6 Mon Sep 17 00:00:00 2001 From: Gowtham Sai Date: Wed, 27 Mar 2019 03:58:31 +0530 Subject: [PATCH 17/26] Cli respects Parent Command and Registers all SubCommands --- internal/cli/cli.go | 30 +++++++++++++++++++++++++----- internal/cli/cli_test.go | 24 ++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/internal/cli/cli.go b/internal/cli/cli.go index e3a5285..a9a18aa 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -28,28 +28,48 @@ func RegisterAsBaseCommand(cmd *Command) error { } func GetBaseCommands() []baseCli.Command { + return getBaseCommandWithActionWrapper(registeredCommands, setup) +} + +func getBaseCommandWithActionWrapper(cmds Commands, fn cmdAction) []baseCli.Command { + if fn == nil { + fn = func(c *Context) error { return nil } + } + baseCliCommands := []baseCli.Command{} - for _, eachCmd := range registeredCommands { + for _, eachCmd := range cmds { baseCmd := baseCli.Command{ Name: eachCmd.name, Usage: eachCmd.usage, Description: eachCmd.description, Flags: eachCmd.flags, - Action: func(ctx *baseCli.Context) error { + } + if eachCmd.isParentCommand { + baseCmd.Subcommands = getBaseCommandWithActionWrapper( + eachCmd.subCommands, + func(c *Context) error { + fn(c) + eachCmd.Exec(c) + return nil + }, + ) + } else { + baseCmd.Action = func(ctx *baseCli.Context) error { c := &Context{Context: ctx} - setup(c) + fn(c) return eachCmd.Exec(c) - }, + } } baseCliCommands = append(baseCliCommands, baseCmd) } return baseCliCommands } -func setup(c *Context) { +func setup(c *Context) error { os.Setenv("LOGGER_LEVEL", c.GlobalString("verbose")) config.Load() logger.SetupLogger() + return nil } func NewApp() *baseCli.App { diff --git a/internal/cli/cli_test.go b/internal/cli/cli_test.go index 510be6b..74e19c5 100644 --- a/internal/cli/cli_test.go +++ b/internal/cli/cli_test.go @@ -69,6 +69,30 @@ func TestAppRunWithOSArgsShouldExecuteBaseCommandAction(t *testing.T) { assert.True(t, isCmdActionExecuted) } +func TestAppRunWithOSArgsShouldExecuteParentsSubCommand(t *testing.T) { + isParentActionExecuted := false + isCmdActionExecuted := false + ts := setupTestApp() + parentCmd := cli.NewParentCommandWithAction("parent", "parent-usage", "parent-desc", func(c *cli.Context) error { isParentActionExecuted = true; return nil }) + + ts.name = "exec-test-sub-command" + ts.cmd = cli.NewDefaultCommand(ts.name, ts.usage, ts.description, func(c *cli.Context) error { isCmdActionExecuted = true; return nil }) + + errFromBaseCommandRegistration := cli.RegisterAsBaseCommand(parentCmd) + assert.NoError(t, errFromBaseCommandRegistration) + + errFromSubCommandRegistration := parentCmd.RegisterCommand(ts.cmd) + assert.NoError(t, errFromSubCommandRegistration) + + baseCliCommands := cli.GetBaseCommands() + app := cli.NewApp() + app.Commands = baseCliCommands + app.Run([]string{"binary", "--verbose", "debug", "parent", ts.name}) + + assert.True(t, isParentActionExecuted) + assert.True(t, isCmdActionExecuted) +} + func setupTestApp() *testAppSetup { setup := &testAppSetup{name: "test", usage: "usage", description: "description"} action := func(c *cli.Context) error { return nil } From 8532d4e2a2813e1f4ab144abd0fc9e1af0daf2ec Mon Sep 17 00:00:00 2001 From: Gowtham Sai Date: Wed, 27 Mar 2019 04:21:04 +0530 Subject: [PATCH 18/26] Basic server start command implementation Advantage of this can seperate cmds, and import only the ones that are required --- cmd/weaver/main.go | 2 ++ go.mod | 6 ++--- go.sum | 12 ++++----- internal/commands/commands.go | 8 ++++++ internal/commands/server/server.go | 39 ++++++++++++++++++++++++++++++ internal/commands/server/start.go | 39 ++++++++++++++++++++++++++++++ 6 files changed, 97 insertions(+), 9 deletions(-) create mode 100644 internal/commands/commands.go create mode 100644 internal/commands/server/server.go create mode 100644 internal/commands/server/start.go diff --git a/cmd/weaver/main.go b/cmd/weaver/main.go index b0b6de9..39702f0 100644 --- a/cmd/weaver/main.go +++ b/cmd/weaver/main.go @@ -3,6 +3,8 @@ package main import ( "fmt" "github.com/gojektech/weaver/internal/cli" + _ "github.com/gojektech/weaver/internal/commands" + _ "github.com/gojektech/weaver/internal/commands/server" "os" ) diff --git a/go.mod b/go.mod index 495000f..147c810 100644 --- a/go.mod +++ b/go.mod @@ -56,9 +56,9 @@ require ( github.com/vulcand/route v0.0.0-20160805191529-61904570391b github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect go.etcd.io/bbolt v1.3.2 // indirect - golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a // indirect - golang.org/x/net v0.0.0-20190313220215-9f648a60d977 // indirect - golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f // indirect + golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576 // indirect + golang.org/x/net v0.0.0-20190322120337-addf6b3196f6 // indirect + golang.org/x/sys v0.0.0-20190322080309-f49334f85ddc // indirect golang.org/x/time v0.0.0-20181108054448-85acf8d2951c // indirect google.golang.org/grpc v1.18.0 // indirect gopkg.in/airbrake/gobrake.v2 v2.0.9 // indirect diff --git a/go.sum b/go.sum index c9ec799..fa564c4 100644 --- a/go.sum +++ b/go.sum @@ -134,15 +134,15 @@ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q go.etcd.io/bbolt v1.3.2 h1:Z/90sZLPOeCy2PwprqkFa25PdkusRzaj9P8zm/KNyvk= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a h1:YX8ljsm6wXlHZO+aRz9Exqr0evNhKRNe5K/gi+zKh4U= -golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576 h1:aUX/1G2gFSs4AsJJg2cL3HuoRhCSCz733FE5GUSuaT4= +golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc h1:a3CU5tJYVj92DY2LaA1kUkrsqD5/3mLDhx2NcNqyW+0= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190313220215-9f648a60d977 h1:actzWV6iWn3GLqN8dZjzsB+CLt+gaV2+wsxroxiQI8I= -golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190322120337-addf6b3196f6 h1:78jEq2G3J16aXneH23HSnTQQTCwMHoyO8VEiUH+bpPM= +golang.org/x/net v0.0.0-20190322120337-addf6b3196f6/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ= @@ -151,8 +151,8 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e h1:o3PsSEY8E4eXWkXrIP9YJALUkVZqzHJT5DOasTyn8Vs= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f h1:yCrMx/EeIue0+Qca57bWZS7VX6ymEoypmhWyPhz0NHM= -golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190322080309-f49334f85ddc h1:4gbWbmmPFp4ySWICouJl6emP0MyS31yy9SrTlAGFT+g= +golang.org/x/sys v0.0.0-20190322080309-f49334f85ddc/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c h1:fqgJT0MGcGpPgpWU7VRdRjuArfcOvC4AoJmILihzhDg= diff --git a/internal/commands/commands.go b/internal/commands/commands.go new file mode 100644 index 0000000..71e6101 --- /dev/null +++ b/internal/commands/commands.go @@ -0,0 +1,8 @@ +package commands + +import ( + "github.com/gojektech/weaver/internal/cli" +) + +var ETCDFLAG = cli.NewStringFlag("etcd-host, etcd", "http://localhost:2379", "HOST Address of ETCD", "ETCD_ENDPOINTS") +var NAMESPACEFLAG = cli.NewStringFlag("namespace, ns", "weaver", "Namespace of Weaver ACLS", "ETCD_KEY_PREFIX") diff --git a/internal/commands/server/server.go b/internal/commands/server/server.go new file mode 100644 index 0000000..35b3f16 --- /dev/null +++ b/internal/commands/server/server.go @@ -0,0 +1,39 @@ +package server + +import ( + "github.com/gojektech/weaver/config" + "github.com/gojektech/weaver/etcd" + "github.com/gojektech/weaver/internal/cli" + "github.com/gojektech/weaver/internal/commands" + "github.com/gojektech/weaver/pkg/logger" + "os" +) + +const ( + serverCmdName = "server" + serverCmdUsage = "Weaver - Run Server" + serverCmdDescription = "Weaver - Run Server" +) + +var weaverServerCmd = cli.NewParentCommandWithAction(serverCmdName, serverCmdName, serverCmdDescription, setupServer) + +func setupServer(c *cli.Context) error { + os.Setenv("ETCD_ENDPOINTS", c.GlobalString("etcd-host")) + os.Setenv("ETCD_KEY_PREFIX", c.GlobalString("namespace")) + config.Load() + rl, err := etcd.NewRouteLoader() + if err == nil { + c.RouteLoader = rl + return nil + } else { + logger.Fatalf("Couldn't create route loader: %s", err) + os.Exit(1) + } + return nil +} + +func init() { + weaverServerCmd.SetFlag(commands.ETCDFLAG) + weaverServerCmd.SetFlag(commands.NAMESPACEFLAG) + cli.RegisterAsBaseCommand(weaverServerCmd) +} diff --git a/internal/commands/server/start.go b/internal/commands/server/start.go new file mode 100644 index 0000000..eee0171 --- /dev/null +++ b/internal/commands/server/start.go @@ -0,0 +1,39 @@ +package server + +import ( + "context" + "github.com/gojektech/weaver/internal/cli" + "github.com/gojektech/weaver/pkg/logger" + "github.com/gojektech/weaver/server" + "os" + "os/signal" + "syscall" +) + +const ( + startCmdName = "start" + startCmdUsage = "Run Weaver server" + startCmdDescription = "Run Weaver server" +) + +var serverStartCmd = cli.NewDefaultCommand(startCmdName, startCmdUsage, startCmdDescription, startServer) + +func startServer(c *cli.Context) error { + sigC := make(chan os.Signal, 1) + signal.Notify(sigC, syscall.SIGINT, syscall.SIGKILL, syscall.SIGTERM) + + ctx, cancel := context.WithCancel(context.Background()) + go server.StartServer(ctx, c.RouteLoader) + + sig := <-sigC + logger.Infof("Received %d, shutting down", sig) + + defer cancel() + server.ShutdownServer(ctx) + + return nil +} + +func init() { + weaverServerCmd.RegisterCommand(serverStartCmd) +} From c14194cd82137d2ee27f3c732838bc5aadd1de04 Mon Sep 17 00:00:00 2001 From: Gowtham Sai Date: Thu, 28 Mar 2019 04:00:34 +0530 Subject: [PATCH 19/26] Remove custom view layer --- internal/views/acls/list.go | 17 ---------------- internal/views/acls/show.go | 40 ------------------------------------- 2 files changed, 57 deletions(-) delete mode 100644 internal/views/acls/list.go delete mode 100644 internal/views/acls/show.go diff --git a/internal/views/acls/list.go b/internal/views/acls/list.go deleted file mode 100644 index 9315afd..0000000 --- a/internal/views/acls/list.go +++ /dev/null @@ -1,17 +0,0 @@ -package acls - -import ( - "github.com/gojektech/weaver" - "github.com/jedib0t/go-pretty/table" - "os" -) - -func RenderList(acls []*weaver.ACL) { - t := table.NewWriter() - t.SetOutputMirror(os.Stdout) - t.AppendHeader(table.Row{"#", "ID", "Criterion"}) - for id, acl := range acls { - t.AppendRow([]interface{}{id, acl.ID, acl.Criterion}) - } - t.Render() -} diff --git a/internal/views/acls/show.go b/internal/views/acls/show.go deleted file mode 100644 index 1a99c44..0000000 --- a/internal/views/acls/show.go +++ /dev/null @@ -1,40 +0,0 @@ -package acls - -import ( - "encoding/json" - "github.com/gojektech/weaver" - "github.com/gojektech/weaver/pkg/logger" - "github.com/gojektech/weaver/pkg/shard" - "github.com/jedib0t/go-pretty/table" - "os" -) - -func RenderShow(acl *weaver.ACL) { - t := table.NewWriter() - t.SetStyle(table.StyleRounded) - t.SetOutputMirror(os.Stdout) - t.AppendRow([]interface{}{"ID", acl.ID}) - t.AppendRow([]interface{}{"Criterion", acl.Criterion}) - t.AppendRow([]interface{}{"-"}) - t.AppendRow([]interface{}{"EndPoint Config"}) - t.AppendRow([]interface{}{"", "Matcher", acl.EndpointConfig.Matcher}) - t.AppendRow([]interface{}{"", "Shard Expression", acl.EndpointConfig.ShardExpr}) - t.AppendRow([]interface{}{"", "Shard Function", acl.EndpointConfig.ShardFunc}) - - shardConfig := map[string]shard.BackendDefinition{} - if err := json.Unmarshal(acl.EndpointConfig.ShardConfig, &shardConfig); err == nil { - t.AppendRow([]interface{}{"-"}) - t.AppendRow([]interface{}{"Backends"}) - for key, backend := range shardConfig { - t.AppendRow([]interface{}{"", key}) - t.AppendRow([]interface{}{"", "", backend.BackendName}) - t.AppendRow([]interface{}{"", "", backend.BackendURL}) - if backend.Timeout != nil { - t.AppendRow([]interface{}{"", "", backend.Timeout}) - } - } - } else { - logger.Debugf("Error %s", err) - } - t.Render() -} From ed5fcd21b01cba28b1d8ab3b4d07010c2a172346 Mon Sep 17 00:00:00 2001 From: Gowtham Sai Date: Thu, 28 Mar 2019 04:01:07 +0530 Subject: [PATCH 20/26] Etcd router ListAll - to list all valid acls --- etcd/routeloader.go | 29 +++++++++++++++-------------- etcd/routeloader_test.go | 22 ++++++++++++++++++++++ 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/etcd/routeloader.go b/etcd/routeloader.go index 4d08a92..61ee8ab 100644 --- a/etcd/routeloader.go +++ b/etcd/routeloader.go @@ -33,20 +33,7 @@ type RouteLoader struct { namespace string } -// PutACL - Upserts a given ACL -func (routeLoader *RouteLoader) PutACL(acl *weaver.ACL) (ACLKey, error) { - key := GenKey(acl, routeLoader.namespace) - val, err := json.Marshal(acl) - if err != nil { - return "", err - } - _, err = etcd.NewKeysAPI(routeLoader.etcdClient).Set(context.Background(), string(key), string(val), nil) - if err != nil { - return "", fmt.Errorf("fail to PUT %s:%s with %s", key, acl, err.Error()) - } - return key, nil -} - +// ListAll - List all valid weaver acls func (routeLoader *RouteLoader) ListAll() ([]*weaver.ACL, error) { keysAPI, key := initEtcd(routeLoader) res, err := keysAPI.Get(context.Background(), key, nil) @@ -69,6 +56,20 @@ func (routeLoader *RouteLoader) ListAll() ([]*weaver.ACL, error) { return acls, nil } +// PutACL - Upserts a given ACL +func (routeLoader *RouteLoader) PutACL(acl *weaver.ACL) (ACLKey, error) { + key := GenKey(acl, routeLoader.namespace) + val, err := json.Marshal(acl) + if err != nil { + return "", err + } + _, err = etcd.NewKeysAPI(routeLoader.etcdClient).Set(context.Background(), string(key), string(val), nil) + if err != nil { + return "", fmt.Errorf("fail to PUT %s:%s with %s", key, acl, err.Error()) + } + return key, nil +} + // GetACL - Fetches an ACL given an ACLKey func (routeLoader *RouteLoader) GetACL(key ACLKey) (*weaver.ACL, error) { res, err := etcd.NewKeysAPI(routeLoader.etcdClient).Get(context.Background(), string(key), nil) diff --git a/etcd/routeloader_test.go b/etcd/routeloader_test.go index 82a8a4d..66c535a 100644 --- a/etcd/routeloader_test.go +++ b/etcd/routeloader_test.go @@ -43,6 +43,28 @@ func TestRouteLoaderSuite(tst *testing.T) { suite.Run(tst, new(RouteLoaderSuite)) } +func (es *RouteLoaderSuite) TestListAll() { + aclPut := &weaver.ACL{ + ID: "svc-01", + Criterion: "Method(`GET`) && Path(`/ping`)", + EndpointConfig: &weaver.EndpointConfig{ + ShardFunc: "lookup", + Matcher: "path", + ShardExpr: "*", + ShardConfig: json.RawMessage(`{}`), + }, + } + + key, err := es.ng.PutACL(aclPut) + assert.NoError(es.T(), err, "failed to PUT %s", aclPut) + + aclList, err := es.ng.ListAll() + assert.Nil(es.T(), err, "fail to ListAll ACLs") + + deepEqual(es.T(), aclPut, aclList[0]) + assert.Nil(es.T(), es.ng.DelACL(key), "fail to DELETE %+v", aclPut) +} + func (es *RouteLoaderSuite) TestPutACL() { aclPut := &weaver.ACL{ ID: "svc-01", From 6d780ebe949e378952aa7027cf574ea907b1faa2 Mon Sep 17 00:00:00 2001 From: Gowtham Sai Date: Thu, 28 Mar 2019 06:07:26 +0530 Subject: [PATCH 21/26] Views renders anything with json tags to stdout --- internal/views/views.go | 15 +++++++++++++++ internal/views/views_test.go | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 internal/views/views_test.go diff --git a/internal/views/views.go b/internal/views/views.go index e29e57d..35ba0b7 100644 --- a/internal/views/views.go +++ b/internal/views/views.go @@ -1 +1,16 @@ package views + +import ( + "encoding/json" + "fmt" + "github.com/gojektech/weaver/pkg/logger" +) + +func Render(o interface{}) { + jsonBytes, err := json.MarshalIndent(o, "", " ") + if err != nil { + logger.Fatalf("Error marshaling outptu: %s", err) + panic(err) + } + fmt.Println(string(jsonBytes)) +} diff --git a/internal/views/views_test.go b/internal/views/views_test.go new file mode 100644 index 0000000..3964c19 --- /dev/null +++ b/internal/views/views_test.go @@ -0,0 +1,34 @@ +package views_test + +import ( + "github.com/gojektech/weaver/internal/views" + "github.com/stretchr/testify/assert" + "io/ioutil" + "os" + "testing" +) + +func TestShouldPrettyPrintAnyJsonEnabledStruct(t *testing.T) { + realStdout := os.Stdout + reader, fakeStdout, err := os.Pipe() + assert.NoError(t, err, "Error in setting fake stdout") + + os.Stdout = fakeStdout + defer func() { os.Stdout = realStdout }() + views.Render(struct { + Name string `json:"name"` + Age int `json:"age"` + }{"gowtham", 23}) + + fakeStdoutCloseErr := fakeStdout.Close() + assert.NoError(t, fakeStdoutCloseErr, "Error close fake stdout") + + outputBuffer, err := ioutil.ReadAll(reader) + assert.NoError(t, err, "Error in reading output from fake stdout") + assert.Equal(t, string(outputBuffer), + `{ + "name": "gowtham", + "age": 23 +} +`) +} From b8a68707a94a420d40bcba67a86aad48bcea9e95 Mon Sep 17 00:00:00 2001 From: Gowtham Sai Date: Thu, 28 Mar 2019 06:15:35 +0530 Subject: [PATCH 22/26] Correct weaver usage and acl command name --- cmd/weaver/main.go | 2 ++ internal/commands/acls/acl.go | 39 +++++++++++++++++++++++++++++++++ internal/commands/acls/list.go | 40 ++++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+) create mode 100644 internal/commands/acls/acl.go create mode 100644 internal/commands/acls/list.go diff --git a/cmd/weaver/main.go b/cmd/weaver/main.go index 39702f0..a699321 100644 --- a/cmd/weaver/main.go +++ b/cmd/weaver/main.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/gojektech/weaver/internal/cli" _ "github.com/gojektech/weaver/internal/commands" + _ "github.com/gojektech/weaver/internal/commands/acls" _ "github.com/gojektech/weaver/internal/commands/server" "os" ) @@ -12,6 +13,7 @@ func main() { app := cli.NewApp() app.Name = "Weaver" app.Version = fmt.Sprintf("%s built on %s (commit: %s)", Version, BuildDate, Commit) + app.Usage = "Start Server, Perform CRUD on ACLs" app.Description = "An Advanced HTTP Reverse Proxy with Dynamic Sharding Strategies" app.Commands = cli.GetBaseCommands() app.Run(os.Args) diff --git a/internal/commands/acls/acl.go b/internal/commands/acls/acl.go new file mode 100644 index 0000000..bbeb200 --- /dev/null +++ b/internal/commands/acls/acl.go @@ -0,0 +1,39 @@ +package acls + +import ( + "github.com/gojektech/weaver/config" + "github.com/gojektech/weaver/etcd" + "github.com/gojektech/weaver/internal/cli" + "github.com/gojektech/weaver/internal/commands" + "github.com/gojektech/weaver/pkg/logger" + "os" +) + +const ( + aclsCmdName = "acls" + aclsCmdUsage = "ACLs - Perform CRUD Operations" + aclsCmdDescription = "ACLs - Perform CRUD Operations" +) + +var weaverACLSCmd = cli.NewParentCommandWithAction(aclsCmdName, aclsCmdUsage, aclsCmdDescription, setupACLs) + +func setupACLs(c *cli.Context) error { + os.Setenv("ETCD_ENDPOINTS", c.GlobalString("etcd-host")) + os.Setenv("ETCD_KEY_PREFIX", c.GlobalString("namespace")) + config.Load() + rl, err := etcd.NewRouteLoader() + + if err != nil { + logger.Fatalf("Couldn't create route loader: %s", err) + os.Exit(1) + } + + c.RouteLoader = rl + return nil +} + +func init() { + weaverACLSCmd.SetFlag(commands.ETCDFLAG) + weaverACLSCmd.SetFlag(commands.NAMESPACEFLAG) + cli.RegisterAsBaseCommand(weaverACLSCmd) +} diff --git a/internal/commands/acls/list.go b/internal/commands/acls/list.go new file mode 100644 index 0000000..41de337 --- /dev/null +++ b/internal/commands/acls/list.go @@ -0,0 +1,40 @@ +package acls + +import ( + "github.com/gojektech/weaver/internal/cli" + "github.com/gojektech/weaver/internal/views" + "github.com/gojektech/weaver/pkg/logger" +) + +const ( + listCmdName = "list" + listCmdUsage = "List Weaver ACLS in ETCD Under a Namespace" + listCmdDescription = "List Weaver ACLS in ETCD Under a Namespace" +) + +var aclListCmd = cli.NewDefaultCommand(listCmdName, listCmdUsage, listCmdDescription, listACL) + +func listACL(c *cli.Context) error { + acls, err := c.RouteLoader.ListAll() + if err != nil { + logger.Fatalf("Error while listing acls: %s", err) + } + + type aclListInfo struct { + ID string `json:"ACL ID"` + Criterion string `json:"Criterion"` + } + + formattedAcls := []aclListInfo{} + + for _, eachACL := range acls { + formattedAcls = append(formattedAcls, aclListInfo{eachACL.ID, eachACL.Criterion}) + } + + views.Render(formattedAcls) + return nil +} + +func init() { + weaverACLSCmd.RegisterCommand(aclListCmd) +} From 5d79aed1fa58fc024bf82cece9f13c14955d6f1c Mon Sep 17 00:00:00 2001 From: Gowtham Sai Date: Thu, 28 Mar 2019 06:17:37 +0530 Subject: [PATCH 23/26] Refactor to idiomatic go way --- internal/cli/cli_test.go | 6 ++++-- internal/commands/server/server.go | 10 +++++----- internal/commands/server/stop.go | 1 + 3 files changed, 10 insertions(+), 7 deletions(-) create mode 100644 internal/commands/server/stop.go diff --git a/internal/cli/cli_test.go b/internal/cli/cli_test.go index 74e19c5..4a48923 100644 --- a/internal/cli/cli_test.go +++ b/internal/cli/cli_test.go @@ -48,9 +48,11 @@ func TestCliGetCommandsExecutionSHouldSetupConfigAndLogger(t *testing.T) { app.Run([]string{"binary", "--verbose", "debug", ts.name}) // config is supposed to have logger level set - // If it setup logger, logging shouldn't panic - assert.NotPanics(t, func() { logger.Info("Should not panic if logger is setup") }) assert.Equal(t, config.LogLevel(), "debug") + + // If it is setup logger, logging shouldn't panic + msg := "Should not panic if logger is setup" + assert.NotPanics(t, func() { logger.Info(msg) }) } func TestAppRunWithOSArgsShouldExecuteBaseCommandAction(t *testing.T) { diff --git a/internal/commands/server/server.go b/internal/commands/server/server.go index 35b3f16..0f7cc4e 100644 --- a/internal/commands/server/server.go +++ b/internal/commands/server/server.go @@ -15,20 +15,20 @@ const ( serverCmdDescription = "Weaver - Run Server" ) -var weaverServerCmd = cli.NewParentCommandWithAction(serverCmdName, serverCmdName, serverCmdDescription, setupServer) +var weaverServerCmd = cli.NewParentCommandWithAction(serverCmdName, serverCmdUsage, serverCmdDescription, setupServer) func setupServer(c *cli.Context) error { os.Setenv("ETCD_ENDPOINTS", c.GlobalString("etcd-host")) os.Setenv("ETCD_KEY_PREFIX", c.GlobalString("namespace")) config.Load() rl, err := etcd.NewRouteLoader() - if err == nil { - c.RouteLoader = rl - return nil - } else { + + if err != nil { logger.Fatalf("Couldn't create route loader: %s", err) os.Exit(1) } + + c.RouteLoader = rl return nil } diff --git a/internal/commands/server/stop.go b/internal/commands/server/stop.go new file mode 100644 index 0000000..abb4e43 --- /dev/null +++ b/internal/commands/server/stop.go @@ -0,0 +1 @@ +package server From 33e0be9edf70a863d36c850c8eef2cf4ed02a837 Mon Sep 17 00:00:00 2001 From: Gowtham Sai Date: Thu, 28 Mar 2019 06:30:27 +0530 Subject: [PATCH 24/26] Not escaping HTML characters as we are printing to stdout --- internal/views/views.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/internal/views/views.go b/internal/views/views.go index 35ba0b7..0319ea6 100644 --- a/internal/views/views.go +++ b/internal/views/views.go @@ -1,16 +1,22 @@ package views import ( + "bytes" "encoding/json" "fmt" "github.com/gojektech/weaver/pkg/logger" ) func Render(o interface{}) { - jsonBytes, err := json.MarshalIndent(o, "", " ") + buffer := &bytes.Buffer{} + encoder := json.NewEncoder(buffer) + encoder.SetEscapeHTML(false) + encoder.SetIndent("", " ") + + err := encoder.Encode(o) if err != nil { logger.Fatalf("Error marshaling outptu: %s", err) panic(err) } - fmt.Println(string(jsonBytes)) + fmt.Print(string(buffer.Bytes())) } From def254945937a24e8a94b11e89c4cabe6685ee31 Mon Sep 17 00:00:00 2001 From: Gowtham Sai Date: Mon, 1 Apr 2019 00:56:10 +0530 Subject: [PATCH 25/26] Implement acl show command --- internal/cli/cli.go | 7 +++--- internal/commands/acls/list.go | 6 ++--- internal/commands/acls/show.go | 40 ++++++++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 7 deletions(-) create mode 100644 internal/commands/acls/show.go diff --git a/internal/cli/cli.go b/internal/cli/cli.go index a9a18aa..8787128 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -37,7 +37,8 @@ func getBaseCommandWithActionWrapper(cmds Commands, fn cmdAction) []baseCli.Comm } baseCliCommands := []baseCli.Command{} - for _, eachCmd := range cmds { + for idx, _ := range cmds { + eachCmd := cmds[idx] baseCmd := baseCli.Command{ Name: eachCmd.name, Usage: eachCmd.usage, @@ -74,8 +75,6 @@ func setup(c *Context) error { func NewApp() *baseCli.App { app := baseCli.NewApp() - app.Flags = []baseCli.Flag{ - NewStringFlag("verbose", "Error", "Verbosity of log level, ex: debug, info, warn, error, fatal, panic", "LOGGER_LEVEL"), - } + app.Flags = []baseCli.Flag{NewStringFlag("verbose", "Error", "Verbosity of log level, ex: debug, info, warn, error, fatal, panic", "LOGGER_LEVEL")} return app } diff --git a/internal/commands/acls/list.go b/internal/commands/acls/list.go index 41de337..94c4131 100644 --- a/internal/commands/acls/list.go +++ b/internal/commands/acls/list.go @@ -20,15 +20,15 @@ func listACL(c *cli.Context) error { logger.Fatalf("Error while listing acls: %s", err) } - type aclListInfo struct { + type aclInfo struct { ID string `json:"ACL ID"` Criterion string `json:"Criterion"` } - formattedAcls := []aclListInfo{} + formattedAcls := []aclInfo{} for _, eachACL := range acls { - formattedAcls = append(formattedAcls, aclListInfo{eachACL.ID, eachACL.Criterion}) + formattedAcls = append(formattedAcls, aclInfo{eachACL.ID, eachACL.Criterion}) } views.Render(formattedAcls) diff --git a/internal/commands/acls/show.go b/internal/commands/acls/show.go new file mode 100644 index 0000000..d85d52e --- /dev/null +++ b/internal/commands/acls/show.go @@ -0,0 +1,40 @@ +package acls + +import ( + "github.com/gojektech/weaver" + "github.com/gojektech/weaver/internal/cli" + "github.com/gojektech/weaver/internal/views" + "github.com/gojektech/weaver/pkg/logger" +) + +const ( + showCmdName = "show" + showCmdUsage = "Show Weaver ACLS Given ACL ID" + showCmdDescription = "Show Weaver ACLS Given ACL ID" +) + +var aclShowCmd = cli.NewDefaultCommand(showCmdName, showCmdUsage, showCmdDescription, showACL) + +func showACL(c *cli.Context) error { + aclID := c.String("id") + acls, err := c.RouteLoader.ListAll() + if err != nil { + logger.Fatalf("Error while showing acls: %s", err) + } + + aclToShow := []weaver.ACL{} + + for _, eachACL := range acls { + if eachACL.ID == aclID { + aclToShow = append(aclToShow, *eachACL) + } + } + + views.Render(aclToShow) + return nil +} + +func init() { + aclShowCmd.SetFlag(cli.NewStringFlag("id", "", "ID Of the ACL", "")) + weaverACLSCmd.RegisterCommand(aclShowCmd) +} From f35a5e845011693d0f93ce240f4462a28d225878 Mon Sep 17 00:00:00 2001 From: Gowtham Sai Date: Thu, 19 Sep 2019 04:42:59 +0530 Subject: [PATCH 26/26] Display help when acl id is not passed for show sub command --- go.mod | 1 - go.sum | 2 -- internal/commands/acls/show.go | 16 ++++++++++------ internal/views/views.go | 2 +- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index 147c810..ceb3df8 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,6 @@ require ( github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect github.com/grpc-ecosystem/grpc-gateway v1.7.0 // indirect github.com/hashicorp/hcl v0.0.0-20170217164738-630949a3c5fa // indirect - github.com/jedib0t/go-pretty v4.0.2+incompatible github.com/jonboulle/clockwork v0.1.0 // indirect github.com/kr/pretty v0.1.0 // indirect github.com/magiconair/properties v0.0.0-20170113111004-b3b15ef068fd // indirect diff --git a/go.sum b/go.sum index fa564c4..8b283fe 100644 --- a/go.sum +++ b/go.sum @@ -51,8 +51,6 @@ github.com/hashicorp/hcl v0.0.0-20170217164738-630949a3c5fa h1:10wM7X2JKPrmcvtI9 github.com/hashicorp/hcl v0.0.0-20170217164738-630949a3c5fa/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/jedib0t/go-pretty v4.0.2+incompatible h1:bU7rhIKsGljSXgWOdElmiciqy+5AWbDpdmBJwl55PPg= -github.com/jedib0t/go-pretty v4.0.2+incompatible/go.mod h1:XemHduiw8R651AF9Pt4FwCTKeG3oo7hrHJAoznj9nag= github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= diff --git a/internal/commands/acls/show.go b/internal/commands/acls/show.go index d85d52e..c02b791 100644 --- a/internal/commands/acls/show.go +++ b/internal/commands/acls/show.go @@ -1,7 +1,8 @@ package acls import ( - "github.com/gojektech/weaver" + baseCli "gopkg.in/urfave/cli.v1" + "github.com/gojektech/weaver/internal/cli" "github.com/gojektech/weaver/internal/views" "github.com/gojektech/weaver/pkg/logger" @@ -17,20 +18,23 @@ var aclShowCmd = cli.NewDefaultCommand(showCmdName, showCmdUsage, showCmdDescrip func showACL(c *cli.Context) error { aclID := c.String("id") + if aclID == "" { + baseCli.ShowSubcommandHelp(c.Context) + return nil + } acls, err := c.RouteLoader.ListAll() if err != nil { logger.Fatalf("Error while showing acls: %s", err) } - aclToShow := []weaver.ACL{} - for _, eachACL := range acls { if eachACL.ID == aclID { - aclToShow = append(aclToShow, *eachACL) + views.Render(eachACL) + return nil } - } - views.Render(aclToShow) + } + logger.Fatalf("ACL with id: %s not found", aclID) return nil } diff --git a/internal/views/views.go b/internal/views/views.go index 0319ea6..4b21dd2 100644 --- a/internal/views/views.go +++ b/internal/views/views.go @@ -15,7 +15,7 @@ func Render(o interface{}) { err := encoder.Encode(o) if err != nil { - logger.Fatalf("Error marshaling outptu: %s", err) + logger.Fatalf("Error marshaling output: %s", err) panic(err) } fmt.Print(string(buffer.Bytes()))