diff --git a/.gitignore b/.gitignore index 30c9b8d..70f413e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ *.dll *.so *.dylib +bin/* # Test binary, built with `go test -c` *.test diff --git a/README.md b/README.md index d774eb5..71289a0 100644 --- a/README.md +++ b/README.md @@ -7,3 +7,27 @@ # BitBox Rudementary implementation of containerization in linux. + + +## Server +For available endpoints view the [gRPC deffinition](grpc/bitbox.proto). +```bash +# Build the Server. +go build -o bin/bitbox cmd/server/main.go +# Run it. +./bin/bitbox +``` + + +## CLI Client +```bash +# Build the CLI +go build -o bin/bitboxcli cmd/client/*.go +# Run it. +./bin/bitboxcli start +``` + +## Development Tools +[gRPC & protoc](https://grpc.io/docs/languages/go/quickstart/) are used by `go generate` to update [bitbox/grpc](grpc/). + +[gRPCox](https://github.com/gusaul/grpcox) is a lightweight docker container for easy manual testing. \ No newline at end of file diff --git a/cmd/client/main.go b/cmd/client/main.go new file mode 100644 index 0000000..4eadae7 --- /dev/null +++ b/cmd/client/main.go @@ -0,0 +1,75 @@ +package main + +import ( + "context" + "fmt" + "log" + "net" + "os" + + bbgrpc "github.com/jmbarzee/bitbox/grpc" + "github.com/spf13/cobra" + "google.golang.org/grpc" +) + +var defaultPort = optionalEnvString("BIT_BOX_PORT", "8443") + +var defaultAddress = optionalEnvString("BIT_BOX_ADDR", func() string { + ip, err := getOutboundIP() + if err != nil { + panic(err) + } + return ip.String() +}()) + +var root = &cobra.Command{ + Use: "bitboxcli", + Short: "A CLI tool for remote BitBox operations", + Long: "Execute remote linux processes on a BitBox server", +} + +func main() { + root.AddCommand(cmdStart) + root.AddCommand(cmdStop) + root.AddCommand(cmdStatus) + root.AddCommand(cmdQuery) + if err := root.Execute(); err != nil { + panic(err) + } +} + +func optionalEnvString(key, defaultValue string) string { + if value, ok := os.LookupEnv(key); ok { + return value + } + log.Printf("Missing optional environment variable %s, using default %s", key, defaultValue) + return defaultValue +} + +func getOutboundIP() (net.IP, error) { + conn, err := net.Dial("udp", "8.8.8.8:80") + if err != nil { + return net.IP{}, err + } + defer conn.Close() + + localAddr := conn.LocalAddr().(*net.UDPAddr) + + return localAddr.IP, nil +} + +func getClient(ctx context.Context) bbgrpc.BitBoxClient { + address := fmt.Sprintf("%s:%s", defaultAddress, defaultPort) + + conn, err := grpc.DialContext( + context.TODO(), + address, + //TODO: replace with mTLS + grpc.WithInsecure(), + grpc.WithBlock()) + if err != nil { + panic(fmt.Errorf("Failed to dial connection during reconnect: %w", err)) + } + + return bbgrpc.NewBitBoxClient(conn) +} diff --git a/cmd/client/query.go b/cmd/client/query.go new file mode 100644 index 0000000..46c7de8 --- /dev/null +++ b/cmd/client/query.go @@ -0,0 +1,70 @@ +package main + +import ( + "context" + "errors" + "fmt" + "io" + "log" + + "github.com/google/uuid" + "github.com/spf13/cobra" + + bbgrpc "github.com/jmbarzee/bitbox/grpc" +) + +var cmdQuery = &cobra.Command{ + Use: "query", + Short: "query", + Long: "Query a process on the bitbox server", + RunE: func(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return errors.New("Require a single id as an argument") + } + + uuid, err := uuid.Parse(args[0]) + if err != nil { + return fmt.Errorf("failed to parse uuid: %s", args[0]) + } + + job := jobQuery{ + id: uuid, + } + ctx := context.Background() + bbClient := getClient(ctx) + return job.execute(ctx, bbClient) + }, +} + +type jobQuery struct { + id uuid.UUID +} + +// Execute querys a job on the remote bitBox +func (j jobQuery) execute(ctx context.Context, c bbgrpc.BitBoxClient) error { + request := &bbgrpc.QueryRequest{ + ID: j.id[:], + } + + queryClient, err := c.Query(ctx, request) + if err != nil { + return fmt.Errorf("failed to stop process %s: %w", j.id, err) + } + + for { + reply, err := queryClient.Recv() + if err == io.EOF { + log.Println("") + } + if err != nil { + return fmt.Errorf("failed to fetch reply: %w", err) + } + switch output := reply.GetOutput().(type) { + case *bbgrpc.QueryReply_Stdouterr: + log.Println(output.Stdouterr) + case *bbgrpc.QueryReply_ExitCode: + log.Printf("Process %v exited with code %v", j.id, output.ExitCode) + return nil + } + } +} diff --git a/cmd/client/start.go b/cmd/client/start.go new file mode 100644 index 0000000..0ddf2c7 --- /dev/null +++ b/cmd/client/start.go @@ -0,0 +1,56 @@ +package main + +import ( + "context" + "errors" + "fmt" + "log" + + "github.com/google/uuid" + "github.com/spf13/cobra" + + bbgrpc "github.com/jmbarzee/bitbox/grpc" +) + +var cmdStart = &cobra.Command{ + Use: "start", + Short: "start", + Long: "Start a process on the bitbox server", + RunE: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("Require atleast a single command as an argument") + } + + job := jobStart{ + command: args[0], + arguments: args[1:], + } + ctx := context.Background() + bbClient := getClient(ctx) + return job.execute(ctx, bbClient) + }, +} + +type jobStart struct { + command string + arguments []string +} + +// Execute starts a job on the remote bitBox +func (j jobStart) execute(ctx context.Context, c bbgrpc.BitBoxClient) error { + request := &bbgrpc.StartRequest{ + Command: j.command, + Arguments: j.arguments, + } + + reply, err := c.Start(ctx, request) + if err != nil { + return fmt.Errorf("failed to run %s: %w", j.command, err) + } + uuid, err := uuid.FromBytes(reply.GetID()) + if err != nil { + return fmt.Errorf("failed to parse uuid: %s", reply.GetID()) + } + log.Println("Successfully started process: ", uuid.String()) + return nil +} diff --git a/cmd/client/status.go b/cmd/client/status.go new file mode 100644 index 0000000..0475539 --- /dev/null +++ b/cmd/client/status.go @@ -0,0 +1,55 @@ +package main + +import ( + "context" + "errors" + "fmt" + "log" + + "github.com/google/uuid" + "github.com/spf13/cobra" + + bbgrpc "github.com/jmbarzee/bitbox/grpc" +) + +var cmdStatus = &cobra.Command{ + Use: "status", + Short: "status", + Long: "Stop a process on the bitbox server", + RunE: func(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return errors.New("Require a single id as an argument") + } + + uuid, err := uuid.Parse(args[0]) + if err != nil { + return fmt.Errorf("failed to parse uuid: %s", args[0]) + } + + job := jobStatus{ + id: uuid, + } + ctx := context.Background() + bbClient := getClient(ctx) + return job.execute(ctx, bbClient) + }, +} + +type jobStatus struct { + id uuid.UUID +} + +// Execute returns the status of a job on the remote bitBox +func (j jobStatus) execute(ctx context.Context, c bbgrpc.BitBoxClient) error { + request := &bbgrpc.StatusRequest{ + ID: j.id[:], + } + + reply, err := c.Status(ctx, request) + if err != nil { + return fmt.Errorf("failed to query process %s: %w", j.id, err) + } + log.Println("Successfully queried status of process: ", j.id, ", ", reply.Status.String()) + + return nil +} diff --git a/cmd/client/stop.go b/cmd/client/stop.go new file mode 100644 index 0000000..fe7fc64 --- /dev/null +++ b/cmd/client/stop.go @@ -0,0 +1,54 @@ +package main + +import ( + "context" + "errors" + "fmt" + "log" + + "github.com/google/uuid" + "github.com/spf13/cobra" + + bbgrpc "github.com/jmbarzee/bitbox/grpc" +) + +var cmdStop = &cobra.Command{ + Use: "stop", + Short: "stop", + Long: "Stop a process on the bitbox server", + RunE: func(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return errors.New("Require a single id as an argument") + } + + uuid, err := uuid.Parse(args[0]) + if err != nil { + return fmt.Errorf("failed to parse uuid: %s", args[0]) + } + + job := jobStop{ + id: uuid, + } + ctx := context.Background() + bbClient := getClient(ctx) + return job.execute(ctx, bbClient) + }, +} + +type jobStop struct { + id uuid.UUID +} + +// Execute stops a job on the remote bitBox +func (j jobStop) execute(ctx context.Context, c bbgrpc.BitBoxClient) error { + request := &bbgrpc.StopRequest{ + ID: j.id[:], + } + + _, err := c.Stop(ctx, request) + if err != nil { + return fmt.Errorf("failed to stop process %s: %w", j.id, err) + } + log.Println("Successfully stopped process: ", j.id) + return nil +} diff --git a/cmd/server/main.go b/cmd/server/main.go new file mode 100644 index 0000000..0b14055 --- /dev/null +++ b/cmd/server/main.go @@ -0,0 +1,45 @@ +package main + +import ( + // temp import for build + "fmt" + "log" + "net" + "os" + + "github.com/jmbarzee/bitbox" + bbgrpc "github.com/jmbarzee/bitbox/grpc" + "google.golang.org/grpc" + "google.golang.org/grpc/reflection" +) + +var defaultPort = optionalEnvString("BIT_BOX_PORT", "8443") +var defaultAddress = optionalEnvString("BIT_BOX_ADDR", "") + +func main() { + + address := fmt.Sprintf("%s:%s", defaultAddress, defaultPort) + + lis, err := net.Listen("tcp", address) + if err != nil { + panic(fmt.Errorf("failed to listen on %s: %w", address, err)) + } + + bitBoxServer := bitbox.NewServer() + + server := grpc.NewServer() + + bbgrpc.RegisterBitBoxServer(server, bitBoxServer) + // Register reflection service on gRPC server. + reflection.Register(server) + + err = server.Serve(lis) +} + +func optionalEnvString(key, defaultValue string) string { + if value, ok := os.LookupEnv(key); ok { + return value + } + log.Printf("Missing optional environment variable %s, using default %s", key, defaultValue) + return defaultValue +} diff --git a/core.go b/core.go index 1007174..9a161e8 100644 --- a/core.go +++ b/core.go @@ -1,6 +1,7 @@ package bitbox import ( + "context" "fmt" "sync" @@ -15,6 +16,12 @@ type Core struct { processes map[uuid.UUID]*proc.Proc } +func NewCore() *Core { + return &Core{ + processes: make(map[uuid.UUID]*proc.Proc), + } +} + // Start initiates a process. func (c *Core) Start(cmd string, args ...string) (uuid.UUID, error) { @@ -57,7 +64,7 @@ func (c *Core) Status(id uuid.UUID) (proc.ProcStatus, error) { } // Query streams the output/result of a process. -func (c *Core) Query(id uuid.UUID) (<-chan proc.ProcOutput, error) { +func (c *Core) Query(ctx context.Context, id uuid.UUID) (<-chan proc.ProcOutput, error) { var p *proc.Proc var err error @@ -65,7 +72,7 @@ func (c *Core) Query(id uuid.UUID) (<-chan proc.ProcOutput, error) { return nil, c.newError("Query", err) } - return p.Query() + return p.Query(ctx) } func (c *Core) findProcess(id uuid.UUID) (*proc.Proc, error) { diff --git a/go.mod b/go.mod index 17d9b09..7626e65 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,12 @@ module github.com/jmbarzee/bitbox go 1.15 -require github.com/google/uuid v1.2.0 +require ( + github.com/google/uuid v1.2.0 + github.com/spf13/cobra v1.1.3 + golang.org/x/net v0.0.0-20190620200207-3b0461eec859 // indirect + golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 // indirect + golang.org/x/text v0.3.2 // indirect + google.golang.org/grpc v1.36.0 + google.golang.org/protobuf v1.26.0 +) diff --git a/go.sum b/go.sum index a4d2b5e..5d2e27d 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,340 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +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/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v1.1.3 h1:xghbfqPkxzxP3C/f3n5DdpAbdKLj4ZE4BWQI362l53M= +github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/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-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/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/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.36.0 h1:o1bcQ6imQMIOpdrO3SWf2z5RV72WbDwdXuK0MDlc8As= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/grpc/bitbox.pb.go b/grpc/bitbox.pb.go new file mode 100644 index 0000000..81fb176 --- /dev/null +++ b/grpc/bitbox.pb.go @@ -0,0 +1,721 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.25.0-devel +// protoc v3.15.6 +// source: bitbox.proto + +package grpc + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type StatusReply_StatusEnum int32 + +const ( + // Running indicates that the process is running. + StatusReply_Running StatusReply_StatusEnum = 0 + // Exited indicates that the process returned a non-zero exit code. + StatusReply_Exited StatusReply_StatusEnum = 1 + // Stopped indicates that the process returned no exit code. + StatusReply_Stopped StatusReply_StatusEnum = 2 +) + +// Enum value maps for StatusReply_StatusEnum. +var ( + StatusReply_StatusEnum_name = map[int32]string{ + 0: "Running", + 1: "Exited", + 2: "Stopped", + } + StatusReply_StatusEnum_value = map[string]int32{ + "Running": 0, + "Exited": 1, + "Stopped": 2, + } +) + +func (x StatusReply_StatusEnum) Enum() *StatusReply_StatusEnum { + p := new(StatusReply_StatusEnum) + *p = x + return p +} + +func (x StatusReply_StatusEnum) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (StatusReply_StatusEnum) Descriptor() protoreflect.EnumDescriptor { + return file_bitbox_proto_enumTypes[0].Descriptor() +} + +func (StatusReply_StatusEnum) Type() protoreflect.EnumType { + return &file_bitbox_proto_enumTypes[0] +} + +func (x StatusReply_StatusEnum) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use StatusReply_StatusEnum.Descriptor instead. +func (StatusReply_StatusEnum) EnumDescriptor() ([]byte, []int) { + return file_bitbox_proto_rawDescGZIP(), []int{5, 0} +} + +// StartRequest holds the command and nessecary parameters to run it. +type StartRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Command is an arbitrary linux command. + // Command should be a fullpath or found in a directory in PATH. + Command string `protobuf:"bytes,1,opt,name=Command,proto3" json:"Command,omitempty"` + // Arguments are handed to the command as ARGV. + // Glob patterns, pipelines, and redirections are not handled + // and will instead be passed as literal strings to the command. + Arguments []string `protobuf:"bytes,2,rep,name=Arguments,proto3" json:"Arguments,omitempty"` +} + +func (x *StartRequest) Reset() { + *x = StartRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_bitbox_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StartRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StartRequest) ProtoMessage() {} + +func (x *StartRequest) ProtoReflect() protoreflect.Message { + mi := &file_bitbox_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StartRequest.ProtoReflect.Descriptor instead. +func (*StartRequest) Descriptor() ([]byte, []int) { + return file_bitbox_proto_rawDescGZIP(), []int{0} +} + +func (x *StartRequest) GetCommand() string { + if x != nil { + return x.Command + } + return "" +} + +func (x *StartRequest) GetArguments() []string { + if x != nil { + return x.Arguments + } + return nil +} + +// StartReply holds the ID of the started process. +type StartReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // ID is the unique identifier of the process. + ID []byte `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"` +} + +func (x *StartReply) Reset() { + *x = StartReply{} + if protoimpl.UnsafeEnabled { + mi := &file_bitbox_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StartReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StartReply) ProtoMessage() {} + +func (x *StartReply) ProtoReflect() protoreflect.Message { + mi := &file_bitbox_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StartReply.ProtoReflect.Descriptor instead. +func (*StartReply) Descriptor() ([]byte, []int) { + return file_bitbox_proto_rawDescGZIP(), []int{1} +} + +func (x *StartReply) GetID() []byte { + if x != nil { + return x.ID + } + return nil +} + +// StopRequest holds the ID of the process that should be stopped. +type StopRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // ID is the unique identifier of the process. + ID []byte `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"` // TODO: Consider making the type of signal sent to the process configurable, not just kill. +} + +func (x *StopRequest) Reset() { + *x = StopRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_bitbox_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StopRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StopRequest) ProtoMessage() {} + +func (x *StopRequest) ProtoReflect() protoreflect.Message { + mi := &file_bitbox_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StopRequest.ProtoReflect.Descriptor instead. +func (*StopRequest) Descriptor() ([]byte, []int) { + return file_bitbox_proto_rawDescGZIP(), []int{2} +} + +func (x *StopRequest) GetID() []byte { + if x != nil { + return x.ID + } + return nil +} + +// StopReply is empty. +type StopReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *StopReply) Reset() { + *x = StopReply{} + if protoimpl.UnsafeEnabled { + mi := &file_bitbox_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StopReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StopReply) ProtoMessage() {} + +func (x *StopReply) ProtoReflect() protoreflect.Message { + mi := &file_bitbox_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StopReply.ProtoReflect.Descriptor instead. +func (*StopReply) Descriptor() ([]byte, []int) { + return file_bitbox_proto_rawDescGZIP(), []int{3} +} + +// StatusRequest holds the ID of the process to check the status of +type StatusRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // ID is the unique identifier of the process. + ID []byte `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"` +} + +func (x *StatusRequest) Reset() { + *x = StatusRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_bitbox_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StatusRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StatusRequest) ProtoMessage() {} + +func (x *StatusRequest) ProtoReflect() protoreflect.Message { + mi := &file_bitbox_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StatusRequest.ProtoReflect.Descriptor instead. +func (*StatusRequest) Descriptor() ([]byte, []int) { + return file_bitbox_proto_rawDescGZIP(), []int{4} +} + +func (x *StatusRequest) GetID() []byte { + if x != nil { + return x.ID + } + return nil +} + +// StatusReply holds the status of the reply +type StatusReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Status StatusReply_StatusEnum `protobuf:"varint,1,opt,name=Status,proto3,enum=grpc.StatusReply_StatusEnum" json:"Status,omitempty"` +} + +func (x *StatusReply) Reset() { + *x = StatusReply{} + if protoimpl.UnsafeEnabled { + mi := &file_bitbox_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StatusReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StatusReply) ProtoMessage() {} + +func (x *StatusReply) ProtoReflect() protoreflect.Message { + mi := &file_bitbox_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StatusReply.ProtoReflect.Descriptor instead. +func (*StatusReply) Descriptor() ([]byte, []int) { + return file_bitbox_proto_rawDescGZIP(), []int{5} +} + +func (x *StatusReply) GetStatus() StatusReply_StatusEnum { + if x != nil { + return x.Status + } + return StatusReply_Running +} + +// QueryRequest holds the ID of the process whose output is being queried. +type QueryRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // ID is the unique identifier of the process. + ID []byte `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"` +} + +func (x *QueryRequest) Reset() { + *x = QueryRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_bitbox_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *QueryRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*QueryRequest) ProtoMessage() {} + +func (x *QueryRequest) ProtoReflect() protoreflect.Message { + mi := &file_bitbox_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use QueryRequest.ProtoReflect.Descriptor instead. +func (*QueryRequest) Descriptor() ([]byte, []int) { + return file_bitbox_proto_rawDescGZIP(), []int{6} +} + +func (x *QueryRequest) GetID() []byte { + if x != nil { + return x.ID + } + return nil +} + +// QueryReply holds output of the process queried. +type QueryReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Output is one of the forms of output that a process may utilize. + // + // Types that are assignable to Output: + // *QueryReply_Stdouterr + // *QueryReply_ExitCode + Output isQueryReply_Output `protobuf_oneof:"Output"` +} + +func (x *QueryReply) Reset() { + *x = QueryReply{} + if protoimpl.UnsafeEnabled { + mi := &file_bitbox_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *QueryReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*QueryReply) ProtoMessage() {} + +func (x *QueryReply) ProtoReflect() protoreflect.Message { + mi := &file_bitbox_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use QueryReply.ProtoReflect.Descriptor instead. +func (*QueryReply) Descriptor() ([]byte, []int) { + return file_bitbox_proto_rawDescGZIP(), []int{7} +} + +func (m *QueryReply) GetOutput() isQueryReply_Output { + if m != nil { + return m.Output + } + return nil +} + +func (x *QueryReply) GetStdouterr() string { + if x, ok := x.GetOutput().(*QueryReply_Stdouterr); ok { + return x.Stdouterr + } + return "" +} + +func (x *QueryReply) GetExitCode() uint32 { + if x, ok := x.GetOutput().(*QueryReply_ExitCode); ok { + return x.ExitCode + } + return 0 +} + +type isQueryReply_Output interface { + isQueryReply_Output() +} + +type QueryReply_Stdouterr struct { + // Stdouterr is any output from the process which was written to Stdouterr. + Stdouterr string `protobuf:"bytes,1,opt,name=Stdouterr,proto3,oneof"` +} + +type QueryReply_ExitCode struct { + // ExitCode is the code the process exited with. + ExitCode uint32 `protobuf:"varint,3,opt,name=ExitCode,proto3,oneof"` // Leaving as 3 to allow for seperation of Stdout & Stderr at some point +} + +func (*QueryReply_Stdouterr) isQueryReply_Output() {} + +func (*QueryReply_ExitCode) isQueryReply_Output() {} + +var File_bitbox_proto protoreflect.FileDescriptor + +var file_bitbox_proto_rawDesc = []byte{ + 0x0a, 0x0c, 0x62, 0x69, 0x74, 0x62, 0x6f, 0x78, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, + 0x67, 0x72, 0x70, 0x63, 0x22, 0x46, 0x0a, 0x0c, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x1c, + 0x0a, 0x09, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x09, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x1c, 0x0a, 0x0a, + 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x49, 0x44, 0x22, 0x1d, 0x0a, 0x0b, 0x53, 0x74, + 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x49, 0x44, 0x22, 0x0b, 0x0a, 0x09, 0x53, 0x74, 0x6f, + 0x70, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x1f, 0x0a, 0x0d, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x02, 0x49, 0x44, 0x22, 0x77, 0x0a, 0x0b, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x34, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x45, 0x6e, 0x75, 0x6d, 0x52, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x32, 0x0a, 0x0a, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x45, 0x6e, 0x75, 0x6d, 0x12, 0x0b, 0x0a, 0x07, 0x52, 0x75, + 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x45, 0x78, 0x69, 0x74, 0x65, + 0x64, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x74, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x10, 0x02, + 0x22, 0x1e, 0x0a, 0x0c, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x49, 0x44, + 0x22, 0x54, 0x0a, 0x0a, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1e, + 0x0a, 0x09, 0x53, 0x74, 0x64, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x48, 0x00, 0x52, 0x09, 0x53, 0x74, 0x64, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x12, 0x1c, + 0x0a, 0x08, 0x45, 0x78, 0x69, 0x74, 0x43, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, + 0x48, 0x00, 0x52, 0x08, 0x45, 0x78, 0x69, 0x74, 0x43, 0x6f, 0x64, 0x65, 0x42, 0x08, 0x0a, 0x06, + 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x32, 0xce, 0x01, 0x0a, 0x06, 0x42, 0x69, 0x74, 0x42, 0x6f, + 0x78, 0x12, 0x2f, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x12, 0x2e, 0x67, 0x72, 0x70, + 0x63, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, + 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, + 0x22, 0x00, 0x12, 0x2c, 0x0a, 0x04, 0x53, 0x74, 0x6f, 0x70, 0x12, 0x11, 0x2e, 0x67, 0x72, 0x70, + 0x63, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0f, 0x2e, + 0x67, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, + 0x12, 0x32, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x13, 0x2e, 0x67, 0x72, 0x70, + 0x63, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x11, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x70, + 0x6c, 0x79, 0x22, 0x00, 0x12, 0x31, 0x0a, 0x05, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x12, 0x2e, + 0x67, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x10, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, + 0x70, 0x6c, 0x79, 0x22, 0x00, 0x30, 0x01, 0x42, 0x21, 0x5a, 0x1f, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6a, 0x6d, 0x62, 0x61, 0x72, 0x7a, 0x65, 0x65, 0x2f, 0x62, + 0x69, 0x74, 0x62, 0x6f, 0x78, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, +} + +var ( + file_bitbox_proto_rawDescOnce sync.Once + file_bitbox_proto_rawDescData = file_bitbox_proto_rawDesc +) + +func file_bitbox_proto_rawDescGZIP() []byte { + file_bitbox_proto_rawDescOnce.Do(func() { + file_bitbox_proto_rawDescData = protoimpl.X.CompressGZIP(file_bitbox_proto_rawDescData) + }) + return file_bitbox_proto_rawDescData +} + +var file_bitbox_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_bitbox_proto_msgTypes = make([]protoimpl.MessageInfo, 8) +var file_bitbox_proto_goTypes = []interface{}{ + (StatusReply_StatusEnum)(0), // 0: grpc.StatusReply.StatusEnum + (*StartRequest)(nil), // 1: grpc.StartRequest + (*StartReply)(nil), // 2: grpc.StartReply + (*StopRequest)(nil), // 3: grpc.StopRequest + (*StopReply)(nil), // 4: grpc.StopReply + (*StatusRequest)(nil), // 5: grpc.StatusRequest + (*StatusReply)(nil), // 6: grpc.StatusReply + (*QueryRequest)(nil), // 7: grpc.QueryRequest + (*QueryReply)(nil), // 8: grpc.QueryReply +} +var file_bitbox_proto_depIdxs = []int32{ + 0, // 0: grpc.StatusReply.Status:type_name -> grpc.StatusReply.StatusEnum + 1, // 1: grpc.BitBox.Start:input_type -> grpc.StartRequest + 3, // 2: grpc.BitBox.Stop:input_type -> grpc.StopRequest + 5, // 3: grpc.BitBox.Status:input_type -> grpc.StatusRequest + 7, // 4: grpc.BitBox.Query:input_type -> grpc.QueryRequest + 2, // 5: grpc.BitBox.Start:output_type -> grpc.StartReply + 4, // 6: grpc.BitBox.Stop:output_type -> grpc.StopReply + 6, // 7: grpc.BitBox.Status:output_type -> grpc.StatusReply + 8, // 8: grpc.BitBox.Query:output_type -> grpc.QueryReply + 5, // [5:9] is the sub-list for method output_type + 1, // [1:5] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_bitbox_proto_init() } +func file_bitbox_proto_init() { + if File_bitbox_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_bitbox_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StartRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_bitbox_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StartReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_bitbox_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StopRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_bitbox_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StopReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_bitbox_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StatusRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_bitbox_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StatusReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_bitbox_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*QueryRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_bitbox_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*QueryReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_bitbox_proto_msgTypes[7].OneofWrappers = []interface{}{ + (*QueryReply_Stdouterr)(nil), + (*QueryReply_ExitCode)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_bitbox_proto_rawDesc, + NumEnums: 1, + NumMessages: 8, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_bitbox_proto_goTypes, + DependencyIndexes: file_bitbox_proto_depIdxs, + EnumInfos: file_bitbox_proto_enumTypes, + MessageInfos: file_bitbox_proto_msgTypes, + }.Build() + File_bitbox_proto = out.File + file_bitbox_proto_rawDesc = nil + file_bitbox_proto_goTypes = nil + file_bitbox_proto_depIdxs = nil +} diff --git a/grpc/bitbox.proto b/grpc/bitbox.proto new file mode 100644 index 0000000..8788a12 --- /dev/null +++ b/grpc/bitbox.proto @@ -0,0 +1,94 @@ +syntax = "proto3"; + +package grpc; + +// The Go package name is "grpcb". +option go_package = "github.com/jmbarzee/bitbox/grpc"; + +service BitBox { + // Start initiates a process. + rpc Start (StartRequest) returns (StartReply) {} + + // Stop halts a process. + rpc Stop (StopRequest) returns (StopReply) {} + + // Status returns the status of a process. + rpc Status (StatusRequest) returns (StatusReply) {} + + // Query streams the output/result of a process. + rpc Query (QueryRequest) returns (stream QueryReply) {} +} + + +// StartRequest holds the command and nessecary parameters to run it. +message StartRequest { + // Command is an arbitrary linux command. + // Command should be a fullpath or found in a directory in PATH. + string Command = 1; + + // Arguments are handed to the command as ARGV. + // Glob patterns, pipelines, and redirections are not handled + // and will instead be passed as literal strings to the command. + repeated string Arguments = 2; +} + +// StartReply holds the ID of the started process. +message StartReply { + // ID is the unique identifier of the process. + bytes ID = 1; +} + + + +// StopRequest holds the ID of the process that should be stopped. +message StopRequest { + // ID is the unique identifier of the process. + bytes ID = 1; + // TODO: Consider making the type of signal sent to the process configurable, not just kill. +} + +// StopReply is empty. +message StopReply { + // TODO: consider including information about how the process was killed, Signal type, time, etc. +} + + + +// StatusRequest holds the ID of the process to check the status of +message StatusRequest { + // ID is the unique identifier of the process. + bytes ID = 1; +} + +// StatusReply holds the status of the reply +message StatusReply { + enum StatusEnum { + // Running indicates that the process is running. + Running = 0; + // Exited indicates that the process returned a non-zero exit code. + Exited = 1; + // Stopped indicates that the process returned no exit code. + Stopped = 2; + } + StatusEnum Status = 1; +} + + + + +// QueryRequest holds the ID of the process whose output is being queried. +message QueryRequest { + // ID is the unique identifier of the process. + bytes ID = 1; +} + +// QueryReply holds output of the process queried. +message QueryReply { + // Output is one of the forms of output that a process may utilize. + oneof Output { + // Stdouterr is any output from the process which was written to Stdouterr. + string Stdouterr = 1; + // ExitCode is the code the process exited with. + uint32 ExitCode = 3; // Leaving as 3 to allow for seperation of Stdout & Stderr at some point + } +} \ No newline at end of file diff --git a/grpc/bitbox_grpc.pb.go b/grpc/bitbox_grpc.pb.go new file mode 100644 index 0000000..083259b --- /dev/null +++ b/grpc/bitbox_grpc.pb.go @@ -0,0 +1,245 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. + +package grpc + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// BitBoxClient is the client API for BitBox service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type BitBoxClient interface { + // Start initiates a process. + Start(ctx context.Context, in *StartRequest, opts ...grpc.CallOption) (*StartReply, error) + // Stop halts a process. + Stop(ctx context.Context, in *StopRequest, opts ...grpc.CallOption) (*StopReply, error) + // Status returns the status of a process. + Status(ctx context.Context, in *StatusRequest, opts ...grpc.CallOption) (*StatusReply, error) + // Query streams the output/result of a process. + Query(ctx context.Context, in *QueryRequest, opts ...grpc.CallOption) (BitBox_QueryClient, error) +} + +type bitBoxClient struct { + cc grpc.ClientConnInterface +} + +func NewBitBoxClient(cc grpc.ClientConnInterface) BitBoxClient { + return &bitBoxClient{cc} +} + +func (c *bitBoxClient) Start(ctx context.Context, in *StartRequest, opts ...grpc.CallOption) (*StartReply, error) { + out := new(StartReply) + err := c.cc.Invoke(ctx, "/grpc.BitBox/Start", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *bitBoxClient) Stop(ctx context.Context, in *StopRequest, opts ...grpc.CallOption) (*StopReply, error) { + out := new(StopReply) + err := c.cc.Invoke(ctx, "/grpc.BitBox/Stop", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *bitBoxClient) Status(ctx context.Context, in *StatusRequest, opts ...grpc.CallOption) (*StatusReply, error) { + out := new(StatusReply) + err := c.cc.Invoke(ctx, "/grpc.BitBox/Status", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *bitBoxClient) Query(ctx context.Context, in *QueryRequest, opts ...grpc.CallOption) (BitBox_QueryClient, error) { + stream, err := c.cc.NewStream(ctx, &BitBox_ServiceDesc.Streams[0], "/grpc.BitBox/Query", opts...) + if err != nil { + return nil, err + } + x := &bitBoxQueryClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type BitBox_QueryClient interface { + Recv() (*QueryReply, error) + grpc.ClientStream +} + +type bitBoxQueryClient struct { + grpc.ClientStream +} + +func (x *bitBoxQueryClient) Recv() (*QueryReply, error) { + m := new(QueryReply) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +// BitBoxServer is the server API for BitBox service. +// All implementations must embed UnimplementedBitBoxServer +// for forward compatibility +type BitBoxServer interface { + // Start initiates a process. + Start(context.Context, *StartRequest) (*StartReply, error) + // Stop halts a process. + Stop(context.Context, *StopRequest) (*StopReply, error) + // Status returns the status of a process. + Status(context.Context, *StatusRequest) (*StatusReply, error) + // Query streams the output/result of a process. + Query(*QueryRequest, BitBox_QueryServer) error + mustEmbedUnimplementedBitBoxServer() +} + +// UnimplementedBitBoxServer must be embedded to have forward compatible implementations. +type UnimplementedBitBoxServer struct { +} + +func (UnimplementedBitBoxServer) Start(context.Context, *StartRequest) (*StartReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method Start not implemented") +} +func (UnimplementedBitBoxServer) Stop(context.Context, *StopRequest) (*StopReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method Stop not implemented") +} +func (UnimplementedBitBoxServer) Status(context.Context, *StatusRequest) (*StatusReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method Status not implemented") +} +func (UnimplementedBitBoxServer) Query(*QueryRequest, BitBox_QueryServer) error { + return status.Errorf(codes.Unimplemented, "method Query not implemented") +} +func (UnimplementedBitBoxServer) mustEmbedUnimplementedBitBoxServer() {} + +// UnsafeBitBoxServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to BitBoxServer will +// result in compilation errors. +type UnsafeBitBoxServer interface { + mustEmbedUnimplementedBitBoxServer() +} + +func RegisterBitBoxServer(s grpc.ServiceRegistrar, srv BitBoxServer) { + s.RegisterService(&BitBox_ServiceDesc, srv) +} + +func _BitBox_Start_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(StartRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BitBoxServer).Start(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/grpc.BitBox/Start", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BitBoxServer).Start(ctx, req.(*StartRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _BitBox_Stop_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(StopRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BitBoxServer).Stop(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/grpc.BitBox/Stop", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BitBoxServer).Stop(ctx, req.(*StopRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _BitBox_Status_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(StatusRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BitBoxServer).Status(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/grpc.BitBox/Status", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BitBoxServer).Status(ctx, req.(*StatusRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _BitBox_Query_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(QueryRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(BitBoxServer).Query(m, &bitBoxQueryServer{stream}) +} + +type BitBox_QueryServer interface { + Send(*QueryReply) error + grpc.ServerStream +} + +type bitBoxQueryServer struct { + grpc.ServerStream +} + +func (x *bitBoxQueryServer) Send(m *QueryReply) error { + return x.ServerStream.SendMsg(m) +} + +// BitBox_ServiceDesc is the grpc.ServiceDesc for BitBox service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var BitBox_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "grpc.BitBox", + HandlerType: (*BitBoxServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Start", + Handler: _BitBox_Start_Handler, + }, + { + MethodName: "Stop", + Handler: _BitBox_Stop_Handler, + }, + { + MethodName: "Status", + Handler: _BitBox_Status_Handler, + }, + }, + Streams: []grpc.StreamDesc{ + { + StreamName: "Query", + Handler: _BitBox_Query_Handler, + ServerStreams: true, + }, + }, + Metadata: "bitbox.proto", +} diff --git a/proc/proc.go b/proc/proc.go index d62d12f..98886f7 100644 --- a/proc/proc.go +++ b/proc/proc.go @@ -1,8 +1,3 @@ -// proc offers control over arbitrary processes. -// proc depends heavily on os/exec. -// If/when proc needs to do resource control & isolation -// its probable that os/exec will need to be replaced with -// either the os package itself or syscall. package proc // Fingers crossed that its easier than rewriting os.ForkExec @@ -20,6 +15,11 @@ const outputReadRefreshInterval = time.Millisecond * 10 // Proc is a BitBox process. // Stderr and Stdout are dumped to temp files on disk. +// Proc offers control over arbitrary processes. +// Proc depends heavily on os/exec. +// If/when proc needs to do resource control & isolation +// its probable that os/exec will need to be replaced with +// either the os package itself or syscall. type Proc struct { outputFileName string waitMutex sync.Mutex // See https://github.com/golang/go/issues/28461 @@ -105,25 +105,32 @@ func (ps ProcStatus) String() string { } // Query streams output from the process to the returned channel. -// The Stdout and Stderr files are opened for reads and polled until +// The output file is opened for reads and polled until // a third routine finds that the process has exited. // The third routine cancels the context of the pollReads. +// After the read routines finish the third routine sends +// the ExitCode and closes the channel. +// Note: this function's structure could probably be cleaned up. // After the read routines finish the third routine sends the ExitCode and closes the channel. -func (p *Proc) Query() (<-chan ProcOutput, error) { +func (p *Proc) Query(ctx context.Context) (<-chan ProcOutput, error) { flags := os.O_RDONLY | os.O_SYNC outputFile, err := os.OpenFile(p.outputFileName, flags, 0600) if err != nil { return nil, err } - ctx, cancel := context.WithCancel(context.Background()) + pollContext, cancelPoll := context.WithCancel(ctx) stream := make(chan ProcOutput) wg := &sync.WaitGroup{} wg.Add(1) go func() { - pollRead(ctx, outputFile, stream) - finishRead(outputFile, stream) + unstreamedOutput := pollRead(pollContext, outputFile, stream) + select { + case <-ctx.Done(): + case stream <- unstreamedOutput: + finishRead(ctx, outputFile, stream) + } outputFile.Close() wg.Done() }() @@ -136,7 +143,7 @@ func (p *Proc) Query() (<-chan ProcOutput, error) { p.cmd.Wait() p.waitMutex.Unlock() - cancel() + cancelPoll() wg.Wait() stream <- &ProcOutput_ExitCode{ ExitCode: uint32(p.cmd.ProcessState.ExitCode()), @@ -151,32 +158,38 @@ func pollRead( ctx context.Context, file *os.File, stream chan<- ProcOutput, -) { +) ProcOutput { buf := make([]byte, 1024) ticker := time.NewTicker(outputReadRefreshInterval) for { select { + case <-ctx.Done(): + return nil case <-ticker.C: // ReadLoop for { n, err := file.Read(buf) if err != nil { // TODO: should we log the error somehow? - return + return nil } if n == 0 { break // move to wait for ticker or context to end } - stream <- newProcOutput_Stdouterr(buf) + select { + case stream <- newProcOutput_Stdouterr(buf): + //Do nothing + case <-ctx.Done(): + return newProcOutput_Stdouterr(buf) + } } - case <-ctx.Done(): - return } } } func finishRead( + ctx context.Context, file *os.File, stream chan<- ProcOutput, ) { @@ -191,7 +204,12 @@ func finishRead( if n == 0 { break } - stream <- newProcOutput_Stdouterr(buf) + select { + case <-ctx.Done(): + return + case stream <- newProcOutput_Stdouterr(buf): + //Do nothing + } } } diff --git a/server.go b/server.go new file mode 100644 index 0000000..4ef8305 --- /dev/null +++ b/server.go @@ -0,0 +1,134 @@ +package bitbox + +//go:generate protoc --proto_path=. --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative bitbox.proto + +import ( + "context" + "fmt" + "log" + + "github.com/google/uuid" + "github.com/jmbarzee/bitbox/grpc" + "github.com/jmbarzee/bitbox/proc" +) + +// ensure Server implements BitBoxServer +var _ grpc.BitBoxServer = (*Server)(nil) + +// Server starts, stops, and tracks arbitrary processes +type Server struct { + // UnimplementedBitBoxServer is embedded to enable forwards compatability + grpc.UnimplementedBitBoxServer + c *Core +} + +func NewServer() *Server { + return &Server{ + c: NewCore(), + } +} + +// Start initiates a process. +func (s *Server) Start(ctx context.Context, request *grpc.StartRequest) (*grpc.StartReply, error) { + cmd := request.GetCommand() + args := request.GetArguments() + log.Println("[Start] ", cmd, args) + + uuid, err := s.c.Start(cmd, args...) + if err != nil { + return nil, err + } + + return &grpc.StartReply{ + ID: uuid[:], + }, nil +} + +// Stop halts a process. +func (s *Server) Stop(ctx context.Context, request *grpc.StopRequest) (*grpc.StopReply, error) { + uuid, err := uuid.FromBytes(request.GetID()) + if err != nil { + return nil, err + } + log.Println("[Stop] ", uuid.String()) + + return &grpc.StopReply{}, s.c.Stop(uuid) +} + +// Status returns the status of a process. +func (s *Server) Status(ctx context.Context, request *grpc.StatusRequest) (*grpc.StatusReply, error) { + uuid, err := uuid.FromBytes(request.GetID()) + if err != nil { + return nil, err + } + log.Println("[Status] ", uuid.String()) + + status, err := s.c.Status(uuid) + if err != nil { + return nil, err + } + + grpcStatus, err := convertToGRPCStatus(status) + if err != nil { + return nil, err + } + + return &grpc.StatusReply{ + Status: grpcStatus, + }, nil +} + +func convertToGRPCStatus(status proc.ProcStatus) (grpc.StatusReply_StatusEnum, error) { + // We could leverage implement this function on the proc.ProcStatus, + // That would force the proc package to know import grpc, which is worth avoiding. + switch status { + case proc.Running: + return grpc.StatusReply_Running, nil + case proc.Exited: + return grpc.StatusReply_Exited, nil + case proc.Stopped: + return grpc.StatusReply_Stopped, nil + } + return 0, fmt.Errorf("Unknown process status: %v", status) +} + +// Query streams the output/result of a process. +func (s *Server) Query(request *grpc.QueryRequest, queryServer grpc.BitBox_QueryServer) error { + uuid, err := uuid.FromBytes(request.GetID()) + if err != nil { + return err + } + + log.Println("[Query] ", uuid.String()) + // TODO: pass context from queryServer to Query + stream, err := s.c.Query(queryServer.Context(), uuid) + if err != nil { + return err + } + + for output := range stream { + // You should ask yourself: why do we initialize reply the same way twice? + // The answer: because QueryReply.Output is of type grpc.isQueryReply_Output + // which is not public. + var reply *grpc.QueryReply + switch output := output.(type) { + case *proc.ProcOutput_Stdouterr: + reply = &grpc.QueryReply{ + Output: &grpc.QueryReply_Stdouterr{ + Stdouterr: output.Output, + }, + } + case *proc.ProcOutput_ExitCode: + reply = &grpc.QueryReply{ + Output: &grpc.QueryReply_ExitCode{ + ExitCode: output.ExitCode, + }, + } + } + err := queryServer.Send(reply) + if err != nil { + return err // TODO: is this really how we should handle this? + } + } + return nil +}