From a13d473950018d485547d97c8ddffdfae36fe83d Mon Sep 17 00:00:00 2001 From: carla Date: Thu, 16 Jan 2020 11:06:42 +0200 Subject: [PATCH] trmcli: add cli and command for close recommendations --- Makefile | 2 + cmd/trmcli/close_recommendations.go | 79 +++++++++++++++++++++++ cmd/trmcli/main.go | 32 ++++++++++ cmd/trmcli/utils.go | 97 +++++++++++++++++++++++++++++ go.mod | 2 + go.sum | 4 ++ 6 files changed, 216 insertions(+) create mode 100644 cmd/trmcli/close_recommendations.go create mode 100644 cmd/trmcli/main.go create mode 100644 cmd/trmcli/utils.go diff --git a/Makefile b/Makefile index 7881d3b..186827b 100644 --- a/Makefile +++ b/Makefile @@ -58,6 +58,7 @@ $(GOACC_BIN): build: @$(call print, "Building terminator.") $(GOBUILD) $(PKG)/cmd/terminator + $(GOBUILD) $(PKG)/cmd/trmcli install: @$(call print, "Installing terminator.") @@ -125,4 +126,5 @@ list: clean: @$(call print, "Cleaning source.$(NC)") $(RM) ./terminator + $(RM) ./trmcli $(RM) coverage.txt diff --git a/cmd/trmcli/close_recommendations.go b/cmd/trmcli/close_recommendations.go new file mode 100644 index 0000000..05bb978 --- /dev/null +++ b/cmd/trmcli/close_recommendations.go @@ -0,0 +1,79 @@ +package main + +import ( + "context" + "time" + + "github.com/lightninglabs/terminator/trmrpc" + "github.com/urfave/cli" +) + +var ( + defaultMinMonitored = time.Hour * 24 * 7 * 4 // four weeks in hours + defaultOutlierMultiplier = 3 +) + +var closeRecommendationCommand = cli.Command{ + Name: "closerecs", + Category: "channel", + Usage: "Get close recommendations for currently open channels.", + Flags: []cli.Flag{ + cli.Int64Flag{ + Name: "min_monitored", + Usage: "amount of time in seconds a channel should " + + "be monitored for to be eligible for close", + Value: int64(defaultMinMonitored.Seconds()), + }, + cli.StringFlag{ + Name: "outlier_mult", + Usage: "(optional with outlier strategy) Number of " + + "inter quartile ranges a channel should be " + + "from quartiles to be considered an outlier. " + + "Recommended values are 1.5 for aggressive " + + "recommendations and 3 for conservative ones.", + }, + cli.StringFlag{ + Name: "uptime_threshold", + Usage: "(optional) Uptime percentage threshold " + + "underneath which a channel will be recommended " + + "for close.", + }, + }, + Action: queryCloseRecommendations, +} + +func queryCloseRecommendations(ctx *cli.Context) error { + client, cleanup := getClient(ctx) + defer cleanup() + + // Set monitored value from cli and default outlier multiplier. The + // outlier multiplier will be overwritten if the user provided it. + req := &trmrpc.CloseRecommendationsRequest{ + MinimumMonitored: ctx.Int64("min_monitored"), + OutlierMultiplier: float32(defaultOutlierMultiplier), + } + + // If an a custom outlier multiple was set, use it. + if ctx.IsSet("outlier_mult") { + req.OutlierMultiplier = float32(ctx.Float64("outlier_mult")) + } + + // If an uptime threshold was set, use it. + if ctx.IsSet("uptime_threshold") { + uptimeThreshold := float32(ctx.Float64("uptime_threshold")) + req.Threshold = + &trmrpc.CloseRecommendationsRequest_UptimeThreshold{ + UptimeThreshold: uptimeThreshold, + } + } + + rpcCtx := context.Background() + recs, err := client.CloseRecommendations(rpcCtx, req) + if err != nil { + return err + } + + printRespJSON(recs) + + return nil +} diff --git a/cmd/trmcli/main.go b/cmd/trmcli/main.go new file mode 100644 index 0000000..412a835 --- /dev/null +++ b/cmd/trmcli/main.go @@ -0,0 +1,32 @@ +package main + +import ( + "os" + + "github.com/urfave/cli" +) + +var ( + defaultRPCPort = "8419" + defaultRPCHostPort = "localhost:" + defaultRPCPort +) + +func main() { + app := cli.NewApp() + app.Name = "trmcli" + app.Usage = "command line tool for terminator" + app.Flags = []cli.Flag{ + cli.StringFlag{ + Name: "rpcserver", + Value: defaultRPCHostPort, + Usage: "host:port of terminator", + }, + } + app.Commands = []cli.Command{ + closeRecommendationCommand, + } + + if err := app.Run(os.Args); err != nil { + fatal(err) + } +} diff --git a/cmd/trmcli/utils.go b/cmd/trmcli/utils.go new file mode 100644 index 0000000..8d7fec9 --- /dev/null +++ b/cmd/trmcli/utils.go @@ -0,0 +1,97 @@ +package main + +import ( + "context" + "fmt" + "net" + "os" + + "github.com/lightninglabs/protobuf-hex-display/jsonpb" + "github.com/lightninglabs/protobuf-hex-display/proto" + "github.com/lightninglabs/terminator/trmrpc" + "github.com/lightningnetwork/lnd/lncfg" + "github.com/urfave/cli" + "google.golang.org/grpc" +) + +var ( + // maxMsgRecvSize is the largest message our client will receive. We + // set this to 200MiB atm. + maxMsgRecvSize = grpc.MaxCallRecvMsgSize(1 * 1024 * 1024 * 200) +) + +// fatal logs and error and exits. +func fatal(err error) { + _, _ = fmt.Fprintf(os.Stderr, "[trmcli] %v\n", err) + os.Exit(1) +} + +// printRespJSON prints a proto message as json. +func printRespJSON(resp proto.Message) { + jsonMarshaler := &jsonpb.Marshaler{ + EmitDefaults: true, + Indent: " ", + } + + jsonStr, err := jsonMarshaler.MarshalToString(resp) + if err != nil { + fmt.Println("unable to decode response: ", err) + return + } + + fmt.Println(jsonStr) +} + +// getClient returns a terminator client. +func getClient(ctx *cli.Context) (trmrpc.TerminatorServerClient, func()) { + conn := getClientConn(ctx) + + cleanUp := func() { + if err := conn.Close(); err != nil { + fatal(err) + } + } + + return trmrpc.NewTerminatorServerClient(conn), cleanUp +} + +// getClientConn gets a client connection to the address provided by the +// rpcserver flag. +func getClientConn(ctx *cli.Context) *grpc.ClientConn { + // We need to use a custom dialer so we can also connect to unix sockets + // and not just TCP addresses. + genericDialer := clientAddressDialer(defaultRPCPort) + + opts := []grpc.DialOption{ + grpc.WithContextDialer(genericDialer), + grpc.WithDefaultCallOptions(maxMsgRecvSize), + // TODO(carla): add tls and remove this option. + grpc.WithInsecure(), + } + + conn, err := grpc.Dial(ctx.GlobalString("rpcserver"), opts...) + if err != nil { + fatal(fmt.Errorf("unable to connect to RPC server: %v", err)) + } + + return conn +} + +// ClientAddressDialer parsed client address and returns a dialer. +func clientAddressDialer(defaultPort string) func(context.Context, + string) (net.Conn, error) { + + return func(ctx context.Context, addr string) (net.Conn, error) { + parsedAddr, err := lncfg.ParseAddressString( + addr, defaultPort, net.ResolveTCPAddr, + ) + if err != nil { + return nil, err + } + + d := net.Dialer{} + return d.DialContext( + ctx, parsedAddr.Network(), parsedAddr.String(), + ) + } +} diff --git a/go.mod b/go.mod index 67bcfca..a6eab33 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,9 @@ require ( github.com/golang/protobuf v1.3.3 github.com/jessevdk/go-flags v1.4.0 github.com/lightninglabs/loop v0.2.4-alpha + github.com/lightninglabs/protobuf-hex-display v1.3.3-0.20191212020323-b444784ce75d github.com/lightningnetwork/lnd v0.8.0-beta-rc3.0.20191025122959-1a0ab538d53c + github.com/urfave/cli v1.20.0 google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 google.golang.org/grpc v1.27.0 ) diff --git a/go.sum b/go.sum index b317d01..bc8471e 100644 --- a/go.sum +++ b/go.sum @@ -75,6 +75,7 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -137,6 +138,8 @@ github.com/lightninglabs/loop v0.2.4-alpha/go.mod h1:n/8uTYPcWrU12xAQmUvjvfxKTFW github.com/lightninglabs/neutrino v0.0.0-20190906012717-f087198de655/go.mod h1:awTrhbCWjWNH4yVwZ4IE7nZbvpQ27e7OyD+jao7wRxA= github.com/lightninglabs/neutrino v0.10.0 h1:yWVy2cOCCXbKFdpYCE9vD1fWRJDd9FtGXhUws4l9RkU= github.com/lightninglabs/neutrino v0.10.0/go.mod h1:C3KhCMk1Mcx3j8v0qRVWM1Ow6rIJSvSPnUAq00ZNAfk= +github.com/lightninglabs/protobuf-hex-display v1.3.3-0.20191212020323-b444784ce75d h1:QWD/5MPnaZfUVP7P8wLa4M8Td2DI7XXHXt2vhVtUgGI= +github.com/lightninglabs/protobuf-hex-display v1.3.3-0.20191212020323-b444784ce75d/go.mod h1:KDb67YMzoh4eudnzClmvs2FbiLG9vxISmLApUkCa4uI= github.com/lightningnetwork/lightning-onion v0.0.0-20190909101754-850081b08b6a h1:GoWPN4i4jTKRxhVNh9a2vvBBO1Y2seiJB+SopUYoKyo= github.com/lightningnetwork/lightning-onion v0.0.0-20190909101754-850081b08b6a/go.mod h1:rigfi6Af/KqsF7Za0hOgcyq2PNH4AN70AaMRxcJkff4= github.com/lightningnetwork/lnd v0.7.1-beta-rc2.0.20190914085956-35027e52fc22/go.mod h1:VaY0b5o38keUN3Ga6GVb/Mgta4B/CcCXwNvPAvhbv/A= @@ -181,6 +184,7 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/tv42/zbase32 v0.0.0-20160707012821-501572607d02/go.mod h1:tHlrkM198S068ZqfrO6S8HsoJq2bF3ETfTL+kt4tInY= github.com/urfave/cli v1.18.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +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= go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=