From 403ac3f8a4c5cae774cfd72719557781564076f1 Mon Sep 17 00:00:00 2001 From: Hylke Visser Date: Fri, 10 Mar 2017 07:12:21 +0100 Subject: [PATCH 01/45] Update vendors --- vendor/vendor.json | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/vendor/vendor.json b/vendor/vendor.json index 2b8b89621..1e3d3f76c 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -77,68 +77,68 @@ { "checksumSHA1": "x795Q87cyvAAeuxXxft5iwd1Los=", "path": "github.com/TheThingsNetwork/go-utils/backoff", - "revision": "4555a883db1fba5c15ee20e714a71372d653c886", - "revisionTime": "2017-03-10T14:29:42Z" + "revision": "a921fe169d25314d639da4f6b75eb67bf598463a", + "revisionTime": "2017-03-09T17:03:45Z" }, { "checksumSHA1": "Dl7PzR7MCkAujv/tceQkrTzVq0U=", "path": "github.com/TheThingsNetwork/go-utils/encoding", - "revision": "4555a883db1fba5c15ee20e714a71372d653c886", - "revisionTime": "2017-03-10T14:29:42Z" + "revision": "a921fe169d25314d639da4f6b75eb67bf598463a", + "revisionTime": "2017-03-09T17:03:45Z" }, { "checksumSHA1": "Q+Nny6VBPlH007VXteMBuMPYikI=", "path": "github.com/TheThingsNetwork/go-utils/grpc/interceptor", - "revision": "4555a883db1fba5c15ee20e714a71372d653c886", - "revisionTime": "2017-03-10T14:29:42Z" + "revision": "a921fe169d25314d639da4f6b75eb67bf598463a", + "revisionTime": "2017-03-09T17:03:45Z" }, { - "checksumSHA1": "/0L+3YQZjPiYtK03Th/ThuUjNN4=", + "checksumSHA1": "QBtwAfoW7uOWdjvLg+glKNNETxs=", "path": "github.com/TheThingsNetwork/go-utils/grpc/restartstream", - "revision": "4555a883db1fba5c15ee20e714a71372d653c886", - "revisionTime": "2017-03-10T14:29:42Z" + "revision": "a921fe169d25314d639da4f6b75eb67bf598463a", + "revisionTime": "2017-03-09T17:03:45Z" }, { "checksumSHA1": "T7iFQUlCUAv4cJNDZC0//46Nbio=", "path": "github.com/TheThingsNetwork/go-utils/handlers/cli", - "revision": "4555a883db1fba5c15ee20e714a71372d653c886", - "revisionTime": "2017-03-10T14:29:42Z" + "revision": "a921fe169d25314d639da4f6b75eb67bf598463a", + "revisionTime": "2017-03-09T17:03:45Z" }, { "checksumSHA1": "aXt7ZSqIfsHWBbJPgHFjqtyxyQ0=", "path": "github.com/TheThingsNetwork/go-utils/log", - "revision": "4555a883db1fba5c15ee20e714a71372d653c886", - "revisionTime": "2017-03-10T14:29:42Z" + "revision": "a921fe169d25314d639da4f6b75eb67bf598463a", + "revisionTime": "2017-03-09T17:03:45Z" }, { "checksumSHA1": "RdI5upcV6MHSjr5Y9zogYvbeURw=", "path": "github.com/TheThingsNetwork/go-utils/log/apex", - "revision": "4555a883db1fba5c15ee20e714a71372d653c886", - "revisionTime": "2017-03-10T14:29:42Z" + "revision": "a921fe169d25314d639da4f6b75eb67bf598463a", + "revisionTime": "2017-03-09T17:03:45Z" }, { "checksumSHA1": "sQ0vy3MCGY1WgK9xldn1V6pMeZk=", "path": "github.com/TheThingsNetwork/go-utils/log/grpc", - "revision": "4555a883db1fba5c15ee20e714a71372d653c886", - "revisionTime": "2017-03-10T14:29:42Z" + "revision": "a921fe169d25314d639da4f6b75eb67bf598463a", + "revisionTime": "2017-03-09T17:03:45Z" }, { "checksumSHA1": "2/v0SMyHM5vgImOb1BEEDWeXZEY=", "path": "github.com/TheThingsNetwork/go-utils/pseudorandom", - "revision": "4555a883db1fba5c15ee20e714a71372d653c886", - "revisionTime": "2017-03-10T14:29:42Z" + "revision": "a921fe169d25314d639da4f6b75eb67bf598463a", + "revisionTime": "2017-03-09T17:03:45Z" }, { "checksumSHA1": "iYa+qSqzqZwpmoibM8/1X+aC3sI=", "path": "github.com/TheThingsNetwork/go-utils/random", - "revision": "4555a883db1fba5c15ee20e714a71372d653c886", - "revisionTime": "2017-03-10T14:29:42Z" + "revision": "a921fe169d25314d639da4f6b75eb67bf598463a", + "revisionTime": "2017-03-09T17:03:45Z" }, { "checksumSHA1": "kLFTtAVcjZbHXybodGAqJ8wxflY=", "path": "github.com/TheThingsNetwork/go-utils/roots", - "revision": "4555a883db1fba5c15ee20e714a71372d653c886", - "revisionTime": "2017-03-10T14:29:42Z" + "revision": "a921fe169d25314d639da4f6b75eb67bf598463a", + "revisionTime": "2017-03-09T17:03:45Z" }, { "checksumSHA1": "EZ0pNaUAiIbJuT5c0Sew85egLgw=", From befc2a6696698e9d1804090704c5aee77df9161f Mon Sep 17 00:00:00 2001 From: Hylke Visser Date: Mon, 13 Mar 2017 13:44:19 +0100 Subject: [PATCH 02/45] Add BackgroundContext to monitor client --- api/monitor/monitor.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/api/monitor/monitor.go b/api/monitor/monitor.go index 7a2f68bc2..2d5e4c5e7 100644 --- a/api/monitor/monitor.go +++ b/api/monitor/monitor.go @@ -36,12 +36,14 @@ type GenericStream interface { // ClientConfig for monitor Client type ClientConfig struct { - BufferSize int + BackgroundContext context.Context + BufferSize int } // DefaultClientConfig for monitor Client var DefaultClientConfig = ClientConfig{ - BufferSize: 10, + BackgroundContext: context.Background(), + BufferSize: 10, } // TLSConfig to use @@ -49,7 +51,7 @@ var TLSConfig *tls.Config // NewClient creates a new Client with the given configuration func NewClient(config ClientConfig) *Client { - ctx, cancel := context.WithCancel(context.Background()) + ctx, cancel := context.WithCancel(config.BackgroundContext) return &Client{ log: log.Get(), From 6bc4bba15264ad87f31371ed1439360e390eb6fd Mon Sep 17 00:00:00 2001 From: Hylke Visser Date: Mon, 6 Mar 2017 15:59:31 +0100 Subject: [PATCH 03/45] Reimplement Router Client --- api/router/client_streams.go | 394 ------------------------------- api/router/communication_test.go | 188 --------------- api/router/gateway_client.go | 109 --------- api/router/reference_server.go | 230 ++++++++++++++++++ api/router/router.go | 304 ++++++++++++++++++++++++ api/router/router_test.go | 89 +++++++ ttnctl/cmd/join.go | 15 +- ttnctl/cmd/uplink.go | 53 ++--- ttnctl/util/router.go | 5 +- 9 files changed, 649 insertions(+), 738 deletions(-) delete mode 100644 api/router/client_streams.go delete mode 100644 api/router/communication_test.go delete mode 100644 api/router/gateway_client.go create mode 100644 api/router/reference_server.go create mode 100644 api/router/router.go create mode 100644 api/router/router_test.go diff --git a/api/router/client_streams.go b/api/router/client_streams.go deleted file mode 100644 index 32126c837..000000000 --- a/api/router/client_streams.go +++ /dev/null @@ -1,394 +0,0 @@ -// Copyright © 2017 The Things Network -// Use of this source code is governed by the MIT license that can be found in the LICENSE file. - -package router - -import ( - "io" - "sync" - "time" - - "github.com/TheThingsNetwork/go-utils/log" - "github.com/TheThingsNetwork/ttn/api/fields" - "github.com/TheThingsNetwork/ttn/api/gateway" - "github.com/TheThingsNetwork/ttn/utils/backoff" - "github.com/golang/protobuf/ptypes/empty" - "golang.org/x/net/context" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" -) - -// GatewayStream interface -type GatewayStream interface { - Close() -} - -type gatewayStream struct { - closing bool - setup sync.WaitGroup - ctx log.Interface - client RouterClientForGateway -} - -// DefaultBufferSize indicates the default send and receive buffer sizes -var DefaultBufferSize = 10 - -// GatewayStatusStream for sending gateway statuses -type GatewayStatusStream interface { - GatewayStream - Send(*gateway.Status) error -} - -// NewMonitoredGatewayStatusStream starts and monitors a GatewayStatusStream -func NewMonitoredGatewayStatusStream(client RouterClientForGateway) GatewayStatusStream { - s := &gatewayStatusStream{ - ch: make(chan *gateway.Status, DefaultBufferSize), - err: make(chan error), - } - s.setup.Add(1) - s.client = client - s.ctx = client.GetLogger() - - go func() { - var retries int - - for { - // Session channels - ch := make(chan *gateway.Status) - errCh := make(chan error) - - // Session client - client, err := s.client.GatewayStatus() - s.setup.Done() - if err != nil { - if grpc.Code(err) == codes.Canceled { - s.ctx.Debug("Stopped GatewayStatus stream") - break - } - s.ctx.WithError(err).Warn("Could not start GatewayStatus stream, retrying...") - s.setup.Add(1) - time.Sleep(backoff.Backoff(retries)) - retries++ - continue - } - - s.ctx.Info("Started GatewayStatus stream") - - // Receive errors - go func() { - empty := new(empty.Empty) - if err := client.RecvMsg(empty); err != nil { - errCh <- err - } - close(errCh) - }() - - // Send - go func() { - for status := range ch { - s.ctx.Debug("Sending GatewayStatus message") - if err := client.Send(status); err != nil { - s.ctx.WithError(err).Warn("Error sending GatewayStatus message") - break - } - } - }() - - // Monitoring - var mErr error - - monitor: - for { - select { - case <-time.After(10 * time.Second): - retries = 0 - case mErr = <-errCh: - break monitor - case msg, ok := <-s.ch: - if !ok { - break monitor // channel closed - } - ch <- msg - case <-s.client.TokenChange(): - s.ctx.Debug("Restarting GatewayStatus stream with new token") - break monitor - } - } - - close(ch) - client.CloseAndRecv() - - if mErr == nil || mErr == io.EOF || grpc.Code(mErr) == codes.Canceled { - s.ctx.Debug("Stopped GatewayStatus stream") - } else { - s.ctx.WithError(mErr).Warn("Error in GatewayStatus stream") - } - - if s.closing { - break - } - - s.setup.Add(1) - time.Sleep(backoff.Backoff(retries)) - retries++ - } - }() - - return s -} - -type gatewayStatusStream struct { - gatewayStream - ch chan *gateway.Status - err chan error -} - -func (s *gatewayStatusStream) Send(status *gateway.Status) error { - select { - case s.ch <- status: - default: - s.ctx.Warn("Dropping GatewayStatus message, buffer full") - } - return nil -} - -func (s *gatewayStatusStream) Close() { - s.setup.Wait() - s.ctx.Debug("Closing GatewayStatus stream") - s.closing = true - close(s.ch) -} - -// UplinkStream for sending uplink messages -type UplinkStream interface { - GatewayStream - Send(*UplinkMessage) error -} - -// NewMonitoredUplinkStream starts and monitors a UplinkStream -func NewMonitoredUplinkStream(client RouterClientForGateway) UplinkStream { - s := &uplinkStream{ - ch: make(chan *UplinkMessage, DefaultBufferSize), - err: make(chan error), - } - s.setup.Add(1) - s.client = client - s.ctx = client.GetLogger() - - go func() { - var retries int - - for { - // Session channels - ch := make(chan *UplinkMessage) - errCh := make(chan error) - - // Session client - client, err := s.client.Uplink() - s.setup.Done() - if err != nil { - if grpc.Code(err) == codes.Canceled { - s.ctx.Debug("Stopped Uplink stream") - break - } - s.ctx.WithError(err).Warn("Could not start Uplink stream, retrying...") - s.setup.Add(1) - time.Sleep(backoff.Backoff(retries)) - retries++ - continue - } - - s.ctx.Info("Started Uplink stream") - - // Receive errors - go func() { - empty := new(empty.Empty) - if err := client.RecvMsg(empty); err != nil { - errCh <- err - } - close(errCh) - }() - - // Send - go func() { - for message := range ch { - s.ctx.WithFields(fields.Get(message)).Debug("Sending Uplink message") - if err := client.Send(message); err != nil { - s.ctx.WithError(err).Warn("Error sending Uplink message") - break - } - } - }() - - // Monitoring - var mErr error - - monitor: - for { - select { - case <-time.After(10 * time.Second): - retries = 0 - case mErr = <-errCh: - break monitor - case msg, ok := <-s.ch: - if !ok { - break monitor // channel closed - } - ch <- msg - case <-s.client.TokenChange(): - s.ctx.Debug("Restarting Uplink stream with new token") - break monitor - } - } - - close(ch) - client.CloseAndRecv() - - if mErr == nil || mErr == io.EOF || grpc.Code(mErr) == codes.Canceled { - s.ctx.Debug("Stopped Uplink stream") - } else { - s.ctx.WithError(mErr).Warn("Error in Uplink stream") - } - - if s.closing { - break - } - - s.setup.Add(1) - time.Sleep(backoff.Backoff(retries)) - retries++ - } - }() - - return s -} - -type uplinkStream struct { - gatewayStream - ch chan *UplinkMessage - err chan error -} - -func (s *uplinkStream) Send(message *UplinkMessage) error { - select { - case s.ch <- message: - default: - s.ctx.Warn("Dropping Uplink message, buffer full") - } - return nil -} - -func (s *uplinkStream) Close() { - s.setup.Wait() - s.ctx.Debug("Closing Uplink stream") - s.closing = true - close(s.ch) -} - -// DownlinkStream for receiving downlink messages -type DownlinkStream interface { - GatewayStream - Channel() <-chan *DownlinkMessage -} - -// NewMonitoredDownlinkStream starts and monitors a DownlinkStream -func NewMonitoredDownlinkStream(client RouterClientForGateway) DownlinkStream { - s := &downlinkStream{ - ch: make(chan *DownlinkMessage, DefaultBufferSize), - err: make(chan error), - } - s.setup.Add(1) - s.client = client - s.ctx = client.GetLogger() - - go func() { - var client Router_SubscribeClient - var err error - var retries int - var message *DownlinkMessage - - for { - client, s.cancel, err = s.client.Subscribe() - s.setup.Done() - if err != nil { - if grpc.Code(err) == codes.Canceled { - s.ctx.Debug("Stopped Downlink stream") - break - } - s.ctx.WithError(err).Warn("Could not start Downlink stream, retrying...") - s.setup.Add(1) - time.Sleep(backoff.Backoff(retries)) - retries++ - continue - } - - go func() { - <-s.client.TokenChange() - s.ctx.Debug("Restarting Downlink stream with new token") - s.cancel() - }() - - s.ctx.Info("Started Downlink stream") - - for { - message, err = client.Recv() - if message != nil { - s.ctx.WithFields(fields.Get(message)).Debug("Receiving Downlink message") - if err := message.Validate(); err != nil { - s.ctx.WithError(err).Warn("Invalid Downlink") - continue - } - if err := message.UnmarshalPayload(); err != nil { - s.ctx.WithError(err).Warn("Could not unmarshal Downlink payload") - } - select { - case s.ch <- message: - default: - s.ctx.Warn("Dropping Downlink message, buffer full") - } - } - if err != nil { - break - } - retries = 0 - } - - if err == nil || err == io.EOF || grpc.Code(err) == codes.Canceled { - s.ctx.Debug("Stopped Downlink stream") - } else { - s.ctx.WithError(err).Warn("Error in Downlink stream") - } - - if s.closing { - break - } - - s.setup.Add(1) - time.Sleep(backoff.Backoff(retries)) - retries++ - } - - close(s.ch) - }() - return s -} - -type downlinkStream struct { - gatewayStream - cancel context.CancelFunc - ch chan *DownlinkMessage - err chan error -} - -func (s *downlinkStream) Close() { - s.setup.Wait() - s.ctx.Debug("Closing Downlink stream") - s.closing = true - if s.cancel != nil { - s.cancel() - } -} - -func (s *downlinkStream) Channel() <-chan *DownlinkMessage { - return s.ch -} diff --git a/api/router/communication_test.go b/api/router/communication_test.go deleted file mode 100644 index 288496cad..000000000 --- a/api/router/communication_test.go +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright © 2017 The Things Network -// Use of this source code is governed by the MIT license that can be found in the LICENSE file. - -package router - -import ( - "fmt" - "math/rand" - "net" - "testing" - "time" - - "github.com/TheThingsNetwork/go-utils/log" - "github.com/TheThingsNetwork/ttn/api" - "github.com/TheThingsNetwork/ttn/api/gateway" - "github.com/TheThingsNetwork/ttn/api/protocol" - "github.com/TheThingsNetwork/ttn/api/protocol/lorawan" - . "github.com/TheThingsNetwork/ttn/utils/testing" - . "github.com/smartystreets/assertions" - "golang.org/x/net/context" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/metadata" -) - -func newTestRouter() *testRouter { - return &testRouter{ - RouterStreamServer: NewRouterStreamServer(), - } -} - -type testRouter struct { - *RouterStreamServer -} - -func (s *testRouter) Activate(context.Context, *DeviceActivationRequest) (*DeviceActivationResponse, error) { - return nil, grpc.Errorf(codes.Unimplemented, "Not implemented") -} - -func (s *testRouter) Serve(port int) { - lis, err := net.Listen("tcp", fmt.Sprintf(":%d", port)) - if err != nil { - panic(err) - } - srv := grpc.NewServer() - RegisterRouterServer(srv, s) - srv.Serve(lis) -} - -func TestRouterCommunication(t *testing.T) { - a := New(t) - - ctx := GetLogger(t, "TestRouterCommunication") - log.Set(ctx) - - rtr := newTestRouter() - rand.Seed(time.Now().UnixNano()) - port := rand.Intn(1000) + 10000 - go rtr.Serve(port) - - conn, _ := api.Dial(fmt.Sprintf("localhost:%d", port)) - - { - rtr.UplinkChanFunc = func(md metadata.MD) (chan *UplinkMessage, error) { - ch := make(chan *UplinkMessage, 1) - go func() { - ctx.Info("[SERVER] Channel opened") - for message := range ch { - ctx.WithField("Message", message).Info("[SERVER] Received Uplink") - } - ctx.Info("[SERVER] Channel closed") - }() - return ch, nil - } - - rtrClient := NewRouterClient(conn) - gtwClient := NewRouterClientForGateway(rtrClient, "dev", "token") - uplink := NewMonitoredUplinkStream(gtwClient) - - err := uplink.Send(&UplinkMessage{ - Payload: []byte{1, 2, 3, 4}, - ProtocolMetadata: &protocol.RxMetadata{Protocol: &protocol.RxMetadata_Lorawan{Lorawan: &lorawan.Metadata{ - Modulation: lorawan.Modulation_LORA, - DataRate: "SF7BW125", - CodingRate: "4/7", - }}}, - GatewayMetadata: &gateway.RxMetadata{ - GatewayId: "dev", - }, - }) - - a.So(err, ShouldBeNil) - - time.Sleep(10 * time.Millisecond) - - uplink.Close() - - time.Sleep(10 * time.Millisecond) - - gtwClient.Close() - - time.Sleep(10 * time.Millisecond) - } - - { - rtr.GatewayStatusChanFunc = func(md metadata.MD) (chan *gateway.Status, error) { - ch := make(chan *gateway.Status, 1) - go func() { - ctx.Info("[SERVER] Channel opened") - for message := range ch { - ctx.WithField("Message", message).Info("[SERVER] Received GatewayStatus") - } - ctx.Info("[SERVER] Channel closed") - }() - return ch, nil - } - - rtrClient := NewRouterClient(conn) - gtwClient := NewRouterClientForGateway(rtrClient, "dev", "token") - status := NewMonitoredGatewayStatusStream(gtwClient) - - err := status.Send(&gateway.Status{Time: time.Now().UnixNano()}) - - a.So(err, ShouldBeNil) - - time.Sleep(10 * time.Millisecond) - - status.Close() - - time.Sleep(10 * time.Millisecond) - - gtwClient.Close() - - time.Sleep(10 * time.Millisecond) - } - - { - rtr.DownlinkChanFunc = func(md metadata.MD) (<-chan *DownlinkMessage, func(), error) { - ch := make(chan *DownlinkMessage, 1) - stop := make(chan struct{}) - cancel := func() { - ctx.Info("[SERVER] Canceling downlink") - close(stop) - } - go func() { - loop: - for { - select { - case <-stop: - break loop - case <-time.After(5 * time.Millisecond): - ctx.Info("[SERVER] Sending Downlink") - ch <- &DownlinkMessage{ - Payload: []byte{1, 2, 3, 4}, - } - } - } - close(ch) - ctx.Info("[SERVER] Closed Downlink") - }() - return ch, cancel, nil - } - - rtrClient := NewRouterClient(conn) - gtwClient := NewRouterClientForGateway(rtrClient, "dev", "token") - downlink := NewMonitoredDownlinkStream(gtwClient) - - ch := downlink.Channel() - - go func() { - for downlink := range ch { - ctx.WithField("Downlink", downlink).Info("[CLIENT] Received Downlink") - } - ctx.Info("[CLIENT] Closed Downlink") - }() - - time.Sleep(10 * time.Millisecond) - - downlink.Close() - - time.Sleep(10 * time.Millisecond) - - gtwClient.Close() - - time.Sleep(10 * time.Millisecond) - } - -} diff --git a/api/router/gateway_client.go b/api/router/gateway_client.go deleted file mode 100644 index 27330eacf..000000000 --- a/api/router/gateway_client.go +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright © 2017 The Things Network -// Use of this source code is governed by the MIT license that can be found in the LICENSE file. - -package router - -import ( - "sync" - - "github.com/TheThingsNetwork/go-utils/log" - "github.com/TheThingsNetwork/ttn/api" - "golang.org/x/net/context" -) - -// RouterClientForGateway is a RouterClient for a specific gateway -type RouterClientForGateway interface { - Close() - - GetLogger() log.Interface - SetLogger(log.Interface) - - SetToken(token string) - TokenChange() <-chan struct{} - - GatewayStatus() (Router_GatewayStatusClient, error) - Uplink() (Router_UplinkClient, error) - Subscribe() (Router_SubscribeClient, context.CancelFunc, error) - Activate(in *DeviceActivationRequest) (*DeviceActivationResponse, error) -} - -// NewRouterClientForGateway returns a new RouterClient for the given gateway ID and access token -func NewRouterClientForGateway(client RouterClient, gatewayID, token string) RouterClientForGateway { - ctx, cancel := context.WithCancel(context.Background()) - return &routerClientForGateway{ - ctx: log.Get().WithField("GatewayID", gatewayID), - client: client, - gatewayID: gatewayID, - token: token, - tokenChange: make(chan struct{}), - bgCtx: ctx, - cancel: cancel, - } -} - -type routerClientForGateway struct { - ctx log.Interface - client RouterClient - gatewayID string - token string - tokenChange chan struct{} - bgCtx context.Context - cancel context.CancelFunc - mu sync.RWMutex -} - -func (c *routerClientForGateway) Close() { - c.cancel() -} - -func (c *routerClientForGateway) GetLogger() log.Interface { - return c.ctx -} - -func (c *routerClientForGateway) SetLogger(logger log.Interface) { - c.ctx = logger -} - -func (c *routerClientForGateway) getContext() context.Context { - c.mu.RLock() - defer c.mu.RUnlock() - ctx := api.ContextWithID(c.bgCtx, c.gatewayID) - ctx = api.ContextWithToken(ctx, c.token) - return ctx -} - -func (c *routerClientForGateway) SetToken(token string) { - c.mu.Lock() - defer c.mu.Unlock() - c.token = token - close(c.tokenChange) - c.tokenChange = make(chan struct{}) -} - -func (c *routerClientForGateway) TokenChange() <-chan struct{} { - c.mu.RLock() - defer c.mu.RUnlock() - return c.tokenChange -} - -func (c *routerClientForGateway) GatewayStatus() (Router_GatewayStatusClient, error) { - c.ctx.Debug("Starting GatewayStatus stream") - return c.client.GatewayStatus(c.getContext()) -} - -func (c *routerClientForGateway) Uplink() (Router_UplinkClient, error) { - c.ctx.Debug("Starting Uplink stream") - return c.client.Uplink(c.getContext()) -} - -func (c *routerClientForGateway) Subscribe() (Router_SubscribeClient, context.CancelFunc, error) { - c.ctx.Debug("Starting Subscribe stream") - ctx, cancel := context.WithCancel(c.getContext()) - client, err := c.client.Subscribe(ctx, &SubscribeRequest{}) - return client, cancel, err -} - -func (c *routerClientForGateway) Activate(in *DeviceActivationRequest) (*DeviceActivationResponse, error) { - c.ctx.Debug("Calling Activate") - return c.client.Activate(c.getContext(), in) -} diff --git a/api/router/reference_server.go b/api/router/reference_server.go new file mode 100644 index 000000000..2cc31f4ff --- /dev/null +++ b/api/router/reference_server.go @@ -0,0 +1,230 @@ +// Copyright © 2017 The Things Network +// Use of this source code is governed by the MIT license that can be found in the LICENSE file. + +package router + +import ( + "io" + "sync" + "sync/atomic" + + "github.com/TheThingsNetwork/go-utils/log" + "github.com/TheThingsNetwork/ttn/api" + "github.com/TheThingsNetwork/ttn/api/gateway" + "github.com/TheThingsNetwork/ttn/utils/errors" + "github.com/golang/protobuf/ptypes/empty" + "golang.org/x/net/context" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" +) + +// NewReferenceRouterServer creates a new reference router server +func NewReferenceRouterServer(bufferSize int) *ReferenceRouterServer { + s := &ReferenceRouterServer{ + ctx: log.Get(), + + bufferSize: bufferSize, + + gatewayStatuses: make(chan *gateway.Status, bufferSize), + uplinkMessages: make(chan *UplinkMessage, bufferSize), + downlink: make(map[string]*downlinkSubscription), + } + for i := 0; i < bufferSize; i++ { + go func() { + for { + select { + case <-s.gatewayStatuses: + atomic.AddUint64(&s.metrics.gatewayStatuses, 1) + case <-s.uplinkMessages: + atomic.AddUint64(&s.metrics.uplinkMessages, 1) + } + } + }() + } + return s +} + +type metrics struct { + gatewayStatuses uint64 + uplinkMessages uint64 +} + +// ReferenceRouterServer is a new reference router server +type ReferenceRouterServer struct { + ctx log.Interface + + bufferSize int + + gatewayStatuses chan *gateway.Status + uplinkMessages chan *UplinkMessage + + mu sync.RWMutex + downlink map[string]*downlinkSubscription + + metrics metrics +} + +type downlinkSubscription struct { + ch chan *DownlinkMessage + subscribers int +} + +func (s *ReferenceRouterServer) addDownlinkSubscriber(gatewayID string) chan *DownlinkMessage { + s.mu.Lock() + defer s.mu.Unlock() + if sub, ok := s.downlink[gatewayID]; ok { + sub.subscribers++ + return sub.ch + } + sub := &downlinkSubscription{ + subscribers: 1, + ch: make(chan *DownlinkMessage, s.bufferSize), + } + s.downlink[gatewayID] = sub + return sub.ch +} + +func (s *ReferenceRouterServer) removeDownlinkSubscriber(gatewayID string) { + s.mu.Lock() + defer s.mu.Unlock() + if sub, ok := s.downlink[gatewayID]; ok && sub.subscribers > 0 { + sub.subscribers-- + } +} + +func (s *ReferenceRouterServer) getAndAuthGateway(ctx context.Context) (string, error) { + id, err := api.IDFromContext(ctx) + if err != nil { + return "", err + } + token, err := api.TokenFromContext(ctx) + if err != nil { + return "", err + } + // Actually validate token here, if failed: return nil, grpc.Errorf(codes.Unauthenticated, "Gateway Authentication Failed") + s.ctx.WithFields(log.Fields{"ID": id, "Token": token}).Info("Gateway Authenticated") + return id, nil +} + +// GatewayStatus RPC +func (s *ReferenceRouterServer) GatewayStatus(stream Router_GatewayStatusServer) (err error) { + gatewayID, err := s.getAndAuthGateway(stream.Context()) + if err != nil { + return errors.NewErrPermissionDenied(err.Error()) + } + ctx := s.ctx.WithField("GatewayID", gatewayID) + ctx.Info("GatewayStatus stream started") + defer func() { + if err != nil { + ctx.WithError(err).Info("GatewayStatus stream ended") + } else { + ctx.Info("GatewayStatus stream ended") + } + }() + var streamErr atomic.Value + go func() { + <-stream.Context().Done() + streamErr.Store(stream.Context().Err()) + }() + for { + streamErr := streamErr.Load() + if streamErr != nil { + return streamErr.(error) + } + msg, err := stream.Recv() + if err == io.EOF { + return stream.SendAndClose(&empty.Empty{}) + } + if err != nil { + return err + } + ctx.Info("Received GatewayStatus") + select { + case s.gatewayStatuses <- msg: + default: + ctx.Warn("Dropping Status") + } + } +} + +// Uplink RPC +func (s *ReferenceRouterServer) Uplink(stream Router_UplinkServer) error { + gatewayID, err := s.getAndAuthGateway(stream.Context()) + if err != nil { + return errors.NewErrPermissionDenied(err.Error()) + } + ctx := s.ctx.WithField("GatewayID", gatewayID) + ctx.Info("GatewayUplink stream started") + defer func() { + if err != nil { + ctx.WithError(err).Info("GatewayUplink stream ended") + } else { + ctx.Info("GatewayUplink stream ended") + } + }() + var streamErr atomic.Value + go func() { + <-stream.Context().Done() + streamErr.Store(stream.Context().Err()) + }() + for { + streamErr := streamErr.Load() + if streamErr != nil { + return streamErr.(error) + } + msg, err := stream.Recv() + if err == io.EOF { + return stream.SendAndClose(&empty.Empty{}) + } + if err != nil { + return err + } + ctx.Info("Received UplinkMessage") + select { + case s.uplinkMessages <- msg: + default: + ctx.Warn("Dropping UplinkMessage") + } + } +} + +// Subscribe RPC +func (s *ReferenceRouterServer) Subscribe(req *SubscribeRequest, stream Router_SubscribeServer) error { + gatewayID, err := s.getAndAuthGateway(stream.Context()) + if err != nil { + return errors.NewErrPermissionDenied(err.Error()) + } + ctx := s.ctx.WithField("GatewayID", gatewayID) + ctx.Info("GatewayDownlink stream started") + defer func() { + if err != nil { + ctx.WithError(err).Info("GatewayDownlink stream ended") + } else { + ctx.Info("GatewayDownlink stream ended") + } + }() + + sub := s.addDownlinkSubscriber(gatewayID) + defer s.removeDownlinkSubscriber(gatewayID) + + for { + select { + case <-stream.Context().Done(): + return stream.Context().Err() + case msg, ok := <-sub: + if !ok { + return nil + } + err := stream.Send(msg) + if err != nil { + return err + } + ctx.Info("Sent DownlinkMessage") + } + } +} + +// Activate RPC +func (s *ReferenceRouterServer) Activate(ctx context.Context, req *DeviceActivationRequest) (*DeviceActivationResponse, error) { + return nil, grpc.Errorf(codes.Unimplemented, "Not implemented") +} diff --git a/api/router/router.go b/api/router/router.go new file mode 100644 index 000000000..052553826 --- /dev/null +++ b/api/router/router.go @@ -0,0 +1,304 @@ +// Copyright © 2017 The Things Network +// Use of this source code is governed by the MIT license that can be found in the LICENSE file. + +package router + +import ( + "context" + "io" + "sync" + + "github.com/TheThingsNetwork/go-utils/grpc/restartstream" + "github.com/TheThingsNetwork/go-utils/log" + "github.com/TheThingsNetwork/ttn/api" + "github.com/TheThingsNetwork/ttn/api/gateway" + "github.com/golang/protobuf/ptypes/empty" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" +) + +// GenericStream is used for sending to and receiving from the router. +type GenericStream interface { + Uplink(*UplinkMessage) + Status(*gateway.Status) + Downlink() <-chan *DownlinkMessage + Close() +} + +// ClientConfig for router Client +type ClientConfig struct { + BackgroundContext context.Context + BufferSize int +} + +// DefaultClientConfig for router Client +var DefaultClientConfig = ClientConfig{ + BackgroundContext: context.Background(), + BufferSize: 10, +} + +// NewClient creates a new Client with the given configuration +func NewClient(config ClientConfig) *Client { + ctx, cancel := context.WithCancel(config.BackgroundContext) + + return &Client{ + log: log.Get(), + ctx: ctx, + cancel: cancel, + + config: config, + } +} + +// Client for router +type Client struct { + log log.Interface + ctx context.Context + cancel context.CancelFunc + + config ClientConfig + serverConns []*serverConn +} + +// AddServer adds a router server +func (c *Client) AddServer(name string, conn *grpc.ClientConn) { + log := c.log.WithField("Router", name) + log.Info("Adding Router server") + s := &serverConn{ + ctx: log, + name: name, + conn: conn, + } + c.serverConns = append(c.serverConns, s) +} + +// Close the client and all its connections +func (c *Client) Close() { + c.cancel() + for _, server := range c.serverConns { + server.Close() + } +} + +type serverConn struct { + ctx log.Interface + name string + + ready chan struct{} + conn *grpc.ClientConn +} + +func (c *serverConn) Close() { + if c.ready != nil { + <-c.ready + } + if c.conn != nil { + c.conn.Close() + } +} + +type gatewayStreams struct { + log log.Interface + ctx context.Context + cancel context.CancelFunc + + mu sync.RWMutex + uplink map[string]chan *UplinkMessage + status map[string]chan *gateway.Status + + downlink chan *DownlinkMessage +} + +func (s *gatewayStreams) Uplink(msg *UplinkMessage) { + s.mu.RLock() + defer s.mu.RUnlock() + s.log.Debug("Sending UplinkMessage to router") + for serverName, ch := range s.uplink { + select { + case ch <- msg: + default: + s.log.WithField("Router", serverName).Warn("UplinkMessage buffer full") + } + } +} + +func (s *gatewayStreams) Status(msg *gateway.Status) { + s.mu.RLock() + defer s.mu.RUnlock() + s.log.Debug("Sending Status to router") + for serverName, ch := range s.status { + select { + case ch <- msg: + default: + s.log.WithField("Router", serverName).Warn("GatewayStatus buffer full") + } + } +} + +func (s *gatewayStreams) Downlink() <-chan *DownlinkMessage { + s.mu.RLock() + defer s.mu.RUnlock() + return s.downlink +} + +func (s *gatewayStreams) Close() { + s.cancel() +} + +// NewGatewayStreams returns new streams using the given gateway ID and token +func (c *Client) NewGatewayStreams(id string, token string) GenericStream { + log := c.log.WithField("GatewayID", id) + ctx, cancel := context.WithCancel(c.ctx) + ctx = api.ContextWithID(ctx, id) + ctx = api.ContextWithToken(ctx, token) + s := &gatewayStreams{ + log: log, + ctx: ctx, + cancel: cancel, + + uplink: make(map[string]chan *UplinkMessage), + status: make(map[string]chan *gateway.Status), + + downlink: make(chan *DownlinkMessage, c.config.BufferSize), + } + + var wgDown sync.WaitGroup + go func() { + wgDown.Wait() + close(s.downlink) + }() + + // Hook up the router servers + for _, server := range c.serverConns { + wgDown.Add(1) + go func(server *serverConn) { + if server.ready != nil { + select { + case <-ctx.Done(): + return + case <-server.ready: + } + } + if server.conn == nil { + return + } + log := log.WithField("Router", server.name) + cli := NewRouterClient(server.conn) + + logStreamErr := func(streamName string, err error) { + switch { + case err == nil: + log.Debugf("%s stream closed", streamName) + case err == io.EOF: + log.WithError(err).Debugf("%s stream ended", streamName) + case err == context.Canceled || grpc.Code(err) == codes.Canceled: + log.WithError(err).Debugf("%s stream canceled", streamName) + case err == context.DeadlineExceeded || grpc.Code(err) == codes.DeadlineExceeded: + log.WithError(err).Debugf("%s stream deadline exceeded", streamName) + case grpc.ErrorDesc(err) == grpc.ErrClientConnClosing.Error(): + log.WithError(err).Debugf("%s stream connection closed", streamName) + default: + log.WithError(err).Warnf("%s stream closed unexpectedly", streamName) + } + } + + // Stream channels + chUplink := make(chan *UplinkMessage, c.config.BufferSize) + chStatus := make(chan *gateway.Status, c.config.BufferSize) + + defer func() { + s.mu.Lock() + defer s.mu.Unlock() + delete(s.uplink, server.name) + delete(s.status, server.name) + close(chUplink) + close(chStatus) + }() + + // Uplink stream + uplink, err := cli.Uplink(ctx) + if err != nil { + log.WithError(err).Warn("Could not set up Uplink stream") + } else { + s.mu.Lock() + s.uplink[server.name] = chUplink + s.mu.Unlock() + go func() { + err := uplink.RecvMsg(new(empty.Empty)) + logStreamErr("Uplink", err) + s.mu.Lock() + defer s.mu.Unlock() + delete(s.uplink, server.name) + }() + } + + // Downlink stream + downlink, err := cli.Subscribe(ctx, &SubscribeRequest{}) + if err != nil { + log.WithError(err).Warn("Could not set up Subscribe stream") + wgDown.Done() + } else { + go func() { + defer func() { + wgDown.Done() + }() + for { + msg, err := downlink.Recv() + if err != nil { + logStreamErr("Subscribe", err) + return + } + select { + case s.downlink <- msg: + default: + log.Warn("Downlink buffer full") + } + } + }() + } + + // Status stream + status, err := cli.GatewayStatus(ctx) + if err != nil { + log.WithError(err).Warn("Could not set up GatewayStatus stream") + } else { + s.mu.Lock() + s.status[server.name] = chStatus + s.mu.Unlock() + go func() { + err := status.RecvMsg(new(empty.Empty)) + logStreamErr("GatewayStatus", err) + s.mu.Lock() + defer s.mu.Unlock() + delete(s.status, server.name) + }() + } + + log.Debug("Start handling Gateway streams") + defer log.Debug("Done handling Gateway streams") + for { + select { + case <-ctx.Done(): + return + case msg := <-chStatus: + if err := status.Send(msg); err != nil { + log.WithError(err).Warn("Could not send GatewayStatus to router") + if err == restartstream.ErrStreamClosed { + return + } + } + case msg := <-chUplink: + if err := uplink.Send(msg); err != nil { + log.WithError(err).Warn("Could not send UplinkMessage to router") + if err == restartstream.ErrStreamClosed { + return + } + } + } + } + + }(server) + } + + return s +} diff --git a/api/router/router_test.go b/api/router/router_test.go new file mode 100644 index 000000000..c1890890d --- /dev/null +++ b/api/router/router_test.go @@ -0,0 +1,89 @@ +// Copyright © 2017 The Things Network +// Use of this source code is governed by the MIT license that can be found in the LICENSE file. + +package router + +import ( + "net" + "testing" + "time" + + "github.com/TheThingsNetwork/go-utils/log" + "github.com/TheThingsNetwork/ttn/api/gateway" + "github.com/TheThingsNetwork/ttn/api/pool" + "github.com/htdvisser/grpc-testing/test" + . "github.com/smartystreets/assertions" + "google.golang.org/grpc" +) + +func TestRouter(t *testing.T) { + waitTime := 10 * time.Millisecond + + a := New(t) + + testLogger := test.NewLogger() + log.Set(testLogger) + defer testLogger.Print(t) + + lis, err := net.Listen("tcp", "localhost:0") + if err != nil { + t.Fatalf("Failed to listen: %v", err) + } + s := grpc.NewServer() + server := NewReferenceRouterServer(10) + + RegisterRouterServer(s, server) + go s.Serve(lis) + + cli := NewClient(DefaultClientConfig) + + conn, err := pool.Global.Dial(lis.Addr().String(), grpc.WithInsecure()) + if err != nil { + t.Fatalf("Failed to connect to server: %v", err) + } + + cli.AddServer("test", conn) + time.Sleep(waitTime) + defer func() { + cli.Close() + time.Sleep(waitTime) + s.Stop() + }() + + testLogger.Print(t) + + gtw := cli.NewGatewayStreams("test", "token") + time.Sleep(waitTime) + for i := 0; i < 20; i++ { + gtw.Uplink(&UplinkMessage{}) + gtw.Status(&gateway.Status{}) + time.Sleep(time.Millisecond) + } + time.Sleep(waitTime) + + a.So(server.metrics.uplinkMessages, ShouldEqual, 20) + a.So(server.metrics.gatewayStatuses, ShouldEqual, 20) + + testLogger.Print(t) + + downlink := gtw.Downlink() + recvDownlink := []*DownlinkMessage{} + var downlinkClosed bool + go func() { + for msg := range downlink { + recvDownlink = append(recvDownlink, msg) + } + downlinkClosed = true + }() + + server.downlink["test"].ch <- &DownlinkMessage{} + + time.Sleep(waitTime) + gtw.Close() + time.Sleep(waitTime) + + a.So(recvDownlink, ShouldHaveLength, 1) + a.So(downlinkClosed, ShouldBeTrue) + + testLogger.Print(t) +} diff --git a/ttnctl/cmd/join.go b/ttnctl/cmd/join.go index 6b77363c8..32c5a8971 100644 --- a/ttnctl/cmd/join.go +++ b/ttnctl/cmd/join.go @@ -48,6 +48,7 @@ var joinCmd = &cobra.Command{ rtrConn, rtrClient := util.GetRouter(ctx) defer rtrConn.Close() + defer rtrClient.Close() gatewayID := viper.GetString("gateway-id") gatewayToken := viper.GetString("gateway-token") @@ -64,11 +65,9 @@ var joinCmd = &cobra.Command{ } } - gtwClient := router.NewRouterClientForGateway(rtrClient, gatewayID, gatewayToken) + gtwClient := rtrClient.NewGatewayStreams(gatewayID, gatewayToken) defer gtwClient.Close() - downlinkStream := router.NewMonitoredDownlinkStream(gtwClient) - defer downlinkStream.Close() time.Sleep(100 * time.Millisecond) joinReq := &pb_lorawan.Message{ @@ -89,20 +88,14 @@ var joinCmd = &cobra.Command{ } uplink.UnmarshalPayload() - uplinkStream := router.NewMonitoredUplinkStream(gtwClient) - defer uplinkStream.Close() - - err = uplinkStream.Send(uplink) - if err != nil { - ctx.WithError(err).Fatal("Could not send uplink to Router") - } + gtwClient.Uplink(uplink) time.Sleep(100 * time.Millisecond) ctx.Info("Sent uplink to Router") select { - case downlinkMessage, ok := <-downlinkStream.Channel(): + case downlinkMessage, ok := <-gtwClient.Downlink(): if !ok { ctx.Info("Did not receive downlink") break diff --git a/ttnctl/cmd/uplink.go b/ttnctl/cmd/uplink.go index ef50b0263..db589ae05 100644 --- a/ttnctl/cmd/uplink.go +++ b/ttnctl/cmd/uplink.go @@ -51,17 +51,13 @@ var uplinkCmd = &cobra.Command{ } } - withDownlink, _ := cmd.Flags().GetBool("downlink") - confirmed, _ := cmd.Flags().GetBool("confirmed") - if confirmed { - withDownlink = true - } ack, _ := cmd.Flags().GetBool("ack") rtrConn, rtrClient := util.GetRouter(ctx) defer rtrConn.Close() + defer rtrClient.Close() gatewayID := viper.GetString("gateway-id") gatewayToken := viper.GetString("gateway-token") @@ -78,25 +74,17 @@ var uplinkCmd = &cobra.Command{ } } - gtwClient := router.NewRouterClientForGateway(rtrClient, gatewayID, gatewayToken) + gtwClient := rtrClient.NewGatewayStreams(gatewayID, gatewayToken) defer gtwClient.Close() - var downlinkStream router.DownlinkStream - if withDownlink { - downlinkStream = router.NewMonitoredDownlinkStream(gtwClient) - defer downlinkStream.Close() - time.Sleep(100 * time.Millisecond) - } + time.Sleep(100 * time.Millisecond) m := &util.Message{} m.SetDevice(devAddr, nwkSKey, appSKey) m.SetMessage(confirmed, ack, fCnt, payload) bytes := m.Bytes() - uplinkStream := router.NewMonitoredUplinkStream(gtwClient) - defer uplinkStream.Close() - - err = uplinkStream.Send(&router.UplinkMessage{ + gtwClient.Uplink(&router.UplinkMessage{ Payload: bytes, GatewayMetadata: util.GetGatewayMetadata(gatewayID, 868100000), ProtocolMetadata: util.GetProtocolMetadata("SF7BW125"), @@ -109,32 +97,29 @@ var uplinkCmd = &cobra.Command{ ctx.Info("Sent uplink to Router") - if downlinkStream != nil { - select { - case downlinkMessage, ok := <-downlinkStream.Channel(): - if !ok { - ctx.Info("Did not receive downlink") - break - } - if err := m.Unmarshal(downlinkMessage.Payload); err != nil { - ctx.WithError(err).Fatal("Could not unmarshal downlink") - } - ctx.WithFields(ttnlog.Fields{ - "Payload": m.Payload, - "FCnt": m.FCnt, - "FPort": m.FPort, - }).Info("Received Downlink") - case <-time.After(2 * time.Second): + select { + case downlinkMessage, ok := <-gtwClient.Downlink(): + if !ok { ctx.Info("Did not receive downlink") + break + } + if err := m.Unmarshal(downlinkMessage.Payload); err != nil { + ctx.WithError(err).Fatal("Could not unmarshal downlink") } + ctx.WithFields(ttnlog.Fields{ + "Payload": m.Payload, + "FCnt": m.FCnt, + "FPort": m.FPort, + }).Info("Received Downlink") + case <-time.After(2 * time.Second): + ctx.Info("Did not receive downlink") } }, } func init() { RootCmd.AddCommand(uplinkCmd) - uplinkCmd.Flags().Bool("downlink", false, "Also start downlink (unstable)") - uplinkCmd.Flags().Bool("confirmed", false, "Use confirmed uplink (this also sets --downlink)") + uplinkCmd.Flags().Bool("confirmed", false, "Use confirmed uplink") uplinkCmd.Flags().Bool("ack", false, "Set ACK bit") uplinkCmd.Flags().String("gateway-id", "", "The ID of the gateway that you are faking (you can only fake gateways that you own)") diff --git a/ttnctl/util/router.go b/ttnctl/util/router.go index c38a07bab..dfd48c4b7 100644 --- a/ttnctl/util/router.go +++ b/ttnctl/util/router.go @@ -13,7 +13,7 @@ import ( ) // GetRouter starts a connection with the router -func GetRouter(ctx ttnlog.Interface) (*grpc.ClientConn, router.RouterClient) { +func GetRouter(ctx ttnlog.Interface) (*grpc.ClientConn, *router.Client) { ctx.Info("Discovering Router...") dscConn, client := GetDiscovery(ctx) defer dscConn.Close() @@ -27,7 +27,8 @@ func GetRouter(ctx ttnlog.Interface) (*grpc.ClientConn, router.RouterClient) { ctx.Info("Connecting with Router...") rtrConn, err := routerAnnouncement.Dial() ctx.Info("Connected to Router") - rtrClient := router.NewRouterClient(rtrConn) + rtrClient := router.NewClient(router.DefaultClientConfig) + rtrClient.AddServer(viper.GetString("router-id"), rtrConn) return rtrConn, rtrClient } From aa6780613d23195f6ebd702438200af148ebe3b1 Mon Sep 17 00:00:00 2001 From: Hylke Visser Date: Thu, 9 Mar 2017 17:06:38 +0100 Subject: [PATCH 04/45] Add Connection Pool for gRPC connections --- api/pool/pool.go | 97 +++++++++++++++++++++++++++++++++++++++ api/pool/pool_test.go | 103 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 200 insertions(+) create mode 100644 api/pool/pool.go create mode 100644 api/pool/pool_test.go diff --git a/api/pool/pool.go b/api/pool/pool.go new file mode 100644 index 000000000..62d7e1d12 --- /dev/null +++ b/api/pool/pool.go @@ -0,0 +1,97 @@ +// Copyright © 2017 The Things Network +// Use of this source code is governed by the MIT license that can be found in the LICENSE file. + +package pool + +import ( + "context" + "sync" + "sync/atomic" + + "github.com/TheThingsNetwork/go-utils/grpc/restartstream" + "google.golang.org/grpc" +) + +// Pool with connections +type Pool struct { + dialOptions []grpc.DialOption + bgCtx context.Context + + mu sync.Mutex + conns map[string]*conn +} + +type conn struct { + sync.WaitGroup + cancel context.CancelFunc + + users int32 + + conn *grpc.ClientConn + err error +} + +// DefaultDialOptions for connecting with servers +var DefaultDialOptions = []grpc.DialOption{ + grpc.WithStreamInterceptor(restartstream.Interceptor(restartstream.DefaultSettings)), +} + +// Global pool with connections +var Global = NewPool(DefaultDialOptions) + +// NewPool returns a new connection pool that uses the given DialOptions +func NewPool(dialOptions []grpc.DialOption) *Pool { + return &Pool{ + dialOptions: dialOptions, + conns: make(map[string]*conn), + } +} + +// Close connections. If target names supplied, considers the other users. Otherwise just closes all. +func (p *Pool) Close(target ...string) { + p.mu.Lock() + defer p.mu.Unlock() + if len(target) == 0 { + // This force-closes all connections + for _, c := range p.conns { + c.cancel() + c.conn.Close() + } + p.conns = make(map[string]*conn) + } + for _, target := range target { + if c, ok := p.conns[target]; ok { + new := atomic.AddInt32(&c.users, -1) + if new < 1 { + c.cancel() + c.conn.Close() + delete(p.conns, target) + } + } + } +} + +// Get a connection from the pool or create a new one +// This function is blocking if grpc.WithBlock() is used (default: yes) +func (p *Pool) Get(target string, opts ...grpc.DialOption) (*grpc.ClientConn, error) { + p.mu.Lock() + if _, ok := p.conns[target]; !ok { + c := new(conn) + c.Add(1) + p.conns[target] = c + go func() { + var ctx context.Context + ctx, c.cancel = context.WithCancel(context.Background()) + opts = append(p.dialOptions, opts...) + c.conn, c.err = grpc.DialContext(ctx, target, opts...) + c.Done() + }() + } + c := p.conns[target] + p.mu.Unlock() + + atomic.AddInt32(&c.users, 1) + + c.Wait() + return c.conn, c.err +} diff --git a/api/pool/pool_test.go b/api/pool/pool_test.go new file mode 100644 index 000000000..bc8f75506 --- /dev/null +++ b/api/pool/pool_test.go @@ -0,0 +1,103 @@ +// Copyright © 2017 The Things Network +// Use of this source code is governed by the MIT license that can be found in the LICENSE file. + +package pool + +import ( + "net" + "testing" + "time" + + "github.com/TheThingsNetwork/go-utils/log" + "github.com/TheThingsNetwork/ttn/api/health" + "github.com/htdvisser/grpc-testing/test" + . "github.com/smartystreets/assertions" + "google.golang.org/grpc" +) + +func TestPool(t *testing.T) { + a := New(t) + + testLogger := test.NewLogger() + log.Set(testLogger) + defer testLogger.Print(t) + + lis, err := net.Listen("tcp", "localhost:0") + if err != nil { + t.Fatalf("Failed to listen: %v", err) + } + s := grpc.NewServer() + health.RegisterServer(s) + go s.Serve(lis) + + addr := lis.Addr().String() + + pool := NewPool([]grpc.DialOption{grpc.WithBlock()}) + + conn1, err := pool.Get(addr, grpc.WithInsecure()) + a.So(err, ShouldBeNil) + a.So(conn1, ShouldNotBeNil) + + conn2, err := pool.Get(addr, grpc.WithInsecure()) + a.So(err, ShouldBeNil) + a.So(conn2, ShouldEqual, conn1) + + { + ok, err := health.Check(conn1) + a.So(err, ShouldBeNil) + a.So(ok, ShouldBeTrue) + } + + { + ok, err := health.Check(conn2) + a.So(err, ShouldBeNil) + a.So(ok, ShouldBeTrue) + } + + s.Stop() + + time.Sleep(200 * time.Millisecond) + + { + ok, err := health.Check(conn1) + a.So(err, ShouldNotBeNil) + a.So(ok, ShouldBeFalse) + } + + lis, err = net.Listen("tcp", addr) + if err != nil { + t.Fatalf("Failed to listen: %v", err) + } + s = grpc.NewServer() + health.RegisterServer(s) + go s.Serve(lis) + + time.Sleep(time.Second) + + { + ok, err := health.Check(conn1) + a.So(err, ShouldBeNil) + a.So(ok, ShouldBeTrue) + } + + pool.Close(addr) + pool.Close(addr) + + conn3, err := pool.Get(addr, grpc.WithInsecure()) + a.So(err, ShouldBeNil) + a.So(conn3, ShouldNotEqual, conn1) // the connection was closed, because there were no more users + + pool.Close() + + pool = NewPool([]grpc.DialOption{}) // Without the grpc.WithBlock() + + conn4, err := pool.Get(addr, grpc.WithInsecure()) + a.So(err, ShouldBeNil) + a.So(conn4, ShouldNotBeNil) + + { + ok, err := health.Check(conn4) + a.So(err, ShouldBeNil) + a.So(ok, ShouldBeTrue) + } +} From a7019e97cc8a90a17546122eacf6ed630b10b4c9 Mon Sep 17 00:00:00 2001 From: Hylke Visser Date: Thu, 9 Mar 2017 18:15:20 +0100 Subject: [PATCH 05/45] Add TCP KeepAlive dialer --- api/pool/pool.go | 8 ++++++++ api/pool/pool_test.go | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/api/pool/pool.go b/api/pool/pool.go index 62d7e1d12..b783e437a 100644 --- a/api/pool/pool.go +++ b/api/pool/pool.go @@ -5,8 +5,10 @@ package pool import ( "context" + "net" "sync" "sync/atomic" + "time" "github.com/TheThingsNetwork/go-utils/grpc/restartstream" "google.golang.org/grpc" @@ -31,9 +33,15 @@ type conn struct { err error } +// KeepAliveDialer is a dialer that adds a 10 second TCP KeepAlive +func KeepAliveDialer(addr string, timeout time.Duration) (net.Conn, error) { + return (&net.Dialer{Timeout: timeout, KeepAlive: 10 * time.Second}).Dial("tcp", addr) +} + // DefaultDialOptions for connecting with servers var DefaultDialOptions = []grpc.DialOption{ grpc.WithStreamInterceptor(restartstream.Interceptor(restartstream.DefaultSettings)), + grpc.WithDialer(KeepAliveDialer), } // Global pool with connections diff --git a/api/pool/pool_test.go b/api/pool/pool_test.go index bc8f75506..87530057c 100644 --- a/api/pool/pool_test.go +++ b/api/pool/pool_test.go @@ -91,7 +91,7 @@ func TestPool(t *testing.T) { pool = NewPool([]grpc.DialOption{}) // Without the grpc.WithBlock() - conn4, err := pool.Get(addr, grpc.WithInsecure()) + conn4, err := pool.Get(addr, append(DefaultDialOptions, grpc.WithInsecure())...) a.So(err, ShouldBeNil) a.So(conn4, ShouldNotBeNil) From 8dc0f192ec97c2811c5904087d1154fad154990d Mon Sep 17 00:00:00 2001 From: Hylke Visser Date: Thu, 9 Mar 2017 18:37:27 +0100 Subject: [PATCH 06/45] Export component Context --- core/component/auth.go | 7 ++++--- core/component/component.go | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/core/component/auth.go b/core/component/auth.go index ef9189cde..c092785ad 100644 --- a/core/component/auth.go +++ b/core/component/auth.go @@ -33,6 +33,7 @@ func (c *Component) InitAuth() error { c.initAuthServers, c.initKeyPair, c.initRoots, + c.initBgCtx, } if c.Config.UseTLS { inits = append(inits, c.initTLS) @@ -165,7 +166,7 @@ func (c *Component) initBgCtx() error { ctx = api.ContextWithID(ctx, c.Identity.Id) ctx = api.ContextWithServiceInfo(ctx, c.Identity.ServiceName, c.Identity.ServiceVersion, c.Identity.NetAddress) } - c.bgCtx = ctx + c.Context = ctx return nil } @@ -186,10 +187,10 @@ func (c *Component) BuildJWT() (string, error) { // GetContext returns a context for outgoing RPC request. If token is "", this function will generate a short lived token from the component func (c *Component) GetContext(token string) context.Context { - if c.bgCtx == nil { + if c.Context == nil { c.initBgCtx() } - ctx := c.bgCtx + ctx := c.Context if token == "" && c.Identity != nil { token, _ = c.BuildJWT() } diff --git a/core/component/component.go b/core/component/component.go index f8f09d174..dc91b8100 100644 --- a/core/component/component.go +++ b/core/component/component.go @@ -31,7 +31,7 @@ type Component struct { Discovery pb_discovery.Client Monitor *pb_monitor.Client Ctx ttnlog.Interface - bgCtx context.Context + Context context.Context AccessToken string privateKey *ecdsa.PrivateKey tlsConfig *tls.Config From c1470c9fe1ae4f02a92c995781f866ba5b03ba86 Mon Sep 17 00:00:00 2001 From: Hylke Visser Date: Thu, 9 Mar 2017 20:15:29 +0100 Subject: [PATCH 07/45] Make Discovery server use TLS --- .env/broker/ca.cert | 1 + .env/broker/server.cert | 17 +++++++++-------- .env/discovery/dev.yml | 1 + .env/discovery/server.cert | 11 +++++++++++ .env/gencerts.sh | 9 +++++++++ .env/handler/ca.cert | 1 + .env/handler/server.cert | 19 ++++++++++--------- .env/networkserver/server.cert | 19 ++++++++++--------- .env/router/ca.cert | 1 + .env/router/server.cert | 17 +++++++++-------- README.md | 1 + 11 files changed, 63 insertions(+), 34 deletions(-) create mode 120000 .env/broker/ca.cert create mode 100644 .env/discovery/server.cert create mode 100755 .env/gencerts.sh create mode 120000 .env/handler/ca.cert create mode 120000 .env/router/ca.cert diff --git a/.env/broker/ca.cert b/.env/broker/ca.cert new file mode 120000 index 000000000..7b18abd5b --- /dev/null +++ b/.env/broker/ca.cert @@ -0,0 +1 @@ +../discovery/server.cert \ No newline at end of file diff --git a/.env/broker/server.cert b/.env/broker/server.cert index b5fd01ef3..96c495b93 100644 --- a/.env/broker/server.cert +++ b/.env/broker/server.cert @@ -1,11 +1,12 @@ -----BEGIN CERTIFICATE----- -MIIBjjCCATSgAwIBAgIQFzR5quaK8FbcvukuVI94uDAKBggqhkjOPQQDAjAdMRsw -GQYDVQQKExJUaGUgVGhpbmdzIE5ldHdvcmswHhcNMTYwNzI5MTIxMTU2WhcNMTcw -NzI5MTIxMTU2WjAdMRswGQYDVQQKExJUaGUgVGhpbmdzIE5ldHdvcmswWTATBgcq +MIIBszCCAVmgAwIBAgIQdB+Af49pdScsT7qifQr/BzAKBggqhkjOPQQDAjAdMRsw +GQYDVQQKExJUaGUgVGhpbmdzIE5ldHdvcmswHhcNMTcwMzA5MTkwNzAyWhcNMTgw +MzA5MTkwNzAyWjAdMRswGQYDVQQKExJUaGUgVGhpbmdzIE5ldHdvcmswWTATBgcq hkjOPQIBBggqhkjOPQMBBwNCAARKJiC1vanAuqbOtVYPwSM8D6oSseAOeUra5dHi -Xtp72d59+kgaEfa6Zgp0KSaes+D2fxE+RJ4G6v7DYbKgWOGto1YwVDAOBgNVHQ8B -Af8EBAMCAqQwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDwYDVR0TAQH/BAUwAwEB/zAc -BgNVHREEFTATgglsb2NhbGhvc3SCBmJyb2tlcjAKBggqhkjOPQQDAgNIADBFAiAU -fiL3mhXOFbG9T3QVv2lH7H58pnhdrJmIBN1n6qvDXgIhAPgpd2ZCkJ04GHQyTuoU -v75tKrSlLiJw62QdY+s93uSh +Xtp72d59+kgaEfa6Zgp0KSaes+D2fxE+RJ4G6v7DYbKgWOGto3sweTAOBgNVHQ8B +Af8EBAMCAqQwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMA8GA1UdEwEB +/wQFMAMBAf8wNwYDVR0RBDAwLoIJbG9jYWxob3N0gglsb2NhbGhvc3SHBH8AAAGH +EAAAAAAAAAAAAAAAAAAAAAEwCgYIKoZIzj0EAwIDSAAwRQIhAO1tHZhOntbGCh1C +NUDGy2XbNF4XP2s/f5N3Rjz/hsvLAiA7WKRmVCu57WTcVEeDmmTHApglmmTmB73N +Bt+Gmd8yaQ== -----END CERTIFICATE----- diff --git a/.env/discovery/dev.yml b/.env/discovery/dev.yml index c13bac9b0..a4aaa0394 100644 --- a/.env/discovery/dev.yml +++ b/.env/discovery/dev.yml @@ -4,6 +4,7 @@ discovery-address: "localhost:1900" auth-servers: ttn-account-v2: "https://account.thethingsnetwork.org" local: "file://.env/discovery/server.pub" +tls: true key-dir: "./.env/discovery/" discovery: master-auth-servers: diff --git a/.env/discovery/server.cert b/.env/discovery/server.cert new file mode 100644 index 000000000..1c72e2ce3 --- /dev/null +++ b/.env/discovery/server.cert @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBqDCCAU6gAwIBAgIQaz96pn/63+3HrDdym3o44TAKBggqhkjOPQQDAjAdMRsw +GQYDVQQKExJUaGUgVGhpbmdzIE5ldHdvcmswHhcNMTcwMzA5MTkwNzAyWhcNMTgw +MzA5MTkwNzAyWjAdMRswGQYDVQQKExJUaGUgVGhpbmdzIE5ldHdvcmswWTATBgcq +hkjOPQIBBggqhkjOPQMBBwNCAAQZ3BU/el/lQZz4hJ2/NAb+oOPNtCJm5dV+WbIS +Q+RRIXHKo9G6mDMEhuwrFA4LyikZ1yftNxKoJR7LIJ3wkdR+o3AwbjAOBgNVHQ8B +Af8EBAMCAqQwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMA8GA1UdEwEB +/wQFMAMBAf8wLAYDVR0RBCUwI4IJbG9jYWxob3N0hwR/AAABhxAAAAAAAAAAAAAA +AAAAAAABMAoGCCqGSM49BAMCA0gAMEUCIQCAzEFCWNstqZUdxnmk+kXMZSFwg3v9 +L18WGBi7jj95DgIgY0QJWSdMU4BhZVGdCoW4Yq+awY2R1s9NC+nHsnL4tV0= +-----END CERTIFICATE----- diff --git a/.env/gencerts.sh b/.env/gencerts.sh new file mode 100755 index 000000000..cfe68eeb9 --- /dev/null +++ b/.env/gencerts.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +env=$(dirname $0) + +for service in discovery router broker networkserver handler +do + # ttn $service gen-keypair --key-dir "$env/$service" + ttn $service gen-cert --key-dir "$env/$service" "localhost" "127.0.0.1" "::1" +done diff --git a/.env/handler/ca.cert b/.env/handler/ca.cert new file mode 120000 index 000000000..7b18abd5b --- /dev/null +++ b/.env/handler/ca.cert @@ -0,0 +1 @@ +../discovery/server.cert \ No newline at end of file diff --git a/.env/handler/server.cert b/.env/handler/server.cert index b52c6d1e1..507536b32 100644 --- a/.env/handler/server.cert +++ b/.env/handler/server.cert @@ -1,11 +1,12 @@ -----BEGIN CERTIFICATE----- -MIIBkDCCATagAwIBAgIRANJJ3rP0cNrdrouE2GCkkaYwCgYIKoZIzj0EAwIwHTEb -MBkGA1UEChMSVGhlIFRoaW5ncyBOZXR3b3JrMB4XDTE2MDcyOTEyMTIwNloXDTE3 -MDcyOTEyMTIwNlowHTEbMBkGA1UEChMSVGhlIFRoaW5ncyBOZXR3b3JrMFkwEwYH -KoZIzj0CAQYIKoZIzj0DAQcDQgAEiXbWvyYjOMP4ebTYtVvdIsBwS+U3laWltR7V -ox4+kQWcGLLEg+suI9SRZyKK+frhw9JPKbVNIgEv/S50YKfMEaNXMFUwDgYDVR0P -AQH/BAQDAgKkMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA8GA1UdEwEB/wQFMAMBAf8w -HQYDVR0RBBYwFIIJbG9jYWxob3N0ggdoYW5kbGVyMAoGCCqGSM49BAMCA0gAMEUC -IQDbNJPUIfKZ/1CkTF3+ukl64l3fn613hnMiqAJYO7yz7QIgTAwlr3vkLquSQZUO -yraf7CGvuvulKs4S8sd8im6Bdgs= +MIIBszCCAVmgAwIBAgIQcrThDOgGdbi+Yz4obDgN5zAKBggqhkjOPQQDAjAdMRsw +GQYDVQQKExJUaGUgVGhpbmdzIE5ldHdvcmswHhcNMTcwMzA5MTkwNzAyWhcNMTgw +MzA5MTkwNzAyWjAdMRswGQYDVQQKExJUaGUgVGhpbmdzIE5ldHdvcmswWTATBgcq +hkjOPQIBBggqhkjOPQMBBwNCAASJdta/JiM4w/h5tNi1W90iwHBL5TeVpaW1HtWj +Hj6RBZwYssSD6y4j1JFnIor5+uHD0k8ptU0iAS/9LnRgp8wRo3sweTAOBgNVHQ8B +Af8EBAMCAqQwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMA8GA1UdEwEB +/wQFMAMBAf8wNwYDVR0RBDAwLoIJbG9jYWxob3N0gglsb2NhbGhvc3SHBH8AAAGH +EAAAAAAAAAAAAAAAAAAAAAEwCgYIKoZIzj0EAwIDSAAwRQIhAKyfSZf1tXd1/PeE +vwpltxjtTsHtHrTaOHcz/RENVdKYAiBZRmpzDQbbEStFKhNubYDfv/HIh74RPoUU +y5j6P1kTTg== -----END CERTIFICATE----- diff --git a/.env/networkserver/server.cert b/.env/networkserver/server.cert index b04453e85..9ee44dee9 100644 --- a/.env/networkserver/server.cert +++ b/.env/networkserver/server.cert @@ -1,11 +1,12 @@ -----BEGIN CERTIFICATE----- -MIIBljCCATygAwIBAgIRAOs3B7qZNgejMf+laNHTRuYwCgYIKoZIzj0EAwIwHTEb -MBkGA1UEChMSVGhlIFRoaW5ncyBOZXR3b3JrMB4XDTE2MDcyOTEyMTIxNVoXDTE3 -MDcyOTEyMTIxNVowHTEbMBkGA1UEChMSVGhlIFRoaW5ncyBOZXR3b3JrMFkwEwYH -KoZIzj0CAQYIKoZIzj0DAQcDQgAEsfIDb4Va9ocbwGuc375Bxw5ICTCXZ60mbgdx -3JSyWm19DW5dihzPFrB0Ezu+lak91rTEaon9WNcVibhFNG5wMqNdMFswDgYDVR0P -AQH/BAQDAgKkMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA8GA1UdEwEB/wQFMAMBAf8w -IwYDVR0RBBwwGoIJbG9jYWxob3N0gg1uZXR3b3Jrc2VydmVyMAoGCCqGSM49BAMC -A0gAMEUCIBrRl5a2PX+fn68Uefq15Cn1C1XE6NGVmI+HvmP1sA1JAiEA0L4WgKdo -HcUc8PnKlUUgN9nLVx98W9Sb2TvOaldspVE= +MIIBszCCAVmgAwIBAgIQDU92ZmhSjs5+l/Hh53oA0DAKBggqhkjOPQQDAjAdMRsw +GQYDVQQKExJUaGUgVGhpbmdzIE5ldHdvcmswHhcNMTcwMzA5MTkwNzAyWhcNMTgw +MzA5MTkwNzAyWjAdMRswGQYDVQQKExJUaGUgVGhpbmdzIE5ldHdvcmswWTATBgcq +hkjOPQIBBggqhkjOPQMBBwNCAASx8gNvhVr2hxvAa5zfvkHHDkgJMJdnrSZuB3Hc +lLJabX0Nbl2KHM8WsHQTO76VqT3WtMRqif1Y1xWJuEU0bnAyo3sweTAOBgNVHQ8B +Af8EBAMCAqQwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMA8GA1UdEwEB +/wQFMAMBAf8wNwYDVR0RBDAwLoIJbG9jYWxob3N0gglsb2NhbGhvc3SHBH8AAAGH +EAAAAAAAAAAAAAAAAAAAAAEwCgYIKoZIzj0EAwIDSAAwRQIhAKl5ksLQouswffPg +uW1TjDcBNAv0icKXc5uGThz6lv9LAiAJAcpAzY1FUqNGYIqF+3W7AnzWjiF7SVfM +NQthfc/8LQ== -----END CERTIFICATE----- diff --git a/.env/router/ca.cert b/.env/router/ca.cert new file mode 120000 index 000000000..7b18abd5b --- /dev/null +++ b/.env/router/ca.cert @@ -0,0 +1 @@ +../discovery/server.cert \ No newline at end of file diff --git a/.env/router/server.cert b/.env/router/server.cert index fdc967e17..32cb7f7c5 100644 --- a/.env/router/server.cert +++ b/.env/router/server.cert @@ -1,11 +1,12 @@ -----BEGIN CERTIFICATE----- -MIIBjjCCATSgAwIBAgIQS/57LijDDb+PB9+WltoR1DAKBggqhkjOPQQDAjAdMRsw -GQYDVQQKExJUaGUgVGhpbmdzIE5ldHdvcmswHhcNMTYwNzI5MTIxMTQ2WhcNMTcw -NzI5MTIxMTQ2WjAdMRswGQYDVQQKExJUaGUgVGhpbmdzIE5ldHdvcmswWTATBgcq +MIIBszCCAVmgAwIBAgIQDqPxNkrxxJN7VEA7IwQVWzAKBggqhkjOPQQDAjAdMRsw +GQYDVQQKExJUaGUgVGhpbmdzIE5ldHdvcmswHhcNMTcwMzA5MTkwNzAyWhcNMTgw +MzA5MTkwNzAyWjAdMRswGQYDVQQKExJUaGUgVGhpbmdzIE5ldHdvcmswWTATBgcq hkjOPQIBBggqhkjOPQMBBwNCAAQrcb9XbpbPrXWn8Qh8kRNxzt+Y3BpxyVgRkeST -30VcppXAv83B64oqklFFTr9BmOSsSXY1iKxcDUV+25TEkuCro1YwVDAOBgNVHQ8B -Af8EBAMCAqQwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDwYDVR0TAQH/BAUwAwEB/zAc -BgNVHREEFTATgglsb2NhbGhvc3SCBnJvdXRlcjAKBggqhkjOPQQDAgNIADBFAiEA -sI4vft9oNO2iT5The9qOzgnM5UxIc/XPrQhpKMgELTwCIFn9pkIsZ0jeeb99uBdS -4MhSRxk4jgkBaWDPjCznaHVm +30VcppXAv83B64oqklFFTr9BmOSsSXY1iKxcDUV+25TEkuCro3sweTAOBgNVHQ8B +Af8EBAMCAqQwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMA8GA1UdEwEB +/wQFMAMBAf8wNwYDVR0RBDAwLoIJbG9jYWxob3N0gglsb2NhbGhvc3SHBH8AAAGH +EAAAAAAAAAAAAAAAAAAAAAEwCgYIKoZIzj0EAwIDSAAwRQIgQYkT8K9tgFCgNinA +FKsH1wD3SnISi59MHHwciOVvl5wCIQDlWvtf9tGDZjdfWIBLZkQDz+a15WOD0yAE +1jNJNybDAQ== -----END CERTIFICATE----- diff --git a/README.md b/README.md index c7524a524..013bcc819 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@ When you get started with The Things Network, you'll probably have some question 7. Run `make dev` to install the go binaries into `$GOPATH/bin/` * Optionally on Linux or Mac you can use `make link` to link them to `$GOPATH/bin/` (In order to run the commands, you should have `export PATH="$GOPATH/bin:$PATH"` in your profile). 8. Configure your `ttnctl` with the settings in `.env/ttnctl.yml.dev-example` by copying that file to `~/.ttnctl.yml`. +9. Trust the CA certificate of your local discovery server by copying `.env/discovery/server.cert` to `~/.ttnctl/ca.cert`. You can check your `ttnctl` configuration by running `ttnctl config`. It should look like this: From 76aa92ea46dd9f78bf1dcd05b1dc7764727c694a Mon Sep 17 00:00:00 2001 From: Hylke Visser Date: Fri, 10 Mar 2017 07:08:29 +0100 Subject: [PATCH 08/45] Refactor Dial behaviour --- api/dial.go | 115 +++++++-------------------------- api/discovery/dial.go | 41 ++++++++++-- api/discovery/dial_test.go | 129 +++++++++++++++++++++++++++++++++++++ api/health/server.go | 17 +++++ api/pool/pool.go | 23 +++++-- api/pool/pool_test.go | 8 +-- cmd/root.go | 8 ++- ttnctl/cmd/root.go | 24 +++---- 8 files changed, 241 insertions(+), 124 deletions(-) create mode 100644 api/discovery/dial_test.go create mode 100644 api/health/server.go diff --git a/api/dial.go b/api/dial.go index 8641030ed..056f7a555 100644 --- a/api/dial.go +++ b/api/dial.go @@ -6,70 +6,14 @@ package api import ( "crypto/tls" "crypto/x509" - "fmt" - "net" - "strings" - "time" "github.com/TheThingsNetwork/go-utils/log" "github.com/TheThingsNetwork/go-utils/roots" - "github.com/TheThingsNetwork/ttn/utils/backoff" + "github.com/TheThingsNetwork/ttn/api/pool" "google.golang.org/grpc" "google.golang.org/grpc/credentials" ) -// KeepAlive indicates the keep-alive time for the Dialer -var KeepAlive = 10 * time.Second - -// MaxRetries indicates how often clients should retry dialing a component -var MaxRetries = 100 - -// DialOptions to use in TTN gRPC -var DialOptions = []grpc.DialOption{ - WithTTNDialer(), - grpc.WithBlock(), - grpc.FailOnNonTempDialError(true), -} - -func dial(address string, tlsConfig *tls.Config, fallback bool) (conn *grpc.ClientConn, err error) { - ctx := log.Get().WithField("Address", address) - opts := DialOptions - if tlsConfig != nil { - tlsConfig.ServerName = strings.SplitN(address, ":", 2)[0] // trim the port - opts = append(opts, grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig))) - } else { - opts = append(opts, grpc.WithInsecure()) - } - conn, err = grpc.Dial( - address, - opts..., - ) - if err == nil { - return - } - - switch err := err.(type) { - case x509.CertificateInvalidError, - x509.ConstraintViolationError, - x509.HostnameError, - x509.InsecureAlgorithmError, - x509.SystemRootsError, - x509.UnhandledCriticalExtension, - x509.UnknownAuthorityError: - // Non-temporary error while connecting to a TLS-enabled server - return nil, err - case tls.RecordHeaderError: - if fallback { - ctx.WithError(err).Warn("Could not connect to gRPC server with TLS, reconnecting without it...") - return dial(address, nil, fallback) - } - return nil, err - } - - log.Get().WithField("ErrType", fmt.Sprintf("%T", err)).WithError(err).Error("Unhandled dial error [please create issue on Github]") - return nil, err -} - // RootCAs to use in API connections var RootCAs *x509.CertPool @@ -81,42 +25,33 @@ func init() { } } -// Dial an address -func Dial(address string) (*grpc.ClientConn, error) { - tlsConfig := &tls.Config{RootCAs: RootCAs} - return dial(address, tlsConfig, true) +// AllowInsecureFallback can be set to true if you need to connect with a server that does not use TLS +var AllowInsecureFallback = false + +// TLSConfig to use when connecting to servers +var TLSConfig *tls.Config + +// Dial an address with default TLS config +func Dial(target string) (*grpc.ClientConn, error) { + conn, err := pool.Global.Dial(target, grpc.WithBlock(), grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(RootCAs, ""))) + if err == nil { + return conn, nil + } + pool.Global.Close(target) + if _, ok := err.(tls.RecordHeaderError); ok && AllowInsecureFallback { + log.Get().Warn("Could not connect to gRPC server with TLS, will reconnect without TLS") + log.Get().Warnf("This is a security risk, you should enable TLS on %s", target) + conn, err = pool.Global.Dial(target, grpc.WithBlock(), grpc.WithInsecure()) + } + return conn, err } -// DialWithCert dials the address using the given TLS cert -func DialWithCert(address string, cert string) (*grpc.ClientConn, error) { - rootCAs := x509.NewCertPool() - ok := rootCAs.AppendCertsFromPEM([]byte(cert)) +// DialWithCert dials the target using the given TLS cert +func DialWithCert(target string, cert string) (*grpc.ClientConn, error) { + certPool := x509.NewCertPool() + ok := certPool.AppendCertsFromPEM([]byte(cert)) if !ok { panic("failed to parse root certificate") } - tlsConfig := &tls.Config{RootCAs: rootCAs} - return dial(address, tlsConfig, false) -} - -// WithTTNDialer creates a dialer for TTN -func WithTTNDialer() grpc.DialOption { - return grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) { - ctx := log.Get().WithField("Address", addr) - d := net.Dialer{Timeout: timeout, KeepAlive: KeepAlive} - var retries int - for { - conn, err := d.Dial("tcp", addr) - if err == nil { - ctx.Debug("Connected to gRPC server") - return conn, nil - } - if err, ok := err.(*net.OpError); ok && err.Op == "dial" && retries <= MaxRetries { - ctx.WithError(err).Debug("Could not connect to gRPC server, reconnecting...") - time.Sleep(backoff.Backoff(retries)) - retries++ - continue - } - return nil, err - } - }) + return pool.Global.Dial(target, grpc.WithBlock(), grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(certPool, ""))) } diff --git a/api/discovery/dial.go b/api/discovery/dial.go index 5a3f5784c..acfe51da7 100644 --- a/api/discovery/dial.go +++ b/api/discovery/dial.go @@ -4,20 +4,49 @@ package discovery import ( + "crypto/tls" + "crypto/x509" "errors" + "net" "strings" - "github.com/TheThingsNetwork/ttn/api" + "github.com/TheThingsNetwork/ttn/api/pool" "google.golang.org/grpc" + "google.golang.org/grpc/credentials" ) -// Dial dials the component represented by this Announcement -func (a *Announcement) Dial() (*grpc.ClientConn, error) { +// TLSConfig for securely connecting with this component +func (a *Announcement) TLSConfig() (*tls.Config, error) { if a.NetAddress == "" { - return nil, errors.New("Can not dial this component") + return nil, errors.New("No address known for this component") } + netAddress := strings.Split(a.NetAddress, ",")[0] + netHost, _, _ := net.SplitHostPort(netAddress) if a.Certificate == "" { - return api.Dial(strings.Split(a.NetAddress, ",")[0]) + return nil, nil } - return api.DialWithCert(strings.Split(a.NetAddress, ",")[0], a.Certificate) + rootCAs := x509.NewCertPool() + ok := rootCAs.AppendCertsFromPEM([]byte(a.Certificate)) + if !ok { + return nil, errors.New("Could not read component certificate") + } + return &tls.Config{ServerName: netHost, RootCAs: rootCAs}, nil +} + +// WithSecure returns a gRPC DialOption with TLS +func (a *Announcement) WithSecure() grpc.DialOption { + tlsConfig, _ := a.TLSConfig() + return grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)) +} + +// Dial the component represented by this Announcement. We use the global connection pool here +func (a *Announcement) Dial(opts ...grpc.DialOption) (*grpc.ClientConn, error) { + if a.NetAddress == "" { + return nil, errors.New("No address known for this component") + } + netAddress := strings.Split(a.NetAddress, ",")[0] + if a.Certificate == "" { + return pool.Global.Dial(netAddress, append(opts, grpc.WithInsecure())...) + } + return pool.Global.Dial(netAddress, append(opts, a.WithSecure())...) } diff --git a/api/discovery/dial_test.go b/api/discovery/dial_test.go new file mode 100644 index 000000000..6cf4d3b31 --- /dev/null +++ b/api/discovery/dial_test.go @@ -0,0 +1,129 @@ +// Copyright © 2017 The Things Network +// Use of this source code is governed by the MIT license that can be found in the LICENSE file. + +package discovery + +import ( + "crypto/tls" + "net" + "path" + "testing" + + "os" + + "io/ioutil" + + "github.com/TheThingsNetwork/go-utils/log" + "github.com/TheThingsNetwork/ttn/api/health" + "github.com/TheThingsNetwork/ttn/utils/security" + "github.com/htdvisser/grpc-testing/test" + . "github.com/smartystreets/assertions" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" +) + +func TestTLSConfig(t *testing.T) { + a := New(t) + svc := new(Announcement) + + { + _, err := svc.TLSConfig() + a.So(err, ShouldNotBeNil) + } + + svc.NetAddress = "localhost:0" + + { + tlsConfig, err := svc.TLSConfig() + a.So(err, ShouldBeNil) + a.So(tlsConfig, ShouldBeNil) + } + + svc.Certificate = "invalid" + + { + _, err := svc.TLSConfig() + a.So(err, ShouldNotBeNil) + } +} + +func TestDialInsecure(t *testing.T) { + a := New(t) + + testLogger := test.NewLogger() + log.Set(testLogger) + defer testLogger.Print(t) + + lis, err := net.Listen("tcp", "localhost:0") + if err != nil { + t.Fatalf("Failed to listen: %v", err) + } + s := grpc.NewServer() + health.RegisterServer(s) + go s.Serve(lis) + + svc := new(Announcement) + + _, err = svc.Dial() + a.So(err, ShouldNotBeNil) + + svc.NetAddress = lis.Addr().String() + + conn, err := svc.Dial() + a.So(err, ShouldBeNil) + + { + ok, err := health.Check(conn) + a.So(err, ShouldBeNil) + a.So(ok, ShouldBeTrue) + } + +} + +func TestDialSecure(t *testing.T) { + a := New(t) + + tmp := os.TempDir() + security.GenerateKeypair(tmp) + security.GenerateCert(tmp, "localhost", "127.0.0.1", "::1") + + cert, _ := ioutil.ReadFile(path.Join(tmp, "server.cert")) + key, _ := ioutil.ReadFile(path.Join(tmp, "server.key")) + + cer, err := tls.X509KeyPair(cert, key) + if err != nil { + panic(err) + } + + tlsConfig := &tls.Config{Certificates: []tls.Certificate{cer}} + + testLogger := test.NewLogger() + log.Set(testLogger) + defer testLogger.Print(t) + + lis, err := net.Listen("tcp", "localhost:0") + if err != nil { + t.Fatalf("Failed to listen: %v", err) + } + s := grpc.NewServer(grpc.Creds(credentials.NewTLS(tlsConfig))) + health.RegisterServer(s) + go s.Serve(lis) + + svc := new(Announcement) + + _, err = svc.Dial() + a.So(err, ShouldNotBeNil) + + svc.NetAddress = lis.Addr().String() + svc.Certificate = string(cert) + + conn, err := svc.Dial() + a.So(err, ShouldBeNil) + + { + ok, err := health.Check(conn) + a.So(err, ShouldBeNil) + a.So(ok, ShouldBeTrue) + } + +} diff --git a/api/health/server.go b/api/health/server.go new file mode 100644 index 000000000..b7e7e98f3 --- /dev/null +++ b/api/health/server.go @@ -0,0 +1,17 @@ +// Copyright © 2017 The Things Network +// Use of this source code is governed by the MIT license that can be found in the LICENSE file. + +package health + +import ( + "google.golang.org/grpc" + "google.golang.org/grpc/health" + healthpb "google.golang.org/grpc/health/grpc_health_v1" +) + +// RegisterServer registers and returns a new Health server +func RegisterServer(s *grpc.Server) *health.Server { + srv := health.NewServer() + healthpb.RegisterHealthServer(s, srv) + return srv +} diff --git a/api/pool/pool.go b/api/pool/pool.go index b783e437a..23e60fba8 100644 --- a/api/pool/pool.go +++ b/api/pool/pool.go @@ -63,7 +63,9 @@ func (p *Pool) Close(target ...string) { // This force-closes all connections for _, c := range p.conns { c.cancel() - c.conn.Close() + if c.conn != nil { + c.conn.Close() + } } p.conns = make(map[string]*conn) } @@ -72,24 +74,25 @@ func (p *Pool) Close(target ...string) { new := atomic.AddInt32(&c.users, -1) if new < 1 { c.cancel() - c.conn.Close() + if c.conn != nil { + c.conn.Close() + } delete(p.conns, target) } } } } -// Get a connection from the pool or create a new one -// This function is blocking if grpc.WithBlock() is used (default: yes) -func (p *Pool) Get(target string, opts ...grpc.DialOption) (*grpc.ClientConn, error) { +// DialContext gets a connection from the pool or creates a new one +// This function is blocking if grpc.WithBlock() is used +func (p *Pool) DialContext(ctx context.Context, target string, opts ...grpc.DialOption) (*grpc.ClientConn, error) { p.mu.Lock() if _, ok := p.conns[target]; !ok { c := new(conn) c.Add(1) p.conns[target] = c go func() { - var ctx context.Context - ctx, c.cancel = context.WithCancel(context.Background()) + ctx, c.cancel = context.WithCancel(ctx) opts = append(p.dialOptions, opts...) c.conn, c.err = grpc.DialContext(ctx, target, opts...) c.Done() @@ -103,3 +106,9 @@ func (p *Pool) Get(target string, opts ...grpc.DialOption) (*grpc.ClientConn, er c.Wait() return c.conn, c.err } + +// Dial gets a connection from the pool or creates a new one +// This function is blocking if grpc.WithBlock() is used +func (p *Pool) Dial(target string, opts ...grpc.DialOption) (*grpc.ClientConn, error) { + return p.DialContext(context.Background(), target, opts...) +} diff --git a/api/pool/pool_test.go b/api/pool/pool_test.go index 87530057c..7859385cb 100644 --- a/api/pool/pool_test.go +++ b/api/pool/pool_test.go @@ -34,11 +34,11 @@ func TestPool(t *testing.T) { pool := NewPool([]grpc.DialOption{grpc.WithBlock()}) - conn1, err := pool.Get(addr, grpc.WithInsecure()) + conn1, err := pool.Dial(addr, grpc.WithInsecure()) a.So(err, ShouldBeNil) a.So(conn1, ShouldNotBeNil) - conn2, err := pool.Get(addr, grpc.WithInsecure()) + conn2, err := pool.Dial(addr, grpc.WithInsecure()) a.So(err, ShouldBeNil) a.So(conn2, ShouldEqual, conn1) @@ -83,7 +83,7 @@ func TestPool(t *testing.T) { pool.Close(addr) pool.Close(addr) - conn3, err := pool.Get(addr, grpc.WithInsecure()) + conn3, err := pool.Dial(addr, grpc.WithInsecure()) a.So(err, ShouldBeNil) a.So(conn3, ShouldNotEqual, conn1) // the connection was closed, because there were no more users @@ -91,7 +91,7 @@ func TestPool(t *testing.T) { pool = NewPool([]grpc.DialOption{}) // Without the grpc.WithBlock() - conn4, err := pool.Get(addr, append(DefaultDialOptions, grpc.WithInsecure())...) + conn4, err := pool.Dial(addr, append(DefaultDialOptions, grpc.WithInsecure())...) a.So(err, ShouldBeNil) a.So(conn4, ShouldNotBeNil) diff --git a/cmd/root.go b/cmd/root.go index 2705e5e0c..d780ebd6e 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -17,6 +17,7 @@ import ( ttnlog "github.com/TheThingsNetwork/go-utils/log" "github.com/TheThingsNetwork/go-utils/log/apex" "github.com/TheThingsNetwork/go-utils/log/grpc" + "github.com/TheThingsNetwork/ttn/api" esHandler "github.com/TheThingsNetwork/ttn/utils/elasticsearch/handler" "github.com/apex/log" jsonHandler "github.com/apex/log/handlers/json" @@ -86,6 +87,10 @@ var RootCmd = &cobra.Command{ ttnlog.Set(ctx) grpclog.SetLogger(grpc.Wrap(ttnlog.Get())) + if viper.GetBool("allow-insecure") { + api.AllowInsecureFallback = true + } + ctx.WithFields(ttnlog.Fields{ "ComponentID": viper.GetString("id"), "Description": viper.GetString("description"), @@ -151,7 +156,8 @@ func init() { } } - RootCmd.PersistentFlags().Bool("tls", false, "Use TLS") + RootCmd.PersistentFlags().Bool("tls", true, "Use TLS") + RootCmd.PersistentFlags().Bool("allow-insecure", false, "Allow insecure fallback if TLS unavailable") RootCmd.PersistentFlags().String("key-dir", path.Clean(dir+"/.ttn/"), "The directory where public/private keys are stored") viper.BindPFlags(RootCmd.PersistentFlags()) diff --git a/ttnctl/cmd/root.go b/ttnctl/cmd/root.go index 961515645..8c15143fb 100644 --- a/ttnctl/cmd/root.go +++ b/ttnctl/cmd/root.go @@ -12,6 +12,7 @@ import ( ttnlog "github.com/TheThingsNetwork/go-utils/log" "github.com/TheThingsNetwork/go-utils/log/apex" "github.com/TheThingsNetwork/go-utils/log/grpc" + "github.com/TheThingsNetwork/ttn/api" "github.com/TheThingsNetwork/ttn/ttnctl/util" "github.com/apex/log" "github.com/spf13/cobra" @@ -47,6 +48,10 @@ var RootCmd = &cobra.Command{ ttnlog.Set(ctx) grpclog.SetLogger(grpc.Wrap(ttnlog.Get())) + + if viper.GetBool("allow-insecure") { + api.AllowInsecureFallback = true + } }, } @@ -63,33 +68,20 @@ func init() { cobra.OnInitialize(initConfig) RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.ttnctl.yml)") - RootCmd.PersistentFlags().StringVar(&dataDir, "data", "", "directory where ttnctl stores data (default is $HOME/.ttnctl)") - viper.BindPFlag("data", RootCmd.PersistentFlags().Lookup("data")) - RootCmd.PersistentFlags().String("discovery-address", "discover.thethingsnetwork.org:1900", "The address of the Discovery server") - viper.BindPFlag("discovery-address", RootCmd.PersistentFlags().Lookup("discovery-address")) - RootCmd.PersistentFlags().String("router-id", "ttn-router-eu", "The ID of the TTN Router as announced in the Discovery server") - viper.BindPFlag("router-id", RootCmd.PersistentFlags().Lookup("router-id")) - RootCmd.PersistentFlags().String("handler-id", "ttn-handler-eu", "The ID of the TTN Handler as announced in the Discovery server") - viper.BindPFlag("handler-id", RootCmd.PersistentFlags().Lookup("handler-id")) - RootCmd.PersistentFlags().String("mqtt-address", "eu.thethings.network:1883", "The address of the MQTT broker") - viper.BindPFlag("mqtt-address", RootCmd.PersistentFlags().Lookup("mqtt-address")) - RootCmd.PersistentFlags().String("mqtt-username", "", "The username for the MQTT broker") - viper.BindPFlag("mqtt-username", RootCmd.PersistentFlags().Lookup("mqtt-username")) - RootCmd.PersistentFlags().String("mqtt-password", "", "The password for the MQTT broker") - viper.BindPFlag("mqtt-password", RootCmd.PersistentFlags().Lookup("mqtt-password")) - RootCmd.PersistentFlags().String("auth-server", "https://account.thethingsnetwork.org", "The address of the OAuth 2.0 server") - viper.BindPFlag("auth-server", RootCmd.PersistentFlags().Lookup("auth-server")) + RootCmd.PersistentFlags().Bool("allow-insecure", false, "Allow insecure fallback if TLS unavailable") viper.SetDefault("gateway-id", "dev") viper.SetDefault("gateway-token", "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ0dG4tYWNjb3VudC12MiIsInN1YiI6ImRldiIsInR5cGUiOiJnYXRld2F5IiwiaWF0IjoxNDgyNDIxMTEyfQ.obhobeREK9bOpi-YO5lZ8rpW4CkXZUSrRBRIjbFThhvAsj_IjkFmCovIVLsGlaDVEKciZmXmWnY-6ZEgUEu6H6_GG4AD6HNHXnT0o0XSPgf5_Bc6dpzuI5FCEpcELihpBMaW3vPUt29NecLo4LvZGAuOllUYKHsZi34GYnR6PFlOgi40drN_iU_8aMCxFxm6ki83QlcyHEmDAh5GAGIym0qnUDh5_L1VE_upmoR72j8_l5lSuUA2_w8CH5_Z9CrXlTKQ2XQXsQXprkhbmOKKC8rfbTjRsB_nxObu0qcTWLH9tMd4KGFkJ20mdMw38fg2Vt7eLrkU1R1kl6a65eo6LZi0JvRSsboVZFWLwI02Azkwsm903K5n1r25Wq2oiwPJpNq5vsYLdYlb-WdAPsEDnfQGLPaqxd5we8tDcHsF4C1JHTwLsKy2Sqj8WNVmLgXiFER0DNfISDgS5SYdOxd9dUf5lTlIYdJU6aG1yYLSEhq80QOcdhCqNMVu1uRIucn_BhHbKo_LCMmD7TGppaXcQ2tCL3qHQaW8GCoun_UPo4C67LIMYUMfwd_h6CaykzlZvDlLa64ZiQ3XPmMcT_gVT7MJS2jGPbtJmcLHAVa5NZLv2d6WZfutPAocl3bYrY-sQmaSwJrzakIb2D-DNsg0qBJAZcm2o021By8U4bKAAFQ") + + viper.BindPFlags(RootCmd.PersistentFlags()) } func assertArgsLength(cmd *cobra.Command, args []string, min, max int) { From e65e21ed8cedd0239d985fdc03e4afd4f9ef3ab7 Mon Sep 17 00:00:00 2001 From: Hylke Visser Date: Fri, 10 Mar 2017 07:12:14 +0100 Subject: [PATCH 09/45] Refactor Broker stream client --- api/broker/broker.go | 398 +++++++++++++++++++++++++++++ api/broker/broker_test.go | 156 ++++++++++++ api/broker/client_streams.go | 412 ------------------------------- api/broker/communication_test.go | 222 ----------------- api/broker/reference_server.go | 293 ++++++++++++++++++++++ core/handler/handler.go | 35 +-- core/router/router.go | 42 ++-- 7 files changed, 892 insertions(+), 666 deletions(-) create mode 100644 api/broker/broker.go create mode 100644 api/broker/broker_test.go delete mode 100644 api/broker/client_streams.go delete mode 100644 api/broker/communication_test.go create mode 100644 api/broker/reference_server.go diff --git a/api/broker/broker.go b/api/broker/broker.go new file mode 100644 index 000000000..1a1ca01fc --- /dev/null +++ b/api/broker/broker.go @@ -0,0 +1,398 @@ +// Copyright © 2017 The Things Network +// Use of this source code is governed by the MIT license that can be found in the LICENSE file. + +package broker + +import ( + "context" + "io" + "sync" + + "github.com/TheThingsNetwork/go-utils/grpc/restartstream" + "github.com/TheThingsNetwork/go-utils/log" + "github.com/TheThingsNetwork/ttn/api" + "github.com/golang/protobuf/ptypes/empty" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" +) + +// RouterStream is used for sending uplink and receiving downlink. +type RouterStream interface { + Uplink(*UplinkMessage) + Downlink() <-chan *DownlinkMessage + Close() +} + +// HandlerStream is used for sending uplink and receiving downlink. +type HandlerStream interface { + Uplink() <-chan *DeduplicatedUplinkMessage + Downlink(*DownlinkMessage) + Close() +} + +// ClientConfig for broker Client +type ClientConfig struct { + BackgroundContext context.Context + BufferSize int +} + +// DefaultClientConfig for broker Client +var DefaultClientConfig = ClientConfig{ + BackgroundContext: context.Background(), + BufferSize: 10, +} + +// NewClient creates a new Client with the given configuration +func NewClient(config ClientConfig) *Client { + ctx, cancel := context.WithCancel(config.BackgroundContext) + + return &Client{ + log: log.Get(), + ctx: ctx, + cancel: cancel, + + config: config, + } +} + +// Client for broker +type Client struct { + log log.Interface + ctx context.Context + cancel context.CancelFunc + + config ClientConfig + serverConns []*serverConn +} + +// AddServer adds a broker server +func (c *Client) AddServer(name string, conn *grpc.ClientConn) { + log := c.log.WithField("Broker", name) + log.Info("Adding Broker server") + s := &serverConn{ + ctx: log, + name: name, + conn: conn, + } + c.serverConns = append(c.serverConns, s) +} + +// Close the client and all its connections +func (c *Client) Close() { + c.cancel() + for _, server := range c.serverConns { + server.Close() + } +} + +func logStreamErr(log log.Interface, streamName string, err error) { + switch { + case err == nil: + log.Debugf("%s stream closed", streamName) + case err == io.EOF: + log.WithError(err).Debugf("%s stream ended", streamName) + case err == context.Canceled || grpc.Code(err) == codes.Canceled: + log.WithError(err).Debugf("%s stream canceled", streamName) + case err == context.DeadlineExceeded || grpc.Code(err) == codes.DeadlineExceeded: + log.WithError(err).Debugf("%s stream deadline exceeded", streamName) + case grpc.ErrorDesc(err) == grpc.ErrClientConnClosing.Error(): + log.WithError(err).Debugf("%s stream connection closed", streamName) + default: + log.WithError(err).Warnf("%s stream closed unexpectedly", streamName) + } +} + +type serverConn struct { + ctx log.Interface + name string + + ready chan struct{} + conn *grpc.ClientConn +} + +func (c *serverConn) Close() { + if c.ready != nil { + <-c.ready + } + if c.conn != nil { + c.conn.Close() + } +} + +type routerStreams struct { + log log.Interface + ctx context.Context + cancel context.CancelFunc + + mu sync.RWMutex + uplink map[string]chan *UplinkMessage + downlink chan *DownlinkMessage +} + +func (s *routerStreams) Uplink(msg *UplinkMessage) { + s.mu.RLock() + defer s.mu.RUnlock() + s.log.Debug("Sending UplinkMessage to broker") + for serverName, ch := range s.uplink { + select { + case ch <- msg: + default: + s.log.WithField("Broker", serverName).Warn("UplinkMessage buffer full") + } + } +} + +func (s *routerStreams) Downlink() <-chan *DownlinkMessage { + s.mu.RLock() + defer s.mu.RUnlock() + return s.downlink +} + +func (s *routerStreams) Close() { + s.cancel() +} + +// NewRouterStreams returns new streams using the given router ID and token +func (c *Client) NewRouterStreams(id string, token string) RouterStream { + log := c.log + ctx, cancel := context.WithCancel(c.ctx) + ctx = api.ContextWithID(ctx, id) + ctx = api.ContextWithToken(ctx, token) + s := &routerStreams{ + log: log, + ctx: ctx, + cancel: cancel, + + uplink: make(map[string]chan *UplinkMessage), + downlink: make(chan *DownlinkMessage), + } + + var wgDown sync.WaitGroup + go func() { + wgDown.Wait() + close(s.downlink) + }() + + // Hook up the broker servers + for _, server := range c.serverConns { + wgDown.Add(1) + go func(server *serverConn) { + if server.ready != nil { + select { + case <-ctx.Done(): + return + case <-server.ready: + } + } + if server.conn == nil { + return + } + log := log.WithField("Broker", server.name) + cli := NewBrokerClient(server.conn) + + // Stream channels + chUplink := make(chan *UplinkMessage, c.config.BufferSize) + + defer func() { + s.mu.Lock() + defer s.mu.Unlock() + delete(s.uplink, server.name) + close(chUplink) + }() + + // Associate stream + associate, err := cli.Associate(ctx) + if err != nil { + log.WithError(err).Warn("Could not set up Associate stream") + wgDown.Done() + } else { + s.mu.Lock() + s.uplink[server.name] = chUplink + s.mu.Unlock() + + // Downlink + go func() { + defer func() { + wgDown.Done() + }() + for { + msg, err := associate.Recv() + if err != nil { + logStreamErr(log, "Associate", err) + return + } + select { + case s.downlink <- msg: + default: + log.Warn("Downlink buffer full") + } + } + }() + } + + log.Debug("Start handling Associate stream") + defer log.Debug("Done handling Associate stream") + for { + select { + case <-ctx.Done(): + return + case msg := <-chUplink: + if err := associate.Send(msg); err != nil { + log.WithError(err).Warn("Could not send UplinkMessage to broker") + if err == restartstream.ErrStreamClosed { + return + } + } + } + } + + }(server) + } + + return s +} + +type handlerStreams struct { + log log.Interface + ctx context.Context + cancel context.CancelFunc + + mu sync.RWMutex + downlink map[string]chan *DownlinkMessage + uplink chan *DeduplicatedUplinkMessage +} + +func (s *handlerStreams) Downlink(msg *DownlinkMessage) { + s.mu.RLock() + defer s.mu.RUnlock() + s.log.Debug("Sending DownlinkMessage to broker") + for serverName, ch := range s.downlink { + select { + case ch <- msg: + default: + s.log.WithField("Broker", serverName).Warn("DownlinkMessage buffer full") + } + } +} + +func (s *handlerStreams) Uplink() <-chan *DeduplicatedUplinkMessage { + s.mu.RLock() + defer s.mu.RUnlock() + return s.uplink +} + +func (s *handlerStreams) Close() { + s.cancel() +} + +// NewHandlerStreams returns new streams using the given handler ID and token +func (c *Client) NewHandlerStreams(id string, token string) HandlerStream { + log := c.log + ctx, cancel := context.WithCancel(c.ctx) + ctx = api.ContextWithID(ctx, id) + ctx = api.ContextWithToken(ctx, token) + s := &handlerStreams{ + log: log, + ctx: ctx, + cancel: cancel, + + downlink: make(map[string]chan *DownlinkMessage), + uplink: make(chan *DeduplicatedUplinkMessage), + } + + var wgUp sync.WaitGroup + go func() { + wgUp.Wait() + close(s.uplink) + }() + + // Hook up the broker servers + for _, server := range c.serverConns { + wgUp.Add(1) + go func(server *serverConn) { + if server.ready != nil { + select { + case <-ctx.Done(): + return + case <-server.ready: + } + } + if server.conn == nil { + return + } + log := log.WithField("Broker", server.name) + cli := NewBrokerClient(server.conn) + + // Stream channels + chDownlink := make(chan *DownlinkMessage, c.config.BufferSize) + + defer func() { + s.mu.Lock() + defer s.mu.Unlock() + delete(s.downlink, server.name) + close(chDownlink) + }() + + // Publish stream + downlink, err := cli.Publish(ctx) + if err != nil { + log.WithError(err).Warn("Could not set up Publish stream") + } else { + s.mu.Lock() + s.downlink[server.name] = chDownlink + s.mu.Unlock() + go func() { + err := downlink.RecvMsg(new(empty.Empty)) + logStreamErr(log, "Publish", err) + s.mu.Lock() + defer s.mu.Unlock() + delete(s.downlink, server.name) + }() + } + + // Subscribe stream + uplink, err := cli.Subscribe(ctx, &SubscribeRequest{}) + if err != nil { + log.WithError(err).Warn("Could not set up Subscribe stream") + wgUp.Done() + } else { + go func() { + defer func() { + wgUp.Done() + }() + for { + msg, err := uplink.Recv() + if err != nil { + logStreamErr(log, "Subscribe", err) + return + } + select { + case s.uplink <- msg: + default: + log.Warn("Uplink buffer full") + } + } + }() + } + + log.Debug("Start handling Publish/Subscribe streams") + defer log.Debug("Done handling Publish/Subscribe streams") + for { + select { + case <-ctx.Done(): + return + case msg := <-chDownlink: + if err := downlink.Send(msg); err != nil { + log.WithError(err).Warn("Could not send DownlinkMessage to broker") + if err == restartstream.ErrStreamClosed { + return + } + } + } + } + + }(server) + } + + return s +} diff --git a/api/broker/broker_test.go b/api/broker/broker_test.go new file mode 100644 index 000000000..3d1098e06 --- /dev/null +++ b/api/broker/broker_test.go @@ -0,0 +1,156 @@ +// Copyright © 2017 The Things Network +// Use of this source code is governed by the MIT license that can be found in the LICENSE file. + +package broker + +import ( + "net" + "testing" + "time" + + "github.com/TheThingsNetwork/go-utils/log" + "github.com/TheThingsNetwork/ttn/api/pool" + "github.com/htdvisser/grpc-testing/test" + . "github.com/smartystreets/assertions" + "google.golang.org/grpc" +) + +func TestRouterBroker(t *testing.T) { + waitTime := 10 * time.Millisecond + + a := New(t) + + testLogger := test.NewLogger() + log.Set(testLogger) + defer testLogger.Print(t) + + lis, err := net.Listen("tcp", "localhost:0") + if err != nil { + t.Fatalf("Failed to listen: %v", err) + } + s := grpc.NewServer() + server := NewReferenceBrokerServer(10) + + RegisterBrokerServer(s, server) + go s.Serve(lis) + + cli := NewClient(DefaultClientConfig) + + conn, err := pool.Global.Dial(lis.Addr().String(), grpc.WithInsecure()) + if err != nil { + t.Fatalf("Failed to connect to server: %v", err) + } + + cli.AddServer("test", conn) + time.Sleep(waitTime) + defer func() { + cli.Close() + time.Sleep(waitTime) + s.Stop() + }() + + testLogger.Print(t) + + rtr := cli.NewRouterStreams("test", "token") + time.Sleep(waitTime) + for i := 0; i < 20; i++ { + rtr.Uplink(&UplinkMessage{}) + time.Sleep(time.Millisecond) + } + time.Sleep(waitTime) + + a.So(server.metrics.uplinkIn, ShouldEqual, 20) + + testLogger.Print(t) + + downlink := rtr.Downlink() + recvDownlink := []*DownlinkMessage{} + var downlinkClosed bool + go func() { + for msg := range downlink { + recvDownlink = append(recvDownlink, msg) + } + downlinkClosed = true + }() + + server.downlinkOut["test"].ch <- &DownlinkMessage{} + + time.Sleep(waitTime) + rtr.Close() + time.Sleep(waitTime) + + a.So(recvDownlink, ShouldHaveLength, 1) + a.So(downlinkClosed, ShouldBeTrue) + + testLogger.Print(t) +} + +func TestHandlerBroker(t *testing.T) { + waitTime := 10 * time.Millisecond + + a := New(t) + + testLogger := test.NewLogger() + log.Set(testLogger) + defer testLogger.Print(t) + + lis, err := net.Listen("tcp", "localhost:0") + if err != nil { + t.Fatalf("Failed to listen: %v", err) + } + s := grpc.NewServer() + server := NewReferenceBrokerServer(10) + + RegisterBrokerServer(s, server) + go s.Serve(lis) + + cli := NewClient(DefaultClientConfig) + + conn, err := pool.Global.Dial(lis.Addr().String(), grpc.WithInsecure()) + if err != nil { + t.Fatalf("Failed to connect to server: %v", err) + } + + cli.AddServer("test", conn) + time.Sleep(waitTime) + defer func() { + cli.Close() + time.Sleep(waitTime) + s.Stop() + }() + + testLogger.Print(t) + + hdl := cli.NewHandlerStreams("test", "token") + time.Sleep(waitTime) + for i := 0; i < 20; i++ { + hdl.Downlink(&DownlinkMessage{}) + time.Sleep(time.Millisecond) + } + time.Sleep(waitTime) + + a.So(server.metrics.downlinkIn, ShouldEqual, 20) + + testLogger.Print(t) + + uplink := hdl.Uplink() + recvUplink := []*DeduplicatedUplinkMessage{} + var uplinkClosed bool + go func() { + for msg := range uplink { + recvUplink = append(recvUplink, msg) + } + uplinkClosed = true + }() + + server.uplinkOut["test"].ch <- &DeduplicatedUplinkMessage{} + + time.Sleep(waitTime) + hdl.Close() + time.Sleep(waitTime) + + a.So(recvUplink, ShouldHaveLength, 1) + a.So(uplinkClosed, ShouldBeTrue) + + testLogger.Print(t) +} diff --git a/api/broker/client_streams.go b/api/broker/client_streams.go deleted file mode 100644 index b027c497f..000000000 --- a/api/broker/client_streams.go +++ /dev/null @@ -1,412 +0,0 @@ -// Copyright © 2017 The Things Network -// Use of this source code is governed by the MIT license that can be found in the LICENSE file. - -package broker - -import ( - "io" - "sync" - "time" - - "github.com/TheThingsNetwork/go-utils/log" - "github.com/TheThingsNetwork/ttn/api/fields" - "github.com/TheThingsNetwork/ttn/utils/backoff" - "github.com/golang/protobuf/ptypes/empty" - "golang.org/x/net/context" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" -) - -// Stream interface -type Stream interface { - SetLogger(log.Interface) - Close() -} - -type stream struct { - closing bool - setup sync.WaitGroup - ctx log.Interface - client BrokerClient -} - -func (s *stream) SetLogger(logger log.Interface) { - s.ctx = logger -} - -// DefaultBufferSize indicates the default send and receive buffer sizes -var DefaultBufferSize = 10 - -// RouterStream for sending gateway statuses -type RouterStream interface { - Stream - Send(*UplinkMessage) error - Channel() <-chan *DownlinkMessage -} - -// NewMonitoredRouterStream starts and monitors a RouterStream -func NewMonitoredRouterStream(client BrokerClient, getContextFunc func() context.Context) RouterStream { - s := &routerStream{ - up: make(chan *UplinkMessage, DefaultBufferSize), - down: make(chan *DownlinkMessage, DefaultBufferSize), - err: make(chan error), - } - s.setup.Add(1) - s.client = client - s.ctx = log.Get() - - go func() { - var retries int - - for { - // Session channels - up := make(chan *UplinkMessage) - errCh := make(chan error) - - // Session client - var ctx context.Context - ctx, s.cancel = context.WithCancel(getContextFunc()) - client, err := s.client.Associate(ctx) - s.setup.Done() - if err != nil { - s.ctx.WithError(err).Warn("Could not start Associate stream, retrying...") - s.setup.Add(1) - time.Sleep(backoff.Backoff(retries)) - retries++ - continue - } - retries = 0 - - s.ctx.Debug("Started Associate stream") - - // Receive downlink errors - go func() { - for { - message, err := client.Recv() - if message != nil { - s.ctx.WithFields(fields.Get(message)).Debug("Receiving Downlink message") - if err := message.Validate(); err != nil { - s.ctx.WithError(err).Warn("Invalid Downlink") - continue - } - if err := message.UnmarshalPayload(); err != nil { - s.ctx.WithError(err).Warn("Could not unmarshal Downlink payload") - } - select { - case s.down <- message: - default: - s.ctx.Warn("Dropping Downlink message, buffer full") - } - } - if err != nil { - errCh <- err - break - } - } - close(errCh) - }() - - // Send uplink - go func() { - for message := range up { - s.ctx.WithFields(fields.Get(message)).Debug("Sending Uplink message") - if err := client.Send(message); err != nil { - s.ctx.WithError(err).Warn("Error sending Uplink message") - break - } - } - }() - - // Monitoring - var mErr error - - monitor: - for { - select { - case mErr = <-errCh: - break monitor - case msg, ok := <-s.up: - if !ok { - break monitor // channel closed - } - up <- msg - } - } - - close(up) - client.CloseSend() - - if mErr == nil || mErr == io.EOF || grpc.Code(mErr) == codes.Canceled { - s.ctx.Debug("Stopped Associate stream") - } else { - s.ctx.WithError(mErr).Warn("Error in Associate stream") - } - - if s.closing { - break - } - - s.setup.Add(1) - time.Sleep(backoff.Backoff(retries)) - retries++ - } - }() - - return s -} - -type routerStream struct { - stream - cancel context.CancelFunc - up chan *UplinkMessage - down chan *DownlinkMessage - err chan error -} - -func (s *routerStream) Send(uplink *UplinkMessage) error { - select { - case s.up <- uplink: - default: - s.ctx.Warn("Dropping Uplink message, buffer full") - } - return nil -} - -func (s *routerStream) Channel() <-chan *DownlinkMessage { - return s.down -} - -func (s *routerStream) Close() { - s.closing = true - close(s.up) - if s.cancel != nil { - s.cancel() - } -} - -// HandlerPublishStream for sending downlink messages to the broker -type HandlerPublishStream interface { - Stream - Send(*DownlinkMessage) error -} - -// NewMonitoredHandlerPublishStream starts and monitors a HandlerPublishStream -func NewMonitoredHandlerPublishStream(client BrokerClient, getContextFunc func() context.Context) HandlerPublishStream { - s := &handlerPublishStream{ - ch: make(chan *DownlinkMessage, DefaultBufferSize), - err: make(chan error), - } - s.setup.Add(1) - s.client = client - s.ctx = log.Get() - - go func() { - var retries int - - for { - // Session channels - ch := make(chan *DownlinkMessage) - errCh := make(chan error) - - // Session client - client, err := s.client.Publish(getContextFunc()) - s.setup.Done() - if err != nil { - if grpc.Code(err) == codes.Canceled { - s.ctx.Debug("Stopped Downlink stream") - break - } - s.ctx.WithError(err).Warn("Could not start Downlink stream, retrying...") - s.setup.Add(1) - time.Sleep(backoff.Backoff(retries)) - retries++ - continue - } - retries = 0 - - s.ctx.Info("Started Downlink stream") - - // Receive errors - go func() { - empty := new(empty.Empty) - if err := client.RecvMsg(empty); err != nil { - errCh <- err - } - close(errCh) - }() - - // Send - go func() { - for message := range ch { - s.ctx.WithFields(fields.Get(message)).Debug("Sending Downlink message") - if err := client.Send(message); err != nil { - s.ctx.WithError(err).Warn("Error sending Downlink message") - break - } - } - }() - - // Monitoring - var mErr error - - monitor: - for { - select { - case mErr = <-errCh: - break monitor - case msg, ok := <-s.ch: - if !ok { - break monitor // channel closed - } - ch <- msg - } - } - - close(ch) - client.CloseAndRecv() - - if mErr == nil || mErr == io.EOF || grpc.Code(mErr) == codes.Canceled { - s.ctx.Debug("Stopped Downlink stream") - } else { - s.ctx.WithError(mErr).Warn("Error in Downlink stream") - } - - if s.closing { - break - } - - s.setup.Add(1) - time.Sleep(backoff.Backoff(retries)) - retries++ - } - }() - - return s -} - -type handlerPublishStream struct { - stream - ch chan *DownlinkMessage - err chan error -} - -func (s *handlerPublishStream) Send(message *DownlinkMessage) error { - select { - case s.ch <- message: - default: - s.ctx.Warn("Dropping Downlink message, buffer full") - } - return nil -} - -func (s *handlerPublishStream) Close() { - s.setup.Wait() - s.ctx.Debug("Closing Downlink stream") - s.closing = true - close(s.ch) -} - -// HandlerSubscribeStream for receiving uplink messages -type HandlerSubscribeStream interface { - Stream - Channel() <-chan *DeduplicatedUplinkMessage -} - -// NewMonitoredHandlerSubscribeStream starts and monitors a HandlerSubscribeStream -func NewMonitoredHandlerSubscribeStream(client BrokerClient, getContextFunc func() context.Context) HandlerSubscribeStream { - s := &handlerSubscribeStream{ - ch: make(chan *DeduplicatedUplinkMessage, DefaultBufferSize), - err: make(chan error), - } - s.setup.Add(1) - s.client = client - s.ctx = log.Get() - - go func() { - var client Broker_SubscribeClient - var err error - var retries int - var message *DeduplicatedUplinkMessage - - for { - // Session client - var ctx context.Context - ctx, s.cancel = context.WithCancel(getContextFunc()) - client, err = s.client.Subscribe(ctx, &SubscribeRequest{}) - s.setup.Done() - if err != nil { - if grpc.Code(err) == codes.Canceled { - s.ctx.Debug("Stopped Uplink stream") - break - } - s.ctx.WithError(err).Warn("Could not start Uplink stream, retrying...") - s.setup.Add(1) - time.Sleep(backoff.Backoff(retries)) - retries++ - continue - } - retries = 0 - - s.ctx.Info("Started Uplink stream") - - for { - message, err = client.Recv() - if message != nil { - s.ctx.WithFields(fields.Get(message)).Debug("Receiving Uplink message") - if err := message.Validate(); err != nil { - s.ctx.WithError(err).Warn("Invalid Uplink") - continue - } - if err := message.UnmarshalPayload(); err != nil { - s.ctx.WithError(err).Warn("Could not unmarshal Uplink payload") - } - select { - case s.ch <- message: - default: - s.ctx.Warn("Dropping Uplink message, buffer full") - } - } - if err != nil { - break - } - } - - if err == nil || err == io.EOF || grpc.Code(err) == codes.Canceled { - s.ctx.Debug("Stopped Uplink stream") - } else { - s.ctx.WithError(err).Warn("Error in Uplink stream") - } - - if s.closing { - break - } - - s.setup.Add(1) - time.Sleep(backoff.Backoff(retries)) - retries++ - } - - close(s.ch) - }() - return s -} - -type handlerSubscribeStream struct { - stream - cancel context.CancelFunc - ch chan *DeduplicatedUplinkMessage - err chan error -} - -func (s *handlerSubscribeStream) Close() { - s.setup.Wait() - s.ctx.Debug("Closing Uplink stream") - s.closing = true - if s.cancel != nil { - s.cancel() - } -} - -func (s *handlerSubscribeStream) Channel() <-chan *DeduplicatedUplinkMessage { - return s.ch -} diff --git a/api/broker/communication_test.go b/api/broker/communication_test.go deleted file mode 100644 index c232cc23c..000000000 --- a/api/broker/communication_test.go +++ /dev/null @@ -1,222 +0,0 @@ -// Copyright © 2017 The Things Network -// Use of this source code is governed by the MIT license that can be found in the LICENSE file. - -package broker - -import ( - "fmt" - "math/rand" - "net" - "testing" - "time" - - "github.com/TheThingsNetwork/go-utils/log" - "github.com/TheThingsNetwork/ttn/api" - . "github.com/TheThingsNetwork/ttn/utils/testing" - . "github.com/smartystreets/assertions" - "golang.org/x/net/context" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/metadata" -) - -func newTestBroker() *testBroker { - return &testBroker{ - BrokerStreamServer: NewBrokerStreamServer(), - } -} - -type testBroker struct { - *BrokerStreamServer -} - -var _ BrokerServer = &testBroker{} - -func (s *testBroker) Activate(context.Context, *DeviceActivationRequest) (*DeviceActivationResponse, error) { - return nil, grpc.Errorf(codes.Unimplemented, "Not implemented") -} - -func (s *testBroker) Serve(port int) { - lis, err := net.Listen("tcp", fmt.Sprintf(":%d", port)) - if err != nil { - panic(err) - } - srv := grpc.NewServer() - RegisterBrokerServer(srv, s) - srv.Serve(lis) -} - -func TestHandlerBrokerCommunication(t *testing.T) { - a := New(t) - - ctx := GetLogger(t, "TestHandlerBrokerCommunication") - log.Set(ctx) - - brk := newTestBroker() - rand.Seed(time.Now().UnixNano()) - port := rand.Intn(1000) + 10000 - go brk.Serve(port) - - conn, _ := api.Dial(fmt.Sprintf("localhost:%d", port)) - - { - brk.HandlerPublishChanFunc = func(md metadata.MD) (chan *DownlinkMessage, error) { - ch := make(chan *DownlinkMessage, 1) - go func() { - ctx.Info("[SERVER] Channel opened") - for message := range ch { - ctx.WithField("Message", message).Info("[SERVER] Received Downlink") - } - ctx.Info("[SERVER] Channel closed") - }() - return ch, nil - } - - brkClient := NewBrokerClient(conn) - downlink := NewMonitoredHandlerPublishStream(brkClient, func() context.Context { - return context.Background() - }) - - err := downlink.Send(&DownlinkMessage{ - Payload: []byte{1, 2, 3, 4}, - }) - - a.So(err, ShouldBeNil) - - time.Sleep(10 * time.Millisecond) - - downlink.Close() - - time.Sleep(10 * time.Millisecond) - } - - { - brk.HandlerSubscribeChanFunc = func(md metadata.MD) (<-chan *DeduplicatedUplinkMessage, func(), error) { - ch := make(chan *DeduplicatedUplinkMessage, 1) - stop := make(chan struct{}) - cancel := func() { - ctx.Info("[SERVER] Canceling uplink") - close(stop) - } - go func() { - loop: - for { - select { - case <-stop: - break loop - case <-time.After(5 * time.Millisecond): - ctx.Info("[SERVER] Sending Uplink") - ch <- &DeduplicatedUplinkMessage{ - Payload: []byte{1, 2, 3, 4}, - } - } - } - close(ch) - ctx.Info("[SERVER] Closed Uplink") - }() - return ch, cancel, nil - } - - brkClient := NewBrokerClient(conn) - uplink := NewMonitoredHandlerSubscribeStream(brkClient, func() context.Context { - return context.Background() - }) - - ch := uplink.Channel() - - go func() { - for uplink := range ch { - ctx.WithField("Uplink", uplink).Info("[CLIENT] Received Uplink") - } - ctx.Info("[CLIENT] Closed Uplink") - }() - - time.Sleep(10 * time.Millisecond) - - uplink.Close() - - time.Sleep(10 * time.Millisecond) - } - -} - -func TestRouterBrokerCommunication(t *testing.T) { - a := New(t) - - ctx := GetLogger(t, "TestRouterBrokerCommunication") - log.Set(ctx) - - brk := newTestBroker() - rand.Seed(time.Now().UnixNano()) - port := rand.Intn(1000) + 10000 - go brk.Serve(port) - - conn, _ := api.Dial(fmt.Sprintf("localhost:%d", port)) - - { - brk.RouterAssociateChanFunc = func(md metadata.MD) (chan *UplinkMessage, <-chan *DownlinkMessage, func(), error) { - up := make(chan *UplinkMessage, 1) - down := make(chan *DownlinkMessage, 1) - - stop := make(chan struct{}) - cancel := func() { - ctx.Info("[SERVER] Canceling downlink") - close(stop) - } - - go func() { - ctx.Info("[SERVER] Uplink channel opened") - for message := range up { - ctx.WithField("Message", message).Info("[SERVER] Received Uplink") - } - ctx.Info("[SERVER] Uplink channel closed") - }() - - go func() { - loop: - for { - select { - case <-stop: - break loop - case <-time.After(5 * time.Millisecond): - ctx.Info("[SERVER] Sending Downlink") - down <- &DownlinkMessage{ - Payload: []byte{1, 2, 3, 4}, - } - } - } - close(down) - ctx.Info("[SERVER] Closed Downlink") - }() - - return up, down, cancel, nil - } - - brkClient := NewBrokerClient(conn) - stream := NewMonitoredRouterStream(brkClient, func() context.Context { - return context.Background() - }) - - ch := stream.Channel() - - go func() { - for downlink := range ch { - ctx.WithField("Downlink", downlink).Info("[CLIENT] Received Downlink") - } - ctx.Info("[CLIENT] Closed Downlink") - }() - - err := stream.Send(&UplinkMessage{ - Payload: []byte{1, 2, 3, 4}, - }) - - a.So(err, ShouldBeNil) - - time.Sleep(10 * time.Millisecond) - - stream.Close() - - time.Sleep(10 * time.Millisecond) - } - -} diff --git a/api/broker/reference_server.go b/api/broker/reference_server.go new file mode 100644 index 000000000..0c8bf9f5b --- /dev/null +++ b/api/broker/reference_server.go @@ -0,0 +1,293 @@ +// Copyright © 2017 The Things Network +// Use of this source code is governed by the MIT license that can be found in the LICENSE file. + +package broker + +import ( + "io" + "sync" + "sync/atomic" + + "github.com/TheThingsNetwork/go-utils/log" + "github.com/TheThingsNetwork/ttn/api" + "github.com/TheThingsNetwork/ttn/utils/errors" + "github.com/golang/protobuf/ptypes/empty" + "golang.org/x/net/context" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" +) + +// NewReferenceBrokerServer creates a new reference broker server +func NewReferenceBrokerServer(bufferSize int) *ReferenceBrokerServer { + s := &ReferenceBrokerServer{ + ctx: log.Get(), + + bufferSize: bufferSize, + + uplinkIn: make(chan *UplinkMessage, bufferSize), + downlinkOut: make(map[string]*downlinkSubscription), + + downlinkIn: make(chan *DownlinkMessage, bufferSize), + uplinkOut: make(map[string]*uplinkSubscription), + } + for i := 0; i < bufferSize; i++ { + go func() { + for { + select { + case <-s.uplinkIn: + atomic.AddUint64(&s.metrics.uplinkIn, 1) + case <-s.downlinkIn: + atomic.AddUint64(&s.metrics.downlinkIn, 1) + } + } + }() + } + return s +} + +type metrics struct { + uplinkIn uint64 + downlinkIn uint64 +} + +// ReferenceBrokerServer is a new reference broker server +type ReferenceBrokerServer struct { + ctx log.Interface + + bufferSize int + + uplinkIn chan *UplinkMessage + downlinkIn chan *DownlinkMessage + + mu sync.RWMutex + downlinkOut map[string]*downlinkSubscription + uplinkOut map[string]*uplinkSubscription + + metrics metrics +} + +type downlinkSubscription struct { + ch chan *DownlinkMessage + subscribers int +} + +func (s *ReferenceBrokerServer) addDownlinkSubscriber(routerID string) chan *DownlinkMessage { + s.mu.Lock() + defer s.mu.Unlock() + if sub, ok := s.downlinkOut[routerID]; ok { + sub.subscribers++ + return sub.ch + } + sub := &downlinkSubscription{ + subscribers: 1, + ch: make(chan *DownlinkMessage, s.bufferSize), + } + s.downlinkOut[routerID] = sub + return sub.ch +} + +func (s *ReferenceBrokerServer) removeDownlinkSubscriber(routerID string) { + s.mu.Lock() + defer s.mu.Unlock() + if sub, ok := s.downlinkOut[routerID]; ok && sub.subscribers > 0 { + sub.subscribers-- + } +} + +type uplinkSubscription struct { + ch chan *DeduplicatedUplinkMessage + subscribers int +} + +func (s *ReferenceBrokerServer) addUplinkSubscriber(handlerID string) chan *DeduplicatedUplinkMessage { + s.mu.Lock() + defer s.mu.Unlock() + if sub, ok := s.uplinkOut[handlerID]; ok { + sub.subscribers++ + return sub.ch + } + sub := &uplinkSubscription{ + subscribers: 1, + ch: make(chan *DeduplicatedUplinkMessage, s.bufferSize), + } + s.uplinkOut[handlerID] = sub + return sub.ch +} + +func (s *ReferenceBrokerServer) removeUplinkSubscriber(handlerID string) { + s.mu.Lock() + defer s.mu.Unlock() + if sub, ok := s.uplinkOut[handlerID]; ok && sub.subscribers > 0 { + sub.subscribers-- + } +} + +func (s *ReferenceBrokerServer) getAndAuthRouter(ctx context.Context) (string, error) { + id, err := api.IDFromContext(ctx) + if err != nil { + return "", err + } + token, err := api.TokenFromContext(ctx) + if err != nil { + return "", err + } + // Actually validate token here, if failed: return nil, grpc.Errorf(codes.Unauthenticated, "Router Authentication Failed") + s.ctx.WithFields(log.Fields{"ID": id, "Token": token}).Info("Router Authenticated") + return id, nil +} + +// Associate RPC +func (s *ReferenceBrokerServer) Associate(stream Broker_AssociateServer) error { + routerID, err := s.getAndAuthRouter(stream.Context()) + if err != nil { + return errors.NewErrPermissionDenied(err.Error()) + } + ctx := s.ctx.WithField("RouterID", routerID) + ctx.Info("Associate stream started") + defer func() { + if err != nil { + ctx.WithError(err).Info("Associate stream ended") + } else { + ctx.Info("Associate stream ended") + } + }() + + sub := s.addDownlinkSubscriber(routerID) + defer s.removeDownlinkSubscriber(routerID) + + var streamErr atomic.Value + + go func() { + for { + select { + case <-stream.Context().Done(): + streamErr.Store(stream.Context().Err()) + return + case msg, ok := <-sub: + if !ok { + streamErr.Store(io.EOF) + return + } + err := stream.Send(msg) + if err != nil { + streamErr.Store(err) + return + } + ctx.Info("Sent DownlinkMessage") + } + } + }() + + for { + streamErr := streamErr.Load() + if streamErr != nil { + return streamErr.(error) + } + msg, err := stream.Recv() + if err != nil { + return err + } + ctx.Info("Received UplinkMessage") + select { + case s.uplinkIn <- msg: + default: + ctx.Warn("Dropping UplinkMessage") + } + } +} + +func (s *ReferenceBrokerServer) getAndAuthHandler(ctx context.Context) (string, error) { + id, err := api.IDFromContext(ctx) + if err != nil { + return "", err + } + token, err := api.TokenFromContext(ctx) + if err != nil { + return "", err + } + // Actually validate token here, if failed: return nil, grpc.Errorf(codes.Unauthenticated, "Handler Authentication Failed") + s.ctx.WithFields(log.Fields{"ID": id, "Token": token}).Info("Handler Authenticated") + return id, nil +} + +// Subscribe RPC +func (s *ReferenceBrokerServer) Subscribe(req *SubscribeRequest, stream Broker_SubscribeServer) error { + handlerID, err := s.getAndAuthHandler(stream.Context()) + if err != nil { + return errors.NewErrPermissionDenied(err.Error()) + } + ctx := s.ctx.WithField("HandlerID", handlerID) + ctx.Info("Subscribe stream started") + defer func() { + if err != nil { + ctx.WithError(err).Info("Subscribe stream ended") + } else { + ctx.Info("Subscribe stream ended") + } + }() + + sub := s.addUplinkSubscriber(handlerID) + defer s.removeUplinkSubscriber(handlerID) + + for { + select { + case <-stream.Context().Done(): + return stream.Context().Err() + case msg, ok := <-sub: + if !ok { + return nil + } + err := stream.Send(msg) + if err != nil { + return err + } + ctx.Info("Sent UplinkMessage") + } + } +} + +// Publish RPC +func (s *ReferenceBrokerServer) Publish(stream Broker_PublishServer) error { + handlerID, err := s.getAndAuthHandler(stream.Context()) + if err != nil { + return errors.NewErrPermissionDenied(err.Error()) + } + ctx := s.ctx.WithField("HandlerID", handlerID) + ctx.Info("Publish stream started") + defer func() { + if err != nil { + ctx.WithError(err).Info("Publish stream ended") + } else { + ctx.Info("Publish stream ended") + } + }() + var streamErr atomic.Value + go func() { + <-stream.Context().Done() + streamErr.Store(stream.Context().Err()) + }() + for { + streamErr := streamErr.Load() + if streamErr != nil { + return streamErr.(error) + } + msg, err := stream.Recv() + if err == io.EOF { + return stream.SendAndClose(&empty.Empty{}) + } + if err != nil { + return err + } + ctx.Info("Received DownlinkMessage") + select { + case s.downlinkIn <- msg: + default: + ctx.Warn("Dropping DownlinkMessage") + } + } +} + +// Activate RPC +func (s *ReferenceBrokerServer) Activate(ctx context.Context, req *DeviceActivationRequest) (*DeviceActivationResponse, error) { + return nil, grpc.Errorf(codes.Unimplemented, "Not implemented") +} diff --git a/core/handler/handler.go b/core/handler/handler.go index aa704eb1a..cff775050 100644 --- a/core/handler/handler.go +++ b/core/handler/handler.go @@ -7,6 +7,7 @@ import ( "fmt" "github.com/TheThingsNetwork/ttn/amqp" + "github.com/TheThingsNetwork/ttn/api/auth" pb_broker "github.com/TheThingsNetwork/ttn/api/broker" pb "github.com/TheThingsNetwork/ttn/api/handler" "github.com/TheThingsNetwork/ttn/core/component" @@ -14,7 +15,6 @@ import ( "github.com/TheThingsNetwork/ttn/core/handler/device" "github.com/TheThingsNetwork/ttn/core/types" "github.com/TheThingsNetwork/ttn/mqtt" - "golang.org/x/net/context" "google.golang.org/grpc" "gopkg.in/redis.v5" ) @@ -147,11 +147,16 @@ func (h *handler) Shutdown() { } func (h *handler) associateBroker() error { + auth := auth.WithTokenFunc(func(_ string) string { + token, _ := h.Component.BuildJWT() + return token + }) + broker, err := h.Discover("broker", h.ttnBrokerID) if err != nil { return err } - conn, err := broker.Dial() + conn, err := broker.Dial(auth.DialOption()) if err != nil { return err } @@ -161,21 +166,21 @@ func (h *handler) associateBroker() error { h.downlink = make(chan *pb_broker.DownlinkMessage) - contextFunc := func() context.Context { return h.GetContext("") } - - upStream := pb_broker.NewMonitoredHandlerSubscribeStream(h.ttnBroker, contextFunc) - downStream := pb_broker.NewMonitoredHandlerPublishStream(h.ttnBroker, contextFunc) - - go func() { - for message := range upStream.Channel() { - go h.HandleUplink(message) - } - }() + config := pb_broker.DefaultClientConfig + config.BackgroundContext = h.Component.Context + cli := pb_broker.NewClient(config) + cli.AddServer(h.ttnBrokerID, h.ttnBrokerConn) + association := cli.NewHandlerStreams(h.Identity.Id, "") go func() { - for message := range h.downlink { - if err := downStream.Send(message); err != nil { - h.Ctx.WithError(err).Warn("Could not send downlink to Broker") + for { + select { + case message := <-h.downlink: + association.Downlink(message) + case message, ok := <-association.Uplink(): + if ok { + go h.HandleUplink(message) + } } } }() diff --git a/core/router/router.go b/core/router/router.go index f42074f54..86535b6f6 100644 --- a/core/router/router.go +++ b/core/router/router.go @@ -9,13 +9,13 @@ import ( "google.golang.org/grpc" + "github.com/TheThingsNetwork/ttn/api/auth" pb_broker "github.com/TheThingsNetwork/ttn/api/broker" pb_discovery "github.com/TheThingsNetwork/ttn/api/discovery" pb_gateway "github.com/TheThingsNetwork/ttn/api/gateway" pb "github.com/TheThingsNetwork/ttn/api/router" "github.com/TheThingsNetwork/ttn/core/component" "github.com/TheThingsNetwork/ttn/core/router/gateway" - "golang.org/x/net/context" ) // Router component @@ -142,34 +142,42 @@ func (r *router) getBroker(brokerAnnouncement *pb_discovery.Announcement) (*brok r.brokersLock.Lock() defer r.brokersLock.Unlock() if _, ok := r.brokers[brokerAnnouncement.Id]; !ok { + var err error + + brk := &broker{ + uplink: make(chan *pb_broker.UplinkMessage), + } + + auth := auth.WithTokenFunc(func(_ string) string { + token, _ := r.Component.BuildJWT() + return token + }) // Connect to the server - // TODO(htdvisser): This is blocking - conn, err := brokerAnnouncement.Dial() + brk.conn, err = brokerAnnouncement.Dial(auth.DialOption()) if err != nil { return nil, err } - client := pb_broker.NewBrokerClient(conn) - association := pb_broker.NewMonitoredRouterStream(client, func() context.Context { - return r.GetContext("") - }) - downlink := association.Channel() + // Set up the non-streaming client + brk.client = pb_broker.NewBrokerClient(brk.conn) - brk := &broker{ - conn: conn, - association: association, - client: client, - uplink: make(chan *pb_broker.UplinkMessage), - } + // Set up the streaming client + config := pb_broker.DefaultClientConfig + config.BackgroundContext = r.Component.Context + cli := pb_broker.NewClient(config) + cli.AddServer(brokerAnnouncement.Id, brk.conn) + brk.association = cli.NewRouterStreams(r.Identity.Id, "") go func() { for { select { case message := <-brk.uplink: - association.Send(message) - case message := <-downlink: - go r.HandleDownlink(message) + brk.association.Uplink(message) + case message, ok := <-brk.association.Downlink(): + if ok { + go r.HandleDownlink(message) + } } } }() From 4a177f98d42d626475e062fa8a99826440c91816 Mon Sep 17 00:00:00 2001 From: Hylke Visser Date: Mon, 13 Mar 2017 15:08:27 +0100 Subject: [PATCH 10/45] Add more log details to stream clients --- api/broker/broker.go | 4 ++-- api/monitor/monitor.go | 10 +++++----- api/router/router.go | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/api/broker/broker.go b/api/broker/broker.go index 1a1ca01fc..49762dde7 100644 --- a/api/broker/broker.go +++ b/api/broker/broker.go @@ -132,7 +132,7 @@ type routerStreams struct { func (s *routerStreams) Uplink(msg *UplinkMessage) { s.mu.RLock() defer s.mu.RUnlock() - s.log.Debug("Sending UplinkMessage to broker") + s.log.WithField("Brokers", len(s.uplink)).Debug("Sending UplinkMessage to broker") for serverName, ch := range s.uplink { select { case ch <- msg: @@ -265,7 +265,7 @@ type handlerStreams struct { func (s *handlerStreams) Downlink(msg *DownlinkMessage) { s.mu.RLock() defer s.mu.RUnlock() - s.log.Debug("Sending DownlinkMessage to broker") + s.log.WithField("Brokers", len(s.downlink)).Debug("Sending DownlinkMessage to broker") for serverName, ch := range s.downlink { select { case ch <- msg: diff --git a/api/monitor/monitor.go b/api/monitor/monitor.go index 2d5e4c5e7..623cf913f 100644 --- a/api/monitor/monitor.go +++ b/api/monitor/monitor.go @@ -169,7 +169,7 @@ func (s *gatewayStreams) Send(msg interface{}) { defer s.mu.RUnlock() switch msg := msg.(type) { case *router.UplinkMessage: - s.log.Debug("Sending UplinkMessage to monitor") + s.log.WithField("Monitors", len(s.uplink)).Debug("Sending UplinkMessage to monitor") for serverName, ch := range s.uplink { select { case ch <- msg: @@ -178,7 +178,7 @@ func (s *gatewayStreams) Send(msg interface{}) { } } case *router.DownlinkMessage: - s.log.Debug("Sending DownlinkMessage to monitor") + s.log.WithField("Monitors", len(s.downlink)).Debug("Sending DownlinkMessage to monitor") for serverName, ch := range s.downlink { select { case ch <- msg: @@ -187,7 +187,7 @@ func (s *gatewayStreams) Send(msg interface{}) { } } case *gateway.Status: - s.log.Debug("Sending Status to monitor") + s.log.WithField("Monitors", len(s.status)).Debug("Sending Status to monitor") for serverName, ch := range s.status { select { case ch <- msg: @@ -365,7 +365,7 @@ func (s *brokerStreams) Send(msg interface{}) { defer s.mu.RUnlock() switch msg := msg.(type) { case *broker.DeduplicatedUplinkMessage: - s.log.Debug("Sending DeduplicatedUplinkMessage to monitor") + s.log.WithField("Monitors", len(s.uplink)).Debug("Sending DeduplicatedUplinkMessage to monitor") for serverName, ch := range s.uplink { select { case ch <- msg: @@ -374,7 +374,7 @@ func (s *brokerStreams) Send(msg interface{}) { } } case *broker.DownlinkMessage: - s.log.Debug("Sending DownlinkMessage to monitor") + s.log.WithField("Monitors", len(s.downlink)).Debug("Sending DownlinkMessage to monitor") for serverName, ch := range s.downlink { select { case ch <- msg: diff --git a/api/router/router.go b/api/router/router.go index 052553826..b93330774 100644 --- a/api/router/router.go +++ b/api/router/router.go @@ -112,7 +112,7 @@ type gatewayStreams struct { func (s *gatewayStreams) Uplink(msg *UplinkMessage) { s.mu.RLock() defer s.mu.RUnlock() - s.log.Debug("Sending UplinkMessage to router") + s.log.WithField("Routers", len(s.uplink)).Debug("Sending UplinkMessage to router") for serverName, ch := range s.uplink { select { case ch <- msg: @@ -125,7 +125,7 @@ func (s *gatewayStreams) Uplink(msg *UplinkMessage) { func (s *gatewayStreams) Status(msg *gateway.Status) { s.mu.RLock() defer s.mu.RUnlock() - s.log.Debug("Sending Status to router") + s.log.WithField("Routers", len(s.status)).Debug("Sending Status to router") for serverName, ch := range s.status { select { case ch <- msg: From ed1da9026efde5f2107990785e596818816bc618 Mon Sep 17 00:00:00 2001 From: Hylke Visser Date: Mon, 13 Mar 2017 15:09:00 +0100 Subject: [PATCH 11/45] Move DialOptions and Context to Pool --- api/broker/broker_test.go | 4 +- api/dial.go | 7 ++- api/discovery/dial.go | 14 ++++-- api/discovery/dial_test.go | 14 +++--- api/pool/pool.go | 89 +++++++++++++++++++--------------- api/pool/pool_test.go | 13 ++--- api/router/router_test.go | 2 +- cmd/discovery.go | 2 +- cmd/handler.go | 2 +- core/broker/broker.go | 2 +- core/component/component.go | 9 ++++ core/handler/handler.go | 8 +-- core/router/router.go | 8 +-- ttnctl/cmd/components_check.go | 2 +- ttnctl/util/handler.go | 2 +- ttnctl/util/router.go | 4 +- 16 files changed, 97 insertions(+), 85 deletions(-) diff --git a/api/broker/broker_test.go b/api/broker/broker_test.go index 3d1098e06..d9149ab77 100644 --- a/api/broker/broker_test.go +++ b/api/broker/broker_test.go @@ -36,7 +36,7 @@ func TestRouterBroker(t *testing.T) { cli := NewClient(DefaultClientConfig) - conn, err := pool.Global.Dial(lis.Addr().String(), grpc.WithInsecure()) + conn, err := pool.Global.DialInsecure(lis.Addr().String()) if err != nil { t.Fatalf("Failed to connect to server: %v", err) } @@ -106,7 +106,7 @@ func TestHandlerBroker(t *testing.T) { cli := NewClient(DefaultClientConfig) - conn, err := pool.Global.Dial(lis.Addr().String(), grpc.WithInsecure()) + conn, err := pool.Global.DialInsecure(lis.Addr().String()) if err != nil { t.Fatalf("Failed to connect to server: %v", err) } diff --git a/api/dial.go b/api/dial.go index 056f7a555..8e3bf5409 100644 --- a/api/dial.go +++ b/api/dial.go @@ -33,15 +33,14 @@ var TLSConfig *tls.Config // Dial an address with default TLS config func Dial(target string) (*grpc.ClientConn, error) { - conn, err := pool.Global.Dial(target, grpc.WithBlock(), grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(RootCAs, ""))) + conn, err := pool.Global.DialSecure(target, credentials.NewClientTLSFromCert(RootCAs, "")) if err == nil { return conn, nil } - pool.Global.Close(target) if _, ok := err.(tls.RecordHeaderError); ok && AllowInsecureFallback { log.Get().Warn("Could not connect to gRPC server with TLS, will reconnect without TLS") log.Get().Warnf("This is a security risk, you should enable TLS on %s", target) - conn, err = pool.Global.Dial(target, grpc.WithBlock(), grpc.WithInsecure()) + conn, err = pool.Global.DialInsecure(target) } return conn, err } @@ -53,5 +52,5 @@ func DialWithCert(target string, cert string) (*grpc.ClientConn, error) { if !ok { panic("failed to parse root certificate") } - return pool.Global.Dial(target, grpc.WithBlock(), grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(certPool, ""))) + return pool.Global.DialSecure(target, credentials.NewClientTLSFromCert(certPool, "")) } diff --git a/api/discovery/dial.go b/api/discovery/dial.go index acfe51da7..f5e3c6627 100644 --- a/api/discovery/dial.go +++ b/api/discovery/dial.go @@ -11,6 +11,7 @@ import ( "strings" "github.com/TheThingsNetwork/ttn/api/pool" + "google.golang.org/grpc" "google.golang.org/grpc/credentials" ) @@ -39,14 +40,19 @@ func (a *Announcement) WithSecure() grpc.DialOption { return grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)) } -// Dial the component represented by this Announcement. We use the global connection pool here -func (a *Announcement) Dial(opts ...grpc.DialOption) (*grpc.ClientConn, error) { +// Dial the component represented by this Announcement. +// This function is blocking if the pool uses grpc.WithBlock() +func (a *Announcement) Dial(p *pool.Pool) (*grpc.ClientConn, error) { + if p == nil { + p = pool.Global + } if a.NetAddress == "" { return nil, errors.New("No address known for this component") } netAddress := strings.Split(a.NetAddress, ",")[0] if a.Certificate == "" { - return pool.Global.Dial(netAddress, append(opts, grpc.WithInsecure())...) + return p.DialInsecure(netAddress) } - return pool.Global.Dial(netAddress, append(opts, a.WithSecure())...) + tlsConfig, _ := a.TLSConfig() + return p.DialSecure(netAddress, credentials.NewTLS(tlsConfig)) } diff --git a/api/discovery/dial_test.go b/api/discovery/dial_test.go index 6cf4d3b31..acd522dbd 100644 --- a/api/discovery/dial_test.go +++ b/api/discovery/dial_test.go @@ -5,14 +5,12 @@ package discovery import ( "crypto/tls" + "io/ioutil" "net" + "os" "path" "testing" - "os" - - "io/ioutil" - "github.com/TheThingsNetwork/go-utils/log" "github.com/TheThingsNetwork/ttn/api/health" "github.com/TheThingsNetwork/ttn/utils/security" @@ -64,12 +62,12 @@ func TestDialInsecure(t *testing.T) { svc := new(Announcement) - _, err = svc.Dial() + _, err = svc.Dial(nil) a.So(err, ShouldNotBeNil) svc.NetAddress = lis.Addr().String() - conn, err := svc.Dial() + conn, err := svc.Dial(nil) a.So(err, ShouldBeNil) { @@ -111,13 +109,13 @@ func TestDialSecure(t *testing.T) { svc := new(Announcement) - _, err = svc.Dial() + _, err = svc.Dial(nil) a.So(err, ShouldNotBeNil) svc.NetAddress = lis.Addr().String() svc.Certificate = string(cert) - conn, err := svc.Dial() + conn, err := svc.Dial(nil) a.So(err, ShouldBeNil) { diff --git a/api/pool/pool.go b/api/pool/pool.go index 23e60fba8..514742d25 100644 --- a/api/pool/pool.go +++ b/api/pool/pool.go @@ -7,11 +7,11 @@ import ( "context" "net" "sync" - "sync/atomic" "time" "github.com/TheThingsNetwork/go-utils/grpc/restartstream" "google.golang.org/grpc" + "google.golang.org/grpc/credentials" ) // Pool with connections @@ -25,12 +25,20 @@ type Pool struct { type conn struct { sync.WaitGroup + target string + opts []grpc.DialOption cancel context.CancelFunc + conn *grpc.ClientConn + err error +} - users int32 - - conn *grpc.ClientConn - err error +func (c *conn) dial(ctx context.Context, opts ...grpc.DialOption) { + c.Add(1) + go func() { + ctx, c.cancel = context.WithCancel(ctx) + c.conn, c.err = grpc.DialContext(ctx, c.target, opts...) + c.Done() + }() } // KeepAliveDialer is a dialer that adds a 10 second TCP KeepAlive @@ -42,73 +50,76 @@ func KeepAliveDialer(addr string, timeout time.Duration) (net.Conn, error) { var DefaultDialOptions = []grpc.DialOption{ grpc.WithStreamInterceptor(restartstream.Interceptor(restartstream.DefaultSettings)), grpc.WithDialer(KeepAliveDialer), + grpc.WithBlock(), } // Global pool with connections -var Global = NewPool(DefaultDialOptions) +var Global = NewPool(context.Background(), DefaultDialOptions...) // NewPool returns a new connection pool that uses the given DialOptions -func NewPool(dialOptions []grpc.DialOption) *Pool { +func NewPool(ctx context.Context, dialOptions ...grpc.DialOption) *Pool { return &Pool{ + bgCtx: ctx, dialOptions: dialOptions, conns: make(map[string]*conn), } } -// Close connections. If target names supplied, considers the other users. Otherwise just closes all. +// SetContext sets a new background context for the pool. Only new connections will use this new context +func (p *Pool) SetContext(ctx context.Context) { + p.bgCtx = ctx +} + +// AddDialOption adds DialOption for the pool. Only new connections will use these new DialOptions +func (p *Pool) AddDialOption(opts ...grpc.DialOption) { + p.dialOptions = append(p.dialOptions, opts...) +} + +// Close connections. If no target names supplied. just closes all. func (p *Pool) Close(target ...string) { p.mu.Lock() defer p.mu.Unlock() if len(target) == 0 { - // This force-closes all connections - for _, c := range p.conns { - c.cancel() - if c.conn != nil { - c.conn.Close() - } + // Select all + for name := range p.conns { + target = append(target, name) } - p.conns = make(map[string]*conn) } for _, target := range target { if c, ok := p.conns[target]; ok { - new := atomic.AddInt32(&c.users, -1) - if new < 1 { - c.cancel() - if c.conn != nil { - c.conn.Close() - } - delete(p.conns, target) + c.cancel() + if c.conn != nil { + c.conn.Close() } + delete(p.conns, target) } } } -// DialContext gets a connection from the pool or creates a new one -// This function is blocking if grpc.WithBlock() is used -func (p *Pool) DialContext(ctx context.Context, target string, opts ...grpc.DialOption) (*grpc.ClientConn, error) { +func (p *Pool) dial(target string, opts ...grpc.DialOption) (*grpc.ClientConn, error) { p.mu.Lock() if _, ok := p.conns[target]; !ok { - c := new(conn) - c.Add(1) + c := &conn{ + target: target, + opts: opts, + } + c.dial(p.bgCtx, append(p.dialOptions, c.opts...)...) p.conns[target] = c - go func() { - ctx, c.cancel = context.WithCancel(ctx) - opts = append(p.dialOptions, opts...) - c.conn, c.err = grpc.DialContext(ctx, target, opts...) - c.Done() - }() } c := p.conns[target] p.mu.Unlock() - - atomic.AddInt32(&c.users, 1) - c.Wait() return c.conn, c.err } -// Dial gets a connection from the pool or creates a new one +// DialInsecure gets a connection from the pool or creates a new one +// This function is blocking if grpc.WithBlock() is used +func (p *Pool) DialInsecure(target string) (*grpc.ClientConn, error) { + return p.dial(target, grpc.WithInsecure()) +} + +// DialSecure gets a connection from the pool or creates a new one // This function is blocking if grpc.WithBlock() is used -func (p *Pool) Dial(target string, opts ...grpc.DialOption) (*grpc.ClientConn, error) { - return p.DialContext(context.Background(), target, opts...) +func (p *Pool) DialSecure(target string, creds credentials.TransportCredentials) (*grpc.ClientConn, error) { + return p.dial(target, grpc.WithTransportCredentials(creds)) } diff --git a/api/pool/pool_test.go b/api/pool/pool_test.go index 7859385cb..c8d450625 100644 --- a/api/pool/pool_test.go +++ b/api/pool/pool_test.go @@ -4,6 +4,7 @@ package pool import ( + "context" "net" "testing" "time" @@ -32,13 +33,13 @@ func TestPool(t *testing.T) { addr := lis.Addr().String() - pool := NewPool([]grpc.DialOption{grpc.WithBlock()}) + pool := NewPool(context.Background(), grpc.WithBlock()) - conn1, err := pool.Dial(addr, grpc.WithInsecure()) + conn1, err := pool.DialInsecure(addr) a.So(err, ShouldBeNil) a.So(conn1, ShouldNotBeNil) - conn2, err := pool.Dial(addr, grpc.WithInsecure()) + conn2, err := pool.DialInsecure(addr) a.So(err, ShouldBeNil) a.So(conn2, ShouldEqual, conn1) @@ -83,15 +84,15 @@ func TestPool(t *testing.T) { pool.Close(addr) pool.Close(addr) - conn3, err := pool.Dial(addr, grpc.WithInsecure()) + conn3, err := pool.DialInsecure(addr) a.So(err, ShouldBeNil) a.So(conn3, ShouldNotEqual, conn1) // the connection was closed, because there were no more users pool.Close() - pool = NewPool([]grpc.DialOption{}) // Without the grpc.WithBlock() + pool = NewPool(context.Background(), grpc.WithInsecure()) // Without the grpc.WithBlock() - conn4, err := pool.Dial(addr, append(DefaultDialOptions, grpc.WithInsecure())...) + conn4, err := pool.DialInsecure(addr) a.So(err, ShouldBeNil) a.So(conn4, ShouldNotBeNil) diff --git a/api/router/router_test.go b/api/router/router_test.go index c1890890d..770aa9be2 100644 --- a/api/router/router_test.go +++ b/api/router/router_test.go @@ -37,7 +37,7 @@ func TestRouter(t *testing.T) { cli := NewClient(DefaultClientConfig) - conn, err := pool.Global.Dial(lis.Addr().String(), grpc.WithInsecure()) + conn, err := pool.Global.DialInsecure(lis.Addr().String()) if err != nil { t.Fatalf("Failed to connect to server: %v", err) } diff --git a/cmd/discovery.go b/cmd/discovery.go index bd7a6faef..d1726489e 100644 --- a/cmd/discovery.go +++ b/cmd/discovery.go @@ -82,7 +82,7 @@ var discoveryCmd = &cobra.Command{ go grpc.Serve(lis) if viper.GetString("discovery.http-address") != "" && viper.GetInt("discovery.http-port") != 0 { - proxyConn, err := component.Identity.Dial() + proxyConn, err := component.Identity.Dial(component.Pool) if err != nil { ctx.WithError(err).Fatal("Could not start client for gRPC proxy") } diff --git a/cmd/handler.go b/cmd/handler.go index c82011dad..ab8c47154 100644 --- a/cmd/handler.go +++ b/cmd/handler.go @@ -132,7 +132,7 @@ var handlerCmd = &cobra.Command{ defer grpc.Stop() if httpActive { - proxyConn, err := component.Identity.Dial() + proxyConn, err := component.Identity.Dial(component.Pool) if err != nil { ctx.WithError(err).Fatal("Could not start client for gRPC proxy") } diff --git a/core/broker/broker.go b/core/broker/broker.go index 401415afe..2e9a37c39 100644 --- a/core/broker/broker.go +++ b/core/broker/broker.go @@ -231,7 +231,7 @@ func (b *broker) getHandlerConn(id string) (*grpc.ClientConn, error) { if err != nil { return nil, err } - conn, err := announcement.Dial() + conn, err := announcement.Dial(b.Pool) if err != nil { return nil, err } diff --git a/core/component/component.go b/core/component/component.go index dc91b8100..952a445cd 100644 --- a/core/component/component.go +++ b/core/component/component.go @@ -15,8 +15,10 @@ import ( "github.com/TheThingsNetwork/go-account-lib/claims" "github.com/TheThingsNetwork/go-account-lib/tokenkey" ttnlog "github.com/TheThingsNetwork/go-utils/log" + "github.com/TheThingsNetwork/ttn/api/auth" pb_discovery "github.com/TheThingsNetwork/ttn/api/discovery" pb_monitor "github.com/TheThingsNetwork/ttn/api/monitor" + "github.com/TheThingsNetwork/ttn/api/pool" "github.com/TheThingsNetwork/ttn/api/trace" "github.com/spf13/viper" "golang.org/x/net/context" // See https://github.com/grpc/grpc-go/issues/711" @@ -27,6 +29,7 @@ import ( // Component contains the common attributes for all TTN components type Component struct { Config Config + Pool *pool.Pool Identity *pb_discovery.Announcement Discovery pb_discovery.Client Monitor *pb_monitor.Client @@ -89,6 +92,12 @@ func New(ctx ttnlog.Interface, serviceName string, announcedAddress string) (*Co return nil, err } + auth := auth.WithTokenFunc(func(_ string) string { + token, _ := component.BuildJWT() + return token + }) + component.Pool = pool.NewPool(component.Context, append(pool.DefaultDialOptions, auth.DialOption())...) + if serviceName != "discovery" && serviceName != "networkserver" { var err error component.Discovery, err = pb_discovery.NewClient( diff --git a/core/handler/handler.go b/core/handler/handler.go index cff775050..1c81e1ac7 100644 --- a/core/handler/handler.go +++ b/core/handler/handler.go @@ -7,7 +7,6 @@ import ( "fmt" "github.com/TheThingsNetwork/ttn/amqp" - "github.com/TheThingsNetwork/ttn/api/auth" pb_broker "github.com/TheThingsNetwork/ttn/api/broker" pb "github.com/TheThingsNetwork/ttn/api/handler" "github.com/TheThingsNetwork/ttn/core/component" @@ -147,16 +146,11 @@ func (h *handler) Shutdown() { } func (h *handler) associateBroker() error { - auth := auth.WithTokenFunc(func(_ string) string { - token, _ := h.Component.BuildJWT() - return token - }) - broker, err := h.Discover("broker", h.ttnBrokerID) if err != nil { return err } - conn, err := broker.Dial(auth.DialOption()) + conn, err := broker.Dial(h.Pool) if err != nil { return err } diff --git a/core/router/router.go b/core/router/router.go index 86535b6f6..728757ec4 100644 --- a/core/router/router.go +++ b/core/router/router.go @@ -9,7 +9,6 @@ import ( "google.golang.org/grpc" - "github.com/TheThingsNetwork/ttn/api/auth" pb_broker "github.com/TheThingsNetwork/ttn/api/broker" pb_discovery "github.com/TheThingsNetwork/ttn/api/discovery" pb_gateway "github.com/TheThingsNetwork/ttn/api/gateway" @@ -148,13 +147,8 @@ func (r *router) getBroker(brokerAnnouncement *pb_discovery.Announcement) (*brok uplink: make(chan *pb_broker.UplinkMessage), } - auth := auth.WithTokenFunc(func(_ string) string { - token, _ := r.Component.BuildJWT() - return token - }) - // Connect to the server - brk.conn, err = brokerAnnouncement.Dial(auth.DialOption()) + brk.conn, err = brokerAnnouncement.Dial(r.Pool) if err != nil { return nil, err } diff --git a/ttnctl/cmd/components_check.go b/ttnctl/cmd/components_check.go index ac33847e6..06692df86 100644 --- a/ttnctl/cmd/components_check.go +++ b/ttnctl/cmd/components_check.go @@ -44,7 +44,7 @@ var checkCmd = &cobra.Command{ ctx.WithError(errors.FromGRPCError(err)).Fatalf("Could not get %s %s", serviceType, serviceID) } - conn, err := res.Dial() + conn, err := res.Dial(nil) if err != nil { ctx.WithError(errors.FromGRPCError(err)).Fatalf("Could not dial %s %s", serviceType, serviceID) } diff --git a/ttnctl/util/handler.go b/ttnctl/util/handler.go index 879d07f5c..5035bcdee 100644 --- a/ttnctl/util/handler.go +++ b/ttnctl/util/handler.go @@ -29,7 +29,7 @@ func GetHandlerManager(ctx ttnlog.Interface, appID string) (*grpc.ClientConn, *h token := TokenForScope(ctx, scope.App(appID)) ctx.WithField("Handler", handlerAnnouncement.NetAddress).Info("Connecting with Handler...") - hdlConn, err := handlerAnnouncement.Dial() + hdlConn, err := handlerAnnouncement.Dial(nil) if err != nil { ctx.WithError(err).Fatal("Could not connect to Handler") } diff --git a/ttnctl/util/router.go b/ttnctl/util/router.go index dfd48c4b7..812faa973 100644 --- a/ttnctl/util/router.go +++ b/ttnctl/util/router.go @@ -25,7 +25,7 @@ func GetRouter(ctx ttnlog.Interface) (*grpc.ClientConn, *router.Client) { ctx.WithError(errors.FromGRPCError(err)).Fatal("Could not get Router from Discovery") } ctx.Info("Connecting with Router...") - rtrConn, err := routerAnnouncement.Dial() + rtrConn, err := routerAnnouncement.Dial(nil) ctx.Info("Connected to Router") rtrClient := router.NewClient(router.DefaultClientConfig) rtrClient.AddServer(viper.GetString("router-id"), rtrConn) @@ -45,7 +45,7 @@ func GetRouterManager(ctx ttnlog.Interface) (*grpc.ClientConn, router.RouterMana ctx.WithError(errors.FromGRPCError(err)).Fatal("Could not get Router from Discovery") } ctx.Info("Connecting with Router...") - rtrConn, err := routerAnnouncement.Dial() + rtrConn, err := routerAnnouncement.Dial(nil) if err != nil { ctx.WithError(err).Fatal("Could not connect to Router") } From caf4e9c51a49471d0827843fcd6c6f888ddf914d Mon Sep 17 00:00:00 2001 From: Hylke Visser Date: Mon, 13 Mar 2017 17:33:57 +0100 Subject: [PATCH 12/45] Wait for streams to be ready to avoid dropping messages --- api/broker/broker.go | 14 ++++++++++++++ api/monitor/monitor.go | 8 ++++++++ api/router/router.go | 8 ++++++++ utils/waitgroup.go | 36 ++++++++++++++++++++++++++++++++++++ 4 files changed, 66 insertions(+) create mode 100644 utils/waitgroup.go diff --git a/api/broker/broker.go b/api/broker/broker.go index 49762dde7..d194bc36b 100644 --- a/api/broker/broker.go +++ b/api/broker/broker.go @@ -7,10 +7,12 @@ import ( "context" "io" "sync" + "time" "github.com/TheThingsNetwork/go-utils/grpc/restartstream" "github.com/TheThingsNetwork/go-utils/log" "github.com/TheThingsNetwork/ttn/api" + "github.com/TheThingsNetwork/ttn/utils" "github.com/golang/protobuf/ptypes/empty" "google.golang.org/grpc" "google.golang.org/grpc/codes" @@ -173,8 +175,11 @@ func (c *Client) NewRouterStreams(id string, token string) RouterStream { close(s.downlink) }() + var wg utils.WaitGroup + // Hook up the broker servers for _, server := range c.serverConns { + wg.Add(1) wgDown.Add(1) go func(server *serverConn) { if server.ready != nil { @@ -230,6 +235,7 @@ func (c *Client) NewRouterStreams(id string, token string) RouterStream { }() } + wg.Done() log.Debug("Start handling Associate stream") defer log.Debug("Done handling Associate stream") for { @@ -249,6 +255,8 @@ func (c *Client) NewRouterStreams(id string, token string) RouterStream { }(server) } + wg.WaitForMax(100 * time.Millisecond) + return s } @@ -306,8 +314,11 @@ func (c *Client) NewHandlerStreams(id string, token string) HandlerStream { close(s.uplink) }() + var wg utils.WaitGroup + // Hook up the broker servers for _, server := range c.serverConns { + wg.Add(1) wgUp.Add(1) go func(server *serverConn) { if server.ready != nil { @@ -375,6 +386,7 @@ func (c *Client) NewHandlerStreams(id string, token string) HandlerStream { }() } + wg.Done() log.Debug("Start handling Publish/Subscribe streams") defer log.Debug("Done handling Publish/Subscribe streams") for { @@ -394,5 +406,7 @@ func (c *Client) NewHandlerStreams(id string, token string) HandlerStream { }(server) } + wg.WaitForMax(100 * time.Millisecond) + return s } diff --git a/api/monitor/monitor.go b/api/monitor/monitor.go index 623cf913f..bdcccafb7 100644 --- a/api/monitor/monitor.go +++ b/api/monitor/monitor.go @@ -8,6 +8,7 @@ import ( "io" "strings" "sync" + "time" "github.com/TheThingsNetwork/go-utils/grpc/restartstream" "github.com/TheThingsNetwork/go-utils/log" @@ -15,6 +16,7 @@ import ( "github.com/TheThingsNetwork/ttn/api/broker" "github.com/TheThingsNetwork/ttn/api/gateway" "github.com/TheThingsNetwork/ttn/api/router" + "github.com/TheThingsNetwork/ttn/utils" "github.com/golang/protobuf/ptypes/empty" "golang.org/x/net/context" "google.golang.org/grpc" @@ -218,8 +220,11 @@ func (c *Client) NewGatewayStreams(id string, token string) GenericStream { status: make(map[string]chan *gateway.Status), } + var wg utils.WaitGroup + // Hook up the monitor servers for _, server := range c.serverConns { + wg.Add(1) go func(server *serverConn) { if server.ready != nil { select { @@ -315,6 +320,7 @@ func (c *Client) NewGatewayStreams(id string, token string) GenericStream { }() } + wg.Done() log.Debug("Start handling Gateway streams") defer log.Debug("Done handling Gateway streams") for { @@ -347,6 +353,8 @@ func (c *Client) NewGatewayStreams(id string, token string) GenericStream { }(server) } + wg.WaitForMax(100 * time.Millisecond) + return s } diff --git a/api/router/router.go b/api/router/router.go index b93330774..607f2ae6e 100644 --- a/api/router/router.go +++ b/api/router/router.go @@ -7,11 +7,13 @@ import ( "context" "io" "sync" + "time" "github.com/TheThingsNetwork/go-utils/grpc/restartstream" "github.com/TheThingsNetwork/go-utils/log" "github.com/TheThingsNetwork/ttn/api" "github.com/TheThingsNetwork/ttn/api/gateway" + "github.com/TheThingsNetwork/ttn/utils" "github.com/golang/protobuf/ptypes/empty" "google.golang.org/grpc" "google.golang.org/grpc/codes" @@ -168,8 +170,11 @@ func (c *Client) NewGatewayStreams(id string, token string) GenericStream { close(s.downlink) }() + var wg utils.WaitGroup + // Hook up the router servers for _, server := range c.serverConns { + wg.Add(1) wgDown.Add(1) go func(server *serverConn) { if server.ready != nil { @@ -274,6 +279,7 @@ func (c *Client) NewGatewayStreams(id string, token string) GenericStream { }() } + wg.Done() log.Debug("Start handling Gateway streams") defer log.Debug("Done handling Gateway streams") for { @@ -300,5 +306,7 @@ func (c *Client) NewGatewayStreams(id string, token string) GenericStream { }(server) } + wg.WaitForMax(100 * time.Millisecond) + return s } diff --git a/utils/waitgroup.go b/utils/waitgroup.go new file mode 100644 index 000000000..d5fa3c83b --- /dev/null +++ b/utils/waitgroup.go @@ -0,0 +1,36 @@ +// Copyright © 2017 The Things Network +// Use of this source code is governed by the MIT license that can be found in the LICENSE file. + +package utils + +import ( + "errors" + "sync" + "sync/atomic" + "time" +) + +// for keeping track of how many goroutines are waiting +var waiting int32 + +// WaitGroup is an extension of sync.WaitGroup that allows waiting with a maximum duration +type WaitGroup struct { + sync.WaitGroup +} + +// WaitForMax waits until the WaitGroup is Done or the specified duration has elapsed +func (wg *WaitGroup) WaitForMax(d time.Duration) error { + waitChan := make(chan struct{}) + go func() { + atomic.AddInt32(&waiting, 1) + wg.Wait() + atomic.AddInt32(&waiting, -1) + close(waitChan) + }() + select { + case <-waitChan: + return nil + case <-time.After(d): + return errors.New("Wait timeout expired") + } +} From 92bcc3cea47d23b9fe28ea6d69de73a27cf89d2e Mon Sep 17 00:00:00 2001 From: Hylke Visser Date: Mon, 13 Mar 2017 18:12:28 +0100 Subject: [PATCH 13/45] Use global connection pool for monitor and proxies --- api/monitor/monitor.go | 29 +++++++---------------------- api/monitor/monitor_test.go | 3 +++ cmd/discovery.go | 3 ++- cmd/handler.go | 3 ++- 4 files changed, 14 insertions(+), 24 deletions(-) diff --git a/api/monitor/monitor.go b/api/monitor/monitor.go index bdcccafb7..cf4c6f40d 100644 --- a/api/monitor/monitor.go +++ b/api/monitor/monitor.go @@ -15,6 +15,7 @@ import ( "github.com/TheThingsNetwork/ttn/api" "github.com/TheThingsNetwork/ttn/api/broker" "github.com/TheThingsNetwork/ttn/api/gateway" + "github.com/TheThingsNetwork/ttn/api/pool" "github.com/TheThingsNetwork/ttn/api/router" "github.com/TheThingsNetwork/ttn/utils" "github.com/golang/protobuf/ptypes/empty" @@ -74,17 +75,10 @@ type Client struct { serverConns []*serverConn } -// DefaultDialOptions for connecting with a monitor server -var DefaultDialOptions = []grpc.DialOption{ - grpc.WithBlock(), - grpc.FailOnNonTempDialError(false), - grpc.WithStreamInterceptor(restartstream.Interceptor(restartstream.DefaultSettings)), -} - // AddServer adds a new monitor server. Supplying DialOptions overrides the default dial options. // If the default DialOptions are used, TLS will be used to connect to monitors with a "-tls" suffix in their name. // This function should not be called after streams have been started -func (c *Client) AddServer(name, address string, opts ...grpc.DialOption) { +func (c *Client) AddServer(name, address string) { log := c.log.WithFields(log.Fields{"Monitor": name, "Address": address}) log.Info("Adding Monitor server") @@ -94,26 +88,17 @@ func (c *Client) AddServer(name, address string, opts ...grpc.DialOption) { ready: make(chan struct{}), } c.serverConns = append(c.serverConns, s) - if len(opts) == 0 { + + go func() { + var err error if strings.HasSuffix(name, "-tls") { - opts = append(DefaultDialOptions, grpc.WithTransportCredentials(credentials.NewTLS(TLSConfig))) + s.conn, err = pool.Global.DialSecure(address, credentials.NewTLS(nil)) } else { - opts = append(DefaultDialOptions, grpc.WithInsecure()) + s.conn, err = pool.Global.DialInsecure(address) } - } - - go func() { - conn, err := grpc.DialContext( - c.ctx, - address, - opts..., - ) if err != nil { log.WithError(err).Error("Could not connect to Monitor server") - close(s.ready) - return } - s.conn = conn close(s.ready) }() } diff --git a/api/monitor/monitor_test.go b/api/monitor/monitor_test.go index 1c5867a5b..915184b59 100644 --- a/api/monitor/monitor_test.go +++ b/api/monitor/monitor_test.go @@ -11,6 +11,7 @@ import ( "github.com/TheThingsNetwork/go-utils/log" "github.com/TheThingsNetwork/ttn/api/broker" "github.com/TheThingsNetwork/ttn/api/gateway" + "github.com/TheThingsNetwork/ttn/api/pool" "github.com/TheThingsNetwork/ttn/api/router" "github.com/htdvisser/grpc-testing/test" . "github.com/smartystreets/assertions" @@ -40,7 +41,9 @@ func TestMonitor(t *testing.T) { cli.AddServer("tls-without-tls", lis.Addr().String()) + time.Sleep(50 * time.Millisecond) testLogger.Print(t) + pool.Global.Close(lis.Addr().String()) cli.AddServer("test", lis.Addr().String()) time.Sleep(waitTime) diff --git a/cmd/discovery.go b/cmd/discovery.go index d1726489e..96c425bd3 100644 --- a/cmd/discovery.go +++ b/cmd/discovery.go @@ -14,6 +14,7 @@ import ( ttnlog "github.com/TheThingsNetwork/go-utils/log" pb "github.com/TheThingsNetwork/ttn/api/discovery" + "github.com/TheThingsNetwork/ttn/api/pool" "github.com/TheThingsNetwork/ttn/core/component" "github.com/TheThingsNetwork/ttn/core/discovery" "github.com/TheThingsNetwork/ttn/core/discovery/announcement" @@ -82,7 +83,7 @@ var discoveryCmd = &cobra.Command{ go grpc.Serve(lis) if viper.GetString("discovery.http-address") != "" && viper.GetInt("discovery.http-port") != 0 { - proxyConn, err := component.Identity.Dial(component.Pool) + proxyConn, err := component.Identity.Dial(pool.Global) if err != nil { ctx.WithError(err).Fatal("Could not start client for gRPC proxy") } diff --git a/cmd/handler.go b/cmd/handler.go index ab8c47154..98f55af91 100644 --- a/cmd/handler.go +++ b/cmd/handler.go @@ -13,6 +13,7 @@ import ( ttnlog "github.com/TheThingsNetwork/go-utils/log" pb "github.com/TheThingsNetwork/ttn/api/handler" + "github.com/TheThingsNetwork/ttn/api/pool" "github.com/TheThingsNetwork/ttn/core/component" "github.com/TheThingsNetwork/ttn/core/handler" "github.com/TheThingsNetwork/ttn/core/proxy" @@ -132,7 +133,7 @@ var handlerCmd = &cobra.Command{ defer grpc.Stop() if httpActive { - proxyConn, err := component.Identity.Dial(component.Pool) + proxyConn, err := component.Identity.Dial(pool.Global) if err != nil { ctx.WithError(err).Fatal("Could not start client for gRPC proxy") } From 5a7505478a82fef9dac5c7113bf7cccca7625adf Mon Sep 17 00:00:00 2001 From: Hylke Visser Date: Wed, 15 Mar 2017 11:33:02 +0100 Subject: [PATCH 14/45] Move Root CAs to pool package --- api/dial.go | 17 +---------------- api/monitor/monitor.go | 3 +-- api/pool/pool.go | 23 +++++++++++++++++++++++ cmd/broker_register_prefix.go | 3 ++- core/component/auth.go | 3 ++- ttnctl/util/discovery.go | 3 ++- 6 files changed, 31 insertions(+), 21 deletions(-) diff --git a/api/dial.go b/api/dial.go index 8e3bf5409..14b5bfc26 100644 --- a/api/dial.go +++ b/api/dial.go @@ -8,32 +8,17 @@ import ( "crypto/x509" "github.com/TheThingsNetwork/go-utils/log" - "github.com/TheThingsNetwork/go-utils/roots" "github.com/TheThingsNetwork/ttn/api/pool" "google.golang.org/grpc" "google.golang.org/grpc/credentials" ) -// RootCAs to use in API connections -var RootCAs *x509.CertPool - -func init() { - var err error - RootCAs, err = x509.SystemCertPool() - if err != nil { - RootCAs = roots.MozillaRootCAs - } -} - // AllowInsecureFallback can be set to true if you need to connect with a server that does not use TLS var AllowInsecureFallback = false -// TLSConfig to use when connecting to servers -var TLSConfig *tls.Config - // Dial an address with default TLS config func Dial(target string) (*grpc.ClientConn, error) { - conn, err := pool.Global.DialSecure(target, credentials.NewClientTLSFromCert(RootCAs, "")) + conn, err := pool.Global.DialSecure(target, nil) if err == nil { return conn, nil } diff --git a/api/monitor/monitor.go b/api/monitor/monitor.go index cf4c6f40d..dd58593f3 100644 --- a/api/monitor/monitor.go +++ b/api/monitor/monitor.go @@ -22,7 +22,6 @@ import ( "golang.org/x/net/context" "google.golang.org/grpc" "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials" ) // GenericStream is used for sending anything to the monitor. @@ -92,7 +91,7 @@ func (c *Client) AddServer(name, address string) { go func() { var err error if strings.HasSuffix(name, "-tls") { - s.conn, err = pool.Global.DialSecure(address, credentials.NewTLS(nil)) + s.conn, err = pool.Global.DialSecure(address, nil) } else { s.conn, err = pool.Global.DialInsecure(address) } diff --git a/api/pool/pool.go b/api/pool/pool.go index 514742d25..d39415a50 100644 --- a/api/pool/pool.go +++ b/api/pool/pool.go @@ -5,15 +5,34 @@ package pool import ( "context" + "crypto/tls" + "crypto/x509" "net" "sync" "time" "github.com/TheThingsNetwork/go-utils/grpc/restartstream" + "github.com/TheThingsNetwork/go-utils/roots" "google.golang.org/grpc" "google.golang.org/grpc/credentials" ) +// RootCAs to use in API connections +var RootCAs *x509.CertPool + +func init() { + var err error + RootCAs, err = x509.SystemCertPool() + if err != nil { + RootCAs = roots.MozillaRootCAs + } +} + +// TLSConfig that will be used when dialing securely without supplying TransportCredentials +func TLSConfig(serverName string) *tls.Config { + return &tls.Config{ServerName: serverName, RootCAs: RootCAs} +} + // Pool with connections type Pool struct { dialOptions []grpc.DialOption @@ -121,5 +140,9 @@ func (p *Pool) DialInsecure(target string) (*grpc.ClientConn, error) { // DialSecure gets a connection from the pool or creates a new one // This function is blocking if grpc.WithBlock() is used func (p *Pool) DialSecure(target string, creds credentials.TransportCredentials) (*grpc.ClientConn, error) { + if creds == nil { + netHost, _, _ := net.SplitHostPort(target) + creds = credentials.NewTLS(TLSConfig(netHost)) + } return p.dial(target, grpc.WithTransportCredentials(creds)) } diff --git a/cmd/broker_register_prefix.go b/cmd/broker_register_prefix.go index ae9742924..4ace784e8 100644 --- a/cmd/broker_register_prefix.go +++ b/cmd/broker_register_prefix.go @@ -10,6 +10,7 @@ import ( "github.com/TheThingsNetwork/ttn/api" "github.com/TheThingsNetwork/ttn/api/discovery" + "github.com/TheThingsNetwork/ttn/api/pool" "github.com/TheThingsNetwork/ttn/core/types" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -30,7 +31,7 @@ var brokerRegisterPrefixCmd = &cobra.Command{ path := filepath.Clean(viper.GetString("key-dir") + "/ca.cert") cert, err := ioutil.ReadFile(path) - if err == nil && !api.RootCAs.AppendCertsFromPEM(cert) { + if err == nil && !pool.RootCAs.AppendCertsFromPEM(cert) { ctx.Warnf("Could not add root certificates from %s", path) } diff --git a/core/component/auth.go b/core/component/auth.go index c092785ad..032d5265b 100644 --- a/core/component/auth.go +++ b/core/component/auth.go @@ -21,6 +21,7 @@ import ( "github.com/TheThingsNetwork/go-account-lib/tokenkey" "github.com/TheThingsNetwork/ttn/api" pb_discovery "github.com/TheThingsNetwork/ttn/api/discovery" + "github.com/TheThingsNetwork/ttn/api/pool" "github.com/TheThingsNetwork/ttn/utils/errors" "github.com/TheThingsNetwork/ttn/utils/security" jwt "github.com/dgrijalva/jwt-go" @@ -154,7 +155,7 @@ func (c *Component) initRoots() error { if err != nil { return nil } - if !api.RootCAs.AppendCertsFromPEM(cert) { + if !pool.RootCAs.AppendCertsFromPEM(cert) { return fmt.Errorf("Could not add root certificates from %s", path) } return nil diff --git a/ttnctl/util/discovery.go b/ttnctl/util/discovery.go index f54baa100..42c9d3b80 100644 --- a/ttnctl/util/discovery.go +++ b/ttnctl/util/discovery.go @@ -10,6 +10,7 @@ import ( ttnlog "github.com/TheThingsNetwork/go-utils/log" "github.com/TheThingsNetwork/ttn/api" "github.com/TheThingsNetwork/ttn/api/discovery" + "github.com/TheThingsNetwork/ttn/api/pool" "github.com/spf13/viper" "google.golang.org/grpc" ) @@ -18,7 +19,7 @@ import ( func GetDiscovery(ctx ttnlog.Interface) (*grpc.ClientConn, discovery.DiscoveryClient) { path := path.Join(GetDataDir(), "/ca.cert") cert, err := ioutil.ReadFile(path) - if err == nil && !api.RootCAs.AppendCertsFromPEM(cert) { + if err == nil && !pool.RootCAs.AppendCertsFromPEM(cert) { ctx.Warnf("Could not add root certificates from %s", path) } From ff93fcc34f6e5d21a106a42d6b6b79d3c416ee14 Mon Sep 17 00:00:00 2001 From: Romeo Van Snick Date: Wed, 15 Mar 2017 17:14:22 +0100 Subject: [PATCH 15/45] Update go-account-lib --- vendor/vendor.json | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/vendor/vendor.json b/vendor/vendor.json index 2b8b89621..30ce1a57f 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -9,10 +9,16 @@ "revisionTime": "2017-02-20T21:12:21Z" }, { - "checksumSHA1": "Mq9f3LBEs4SMMIQHdPa9U9EWEv8=", + "checksumSHA1": "MBoJo9G+PWFsRRIkb55n+9WCcDo=", + "path": "github.com/TheThingsNetwork/go-account-lib", + "revision": "c3634eb20045e94abd340fac873c3be17e506e36", + "revisionTime": "2017-03-15T15:57:52Z" + }, + { + "checksumSHA1": "+3cLRNxFCwQQyfaTDFJWE/v2lKM=", "path": "github.com/TheThingsNetwork/go-account-lib/account", - "revision": "90ce9cacdb918a924728960a7e40784573c8de25", - "revisionTime": "2017-02-28T11:03:51Z" + "revision": "c3634eb20045e94abd340fac873c3be17e506e36", + "revisionTime": "2017-03-15T15:57:52Z" }, { "checksumSHA1": "b6pCShOzSh4N9LujOUFzQDzqhz8=", From 966fff535c78dbd2ccc2d1aa103d07d6cccbfbc1 Mon Sep 17 00:00:00 2001 From: Romeo Van Snick Date: Wed, 15 Mar 2017 17:16:17 +0100 Subject: [PATCH 16/45] Fix breaking changes --- ttnctl/cmd/devices_register.go | 6 ++++-- ttnctl/cmd/gateways_edit.go | 2 +- ttnctl/cmd/gateways_info.go | 16 ++++++++++++---- ttnctl/cmd/gateways_list.go | 6 +++--- ttnctl/cmd/gateways_register.go | 4 ++-- ttnctl/util/location.go | 8 ++++---- 6 files changed, 26 insertions(+), 16 deletions(-) diff --git a/ttnctl/cmd/devices_register.go b/ttnctl/cmd/devices_register.go index 983331962..4dffb9833 100644 --- a/ttnctl/cmd/devices_register.go +++ b/ttnctl/cmd/devices_register.go @@ -80,8 +80,10 @@ var devicesRegisterCmd = &cobra.Command{ if err != nil { ctx.WithError(err).Fatal("Invalid location") } - device.Latitude = float32(location.Latitude) - device.Longitude = float32(location.Longitude) + if location.Latitude != nil && location.Longitude != nil { + device.Latitude = float32(*location.Latitude) + device.Longitude = float32(*location.Longitude) + } } conn, manager := util.GetHandlerManager(ctx, appID) diff --git a/ttnctl/cmd/gateways_edit.go b/ttnctl/cmd/gateways_edit.go index e3e006314..d9df6838c 100644 --- a/ttnctl/cmd/gateways_edit.go +++ b/ttnctl/cmd/gateways_edit.go @@ -46,7 +46,7 @@ var gatewaysEditCmd = &cobra.Command{ if err != nil { ctx.WithError(err).Fatal("Invalid location") } - edits.Location = location + edits.AntennaLocation = location } act := util.GetAccount(ctx) diff --git a/ttnctl/cmd/gateways_info.go b/ttnctl/cmd/gateways_info.go index 647245f73..2659d8c20 100644 --- a/ttnctl/cmd/gateways_info.go +++ b/ttnctl/cmd/gateways_info.go @@ -6,6 +6,7 @@ package cmd import ( "fmt" + "github.com/TheThingsNetwork/go-account-lib/rights" "github.com/TheThingsNetwork/ttn/api" "github.com/TheThingsNetwork/ttn/ttnctl/util" "github.com/spf13/cobra" @@ -36,14 +37,21 @@ var gatewaysInfoCmd = &cobra.Command{ fmt.Printf("Gateway ID: %s\n", gateway.ID) fmt.Printf("Activated: %v\n", gateway.Activated) fmt.Printf("Frequency Plan: %s\n", gateway.FrequencyPlan) + locationAccess := "private" - if gateway.LocationPublic { + if gateway.IsPublic(rights.GatewayLocation) { locationAccess = "public" } - if gateway.Location != nil { - fmt.Printf("Location Info : (%f, %f) (%s) \n", gateway.Location.Latitude, gateway.Location.Longitude, locationAccess) + + if gateway.AntennaLocation != nil && gateway.AntennaLocation.Latitude != nil { + fmt.Printf("Location Info : (%f, %f) (%s) \n", *gateway.AntennaLocation.Latitude, *gateway.AntennaLocation.Longitude, locationAccess) } - if gateway.StatusPublic { + + if gateway.AntennaLocation != nil && gateway.AntennaLocation.Altitude != nil { + fmt.Printf("Altitude : %fm \n", *gateway.AntennaLocation.Altitude) + } + + if gateway.IsPublic(rights.GatewayStatus) { fmt.Printf("Status Info: public (see ttnctl gateways status %s)\n", gatewayID) } else { fmt.Print("Status Info: private\n") diff --git a/ttnctl/cmd/gateways_list.go b/ttnctl/cmd/gateways_list.go index 44e800dc2..4eeedb65b 100644 --- a/ttnctl/cmd/gateways_list.go +++ b/ttnctl/cmd/gateways_list.go @@ -35,9 +35,9 @@ var gatewaysListCmd = &cobra.Command{ for i, gateway := range gateways { var lat float64 var lng float64 - if gateway.Location != nil { - lat = gateway.Location.Latitude - lng = gateway.Location.Longitude + if gateway.AntennaLocation != nil && gateway.AntennaLocation.Latitude != nil && gateway.AntennaLocation.Longitude != nil { + lat = *gateway.AntennaLocation.Latitude + lng = *gateway.AntennaLocation.Longitude } table.AddRow(i+1, gateway.ID, gateway.Activated, gateway.FrequencyPlan, fmt.Sprintf("(%f, %f)", lat, lng)) } diff --git a/ttnctl/cmd/gateways_register.go b/ttnctl/cmd/gateways_register.go index 4a6da185d..21e3cfb43 100644 --- a/ttnctl/cmd/gateways_register.go +++ b/ttnctl/cmd/gateways_register.go @@ -28,7 +28,7 @@ var gatewaysRegisterCmd = &cobra.Command{ frequencyPlan := args[1] var err error - var location *account.Location + var location *account.AntennaLocation if len(args) == 3 { location, err = util.ParseLocation(args[2]) if err != nil { @@ -37,7 +37,7 @@ var gatewaysRegisterCmd = &cobra.Command{ } settings := account.GatewaySettings{ - Location: location, + AntennaLocation: location, } act := util.GetAccount(ctx) diff --git a/ttnctl/util/location.go b/ttnctl/util/location.go index ede6680d3..499d70446 100644 --- a/ttnctl/util/location.go +++ b/ttnctl/util/location.go @@ -11,7 +11,7 @@ import ( "github.com/TheThingsNetwork/go-account-lib/account" ) -func ParseLocation(locationStr string) (*account.Location, error) { +func ParseLocation(locationStr string) (*account.AntennaLocation, error) { parts := strings.Split(locationStr, ",") if len(parts) != 2 { return nil, errors.New("Location should be on the , format") @@ -35,8 +35,8 @@ func ParseLocation(locationStr string) (*account.Location, error) { return nil, errors.New("Longitude should be in range [-180, 180]") } - return &account.Location{ - Latitude: lat, - Longitude: lng, + return &account.AntennaLocation{ + Latitude: &lat, + Longitude: &lng, }, nil } From e1787492ecc2da5f223f4336ecff97a9061cc059 Mon Sep 17 00:00:00 2001 From: Romeo Van Snick Date: Wed, 15 Mar 2017 17:28:22 +0100 Subject: [PATCH 17/45] Fix test --- ttnctl/util/location_test.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/ttnctl/util/location_test.go b/ttnctl/util/location_test.go index b9abc66d0..9406893f8 100644 --- a/ttnctl/util/location_test.go +++ b/ttnctl/util/location_test.go @@ -14,12 +14,14 @@ func TestParseLocation(t *testing.T) { a := New(t) str := "10.5,33.4" - loc := &account.Location{ - Latitude: float64(10.5), - Longitude: float64(33.4), + lat := float64(10.5) + lng := float64(33.4) + loc := &account.AntennaLocation{ + Latitude: &lat, + Longitude: &lng, } parsed, err := ParseLocation(str) a.So(err, ShouldBeNil) - a.So(loc.Latitude, ShouldEqual, parsed.Latitude) - a.So(loc.Longitude, ShouldEqual, parsed.Longitude) + a.So(*loc.Latitude, ShouldEqual, *parsed.Latitude) + a.So(*loc.Longitude, ShouldEqual, *parsed.Longitude) } From a0d78ab222af7dacc3e8ba1459ef1d1debd72655 Mon Sep 17 00:00:00 2001 From: Hylke Visser Date: Thu, 16 Mar 2017 08:23:36 +0100 Subject: [PATCH 18/45] Fix gRPC stream limit --- core/component/grpc.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/component/grpc.go b/core/component/grpc.go index 539db4373..fee24fc1f 100644 --- a/core/component/grpc.go +++ b/core/component/grpc.go @@ -4,6 +4,8 @@ package component import ( + "math" + "github.com/TheThingsNetwork/go-utils/grpc/interceptor" "github.com/TheThingsNetwork/go-utils/log" "github.com/TheThingsNetwork/ttn/api/fields" @@ -37,6 +39,7 @@ func (c *Component) ServerOptions() []grpc.ServerOption { } opts := []grpc.ServerOption{ + grpc.MaxConcurrentStreams(math.MaxUint16), grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(unaryErr, unaryLog)), grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(streamErr, streamLog)), } From 001da12e4d13c50bbc5d2aa9783365c07c27c91d Mon Sep 17 00:00:00 2001 From: Romeo Van Snick Date: Thu, 16 Mar 2017 11:08:37 +0100 Subject: [PATCH 19/45] Use latest version of go-account-lib, which has less indirection --- ttnctl/cmd/devices_register.go | 6 ++---- ttnctl/cmd/gateways_info.go | 8 ++------ ttnctl/cmd/gateways_list.go | 10 ++++++---- ttnctl/cmd/gateways_register.go | 2 +- ttnctl/util/location.go | 8 ++++---- ttnctl/util/location_test.go | 12 +++++------- vendor/vendor.json | 6 +++--- 7 files changed, 23 insertions(+), 29 deletions(-) diff --git a/ttnctl/cmd/devices_register.go b/ttnctl/cmd/devices_register.go index 4dffb9833..983331962 100644 --- a/ttnctl/cmd/devices_register.go +++ b/ttnctl/cmd/devices_register.go @@ -80,10 +80,8 @@ var devicesRegisterCmd = &cobra.Command{ if err != nil { ctx.WithError(err).Fatal("Invalid location") } - if location.Latitude != nil && location.Longitude != nil { - device.Latitude = float32(*location.Latitude) - device.Longitude = float32(*location.Longitude) - } + device.Latitude = float32(location.Latitude) + device.Longitude = float32(location.Longitude) } conn, manager := util.GetHandlerManager(ctx, appID) diff --git a/ttnctl/cmd/gateways_info.go b/ttnctl/cmd/gateways_info.go index 2659d8c20..f54b8225f 100644 --- a/ttnctl/cmd/gateways_info.go +++ b/ttnctl/cmd/gateways_info.go @@ -43,12 +43,8 @@ var gatewaysInfoCmd = &cobra.Command{ locationAccess = "public" } - if gateway.AntennaLocation != nil && gateway.AntennaLocation.Latitude != nil { - fmt.Printf("Location Info : (%f, %f) (%s) \n", *gateway.AntennaLocation.Latitude, *gateway.AntennaLocation.Longitude, locationAccess) - } - - if gateway.AntennaLocation != nil && gateway.AntennaLocation.Altitude != nil { - fmt.Printf("Altitude : %fm \n", *gateway.AntennaLocation.Altitude) + if gateway.AntennaLocation != nil { + fmt.Printf("Location Info : (%f, %f, %f) (%s) \n", gateway.AntennaLocation.Latitude, gateway.AntennaLocation.Longitude, gateway.AntennaLocation.Altitude, locationAccess) } if gateway.IsPublic(rights.GatewayStatus) { diff --git a/ttnctl/cmd/gateways_list.go b/ttnctl/cmd/gateways_list.go index 4eeedb65b..8b931a98f 100644 --- a/ttnctl/cmd/gateways_list.go +++ b/ttnctl/cmd/gateways_list.go @@ -35,11 +35,13 @@ var gatewaysListCmd = &cobra.Command{ for i, gateway := range gateways { var lat float64 var lng float64 - if gateway.AntennaLocation != nil && gateway.AntennaLocation.Latitude != nil && gateway.AntennaLocation.Longitude != nil { - lat = *gateway.AntennaLocation.Latitude - lng = *gateway.AntennaLocation.Longitude + var alt int + if gateway.AntennaLocation != nil { + lat = gateway.AntennaLocation.Latitude + lng = gateway.AntennaLocation.Longitude + alt = gateway.AntennaLocation.Altitude } - table.AddRow(i+1, gateway.ID, gateway.Activated, gateway.FrequencyPlan, fmt.Sprintf("(%f, %f)", lat, lng)) + table.AddRow(i+1, gateway.ID, gateway.Activated, gateway.FrequencyPlan, fmt.Sprintf("(%f, %f, %f)", lat, lng, alt)) } fmt.Println() diff --git a/ttnctl/cmd/gateways_register.go b/ttnctl/cmd/gateways_register.go index 21e3cfb43..dc81a781b 100644 --- a/ttnctl/cmd/gateways_register.go +++ b/ttnctl/cmd/gateways_register.go @@ -28,7 +28,7 @@ var gatewaysRegisterCmd = &cobra.Command{ frequencyPlan := args[1] var err error - var location *account.AntennaLocation + var location *account.Location if len(args) == 3 { location, err = util.ParseLocation(args[2]) if err != nil { diff --git a/ttnctl/util/location.go b/ttnctl/util/location.go index 499d70446..ede6680d3 100644 --- a/ttnctl/util/location.go +++ b/ttnctl/util/location.go @@ -11,7 +11,7 @@ import ( "github.com/TheThingsNetwork/go-account-lib/account" ) -func ParseLocation(locationStr string) (*account.AntennaLocation, error) { +func ParseLocation(locationStr string) (*account.Location, error) { parts := strings.Split(locationStr, ",") if len(parts) != 2 { return nil, errors.New("Location should be on the , format") @@ -35,8 +35,8 @@ func ParseLocation(locationStr string) (*account.AntennaLocation, error) { return nil, errors.New("Longitude should be in range [-180, 180]") } - return &account.AntennaLocation{ - Latitude: &lat, - Longitude: &lng, + return &account.Location{ + Latitude: lat, + Longitude: lng, }, nil } diff --git a/ttnctl/util/location_test.go b/ttnctl/util/location_test.go index 9406893f8..b9abc66d0 100644 --- a/ttnctl/util/location_test.go +++ b/ttnctl/util/location_test.go @@ -14,14 +14,12 @@ func TestParseLocation(t *testing.T) { a := New(t) str := "10.5,33.4" - lat := float64(10.5) - lng := float64(33.4) - loc := &account.AntennaLocation{ - Latitude: &lat, - Longitude: &lng, + loc := &account.Location{ + Latitude: float64(10.5), + Longitude: float64(33.4), } parsed, err := ParseLocation(str) a.So(err, ShouldBeNil) - a.So(*loc.Latitude, ShouldEqual, *parsed.Latitude) - a.So(*loc.Longitude, ShouldEqual, *parsed.Longitude) + a.So(loc.Latitude, ShouldEqual, parsed.Latitude) + a.So(loc.Longitude, ShouldEqual, parsed.Longitude) } diff --git a/vendor/vendor.json b/vendor/vendor.json index 30ce1a57f..3bd12aff1 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -15,10 +15,10 @@ "revisionTime": "2017-03-15T15:57:52Z" }, { - "checksumSHA1": "+3cLRNxFCwQQyfaTDFJWE/v2lKM=", + "checksumSHA1": "FuCt7YZsBRGm3v6N37ZAHUZtY14=", "path": "github.com/TheThingsNetwork/go-account-lib/account", - "revision": "c3634eb20045e94abd340fac873c3be17e506e36", - "revisionTime": "2017-03-15T15:57:52Z" + "revision": "a7ace38bb103f59b438c9347c867c6c722980c46", + "revisionTime": "2017-03-16T10:02:09Z" }, { "checksumSHA1": "b6pCShOzSh4N9LujOUFzQDzqhz8=", From 2ed05a0f41f9fe956b8c12b4202764798e3ced71 Mon Sep 17 00:00:00 2001 From: Roman Volosatovs Date: Thu, 16 Mar 2017 13:08:45 +0100 Subject: [PATCH 20/45] errors: add helpers to determine type --- utils/errors/errors.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/utils/errors/errors.go b/utils/errors/errors.go index 45c55f27b..ca9a7fea5 100644 --- a/utils/errors/errors.go +++ b/utils/errors/errors.go @@ -44,6 +44,22 @@ func GetErrType(err error) ErrType { return Unknown } +func IsPermissionDenied(err error) bool { + return GetErrType(err) == PermissionDenied +} +func IsNotFound(err error) bool { + return GetErrType(err) == NotFound +} +func IsInvalidArgument(err error) bool { + return GetErrType(err) == InvalidArgument +} +func IsInternal(err error) bool { + return GetErrType(err) == Internal +} +func IsAlreadyExists(err error) bool { + return GetErrType(err) == AlreadyExists +} + // BuildGRPCError returns the error with a GRPC code func BuildGRPCError(err error) error { if err == nil { From 9625639ef7c25692e5c6f17220cd1d925fe2cc71 Mon Sep 17 00:00:00 2001 From: Roman Volosatovs Date: Thu, 16 Mar 2017 13:13:46 +0100 Subject: [PATCH 21/45] errors: add comments --- utils/errors/errors.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/utils/errors/errors.go b/utils/errors/errors.go index ca9a7fea5..0a7c58357 100644 --- a/utils/errors/errors.go +++ b/utils/errors/errors.go @@ -44,18 +44,27 @@ func GetErrType(err error) ErrType { return Unknown } +// IsPermissionDenied returns whether error type is PermissionDenied func IsPermissionDenied(err error) bool { return GetErrType(err) == PermissionDenied } + +// IsNotFound returns whether error type is NotFound func IsNotFound(err error) bool { return GetErrType(err) == NotFound } + +// IsInvalidArgument returns whether error type is InvalidArgument func IsInvalidArgument(err error) bool { return GetErrType(err) == InvalidArgument } + +// IsInternal returns whether error type is Internal func IsInternal(err error) bool { return GetErrType(err) == Internal } + +// IsAlreadyExists returns whether error type is AlreadyExists func IsAlreadyExists(err error) bool { return GetErrType(err) == AlreadyExists } From 710476b143b2dc2bb24dffa764a3530da76c5432 Mon Sep 17 00:00:00 2001 From: Romeo Van Snick Date: Thu, 16 Mar 2017 13:48:31 +0100 Subject: [PATCH 22/45] Update go-account-lib to no longer make use of deprecated public_rights --- ttnctl/cmd/gateways_info.go | 5 ++--- vendor/vendor.json | 6 +++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/ttnctl/cmd/gateways_info.go b/ttnctl/cmd/gateways_info.go index f54b8225f..2d9005c36 100644 --- a/ttnctl/cmd/gateways_info.go +++ b/ttnctl/cmd/gateways_info.go @@ -6,7 +6,6 @@ package cmd import ( "fmt" - "github.com/TheThingsNetwork/go-account-lib/rights" "github.com/TheThingsNetwork/ttn/api" "github.com/TheThingsNetwork/ttn/ttnctl/util" "github.com/spf13/cobra" @@ -39,7 +38,7 @@ var gatewaysInfoCmd = &cobra.Command{ fmt.Printf("Frequency Plan: %s\n", gateway.FrequencyPlan) locationAccess := "private" - if gateway.IsPublic(rights.GatewayLocation) { + if gateway.LocationPublic { locationAccess = "public" } @@ -47,7 +46,7 @@ var gatewaysInfoCmd = &cobra.Command{ fmt.Printf("Location Info : (%f, %f, %f) (%s) \n", gateway.AntennaLocation.Latitude, gateway.AntennaLocation.Longitude, gateway.AntennaLocation.Altitude, locationAccess) } - if gateway.IsPublic(rights.GatewayStatus) { + if gateway.StatusPublic { fmt.Printf("Status Info: public (see ttnctl gateways status %s)\n", gatewayID) } else { fmt.Print("Status Info: private\n") diff --git a/vendor/vendor.json b/vendor/vendor.json index 3bd12aff1..b89d24817 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -15,10 +15,10 @@ "revisionTime": "2017-03-15T15:57:52Z" }, { - "checksumSHA1": "FuCt7YZsBRGm3v6N37ZAHUZtY14=", + "checksumSHA1": "EyqK2NXpCzuD58zuZaSJ43jwvYI=", "path": "github.com/TheThingsNetwork/go-account-lib/account", - "revision": "a7ace38bb103f59b438c9347c867c6c722980c46", - "revisionTime": "2017-03-16T10:02:09Z" + "revision": "25435ec2cfb8b5d6d7734d6ec3f86ef6bf0e53f5", + "revisionTime": "2017-03-16T12:34:39Z" }, { "checksumSHA1": "b6pCShOzSh4N9LujOUFzQDzqhz8=", From c0a46ade3fe31fcd976542e238c4e3a5864870ca Mon Sep 17 00:00:00 2001 From: Hylke Visser Date: Fri, 17 Mar 2017 07:07:16 +0100 Subject: [PATCH 23/45] Make redis password configurable --- cmd/discovery.go | 4 +++- cmd/handler.go | 4 +++- cmd/networkserver.go | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/cmd/discovery.go b/cmd/discovery.go index bd7a6faef..cfb43f6de 100644 --- a/cmd/discovery.go +++ b/cmd/discovery.go @@ -44,7 +44,7 @@ var discoveryCmd = &cobra.Command{ // Redis Client client := redis.NewClient(&redis.Options{ Addr: viper.GetString("discovery.redis-address"), - Password: "", // no password set + Password: viper.GetString("discovery.redis-password"), DB: viper.GetInt("discovery.redis-db"), }) @@ -120,6 +120,8 @@ func init() { discoveryCmd.Flags().String("redis-address", "localhost:6379", "Redis server and port") viper.BindPFlag("discovery.redis-address", discoveryCmd.Flags().Lookup("redis-address")) + discoveryCmd.Flags().String("redis-password", "", "Redis password") + viper.BindPFlag("discovery.redis-password", discoveryCmd.Flags().Lookup("redis-password")) discoveryCmd.Flags().Int("redis-db", 0, "Redis database") viper.BindPFlag("discovery.redis-db", discoveryCmd.Flags().Lookup("redis-db")) diff --git a/cmd/handler.go b/cmd/handler.go index c82011dad..2e163f761 100644 --- a/cmd/handler.go +++ b/cmd/handler.go @@ -48,7 +48,7 @@ var handlerCmd = &cobra.Command{ // Redis Client client := redis.NewClient(&redis.Options{ Addr: viper.GetString("handler.redis-address"), - Password: "", // no password set + Password: viper.GetString("handler.redis-password"), DB: viper.GetInt("handler.redis-db"), }) @@ -170,6 +170,8 @@ func init() { handlerCmd.Flags().String("redis-address", "localhost:6379", "Redis host and port") viper.BindPFlag("handler.redis-address", handlerCmd.Flags().Lookup("redis-address")) + handlerCmd.Flags().String("redis-password", "", "Redis password") + viper.BindPFlag("handler.redis-password", handlerCmd.Flags().Lookup("redis-password")) handlerCmd.Flags().Int("redis-db", 0, "Redis database") viper.BindPFlag("handler.redis-db", handlerCmd.Flags().Lookup("redis-db")) diff --git a/cmd/networkserver.go b/cmd/networkserver.go index 2e1ea5c43..5c7f782a5 100644 --- a/cmd/networkserver.go +++ b/cmd/networkserver.go @@ -39,7 +39,7 @@ var networkserverCmd = &cobra.Command{ // Redis Client client := redis.NewClient(&redis.Options{ Addr: viper.GetString("networkserver.redis-address"), - Password: "", // no password set + Password: viper.GetString("networkserver.redis-password"), DB: viper.GetInt("networkserver.redis-db"), }) @@ -103,6 +103,8 @@ func init() { networkserverCmd.Flags().String("redis-address", "localhost:6379", "Redis server and port") viper.BindPFlag("networkserver.redis-address", networkserverCmd.Flags().Lookup("redis-address")) + networkserverCmd.Flags().String("redis-password", "", "Redis password") + viper.BindPFlag("networkserver.redis-password", networkserverCmd.Flags().Lookup("redis-password")) networkserverCmd.Flags().Int("redis-db", 0, "Redis database") viper.BindPFlag("networkserver.redis-db", networkserverCmd.Flags().Lookup("redis-db")) From 5d8eadad897a196f5966ac30213a5817e26c4c22 Mon Sep 17 00:00:00 2001 From: Jan Kramer Date: Mon, 20 Mar 2017 07:24:19 +0100 Subject: [PATCH 24/45] Add uplink/downlink monitor to Handler --- api/monitor/monitor.go | 162 ++++++++++++++++++++++++++++++ api/monitor/monitor.pb.go | 173 ++++++++++++++++++++++++++++---- api/monitor/monitor.proto | 3 + api/monitor/reference_server.go | 118 +++++++++++++++++++++- core/handler/downlink.go | 3 + core/handler/handler.go | 7 +- core/handler/uplink.go | 3 + 7 files changed, 444 insertions(+), 25 deletions(-) diff --git a/api/monitor/monitor.go b/api/monitor/monitor.go index 7a2f68bc2..7d52577d5 100644 --- a/api/monitor/monitor.go +++ b/api/monitor/monitor.go @@ -509,3 +509,165 @@ func (c *Client) NewBrokerStreams(id string, token string) GenericStream { return s } + +type handlerStreams struct { + log log.Interface + ctx context.Context + cancel context.CancelFunc + + mu sync.RWMutex + uplink map[string]chan *broker.DeduplicatedUplinkMessage + downlink map[string]chan *broker.DownlinkMessage +} + +func (s *handlerStreams) Send(msg interface{}) { + s.mu.RLock() + defer s.mu.RUnlock() + switch msg := msg.(type) { + case *broker.DeduplicatedUplinkMessage: + s.log.Debug("Sending DeduplicatedUplinkMessage to monitor") + for serverName, ch := range s.uplink { + select { + case ch <- msg: + default: + s.log.WithField("Monitor", serverName).Warn("DeduplicatedUplinkMessage buffer full") + } + } + case *broker.DownlinkMessage: + s.log.Debug("Sending DownlinkMessage to monitor") + for serverName, ch := range s.downlink { + select { + case ch <- msg: + default: + s.log.WithField("Monitor", serverName).Warn("DownlinkMessage buffer full") + } + } + } +} + +func (s *handlerStreams) Close() { + s.cancel() +} + +// NewHandlerStreams returns new streams using the given handler ID and token +func (c *Client) NewHandlerStreams(id string, token string) GenericStream { + log := c.log + ctx, cancel := context.WithCancel(c.ctx) + ctx = api.ContextWithID(ctx, id) + ctx = api.ContextWithToken(ctx, token) + s := &handlerStreams{ + log: log, + ctx: ctx, + cancel: cancel, + + uplink: make(map[string]chan *broker.DeduplicatedUplinkMessage), + downlink: make(map[string]chan *broker.DownlinkMessage), + } + + // Hook up the monitor servers + for _, server := range c.serverConns { + go func(server *serverConn) { + if server.ready != nil { + select { + case <-ctx.Done(): + return + case <-server.ready: + } + } + if server.conn == nil { + return + } + + log := log.WithField("Monitor", server.name) + cli := NewMonitorClient(server.conn) + + monitor := func(streamName string, stream grpc.ClientStream) { + err := stream.RecvMsg(new(empty.Empty)) + switch { + case err == nil: + log.Debugf("%s stream closed", streamName) + case err == io.EOF: + log.WithError(err).Debugf("%s stream ended", streamName) + case err == context.Canceled || grpc.Code(err) == codes.Canceled: + log.WithError(err).Debugf("%s stream canceled", streamName) + case err == context.DeadlineExceeded || grpc.Code(err) == codes.DeadlineExceeded: + log.WithError(err).Debugf("%s stream deadline exceeded", streamName) + case grpc.ErrorDesc(err) == grpc.ErrClientConnClosing.Error(): + log.WithError(err).Debugf("%s stream connection closed", streamName) + default: + log.WithError(err).Warnf("%s stream closed unexpectedly", streamName) + } + } + + chUplink := make(chan *broker.DeduplicatedUplinkMessage, c.config.BufferSize) + chDownlink := make(chan *broker.DownlinkMessage, c.config.BufferSize) + + defer func() { + s.mu.Lock() + defer s.mu.Unlock() + delete(s.uplink, server.name) + delete(s.downlink, server.name) + close(chUplink) + close(chDownlink) + }() + + // Uplink stream + uplink, err := cli.HandlerUplink(ctx) + if err != nil { + log.WithError(err).Warn("Could not set up HandlerUplink stream") + } else { + s.mu.Lock() + s.uplink[server.name] = chUplink + s.mu.Unlock() + go func() { + monitor("HandlerUplink", uplink) + s.mu.Lock() + defer s.mu.Unlock() + delete(s.uplink, server.name) + }() + } + + // Downlink stream + downlink, err := cli.HandlerDownlink(ctx) + if err != nil { + log.WithError(err).Warn("Could not set up HandlerDownlink stream") + } else { + s.mu.Lock() + s.downlink[server.name] = chDownlink + s.mu.Unlock() + go func() { + monitor("HandlerDownlink", downlink) + s.mu.Lock() + defer s.mu.Unlock() + delete(s.downlink, server.name) + }() + } + + log.Debug("Start handling Handler streams") + defer log.Debug("Done handling Handler streams") + for { + select { + case <-ctx.Done(): + return + case msg := <-chUplink: + if err := uplink.Send(msg); err != nil { + log.WithError(err).Warn("Could not send UplinkMessage to monitor") + if err == restartstream.ErrStreamClosed { + return + } + } + case msg := <-chDownlink: + if err := downlink.Send(msg); err != nil { + log.WithError(err).Warn("Could not send DownlinkMessage to monitor") + if err == restartstream.ErrStreamClosed { + return + } + } + } + } + + }(server) + } + + return s +} diff --git a/api/monitor/monitor.pb.go b/api/monitor/monitor.pb.go index 45a4bb355..fe74f3af0 100644 --- a/api/monitor/monitor.pb.go +++ b/api/monitor/monitor.pb.go @@ -52,6 +52,8 @@ type MonitorClient interface { GatewayDownlink(ctx context.Context, opts ...grpc.CallOption) (Monitor_GatewayDownlinkClient, error) BrokerUplink(ctx context.Context, opts ...grpc.CallOption) (Monitor_BrokerUplinkClient, error) BrokerDownlink(ctx context.Context, opts ...grpc.CallOption) (Monitor_BrokerDownlinkClient, error) + HandlerUplink(ctx context.Context, opts ...grpc.CallOption) (Monitor_HandlerUplinkClient, error) + HandlerDownlink(ctx context.Context, opts ...grpc.CallOption) (Monitor_HandlerDownlinkClient, error) } type monitorClient struct { @@ -232,6 +234,74 @@ func (x *monitorBrokerDownlinkClient) CloseAndRecv() (*google_protobuf1.Empty, e return m, nil } +func (c *monitorClient) HandlerUplink(ctx context.Context, opts ...grpc.CallOption) (Monitor_HandlerUplinkClient, error) { + stream, err := grpc.NewClientStream(ctx, &_Monitor_serviceDesc.Streams[5], c.cc, "/monitor.Monitor/HandlerUplink", opts...) + if err != nil { + return nil, err + } + x := &monitorHandlerUplinkClient{stream} + return x, nil +} + +type Monitor_HandlerUplinkClient interface { + Send(*broker.DeduplicatedUplinkMessage) error + CloseAndRecv() (*google_protobuf1.Empty, error) + grpc.ClientStream +} + +type monitorHandlerUplinkClient struct { + grpc.ClientStream +} + +func (x *monitorHandlerUplinkClient) Send(m *broker.DeduplicatedUplinkMessage) error { + return x.ClientStream.SendMsg(m) +} + +func (x *monitorHandlerUplinkClient) CloseAndRecv() (*google_protobuf1.Empty, error) { + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + m := new(google_protobuf1.Empty) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *monitorClient) HandlerDownlink(ctx context.Context, opts ...grpc.CallOption) (Monitor_HandlerDownlinkClient, error) { + stream, err := grpc.NewClientStream(ctx, &_Monitor_serviceDesc.Streams[6], c.cc, "/monitor.Monitor/HandlerDownlink", opts...) + if err != nil { + return nil, err + } + x := &monitorHandlerDownlinkClient{stream} + return x, nil +} + +type Monitor_HandlerDownlinkClient interface { + Send(*broker.DownlinkMessage) error + CloseAndRecv() (*google_protobuf1.Empty, error) + grpc.ClientStream +} + +type monitorHandlerDownlinkClient struct { + grpc.ClientStream +} + +func (x *monitorHandlerDownlinkClient) Send(m *broker.DownlinkMessage) error { + return x.ClientStream.SendMsg(m) +} + +func (x *monitorHandlerDownlinkClient) CloseAndRecv() (*google_protobuf1.Empty, error) { + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + m := new(google_protobuf1.Empty) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + // Server API for Monitor service type MonitorServer interface { @@ -240,6 +310,8 @@ type MonitorServer interface { GatewayDownlink(Monitor_GatewayDownlinkServer) error BrokerUplink(Monitor_BrokerUplinkServer) error BrokerDownlink(Monitor_BrokerDownlinkServer) error + HandlerUplink(Monitor_HandlerUplinkServer) error + HandlerDownlink(Monitor_HandlerDownlinkServer) error } func RegisterMonitorServer(s *grpc.Server, srv MonitorServer) { @@ -376,6 +448,58 @@ func (x *monitorBrokerDownlinkServer) Recv() (*broker.DownlinkMessage, error) { return m, nil } +func _Monitor_HandlerUplink_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(MonitorServer).HandlerUplink(&monitorHandlerUplinkServer{stream}) +} + +type Monitor_HandlerUplinkServer interface { + SendAndClose(*google_protobuf1.Empty) error + Recv() (*broker.DeduplicatedUplinkMessage, error) + grpc.ServerStream +} + +type monitorHandlerUplinkServer struct { + grpc.ServerStream +} + +func (x *monitorHandlerUplinkServer) SendAndClose(m *google_protobuf1.Empty) error { + return x.ServerStream.SendMsg(m) +} + +func (x *monitorHandlerUplinkServer) Recv() (*broker.DeduplicatedUplinkMessage, error) { + m := new(broker.DeduplicatedUplinkMessage) + if err := x.ServerStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func _Monitor_HandlerDownlink_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(MonitorServer).HandlerDownlink(&monitorHandlerDownlinkServer{stream}) +} + +type Monitor_HandlerDownlinkServer interface { + SendAndClose(*google_protobuf1.Empty) error + Recv() (*broker.DownlinkMessage, error) + grpc.ServerStream +} + +type monitorHandlerDownlinkServer struct { + grpc.ServerStream +} + +func (x *monitorHandlerDownlinkServer) SendAndClose(m *google_protobuf1.Empty) error { + return x.ServerStream.SendMsg(m) +} + +func (x *monitorHandlerDownlinkServer) Recv() (*broker.DownlinkMessage, error) { + m := new(broker.DownlinkMessage) + if err := x.ServerStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + var _Monitor_serviceDesc = grpc.ServiceDesc{ ServiceName: "monitor.Monitor", HandlerType: (*MonitorServer)(nil), @@ -406,6 +530,16 @@ var _Monitor_serviceDesc = grpc.ServiceDesc{ Handler: _Monitor_BrokerDownlink_Handler, ClientStreams: true, }, + { + StreamName: "HandlerUplink", + Handler: _Monitor_HandlerUplink_Handler, + ClientStreams: true, + }, + { + StreamName: "HandlerDownlink", + Handler: _Monitor_HandlerDownlink_Handler, + ClientStreams: true, + }, }, Metadata: "github.com/TheThingsNetwork/ttn/api/monitor/monitor.proto", } @@ -415,23 +549,24 @@ func init() { } var fileDescriptorMonitor = []byte{ - // 285 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x94, 0x92, 0xcd, 0x4a, 0xf4, 0x30, - 0x14, 0x86, 0xe9, 0xb7, 0xf8, 0x06, 0x82, 0x3a, 0x50, 0x50, 0xa1, 0x62, 0xc1, 0x9d, 0x20, 0x24, - 0xa0, 0xab, 0x71, 0x25, 0x63, 0xc5, 0x85, 0x8c, 0x1b, 0xc7, 0x8d, 0xbb, 0x74, 0xe6, 0x98, 0x86, - 0xfe, 0x9c, 0x92, 0x9e, 0x50, 0xbc, 0x43, 0x97, 0xde, 0x81, 0xd2, 0x2b, 0x11, 0x9b, 0x44, 0xc4, - 0x85, 0x8e, 0xab, 0x97, 0xf3, 0xd3, 0xa7, 0x4f, 0x42, 0xd8, 0x4c, 0x69, 0x2a, 0x6c, 0xce, 0x57, - 0x58, 0x8b, 0x65, 0x01, 0xcb, 0x42, 0x37, 0xaa, 0xbb, 0x05, 0xea, 0xd1, 0x94, 0x82, 0xa8, 0x11, - 0xb2, 0xd5, 0xa2, 0xc6, 0x46, 0x13, 0x9a, 0x90, 0xbc, 0x35, 0x48, 0x18, 0x4f, 0x7c, 0x99, 0x1c, - 0x86, 0x3d, 0x25, 0x09, 0x7a, 0xf9, 0x14, 0xd2, 0xed, 0x25, 0x07, 0x61, 0x6c, 0xd0, 0x12, 0x18, - 0x1f, 0xdf, 0x87, 0xb9, 0xc1, 0x12, 0x8c, 0x8f, 0x30, 0x54, 0x88, 0xaa, 0x02, 0x31, 0x56, 0xb9, - 0x7d, 0x14, 0x50, 0xb7, 0xe4, 0xb1, 0xa7, 0xaf, 0xff, 0xd8, 0x64, 0xe1, 0x0c, 0xe2, 0x73, 0xb6, - 0x7d, 0xed, 0xfe, 0x79, 0x47, 0x92, 0x6c, 0x17, 0x4f, 0x79, 0x70, 0x70, 0x8d, 0x64, 0x8f, 0x3b, - 0x16, 0x0f, 0x2c, 0x7e, 0xf5, 0xc1, 0x3a, 0x8e, 0xe2, 0x8b, 0xcf, 0x6f, 0xef, 0xdb, 0x4a, 0x37, - 0x65, 0xbc, 0xcb, 0xbd, 0xa1, 0xab, 0x17, 0xd0, 0x75, 0x52, 0xc1, 0x0f, 0x84, 0x8c, 0x4d, 0x3d, - 0x21, 0xc3, 0xbe, 0x19, 0x19, 0xfb, 0x81, 0x11, 0x3a, 0xbf, 0x53, 0x6e, 0xd8, 0xd6, 0x7c, 0x3c, - 0xbc, 0xd7, 0x38, 0xe2, 0xfe, 0x2e, 0x32, 0x58, 0xdb, 0xb6, 0xd2, 0x2b, 0x49, 0xb0, 0xde, 0x54, - 0xe9, 0x92, 0xed, 0x38, 0xd8, 0x17, 0xa3, 0x80, 0xdb, 0xd4, 0x68, 0x3e, 0x7b, 0x1e, 0xd2, 0xe8, - 0x65, 0x48, 0xa3, 0xb7, 0x21, 0x8d, 0x1e, 0x4e, 0xfe, 0xf0, 0x52, 0xf2, 0xff, 0x23, 0xec, 0xec, - 0x3d, 0x00, 0x00, 0xff, 0xff, 0xf2, 0x07, 0x36, 0xdd, 0x5f, 0x02, 0x00, 0x00, + // 302 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x92, 0x4f, 0x4a, 0xc4, 0x30, + 0x14, 0x87, 0x29, 0x82, 0x03, 0xc1, 0x71, 0xa0, 0xa0, 0x42, 0xc5, 0x82, 0x3b, 0x41, 0x48, 0x40, + 0x57, 0xe3, 0x4a, 0xc6, 0x8a, 0x82, 0x8e, 0x1b, 0xc7, 0x8d, 0xbb, 0x74, 0x1a, 0xd3, 0xd0, 0x36, + 0xaf, 0xa4, 0xaf, 0x14, 0x6f, 0xe8, 0xd2, 0x23, 0x48, 0xaf, 0xe0, 0x05, 0xc4, 0x26, 0x11, 0x71, + 0xa1, 0x23, 0xba, 0xfa, 0xf1, 0xfe, 0xf4, 0xeb, 0x97, 0x10, 0x32, 0x95, 0x0a, 0xf3, 0x36, 0xa5, + 0x4b, 0xa8, 0xd8, 0x22, 0x17, 0x8b, 0x5c, 0x69, 0xd9, 0xdc, 0x08, 0xec, 0xc0, 0x14, 0x0c, 0x51, + 0x33, 0x5e, 0x2b, 0x56, 0x81, 0x56, 0x08, 0xc6, 0x27, 0xad, 0x0d, 0x20, 0x84, 0x23, 0x57, 0x46, + 0x7b, 0x7e, 0x4f, 0x72, 0x14, 0x1d, 0x7f, 0xf4, 0x69, 0xf7, 0xa2, 0x5d, 0x3f, 0x36, 0xd0, 0xa2, + 0x30, 0x2e, 0xbe, 0x0e, 0x53, 0x03, 0x85, 0x30, 0x2e, 0xfc, 0x50, 0x02, 0xc8, 0x52, 0xb0, 0xa1, + 0x4a, 0xdb, 0x07, 0x26, 0xaa, 0x1a, 0x1d, 0xf6, 0xe8, 0x75, 0x8d, 0x8c, 0xe6, 0xd6, 0x20, 0x3c, + 0x21, 0xe3, 0x0b, 0xfb, 0xcf, 0x5b, 0xe4, 0xd8, 0x36, 0xe1, 0x84, 0x7a, 0x07, 0xdb, 0x88, 0xb6, + 0xa9, 0x65, 0x51, 0xcf, 0xa2, 0xe7, 0xef, 0xac, 0x83, 0x20, 0x3c, 0xfd, 0xf8, 0xf6, 0xae, 0x2e, + 0x95, 0x2e, 0xc2, 0x2d, 0xea, 0x0c, 0x6d, 0x3d, 0x17, 0x4d, 0xc3, 0xa5, 0xf8, 0x86, 0x90, 0x90, + 0x89, 0x23, 0x24, 0xd0, 0xe9, 0x81, 0xb1, 0xe3, 0x19, 0xbe, 0xf3, 0x33, 0xe5, 0x8a, 0x6c, 0xcc, + 0x86, 0xc3, 0x3b, 0x8d, 0x7d, 0xea, 0xee, 0x22, 0x11, 0x59, 0x5b, 0x97, 0x6a, 0xc9, 0x51, 0x64, + 0xab, 0x2a, 0x9d, 0x91, 0x4d, 0x0b, 0xfb, 0x64, 0xe4, 0x71, 0x2b, 0x1b, 0x5d, 0x93, 0xf1, 0x25, + 0xd7, 0x59, 0xf9, 0x3f, 0x4a, 0x09, 0x99, 0x38, 0xda, 0x1f, 0x9c, 0x66, 0xd3, 0xa7, 0x3e, 0x0e, + 0x9e, 0xfb, 0x38, 0x78, 0xe9, 0xe3, 0xe0, 0xfe, 0xf0, 0x17, 0xaf, 0x37, 0x5d, 0x1f, 0x60, 0xc7, + 0x6f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x63, 0x30, 0xec, 0x2e, 0xf3, 0x02, 0x00, 0x00, } diff --git a/api/monitor/monitor.proto b/api/monitor/monitor.proto index cc1e8d832..67b5c4cfa 100644 --- a/api/monitor/monitor.proto +++ b/api/monitor/monitor.proto @@ -20,4 +20,7 @@ service Monitor { rpc BrokerUplink(stream broker.DeduplicatedUplinkMessage) returns (google.protobuf.Empty); rpc BrokerDownlink(stream broker.DownlinkMessage) returns (google.protobuf.Empty); + + rpc HandlerUplink(stream broker.DeduplicatedUplinkMessage) returns (google.protobuf.Empty); + rpc HandlerDownlink(stream broker.DownlinkMessage) returns (google.protobuf.Empty); } diff --git a/api/monitor/reference_server.go b/api/monitor/reference_server.go index 14fe0fd3d..6c87511be 100644 --- a/api/monitor/reference_server.go +++ b/api/monitor/reference_server.go @@ -28,6 +28,9 @@ func NewReferenceMonitorServer(bufferSize int) *ReferenceMonitorServer { brokerUplinkMessages: make(chan *broker.DeduplicatedUplinkMessage, bufferSize), brokerDownlinkMessages: make(chan *broker.DownlinkMessage, bufferSize), + + handlerUplinkMessages: make(chan *broker.DeduplicatedUplinkMessage, bufferSize), + handlerDownlinkMessages: make(chan *broker.DownlinkMessage, bufferSize), } for i := 0; i < bufferSize; i++ { go func() { @@ -43,6 +46,10 @@ func NewReferenceMonitorServer(bufferSize int) *ReferenceMonitorServer { atomic.AddUint64(&s.metrics.brokerUplinkMessages, 1) case <-s.brokerDownlinkMessages: atomic.AddUint64(&s.metrics.brokerDownlinkMessages, 1) + case <-s.handlerUplinkMessages: + atomic.AddUint64(&s.metrics.handlerUplinkMessages, 1) + case <-s.handlerDownlinkMessages: + atomic.AddUint64(&s.metrics.handlerDownlinkMessages, 1) } } }() @@ -51,11 +58,13 @@ func NewReferenceMonitorServer(bufferSize int) *ReferenceMonitorServer { } type metrics struct { - gatewayStatuses uint64 - uplinkMessages uint64 - downlinkMessages uint64 - brokerUplinkMessages uint64 - brokerDownlinkMessages uint64 + gatewayStatuses uint64 + uplinkMessages uint64 + downlinkMessages uint64 + brokerUplinkMessages uint64 + brokerDownlinkMessages uint64 + handlerUplinkMessages uint64 + handlerDownlinkMessages uint64 } // ReferenceMonitorServer is a new reference monitor server @@ -69,6 +78,9 @@ type ReferenceMonitorServer struct { brokerUplinkMessages chan *broker.DeduplicatedUplinkMessage brokerDownlinkMessages chan *broker.DownlinkMessage + handlerUplinkMessages chan *broker.DeduplicatedUplinkMessage + handlerDownlinkMessages chan *broker.DownlinkMessage + metrics metrics } @@ -304,3 +316,99 @@ func (s *ReferenceMonitorServer) BrokerDownlink(stream Monitor_BrokerDownlinkSer } } } + +func (s *ReferenceMonitorServer) getAndAuthHandler(ctx context.Context) (string, error) { + id, err := api.IDFromContext(ctx) + if err != nil { + return "", err + } + token, err := api.TokenFromContext(ctx) + if err != nil { + return "", err + } + // Actually validate token here, if failed: return nil, grpc.Errorf(codes.Unauthenticated, "Handler Authentication Failed") + s.ctx.WithFields(log.Fields{"ID": id, "Token": token}).Info("Handler Authenticated") + return id, nil +} + +// HandlerUplink RPC +func (s *ReferenceMonitorServer) HandlerUplink(stream Monitor_HandlerUplinkServer) error { + handlerID, err := s.getAndAuthHandler(stream.Context()) + if err != nil { + return errors.NewErrPermissionDenied(err.Error()) + } + ctx := s.ctx.WithField("HandlerID", handlerID) + ctx.Info("HandlerUplink stream started") + defer func() { + if err != nil { + ctx.WithError(err).Info("HandlerUplink stream ended") + } else { + ctx.Info("HandlerUplink stream ended") + } + }() + var streamErr atomic.Value + go func() { + <-stream.Context().Done() + streamErr.Store(stream.Context().Err()) + }() + for { + streamErr := streamErr.Load() + if streamErr != nil { + return streamErr.(error) + } + msg, err := stream.Recv() + if err == io.EOF { + return stream.SendAndClose(&empty.Empty{}) + } + if err != nil { + return err + } + ctx.Info("Received DeduplicatedUplinkMessage") + select { + case s.handlerUplinkMessages <- msg: + default: + ctx.Warn("Dropping DeduplicatedUplinkMessage") + } + } +} + +// HandlerDownlink RPC +func (s *ReferenceMonitorServer) HandlerDownlink(stream Monitor_HandlerDownlinkServer) error { + handlerID, err := s.getAndAuthHandler(stream.Context()) + if err != nil { + return errors.NewErrPermissionDenied(err.Error()) + } + ctx := s.ctx.WithField("HandlerID", handlerID) + ctx.Info("HandlerUplink stream started") + defer func() { + if err != nil { + ctx.WithError(err).Info("HandlerUplink stream ended") + } else { + ctx.Info("HandlerUplink stream ended") + } + }() + var streamErr atomic.Value + go func() { + <-stream.Context().Done() + streamErr.Store(stream.Context().Err()) + }() + for { + streamErr := streamErr.Load() + if streamErr != nil { + return streamErr.(error) + } + msg, err := stream.Recv() + if err == io.EOF { + return stream.SendAndClose(&empty.Empty{}) + } + if err != nil { + return err + } + ctx.Info("Received DownlinkMessage") + select { + case s.handlerDownlinkMessages <- msg: + default: + ctx.Warn("Dropping DownlinkMessage") + } + } +} diff --git a/core/handler/downlink.go b/core/handler/downlink.go index 2f8bedbf7..889b36399 100644 --- a/core/handler/downlink.go +++ b/core/handler/downlink.go @@ -117,6 +117,9 @@ func (h *handler) HandleDownlink(appDownlink *types.DownlinkMessage, downlink *p } ctx.WithError(err).Warn("Could not handle downlink") } + if downlink != nil && h.monitorStream != nil { + h.monitorStream.Send(downlink) + } }() dev, err := h.devices.Get(appID, devID) diff --git a/core/handler/handler.go b/core/handler/handler.go index aa704eb1a..784842d2a 100644 --- a/core/handler/handler.go +++ b/core/handler/handler.go @@ -9,6 +9,7 @@ import ( "github.com/TheThingsNetwork/ttn/amqp" pb_broker "github.com/TheThingsNetwork/ttn/api/broker" pb "github.com/TheThingsNetwork/ttn/api/handler" + pb_monitor "github.com/TheThingsNetwork/ttn/api/monitor" "github.com/TheThingsNetwork/ttn/core/component" "github.com/TheThingsNetwork/ttn/core/handler/application" "github.com/TheThingsNetwork/ttn/core/handler/device" @@ -71,7 +72,8 @@ type handler struct { amqpEnabled bool amqpUp chan *types.UplinkMessage - status *status + status *status + monitorStream pb_monitor.GenericStream } var ( @@ -133,6 +135,9 @@ func (h *handler) Init(c *component.Component) error { } h.Component.SetStatus(component.StatusHealthy) + if h.Component.Monitor != nil { + h.monitorStream = h.Component.Monitor.NewHandlerStreams(h.Identity.Id, h.AccessToken) + } return nil } diff --git a/core/handler/uplink.go b/core/handler/uplink.go index cdc7979f6..f451a14c0 100644 --- a/core/handler/uplink.go +++ b/core/handler/uplink.go @@ -31,6 +31,9 @@ func (h *handler) HandleUplink(uplink *pb_broker.DeduplicatedUplinkMessage) (err } else { ctx.WithField("Duration", time.Now().Sub(start)).Info("Handled uplink") } + if uplink != nil && h.monitorStream != nil { + h.monitorStream.Send(uplink) + } }() h.status.uplink.Mark(1) From eeb999ebe120e67e128c0e54e94153f26e05311d Mon Sep 17 00:00:00 2001 From: Joscha Feth Date: Wed, 15 Mar 2017 22:07:29 +1100 Subject: [PATCH 25/45] allow ttnctl pf set to be non-interactive --- ttnctl/cmd/applications_pf_set.go | 91 ++++++++++++++++--------------- ttnctl/cmd/docs/README.md | 6 ++ 2 files changed, 54 insertions(+), 43 deletions(-) diff --git a/ttnctl/cmd/applications_pf_set.go b/ttnctl/cmd/applications_pf_set.go index 39c5fd8c3..95ea6147d 100644 --- a/ttnctl/cmd/applications_pf_set.go +++ b/ttnctl/cmd/applications_pf_set.go @@ -15,6 +15,8 @@ import ( "github.com/spf13/cobra" ) +var omitTests bool + var applicationsPayloadFunctionsSetCmd = &cobra.Command{ Use: "set [decoder/converter/validator/encoder] [file.js]", Short: "Set payload functions of an application", @@ -157,50 +159,52 @@ Function read from %s: } } - fmt.Printf("\nDo you want to test the payload functions? (Y/n)\n") - var response string - fmt.Scanln(&response) - - if strings.ToLower(response) == "y" || strings.ToLower(response) == "yes" || response == "" { - switch function { - case "decoder", "converter", "validator": - payload, err := util.ReadPayload() - if err != nil { - ctx.WithError(err).Fatal("Could not parse the payload") - } - - port, err := util.ReadPort() - if err != nil { - ctx.WithError(err).Fatal("Could not parse the port") - } - - result, err := manager.DryUplink(payload, app, uint32(port)) - if err != nil { - ctx.WithError(err).Fatal("Could not set the payload function") - } - - if !result.Valid { - ctx.Fatal("Could not set the payload function: Invalid result") + if !omitTests { + fmt.Printf("\nDo you want to test the payload functions? (Y/n)\n") + var response string + fmt.Scanln(&response) + + if strings.ToLower(response) == "y" || strings.ToLower(response) == "yes" || response == "" { + switch function { + case "decoder", "converter", "validator": + payload, err := util.ReadPayload() + if err != nil { + ctx.WithError(err).Fatal("Could not parse the payload") + } + + port, err := util.ReadPort() + if err != nil { + ctx.WithError(err).Fatal("Could not parse the port") + } + + result, err := manager.DryUplink(payload, app, uint32(port)) + if err != nil { + ctx.WithError(err).Fatal("Could not set the payload function") + } + + if !result.Valid { + ctx.Fatal("Could not set the payload function: Invalid result") + } + ctx.Infof("Function tested successfully. Object returned by the converter: %s", result.Fields) + case "encoder": + fields, err := util.ReadFields() + if err != nil { + ctx.WithError(err).Fatal("Could not parse the fields") + } + + port, err := util.ReadPort() + if err != nil { + ctx.WithError(err).Fatal("Could not parse the port") + } + + result, err := manager.DryDownlinkWithFields(fields, app, uint32(port)) + if err != nil { + ctx.WithError(err).Fatal("Could not set the payload function") + } + ctx.Infof("Function tested successfully. Encoded message: %v", result.Payload) + default: + ctx.Fatalf("Function %s does not exist", function) } - ctx.Infof("Function tested successfully. Object returned by the converter: %s", result.Fields) - case "encoder": - fields, err := util.ReadFields() - if err != nil { - ctx.WithError(err).Fatal("Could not parse the fields") - } - - port, err := util.ReadPort() - if err != nil { - ctx.WithError(err).Fatal("Could not parse the port") - } - - result, err := manager.DryDownlinkWithFields(fields, app, uint32(port)) - if err != nil { - ctx.WithError(err).Fatal("Could not set the payload function") - } - ctx.Infof("Function tested successfully. Encoded message: %v", result.Payload) - default: - ctx.Fatalf("Function %s does not exist", function) } } @@ -216,6 +220,7 @@ Function read from %s: } func init() { + applicationsPayloadFunctionsSetCmd.Flags().BoolVarP(&omitTests, "no-tests", "N", false, "omit the tests") applicationsPayloadFunctionsCmd.AddCommand(applicationsPayloadFunctionsSetCmd) } diff --git a/ttnctl/cmd/docs/README.md b/ttnctl/cmd/docs/README.md index 06ddc83c2..2cde5237d 100644 --- a/ttnctl/cmd/docs/README.md +++ b/ttnctl/cmd/docs/README.md @@ -126,6 +126,12 @@ The functions are read from the supplied file or from STDIN. **Usage:** `ttnctl applications pf set [decoder/converter/validator/encoder] [file.js]` +**Options** + +``` + -N, --no-tests omit the tests +``` + **Example** ``` From 0c29f08a370323b79aa7f11f1113743659834514 Mon Sep 17 00:00:00 2001 From: Hylke Visser Date: Mon, 20 Mar 2017 09:21:54 +0100 Subject: [PATCH 26/45] PR changes --- ttnctl/cmd/applications_pf_set.go | 6 ++---- ttnctl/cmd/docs/README.md | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/ttnctl/cmd/applications_pf_set.go b/ttnctl/cmd/applications_pf_set.go index 95ea6147d..05ad6cd46 100644 --- a/ttnctl/cmd/applications_pf_set.go +++ b/ttnctl/cmd/applications_pf_set.go @@ -15,8 +15,6 @@ import ( "github.com/spf13/cobra" ) -var omitTests bool - var applicationsPayloadFunctionsSetCmd = &cobra.Command{ Use: "set [decoder/converter/validator/encoder] [file.js]", Short: "Set payload functions of an application", @@ -159,7 +157,7 @@ Function read from %s: } } - if !omitTests { + if skipTest, _ := cmd.Flags().GetBool("skip-test"); !skipTest { fmt.Printf("\nDo you want to test the payload functions? (Y/n)\n") var response string fmt.Scanln(&response) @@ -220,7 +218,7 @@ Function read from %s: } func init() { - applicationsPayloadFunctionsSetCmd.Flags().BoolVarP(&omitTests, "no-tests", "N", false, "omit the tests") + applicationsPayloadFunctionsSetCmd.Flags().Bool("skip-test", false, "skip payload function test") applicationsPayloadFunctionsCmd.AddCommand(applicationsPayloadFunctionsSetCmd) } diff --git a/ttnctl/cmd/docs/README.md b/ttnctl/cmd/docs/README.md index 2cde5237d..9b8927c08 100644 --- a/ttnctl/cmd/docs/README.md +++ b/ttnctl/cmd/docs/README.md @@ -129,7 +129,7 @@ The functions are read from the supplied file or from STDIN. **Options** ``` - -N, --no-tests omit the tests + --skip-test skip payload function test ``` **Example** From 0ff0129d1adfc2c70191ef76bac23744276e12bc Mon Sep 17 00:00:00 2001 From: Hylke Visser Date: Mon, 20 Mar 2017 12:13:57 +0100 Subject: [PATCH 27/45] Fix incorrect validation message --- api/protocol/lorawan/validation.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/protocol/lorawan/validation.go b/api/protocol/lorawan/validation.go index 1e5b578a5..cece862f2 100644 --- a/api/protocol/lorawan/validation.go +++ b/api/protocol/lorawan/validation.go @@ -99,7 +99,7 @@ func (m *ActivationMetadata) Validate() error { // Validate implements the api.Validator interface func (m *Message) Validate() error { if m.Major != Major_LORAWAN_R1 { - return errors.NewErrInvalidArgument("Major", "invalid value "+Major_LORAWAN_R1.String()) + return errors.NewErrInvalidArgument("Major", "invalid value "+m.Major.String()) } switch m.MType { case MType_JOIN_REQUEST: From 7ca4a28ab6b4d1c4cd08bcf261c84d2644ff58c9 Mon Sep 17 00:00:00 2001 From: Hylke Visser Date: Mon, 20 Mar 2017 12:15:21 +0100 Subject: [PATCH 28/45] Don't forget to start Gateway monitor streams if there is no token change --- core/router/gateway/gateway.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/core/router/gateway/gateway.go b/core/router/gateway/gateway.go index 88a1b7846..5b6790eaa 100644 --- a/core/router/gateway/gateway.go +++ b/core/router/gateway/gateway.go @@ -51,15 +51,17 @@ func (g *Gateway) SetAuth(token string, authenticated bool) { g.mu.Lock() defer g.mu.Unlock() g.authenticated = authenticated - if token == g.token { - return - } - g.token = token if g.MonitorStream != nil { + if token == g.token { + return + } + g.Ctx.Debug("Stopping Monitor stream (token changed)") g.MonitorStream.Close() } + g.token = token if g.Monitor != nil { - g.MonitorStream = g.Monitor.NewGatewayStreams(g.ID, token) + g.Ctx.Debug("Starting Gateway Monitor Stream") + g.MonitorStream = g.Monitor.NewGatewayStreams(g.ID, g.token) } } From 8884eba673338fe54380489defb3ebaa4c440531 Mon Sep 17 00:00:00 2001 From: Hylke Visser Date: Mon, 20 Mar 2017 14:41:08 +0100 Subject: [PATCH 29/45] Rename Region to FrequencyPlan Resolves #520 --- api/gateway/gateway.pb.go | 20 +-- api/gateway/gateway.proto | 4 +- api/gateway/random.go | 3 +- api/protocol/lorawan/lorawan.pb.go | 263 +++++++++++++++-------------- api/protocol/lorawan/lorawan.proto | 6 +- core/band/band.go | 46 ++--- core/networkserver/activation.go | 2 +- core/networkserver/adr.go | 2 +- core/router/activation.go | 4 +- core/router/downlink.go | 22 +-- core/router/gateway/gateway.go | 6 +- core/router/uplink_test.go | 4 +- ttnctl/cmd/gateways_status.go | 2 +- 13 files changed, 193 insertions(+), 191 deletions(-) diff --git a/api/gateway/gateway.pb.go b/api/gateway/gateway.pb.go index 34b24c077..65d716c31 100644 --- a/api/gateway/gateway.pb.go +++ b/api/gateway/gateway.pb.go @@ -242,8 +242,8 @@ type Status struct { Platform string `protobuf:"bytes,12,opt,name=platform,proto3" json:"platform,omitempty"` ContactEmail string `protobuf:"bytes,13,opt,name=contact_email,json=contactEmail,proto3" json:"contact_email,omitempty"` Description string `protobuf:"bytes,14,opt,name=description,proto3" json:"description,omitempty"` - // The gateway's region: one of EU_863_870, US_902_928, CN_779_787, EU_433, AU_915_928, CN_470_510, AS_923, KR_920_923 - Region string `protobuf:"bytes,15,opt,name=region,proto3" json:"region,omitempty"` + // The gateway's frequency plan: one of EU_863_870, US_902_928, CN_779_787, EU_433, AU_915_928, CN_470_510, AS_923, AS_920_923, AS_923_925, KR_920_923 + FrequencyPlan string `protobuf:"bytes,15,opt,name=frequency_plan,json=frequencyPlan,proto3" json:"frequency_plan,omitempty"` // The value of Bridge is set by the Bridge Bridge string `protobuf:"bytes,16,opt,name=bridge,proto3" json:"bridge,omitempty"` // The value of Router is set by the Router @@ -316,9 +316,9 @@ func (m *Status) GetDescription() string { return "" } -func (m *Status) GetRegion() string { +func (m *Status) GetFrequencyPlan() string { if m != nil { - return m.Region + return m.FrequencyPlan } return "" } @@ -710,11 +710,11 @@ func (m *Status) MarshalTo(dAtA []byte) (int, error) { i = encodeVarintGateway(dAtA, i, uint64(len(m.Description))) i += copy(dAtA[i:], m.Description) } - if len(m.Region) > 0 { + if len(m.FrequencyPlan) > 0 { dAtA[i] = 0x7a i++ - i = encodeVarintGateway(dAtA, i, uint64(len(m.Region))) - i += copy(dAtA[i:], m.Region) + i = encodeVarintGateway(dAtA, i, uint64(len(m.FrequencyPlan))) + i += copy(dAtA[i:], m.FrequencyPlan) } if len(m.Bridge) > 0 { dAtA[i] = 0x82 @@ -983,7 +983,7 @@ func (m *Status) Size() (n int) { if l > 0 { n += 1 + l + sovGateway(uint64(l)) } - l = len(m.Region) + l = len(m.FrequencyPlan) if l > 0 { n += 1 + l + sovGateway(uint64(l)) } @@ -1799,7 +1799,7 @@ func (m *Status) Unmarshal(dAtA []byte) error { iNdEx = postIndex case 15: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Region", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field FrequencyPlan", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -1824,7 +1824,7 @@ func (m *Status) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Region = string(dAtA[iNdEx:postIndex]) + m.FrequencyPlan = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 16: if wireType != 2 { diff --git a/api/gateway/gateway.proto b/api/gateway/gateway.proto index df034ee8d..2fcd2d7e3 100644 --- a/api/gateway/gateway.proto +++ b/api/gateway/gateway.proto @@ -78,8 +78,8 @@ message Status { string contact_email = 13; string description = 14; - // The gateway's region: one of EU_863_870, US_902_928, CN_779_787, EU_433, AU_915_928, CN_470_510, AS_923, KR_920_923 - string region = 15; + // The gateway's frequency plan: one of EU_863_870, US_902_928, CN_779_787, EU_433, AU_915_928, CN_470_510, AS_923, AS_920_923, AS_923_925, KR_920_923 + string frequency_plan = 15; // The value of Bridge is set by the Bridge string bridge = 16; // The value of Router is set by the Router diff --git a/api/gateway/random.go b/api/gateway/random.go index ec60c0401..8ebe20d86 100644 --- a/api/gateway/random.go +++ b/api/gateway/random.go @@ -7,6 +7,7 @@ import ( "fmt" "math/rand" + "github.com/TheThingsNetwork/ttn/api/protocol/lorawan" "github.com/TheThingsNetwork/ttn/utils/random" ) @@ -61,7 +62,7 @@ func RandomStatus() *Status { Platform: random.String(rand.Intn(10)), ContactEmail: fmt.Sprintf("%s@%s.%s", random.String(rand.Intn(10)), random.String(rand.Intn(10)), random.String(rand.Intn(3))), Description: random.String(rand.Intn(10)), - Region: random.String(rand.Intn(10)), + FrequencyPlan: lorawan.FrequencyPlan(rand.Intn(4)).String(), Bridge: random.String(rand.Intn(10)), Router: random.ID(), Rtt: rand.Uint32(), diff --git a/api/protocol/lorawan/lorawan.pb.go b/api/protocol/lorawan/lorawan.pb.go index 02e29f3c2..43279f2f8 100644 --- a/api/protocol/lorawan/lorawan.pb.go +++ b/api/protocol/lorawan/lorawan.pb.go @@ -66,22 +66,22 @@ func (x Modulation) String() string { } func (Modulation) EnumDescriptor() ([]byte, []int) { return fileDescriptorLorawan, []int{0} } -type Region int32 +type FrequencyPlan int32 const ( - Region_EU_863_870 Region = 0 - Region_US_902_928 Region = 1 - Region_CN_779_787 Region = 2 - Region_EU_433 Region = 3 - Region_AU_915_928 Region = 4 - Region_CN_470_510 Region = 5 - Region_AS_923 Region = 6 - Region_AS_920_923 Region = 61 - Region_AS_923_925 Region = 62 - Region_KR_920_923 Region = 7 + FrequencyPlan_EU_863_870 FrequencyPlan = 0 + FrequencyPlan_US_902_928 FrequencyPlan = 1 + FrequencyPlan_CN_779_787 FrequencyPlan = 2 + FrequencyPlan_EU_433 FrequencyPlan = 3 + FrequencyPlan_AU_915_928 FrequencyPlan = 4 + FrequencyPlan_CN_470_510 FrequencyPlan = 5 + FrequencyPlan_AS_923 FrequencyPlan = 6 + FrequencyPlan_AS_920_923 FrequencyPlan = 61 + FrequencyPlan_AS_923_925 FrequencyPlan = 62 + FrequencyPlan_KR_920_923 FrequencyPlan = 7 ) -var Region_name = map[int32]string{ +var FrequencyPlan_name = map[int32]string{ 0: "EU_863_870", 1: "US_902_928", 2: "CN_779_787", @@ -93,7 +93,7 @@ var Region_name = map[int32]string{ 62: "AS_923_925", 7: "KR_920_923", } -var Region_value = map[string]int32{ +var FrequencyPlan_value = map[string]int32{ "EU_863_870": 0, "US_902_928": 1, "CN_779_787": 2, @@ -106,10 +106,10 @@ var Region_value = map[string]int32{ "KR_920_923": 7, } -func (x Region) String() string { - return proto.EnumName(Region_name, int32(x)) +func (x FrequencyPlan) String() string { + return proto.EnumName(FrequencyPlan_name, int32(x)) } -func (Region) EnumDescriptor() ([]byte, []int) { return fileDescriptorLorawan, []int{1} } +func (FrequencyPlan) EnumDescriptor() ([]byte, []int) { return fileDescriptorLorawan, []int{1} } type Major int32 @@ -171,8 +171,8 @@ type Metadata struct { // LoRa coding rate CodingRate string `protobuf:"bytes,14,opt,name=coding_rate,json=codingRate,proto3" json:"coding_rate,omitempty"` // Store the full 32 bit FCnt (deprecated; do not use) - FCnt uint32 `protobuf:"varint,15,opt,name=f_cnt,json=fCnt,proto3" json:"f_cnt,omitempty"` - Region Region `protobuf:"varint,16,opt,name=region,proto3,enum=lorawan.Region" json:"region,omitempty"` + FCnt uint32 `protobuf:"varint,15,opt,name=f_cnt,json=fCnt,proto3" json:"f_cnt,omitempty"` + FrequencyPlan FrequencyPlan `protobuf:"varint,16,opt,name=frequency_plan,json=frequencyPlan,proto3,enum=lorawan.FrequencyPlan" json:"frequency_plan,omitempty"` } func (m *Metadata) Reset() { *m = Metadata{} } @@ -215,11 +215,11 @@ func (m *Metadata) GetFCnt() uint32 { return 0 } -func (m *Metadata) GetRegion() Region { +func (m *Metadata) GetFrequencyPlan() FrequencyPlan { if m != nil { - return m.Region + return m.FrequencyPlan } - return Region_EU_863_870 + return FrequencyPlan_EU_863_870 } type TxConfiguration struct { @@ -275,15 +275,15 @@ func (m *TxConfiguration) GetFCnt() uint32 { } type ActivationMetadata struct { - AppEui *github_com_TheThingsNetwork_ttn_core_types.AppEUI `protobuf:"bytes,1,opt,name=app_eui,json=appEui,proto3,customtype=github.com/TheThingsNetwork/ttn/core/types.AppEUI" json:"app_eui,omitempty"` - DevEui *github_com_TheThingsNetwork_ttn_core_types.DevEUI `protobuf:"bytes,2,opt,name=dev_eui,json=devEui,proto3,customtype=github.com/TheThingsNetwork/ttn/core/types.DevEUI" json:"dev_eui,omitempty"` - DevAddr *github_com_TheThingsNetwork_ttn_core_types.DevAddr `protobuf:"bytes,3,opt,name=dev_addr,json=devAddr,proto3,customtype=github.com/TheThingsNetwork/ttn/core/types.DevAddr" json:"dev_addr,omitempty"` - NwkSKey *github_com_TheThingsNetwork_ttn_core_types.NwkSKey `protobuf:"bytes,4,opt,name=nwk_s_key,json=nwkSKey,proto3,customtype=github.com/TheThingsNetwork/ttn/core/types.NwkSKey" json:"nwk_s_key,omitempty"` - Rx1DrOffset uint32 `protobuf:"varint,11,opt,name=rx1_dr_offset,json=rx1DrOffset,proto3" json:"rx1_dr_offset,omitempty"` - Rx2Dr uint32 `protobuf:"varint,12,opt,name=rx2_dr,json=rx2Dr,proto3" json:"rx2_dr,omitempty"` - RxDelay uint32 `protobuf:"varint,13,opt,name=rx_delay,json=rxDelay,proto3" json:"rx_delay,omitempty"` - CfList *CFList `protobuf:"bytes,14,opt,name=cf_list,json=cfList" json:"cf_list,omitempty"` - Region Region `protobuf:"varint,15,opt,name=region,proto3,enum=lorawan.Region" json:"region,omitempty"` + AppEui *github_com_TheThingsNetwork_ttn_core_types.AppEUI `protobuf:"bytes,1,opt,name=app_eui,json=appEui,proto3,customtype=github.com/TheThingsNetwork/ttn/core/types.AppEUI" json:"app_eui,omitempty"` + DevEui *github_com_TheThingsNetwork_ttn_core_types.DevEUI `protobuf:"bytes,2,opt,name=dev_eui,json=devEui,proto3,customtype=github.com/TheThingsNetwork/ttn/core/types.DevEUI" json:"dev_eui,omitempty"` + DevAddr *github_com_TheThingsNetwork_ttn_core_types.DevAddr `protobuf:"bytes,3,opt,name=dev_addr,json=devAddr,proto3,customtype=github.com/TheThingsNetwork/ttn/core/types.DevAddr" json:"dev_addr,omitempty"` + NwkSKey *github_com_TheThingsNetwork_ttn_core_types.NwkSKey `protobuf:"bytes,4,opt,name=nwk_s_key,json=nwkSKey,proto3,customtype=github.com/TheThingsNetwork/ttn/core/types.NwkSKey" json:"nwk_s_key,omitempty"` + Rx1DrOffset uint32 `protobuf:"varint,11,opt,name=rx1_dr_offset,json=rx1DrOffset,proto3" json:"rx1_dr_offset,omitempty"` + Rx2Dr uint32 `protobuf:"varint,12,opt,name=rx2_dr,json=rx2Dr,proto3" json:"rx2_dr,omitempty"` + RxDelay uint32 `protobuf:"varint,13,opt,name=rx_delay,json=rxDelay,proto3" json:"rx_delay,omitempty"` + CfList *CFList `protobuf:"bytes,14,opt,name=cf_list,json=cfList" json:"cf_list,omitempty"` + FrequencyPlan FrequencyPlan `protobuf:"varint,15,opt,name=frequency_plan,json=frequencyPlan,proto3,enum=lorawan.FrequencyPlan" json:"frequency_plan,omitempty"` } func (m *ActivationMetadata) Reset() { *m = ActivationMetadata{} } @@ -319,11 +319,11 @@ func (m *ActivationMetadata) GetCfList() *CFList { return nil } -func (m *ActivationMetadata) GetRegion() Region { +func (m *ActivationMetadata) GetFrequencyPlan() FrequencyPlan { if m != nil { - return m.Region + return m.FrequencyPlan } - return Region_EU_863_870 + return FrequencyPlan_EU_863_870 } type Message struct { @@ -730,7 +730,7 @@ func init() { proto.RegisterType((*DLSettings)(nil), "lorawan.DLSettings") proto.RegisterType((*CFList)(nil), "lorawan.CFList") proto.RegisterEnum("lorawan.Modulation", Modulation_name, Modulation_value) - proto.RegisterEnum("lorawan.Region", Region_name, Region_value) + proto.RegisterEnum("lorawan.FrequencyPlan", FrequencyPlan_name, FrequencyPlan_value) proto.RegisterEnum("lorawan.Major", Major_name, Major_value) proto.RegisterEnum("lorawan.MType", MType_name, MType_value) } @@ -776,12 +776,12 @@ func (m *Metadata) MarshalTo(dAtA []byte) (int, error) { i++ i = encodeVarintLorawan(dAtA, i, uint64(m.FCnt)) } - if m.Region != 0 { + if m.FrequencyPlan != 0 { dAtA[i] = 0x80 i++ dAtA[i] = 0x1 i++ - i = encodeVarintLorawan(dAtA, i, uint64(m.Region)) + i = encodeVarintLorawan(dAtA, i, uint64(m.FrequencyPlan)) } return i, nil } @@ -911,10 +911,10 @@ func (m *ActivationMetadata) MarshalTo(dAtA []byte) (int, error) { } i += n5 } - if m.Region != 0 { + if m.FrequencyPlan != 0 { dAtA[i] = 0x78 i++ - i = encodeVarintLorawan(dAtA, i, uint64(m.Region)) + i = encodeVarintLorawan(dAtA, i, uint64(m.FrequencyPlan)) } return i, nil } @@ -1426,8 +1426,8 @@ func (m *Metadata) Size() (n int) { if m.FCnt != 0 { n += 1 + sovLorawan(uint64(m.FCnt)) } - if m.Region != 0 { - n += 2 + sovLorawan(uint64(m.Region)) + if m.FrequencyPlan != 0 { + n += 2 + sovLorawan(uint64(m.FrequencyPlan)) } return n } @@ -1487,8 +1487,8 @@ func (m *ActivationMetadata) Size() (n int) { l = m.CfList.Size() n += 1 + l + sovLorawan(uint64(l)) } - if m.Region != 0 { - n += 1 + sovLorawan(uint64(m.Region)) + if m.FrequencyPlan != 0 { + n += 1 + sovLorawan(uint64(m.FrequencyPlan)) } return n } @@ -1833,9 +1833,9 @@ func (m *Metadata) Unmarshal(dAtA []byte) error { } case 16: if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Region", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field FrequencyPlan", wireType) } - m.Region = 0 + m.FrequencyPlan = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowLorawan @@ -1845,7 +1845,7 @@ func (m *Metadata) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Region |= (Region(b) & 0x7F) << shift + m.FrequencyPlan |= (FrequencyPlan(b) & 0x7F) << shift if b < 0x80 { break } @@ -2285,9 +2285,9 @@ func (m *ActivationMetadata) Unmarshal(dAtA []byte) error { iNdEx = postIndex case 15: if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Region", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field FrequencyPlan", wireType) } - m.Region = 0 + m.FrequencyPlan = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowLorawan @@ -2297,7 +2297,7 @@ func (m *ActivationMetadata) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Region |= (Region(b) & 0x7F) << shift + m.FrequencyPlan |= (FrequencyPlan(b) & 0x7F) << shift if b < 0x80 { break } @@ -3841,87 +3841,88 @@ func init() { } var fileDescriptorLorawan = []byte{ - // 1311 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xcc, 0x57, 0x4b, 0x6f, 0xdb, 0xc6, - 0x16, 0x36, 0x2d, 0x52, 0x92, 0x8f, 0xfc, 0x60, 0x26, 0x09, 0xae, 0x6e, 0x12, 0xd8, 0x86, 0x70, - 0x2f, 0xae, 0x61, 0xdc, 0x5a, 0xb2, 0x14, 0xc7, 0x56, 0x8b, 0x06, 0xd0, 0xcb, 0x8d, 0x13, 0x5b, - 0x72, 0x46, 0x16, 0x52, 0x74, 0x33, 0xa0, 0xc9, 0xa1, 0x4c, 0x4b, 0x7c, 0x64, 0x34, 0xb2, 0xa5, - 0xae, 0xfb, 0x1b, 0xba, 0xee, 0xbe, 0xdb, 0x2e, 0xfa, 0x13, 0xb2, 0xcc, 0xa6, 0x5d, 0x64, 0x61, - 0x14, 0xf9, 0x25, 0xc5, 0x0c, 0xa9, 0x87, 0xe5, 0x36, 0x45, 0x9c, 0x2e, 0xba, 0xe2, 0x79, 0x7e, - 0x73, 0x66, 0xce, 0x4b, 0x82, 0x72, 0xdb, 0xe1, 0x67, 0xfd, 0xd3, 0x2d, 0xd3, 0x77, 0xb3, 0x27, - 0x67, 0xf4, 0xe4, 0xcc, 0xf1, 0xda, 0xbd, 0x3a, 0xe5, 0x97, 0x3e, 0xeb, 0x64, 0x39, 0xf7, 0xb2, - 0x46, 0xe0, 0x64, 0x03, 0xe6, 0x73, 0xdf, 0xf4, 0xbb, 0xd9, 0xae, 0xcf, 0x8c, 0x4b, 0xc3, 0x1b, - 0x7d, 0xb7, 0xa4, 0x02, 0x25, 0x22, 0xf6, 0xc1, 0x67, 0x53, 0x60, 0x6d, 0xbf, 0xed, 0x87, 0x8e, - 0xa7, 0x7d, 0x5b, 0x72, 0x92, 0x91, 0x54, 0xe8, 0x97, 0xf9, 0x55, 0x81, 0xe4, 0x11, 0xe5, 0x86, - 0x65, 0x70, 0x03, 0x15, 0x00, 0x5c, 0xdf, 0xea, 0x77, 0x0d, 0xee, 0xf8, 0x5e, 0x3a, 0xb5, 0xae, - 0x6c, 0x2c, 0xe7, 0xef, 0x6e, 0x8d, 0x0e, 0x3a, 0x1a, 0xab, 0xf0, 0x94, 0x19, 0x7a, 0x08, 0x0b, - 0xc2, 0x99, 0x30, 0x83, 0xd3, 0xf4, 0xe2, 0xba, 0xb2, 0xb1, 0x80, 0x93, 0x42, 0x80, 0x0d, 0x4e, - 0xd1, 0xbf, 0x21, 0x79, 0xea, 0xf0, 0x50, 0xb7, 0xb4, 0xae, 0x6c, 0x2c, 0xe1, 0xc4, 0xa9, 0xc3, - 0xa5, 0x6a, 0x0d, 0x52, 0xa6, 0x6f, 0x39, 0x5e, 0x3b, 0xd4, 0x2e, 0x4b, 0x4f, 0x08, 0x45, 0xd2, - 0xe0, 0x2e, 0x68, 0x36, 0x31, 0x3d, 0x9e, 0x5e, 0x91, 0x8e, 0xaa, 0x5d, 0xf1, 0x38, 0xfa, 0x1f, - 0xc4, 0x19, 0x6d, 0x8b, 0xf0, 0x74, 0x19, 0xde, 0xca, 0x38, 0x3c, 0x2c, 0xc5, 0x38, 0x52, 0x67, - 0x7e, 0x52, 0x60, 0xe5, 0x64, 0x50, 0xf1, 0x3d, 0xdb, 0x69, 0xf7, 0x59, 0x18, 0xea, 0x3f, 0xff, - 0x7e, 0x99, 0xef, 0x54, 0x40, 0x25, 0x93, 0x3b, 0x17, 0xf2, 0xf0, 0x71, 0x66, 0xea, 0x90, 0x30, - 0x82, 0x80, 0xd0, 0xbe, 0x93, 0x56, 0xd6, 0x95, 0x8d, 0xc5, 0xf2, 0xce, 0xbb, 0xab, 0xb5, 0xed, - 0xbf, 0xaa, 0x1b, 0xd3, 0x67, 0x34, 0xcb, 0x87, 0x01, 0xed, 0x6d, 0x95, 0x82, 0xa0, 0xd6, 0x3a, - 0xc0, 0x71, 0x23, 0x08, 0x6a, 0x7d, 0x47, 0xe0, 0x59, 0xf4, 0x42, 0xe2, 0xcd, 0xdf, 0x0a, 0xaf, - 0x4a, 0x2f, 0x24, 0x9e, 0x45, 0x2f, 0x04, 0xde, 0x4b, 0x48, 0x0a, 0x3c, 0xc3, 0xb2, 0x58, 0x3a, - 0x26, 0x01, 0x9f, 0xbc, 0xbb, 0x5a, 0xcb, 0x7f, 0x1c, 0x60, 0xc9, 0xb2, 0x18, 0x16, 0x71, 0x09, - 0x02, 0x61, 0x58, 0xf0, 0x2e, 0x3b, 0xa4, 0x47, 0x3a, 0x74, 0x98, 0x56, 0x6f, 0x85, 0x59, 0xbf, - 0xec, 0x34, 0x5f, 0xd0, 0x21, 0x4e, 0x78, 0x21, 0x81, 0x32, 0xb0, 0xc4, 0x06, 0xdb, 0xc4, 0x62, - 0xc4, 0xb7, 0xed, 0x1e, 0xe5, 0xb2, 0x06, 0x96, 0x70, 0x8a, 0x0d, 0xb6, 0xab, 0xac, 0x21, 0x45, - 0xe8, 0x3e, 0xc4, 0xd9, 0x20, 0x4f, 0x2c, 0x26, 0x93, 0xbd, 0x84, 0x35, 0x36, 0xc8, 0x57, 0x99, - 0xc8, 0x34, 0x1b, 0x10, 0x8b, 0x76, 0x8d, 0xe1, 0x28, 0xd3, 0x6c, 0x50, 0x15, 0x2c, 0xda, 0x80, - 0x84, 0x69, 0x93, 0xae, 0xd3, 0xe3, 0x32, 0xcb, 0xa9, 0xa9, 0xa2, 0xac, 0xec, 0x1f, 0x3a, 0x3d, - 0x8e, 0xe3, 0xa6, 0x2d, 0xbe, 0x53, 0xd5, 0xbb, 0xf2, 0xe1, 0xea, 0xfd, 0x71, 0x1e, 0x12, 0x47, - 0xb4, 0xd7, 0x33, 0xda, 0x14, 0xfd, 0x1f, 0x34, 0x97, 0x9c, 0x59, 0x4c, 0x66, 0x3e, 0x95, 0x5f, - 0x9a, 0x14, 0xec, 0xb3, 0x2a, 0x2e, 0x27, 0xdf, 0x5c, 0xad, 0xcd, 0xbd, 0xbd, 0x5a, 0x53, 0xb0, - 0xea, 0x3e, 0xb3, 0x18, 0xd2, 0x21, 0xe6, 0x3a, 0x66, 0x98, 0x55, 0x2c, 0x48, 0xf4, 0x04, 0x52, - 0xae, 0x61, 0x92, 0xc0, 0x18, 0x76, 0x7d, 0xc3, 0x92, 0xe9, 0x49, 0x4d, 0x97, 0x7d, 0xa9, 0x72, - 0x1c, 0xaa, 0x9e, 0xcd, 0x61, 0x70, 0x0d, 0x33, 0xe2, 0x50, 0x03, 0xee, 0x9d, 0xfb, 0x8e, 0x47, - 0x18, 0x7d, 0xdd, 0xa7, 0x3d, 0x3e, 0x06, 0x50, 0x25, 0xc0, 0xc3, 0x31, 0xc0, 0x73, 0xdf, 0xf1, - 0x70, 0x68, 0x33, 0x01, 0x42, 0xe7, 0x37, 0xa4, 0xe8, 0x10, 0xee, 0x4a, 0x40, 0xc3, 0x34, 0x69, - 0x30, 0xc1, 0xd3, 0x24, 0xde, 0x83, 0x6b, 0x78, 0x25, 0x69, 0x32, 0x81, 0xbb, 0x73, 0x3e, 0x2b, - 0x2c, 0x2f, 0x40, 0x22, 0x22, 0x33, 0x4d, 0x50, 0xc5, 0x5b, 0xa0, 0xff, 0x42, 0xdc, 0x25, 0x22, - 0xf5, 0xf2, 0xa9, 0x96, 0xf3, 0xcb, 0x93, 0x4b, 0x9e, 0x0c, 0x03, 0x8a, 0x35, 0x57, 0x7c, 0xd0, - 0x7f, 0x40, 0x73, 0x8d, 0x73, 0x9f, 0xc9, 0x47, 0xba, 0x66, 0x25, 0xa4, 0x38, 0x54, 0x66, 0x18, - 0xc0, 0xe4, 0x69, 0x44, 0x12, 0xec, 0x3f, 0x4c, 0xc2, 0xfe, 0x4c, 0x12, 0x6c, 0x91, 0x84, 0xfb, - 0x10, 0xb7, 0x49, 0xe0, 0x33, 0x2e, 0x8f, 0xd0, 0xb0, 0x66, 0x1f, 0xfb, 0x8c, 0x8b, 0x91, 0x60, - 0x33, 0xf7, 0x5a, 0x26, 0x16, 0x31, 0xd8, 0xcc, 0x1d, 0x5d, 0xe4, 0x17, 0x05, 0x54, 0x01, 0x88, - 0x5a, 0x53, 0xfd, 0x14, 0x36, 0xfc, 0xe7, 0xe2, 0x88, 0x4f, 0xed, 0xa9, 0xac, 0x88, 0xcb, 0xe4, - 0xac, 0x2b, 0xe3, 0x4a, 0x4d, 0x5d, 0x7d, 0xbf, 0xc2, 0x59, 0x77, 0xea, 0x1e, 0x9a, 0x2d, 0x04, - 0x93, 0x19, 0x15, 0x9b, 0x9a, 0xc1, 0x39, 0x81, 0xe2, 0x07, 0xbc, 0x97, 0x56, 0xd7, 0x63, 0xb3, - 0xb5, 0x54, 0xf1, 0x5d, 0xd7, 0xf0, 0xac, 0xb2, 0x2a, 0xa0, 0xb0, 0x66, 0x37, 0x02, 0xde, 0xcb, - 0x9c, 0x81, 0x26, 0x0f, 0x10, 0xd5, 0x69, 0x44, 0x57, 0x4a, 0x62, 0x41, 0xa2, 0x55, 0x48, 0x19, - 0x16, 0x23, 0x86, 0xd9, 0x11, 0x85, 0x26, 0xe3, 0x4a, 0xe2, 0x05, 0xc3, 0x62, 0x25, 0xb3, 0x83, - 0xe9, 0x6b, 0xe9, 0x61, 0x76, 0xe4, 0xf9, 0xc2, 0xc3, 0xec, 0x88, 0x81, 0x6c, 0x93, 0x80, 0x7a, - 0x62, 0x90, 0xca, 0x62, 0x4c, 0xe2, 0xa4, 0x7d, 0x1c, 0xf2, 0x99, 0x3d, 0x99, 0xb5, 0x28, 0x08, - 0xe1, 0x6c, 0x3a, 0x96, 0x3c, 0x6e, 0x09, 0x0b, 0x12, 0xa5, 0x21, 0x31, 0x7a, 0xfe, 0xb0, 0x45, - 0x46, 0x6c, 0xe6, 0xfb, 0x79, 0x40, 0x37, 0x4b, 0x19, 0xe1, 0xd9, 0xc9, 0x5b, 0x8c, 0x12, 0xf1, - 0x09, 0xd3, 0x17, 0xcf, 0x4e, 0xdf, 0xdb, 0x60, 0xce, 0x4c, 0xe0, 0xaf, 0x61, 0x41, 0x60, 0x7a, - 0xbe, 0x67, 0xd2, 0x68, 0x04, 0x7f, 0x11, 0xa1, 0x16, 0x3e, 0x0e, 0xb5, 0x2e, 0x20, 0xb0, 0xa8, - 0x3f, 0x49, 0x65, 0x7e, 0x8e, 0xc1, 0x9d, 0x1b, 0x3d, 0x89, 0x1e, 0xc1, 0x02, 0xf5, 0x4c, 0x36, - 0x0c, 0x38, 0x0d, 0x1f, 0x78, 0x11, 0x4f, 0x04, 0x22, 0x1a, 0xf1, 0x6a, 0x61, 0x34, 0xf3, 0xb7, - 0x8e, 0xa6, 0x14, 0x04, 0x51, 0x34, 0x46, 0x44, 0xa1, 0x06, 0xc4, 0x3d, 0xca, 0x89, 0x13, 0xb5, - 0x4f, 0x79, 0x2f, 0x82, 0xcd, 0x7d, 0xcc, 0x5e, 0xa0, 0xfc, 0xa0, 0x8a, 0x35, 0x8f, 0xf2, 0x03, - 0xeb, 0x5a, 0xab, 0xa9, 0x7f, 0x5f, 0xab, 0x3d, 0x85, 0x94, 0xd5, 0x25, 0x3d, 0xca, 0xb9, 0xf0, - 0x8a, 0x86, 0xdc, 0xa4, 0x53, 0xaa, 0x87, 0xcd, 0x48, 0x35, 0xd5, 0x74, 0x60, 0x75, 0x47, 0xd2, - 0x6b, 0xfb, 0x26, 0xfe, 0xa7, 0xfb, 0x26, 0xf1, 0xc1, 0x7d, 0x93, 0xf9, 0x0a, 0x60, 0x72, 0xd0, - 0xcd, 0xed, 0xa7, 0x7c, 0x68, 0xfb, 0xcd, 0x4f, 0x6d, 0xbf, 0xcc, 0x23, 0x88, 0x87, 0xd0, 0x08, - 0x81, 0x6a, 0x8b, 0x46, 0x55, 0xd6, 0x63, 0x72, 0x20, 0x30, 0xfa, 0x7a, 0x73, 0x0d, 0x60, 0xf2, - 0xe3, 0x09, 0x25, 0x41, 0x3d, 0x6c, 0xe0, 0x92, 0x3e, 0x87, 0x12, 0x10, 0xdb, 0x6f, 0xbe, 0xd0, - 0x95, 0xcd, 0x1f, 0x14, 0x88, 0x87, 0x1b, 0x0e, 0x2d, 0x03, 0xd4, 0x5a, 0x64, 0xef, 0x49, 0x81, - 0xec, 0xed, 0xe6, 0xf4, 0x39, 0xc1, 0xb7, 0x9a, 0xa4, 0x98, 0xcb, 0x93, 0x62, 0x7e, 0x4f, 0x57, - 0x04, 0x5f, 0xa9, 0x93, 0xdd, 0xdd, 0x22, 0xd9, 0xdd, 0xdb, 0xd5, 0xe7, 0x11, 0x40, 0xbc, 0xd6, - 0x22, 0x8f, 0x0b, 0x05, 0x3d, 0x26, 0x74, 0xa5, 0x16, 0x29, 0x6e, 0xef, 0x48, 0x5b, 0x35, 0xb2, - 0x7d, 0xbc, 0x9b, 0x23, 0x3b, 0xdb, 0x39, 0x5d, 0x13, 0xb6, 0xa5, 0x26, 0x29, 0xe6, 0x0b, 0x7a, - 0x5c, 0xda, 0x0a, 0x3a, 0x27, 0xf9, 0x2f, 0xc7, 0x7c, 0x81, 0x14, 0xf3, 0x3b, 0xfa, 0x53, 0xc1, - 0xbf, 0xc0, 0x63, 0x7d, 0x62, 0xf3, 0x5f, 0xa0, 0xc9, 0xf1, 0x2f, 0x14, 0x22, 0xfc, 0x57, 0xa5, - 0x3a, 0xc1, 0xdb, 0xfa, 0xdc, 0xe6, 0xb7, 0xa0, 0xc9, 0xed, 0x81, 0x74, 0x58, 0x7c, 0xde, 0x38, - 0xa8, 0x13, 0x5c, 0x7b, 0xd9, 0xaa, 0x35, 0x4f, 0xf4, 0x39, 0xb4, 0x02, 0x29, 0x29, 0x29, 0x55, - 0x2a, 0xb5, 0xe3, 0x13, 0x5d, 0x41, 0x08, 0x96, 0x5b, 0xf5, 0x4a, 0xa3, 0xbe, 0x7f, 0x80, 0x8f, - 0x6a, 0x55, 0xd2, 0x3a, 0xd6, 0xe7, 0xd1, 0x3d, 0xd0, 0xa7, 0x65, 0xd5, 0xc6, 0xab, 0xba, 0x1e, - 0x13, 0x60, 0xd7, 0xec, 0x54, 0xe1, 0x3b, 0x63, 0xa5, 0x95, 0xcb, 0x6f, 0xde, 0xaf, 0x2a, 0x6f, - 0xdf, 0xaf, 0x2a, 0xbf, 0xbd, 0x5f, 0x55, 0xbe, 0x79, 0x7c, 0x9b, 0xff, 0x09, 0xa7, 0x71, 0x29, - 0x29, 0xfc, 0x1e, 0x00, 0x00, 0xff, 0xff, 0xd4, 0xd0, 0x69, 0x31, 0x66, 0x0c, 0x00, 0x00, + // 1327 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xcc, 0x57, 0xcb, 0x52, 0x1b, 0xc7, + 0x1a, 0x66, 0xd0, 0x8c, 0x24, 0x7e, 0x21, 0x18, 0xb7, 0xed, 0x73, 0x74, 0x6c, 0x17, 0x50, 0xaa, + 0x73, 0xaa, 0x28, 0xea, 0x84, 0x8b, 0x64, 0x0c, 0x24, 0x65, 0x57, 0xe9, 0x46, 0x8c, 0x0d, 0x12, + 0x6e, 0x50, 0x39, 0x95, 0x4d, 0x57, 0x33, 0xd3, 0x03, 0x83, 0x34, 0x17, 0xb7, 0x9a, 0x8b, 0xf2, + 0x20, 0x59, 0xe4, 0x05, 0xb2, 0xc8, 0x36, 0x8b, 0x3c, 0x82, 0x97, 0xde, 0x64, 0xe3, 0x05, 0x95, + 0xf2, 0x3a, 0x0f, 0x91, 0xea, 0x9e, 0xd1, 0x15, 0xc7, 0x29, 0x70, 0x16, 0x59, 0xcd, 0x7f, 0xfd, + 0xfa, 0xef, 0xfe, 0x6f, 0x12, 0x94, 0x8f, 0x5d, 0x71, 0x72, 0x76, 0xb4, 0x6c, 0x05, 0xde, 0xca, + 0xe1, 0x09, 0x3b, 0x3c, 0x71, 0xfd, 0xe3, 0x4e, 0x9d, 0x89, 0x8b, 0x80, 0xb7, 0x56, 0x84, 0xf0, + 0x57, 0x68, 0xe8, 0xae, 0x84, 0x3c, 0x10, 0x81, 0x15, 0xb4, 0x57, 0xda, 0x01, 0xa7, 0x17, 0xd4, + 0xef, 0x7d, 0x97, 0x95, 0x02, 0xa5, 0x62, 0xf6, 0xc1, 0x17, 0x43, 0x60, 0xc7, 0xc1, 0x71, 0x10, + 0x39, 0x1e, 0x9d, 0x39, 0x8a, 0x53, 0x8c, 0xa2, 0x22, 0xbf, 0xfc, 0xef, 0x1a, 0xa4, 0xf7, 0x98, + 0xa0, 0x36, 0x15, 0x14, 0x15, 0x01, 0xbc, 0xc0, 0x3e, 0x6b, 0x53, 0xe1, 0x06, 0x7e, 0x2e, 0xb3, + 0xa0, 0x2d, 0xce, 0x14, 0xee, 0x2e, 0xf7, 0x0e, 0xda, 0xeb, 0xab, 0xf0, 0x90, 0x19, 0x7a, 0x08, + 0x53, 0xd2, 0x99, 0x70, 0x2a, 0x58, 0x6e, 0x7a, 0x41, 0x5b, 0x9c, 0xc2, 0x69, 0x29, 0xc0, 0x54, + 0x30, 0xf4, 0x1f, 0x48, 0x1f, 0xb9, 0x22, 0xd2, 0x65, 0x17, 0xb4, 0xc5, 0x2c, 0x4e, 0x1d, 0xb9, + 0x42, 0xa9, 0xe6, 0x21, 0x63, 0x05, 0xb6, 0xeb, 0x1f, 0x47, 0xda, 0x19, 0xe5, 0x09, 0x91, 0x48, + 0x19, 0xdc, 0x05, 0xc3, 0x21, 0x96, 0x2f, 0x72, 0xb3, 0xca, 0x51, 0x77, 0x2a, 0xbe, 0x40, 0x4f, + 0x61, 0xc6, 0xe1, 0xec, 0xcd, 0x19, 0xf3, 0xad, 0x2e, 0x09, 0xdb, 0xd4, 0xcf, 0x99, 0x2a, 0xcc, + 0x7f, 0xf5, 0xc3, 0xdc, 0xee, 0xa9, 0xf7, 0xdb, 0xd4, 0xc7, 0x59, 0x67, 0x98, 0xcd, 0xff, 0xac, + 0xc1, 0xec, 0xe1, 0x65, 0x25, 0xf0, 0x1d, 0xf7, 0xf8, 0x8c, 0x47, 0x17, 0xf8, 0xe7, 0xdf, 0x3a, + 0xff, 0x83, 0x0e, 0xa8, 0x64, 0x09, 0xf7, 0x5c, 0x1d, 0xde, 0xcf, 0x57, 0x1d, 0x52, 0x34, 0x0c, + 0x09, 0x3b, 0x73, 0x73, 0xda, 0x82, 0xb6, 0x38, 0x5d, 0x5e, 0x7f, 0x7f, 0x35, 0xbf, 0xf6, 0x57, + 0xd5, 0x64, 0x05, 0x9c, 0xad, 0x88, 0x6e, 0xc8, 0x3a, 0xcb, 0xa5, 0x30, 0xac, 0x35, 0x77, 0x70, + 0x92, 0x86, 0x61, 0xed, 0xcc, 0x95, 0x78, 0x36, 0x3b, 0x57, 0x78, 0x93, 0xb7, 0xc2, 0xab, 0xb2, + 0x73, 0x85, 0x67, 0xb3, 0x73, 0x89, 0xf7, 0x0a, 0xd2, 0x12, 0x8f, 0xda, 0x36, 0xcf, 0x25, 0x14, + 0xe0, 0x93, 0xf7, 0x57, 0xf3, 0x85, 0x9b, 0x01, 0x96, 0x6c, 0x9b, 0x63, 0x19, 0x97, 0x24, 0x10, + 0x86, 0x29, 0xff, 0xa2, 0x45, 0x3a, 0xa4, 0xc5, 0xba, 0x39, 0xfd, 0x56, 0x98, 0xf5, 0x8b, 0xd6, + 0xc1, 0x4b, 0xd6, 0xc5, 0x29, 0x3f, 0x22, 0x50, 0x1e, 0xb2, 0xfc, 0x72, 0x8d, 0xd8, 0x9c, 0x04, + 0x8e, 0xd3, 0x61, 0x42, 0xd5, 0x40, 0x16, 0x67, 0xf8, 0xe5, 0x5a, 0x95, 0x37, 0x94, 0x08, 0xdd, + 0x87, 0x24, 0xbf, 0x2c, 0x10, 0x9b, 0xab, 0x64, 0x67, 0xb1, 0xc1, 0x2f, 0x0b, 0x55, 0x2e, 0x33, + 0xcd, 0x2f, 0x89, 0xcd, 0xda, 0xb4, 0xdb, 0xcb, 0x34, 0xbf, 0xac, 0x4a, 0x16, 0x2d, 0x42, 0xca, + 0x72, 0x48, 0xdb, 0xed, 0x08, 0x95, 0xe5, 0x4c, 0x61, 0xb6, 0x5f, 0x53, 0x95, 0xed, 0x5d, 0xb7, + 0x23, 0x70, 0xd2, 0x72, 0xe4, 0xf7, 0x23, 0x35, 0x3d, 0x7b, 0x93, 0x9a, 0xfe, 0x69, 0x12, 0x52, + 0x7b, 0xac, 0xd3, 0xa1, 0xc7, 0x0c, 0xfd, 0x1f, 0x0c, 0x8f, 0x9c, 0xd8, 0x5c, 0xd5, 0x43, 0xa6, + 0x90, 0x1d, 0x94, 0xf1, 0xf3, 0x2a, 0x2e, 0xa7, 0xdf, 0x5e, 0xcd, 0x4f, 0xbc, 0xbb, 0x9a, 0xd7, + 0xb0, 0xee, 0x3d, 0xb7, 0x39, 0x32, 0x21, 0xe1, 0xb9, 0x56, 0x94, 0x6b, 0x2c, 0x49, 0xf4, 0x04, + 0x32, 0x1e, 0xb5, 0x48, 0x48, 0xbb, 0xed, 0x80, 0xda, 0x2a, 0x69, 0x99, 0xe1, 0x66, 0x28, 0x55, + 0xf6, 0x23, 0xd5, 0xf3, 0x09, 0x0c, 0x1e, 0xb5, 0x62, 0x0e, 0x35, 0xe0, 0xde, 0x69, 0xe0, 0xfa, + 0x44, 0x05, 0xd6, 0x11, 0x7d, 0x00, 0x5d, 0x01, 0x3c, 0xec, 0x03, 0xbc, 0x08, 0x5c, 0x1f, 0x47, + 0x36, 0x03, 0x20, 0x74, 0x7a, 0x4d, 0x8a, 0x76, 0xe1, 0xae, 0x02, 0xa4, 0x96, 0xc5, 0xc2, 0x01, + 0x9e, 0xa1, 0xf0, 0x1e, 0x8c, 0xe0, 0x95, 0x94, 0xc9, 0x00, 0xee, 0xce, 0xe9, 0xb8, 0xb0, 0x3c, + 0x05, 0xa9, 0x98, 0xcc, 0x1f, 0x80, 0x2e, 0xdf, 0x02, 0xfd, 0x0f, 0x92, 0x1e, 0x91, 0x05, 0xa1, + 0x9e, 0x6a, 0xa6, 0x30, 0x33, 0xb8, 0xe4, 0x61, 0x37, 0x64, 0xd8, 0xf0, 0xe4, 0x07, 0xfd, 0x17, + 0x0c, 0x8f, 0x9e, 0x06, 0x5c, 0x3d, 0xd2, 0x88, 0x95, 0x94, 0xe2, 0x48, 0x99, 0xe7, 0x00, 0x83, + 0xa7, 0x91, 0x49, 0x70, 0x3e, 0x9a, 0x84, 0xed, 0xb1, 0x24, 0x38, 0x32, 0x09, 0xf7, 0x21, 0xe9, + 0x90, 0x30, 0xe0, 0x42, 0x1d, 0x61, 0x60, 0xc3, 0xd9, 0x0f, 0xb8, 0x90, 0x83, 0xc2, 0xe1, 0xde, + 0x48, 0x26, 0xa6, 0x31, 0x38, 0xdc, 0xeb, 0x5d, 0xe4, 0x57, 0x0d, 0x74, 0x09, 0x88, 0x9a, 0x43, + 0x5d, 0x16, 0x8d, 0x81, 0x2f, 0xe5, 0x11, 0x9f, 0xdb, 0x69, 0x2b, 0x32, 0x2e, 0x4b, 0xf0, 0xb6, + 0x8a, 0x2b, 0x33, 0x74, 0xf5, 0xed, 0x8a, 0xe0, 0xed, 0xa1, 0x7b, 0x18, 0x8e, 0x14, 0x0c, 0x26, + 0x57, 0x62, 0x68, 0x5e, 0xaf, 0x4a, 0x94, 0x20, 0x14, 0x9d, 0x9c, 0xbe, 0x90, 0x18, 0xaf, 0xa5, + 0x4a, 0xe0, 0x79, 0xd4, 0xb7, 0xcb, 0xba, 0x84, 0xc2, 0x86, 0xd3, 0x08, 0x45, 0x27, 0x7f, 0x02, + 0x86, 0x3a, 0x40, 0x56, 0x27, 0x8d, 0xaf, 0x94, 0xc6, 0x92, 0x44, 0x73, 0x90, 0xa1, 0x36, 0x27, + 0xd4, 0x6a, 0xc9, 0x42, 0x53, 0x71, 0xa5, 0xf1, 0x14, 0xb5, 0x79, 0xc9, 0x6a, 0x61, 0xf6, 0x46, + 0x79, 0x58, 0x2d, 0x75, 0xbe, 0xf4, 0xb0, 0x5a, 0x72, 0x4c, 0x3b, 0x24, 0x64, 0xbe, 0x1c, 0xaf, + 0xaa, 0x18, 0xd3, 0x38, 0xed, 0xec, 0x47, 0x7c, 0x7e, 0x53, 0x65, 0x2d, 0x0e, 0x42, 0x3a, 0x5b, + 0xae, 0xad, 0x8e, 0xcb, 0x62, 0x49, 0xa2, 0x1c, 0xa4, 0x7a, 0xcf, 0x1f, 0xb5, 0x48, 0x8f, 0xcd, + 0x7f, 0x3f, 0x09, 0xe8, 0x7a, 0x29, 0x23, 0x3c, 0x3e, 0x8f, 0xb7, 0xe2, 0x44, 0x7c, 0xc6, 0x4c, + 0xc6, 0xe3, 0x33, 0xf9, 0x36, 0x98, 0x63, 0x73, 0xf9, 0x1b, 0x98, 0x92, 0x98, 0x7e, 0xe0, 0x5b, + 0x2c, 0x1e, 0xcc, 0x5f, 0xc5, 0xa8, 0xc5, 0x9b, 0xa1, 0xd6, 0x25, 0x04, 0x96, 0xf5, 0xa7, 0xa8, + 0xfc, 0x2f, 0x09, 0xb8, 0x73, 0xad, 0x27, 0xd1, 0x23, 0x98, 0x62, 0xbe, 0xc5, 0xbb, 0xa1, 0x60, + 0xd1, 0x03, 0x4f, 0xe3, 0x81, 0x40, 0x46, 0x23, 0x5f, 0x2d, 0x8a, 0x66, 0xf2, 0xd6, 0xd1, 0x94, + 0xc2, 0x30, 0x8e, 0x86, 0xc6, 0x14, 0x6a, 0x40, 0xd2, 0x67, 0x82, 0xb8, 0x71, 0xfb, 0x94, 0x37, + 0x63, 0xd8, 0xd5, 0x9b, 0x6c, 0x0b, 0x26, 0x76, 0xaa, 0xd8, 0xf0, 0x99, 0xd8, 0xb1, 0x47, 0x5a, + 0x4d, 0xff, 0xfb, 0x5a, 0xed, 0x19, 0x64, 0xec, 0x36, 0xe9, 0x30, 0x21, 0xa4, 0x57, 0x3c, 0xe4, + 0x06, 0x9d, 0x52, 0xdd, 0x3d, 0x88, 0x55, 0x43, 0x4d, 0x07, 0x76, 0xbb, 0x27, 0x1d, 0xd9, 0x42, + 0xc9, 0x3f, 0xdd, 0x42, 0xa9, 0x4f, 0x6e, 0xa1, 0xfc, 0xd7, 0x00, 0x83, 0x83, 0xae, 0xef, 0x44, + 0xed, 0x53, 0x3b, 0x71, 0x72, 0x68, 0x27, 0xe6, 0x1f, 0x41, 0x32, 0x82, 0x46, 0x08, 0x74, 0xb9, + 0xaa, 0x72, 0xda, 0x42, 0x42, 0x0d, 0x04, 0xce, 0xde, 0x2c, 0xcd, 0x03, 0x0c, 0x7e, 0x52, 0xa1, + 0x34, 0xe8, 0xbb, 0x0d, 0x5c, 0x32, 0x27, 0x50, 0x0a, 0x12, 0xdb, 0x07, 0x2f, 0x4d, 0x6d, 0xe9, + 0x47, 0x0d, 0xb2, 0x23, 0xfb, 0x0e, 0xcd, 0x00, 0xd4, 0x9a, 0x64, 0xf3, 0x49, 0x91, 0x6c, 0x6e, + 0xac, 0x9a, 0x13, 0x92, 0x6f, 0x1e, 0x90, 0xad, 0xd5, 0x02, 0xd9, 0x2a, 0x6c, 0x9a, 0x9a, 0xe4, + 0x2b, 0x75, 0xb2, 0xb1, 0xb1, 0x45, 0x36, 0x36, 0x37, 0xcc, 0x49, 0x04, 0x90, 0xac, 0x35, 0xc9, + 0xe3, 0x62, 0xd1, 0x4c, 0x48, 0x5d, 0xa9, 0x49, 0xb6, 0xd6, 0xd6, 0x95, 0xad, 0x1e, 0xdb, 0x3e, + 0xde, 0x58, 0x25, 0xeb, 0x6b, 0xab, 0xa6, 0x21, 0x6d, 0x4b, 0x07, 0x64, 0xab, 0x50, 0x34, 0x93, + 0xca, 0x56, 0xd2, 0xab, 0x8a, 0x7f, 0xda, 0xe7, 0x8b, 0x64, 0xab, 0xb0, 0x6e, 0x3e, 0x93, 0xfc, + 0x4b, 0xdc, 0xd7, 0xa7, 0x96, 0xfe, 0x0d, 0x86, 0xda, 0x02, 0x52, 0x21, 0x6f, 0xf1, 0xba, 0x54, + 0x27, 0x78, 0xcd, 0x9c, 0x58, 0xfa, 0x0e, 0x0c, 0xb5, 0x44, 0x90, 0x09, 0xd3, 0x2f, 0x1a, 0x3b, + 0x75, 0x82, 0x6b, 0xaf, 0x9a, 0xb5, 0x83, 0x43, 0x73, 0x02, 0xcd, 0x42, 0x46, 0x49, 0x4a, 0x95, + 0x4a, 0x6d, 0xff, 0xd0, 0xd4, 0x10, 0x82, 0x99, 0x66, 0xbd, 0xd2, 0xa8, 0x6f, 0xef, 0xe0, 0xbd, + 0x5a, 0x95, 0x34, 0xf7, 0xcd, 0x49, 0x74, 0x0f, 0xcc, 0x61, 0x59, 0xb5, 0xf1, 0xba, 0x6e, 0x26, + 0x24, 0xd8, 0x88, 0x9d, 0x2e, 0x7d, 0xc7, 0xac, 0x8c, 0x72, 0xf9, 0xed, 0x87, 0x39, 0xed, 0xdd, + 0x87, 0x39, 0xed, 0xb7, 0x0f, 0x73, 0xda, 0xb7, 0x8f, 0x6f, 0xf3, 0xd7, 0xe2, 0x28, 0xa9, 0x24, + 0xc5, 0x3f, 0x02, 0x00, 0x00, 0xff, 0xff, 0x5c, 0xfb, 0x13, 0x90, 0x99, 0x0c, 0x00, 0x00, } diff --git a/api/protocol/lorawan/lorawan.proto b/api/protocol/lorawan/lorawan.proto index b079ebcd4..eb44a1a06 100644 --- a/api/protocol/lorawan/lorawan.proto +++ b/api/protocol/lorawan/lorawan.proto @@ -26,7 +26,7 @@ message Metadata { // Store the full 32 bit FCnt (deprecated; do not use) uint32 f_cnt = 15; - Region region = 16; + FrequencyPlan frequency_plan = 16; } message TxConfiguration { @@ -52,10 +52,10 @@ message ActivationMetadata { uint32 rx2_dr = 12; uint32 rx_delay = 13; CFList cf_list = 14; - Region region = 15; + FrequencyPlan frequency_plan = 15; } -enum Region { +enum FrequencyPlan { EU_863_870 = 0; US_902_928 = 1; diff --git a/core/band/band.go b/core/band/band.go index ba656acce..59163f8ad 100644 --- a/core/band/band.go +++ b/core/band/band.go @@ -49,9 +49,9 @@ func Guess(frequency uint64) string { switch { case frequency == 923200000 && frequency <= 923400000: // not considering AS_920_923 and AS_923_925 because we're not sure - return pb_lorawan.Region_AS_923.String() + return pb_lorawan.FrequencyPlan_AS_923.String() case frequency == 922100000 || frequency == 922300000 || frequency == 922500000: - return pb_lorawan.Region_KR_920_923.String() + return pb_lorawan.FrequencyPlan_KR_920_923.String() } // Existing Channels @@ -69,7 +69,7 @@ func Get(region string) (frequencyPlan FrequencyPlan, err error) { return fp, nil } switch region { - case pb_lorawan.Region_EU_863_870.String(): + case pb_lorawan.FrequencyPlan_EU_863_870.String(): frequencyPlan.Band, err = lora.GetConfig(lora.EU_863_870, false, lorawan.DwellTimeNoLimit) // TTN uses SF9BW125 in RX2 frequencyPlan.RX2DataRate = 3 @@ -88,19 +88,19 @@ func Get(region string) (frequencyPlan FrequencyPlan, err error) { frequencyPlan.DownlinkChannels = frequencyPlan.UplinkChannels frequencyPlan.CFList = &lorawan.CFList{867100000, 867300000, 867500000, 867700000, 867900000} frequencyPlan.ADR = &ADRConfig{MinDataRate: 0, MaxDataRate: 5, MinTXPower: 2, MaxTXPower: 14} - case pb_lorawan.Region_US_902_928.String(): + case pb_lorawan.FrequencyPlan_US_902_928.String(): frequencyPlan.Band, err = lora.GetConfig(lora.US_902_928, false, lorawan.DwellTime400ms) - case pb_lorawan.Region_CN_779_787.String(): + case pb_lorawan.FrequencyPlan_CN_779_787.String(): frequencyPlan.Band, err = lora.GetConfig(lora.CN_779_787, false, lorawan.DwellTimeNoLimit) - case pb_lorawan.Region_EU_433.String(): + case pb_lorawan.FrequencyPlan_EU_433.String(): frequencyPlan.Band, err = lora.GetConfig(lora.EU_433, false, lorawan.DwellTimeNoLimit) - case pb_lorawan.Region_AU_915_928.String(): + case pb_lorawan.FrequencyPlan_AU_915_928.String(): frequencyPlan.Band, err = lora.GetConfig(lora.AU_915_928, false, lorawan.DwellTime400ms) - case pb_lorawan.Region_CN_470_510.String(): + case pb_lorawan.FrequencyPlan_CN_470_510.String(): frequencyPlan.Band, err = lora.GetConfig(lora.CN_470_510, false, lorawan.DwellTimeNoLimit) - case pb_lorawan.Region_AS_923.String(): + case pb_lorawan.FrequencyPlan_AS_923.String(): frequencyPlan.Band, err = lora.GetConfig(lora.AS_923, false, lorawan.DwellTime400ms) - case pb_lorawan.Region_AS_920_923.String(): + case pb_lorawan.FrequencyPlan_AS_920_923.String(): frequencyPlan.Band, err = lora.GetConfig(lora.AS_923, false, lorawan.DwellTime400ms) frequencyPlan.UplinkChannels = []lora.Channel{ lora.Channel{Frequency: 923200000, DataRates: []int{0, 1, 2, 3, 4, 5}}, @@ -116,7 +116,7 @@ func Get(region string) (frequencyPlan FrequencyPlan, err error) { } frequencyPlan.DownlinkChannels = frequencyPlan.UplinkChannels frequencyPlan.CFList = &lorawan.CFList{922200000, 922400000, 922600000, 922800000, 923000000} - case pb_lorawan.Region_AS_923_925.String(): + case pb_lorawan.FrequencyPlan_AS_923_925.String(): frequencyPlan.Band, err = lora.GetConfig(lora.AS_923, false, lorawan.DwellTime400ms) frequencyPlan.UplinkChannels = []lora.Channel{ lora.Channel{Frequency: 923200000, DataRates: []int{0, 1, 2, 3, 4, 5}}, @@ -132,7 +132,7 @@ func Get(region string) (frequencyPlan FrequencyPlan, err error) { } frequencyPlan.DownlinkChannels = frequencyPlan.UplinkChannels frequencyPlan.CFList = &lorawan.CFList{923600000, 923800000, 924000000, 924200000, 924400000} - case pb_lorawan.Region_KR_920_923.String(): + case pb_lorawan.FrequencyPlan_KR_920_923.String(): frequencyPlan.Band, err = lora.GetConfig(lora.KR_920_923, false, lorawan.DwellTimeNoLimit) // TTN frequency plan includes extra channels next to the default channels: frequencyPlan.UplinkChannels = []lora.Channel{ @@ -158,17 +158,17 @@ var channels map[int]string func init() { frequencyPlans = make(map[string]FrequencyPlan) channels = make(map[int]string) - for _, r := range []pb_lorawan.Region{ // ordering is important here - pb_lorawan.Region_EU_863_870, - pb_lorawan.Region_US_902_928, - pb_lorawan.Region_CN_779_787, - pb_lorawan.Region_EU_433, - pb_lorawan.Region_AS_923, - pb_lorawan.Region_AS_920_923, - pb_lorawan.Region_AS_923_925, - pb_lorawan.Region_KR_920_923, - pb_lorawan.Region_AU_915_928, - pb_lorawan.Region_CN_470_510, + for _, r := range []pb_lorawan.FrequencyPlan{ // ordering is important here + pb_lorawan.FrequencyPlan_EU_863_870, + pb_lorawan.FrequencyPlan_US_902_928, + pb_lorawan.FrequencyPlan_CN_779_787, + pb_lorawan.FrequencyPlan_EU_433, + pb_lorawan.FrequencyPlan_AS_923, + pb_lorawan.FrequencyPlan_AS_920_923, + pb_lorawan.FrequencyPlan_AS_923_925, + pb_lorawan.FrequencyPlan_KR_920_923, + pb_lorawan.FrequencyPlan_AU_915_928, + pb_lorawan.FrequencyPlan_CN_470_510, } { region := r.String() frequencyPlans[region], _ = Get(region) diff --git a/core/networkserver/activation.go b/core/networkserver/activation.go index d7a6b348c..3566bf19b 100644 --- a/core/networkserver/activation.go +++ b/core/networkserver/activation.go @@ -135,7 +135,7 @@ func (n *networkServer) HandleActivate(activation *pb_handler.DeviceActivationRe dev.FCntDown = 0 dev.ADR = device.ADRSettings{Band: dev.ADR.Band, Margin: dev.ADR.Margin} - if band := meta.GetLorawan().GetRegion().String(); band != "" { + if band := meta.GetLorawan().GetFrequencyPlan().String(); band != "" { dev.ADR.Band = band } diff --git a/core/networkserver/adr.go b/core/networkserver/adr.go index ef4b22a10..74a7c9862 100644 --- a/core/networkserver/adr.go +++ b/core/networkserver/adr.go @@ -56,7 +56,7 @@ func (n *networkServer) handleUplinkADR(message *pb_broker.DeduplicatedUplinkMes n.Ctx.WithError(err).Error("Could not push frame for device") } if dev.ADR.Band == "" { - dev.ADR.Band = message.GetProtocolMetadata().GetLorawan().GetRegion().String() + dev.ADR.Band = message.GetProtocolMetadata().GetLorawan().GetFrequencyPlan().String() } dataRate := message.GetProtocolMetadata().GetLorawan().GetDataRate() diff --git a/core/router/activation.go b/core/router/activation.go index 21ddb8c0d..36dc81223 100644 --- a/core/router/activation.go +++ b/core/router/activation.go @@ -86,7 +86,7 @@ func (r *router) HandleActivation(gatewayID string, activation *pb.DeviceActivat if err != nil { return nil, err } - region := status.Region + region := status.FrequencyPlan if region == "" { region = band.Guess(uplink.GatewayMetadata.Frequency) } @@ -95,7 +95,7 @@ func (r *router) HandleActivation(gatewayID string, activation *pb.DeviceActivat return nil, err } lorawan := request.ActivationMetadata.GetLorawan() - lorawan.Region = pb_lorawan.Region(pb_lorawan.Region_value[region]) + lorawan.FrequencyPlan = pb_lorawan.FrequencyPlan(pb_lorawan.FrequencyPlan_value[region]) lorawan.Rx1DrOffset = 0 lorawan.Rx2Dr = uint32(band.RX2DataRate) lorawan.RxDelay = uint32(band.ReceiveDelay1.Seconds()) diff --git a/core/router/downlink.go b/core/router/downlink.go index 81ed1f891..ac1754b6b 100644 --- a/core/router/downlink.go +++ b/core/router/downlink.go @@ -102,15 +102,15 @@ func (r *router) buildDownlinkOptions(uplink *pb.UplinkMessage, isActivation boo return // We can't handle any other protocols than LoRaWAN yet } - region := gatewayStatus.Region - if region == "" { - region = band.Guess(uplink.GatewayMetadata.Frequency) + frequencyPlan := gatewayStatus.FrequencyPlan + if frequencyPlan == "" { + frequencyPlan = band.Guess(uplink.GatewayMetadata.Frequency) } - band, err := band.Get(region) + band, err := band.Get(frequencyPlan) if err != nil { - return // We can't handle this region + return // We can't handle this frequency plan } - if region == "EU_863_870" && isActivation { + if frequencyPlan == "EU_863_870" && isActivation { band.RX2DataRate = 0 } @@ -122,7 +122,7 @@ func (r *router) buildDownlinkOptions(uplink *pb.UplinkMessage, isActivation boo // Configuration for RX2 buildRX2 := func() (*pb_broker.DownlinkOption, error) { option := r.buildDownlinkOption(gateway.ID, band) - if region == "EU_863_870" { + if frequencyPlan == "EU_863_870" { option.GatewayConfig.Power = 27 // The EU RX2 frequency allows up to 27dBm } if isActivation { @@ -199,9 +199,9 @@ func (r *router) buildDownlinkOptions(uplink *pb.UplinkMessage, isActivation boo func computeDownlinkScores(gateway *gateway.Gateway, uplink *pb.UplinkMessage, options []*pb_broker.DownlinkOption) { gatewayStatus, _ := gateway.Status.Get() // This just returns empty if non-existing - region := gatewayStatus.Region - if region == "" { - region = band.Guess(uplink.GatewayMetadata.Frequency) + frequencyPlan := gatewayStatus.FrequencyPlan + if frequencyPlan == "" { + frequencyPlan = band.Guess(uplink.GatewayMetadata.Frequency) } gatewayRx, _ := gateway.Utilization.Get() @@ -262,7 +262,7 @@ func computeDownlinkScores(gateway *gateway.Gateway, uplink *pb.UplinkMessage, o utilizationScore += math.Min((channelTx+channelRx)*200, 20) / 2 // 10% utilization = 10 (max) // European Duty Cycle - if region == "EU_863_870" { + if frequencyPlan == "EU_863_870" { var duty float64 switch { case freq >= 863000000 && freq < 868000000: diff --git a/core/router/gateway/gateway.go b/core/router/gateway/gateway.go index 5b6790eaa..412fc6bdc 100644 --- a/core/router/gateway/gateway.go +++ b/core/router/gateway/gateway.go @@ -97,10 +97,10 @@ func (g *Gateway) HandleUplink(uplink *pb_router.UplinkMessage) (err error) { if uplink.GatewayMetadata.Gps == nil { uplink.GatewayMetadata.Gps = status.GetGps() } - // Inject Gateway region - if region, ok := pb_lorawan.Region_value[status.Region]; ok { + // Inject Gateway frequency plan + if frequencyPlan, ok := pb_lorawan.FrequencyPlan_value[status.FrequencyPlan]; ok { if lorawan := uplink.GetProtocolMetadata().GetLorawan(); lorawan != nil { - lorawan.Region = pb_lorawan.Region(region) + lorawan.FrequencyPlan = pb_lorawan.FrequencyPlan(frequencyPlan) } } } diff --git a/core/router/uplink_test.go b/core/router/uplink_test.go index 9d748e65a..d515667ce 100644 --- a/core/router/uplink_test.go +++ b/core/router/uplink_test.go @@ -19,10 +19,10 @@ import ( ) // newReferenceGateway returns a default gateway -func newReferenceGateway(t *testing.T, region string) *gateway.Gateway { +func newReferenceGateway(t *testing.T, frequencyPlan string) *gateway.Gateway { gtw := gateway.NewGateway(GetLogger(t, "ReferenceGateway"), "eui-0102030405060708") gtw.Status.Update(&pb_gateway.Status{ - Region: region, + FrequencyPlan: frequencyPlan, }) return gtw } diff --git a/ttnctl/cmd/gateways_status.go b/ttnctl/cmd/gateways_status.go index cafcaaaa0..bc301257e 100644 --- a/ttnctl/cmd/gateways_status.go +++ b/ttnctl/cmd/gateways_status.go @@ -63,7 +63,7 @@ var gatewaysStatusCmd = &cobra.Command{ printKV("Description", resp.Status.Description) printKV("Platform", resp.Status.Platform) printKV("Contact email", resp.Status.ContactEmail) - printKV("Region", resp.Status.Region) + printKV("Frequency Plan", resp.Status.FrequencyPlan) printKV("Bridge", resp.Status.Bridge) printKV("IP Address", strings.Join(resp.Status.Ip, ", ")) printKV("GPS coordinates", func() interface{} { From 07fe4b9af929bba14c0e501c365458f4dd65bb40 Mon Sep 17 00:00:00 2001 From: Hylke Visser Date: Mon, 20 Mar 2017 14:53:13 +0100 Subject: [PATCH 30/45] Add new gateway fields --- api/gateway/gateway.pb.go | 482 ++++++++++++++++++++++++++++++++++---- api/gateway/gateway.proto | 19 ++ 2 files changed, 450 insertions(+), 51 deletions(-) diff --git a/api/gateway/gateway.pb.go b/api/gateway/gateway.pb.go index 34b24c077..63c0a6999 100644 --- a/api/gateway/gateway.pb.go +++ b/api/gateway/gateway.pb.go @@ -82,9 +82,12 @@ type RxMetadata struct { // Timestamp (uptime of LoRa module) in microseconds with rollover Timestamp uint32 `protobuf:"varint,11,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // Time in Unix nanoseconds - Time int64 `protobuf:"varint,12,opt,name=time,proto3" json:"time,omitempty"` - RfChain uint32 `protobuf:"varint,21,opt,name=rf_chain,json=rfChain,proto3" json:"rf_chain,omitempty"` - Channel uint32 `protobuf:"varint,22,opt,name=channel,proto3" json:"channel,omitempty"` + Time int64 `protobuf:"varint,12,opt,name=time,proto3" json:"time,omitempty"` + // Encrypted time from the Gateway FPGA + EncryptedTime []byte `protobuf:"bytes,13,opt,name=encrypted_time,json=encryptedTime,proto3" json:"encrypted_time,omitempty"` + RfChain uint32 `protobuf:"varint,21,opt,name=rf_chain,json=rfChain,proto3" json:"rf_chain,omitempty"` + Channel uint32 `protobuf:"varint,22,opt,name=channel,proto3" json:"channel,omitempty"` + Antennas []*RxMetadata_Antenna `protobuf:"bytes,30,rep,name=antennas" json:"antennas,omitempty"` // Frequency in Hz Frequency uint64 `protobuf:"varint,31,opt,name=frequency,proto3" json:"frequency,omitempty"` // Received signal strength in dBm @@ -127,6 +130,13 @@ func (m *RxMetadata) GetTime() int64 { return 0 } +func (m *RxMetadata) GetEncryptedTime() []byte { + if m != nil { + return m.EncryptedTime + } + return nil +} + func (m *RxMetadata) GetRfChain() uint32 { if m != nil { return m.RfChain @@ -141,6 +151,13 @@ func (m *RxMetadata) GetChannel() uint32 { return 0 } +func (m *RxMetadata) GetAntennas() []*RxMetadata_Antenna { + if m != nil { + return m.Antennas + } + return nil +} + func (m *RxMetadata) GetFrequency() uint64 { if m != nil { return m.Frequency @@ -169,6 +186,57 @@ func (m *RxMetadata) GetGps() *GPSMetadata { return nil } +type RxMetadata_Antenna struct { + Antenna uint32 `protobuf:"varint,1,opt,name=antenna,proto3" json:"antenna,omitempty"` + Channel uint32 `protobuf:"varint,2,opt,name=channel,proto3" json:"channel,omitempty"` + // Received signal strength in dBm + Rssi float32 `protobuf:"fixed32,3,opt,name=rssi,proto3" json:"rssi,omitempty"` + // Signal-to-noise-ratio in dB + Snr float32 `protobuf:"fixed32,4,opt,name=snr,proto3" json:"snr,omitempty"` + // Encrypted time from the Gateway FPGA + EncryptedTime []byte `protobuf:"bytes,10,opt,name=encrypted_time,json=encryptedTime,proto3" json:"encrypted_time,omitempty"` +} + +func (m *RxMetadata_Antenna) Reset() { *m = RxMetadata_Antenna{} } +func (m *RxMetadata_Antenna) String() string { return proto.CompactTextString(m) } +func (*RxMetadata_Antenna) ProtoMessage() {} +func (*RxMetadata_Antenna) Descriptor() ([]byte, []int) { return fileDescriptorGateway, []int{1, 0} } + +func (m *RxMetadata_Antenna) GetAntenna() uint32 { + if m != nil { + return m.Antenna + } + return 0 +} + +func (m *RxMetadata_Antenna) GetChannel() uint32 { + if m != nil { + return m.Channel + } + return 0 +} + +func (m *RxMetadata_Antenna) GetRssi() float32 { + if m != nil { + return m.Rssi + } + return 0 +} + +func (m *RxMetadata_Antenna) GetSnr() float32 { + if m != nil { + return m.Snr + } + return 0 +} + +func (m *RxMetadata_Antenna) GetEncryptedTime() []byte { + if m != nil { + return m.EncryptedTime + } + return nil +} + type TxConfiguration struct { // Timestamp (uptime of LoRa module) in microseconds with rollover Timestamp uint32 `protobuf:"varint,11,opt,name=timestamp,proto3" json:"timestamp,omitempty"` @@ -446,6 +514,7 @@ func (m *Status_OSMetrics) GetTemperature() float32 { func init() { proto.RegisterType((*GPSMetadata)(nil), "gateway.GPSMetadata") proto.RegisterType((*RxMetadata)(nil), "gateway.RxMetadata") + proto.RegisterType((*RxMetadata_Antenna)(nil), "gateway.RxMetadata.Antenna") proto.RegisterType((*TxConfiguration)(nil), "gateway.TxConfiguration") proto.RegisterType((*Status)(nil), "gateway.Status") proto.RegisterType((*Status_OSMetrics)(nil), "gateway.Status.OSMetrics") @@ -529,6 +598,12 @@ func (m *RxMetadata) MarshalTo(dAtA []byte) (int, error) { i++ i = encodeVarintGateway(dAtA, i, uint64(m.Time)) } + if len(m.EncryptedTime) > 0 { + dAtA[i] = 0x6a + i++ + i = encodeVarintGateway(dAtA, i, uint64(len(m.EncryptedTime))) + i += copy(dAtA[i:], m.EncryptedTime) + } if m.RfChain != 0 { dAtA[i] = 0xa8 i++ @@ -543,6 +618,20 @@ func (m *RxMetadata) MarshalTo(dAtA []byte) (int, error) { i++ i = encodeVarintGateway(dAtA, i, uint64(m.Channel)) } + if len(m.Antennas) > 0 { + for _, msg := range m.Antennas { + dAtA[i] = 0xf2 + i++ + dAtA[i] = 0x1 + i++ + i = encodeVarintGateway(dAtA, i, uint64(msg.Size())) + n, err := msg.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n + } + } if m.Frequency != 0 { dAtA[i] = 0xf8 i++ @@ -579,6 +668,50 @@ func (m *RxMetadata) MarshalTo(dAtA []byte) (int, error) { return i, nil } +func (m *RxMetadata_Antenna) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RxMetadata_Antenna) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.Antenna != 0 { + dAtA[i] = 0x8 + i++ + i = encodeVarintGateway(dAtA, i, uint64(m.Antenna)) + } + if m.Channel != 0 { + dAtA[i] = 0x10 + i++ + i = encodeVarintGateway(dAtA, i, uint64(m.Channel)) + } + if m.Rssi != 0 { + dAtA[i] = 0x1d + i++ + i = encodeFixed32Gateway(dAtA, i, uint32(math.Float32bits(float32(m.Rssi)))) + } + if m.Snr != 0 { + dAtA[i] = 0x25 + i++ + i = encodeFixed32Gateway(dAtA, i, uint32(math.Float32bits(float32(m.Snr)))) + } + if len(m.EncryptedTime) > 0 { + dAtA[i] = 0x52 + i++ + i = encodeVarintGateway(dAtA, i, uint64(len(m.EncryptedTime))) + i += copy(dAtA[i:], m.EncryptedTime) + } + return i, nil +} + func (m *TxConfiguration) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -907,12 +1040,22 @@ func (m *RxMetadata) Size() (n int) { if m.Time != 0 { n += 1 + sovGateway(uint64(m.Time)) } + l = len(m.EncryptedTime) + if l > 0 { + n += 1 + l + sovGateway(uint64(l)) + } if m.RfChain != 0 { n += 2 + sovGateway(uint64(m.RfChain)) } if m.Channel != 0 { n += 2 + sovGateway(uint64(m.Channel)) } + if len(m.Antennas) > 0 { + for _, e := range m.Antennas { + l = e.Size() + n += 2 + l + sovGateway(uint64(l)) + } + } if m.Frequency != 0 { n += 2 + sovGateway(uint64(m.Frequency)) } @@ -929,6 +1072,28 @@ func (m *RxMetadata) Size() (n int) { return n } +func (m *RxMetadata_Antenna) Size() (n int) { + var l int + _ = l + if m.Antenna != 0 { + n += 1 + sovGateway(uint64(m.Antenna)) + } + if m.Channel != 0 { + n += 1 + sovGateway(uint64(m.Channel)) + } + if m.Rssi != 0 { + n += 5 + } + if m.Snr != 0 { + n += 5 + } + l = len(m.EncryptedTime) + if l > 0 { + n += 1 + l + sovGateway(uint64(l)) + } + return n +} + func (m *TxConfiguration) Size() (n int) { var l int _ = l @@ -1290,6 +1455,37 @@ func (m *RxMetadata) Unmarshal(dAtA []byte) error { break } } + case 13: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field EncryptedTime", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthGateway + } + postIndex := iNdEx + byteLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.EncryptedTime = append(m.EncryptedTime[:0], dAtA[iNdEx:postIndex]...) + if m.EncryptedTime == nil { + m.EncryptedTime = []byte{} + } + iNdEx = postIndex case 21: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field RfChain", wireType) @@ -1328,6 +1524,37 @@ func (m *RxMetadata) Unmarshal(dAtA []byte) error { break } } + case 30: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Antennas", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGateway + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Antennas = append(m.Antennas, &RxMetadata_Antenna{}) + if err := m.Antennas[len(m.Antennas)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex case 31: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Frequency", wireType) @@ -1429,6 +1656,153 @@ func (m *RxMetadata) Unmarshal(dAtA []byte) error { } return nil } +func (m *RxMetadata_Antenna) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Antenna: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Antenna: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Antenna", wireType) + } + m.Antenna = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Antenna |= (uint32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Channel", wireType) + } + m.Channel = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Channel |= (uint32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 5 { + return fmt.Errorf("proto: wrong wireType = %d for field Rssi", wireType) + } + var v uint32 + if (iNdEx + 4) > l { + return io.ErrUnexpectedEOF + } + iNdEx += 4 + v = uint32(dAtA[iNdEx-4]) + v |= uint32(dAtA[iNdEx-3]) << 8 + v |= uint32(dAtA[iNdEx-2]) << 16 + v |= uint32(dAtA[iNdEx-1]) << 24 + m.Rssi = float32(math.Float32frombits(v)) + case 4: + if wireType != 5 { + return fmt.Errorf("proto: wrong wireType = %d for field Snr", wireType) + } + var v uint32 + if (iNdEx + 4) > l { + return io.ErrUnexpectedEOF + } + iNdEx += 4 + v = uint32(dAtA[iNdEx-4]) + v |= uint32(dAtA[iNdEx-3]) << 8 + v |= uint32(dAtA[iNdEx-2]) << 16 + v |= uint32(dAtA[iNdEx-1]) << 24 + m.Snr = float32(math.Float32frombits(v)) + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field EncryptedTime", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthGateway + } + postIndex := iNdEx + byteLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.EncryptedTime = append(m.EncryptedTime[:0], dAtA[iNdEx:postIndex]...) + if m.EncryptedTime == nil { + m.EncryptedTime = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGateway(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGateway + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *TxConfiguration) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -2310,52 +2684,58 @@ func init() { } var fileDescriptorGateway = []byte{ - // 748 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x94, 0x54, 0x4f, 0x93, 0xdb, 0x34, - 0x14, 0x1f, 0x3b, 0xff, 0x95, 0x26, 0xbb, 0x55, 0x9b, 0x54, 0xdd, 0x81, 0xc5, 0x84, 0x01, 0x52, - 0x16, 0x92, 0x59, 0x3a, 0x39, 0xf4, 0x4a, 0x61, 0x98, 0x3d, 0xc0, 0x76, 0xd4, 0x9c, 0xb8, 0x78, - 0x14, 0x5b, 0x71, 0x34, 0xb1, 0x25, 0x23, 0xcb, 0x4d, 0x96, 0x8f, 0xc3, 0x85, 0xaf, 0xd2, 0x23, - 0x1f, 0x81, 0xd9, 0x03, 0x9f, 0x83, 0xd1, 0xf3, 0x9f, 0x75, 0x99, 0xc2, 0x4e, 0x4f, 0x79, 0xbf, - 0x3f, 0xf2, 0x7b, 0x4f, 0xef, 0x45, 0xe8, 0x45, 0x24, 0xcc, 0x2e, 0xdf, 0x2c, 0x02, 0x95, 0x2c, - 0xd7, 0x3b, 0xbe, 0xde, 0x09, 0x19, 0x65, 0x3f, 0x73, 0x73, 0x50, 0x7a, 0xbf, 0x34, 0x46, 0x2e, - 0x59, 0x2a, 0x96, 0x11, 0x33, 0xfc, 0xc0, 0x6e, 0xaa, 0xdf, 0x45, 0xaa, 0x95, 0x51, 0xb8, 0x57, - 0xc2, 0xb3, 0x6f, 0x1a, 0xdf, 0x88, 0x54, 0xa4, 0x96, 0xa0, 0x6f, 0xf2, 0x2d, 0x20, 0x00, 0x10, - 0x15, 0xe7, 0x66, 0x07, 0x34, 0xfc, 0xf1, 0xd5, 0xeb, 0x9f, 0xb8, 0x61, 0x21, 0x33, 0x0c, 0x63, - 0xd4, 0x36, 0x22, 0xe1, 0xc4, 0xf1, 0x9c, 0x79, 0x8b, 0x42, 0x8c, 0xcf, 0x50, 0x3f, 0x66, 0x46, - 0x98, 0x3c, 0xe4, 0xc4, 0xf5, 0x9c, 0xb9, 0x4b, 0x6b, 0x8c, 0x3f, 0x42, 0x83, 0x58, 0xc9, 0xa8, - 0x10, 0x5b, 0x20, 0xde, 0x11, 0xf6, 0x24, 0x8b, 0xcb, 0x93, 0x6d, 0xcf, 0x99, 0x77, 0x68, 0x8d, - 0x67, 0x7f, 0xb8, 0x08, 0xd1, 0x63, 0x9d, 0xf8, 0x63, 0x84, 0xca, 0x0e, 0x7c, 0x11, 0x42, 0xfa, - 0x01, 0x1d, 0x94, 0xcc, 0x55, 0x88, 0xbf, 0x44, 0x27, 0x95, 0x6c, 0x74, 0x9e, 0x19, 0x1e, 0x42, - 0x29, 0x7d, 0x3a, 0x2e, 0xe9, 0x75, 0xc1, 0xda, 0x82, 0x6c, 0xd1, 0x99, 0x61, 0x49, 0x4a, 0x86, - 0x9e, 0x33, 0x1f, 0xd1, 0x3b, 0xa2, 0x6e, 0xef, 0x41, 0xa3, 0xbd, 0xa7, 0xa8, 0xaf, 0xb7, 0x7e, - 0xb0, 0x63, 0x42, 0x92, 0x09, 0x1c, 0xe8, 0xe9, 0xed, 0x4b, 0x0b, 0x31, 0x41, 0xbd, 0x60, 0xc7, - 0xa4, 0xe4, 0x31, 0x99, 0x16, 0x4a, 0x09, 0x6d, 0x9a, 0xad, 0xe6, 0xbf, 0xe6, 0x5c, 0x06, 0x37, - 0xe4, 0x13, 0xcf, 0x99, 0xb7, 0xe9, 0x1d, 0x61, 0xd3, 0xe8, 0x2c, 0x13, 0xc4, 0x83, 0x0b, 0x81, - 0x18, 0x9f, 0xa2, 0x56, 0x26, 0x35, 0xf9, 0x14, 0x28, 0x1b, 0xe2, 0x2f, 0x50, 0x2b, 0x4a, 0x33, - 0xf2, 0xcc, 0x73, 0xe6, 0xc3, 0x6f, 0x1f, 0x2f, 0xaa, 0x79, 0x36, 0xc6, 0x41, 0xad, 0x61, 0xf6, - 0xb7, 0x83, 0x4e, 0xd6, 0xc7, 0x97, 0x4a, 0x6e, 0x45, 0x94, 0x6b, 0x66, 0x84, 0x92, 0xf7, 0xb4, - 0xf9, 0x3f, 0x2d, 0xbd, 0x53, 0xf8, 0xf4, 0xdf, 0x85, 0x3f, 0x46, 0x9d, 0x54, 0x1d, 0xb8, 0x26, - 0x4f, 0x60, 0x5a, 0x05, 0xc0, 0x2b, 0x34, 0x4d, 0x55, 0xcc, 0xb4, 0xf8, 0x0d, 0x92, 0xfb, 0x42, - 0xbe, 0xe1, 0x3a, 0x13, 0x4a, 0x42, 0xe7, 0x7d, 0x3a, 0x69, 0xaa, 0x57, 0x95, 0x88, 0x97, 0xe8, - 0x51, 0xfd, 0x65, 0x3f, 0xe4, 0x6f, 0x04, 0xe8, 0x70, 0x29, 0x23, 0x8a, 0x6b, 0xe9, 0xfb, 0x4a, - 0x99, 0xfd, 0xde, 0x41, 0xdd, 0xd7, 0x86, 0x99, 0x3c, 0x7b, 0xb7, 0x3f, 0xe7, 0xbf, 0xc6, 0xe8, - 0x36, 0xc6, 0xf8, 0x9e, 0x0d, 0x69, 0xbd, 0x77, 0x43, 0xc6, 0xc8, 0x15, 0xf6, 0xce, 0x5a, 0xf3, - 0x01, 0x75, 0x45, 0x6a, 0x97, 0x34, 0x8d, 0x99, 0xd9, 0x2a, 0x9d, 0xc0, 0x5e, 0x0c, 0x68, 0x8d, - 0xf1, 0x67, 0x68, 0x14, 0x28, 0x69, 0x58, 0x60, 0x7c, 0x9e, 0x30, 0x11, 0x93, 0x11, 0x18, 0x1e, - 0x94, 0xe4, 0x0f, 0x96, 0xc3, 0x1e, 0x1a, 0x86, 0x3c, 0x0b, 0xb4, 0x48, 0xa1, 0xbf, 0x31, 0x58, - 0x9a, 0x14, 0x9e, 0xa2, 0xae, 0xe6, 0x91, 0x15, 0x4f, 0x40, 0x2c, 0x91, 0xe5, 0x37, 0x5a, 0x84, - 0x11, 0x27, 0xa7, 0x05, 0x5f, 0x20, 0xf0, 0xab, 0xdc, 0x70, 0x4d, 0x1e, 0x96, 0x7e, 0x40, 0xd5, - 0xc6, 0x4c, 0xee, 0xd9, 0x18, 0xbb, 0x6b, 0xda, 0x18, 0x98, 0xce, 0x88, 0xda, 0x10, 0x3f, 0x42, - 0x1d, 0x7d, 0xf4, 0x85, 0x84, 0x6d, 0x1b, 0xd1, 0xb6, 0x3e, 0x5e, 0xc9, 0x92, 0x54, 0x7b, 0xf2, - 0x55, 0x45, 0x5e, 0xef, 0x2d, 0x69, 0xc0, 0x79, 0x51, 0x90, 0xa6, 0x74, 0x1a, 0x70, 0x7e, 0x5d, - 0x91, 0xd7, 0x7b, 0xfc, 0x0c, 0xb9, 0x2a, 0x23, 0xcf, 0xa1, 0x98, 0xa7, 0x75, 0x31, 0xc5, 0x00, - 0x17, 0xd7, 0xb6, 0x24, 0x2d, 0x82, 0x8c, 0xba, 0x2a, 0x3b, 0x7b, 0xeb, 0xa0, 0x41, 0xcd, 0xe0, - 0x09, 0xea, 0xc6, 0x8a, 0x85, 0xfe, 0x25, 0x4c, 0xd6, 0xa5, 0x1d, 0x8b, 0x2e, 0x6b, 0x7a, 0x55, - 0xbe, 0x32, 0x40, 0xaf, 0xf0, 0x13, 0xd4, 0x2b, 0xdc, 0xab, 0xf2, 0x81, 0x01, 0xd7, 0xe5, 0x0a, - 0x7f, 0x8e, 0xc6, 0x41, 0x9a, 0xfb, 0x29, 0xd7, 0x01, 0x97, 0x86, 0x45, 0x1c, 0xfe, 0x08, 0x2e, - 0x1d, 0x05, 0x69, 0xfe, 0xaa, 0x26, 0xf1, 0x05, 0x7a, 0x98, 0xf0, 0x44, 0xe9, 0x9b, 0xa6, 0x73, - 0x02, 0xce, 0xd3, 0x42, 0x68, 0x98, 0x3d, 0x34, 0x34, 0x3c, 0x49, 0xb9, 0x66, 0x26, 0xd7, 0x1c, - 0x6e, 0xd0, 0xa5, 0x4d, 0xea, 0xbb, 0x17, 0x6f, 0x6f, 0xcf, 0x9d, 0x3f, 0x6f, 0xcf, 0x9d, 0xbf, - 0x6e, 0xcf, 0x9d, 0x5f, 0x2e, 0x3e, 0xe0, 0xc5, 0xde, 0x74, 0xe1, 0xc9, 0x7d, 0xfe, 0x4f, 0x00, - 0x00, 0x00, 0xff, 0xff, 0xc3, 0x51, 0x8b, 0xc4, 0xe7, 0x05, 0x00, 0x00, + // 840 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x94, 0x55, 0xcd, 0x72, 0xdb, 0x44, + 0x1c, 0x1f, 0xc9, 0x76, 0x6c, 0xff, 0x1d, 0x25, 0xe9, 0xb6, 0x4e, 0xd5, 0x00, 0x41, 0x84, 0x01, + 0x5c, 0x02, 0xf6, 0xa4, 0x1d, 0x0f, 0xd3, 0x23, 0x14, 0x86, 0xc9, 0x01, 0x92, 0xd9, 0xfa, 0xc4, + 0x45, 0xb3, 0x91, 0xd6, 0xf2, 0x4e, 0xe4, 0x5d, 0xb1, 0x5a, 0x35, 0x09, 0x8f, 0xc0, 0x99, 0x17, + 0xe1, 0x2d, 0x7a, 0xe4, 0x11, 0x98, 0x1c, 0x78, 0x0e, 0x66, 0xff, 0xfa, 0xb0, 0x42, 0x03, 0x1d, + 0x4e, 0xde, 0xdf, 0x87, 0xb4, 0xff, 0x4f, 0x0b, 0x5e, 0x24, 0xc2, 0xac, 0x8a, 0x8b, 0x69, 0xa4, + 0xd6, 0xb3, 0xc5, 0x8a, 0x2f, 0x56, 0x42, 0x26, 0xf9, 0x8f, 0xdc, 0x5c, 0x29, 0x7d, 0x39, 0x33, + 0x46, 0xce, 0x58, 0x26, 0x66, 0x09, 0x33, 0xfc, 0x8a, 0xdd, 0xd4, 0xbf, 0xd3, 0x4c, 0x2b, 0xa3, + 0x48, 0xbf, 0x82, 0x07, 0x5f, 0xb6, 0xde, 0x91, 0xa8, 0x44, 0xcd, 0x50, 0xbf, 0x28, 0x96, 0x88, + 0x10, 0xe0, 0xa9, 0x7c, 0xee, 0xe8, 0x0a, 0x46, 0xdf, 0x9f, 0xbf, 0xfa, 0x81, 0x1b, 0x16, 0x33, + 0xc3, 0x08, 0x81, 0xae, 0x11, 0x6b, 0xee, 0x3b, 0x81, 0x33, 0xe9, 0x50, 0x3c, 0x93, 0x03, 0x18, + 0xa4, 0xcc, 0x08, 0x53, 0xc4, 0xdc, 0x77, 0x03, 0x67, 0xe2, 0xd2, 0x06, 0x93, 0xf7, 0x61, 0x98, + 0x2a, 0x99, 0x94, 0x62, 0x07, 0xc5, 0x0d, 0x61, 0x9f, 0x64, 0x69, 0xf5, 0x64, 0x37, 0x70, 0x26, + 0x3d, 0xda, 0xe0, 0xa3, 0xdf, 0xba, 0x00, 0xf4, 0xba, 0xb9, 0xf8, 0x03, 0x80, 0x2a, 0x83, 0x50, + 0xc4, 0x78, 0xfd, 0x90, 0x0e, 0x2b, 0xe6, 0x34, 0x26, 0x9f, 0xc1, 0x6e, 0x2d, 0x1b, 0x5d, 0xe4, + 0x86, 0xc7, 0x18, 0xca, 0x80, 0xee, 0x54, 0xf4, 0xa2, 0x64, 0x6d, 0x40, 0x36, 0xe8, 0xdc, 0xb0, + 0x75, 0xe6, 0x8f, 0x02, 0x67, 0xe2, 0xd1, 0x0d, 0xd1, 0xa4, 0xb7, 0xdd, 0x4a, 0xef, 0x13, 0xd8, + 0xe1, 0x32, 0xd2, 0x37, 0x99, 0xe1, 0x71, 0x88, 0xaa, 0x17, 0x38, 0x93, 0x6d, 0xea, 0x35, 0xec, + 0xc2, 0xda, 0x9e, 0xc0, 0x40, 0x2f, 0xc3, 0x68, 0xc5, 0x84, 0xf4, 0xc7, 0xf8, 0xde, 0xbe, 0x5e, + 0xbe, 0xb4, 0x90, 0xf8, 0xd0, 0x8f, 0x56, 0x4c, 0x4a, 0x9e, 0xfa, 0xfb, 0xa5, 0x52, 0x41, 0xf2, + 0x15, 0x0c, 0x98, 0x34, 0x5c, 0x4a, 0x96, 0xfb, 0x87, 0x41, 0x67, 0x32, 0x7a, 0xf6, 0xde, 0xb4, + 0xee, 0xdb, 0x26, 0xf9, 0xe9, 0xd7, 0xa5, 0x87, 0x36, 0x66, 0x9b, 0xc6, 0x52, 0xf3, 0x9f, 0x0b, + 0x2e, 0xa3, 0x1b, 0xff, 0xc3, 0xc0, 0x99, 0x74, 0xe9, 0x86, 0xb0, 0x69, 0xe8, 0x3c, 0x17, 0x7e, + 0x80, 0x05, 0xc7, 0x33, 0xd9, 0x83, 0x4e, 0x2e, 0xb5, 0xff, 0x11, 0x52, 0xf6, 0x48, 0x3e, 0x85, + 0x4e, 0x92, 0xe5, 0xfe, 0xd3, 0xc0, 0x99, 0x8c, 0x9e, 0x3d, 0x6a, 0xee, 0x6d, 0xb5, 0x9b, 0x5a, + 0xc3, 0xc1, 0xaf, 0x0e, 0xf4, 0xab, 0x08, 0x6c, 0x2a, 0x55, 0x0c, 0xd8, 0x03, 0x8f, 0xd6, 0xb0, + 0x9d, 0xa4, 0x7b, 0x37, 0xc9, 0x3a, 0x9a, 0xce, 0xdb, 0xd1, 0x74, 0x37, 0xd1, 0xbc, 0x5d, 0x66, + 0xb8, 0xa7, 0xcc, 0x47, 0x7f, 0x39, 0xb0, 0xbb, 0xb8, 0x7e, 0xa9, 0xe4, 0x52, 0x24, 0x85, 0x66, + 0x46, 0x28, 0xf9, 0x8e, 0x9e, 0xfe, 0x47, 0x63, 0xee, 0x54, 0x71, 0xff, 0x9f, 0x55, 0x7c, 0x04, + 0xbd, 0x4c, 0x5d, 0x71, 0xed, 0x3f, 0xc6, 0xd1, 0x2c, 0x01, 0x99, 0xc3, 0x7e, 0xa6, 0x52, 0xa6, + 0xc5, 0x2f, 0x78, 0x79, 0x28, 0xe4, 0x6b, 0xae, 0x73, 0xa1, 0x24, 0xb6, 0x61, 0x40, 0xc7, 0x6d, + 0xf5, 0xb4, 0x16, 0xc9, 0x0c, 0x1e, 0x36, 0x6f, 0x0e, 0x63, 0xfe, 0x5a, 0xa0, 0x8e, 0x1d, 0xf2, + 0x28, 0x69, 0xa4, 0x6f, 0x6b, 0xe5, 0xe8, 0xf7, 0x1e, 0x6c, 0xbd, 0x32, 0xcc, 0x14, 0xf9, 0xdd, + 0xfc, 0x9c, 0x7f, 0x9b, 0x59, 0xb7, 0x35, 0xb3, 0xf7, 0xac, 0x43, 0xe7, 0xde, 0x75, 0xd8, 0x01, + 0x57, 0xd8, 0x9a, 0x75, 0x26, 0x43, 0xea, 0x8a, 0xcc, 0x6e, 0x64, 0x96, 0x32, 0xb3, 0x54, 0x7a, + 0x8d, 0x4b, 0x30, 0xa4, 0x0d, 0x26, 0x1f, 0x83, 0x17, 0x29, 0x69, 0x58, 0x64, 0x42, 0xbe, 0x66, + 0x22, 0xc5, 0x3d, 0x18, 0xd2, 0xed, 0x8a, 0xfc, 0xce, 0x72, 0x24, 0x80, 0x51, 0xcc, 0xf3, 0x48, + 0x8b, 0x0c, 0xf3, 0xdb, 0x41, 0x4b, 0x9b, 0xb2, 0x8d, 0xde, 0x54, 0x22, 0x4b, 0x99, 0xf4, 0x77, + 0xd1, 0xe4, 0x35, 0xec, 0x79, 0xca, 0x24, 0xd9, 0x87, 0xad, 0x0b, 0x2d, 0xe2, 0x84, 0xfb, 0x7b, + 0x28, 0x57, 0xc8, 0xf2, 0x5a, 0x15, 0x86, 0x6b, 0xff, 0x41, 0xc9, 0x97, 0xa8, 0x9e, 0xe6, 0xf1, + 0x3b, 0xa6, 0xd9, 0x4e, 0x9e, 0x36, 0x06, 0x9b, 0xe5, 0x51, 0x7b, 0x24, 0x0f, 0xa1, 0xa7, 0xaf, + 0x43, 0x21, 0x71, 0x13, 0x3c, 0xda, 0xd5, 0xd7, 0xa7, 0xb2, 0x22, 0xd5, 0xa5, 0xff, 0x79, 0x4d, + 0x9e, 0x5d, 0x5a, 0xd2, 0xa0, 0xf3, 0xb8, 0x24, 0x4d, 0xe5, 0x34, 0xe8, 0xfc, 0xa2, 0x26, 0xcf, + 0x2e, 0xc9, 0x53, 0x70, 0x55, 0xee, 0x3f, 0xc7, 0x60, 0x9e, 0x34, 0xc1, 0x94, 0xfd, 0x9c, 0x9e, + 0xd9, 0x90, 0xb4, 0x88, 0x72, 0xea, 0xaa, 0xfc, 0xe0, 0x8d, 0x03, 0xc3, 0x86, 0x21, 0x63, 0xd8, + 0x4a, 0x15, 0x8b, 0xc3, 0x13, 0x6c, 0xb4, 0x4b, 0x7b, 0x16, 0x9d, 0x34, 0xf4, 0xbc, 0xfa, 0x87, + 0x45, 0x7a, 0x4e, 0x1e, 0x43, 0xbf, 0x74, 0xcf, 0xab, 0xed, 0x42, 0xd7, 0xc9, 0xdc, 0x16, 0x39, + 0xca, 0x8a, 0x30, 0xe3, 0x3a, 0xe2, 0xd2, 0xb0, 0x84, 0xe3, 0x5e, 0xb8, 0xd4, 0x8b, 0xb2, 0xe2, + 0xbc, 0x21, 0xc9, 0x31, 0x3c, 0x58, 0xf3, 0xb5, 0xd2, 0x37, 0x6d, 0xe7, 0x18, 0x9d, 0x7b, 0xa5, + 0xd0, 0x32, 0x07, 0x30, 0x32, 0x7c, 0x9d, 0x71, 0xcd, 0x4c, 0xa1, 0x39, 0x56, 0xd0, 0xa5, 0x6d, + 0xea, 0x9b, 0x17, 0x6f, 0x6e, 0x0f, 0x9d, 0x3f, 0x6e, 0x0f, 0x9d, 0x3f, 0x6f, 0x0f, 0x9d, 0x9f, + 0x8e, 0xff, 0xc7, 0xd7, 0xea, 0x62, 0x0b, 0x3f, 0x37, 0xcf, 0xff, 0x0e, 0x00, 0x00, 0xff, 0xff, + 0x81, 0x52, 0xf6, 0x64, 0xe3, 0x06, 0x00, 0x00, } diff --git a/api/gateway/gateway.proto b/api/gateway/gateway.proto index df034ee8d..0324e96dd 100644 --- a/api/gateway/gateway.proto +++ b/api/gateway/gateway.proto @@ -29,9 +29,14 @@ message RxMetadata { // Time in Unix nanoseconds int64 time = 12; + // Encrypted time from the Gateway FPGA + bytes encrypted_time = 13; + uint32 rf_chain = 21; uint32 channel = 22; + repeated Antenna antennas = 30; + // Frequency in Hz uint64 frequency = 31; // Received signal strength in dBm @@ -39,6 +44,20 @@ message RxMetadata { // Signal-to-noise-ratio in dB float snr = 33; + message Antenna { + uint32 antenna = 1; + uint32 channel = 2; + + // Received signal strength in dBm + float rssi = 3; + + // Signal-to-noise-ratio in dB + float snr = 4; + + // Encrypted time from the Gateway FPGA + bytes encrypted_time = 10; + } + GPSMetadata gps = 41; } From 5de79f82ca65945eb0f43707e11ba743795e9f18 Mon Sep 17 00:00:00 2001 From: Eric Gourlaouen Date: Mon, 20 Mar 2017 15:03:47 +0100 Subject: [PATCH 31/45] Changed Gateway proto --- api/gateway/gateway.proto | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/api/gateway/gateway.proto b/api/gateway/gateway.proto index df034ee8d..a6de5d92a 100644 --- a/api/gateway/gateway.proto +++ b/api/gateway/gateway.proto @@ -69,6 +69,9 @@ message Status { // Indicates whether the gateway is trusted. Components that are able to verify gateway trust MUST do so and set this value accordingly bool gateway_trusted = 3; + // Boot time in Unix nanoseconds + int64 boot_time = 4; + // Configuration and relatively static stuff // These values may be left out if they don't change // Reset using "-" @@ -84,6 +87,12 @@ message Status { string bridge = 16; // The value of Router is set by the Router string router = 17; + // Version of Gateway FPGA + uint32 fpga = 18; + // Version of Gateway DSP software + uint32 dsp = 19; + // Version of gateway driver (in X.X.X format) + string hal = 20; GPSMetadata gps = 21; @@ -102,6 +111,14 @@ message Status { uint32 tx_in = 43; // Total number of successfully sent downlink packets since boot uint32 tx_ok = 44; + // Total number of packets received from link testing mote, with CRC OK + uint32 lm_ok = 45; + // Sequence number of the first packet received from the link testing mote + uint32 lm_st = 46; + // Sequence number of the last packet received from the link testing mote + uint32 lm_nw = 47; + // Number of lost PPS pulses + uint32 l_pps = 48; // Additional metrics from the operating system message OSMetrics { From ab765c596d026babad6b648d59ca4723bae0f2a8 Mon Sep 17 00:00:00 2001 From: Eric Gourlaouen Date: Mon, 20 Mar 2017 15:03:47 +0100 Subject: [PATCH 32/45] Changed Gateway Protobuf Declaration --- api/gateway/gateway.pb.go | 431 +++++++++++++++++++++++++++++++++----- api/gateway/gateway.proto | 17 ++ 2 files changed, 392 insertions(+), 56 deletions(-) diff --git a/api/gateway/gateway.pb.go b/api/gateway/gateway.pb.go index 34b24c077..834c552c5 100644 --- a/api/gateway/gateway.pb.go +++ b/api/gateway/gateway.pb.go @@ -237,18 +237,26 @@ type Status struct { // Time in Unix nanoseconds Time int64 `protobuf:"varint,2,opt,name=time,proto3" json:"time,omitempty"` // Indicates whether the gateway is trusted. Components that are able to verify gateway trust MUST do so and set this value accordingly - GatewayTrusted bool `protobuf:"varint,3,opt,name=gateway_trusted,json=gatewayTrusted,proto3" json:"gateway_trusted,omitempty"` - Ip []string `protobuf:"bytes,11,rep,name=ip" json:"ip,omitempty"` - Platform string `protobuf:"bytes,12,opt,name=platform,proto3" json:"platform,omitempty"` - ContactEmail string `protobuf:"bytes,13,opt,name=contact_email,json=contactEmail,proto3" json:"contact_email,omitempty"` - Description string `protobuf:"bytes,14,opt,name=description,proto3" json:"description,omitempty"` + GatewayTrusted bool `protobuf:"varint,3,opt,name=gateway_trusted,json=gatewayTrusted,proto3" json:"gateway_trusted,omitempty"` + // Boot time in Unix nanoseconds + BootTime int64 `protobuf:"varint,4,opt,name=boot_time,json=bootTime,proto3" json:"boot_time,omitempty"` + Ip []string `protobuf:"bytes,11,rep,name=ip" json:"ip,omitempty"` + Platform string `protobuf:"bytes,12,opt,name=platform,proto3" json:"platform,omitempty"` + ContactEmail string `protobuf:"bytes,13,opt,name=contact_email,json=contactEmail,proto3" json:"contact_email,omitempty"` + Description string `protobuf:"bytes,14,opt,name=description,proto3" json:"description,omitempty"` // The gateway's region: one of EU_863_870, US_902_928, CN_779_787, EU_433, AU_915_928, CN_470_510, AS_923, KR_920_923 Region string `protobuf:"bytes,15,opt,name=region,proto3" json:"region,omitempty"` // The value of Bridge is set by the Bridge Bridge string `protobuf:"bytes,16,opt,name=bridge,proto3" json:"bridge,omitempty"` // The value of Router is set by the Router - Router string `protobuf:"bytes,17,opt,name=router,proto3" json:"router,omitempty"` - Gps *GPSMetadata `protobuf:"bytes,21,opt,name=gps" json:"gps,omitempty"` + Router string `protobuf:"bytes,17,opt,name=router,proto3" json:"router,omitempty"` + // Version of Gateway FPGA + Fpga uint32 `protobuf:"varint,18,opt,name=fpga,proto3" json:"fpga,omitempty"` + // Version of Gateway DSP software + Dsp uint32 `protobuf:"varint,19,opt,name=dsp,proto3" json:"dsp,omitempty"` + // Version of gateway driver (in X.X.X format) + Hal string `protobuf:"bytes,20,opt,name=hal,proto3" json:"hal,omitempty"` + Gps *GPSMetadata `protobuf:"bytes,21,opt,name=gps" json:"gps,omitempty"` // Round-trip time to the server in milliseconds Rtt uint32 `protobuf:"varint,31,opt,name=rtt,proto3" json:"rtt,omitempty"` // Total number of received uplink packets since boot @@ -258,7 +266,15 @@ type Status struct { // Total number of received downlink packets since boot TxIn uint32 `protobuf:"varint,43,opt,name=tx_in,json=txIn,proto3" json:"tx_in,omitempty"` // Total number of successfully sent downlink packets since boot - TxOk uint32 `protobuf:"varint,44,opt,name=tx_ok,json=txOk,proto3" json:"tx_ok,omitempty"` + TxOk uint32 `protobuf:"varint,44,opt,name=tx_ok,json=txOk,proto3" json:"tx_ok,omitempty"` + // Total number of packets received from link testing mote, with CRC OK + LmOk uint32 `protobuf:"varint,45,opt,name=lm_ok,json=lmOk,proto3" json:"lm_ok,omitempty"` + // Sequence number of the first packet received from the link testing mote + LmSt uint32 `protobuf:"varint,46,opt,name=lm_st,json=lmSt,proto3" json:"lm_st,omitempty"` + // Sequence number of the last packet received from the link testing mote + LmNw uint32 `protobuf:"varint,47,opt,name=lm_nw,json=lmNw,proto3" json:"lm_nw,omitempty"` + // Number of lost PPS pulses + LPps uint32 `protobuf:"varint,48,opt,name=l_pps,json=lPps,proto3" json:"l_pps,omitempty"` Os *Status_OSMetrics `protobuf:"bytes,51,opt,name=os" json:"os,omitempty"` } @@ -288,6 +304,13 @@ func (m *Status) GetGatewayTrusted() bool { return false } +func (m *Status) GetBootTime() int64 { + if m != nil { + return m.BootTime + } + return 0 +} + func (m *Status) GetIp() []string { if m != nil { return m.Ip @@ -337,6 +360,27 @@ func (m *Status) GetRouter() string { return "" } +func (m *Status) GetFpga() uint32 { + if m != nil { + return m.Fpga + } + return 0 +} + +func (m *Status) GetDsp() uint32 { + if m != nil { + return m.Dsp + } + return 0 +} + +func (m *Status) GetHal() string { + if m != nil { + return m.Hal + } + return "" +} + func (m *Status) GetGps() *GPSMetadata { if m != nil { return m.Gps @@ -379,6 +423,34 @@ func (m *Status) GetTxOk() uint32 { return 0 } +func (m *Status) GetLmOk() uint32 { + if m != nil { + return m.LmOk + } + return 0 +} + +func (m *Status) GetLmSt() uint32 { + if m != nil { + return m.LmSt + } + return 0 +} + +func (m *Status) GetLmNw() uint32 { + if m != nil { + return m.LmNw + } + return 0 +} + +func (m *Status) GetLPps() uint32 { + if m != nil { + return m.LPps + } + return 0 +} + func (m *Status) GetOs() *Status_OSMetrics { if m != nil { return m.Os @@ -677,6 +749,11 @@ func (m *Status) MarshalTo(dAtA []byte) (int, error) { } i++ } + if m.BootTime != 0 { + dAtA[i] = 0x20 + i++ + i = encodeVarintGateway(dAtA, i, uint64(m.BootTime)) + } if len(m.Ip) > 0 { for _, s := range m.Ip { dAtA[i] = 0x5a @@ -732,6 +809,28 @@ func (m *Status) MarshalTo(dAtA []byte) (int, error) { i = encodeVarintGateway(dAtA, i, uint64(len(m.Router))) i += copy(dAtA[i:], m.Router) } + if m.Fpga != 0 { + dAtA[i] = 0x90 + i++ + dAtA[i] = 0x1 + i++ + i = encodeVarintGateway(dAtA, i, uint64(m.Fpga)) + } + if m.Dsp != 0 { + dAtA[i] = 0x98 + i++ + dAtA[i] = 0x1 + i++ + i = encodeVarintGateway(dAtA, i, uint64(m.Dsp)) + } + if len(m.Hal) > 0 { + dAtA[i] = 0xa2 + i++ + dAtA[i] = 0x1 + i++ + i = encodeVarintGateway(dAtA, i, uint64(len(m.Hal))) + i += copy(dAtA[i:], m.Hal) + } if m.Gps != nil { dAtA[i] = 0xaa i++ @@ -779,6 +878,34 @@ func (m *Status) MarshalTo(dAtA []byte) (int, error) { i++ i = encodeVarintGateway(dAtA, i, uint64(m.TxOk)) } + if m.LmOk != 0 { + dAtA[i] = 0xe8 + i++ + dAtA[i] = 0x2 + i++ + i = encodeVarintGateway(dAtA, i, uint64(m.LmOk)) + } + if m.LmSt != 0 { + dAtA[i] = 0xf0 + i++ + dAtA[i] = 0x2 + i++ + i = encodeVarintGateway(dAtA, i, uint64(m.LmSt)) + } + if m.LmNw != 0 { + dAtA[i] = 0xf8 + i++ + dAtA[i] = 0x2 + i++ + i = encodeVarintGateway(dAtA, i, uint64(m.LmNw)) + } + if m.LPps != 0 { + dAtA[i] = 0x80 + i++ + dAtA[i] = 0x3 + i++ + i = encodeVarintGateway(dAtA, i, uint64(m.LPps)) + } if m.Os != nil { dAtA[i] = 0x9a i++ @@ -965,6 +1092,9 @@ func (m *Status) Size() (n int) { if m.GatewayTrusted { n += 2 } + if m.BootTime != 0 { + n += 1 + sovGateway(uint64(m.BootTime)) + } if len(m.Ip) > 0 { for _, s := range m.Ip { l = len(s) @@ -995,6 +1125,16 @@ func (m *Status) Size() (n int) { if l > 0 { n += 2 + l + sovGateway(uint64(l)) } + if m.Fpga != 0 { + n += 2 + sovGateway(uint64(m.Fpga)) + } + if m.Dsp != 0 { + n += 2 + sovGateway(uint64(m.Dsp)) + } + l = len(m.Hal) + if l > 0 { + n += 2 + l + sovGateway(uint64(l)) + } if m.Gps != nil { l = m.Gps.Size() n += 2 + l + sovGateway(uint64(l)) @@ -1014,6 +1154,18 @@ func (m *Status) Size() (n int) { if m.TxOk != 0 { n += 2 + sovGateway(uint64(m.TxOk)) } + if m.LmOk != 0 { + n += 2 + sovGateway(uint64(m.LmOk)) + } + if m.LmSt != 0 { + n += 2 + sovGateway(uint64(m.LmSt)) + } + if m.LmNw != 0 { + n += 2 + sovGateway(uint64(m.LmNw)) + } + if m.LPps != 0 { + n += 2 + sovGateway(uint64(m.LPps)) + } if m.Os != nil { l = m.Os.Size() n += 2 + l + sovGateway(uint64(l)) @@ -1681,6 +1833,25 @@ func (m *Status) Unmarshal(dAtA []byte) error { } } m.GatewayTrusted = bool(v != 0) + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BootTime", wireType) + } + m.BootTime = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.BootTime |= (int64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } case 11: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Ip", wireType) @@ -1884,6 +2055,73 @@ func (m *Status) Unmarshal(dAtA []byte) error { } m.Router = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 18: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Fpga", wireType) + } + m.Fpga = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Fpga |= (uint32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 19: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Dsp", wireType) + } + m.Dsp = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Dsp |= (uint32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 20: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Hal", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGateway + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Hal = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex case 21: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Gps", wireType) @@ -2012,6 +2250,82 @@ func (m *Status) Unmarshal(dAtA []byte) error { break } } + case 45: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field LmOk", wireType) + } + m.LmOk = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.LmOk |= (uint32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 46: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field LmSt", wireType) + } + m.LmSt = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.LmSt |= (uint32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 47: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field LmNw", wireType) + } + m.LmNw = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.LmNw |= (uint32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 48: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field LPps", wireType) + } + m.LPps = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGateway + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.LPps |= (uint32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } case 51: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Os", wireType) @@ -2310,52 +2624,57 @@ func init() { } var fileDescriptorGateway = []byte{ - // 748 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x94, 0x54, 0x4f, 0x93, 0xdb, 0x34, - 0x14, 0x1f, 0x3b, 0xff, 0x95, 0x26, 0xbb, 0x55, 0x9b, 0x54, 0xdd, 0x81, 0xc5, 0x84, 0x01, 0x52, - 0x16, 0x92, 0x59, 0x3a, 0x39, 0xf4, 0x4a, 0x61, 0x98, 0x3d, 0xc0, 0x76, 0xd4, 0x9c, 0xb8, 0x78, - 0x14, 0x5b, 0x71, 0x34, 0xb1, 0x25, 0x23, 0xcb, 0x4d, 0x96, 0x8f, 0xc3, 0x85, 0xaf, 0xd2, 0x23, - 0x1f, 0x81, 0xd9, 0x03, 0x9f, 0x83, 0xd1, 0xf3, 0x9f, 0x75, 0x99, 0xc2, 0x4e, 0x4f, 0x79, 0xbf, - 0x3f, 0xf2, 0x7b, 0x4f, 0xef, 0x45, 0xe8, 0x45, 0x24, 0xcc, 0x2e, 0xdf, 0x2c, 0x02, 0x95, 0x2c, - 0xd7, 0x3b, 0xbe, 0xde, 0x09, 0x19, 0x65, 0x3f, 0x73, 0x73, 0x50, 0x7a, 0xbf, 0x34, 0x46, 0x2e, - 0x59, 0x2a, 0x96, 0x11, 0x33, 0xfc, 0xc0, 0x6e, 0xaa, 0xdf, 0x45, 0xaa, 0x95, 0x51, 0xb8, 0x57, - 0xc2, 0xb3, 0x6f, 0x1a, 0xdf, 0x88, 0x54, 0xa4, 0x96, 0xa0, 0x6f, 0xf2, 0x2d, 0x20, 0x00, 0x10, - 0x15, 0xe7, 0x66, 0x07, 0x34, 0xfc, 0xf1, 0xd5, 0xeb, 0x9f, 0xb8, 0x61, 0x21, 0x33, 0x0c, 0x63, - 0xd4, 0x36, 0x22, 0xe1, 0xc4, 0xf1, 0x9c, 0x79, 0x8b, 0x42, 0x8c, 0xcf, 0x50, 0x3f, 0x66, 0x46, - 0x98, 0x3c, 0xe4, 0xc4, 0xf5, 0x9c, 0xb9, 0x4b, 0x6b, 0x8c, 0x3f, 0x42, 0x83, 0x58, 0xc9, 0xa8, - 0x10, 0x5b, 0x20, 0xde, 0x11, 0xf6, 0x24, 0x8b, 0xcb, 0x93, 0x6d, 0xcf, 0x99, 0x77, 0x68, 0x8d, - 0x67, 0x7f, 0xb8, 0x08, 0xd1, 0x63, 0x9d, 0xf8, 0x63, 0x84, 0xca, 0x0e, 0x7c, 0x11, 0x42, 0xfa, - 0x01, 0x1d, 0x94, 0xcc, 0x55, 0x88, 0xbf, 0x44, 0x27, 0x95, 0x6c, 0x74, 0x9e, 0x19, 0x1e, 0x42, - 0x29, 0x7d, 0x3a, 0x2e, 0xe9, 0x75, 0xc1, 0xda, 0x82, 0x6c, 0xd1, 0x99, 0x61, 0x49, 0x4a, 0x86, - 0x9e, 0x33, 0x1f, 0xd1, 0x3b, 0xa2, 0x6e, 0xef, 0x41, 0xa3, 0xbd, 0xa7, 0xa8, 0xaf, 0xb7, 0x7e, - 0xb0, 0x63, 0x42, 0x92, 0x09, 0x1c, 0xe8, 0xe9, 0xed, 0x4b, 0x0b, 0x31, 0x41, 0xbd, 0x60, 0xc7, - 0xa4, 0xe4, 0x31, 0x99, 0x16, 0x4a, 0x09, 0x6d, 0x9a, 0xad, 0xe6, 0xbf, 0xe6, 0x5c, 0x06, 0x37, - 0xe4, 0x13, 0xcf, 0x99, 0xb7, 0xe9, 0x1d, 0x61, 0xd3, 0xe8, 0x2c, 0x13, 0xc4, 0x83, 0x0b, 0x81, - 0x18, 0x9f, 0xa2, 0x56, 0x26, 0x35, 0xf9, 0x14, 0x28, 0x1b, 0xe2, 0x2f, 0x50, 0x2b, 0x4a, 0x33, - 0xf2, 0xcc, 0x73, 0xe6, 0xc3, 0x6f, 0x1f, 0x2f, 0xaa, 0x79, 0x36, 0xc6, 0x41, 0xad, 0x61, 0xf6, - 0xb7, 0x83, 0x4e, 0xd6, 0xc7, 0x97, 0x4a, 0x6e, 0x45, 0x94, 0x6b, 0x66, 0x84, 0x92, 0xf7, 0xb4, - 0xf9, 0x3f, 0x2d, 0xbd, 0x53, 0xf8, 0xf4, 0xdf, 0x85, 0x3f, 0x46, 0x9d, 0x54, 0x1d, 0xb8, 0x26, - 0x4f, 0x60, 0x5a, 0x05, 0xc0, 0x2b, 0x34, 0x4d, 0x55, 0xcc, 0xb4, 0xf8, 0x0d, 0x92, 0xfb, 0x42, - 0xbe, 0xe1, 0x3a, 0x13, 0x4a, 0x42, 0xe7, 0x7d, 0x3a, 0x69, 0xaa, 0x57, 0x95, 0x88, 0x97, 0xe8, - 0x51, 0xfd, 0x65, 0x3f, 0xe4, 0x6f, 0x04, 0xe8, 0x70, 0x29, 0x23, 0x8a, 0x6b, 0xe9, 0xfb, 0x4a, - 0x99, 0xfd, 0xde, 0x41, 0xdd, 0xd7, 0x86, 0x99, 0x3c, 0x7b, 0xb7, 0x3f, 0xe7, 0xbf, 0xc6, 0xe8, - 0x36, 0xc6, 0xf8, 0x9e, 0x0d, 0x69, 0xbd, 0x77, 0x43, 0xc6, 0xc8, 0x15, 0xf6, 0xce, 0x5a, 0xf3, - 0x01, 0x75, 0x45, 0x6a, 0x97, 0x34, 0x8d, 0x99, 0xd9, 0x2a, 0x9d, 0xc0, 0x5e, 0x0c, 0x68, 0x8d, - 0xf1, 0x67, 0x68, 0x14, 0x28, 0x69, 0x58, 0x60, 0x7c, 0x9e, 0x30, 0x11, 0x93, 0x11, 0x18, 0x1e, - 0x94, 0xe4, 0x0f, 0x96, 0xc3, 0x1e, 0x1a, 0x86, 0x3c, 0x0b, 0xb4, 0x48, 0xa1, 0xbf, 0x31, 0x58, - 0x9a, 0x14, 0x9e, 0xa2, 0xae, 0xe6, 0x91, 0x15, 0x4f, 0x40, 0x2c, 0x91, 0xe5, 0x37, 0x5a, 0x84, - 0x11, 0x27, 0xa7, 0x05, 0x5f, 0x20, 0xf0, 0xab, 0xdc, 0x70, 0x4d, 0x1e, 0x96, 0x7e, 0x40, 0xd5, - 0xc6, 0x4c, 0xee, 0xd9, 0x18, 0xbb, 0x6b, 0xda, 0x18, 0x98, 0xce, 0x88, 0xda, 0x10, 0x3f, 0x42, - 0x1d, 0x7d, 0xf4, 0x85, 0x84, 0x6d, 0x1b, 0xd1, 0xb6, 0x3e, 0x5e, 0xc9, 0x92, 0x54, 0x7b, 0xf2, - 0x55, 0x45, 0x5e, 0xef, 0x2d, 0x69, 0xc0, 0x79, 0x51, 0x90, 0xa6, 0x74, 0x1a, 0x70, 0x7e, 0x5d, - 0x91, 0xd7, 0x7b, 0xfc, 0x0c, 0xb9, 0x2a, 0x23, 0xcf, 0xa1, 0x98, 0xa7, 0x75, 0x31, 0xc5, 0x00, - 0x17, 0xd7, 0xb6, 0x24, 0x2d, 0x82, 0x8c, 0xba, 0x2a, 0x3b, 0x7b, 0xeb, 0xa0, 0x41, 0xcd, 0xe0, - 0x09, 0xea, 0xc6, 0x8a, 0x85, 0xfe, 0x25, 0x4c, 0xd6, 0xa5, 0x1d, 0x8b, 0x2e, 0x6b, 0x7a, 0x55, - 0xbe, 0x32, 0x40, 0xaf, 0xf0, 0x13, 0xd4, 0x2b, 0xdc, 0xab, 0xf2, 0x81, 0x01, 0xd7, 0xe5, 0x0a, - 0x7f, 0x8e, 0xc6, 0x41, 0x9a, 0xfb, 0x29, 0xd7, 0x01, 0x97, 0x86, 0x45, 0x1c, 0xfe, 0x08, 0x2e, - 0x1d, 0x05, 0x69, 0xfe, 0xaa, 0x26, 0xf1, 0x05, 0x7a, 0x98, 0xf0, 0x44, 0xe9, 0x9b, 0xa6, 0x73, - 0x02, 0xce, 0xd3, 0x42, 0x68, 0x98, 0x3d, 0x34, 0x34, 0x3c, 0x49, 0xb9, 0x66, 0x26, 0xd7, 0x1c, - 0x6e, 0xd0, 0xa5, 0x4d, 0xea, 0xbb, 0x17, 0x6f, 0x6f, 0xcf, 0x9d, 0x3f, 0x6f, 0xcf, 0x9d, 0xbf, - 0x6e, 0xcf, 0x9d, 0x5f, 0x2e, 0x3e, 0xe0, 0xc5, 0xde, 0x74, 0xe1, 0xc9, 0x7d, 0xfe, 0x4f, 0x00, - 0x00, 0x00, 0xff, 0xff, 0xc3, 0x51, 0x8b, 0xc4, 0xe7, 0x05, 0x00, 0x00, + // 825 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x95, 0xdd, 0x72, 0x1b, 0x35, + 0x14, 0xc7, 0x67, 0xd7, 0x8e, 0x63, 0xcb, 0x75, 0x92, 0x2a, 0x71, 0xaa, 0x06, 0x08, 0x4b, 0x18, + 0xc0, 0x25, 0xd4, 0x26, 0x74, 0x7c, 0xd1, 0x5b, 0x0a, 0xc3, 0xe4, 0x82, 0x36, 0xa3, 0xf8, 0x8a, + 0x9b, 0x1d, 0x79, 0x57, 0x5e, 0x6b, 0xb2, 0xbb, 0x12, 0x92, 0xb6, 0x76, 0x78, 0x21, 0x5e, 0xa5, + 0x33, 0xdc, 0xf0, 0x08, 0x4c, 0x2e, 0x78, 0x0e, 0x46, 0x67, 0x3f, 0xb2, 0x65, 0x0a, 0x9d, 0x5e, + 0xe5, 0x9c, 0xdf, 0xff, 0xaf, 0x8f, 0xa3, 0x73, 0xe2, 0x45, 0xcf, 0x13, 0x61, 0xd7, 0xc5, 0x72, + 0x1a, 0xc9, 0x6c, 0xb6, 0x58, 0xf3, 0xc5, 0x5a, 0xe4, 0x89, 0x79, 0xc9, 0xed, 0x46, 0xea, 0x9b, + 0x99, 0xb5, 0xf9, 0x8c, 0x29, 0x31, 0x4b, 0x98, 0xe5, 0x1b, 0x76, 0x5b, 0xff, 0x9d, 0x2a, 0x2d, + 0xad, 0xc4, 0xbb, 0x55, 0x7a, 0xf2, 0xb4, 0xb5, 0x47, 0x22, 0x13, 0x39, 0x03, 0x7d, 0x59, 0xac, + 0x20, 0x83, 0x04, 0xa2, 0x72, 0xdd, 0xd9, 0x06, 0x0d, 0x7f, 0xba, 0xba, 0xfe, 0x99, 0x5b, 0x16, + 0x33, 0xcb, 0x30, 0x46, 0x5d, 0x2b, 0x32, 0x4e, 0xbc, 0xc0, 0x9b, 0x74, 0x28, 0xc4, 0xf8, 0x04, + 0xf5, 0x53, 0x66, 0x85, 0x2d, 0x62, 0x4e, 0xfc, 0xc0, 0x9b, 0xf8, 0xb4, 0xc9, 0xf1, 0xc7, 0x68, + 0x90, 0xca, 0x3c, 0x29, 0xc5, 0x0e, 0x88, 0xf7, 0xc0, 0xad, 0x64, 0x69, 0xb5, 0xb2, 0x1b, 0x78, + 0x93, 0x1d, 0xda, 0xe4, 0x67, 0xbf, 0xfb, 0x08, 0xd1, 0x6d, 0x73, 0xf0, 0x27, 0x08, 0x55, 0x15, + 0x84, 0x22, 0x86, 0xe3, 0x07, 0x74, 0x50, 0x91, 0xcb, 0x18, 0x7f, 0x85, 0xf6, 0x6b, 0xd9, 0xea, + 0xc2, 0x58, 0x1e, 0xc3, 0x55, 0xfa, 0x74, 0xaf, 0xc2, 0x8b, 0x92, 0xba, 0x0b, 0xb9, 0x4b, 0x1b, + 0xcb, 0x32, 0x45, 0x86, 0x81, 0x37, 0x19, 0xd1, 0x7b, 0xd0, 0x94, 0xf7, 0xa0, 0x55, 0xde, 0x63, + 0xd4, 0xd7, 0xab, 0x30, 0x5a, 0x33, 0x91, 0x93, 0x31, 0x2c, 0xd8, 0xd5, 0xab, 0x17, 0x2e, 0xc5, + 0x04, 0xed, 0x46, 0x6b, 0x96, 0xe7, 0x3c, 0x25, 0xc7, 0xa5, 0x52, 0xa5, 0xee, 0x98, 0x95, 0xe6, + 0xbf, 0x16, 0x3c, 0x8f, 0x6e, 0xc9, 0xa7, 0x81, 0x37, 0xe9, 0xd2, 0x7b, 0xe0, 0x8e, 0xd1, 0xc6, + 0x08, 0x12, 0xc0, 0x83, 0x40, 0x8c, 0x0f, 0x50, 0xc7, 0xe4, 0x9a, 0x7c, 0x06, 0xc8, 0x85, 0xf8, + 0x4b, 0xd4, 0x49, 0x94, 0x21, 0x4f, 0x02, 0x6f, 0x32, 0xfc, 0xee, 0x68, 0x5a, 0xf7, 0xb3, 0xd5, + 0x0e, 0xea, 0x0c, 0x67, 0x7f, 0x7b, 0x68, 0x7f, 0xb1, 0x7d, 0x21, 0xf3, 0x95, 0x48, 0x0a, 0xcd, + 0xac, 0x90, 0xf9, 0x7b, 0xca, 0xfc, 0x9f, 0x92, 0xde, 0xba, 0xf8, 0xf1, 0xbf, 0x2f, 0x7e, 0x84, + 0x76, 0x94, 0xdc, 0x70, 0x4d, 0x1e, 0x41, 0xb7, 0xca, 0x04, 0xcf, 0xd1, 0xb1, 0x92, 0x29, 0xd3, + 0xe2, 0x37, 0x38, 0x3c, 0x14, 0xf9, 0x6b, 0xae, 0x8d, 0x90, 0x39, 0x54, 0xde, 0xa7, 0xe3, 0xb6, + 0x7a, 0x59, 0x8b, 0x78, 0x86, 0x0e, 0x9b, 0x9d, 0xc3, 0x98, 0xbf, 0x16, 0xa0, 0xc3, 0xa3, 0x8c, + 0x28, 0x6e, 0xa4, 0x1f, 0x6a, 0xe5, 0xec, 0x8f, 0x1e, 0xea, 0x5d, 0x5b, 0x66, 0x0b, 0xf3, 0x76, + 0x7d, 0xde, 0x7f, 0xb5, 0xd1, 0x6f, 0xb5, 0xf1, 0x1d, 0x13, 0xd2, 0x79, 0xe7, 0x84, 0x7c, 0x84, + 0x06, 0x4b, 0x29, 0x6d, 0x08, 0x3b, 0x74, 0x61, 0x87, 0xbe, 0x03, 0x0b, 0xb7, 0xcb, 0x1e, 0xf2, + 0x85, 0x7b, 0xd0, 0xce, 0x64, 0x40, 0x7d, 0xa1, 0xdc, 0x04, 0xab, 0x94, 0xd9, 0x95, 0xd4, 0x19, + 0x0c, 0xcd, 0x80, 0x36, 0x39, 0xfe, 0x1c, 0x8d, 0x22, 0x99, 0x5b, 0x16, 0xd9, 0x90, 0x67, 0x4c, + 0xa4, 0x64, 0x04, 0x86, 0x07, 0x15, 0xfc, 0xd1, 0x31, 0x1c, 0xa0, 0x61, 0xcc, 0x4d, 0xa4, 0x85, + 0x82, 0xe2, 0xf7, 0xc0, 0xd2, 0x46, 0xf8, 0x18, 0xf5, 0x34, 0x4f, 0x9c, 0xb8, 0x0f, 0x62, 0x95, + 0x39, 0xbe, 0xd4, 0x22, 0x4e, 0x38, 0x39, 0x28, 0x79, 0x99, 0x81, 0x5f, 0x16, 0x96, 0x6b, 0xf2, + 0xb0, 0xf2, 0x43, 0xe6, 0x1e, 0x65, 0xa5, 0x12, 0x46, 0x30, 0xbc, 0x16, 0xc4, 0x6e, 0xe8, 0x62, + 0xa3, 0xc8, 0x21, 0x20, 0x17, 0x3a, 0xb2, 0x66, 0x29, 0x39, 0x82, 0xa5, 0x2e, 0xac, 0xc7, 0x70, + 0xfc, 0x9e, 0x31, 0x74, 0x2b, 0xb5, 0xb5, 0xd0, 0xf2, 0x11, 0x75, 0x21, 0x3e, 0x44, 0x3b, 0x7a, + 0x1b, 0x8a, 0x1c, 0x46, 0x78, 0x44, 0xbb, 0x7a, 0x7b, 0x99, 0x57, 0x50, 0xde, 0x90, 0xaf, 0x6b, + 0xf8, 0xea, 0xc6, 0x41, 0x0b, 0xce, 0xf3, 0x12, 0xda, 0xca, 0x69, 0xc1, 0xf9, 0x4d, 0x0d, 0x4b, + 0x67, 0x9a, 0x39, 0xf8, 0xb4, 0x84, 0x69, 0xd6, 0x40, 0x63, 0xc9, 0xb4, 0x86, 0xd7, 0xb6, 0x82, + 0xf9, 0x86, 0xcc, 0x6a, 0xf8, 0x72, 0x03, 0x30, 0x54, 0xca, 0x90, 0x6f, 0x2b, 0x78, 0xa5, 0x0c, + 0x7e, 0x82, 0x7c, 0x69, 0xc8, 0x33, 0x28, 0xf0, 0x71, 0x53, 0x60, 0x39, 0x69, 0xd3, 0x57, 0xae, + 0x4c, 0x2d, 0x22, 0x43, 0x7d, 0x69, 0x4e, 0xde, 0x78, 0x68, 0xd0, 0x10, 0x3c, 0x46, 0xbd, 0x54, + 0xb2, 0x38, 0xbc, 0x80, 0x11, 0xf4, 0xe9, 0x8e, 0xcb, 0x2e, 0x1a, 0x3c, 0xaf, 0x7e, 0x0e, 0x01, + 0xcf, 0xf1, 0x23, 0xb4, 0x5b, 0xba, 0xe7, 0xd5, 0x2f, 0x21, 0xb8, 0x2e, 0xe6, 0xf8, 0x0b, 0xb4, + 0x17, 0xa9, 0x22, 0x54, 0x5c, 0x47, 0x3c, 0xb7, 0x2c, 0xe1, 0xf0, 0x1f, 0xeb, 0xd3, 0x51, 0xa4, + 0x8a, 0xab, 0x06, 0xe2, 0x73, 0xf4, 0x30, 0xe3, 0x99, 0xd4, 0xb7, 0x6d, 0xe7, 0x18, 0x9c, 0x07, + 0xa5, 0xd0, 0x32, 0x07, 0x68, 0x68, 0x79, 0xa6, 0xb8, 0x66, 0xb6, 0xd0, 0x1c, 0xba, 0xe2, 0xd3, + 0x36, 0xfa, 0xfe, 0xf9, 0x9b, 0xbb, 0x53, 0xef, 0xcf, 0xbb, 0x53, 0xef, 0xaf, 0xbb, 0x53, 0xef, + 0x97, 0xf3, 0x0f, 0xf8, 0xb4, 0x2c, 0x7b, 0xf0, 0x6d, 0x78, 0xf6, 0x4f, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x96, 0xf8, 0x3a, 0xbe, 0x90, 0x06, 0x00, 0x00, } diff --git a/api/gateway/gateway.proto b/api/gateway/gateway.proto index df034ee8d..a6de5d92a 100644 --- a/api/gateway/gateway.proto +++ b/api/gateway/gateway.proto @@ -69,6 +69,9 @@ message Status { // Indicates whether the gateway is trusted. Components that are able to verify gateway trust MUST do so and set this value accordingly bool gateway_trusted = 3; + // Boot time in Unix nanoseconds + int64 boot_time = 4; + // Configuration and relatively static stuff // These values may be left out if they don't change // Reset using "-" @@ -84,6 +87,12 @@ message Status { string bridge = 16; // The value of Router is set by the Router string router = 17; + // Version of Gateway FPGA + uint32 fpga = 18; + // Version of Gateway DSP software + uint32 dsp = 19; + // Version of gateway driver (in X.X.X format) + string hal = 20; GPSMetadata gps = 21; @@ -102,6 +111,14 @@ message Status { uint32 tx_in = 43; // Total number of successfully sent downlink packets since boot uint32 tx_ok = 44; + // Total number of packets received from link testing mote, with CRC OK + uint32 lm_ok = 45; + // Sequence number of the first packet received from the link testing mote + uint32 lm_st = 46; + // Sequence number of the last packet received from the link testing mote + uint32 lm_nw = 47; + // Number of lost PPS pulses + uint32 l_pps = 48; // Additional metrics from the operating system message OSMetrics { From ab6a9e7f4c9c3fc8508a31859669dc5ce917d3cd Mon Sep 17 00:00:00 2001 From: Hylke Visser Date: Tue, 21 Mar 2017 14:56:39 +0100 Subject: [PATCH 33/45] Update vendors and docs --- Makefile | 2 +- api/api.pb.go | 2 +- api/broker/broker.pb.go | 2 +- api/discovery/discovery.pb.go | 4 +- api/handler/handler.pb.go | 4 +- api/networkserver/networkserver.pb.go | 2 +- api/protocol/lorawan/device.pb.go | 2 +- api/protocol/lorawan/device_address.pb.go | 2 +- api/protocol/lorawan/lorawan.pb.go | 2 +- api/protocol/protocol.pb.go | 2 +- api/router/router.pb.go | 2 +- api/trace/trace.pb.go | 2 +- cmd/docs/README.md | 7 +- ttnctl/cmd/docs/README.md | 1 + utils/protoc-gen-ttndoc/build_tree.go | 8 +- vendor/vendor.json | 534 ++++++++++++---------- 16 files changed, 307 insertions(+), 271 deletions(-) diff --git a/Makefile b/Makefile index 880673e82..06031fd46 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,7 @@ COMPILED_PROTO_FILES = $(patsubst api%.proto, api%.pb.go, $(PROTO_FILES)) PROTOC_IMPORTS= -I/usr/local/include -I$(GO_PATH)/src -I$(PARENT_DIRECTORY) \ -I$(GO_PATH)/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis PROTOC = protoc $(PROTOC_IMPORTS) \ ---gogofast_out=Mgoogle/api/annotations.proto=github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis/google/api,plugins=grpc:$(GO_SRC) \ +--gogofast_out=plugins=grpc:$(GO_SRC) \ --grpc-gateway_out=:$(GO_SRC) `pwd`/ protos-clean: diff --git a/api/api.pb.go b/api/api.pb.go index 9e8e653bc..98b70dee4 100644 --- a/api/api.pb.go +++ b/api/api.pb.go @@ -2181,7 +2181,7 @@ func init() { proto.RegisterFile("github.com/TheThingsNetwork/ttn/api/api.proto" var fileDescriptorApi = []byte{ // 558 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xac, 0x54, 0xcb, 0x6a, 0xdb, 0x40, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x54, 0xcb, 0x6a, 0xdb, 0x40, 0x14, 0xc5, 0xb1, 0xe2, 0x26, 0x57, 0x7d, 0xc0, 0x50, 0xcc, 0xd4, 0x04, 0x37, 0xb8, 0x50, 0x0a, 0xa5, 0xb2, 0xe3, 0x56, 0x18, 0x6f, 0x6b, 0xe8, 0xa2, 0xcf, 0xa0, 0x24, 0x9b, 0x6e, 0xc2, 0x58, 0x9e, 0xda, 0x22, 0x92, 0x66, 0xd0, 0x8c, 0x1a, 0xf2, 0x0b, 0x5d, 0xf7, 0xa3, 0xba, 0x29, 0xf4, diff --git a/api/broker/broker.pb.go b/api/broker/broker.pb.go index ff4ea3390..139456dd6 100644 --- a/api/broker/broker.pb.go +++ b/api/broker/broker.pb.go @@ -5767,7 +5767,7 @@ func init() { var fileDescriptorBroker = []byte{ // 1208 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xec, 0x58, 0x4d, 0x8f, 0xdb, 0x44, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x58, 0x4d, 0x8f, 0xdb, 0x44, 0x18, 0x96, 0xf7, 0x23, 0xdb, 0x7d, 0xb3, 0xd9, 0x24, 0xd3, 0xee, 0xae, 0x9b, 0xd2, 0x4d, 0x08, 0x52, 0x15, 0x28, 0x4d, 0xda, 0x20, 0xbe, 0x24, 0x44, 0x95, 0xed, 0x56, 0xb0, 0x48, 0x29, 0x95, 0x9b, 0x72, 0x40, 0x48, 0xd1, 0xc4, 0x7e, 0xeb, 0x8c, 0xea, 0xd8, 0xae, 0x67, 0x9c, 0x36, 0x7f, diff --git a/api/discovery/discovery.pb.go b/api/discovery/discovery.pb.go index 74ecc4412..fefbeb8d2 100644 --- a/api/discovery/discovery.pb.go +++ b/api/discovery/discovery.pb.go @@ -22,7 +22,7 @@ import proto "github.com/gogo/protobuf/proto" import fmt "fmt" import math "math" import google_protobuf "github.com/golang/protobuf/ptypes/empty" -import _ "github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis/google/api" +import _ "google.golang.org/genproto/googleapis/api/annotations" import ( context "golang.org/x/net/context" @@ -2223,7 +2223,7 @@ func init() { var fileDescriptorDiscovery = []byte{ // 681 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xa4, 0x54, 0x5d, 0x4f, 0x13, 0x4b, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x54, 0x5d, 0x4f, 0x13, 0x4b, 0x18, 0x66, 0xdb, 0x43, 0x4f, 0xfb, 0xb6, 0xb4, 0x9c, 0x39, 0x07, 0xd8, 0x53, 0xa1, 0x94, 0x8d, 0xc6, 0xc6, 0xc4, 0x6e, 0x02, 0x89, 0x37, 0xc4, 0x98, 0x12, 0x48, 0x31, 0x0a, 0x31, 0x2b, 0xf1, 0xc2, 0x9b, 0x66, 0xba, 0xf3, 0x52, 0x26, 0x74, 0x67, 0x87, 0xdd, 0xd9, 0x2a, 0x21, 0xdc, 0xf8, diff --git a/api/handler/handler.pb.go b/api/handler/handler.pb.go index 5e27392e0..855ed5944 100644 --- a/api/handler/handler.pb.go +++ b/api/handler/handler.pb.go @@ -30,7 +30,7 @@ import proto "github.com/gogo/protobuf/proto" import fmt "fmt" import math "math" import google_protobuf "github.com/golang/protobuf/ptypes/empty" -import _ "github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis/google/api" +import _ "google.golang.org/genproto/googleapis/api/annotations" import api "github.com/TheThingsNetwork/ttn/api" import broker "github.com/TheThingsNetwork/ttn/api/broker" import protocol "github.com/TheThingsNetwork/ttn/api/protocol" @@ -4268,7 +4268,7 @@ func init() { var fileDescriptorHandler = []byte{ // 1248 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xac, 0x57, 0xdb, 0x6e, 0xdb, 0x46, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x57, 0xdb, 0x6e, 0xdb, 0x46, 0x13, 0xfe, 0xe9, 0x83, 0x2c, 0x8d, 0x6c, 0x39, 0x5e, 0x27, 0xfa, 0x19, 0x39, 0x50, 0x54, 0x06, 0x49, 0x1d, 0x27, 0xa0, 0x50, 0xb5, 0x40, 0x93, 0x5c, 0xa4, 0x39, 0x38, 0x07, 0x03, 0x71, 0x0b, 0xd0, 0xee, 0x8d, 0x2f, 0x6a, 0xac, 0xc5, 0x31, 0x45, 0x98, 0xe2, 0xb2, 0xe4, 0x4a, 0x86, 0x10, diff --git a/api/networkserver/networkserver.pb.go b/api/networkserver/networkserver.pb.go index 60be76a98..58648466b 100644 --- a/api/networkserver/networkserver.pb.go +++ b/api/networkserver/networkserver.pb.go @@ -1284,7 +1284,7 @@ func init() { var fileDescriptorNetworkserver = []byte{ // 603 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x94, 0x94, 0x5d, 0x4f, 0x13, 0x4f, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x94, 0x5d, 0x4f, 0x13, 0x4f, 0x14, 0xc6, 0xb3, 0xf0, 0xff, 0x97, 0x72, 0x4a, 0x45, 0x06, 0x89, 0x4d, 0x95, 0x0a, 0x4d, 0x34, 0x35, 0xea, 0x6e, 0xa8, 0x89, 0x57, 0x24, 0xf2, 0x52, 0xc3, 0x85, 0x81, 0xd4, 0x05, 0x13, 0xe3, 0x0d, 0x99, 0xee, 0x1e, 0xda, 0x0d, 0xdb, 0x99, 0x75, 0x66, 0xb6, 0xc8, 0xc7, 0xf1, 0xda, 0x2f, diff --git a/api/protocol/lorawan/device.pb.go b/api/protocol/lorawan/device.pb.go index 0e1c9a775..a0dabd55b 100644 --- a/api/protocol/lorawan/device.pb.go +++ b/api/protocol/lorawan/device.pb.go @@ -1223,7 +1223,7 @@ func init() { var fileDescriptorDevice = []byte{ // 579 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xcc, 0x94, 0x4f, 0x6f, 0xd3, 0x3e, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x94, 0x4f, 0x6f, 0xd3, 0x3e, 0x18, 0xc7, 0x95, 0xdf, 0x7e, 0x6b, 0x13, 0xb3, 0x8a, 0xc9, 0x68, 0x93, 0xe9, 0xd0, 0x56, 0xed, 0x42, 0x2f, 0x4b, 0xc4, 0xfe, 0xc0, 0xb9, 0xff, 0x40, 0x15, 0x62, 0x12, 0xd9, 0x76, 0xe1, 0x12, 0xb9, 0xf1, 0xd3, 0xd4, 0x6a, 0x66, 0x5b, 0x89, 0x93, 0xa8, 0x2f, 0x80, 0x37, 0xc4, 0x3b, 0xe0, diff --git a/api/protocol/lorawan/device_address.pb.go b/api/protocol/lorawan/device_address.pb.go index ba5125f48..addb419f8 100644 --- a/api/protocol/lorawan/device_address.pb.go +++ b/api/protocol/lorawan/device_address.pb.go @@ -992,7 +992,7 @@ func init() { var fileDescriptorDeviceAddress = []byte{ // 362 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x9c, 0x52, 0x4d, 0x4f, 0xe3, 0x30, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x52, 0x4d, 0x4f, 0xe3, 0x30, 0x14, 0x94, 0xb7, 0xda, 0x7e, 0xb8, 0xbb, 0xdb, 0x5d, 0x6b, 0x05, 0x21, 0x87, 0x52, 0xf5, 0x00, 0xbd, 0x90, 0x48, 0x05, 0x71, 0x43, 0x88, 0x80, 0x54, 0x71, 0x28, 0x82, 0xa8, 0x27, 0x2e, 0xc8, 0x8d, 0x5f, 0xd3, 0x88, 0x12, 0x07, 0xdb, 0x69, 0xe1, 0x87, 0xc0, 0x6f, 0xe2, 0xc8, 0x99, 0x03, diff --git a/api/protocol/lorawan/lorawan.pb.go b/api/protocol/lorawan/lorawan.pb.go index 43279f2f8..b8f9400f0 100644 --- a/api/protocol/lorawan/lorawan.pb.go +++ b/api/protocol/lorawan/lorawan.pb.go @@ -3842,7 +3842,7 @@ func init() { var fileDescriptorLorawan = []byte{ // 1327 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xcc, 0x57, 0xcb, 0x52, 0x1b, 0xc7, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x57, 0xcb, 0x52, 0x1b, 0xc7, 0x1a, 0x66, 0xd0, 0x8c, 0x24, 0x7e, 0x21, 0x18, 0xb7, 0xed, 0x73, 0x74, 0x6c, 0x17, 0x50, 0xaa, 0x73, 0xaa, 0x28, 0xea, 0x84, 0x8b, 0x64, 0x0c, 0x24, 0x65, 0x57, 0xe9, 0x46, 0x8c, 0x0d, 0x12, 0x6e, 0x50, 0x39, 0x95, 0x4d, 0x57, 0x33, 0xd3, 0x03, 0x83, 0x34, 0x17, 0xb7, 0x9a, 0x8b, 0xf2, diff --git a/api/protocol/protocol.pb.go b/api/protocol/protocol.pb.go index 843d74dc9..50dd3df95 100644 --- a/api/protocol/protocol.pb.go +++ b/api/protocol/protocol.pb.go @@ -1116,7 +1116,7 @@ func init() { var fileDescriptorProtocol = []byte{ // 236 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xb2, 0x4e, 0xcf, 0x2c, 0xc9, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xb2, 0x4e, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x0f, 0xc9, 0x48, 0x0d, 0xc9, 0xc8, 0xcc, 0x4b, 0x2f, 0xf6, 0x4b, 0x2d, 0x29, 0xcf, 0x2f, 0xca, 0xd6, 0x2f, 0x29, 0xc9, 0xd3, 0x4f, 0x2c, 0xc8, 0xd4, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0xce, 0xcf, 0x81, 0x33, 0xf4, 0xc0, 0x0c, 0x21, 0x0e, 0x18, diff --git a/api/router/router.pb.go b/api/router/router.pb.go index 4258ca925..0015a59a8 100644 --- a/api/router/router.pb.go +++ b/api/router/router.pb.go @@ -2848,7 +2848,7 @@ func init() { var fileDescriptorRouter = []byte{ // 884 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xb4, 0x56, 0xcb, 0x6e, 0xdb, 0x46, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0xcb, 0x6e, 0xdb, 0x46, 0x14, 0x05, 0x1d, 0x94, 0xb6, 0xae, 0x25, 0x5b, 0x1e, 0x5b, 0x36, 0xa3, 0xc4, 0x0f, 0x70, 0xd1, 0x1a, 0x4d, 0x43, 0xd5, 0x2a, 0x82, 0x3e, 0x16, 0x45, 0xed, 0xd8, 0x08, 0x02, 0x54, 0x41, 0x41, 0x3b, 0x9b, 0x02, 0x85, 0x30, 0xa2, 0x6e, 0x68, 0xc2, 0x12, 0x87, 0xe5, 0x0c, 0xe5, 0xe8, 0x2f, diff --git a/api/trace/trace.pb.go b/api/trace/trace.pb.go index c70d5afd8..015d33422 100644 --- a/api/trace/trace.pb.go +++ b/api/trace/trace.pb.go @@ -703,7 +703,7 @@ func init() { var fileDescriptorTrace = []byte{ // 292 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x8c, 0x50, 0x4d, 0x4b, 0xc3, 0x40, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x50, 0x4d, 0x4b, 0xc3, 0x40, 0x10, 0x65, 0x93, 0xa6, 0xda, 0x69, 0x15, 0x59, 0x3c, 0x2c, 0x05, 0x4b, 0xf5, 0x20, 0xf5, 0x92, 0x80, 0xe2, 0x07, 0x7a, 0x13, 0x3c, 0x78, 0xb0, 0x87, 0x90, 0x93, 0x17, 0xd9, 0x26, 0x43, 0xb3, 0xd4, 0x6c, 0xc2, 0x66, 0x1a, 0xe9, 0xbf, 0xf1, 0xe7, 0x78, 0xf4, 0x27, 0x48, 0x7e, 0x89, 0x64, diff --git a/cmd/docs/README.md b/cmd/docs/README.md index 040aad216..cf2a00b69 100644 --- a/cmd/docs/README.md +++ b/cmd/docs/README.md @@ -5,6 +5,7 @@ The Things Network's backend servers. **Options** ``` + --allow-insecure Allow insecure fallback if TLS unavailable --auth-token string The JWT token to be used for the discovery server --config string config file (default "$HOME/.ttn.yml") --description string The description of this component @@ -16,7 +17,7 @@ The Things Network's backend servers. --log-file string Location of the log file --no-cli-logs Disable CLI logs --public Announce this component as part of The Things Network (public community network) - --tls Use TLS + --tls Use TLS (default true) ``` @@ -71,6 +72,7 @@ ttn broker register prefix registers a prefix to this Broker --master-auth-servers stringSlice Auth servers that are allowed to manage this network (default [ttn-account-v2]) --redis-address string Redis server and port (default "localhost:6379") --redis-db int Redis database + --redis-password string Redis password --server-address string The IP address to listen for communication (default "0.0.0.0") --server-port int The port for communication (default 1900) ``` @@ -110,6 +112,7 @@ ttn gen-keypair generates a public/private keypair --mqtt-username string MQTT username --redis-address string Redis host and port (default "localhost:6379") --redis-db int Redis database + --redis-password string Redis password --server-address string The IP address to listen for communication (default "0.0.0.0") --server-address-announce string The public IP address to announce (default "localhost") --server-port int The port for communication (default 1904) @@ -139,6 +142,7 @@ ttn gen-keypair generates a public/private keypair --net-id int LoRaWAN NetID (default 19) --redis-address string Redis server and port (default "localhost:6379") --redis-db int Redis database + --redis-password string Redis password --server-address string The IP address to listen for communication (default "0.0.0.0") --server-address-announce string The public IP address to announce (default "localhost") --server-port int The port for communication (default 1903) @@ -177,6 +181,7 @@ ttn gen-keypair generates a public/private keypair **Options** ``` + --mqtt-address-announce string MQTT address to announce --server-address string The IP address to listen for communication (default "0.0.0.0") --server-address-announce string The public IP address to announce (default "localhost") --server-port int The port for communication (default 1901) diff --git a/ttnctl/cmd/docs/README.md b/ttnctl/cmd/docs/README.md index 9b8927c08..b31fa9241 100644 --- a/ttnctl/cmd/docs/README.md +++ b/ttnctl/cmd/docs/README.md @@ -5,6 +5,7 @@ Control The Things Network from the command line. **Options** ``` + --allow-insecure Allow insecure fallback if TLS unavailable --auth-server string The address of the OAuth 2.0 server (default "https://account.thethingsnetwork.org") --config string config file (default is $HOME/.ttnctl.yml) --data string directory where ttnctl stores data (default is $HOME/.ttnctl) diff --git a/utils/protoc-gen-ttndoc/build_tree.go b/utils/protoc-gen-ttndoc/build_tree.go index 59c715869..e82440614 100644 --- a/utils/protoc-gen-ttndoc/build_tree.go +++ b/utils/protoc-gen-ttndoc/build_tree.go @@ -10,7 +10,7 @@ import ( protobuf "github.com/golang/protobuf/proto" "github.com/golang/protobuf/protoc-gen-go/descriptor" - gateway "github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis/google/api" + "google.golang.org/genproto/googleapis/api/annotations" ) func buildTree(files []*descriptor.FileDescriptorProto) *tree { @@ -85,10 +85,10 @@ func fillTreeWithMethod(tree *tree, key string, proto *descriptor.MethodDescript if proto.GetServerStreaming() { tree.methods[key].outputStream = true } - if proto.Options != nil && protobuf.HasExtension(proto.Options, gateway.E_Http) { - ext, err := protobuf.GetExtension(proto.Options, gateway.E_Http) + if proto.Options != nil && protobuf.HasExtension(proto.Options, annotations.E_Http) { + ext, err := protobuf.GetExtension(proto.Options, annotations.E_Http) if err == nil { - if opts, ok := ext.(*gateway.HttpRule); ok { + if opts, ok := ext.(*annotations.HttpRule); ok { if endpoint := newEndpoint(opts); endpoint != nil { tree.methods[key].endpoints = append(tree.methods[key].endpoints, endpoint) } diff --git a/vendor/vendor.json b/vendor/vendor.json index 27d7e9bb5..55ad0f7ae 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -9,7 +9,7 @@ "revisionTime": "2017-02-20T21:12:21Z" }, { - "checksumSHA1": "MBoJo9G+PWFsRRIkb55n+9WCcDo=", + "checksumSHA1": "W0V/gwsdrx63Qnfep7VJbSaWIWE=", "path": "github.com/TheThingsNetwork/go-account-lib", "revision": "c3634eb20045e94abd340fac873c3be17e506e36", "revisionTime": "2017-03-15T15:57:52Z" @@ -17,134 +17,134 @@ { "checksumSHA1": "EyqK2NXpCzuD58zuZaSJ43jwvYI=", "path": "github.com/TheThingsNetwork/go-account-lib/account", - "revision": "25435ec2cfb8b5d6d7734d6ec3f86ef6bf0e53f5", - "revisionTime": "2017-03-16T12:34:39Z" + "revision": "ec41fbe9d998e431319d83b5625b625b3c7fb4e5", + "revisionTime": "2017-03-16T12:49:24Z" }, { "checksumSHA1": "b6pCShOzSh4N9LujOUFzQDzqhz8=", "path": "github.com/TheThingsNetwork/go-account-lib/auth", - "revision": "90ce9cacdb918a924728960a7e40784573c8de25", - "revisionTime": "2017-02-28T11:03:51Z" + "revision": "ec41fbe9d998e431319d83b5625b625b3c7fb4e5", + "revisionTime": "2017-03-16T12:49:24Z" }, { "checksumSHA1": "d1nFTGUlP4sNEf1lelyh6L59mjE=", "path": "github.com/TheThingsNetwork/go-account-lib/cache", - "revision": "90ce9cacdb918a924728960a7e40784573c8de25", - "revisionTime": "2017-02-28T11:03:51Z" + "revision": "ec41fbe9d998e431319d83b5625b625b3c7fb4e5", + "revisionTime": "2017-03-16T12:49:24Z" }, { "checksumSHA1": "8LlsoZGpmrUfqDNDJKU/IJ/x6TM=", "path": "github.com/TheThingsNetwork/go-account-lib/claims", - "revision": "90ce9cacdb918a924728960a7e40784573c8de25", - "revisionTime": "2017-02-28T11:03:51Z" + "revision": "ec41fbe9d998e431319d83b5625b625b3c7fb4e5", + "revisionTime": "2017-03-16T12:49:24Z" }, { "checksumSHA1": "3IiXhWt/UvtK73ANnQVxm0g9uGU=", "path": "github.com/TheThingsNetwork/go-account-lib/keys", - "revision": "90ce9cacdb918a924728960a7e40784573c8de25", - "revisionTime": "2017-02-28T11:03:51Z" + "revision": "ec41fbe9d998e431319d83b5625b625b3c7fb4e5", + "revisionTime": "2017-03-16T12:49:24Z" }, { "checksumSHA1": "9CSKNlGHiO9OFMDk6CmHTupXRRo=", "path": "github.com/TheThingsNetwork/go-account-lib/oauth", - "revision": "90ce9cacdb918a924728960a7e40784573c8de25", - "revisionTime": "2017-02-28T11:03:51Z" + "revision": "ec41fbe9d998e431319d83b5625b625b3c7fb4e5", + "revisionTime": "2017-03-16T12:49:24Z" }, { - "checksumSHA1": "8xHrMYdHShABfXqVHE9hFArEyr8=", + "checksumSHA1": "z3nvCzyF6t4c68evbgXRvB+O1ws=", "path": "github.com/TheThingsNetwork/go-account-lib/rights", - "revision": "90ce9cacdb918a924728960a7e40784573c8de25", - "revisionTime": "2017-02-28T11:03:51Z" + "revision": "ec41fbe9d998e431319d83b5625b625b3c7fb4e5", + "revisionTime": "2017-03-16T12:49:24Z" }, { "checksumSHA1": "SvUkgVuVVVssqpXbE8OfeWCm0KU=", "path": "github.com/TheThingsNetwork/go-account-lib/scope", - "revision": "90ce9cacdb918a924728960a7e40784573c8de25", - "revisionTime": "2017-02-28T11:03:51Z" + "revision": "ec41fbe9d998e431319d83b5625b625b3c7fb4e5", + "revisionTime": "2017-03-16T12:49:24Z" }, { "checksumSHA1": "RpKXQd5sp9/jsWM991S7OhE9/ME=", "path": "github.com/TheThingsNetwork/go-account-lib/tokenkey", - "revision": "90ce9cacdb918a924728960a7e40784573c8de25", - "revisionTime": "2017-02-28T11:03:51Z" + "revision": "ec41fbe9d998e431319d83b5625b625b3c7fb4e5", + "revisionTime": "2017-03-16T12:49:24Z" }, { "checksumSHA1": "48WYq5L+4Gkl5NXlhDJM0Uzt/7o=", "path": "github.com/TheThingsNetwork/go-account-lib/tokens", - "revision": "90ce9cacdb918a924728960a7e40784573c8de25", - "revisionTime": "2017-02-28T11:03:51Z" + "revision": "ec41fbe9d998e431319d83b5625b625b3c7fb4e5", + "revisionTime": "2017-03-16T12:49:24Z" }, { "checksumSHA1": "ceMHzBTkbEJGWevGmCq+QwSLRDE=", "path": "github.com/TheThingsNetwork/go-account-lib/util", - "revision": "90ce9cacdb918a924728960a7e40784573c8de25", - "revisionTime": "2017-02-28T11:03:51Z" + "revision": "ec41fbe9d998e431319d83b5625b625b3c7fb4e5", + "revisionTime": "2017-03-16T12:49:24Z" }, { "checksumSHA1": "x795Q87cyvAAeuxXxft5iwd1Los=", "path": "github.com/TheThingsNetwork/go-utils/backoff", - "revision": "a921fe169d25314d639da4f6b75eb67bf598463a", - "revisionTime": "2017-03-09T17:03:45Z" + "revision": "9633d6a3fb4cce550ba1298effc32bed74d52c84", + "revisionTime": "2017-03-21T13:11:45Z" }, { "checksumSHA1": "Dl7PzR7MCkAujv/tceQkrTzVq0U=", "path": "github.com/TheThingsNetwork/go-utils/encoding", - "revision": "a921fe169d25314d639da4f6b75eb67bf598463a", - "revisionTime": "2017-03-09T17:03:45Z" + "revision": "9633d6a3fb4cce550ba1298effc32bed74d52c84", + "revisionTime": "2017-03-21T13:11:45Z" }, { "checksumSHA1": "Q+Nny6VBPlH007VXteMBuMPYikI=", "path": "github.com/TheThingsNetwork/go-utils/grpc/interceptor", - "revision": "a921fe169d25314d639da4f6b75eb67bf598463a", - "revisionTime": "2017-03-09T17:03:45Z" + "revision": "9633d6a3fb4cce550ba1298effc32bed74d52c84", + "revisionTime": "2017-03-21T13:11:45Z" }, { - "checksumSHA1": "QBtwAfoW7uOWdjvLg+glKNNETxs=", + "checksumSHA1": "/0L+3YQZjPiYtK03Th/ThuUjNN4=", "path": "github.com/TheThingsNetwork/go-utils/grpc/restartstream", - "revision": "a921fe169d25314d639da4f6b75eb67bf598463a", - "revisionTime": "2017-03-09T17:03:45Z" + "revision": "9633d6a3fb4cce550ba1298effc32bed74d52c84", + "revisionTime": "2017-03-21T13:11:45Z" }, { "checksumSHA1": "T7iFQUlCUAv4cJNDZC0//46Nbio=", "path": "github.com/TheThingsNetwork/go-utils/handlers/cli", - "revision": "a921fe169d25314d639da4f6b75eb67bf598463a", - "revisionTime": "2017-03-09T17:03:45Z" + "revision": "9633d6a3fb4cce550ba1298effc32bed74d52c84", + "revisionTime": "2017-03-21T13:11:45Z" }, { "checksumSHA1": "aXt7ZSqIfsHWBbJPgHFjqtyxyQ0=", "path": "github.com/TheThingsNetwork/go-utils/log", - "revision": "a921fe169d25314d639da4f6b75eb67bf598463a", - "revisionTime": "2017-03-09T17:03:45Z" + "revision": "9633d6a3fb4cce550ba1298effc32bed74d52c84", + "revisionTime": "2017-03-21T13:11:45Z" }, { "checksumSHA1": "RdI5upcV6MHSjr5Y9zogYvbeURw=", "path": "github.com/TheThingsNetwork/go-utils/log/apex", - "revision": "a921fe169d25314d639da4f6b75eb67bf598463a", - "revisionTime": "2017-03-09T17:03:45Z" + "revision": "9633d6a3fb4cce550ba1298effc32bed74d52c84", + "revisionTime": "2017-03-21T13:11:45Z" }, { "checksumSHA1": "sQ0vy3MCGY1WgK9xldn1V6pMeZk=", "path": "github.com/TheThingsNetwork/go-utils/log/grpc", - "revision": "a921fe169d25314d639da4f6b75eb67bf598463a", - "revisionTime": "2017-03-09T17:03:45Z" + "revision": "9633d6a3fb4cce550ba1298effc32bed74d52c84", + "revisionTime": "2017-03-21T13:11:45Z" }, { "checksumSHA1": "2/v0SMyHM5vgImOb1BEEDWeXZEY=", "path": "github.com/TheThingsNetwork/go-utils/pseudorandom", - "revision": "a921fe169d25314d639da4f6b75eb67bf598463a", - "revisionTime": "2017-03-09T17:03:45Z" + "revision": "9633d6a3fb4cce550ba1298effc32bed74d52c84", + "revisionTime": "2017-03-21T13:11:45Z" }, { "checksumSHA1": "iYa+qSqzqZwpmoibM8/1X+aC3sI=", "path": "github.com/TheThingsNetwork/go-utils/random", - "revision": "a921fe169d25314d639da4f6b75eb67bf598463a", - "revisionTime": "2017-03-09T17:03:45Z" + "revision": "9633d6a3fb4cce550ba1298effc32bed74d52c84", + "revisionTime": "2017-03-21T13:11:45Z" }, { "checksumSHA1": "kLFTtAVcjZbHXybodGAqJ8wxflY=", "path": "github.com/TheThingsNetwork/go-utils/roots", - "revision": "a921fe169d25314d639da4f6b75eb67bf598463a", - "revisionTime": "2017-03-09T17:03:45Z" + "revision": "9633d6a3fb4cce550ba1298effc32bed74d52c84", + "revisionTime": "2017-03-21T13:11:45Z" }, { "checksumSHA1": "EZ0pNaUAiIbJuT5c0Sew85egLgw=", @@ -177,28 +177,28 @@ "revisionTime": "2017-02-22T07:03:41Z" }, { - "checksumSHA1": "dPI35hOM4TMrLugm6M1js14jHVQ=", + "checksumSHA1": "YL0j2l6a5wmMjbt8S8O8lwCIkys=", "path": "github.com/asaskevich/govalidator", - "revision": "fdf19785fd3558d619ef81212f5edf1d6c2a5911", - "revisionTime": "2017-01-04T21:11:26Z" + "revision": "872bb01704d183fe276bce6fa429408a87661998", + "revisionTime": "2017-03-20T17:24:29Z" }, { - "checksumSHA1": "h/y88wOmQRm6qE29txL9LndV1yY=", + "checksumSHA1": "FzRaJjgGYmcDaaAQXZGGaZieNpE=", "path": "github.com/bluele/gcache", - "revision": "d920a928be099e4b9a6272f41699f4693cdcee5b", - "revisionTime": "2016-12-12T14:19:04Z" + "revision": "aa23c07ed35b259f984c47532ae8989300187199", + "revisionTime": "2017-03-15T08:16:58Z" }, { "checksumSHA1": "ZHpBCsUv5lUxdt1gF9HWRZ4IffI=", "path": "github.com/brocaar/lorawan", - "revision": "14f9457caf7092dc7975ba594e7c1206cc6901d5", - "revisionTime": "2017-03-01T09:28:16Z" + "revision": "c61721fa96c85c25ea7ba635fc477224344ddbe3", + "revisionTime": "2017-03-20T08:06:39Z" }, { "checksumSHA1": "SlDtchaAX7SV5nD/BFP4x6fnmVk=", "path": "github.com/brocaar/lorawan/band", - "revision": "14f9457caf7092dc7975ba594e7c1206cc6901d5", - "revisionTime": "2017-03-01T09:28:16Z" + "revision": "c61721fa96c85c25ea7ba635fc477224344ddbe3", + "revisionTime": "2017-03-20T08:06:39Z" }, { "checksumSHA1": "2Fy1Y6Z3lRRX1891WF/+HT4XS2I=", @@ -207,16 +207,16 @@ "revisionTime": "2017-02-01T22:58:49Z" }, { - "checksumSHA1": "br8f8s0vtwRq5P2DEtByXg5s4V0=", + "checksumSHA1": "YJQqkH5JJ/h8r8245J9GCP4zG38=", "path": "github.com/eclipse/paho.mqtt.golang", - "revision": "d4f545eb108a2d19f9b1a735689dbfb719bc21fb", - "revisionTime": "2016-12-24T12:24:24Z" + "revision": "dfe35ae8bd6ead198596c5e5f4af1f55914741cf", + "revisionTime": "2017-03-08T16:30:23Z" }, { "checksumSHA1": "wPreCwXsA/oU2R+lkOGpR6skdA0=", "path": "github.com/eclipse/paho.mqtt.golang/packets", - "revision": "d4f545eb108a2d19f9b1a735689dbfb719bc21fb", - "revisionTime": "2016-12-24T12:24:24Z" + "revision": "dfe35ae8bd6ead198596c5e5f4af1f55914741cf", + "revisionTime": "2017-03-08T16:30:23Z" }, { "checksumSHA1": "40Ns85VYa4smQPcewZ7SOdfLnKU=", @@ -225,10 +225,10 @@ "revisionTime": "2017-01-03T08:10:50Z" }, { - "checksumSHA1": "JhI3dzfib2NMGL11NiUswioZP8U=", + "checksumSHA1": "D1iQuhVl+JB2+WCjsspnlXQCPnM=", "path": "github.com/fsnotify/fsnotify", - "revision": "7d7316ed6e1ed2de075aab8dfc76de5d158d66e1", - "revisionTime": "2016-08-16T05:19:40Z" + "revision": "ff7bc41d4007f67e5456703c34342df4e0113f64", + "revisionTime": "2017-03-21T12:55:22Z" }, { "checksumSHA1": "wDZdTaY9JiqqqnF4c3pHP71nWmk=", @@ -243,40 +243,40 @@ "revisionTime": "2017-02-09T15:13:32Z" }, { - "checksumSHA1": "KNyoFOwJJ2A4Jdsf/5E80sfPfqw=", + "checksumSHA1": "BEi3mhcDkClKwleMuQGVPCQXFR4=", "path": "github.com/gogo/protobuf/gogoproto", - "revision": "83faaee7bbbdf0c59310ec67d340bc0cbe77053f", - "revisionTime": "2017-02-26T19:25:51Z" + "revision": "100ba4e885062801d56799d78530b73b178a78f3", + "revisionTime": "2017-03-07T18:04:53Z" }, { "checksumSHA1": "+RHv6D0Db8Yke6G9cxGjEW61viM=", "path": "github.com/gogo/protobuf/jsonpb", - "revision": "83faaee7bbbdf0c59310ec67d340bc0cbe77053f", - "revisionTime": "2017-02-26T19:25:51Z" + "revision": "100ba4e885062801d56799d78530b73b178a78f3", + "revisionTime": "2017-03-07T18:04:53Z" }, { "checksumSHA1": "6ZxSmrIx3Jd15aou16oG0HPylP4=", "path": "github.com/gogo/protobuf/proto", - "revision": "83faaee7bbbdf0c59310ec67d340bc0cbe77053f", - "revisionTime": "2017-02-26T19:25:51Z" + "revision": "100ba4e885062801d56799d78530b73b178a78f3", + "revisionTime": "2017-03-07T18:04:53Z" }, { "checksumSHA1": "wg7MHG+Fuc0ZGWFIlAZJulPxR2s=", "path": "github.com/gogo/protobuf/protoc-gen-gogo/descriptor", - "revision": "83faaee7bbbdf0c59310ec67d340bc0cbe77053f", - "revisionTime": "2017-02-26T19:25:51Z" + "revision": "100ba4e885062801d56799d78530b73b178a78f3", + "revisionTime": "2017-03-07T18:04:53Z" }, { "checksumSHA1": "HPVQZu059/Rfw2bAWM538bVTcUc=", "path": "github.com/gogo/protobuf/sortkeys", - "revision": "83faaee7bbbdf0c59310ec67d340bc0cbe77053f", - "revisionTime": "2017-02-26T19:25:51Z" + "revision": "100ba4e885062801d56799d78530b73b178a78f3", + "revisionTime": "2017-03-07T18:04:53Z" }, { "checksumSHA1": "tT8kN2bRsmY6EKdR5U5sfBDAJFc=", "path": "github.com/gogo/protobuf/types", - "revision": "83faaee7bbbdf0c59310ec67d340bc0cbe77053f", - "revisionTime": "2017-02-26T19:25:51Z" + "revision": "100ba4e885062801d56799d78530b73b178a78f3", + "revisionTime": "2017-03-07T18:04:53Z" }, { "checksumSHA1": "JSHl8b3nI8EWvzm+uyrIqj2Hiu4=", @@ -287,38 +287,38 @@ { "checksumSHA1": "APDDi2ohrU7OkChQCekD9tSVUhs=", "path": "github.com/golang/protobuf/jsonpb", - "revision": "69b215d01a5606c843240eab4937eab3acee6530", - "revisionTime": "2017-02-17T23:44:32Z" + "revision": "c9c7427a2a70d2eb3bafa0ab2dc163e45f143317", + "revisionTime": "2017-03-07T00:15:33Z" }, { "checksumSHA1": "kBeNcaKk56FguvPSUCEaH6AxpRc=", "path": "github.com/golang/protobuf/proto", - "revision": "69b215d01a5606c843240eab4937eab3acee6530", - "revisionTime": "2017-02-17T23:44:32Z" + "revision": "c9c7427a2a70d2eb3bafa0ab2dc163e45f143317", + "revisionTime": "2017-03-07T00:15:33Z" }, { "checksumSHA1": "AjyXQ5eohrCPS/jSWZFPn5E8wnQ=", "path": "github.com/golang/protobuf/protoc-gen-go/descriptor", - "revision": "69b215d01a5606c843240eab4937eab3acee6530", - "revisionTime": "2017-02-17T23:44:32Z" + "revision": "c9c7427a2a70d2eb3bafa0ab2dc163e45f143317", + "revisionTime": "2017-03-07T00:15:33Z" }, { "checksumSHA1": "T/EqMkqzvjQUL1c+yN32kketgfE=", "path": "github.com/golang/protobuf/protoc-gen-go/generator", - "revision": "69b215d01a5606c843240eab4937eab3acee6530", - "revisionTime": "2017-02-17T23:44:32Z" + "revision": "c9c7427a2a70d2eb3bafa0ab2dc163e45f143317", + "revisionTime": "2017-03-07T00:15:33Z" }, { "checksumSHA1": "zps2+aJoFhpFf2F8TsU9zCGXL2c=", "path": "github.com/golang/protobuf/protoc-gen-go/plugin", - "revision": "69b215d01a5606c843240eab4937eab3acee6530", - "revisionTime": "2017-02-17T23:44:32Z" + "revision": "c9c7427a2a70d2eb3bafa0ab2dc163e45f143317", + "revisionTime": "2017-03-07T00:15:33Z" }, { "checksumSHA1": "9wOTz0iWfOSTSTmUkoq0WYkiMdY=", "path": "github.com/golang/protobuf/ptypes/empty", - "revision": "69b215d01a5606c843240eab4937eab3acee6530", - "revisionTime": "2017-02-17T23:44:32Z" + "revision": "c9c7427a2a70d2eb3bafa0ab2dc163e45f143317", + "revisionTime": "2017-03-07T00:15:33Z" }, { "checksumSHA1": "cACEkFM7kIL+NVF6jSJPY2tW4d8=", @@ -339,28 +339,28 @@ "revisionTime": "2016-04-04T20:39:58Z" }, { - "checksumSHA1": "SeAuuufiXeiMVlYi/j8YT9kPrCM=", + "checksumSHA1": "wFjV19ovhVabAqB6NTF8sWAJDIA=", "path": "github.com/grpc-ecosystem/grpc-gateway/runtime", - "revision": "bf8e298852d5c258796b43fd5b0db27c53b8787d", - "revisionTime": "2017-02-28T21:32:21Z" + "revision": "7980281cb6ac1dacf3f0025c53870c817c1c9511", + "revisionTime": "2017-03-15T21:35:39Z" }, { - "checksumSHA1": "x396LPNfci/5x8aVJbliQHH11HQ=", + "checksumSHA1": "et8uiXNw6yM96U/UZgn2vKtyr3Q=", "path": "github.com/grpc-ecosystem/grpc-gateway/runtime/internal", - "revision": "bf8e298852d5c258796b43fd5b0db27c53b8787d", - "revisionTime": "2017-02-28T21:32:21Z" + "revision": "7980281cb6ac1dacf3f0025c53870c817c1c9511", + "revisionTime": "2017-03-15T21:35:39Z" }, { - "checksumSHA1": "NCyVGekDqPMTHHK4ZbEDPZeiN2s=", + "checksumSHA1": "CKrlwtWMXiznQrgS2o/obvP7HTM=", "path": "github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis/google/api", - "revision": "bf8e298852d5c258796b43fd5b0db27c53b8787d", - "revisionTime": "2017-02-28T21:32:21Z" + "revision": "7980281cb6ac1dacf3f0025c53870c817c1c9511", + "revisionTime": "2017-03-15T21:35:39Z" }, { "checksumSHA1": "vqiK5r5dntV7JNZ+ZsGlD0Samos=", "path": "github.com/grpc-ecosystem/grpc-gateway/utilities", - "revision": "bf8e298852d5c258796b43fd5b0db27c53b8787d", - "revisionTime": "2017-02-28T21:32:21Z" + "revision": "7980281cb6ac1dacf3f0025c53870c817c1c9511", + "revisionTime": "2017-03-15T21:35:39Z" }, { "checksumSHA1": "Ok3Csn6Voou7pQT6Dv2mkwpqFtw=", @@ -447,22 +447,22 @@ "revisionTime": "2016-11-11T03:08:13Z" }, { - "checksumSHA1": "sKheT5xw89Tbu2Q071FQO27CVmE=", + "checksumSHA1": "+NJzbj9fa71GPyEhyYcB2urBiXY=", "path": "github.com/juju/ratelimit", - "revision": "77ed1c8a01217656d2080ad51981f6e99adaa177", - "revisionTime": "2015-11-25T20:19:25Z" + "revision": "acf38b000a03e4ab89e40f20f1e548f4e6ac7f72", + "revisionTime": "2017-03-14T01:17:55Z" }, { - "checksumSHA1": "Mnp6EGM0pdGxMe19f6dg+ipjaSo=", + "checksumSHA1": "eWtEV0iBNTL6DRJVqEeniS+thHA=", "path": "github.com/kardianos/osext", - "revision": "9b883c5eb462dd5cb1b0a7a104fe86bc6b9bd391", - "revisionTime": "2017-02-07T19:16:55Z" + "revision": "9d302b58e975387d0b4d9be876622c86cefe64be", + "revisionTime": "2017-03-09T17:28:38Z" }, { - "checksumSHA1": "KR72MpmwQRMYJuK0BHi2RWgGU2o=", + "checksumSHA1": "UvboqgDDSAbGORdtr5tBpkwYR0A=", "path": "github.com/magiconair/properties", - "revision": "b3b15ef068fd0b17ddf408a23669f20811d194d2", - "revisionTime": "2017-01-13T09:48:12Z" + "revision": "51463bfca2576e06c62a8504b5c0f06d61312647", + "revisionTime": "2017-03-21T09:30:39Z" }, { "checksumSHA1": "MNkKJyk2TazKMJYbal5wFHybpyA=", @@ -477,16 +477,16 @@ "revisionTime": "2016-12-03T19:45:07Z" }, { - "checksumSHA1": "wTMmmuol+KHkz9EwVKaOjd2O4cs=", + "checksumSHA1": "MlX15lJuV8DYARX5RJY8rqrSEWQ=", "path": "github.com/mitchellh/mapstructure", - "revision": "db1efb556f84b25a0a13a04aad883943538ad2e0", - "revisionTime": "2017-01-25T05:19:37Z" + "revision": "53818660ed4955e899c0bcafa97299a388bd7c8e", + "revisionTime": "2017-03-07T20:11:23Z" }, { "checksumSHA1": "yek3BXs6poPtAGqDKC1zTz6+kD0=", "path": "github.com/mwitkow/go-grpc-middleware", - "revision": "6dc2fa3f90db6652172ece08c2d237c1aa0da2d3", - "revisionTime": "2017-02-26T23:31:47Z" + "revision": "2b82014fe1a83cf1d9190e0e8c7447b47a3fd57e", + "revisionTime": "2017-03-18T12:30:55Z" }, { "checksumSHA1": "F1IYMLBLAZaTOWnmXsgaxTGvrWI=", @@ -495,16 +495,16 @@ "revisionTime": "2017-02-27T22:03:11Z" }, { - "checksumSHA1": "t9EsJFHroSYzr8d236ZVqw4Mzvc=", + "checksumSHA1": "zURAtoX9XMigy+k8HUhHFAEir5U=", "path": "github.com/pelletier/go-toml", - "revision": "361678322880708ac144df8575e6f01144ba1404", - "revisionTime": "2017-02-27T22:29:04Z" + "revision": "62e2d802edc0351341ea74d9851dc7dd66c3b279", + "revisionTime": "2017-03-21T09:01:44Z" }, { "checksumSHA1": "ynJSWoF6v+3zMnh9R0QmmG6iGV8=", "path": "github.com/pkg/errors", - "revision": "bfd5150e4e41705ded2129ec33379de1cb90b513", - "revisionTime": "2017-02-27T22:00:37Z" + "revision": "ff09b135c25aae272398c51a07235b90a75aa4f0", + "revisionTime": "2017-03-16T20:15:38Z" }, { "checksumSHA1": "KAzbLjI9MzW2tjfcAsK75lVRp6I=", @@ -515,86 +515,86 @@ { "checksumSHA1": "5uqO4ITTDMklKi3uNaE/D9LQ5nM=", "path": "github.com/robertkrimen/otto", - "revision": "6a77b7cbc37d0c39f7d5fa5766826e541df31fd5", - "revisionTime": "2017-02-05T01:36:59Z" + "revision": "1861f2408b93febb6dca3e39483fa6fe63d6e70f", + "revisionTime": "2017-03-08T08:57:57Z" }, { "checksumSHA1": "qgziiO3/QDVJMKw2nGrUbC8QldY=", "path": "github.com/robertkrimen/otto/ast", - "revision": "6a77b7cbc37d0c39f7d5fa5766826e541df31fd5", - "revisionTime": "2017-02-05T01:36:59Z" + "revision": "1861f2408b93febb6dca3e39483fa6fe63d6e70f", + "revisionTime": "2017-03-08T08:57:57Z" }, { "checksumSHA1": "L0KsB2EzTlPgv0iae3q3SukNW7U=", "path": "github.com/robertkrimen/otto/dbg", - "revision": "6a77b7cbc37d0c39f7d5fa5766826e541df31fd5", - "revisionTime": "2017-02-05T01:36:59Z" + "revision": "1861f2408b93febb6dca3e39483fa6fe63d6e70f", + "revisionTime": "2017-03-08T08:57:57Z" }, { "checksumSHA1": "euDLJKhw4doeTSxjEoezjxYXLzs=", "path": "github.com/robertkrimen/otto/file", - "revision": "6a77b7cbc37d0c39f7d5fa5766826e541df31fd5", - "revisionTime": "2017-02-05T01:36:59Z" + "revision": "1861f2408b93febb6dca3e39483fa6fe63d6e70f", + "revisionTime": "2017-03-08T08:57:57Z" }, { "checksumSHA1": "LLuLITFO8chqSG0+APJIy5NtOHU=", "path": "github.com/robertkrimen/otto/parser", - "revision": "6a77b7cbc37d0c39f7d5fa5766826e541df31fd5", - "revisionTime": "2017-02-05T01:36:59Z" + "revision": "1861f2408b93febb6dca3e39483fa6fe63d6e70f", + "revisionTime": "2017-03-08T08:57:57Z" }, { "checksumSHA1": "7J/7NaYRqKhBvZ+dTIutsEoEgFw=", "path": "github.com/robertkrimen/otto/registry", - "revision": "6a77b7cbc37d0c39f7d5fa5766826e541df31fd5", - "revisionTime": "2017-02-05T01:36:59Z" + "revision": "1861f2408b93febb6dca3e39483fa6fe63d6e70f", + "revisionTime": "2017-03-08T08:57:57Z" }, { "checksumSHA1": "/jMXYuXycBpTqWhRyJ2xsqvHvQI=", "path": "github.com/robertkrimen/otto/token", - "revision": "6a77b7cbc37d0c39f7d5fa5766826e541df31fd5", - "revisionTime": "2017-02-05T01:36:59Z" + "revision": "1861f2408b93febb6dca3e39483fa6fe63d6e70f", + "revisionTime": "2017-03-08T08:57:57Z" }, { - "checksumSHA1": "P1XAi2KfwlQvZijLYIV9hyayRPc=", + "checksumSHA1": "oHrXRExaBdgZHdy3QiATjS+eprc=", "path": "github.com/shirou/gopsutil/cpu", - "revision": "d371ba1293cb48fedc6850526ea48b3846c54f2c", - "revisionTime": "2017-02-22T14:42:03Z" + "revision": "423a8ff2a6da39265b3d05d36ca7e3f18fc8d733", + "revisionTime": "2017-03-21T01:27:21Z" }, { - "checksumSHA1": "I0607ubCPNWferRg3QIitF/UdRA=", + "checksumSHA1": "iT9WvLvEKQREitNP/mAyN145Rpk=", "path": "github.com/shirou/gopsutil/host", - "revision": "d371ba1293cb48fedc6850526ea48b3846c54f2c", - "revisionTime": "2017-02-22T14:42:03Z" + "revision": "423a8ff2a6da39265b3d05d36ca7e3f18fc8d733", + "revisionTime": "2017-03-21T01:27:21Z" }, { - "checksumSHA1": "S8QxcgsIi8ckcQRKNGCn4Z5Lymw=", + "checksumSHA1": "hKDsT0KAOtA7UqiXYdO0RahnQZ8=", "path": "github.com/shirou/gopsutil/internal/common", - "revision": "d371ba1293cb48fedc6850526ea48b3846c54f2c", - "revisionTime": "2017-02-22T14:42:03Z" + "revision": "423a8ff2a6da39265b3d05d36ca7e3f18fc8d733", + "revisionTime": "2017-03-21T01:27:21Z" }, { "checksumSHA1": "jB8En6qWQ7G2yPJey4uY1FvOjWM=", "path": "github.com/shirou/gopsutil/load", - "revision": "d371ba1293cb48fedc6850526ea48b3846c54f2c", - "revisionTime": "2017-02-22T14:42:03Z" + "revision": "423a8ff2a6da39265b3d05d36ca7e3f18fc8d733", + "revisionTime": "2017-03-21T01:27:21Z" }, { - "checksumSHA1": "XQwjGKI51Y3aQ3/jNyRh9Gnprgg=", + "checksumSHA1": "zkNNauN735M7Jghr+MsARbb+/xQ=", "path": "github.com/shirou/gopsutil/mem", - "revision": "d371ba1293cb48fedc6850526ea48b3846c54f2c", - "revisionTime": "2017-02-22T14:42:03Z" + "revision": "423a8ff2a6da39265b3d05d36ca7e3f18fc8d733", + "revisionTime": "2017-03-21T01:27:21Z" }, { "checksumSHA1": "zqdJo70e2vfUaNAs8hs3CA7ZmfQ=", "path": "github.com/shirou/gopsutil/net", - "revision": "d371ba1293cb48fedc6850526ea48b3846c54f2c", - "revisionTime": "2017-02-22T14:42:03Z" + "revision": "423a8ff2a6da39265b3d05d36ca7e3f18fc8d733", + "revisionTime": "2017-03-21T01:27:21Z" }, { "checksumSHA1": "g/TH+QY+CH4rJTg0BJ1vdLWIFaA=", "path": "github.com/shirou/gopsutil/process", - "revision": "d371ba1293cb48fedc6850526ea48b3846c54f2c", - "revisionTime": "2017-02-22T14:42:03Z" + "revision": "423a8ff2a6da39265b3d05d36ca7e3f18fc8d733", + "revisionTime": "2017-03-21T01:27:21Z" }, { "checksumSHA1": "Nve7SpDmjsv6+rhkXAkfg/UQx94=", @@ -639,16 +639,16 @@ "revisionTime": "2017-02-17T16:41:46Z" }, { - "checksumSHA1": "Vg25dPyNytU7tOwM8wMqSk3zLDI=", + "checksumSHA1": "iPdWlXqln9EMtGPJaabe82Nnr1s=", "path": "github.com/spf13/cast", - "revision": "f820543c3592e283e311a60d2a600a664e39f6f7", - "revisionTime": "2017-02-21T15:10:31Z" + "revision": "ce135a4ebeee6cfe9a26c93ee0d37825f26113c7", + "revisionTime": "2017-03-03T19:59:13Z" }, { - "checksumSHA1": "hgI9+1CnDX7GUG7WuHqjYH2nyA0=", + "checksumSHA1": "lU+gVOtSFnBKx11uhPL8b9v0uTE=", "path": "github.com/spf13/cobra", - "revision": "fcd0c5a1df88f5d6784cb4feead962c3f3d0b66c", - "revisionTime": "2017-02-28T19:17:48Z" + "revision": "7be4beda01ec05d0b93d80b3facd2b6f44080d94", + "revisionTime": "2017-03-14T17:12:53Z" }, { "checksumSHA1": "9pkkhgKp3mwSreiML3plQlQYdLQ=", @@ -663,16 +663,16 @@ "revisionTime": "2017-01-30T21:42:45Z" }, { - "checksumSHA1": "cNe3MKwsFLDRzRjKEtOduUiG344=", + "checksumSHA1": "8D41Y1ZTK8W3pxzvp1eH9u3CNl4=", "path": "github.com/spf13/viper", - "revision": "7538d73b4eb9511d85a9f1dfef202eeb8ac260f4", - "revisionTime": "2017-02-17T16:38:17Z" + "revision": "84f94806c67f59dd7ae87bc5351f7a9c94a4558d", + "revisionTime": "2017-03-15T13:43:09Z" }, { - "checksumSHA1": "PNFiPbDwLwZs7CcqH91LH7gXI5U=", + "checksumSHA1": "Om0cn0L5ewCIBcORQAXD8guTa9M=", "path": "github.com/streadway/amqp", - "revision": "d75c3a341ff43309ad0cb69ac8bdbd1d8772775f", - "revisionTime": "2017-02-03T13:40:38Z" + "revision": "afe8eee29a74d213b1f3fb2586058157c397da60", + "revisionTime": "2017-03-13T17:48:48Z" }, { "checksumSHA1": "F9X1T07FTXRxBrskitXNtlxZJ6w=", @@ -695,242 +695,272 @@ { "checksumSHA1": "xiderUuvye8Kpn7yX3niiJg32bE=", "path": "golang.org/x/crypto/ssh/terminal", - "revision": "453249f01cfeb54c3d549ddb75ff152ca243f9d8", - "revisionTime": "2017-02-08T20:51:15Z" + "revision": "459e26527287adbc2adcc5d0d49abff9a5f315a7", + "revisionTime": "2017-03-17T13:29:17Z" }, { "checksumSHA1": "Y+HGqEkYM15ir+J93MEaHdyFy0c=", "path": "golang.org/x/net/context", - "revision": "906cda9512f77671ab44f8c8563b13a8e707b230", - "revisionTime": "2017-02-18T20:36:51Z" + "revision": "a6577fac2d73be281a500b310739095313165611", + "revisionTime": "2017-03-08T20:54:49Z" }, { - "checksumSHA1": "HwLsnapk27meOcqcx4G2iaAysq4=", + "checksumSHA1": "cdT+oqPhYKGf3r+HkPo51IauZnA=", "path": "golang.org/x/net/http2", - "revision": "906cda9512f77671ab44f8c8563b13a8e707b230", - "revisionTime": "2017-02-18T20:36:51Z" + "revision": "a6577fac2d73be281a500b310739095313165611", + "revisionTime": "2017-03-08T20:54:49Z" }, { "checksumSHA1": "kyClpesDqa6LA7CFPGgKrX9NKkA=", "path": "golang.org/x/net/http2/hpack", - "revision": "906cda9512f77671ab44f8c8563b13a8e707b230", - "revisionTime": "2017-02-18T20:36:51Z" + "revision": "a6577fac2d73be281a500b310739095313165611", + "revisionTime": "2017-03-08T20:54:49Z" }, { "checksumSHA1": "GIGmSrYACByf5JDIP9ByBZksY80=", "path": "golang.org/x/net/idna", - "revision": "906cda9512f77671ab44f8c8563b13a8e707b230", - "revisionTime": "2017-02-18T20:36:51Z" + "revision": "a6577fac2d73be281a500b310739095313165611", + "revisionTime": "2017-03-08T20:54:49Z" }, { "checksumSHA1": "UxahDzW2v4mf/+aFxruuupaoIwo=", "path": "golang.org/x/net/internal/timeseries", - "revision": "906cda9512f77671ab44f8c8563b13a8e707b230", - "revisionTime": "2017-02-18T20:36:51Z" + "revision": "a6577fac2d73be281a500b310739095313165611", + "revisionTime": "2017-03-08T20:54:49Z" }, { "checksumSHA1": "3xyuaSNmClqG4YWC7g0isQIbUTc=", "path": "golang.org/x/net/lex/httplex", - "revision": "906cda9512f77671ab44f8c8563b13a8e707b230", - "revisionTime": "2017-02-18T20:36:51Z" + "revision": "a6577fac2d73be281a500b310739095313165611", + "revisionTime": "2017-03-08T20:54:49Z" }, { "checksumSHA1": "GQHKESPeCcAsnerZPtHadvKUIzs=", "path": "golang.org/x/net/trace", - "revision": "906cda9512f77671ab44f8c8563b13a8e707b230", - "revisionTime": "2017-02-18T20:36:51Z" + "revision": "a6577fac2d73be281a500b310739095313165611", + "revisionTime": "2017-03-08T20:54:49Z" }, { "checksumSHA1": "7EZyXN0EmZLgGxZxK01IJua4c8o=", "path": "golang.org/x/net/websocket", - "revision": "906cda9512f77671ab44f8c8563b13a8e707b230", - "revisionTime": "2017-02-18T20:36:51Z" + "revision": "a6577fac2d73be281a500b310739095313165611", + "revisionTime": "2017-03-08T20:54:49Z" }, { - "checksumSHA1": "Zt7DIRCaUg5qfhfxyR1wCA+EjCE=", + "checksumSHA1": "SjCoL7KD7qBmgSuqGTCAuUhigDk=", "path": "golang.org/x/oauth2", - "revision": "810daf0509f84359cfa9527f6ea4886f1dbabfc5", - "revisionTime": "2017-02-27T10:37:29Z" + "revision": "7fdf09982454086d5570c7db3e11f360194830ca", + "revisionTime": "2017-03-21T01:28:43Z" }, { - "checksumSHA1": "gChvVZYdb6Bw/vjIpfYJfNvXPoU=", + "checksumSHA1": "BAkyxbaxkrZbzGtfG5iX8v6ypIo=", "path": "golang.org/x/oauth2/internal", - "revision": "810daf0509f84359cfa9527f6ea4886f1dbabfc5", - "revisionTime": "2017-02-27T10:37:29Z" + "revision": "7fdf09982454086d5570c7db3e11f360194830ca", + "revisionTime": "2017-03-21T01:28:43Z" }, { - "checksumSHA1": "hYqtXfZxF9wtVdtl2Ib3YvO/49Y=", + "checksumSHA1": "/oZpHfYc+ZgOwYAhlvcMhmETYpw=", "path": "golang.org/x/sys/unix", - "revision": "76cc09b634294339fa19ec41b5f2a0b3932cea8b", - "revisionTime": "2017-02-28T22:35:16Z" + "revision": "99f16d856c9836c42d24e7ab64ea72916925fa97", + "revisionTime": "2017-03-08T15:04:45Z" + }, + { + "checksumSHA1": "kv3jbPJGCczHVQ7g51am1MxlD1c=", + "path": "golang.org/x/text/internal/gen", + "revision": "f28f36722d5ef2f9655ad3de1f248e3e52ad5ebd", + "revisionTime": "2017-02-28T17:26:26Z" + }, + { + "checksumSHA1": "47nwiUyVBY2RKoEGXmCSvusY4Js=", + "path": "golang.org/x/text/internal/triegen", + "revision": "f28f36722d5ef2f9655ad3de1f248e3e52ad5ebd", + "revisionTime": "2017-02-28T17:26:26Z" + }, + { + "checksumSHA1": "Yd5wMObzagIfCiKLpZbtBIrOUA4=", + "path": "golang.org/x/text/internal/ucd", + "revision": "f28f36722d5ef2f9655ad3de1f248e3e52ad5ebd", + "revisionTime": "2017-02-28T17:26:26Z" }, { "checksumSHA1": "ziMb9+ANGRJSSIuxYdRbA+cDRBQ=", "path": "golang.org/x/text/transform", - "revision": "d680ca3ed853995402af43b866311167281bdc20", - "revisionTime": "2017-02-21T19:42:33Z" + "revision": "f28f36722d5ef2f9655ad3de1f248e3e52ad5ebd", + "revisionTime": "2017-02-28T17:26:26Z" + }, + { + "checksumSHA1": "i14IZXKECObKRUNvTr7xivSL1IU=", + "path": "golang.org/x/text/unicode/cldr", + "revision": "f28f36722d5ef2f9655ad3de1f248e3e52ad5ebd", + "revisionTime": "2017-02-28T17:26:26Z" }, { "checksumSHA1": "7Hjtgu1Yu+Ks0cKf2ldRhXAu/LE=", "path": "golang.org/x/text/unicode/norm", - "revision": "d680ca3ed853995402af43b866311167281bdc20", - "revisionTime": "2017-02-21T19:42:33Z" + "revision": "f28f36722d5ef2f9655ad3de1f248e3e52ad5ebd", + "revisionTime": "2017-02-28T17:26:26Z" }, { "checksumSHA1": "R8rc2A/LgT4IRS6TzUZfhkUVQzQ=", "path": "google.golang.org/appengine/internal", - "revision": "3a452f9e00122ead39586d68ffdb9c6e1326af3c", - "revisionTime": "2017-02-22T22:47:31Z" + "revision": "b79c28f0197795b4050bfcd7c4c2209136c594b1", + "revisionTime": "2017-03-07T23:55:54Z" }, { "checksumSHA1": "TsNO8P0xUlLNyh3Ic/tzSp/fDWM=", "path": "google.golang.org/appengine/internal/base", - "revision": "3a452f9e00122ead39586d68ffdb9c6e1326af3c", - "revisionTime": "2017-02-22T22:47:31Z" + "revision": "b79c28f0197795b4050bfcd7c4c2209136c594b1", + "revisionTime": "2017-03-07T23:55:54Z" }, { "checksumSHA1": "5QsV5oLGSfKZqTCVXP6NRz5T4Tw=", "path": "google.golang.org/appengine/internal/datastore", - "revision": "3a452f9e00122ead39586d68ffdb9c6e1326af3c", - "revisionTime": "2017-02-22T22:47:31Z" + "revision": "b79c28f0197795b4050bfcd7c4c2209136c594b1", + "revisionTime": "2017-03-07T23:55:54Z" }, { "checksumSHA1": "Gep2T9zmVYV8qZfK2gu3zrmG6QE=", "path": "google.golang.org/appengine/internal/log", - "revision": "3a452f9e00122ead39586d68ffdb9c6e1326af3c", - "revisionTime": "2017-02-22T22:47:31Z" + "revision": "b79c28f0197795b4050bfcd7c4c2209136c594b1", + "revisionTime": "2017-03-07T23:55:54Z" }, { "checksumSHA1": "a1XY7rz3BieOVqVI2Et6rKiwQCk=", "path": "google.golang.org/appengine/internal/remote_api", - "revision": "3a452f9e00122ead39586d68ffdb9c6e1326af3c", - "revisionTime": "2017-02-22T22:47:31Z" + "revision": "b79c28f0197795b4050bfcd7c4c2209136c594b1", + "revisionTime": "2017-03-07T23:55:54Z" }, { "checksumSHA1": "QtAbHtHmDzcf6vOV9eqlCpKgjiw=", "path": "google.golang.org/appengine/internal/urlfetch", - "revision": "3a452f9e00122ead39586d68ffdb9c6e1326af3c", - "revisionTime": "2017-02-22T22:47:31Z" + "revision": "b79c28f0197795b4050bfcd7c4c2209136c594b1", + "revisionTime": "2017-03-07T23:55:54Z" }, { "checksumSHA1": "akOV9pYnCbcPA8wJUutSQVibdyg=", "path": "google.golang.org/appengine/urlfetch", - "revision": "3a452f9e00122ead39586d68ffdb9c6e1326af3c", - "revisionTime": "2017-02-22T22:47:31Z" + "revision": "b79c28f0197795b4050bfcd7c4c2209136c594b1", + "revisionTime": "2017-03-07T23:55:54Z" }, { - "checksumSHA1": "BfI6QLr9TNu0e9KiZTMe1gedITw=", + "checksumSHA1": "e1bhDjvqgxsItONMBJSDt3PRVJY=", "path": "google.golang.org/grpc", - "revision": "8b2e129857480cb0f07ef7d9d10b8b252c7ac984", - "revisionTime": "2017-03-01T22:46:18Z" + "revision": "cdee119ee21e61eef7093a41ba148fa83585e143", + "revisionTime": "2017-03-14T22:44:13Z" }, { "checksumSHA1": "08icuA15HRkdYCt6H+Cs90RPQsY=", "path": "google.golang.org/grpc/codes", - "revision": "8b2e129857480cb0f07ef7d9d10b8b252c7ac984", - "revisionTime": "2017-03-01T22:46:18Z" + "revision": "cdee119ee21e61eef7093a41ba148fa83585e143", + "revisionTime": "2017-03-14T22:44:13Z" }, { "checksumSHA1": "GHCDufYjDX7weDH2udXY1pkyvEo=", "path": "google.golang.org/grpc/credentials", - "revision": "8b2e129857480cb0f07ef7d9d10b8b252c7ac984", - "revisionTime": "2017-03-01T22:46:18Z" + "revision": "cdee119ee21e61eef7093a41ba148fa83585e143", + "revisionTime": "2017-03-14T22:44:13Z" }, { "checksumSHA1": "3Lt5hNAG8qJAYSsNghR5uA1zQns=", "path": "google.golang.org/grpc/grpclog", - "revision": "8b2e129857480cb0f07ef7d9d10b8b252c7ac984", - "revisionTime": "2017-03-01T22:46:18Z" + "revision": "cdee119ee21e61eef7093a41ba148fa83585e143", + "revisionTime": "2017-03-14T22:44:13Z" }, { "checksumSHA1": "d0iunsiWfA0qXxLMNkTC4tGJnOo=", "path": "google.golang.org/grpc/health", - "revision": "8b2e129857480cb0f07ef7d9d10b8b252c7ac984", - "revisionTime": "2017-03-01T22:46:18Z" + "revision": "cdee119ee21e61eef7093a41ba148fa83585e143", + "revisionTime": "2017-03-14T22:44:13Z" }, { "checksumSHA1": "pSFXzfvPlaDBK2RsMcTiIeks4ok=", "path": "google.golang.org/grpc/health/grpc_health_v1", - "revision": "8b2e129857480cb0f07ef7d9d10b8b252c7ac984", - "revisionTime": "2017-03-01T22:46:18Z" + "revision": "cdee119ee21e61eef7093a41ba148fa83585e143", + "revisionTime": "2017-03-14T22:44:13Z" }, { "checksumSHA1": "T3Q0p8kzvXFnRkMaK/G8mCv6mc0=", "path": "google.golang.org/grpc/internal", - "revision": "8b2e129857480cb0f07ef7d9d10b8b252c7ac984", - "revisionTime": "2017-03-01T22:46:18Z" + "revision": "cdee119ee21e61eef7093a41ba148fa83585e143", + "revisionTime": "2017-03-14T22:44:13Z" + }, + { + "checksumSHA1": "XD7o4ECSRh9t2SdmCl/HkqhwAi8=", + "path": "google.golang.org/grpc/keepalive", + "revision": "cdee119ee21e61eef7093a41ba148fa83585e143", + "revisionTime": "2017-03-14T22:44:13Z" }, { "checksumSHA1": "T05Mzg3hEv2Vxao9hZn0Kv+nwUQ=", "path": "google.golang.org/grpc/metadata", - "revision": "8b2e129857480cb0f07ef7d9d10b8b252c7ac984", - "revisionTime": "2017-03-01T22:46:18Z" + "revision": "cdee119ee21e61eef7093a41ba148fa83585e143", + "revisionTime": "2017-03-14T22:44:13Z" }, { "checksumSHA1": "4GSUFhOQ0kdFlBH4D5OTeKy78z0=", "path": "google.golang.org/grpc/naming", - "revision": "8b2e129857480cb0f07ef7d9d10b8b252c7ac984", - "revisionTime": "2017-03-01T22:46:18Z" + "revision": "cdee119ee21e61eef7093a41ba148fa83585e143", + "revisionTime": "2017-03-14T22:44:13Z" }, { "checksumSHA1": "3RRoLeH6X2//7tVClOVzxW2bY+E=", "path": "google.golang.org/grpc/peer", - "revision": "8b2e129857480cb0f07ef7d9d10b8b252c7ac984", - "revisionTime": "2017-03-01T22:46:18Z" + "revision": "cdee119ee21e61eef7093a41ba148fa83585e143", + "revisionTime": "2017-03-14T22:44:13Z" }, { "checksumSHA1": "wzkOAxlah+y75EpH0QVgzb8hdfc=", "path": "google.golang.org/grpc/stats", - "revision": "8b2e129857480cb0f07ef7d9d10b8b252c7ac984", - "revisionTime": "2017-03-01T22:46:18Z" + "revision": "cdee119ee21e61eef7093a41ba148fa83585e143", + "revisionTime": "2017-03-14T22:44:13Z" }, { "checksumSHA1": "N0TftT6/CyWqp6VRi2DqDx60+Fo=", "path": "google.golang.org/grpc/tap", - "revision": "8b2e129857480cb0f07ef7d9d10b8b252c7ac984", - "revisionTime": "2017-03-01T22:46:18Z" + "revision": "cdee119ee21e61eef7093a41ba148fa83585e143", + "revisionTime": "2017-03-14T22:44:13Z" }, { - "checksumSHA1": "E5qFj7utq4q9eSNtRWCG0uhT31k=", + "checksumSHA1": "oz9C/CDSyhyxXDmzc55fH5dM/Xc=", "path": "google.golang.org/grpc/transport", - "revision": "8b2e129857480cb0f07ef7d9d10b8b252c7ac984", - "revisionTime": "2017-03-01T22:46:18Z" + "revision": "cdee119ee21e61eef7093a41ba148fa83585e143", + "revisionTime": "2017-03-14T22:44:13Z" }, { - "checksumSHA1": "rbRdLz0j5yxdI1+4Vgi3z3HO5F4=", + "checksumSHA1": "OU/wHTJqhyQfyRnXMVWx1Ox06kQ=", "path": "gopkg.in/redis.v5", - "revision": "cbad034630445e15fbcaa7e9904064dd67bce69b", - "revisionTime": "2017-02-24T10:01:17Z" + "revision": "a16aeec10ff407b1e7be6dd35797ccf5426ef0f0", + "revisionTime": "2017-03-04T11:38:25Z" }, { "checksumSHA1": "efyYmNqK7vcPhXW4KXfwbdA1wr4=", "path": "gopkg.in/redis.v5/internal", - "revision": "cbad034630445e15fbcaa7e9904064dd67bce69b", - "revisionTime": "2017-02-24T10:01:17Z" + "revision": "a16aeec10ff407b1e7be6dd35797ccf5426ef0f0", + "revisionTime": "2017-03-04T11:38:25Z" }, { "checksumSHA1": "2Ek4SixeRSKOX3mUiBMs3Aw+Guc=", "path": "gopkg.in/redis.v5/internal/consistenthash", - "revision": "cbad034630445e15fbcaa7e9904064dd67bce69b", - "revisionTime": "2017-02-24T10:01:17Z" + "revision": "a16aeec10ff407b1e7be6dd35797ccf5426ef0f0", + "revisionTime": "2017-03-04T11:38:25Z" }, { "checksumSHA1": "rJYVKcBrwYUGl7nuuusmZGrt8mY=", "path": "gopkg.in/redis.v5/internal/hashtag", - "revision": "cbad034630445e15fbcaa7e9904064dd67bce69b", - "revisionTime": "2017-02-24T10:01:17Z" + "revision": "a16aeec10ff407b1e7be6dd35797ccf5426ef0f0", + "revisionTime": "2017-03-04T11:38:25Z" }, { - "checksumSHA1": "KQHbi6qd6MlmEYYCdeH6NypL2wY=", + "checksumSHA1": "zsH5BF9qc31R7eEEVYLsjbIigDQ=", "path": "gopkg.in/redis.v5/internal/pool", - "revision": "cbad034630445e15fbcaa7e9904064dd67bce69b", - "revisionTime": "2017-02-24T10:01:17Z" + "revision": "a16aeec10ff407b1e7be6dd35797ccf5426ef0f0", + "revisionTime": "2017-03-04T11:38:25Z" }, { "checksumSHA1": "EqPdu5g8NhzxQOMCvzbreTQlzVE=", "path": "gopkg.in/redis.v5/internal/proto", - "revision": "cbad034630445e15fbcaa7e9904064dd67bce69b", - "revisionTime": "2017-02-24T10:01:17Z" + "revision": "a16aeec10ff407b1e7be6dd35797ccf5426ef0f0", + "revisionTime": "2017-03-04T11:38:25Z" }, { "checksumSHA1": "4BwmmgQUhWtizsR2soXND0nqZ1I=", From 41b23c67d3579009c05ee024a9bf7685cdc1e3eb Mon Sep 17 00:00:00 2001 From: Hylke Visser Date: Tue, 21 Mar 2017 15:01:17 +0100 Subject: [PATCH 34/45] Add missing vendor --- vendor/vendor.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/vendor/vendor.json b/vendor/vendor.json index 55ad0f7ae..900dbaeca 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -842,6 +842,12 @@ "revision": "b79c28f0197795b4050bfcd7c4c2209136c594b1", "revisionTime": "2017-03-07T23:55:54Z" }, + { + "checksumSHA1": "kGGAT2jr6A8fgFrrpP3c3BYmnpY=", + "path": "google.golang.org/genproto/googleapis/api/annotations", + "revision": "de9f5e90fe9b278809363f08c2072d2f2a429de7", + "revisionTime": "2017-03-17T11:18:29Z" + }, { "checksumSHA1": "e1bhDjvqgxsItONMBJSDt3PRVJY=", "path": "google.golang.org/grpc", From c2e134b7e4c57cfcb698fcd903b91bab54aeee74 Mon Sep 17 00:00:00 2001 From: Hylke Visser Date: Tue, 21 Mar 2017 15:09:22 +0100 Subject: [PATCH 35/45] Remove vendor caching from Travis config --- .travis.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index f1f95a3f7..f2ad346d5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,10 +10,6 @@ services: go: - 1.8 -cache: - directories: - - vendor - install: - make deps - make cover-deps From 6bf95b13139c15ee70d2005f8b35a48018454594 Mon Sep 17 00:00:00 2001 From: Hylke Visser Date: Wed, 22 Mar 2017 08:19:10 +0100 Subject: [PATCH 36/45] Update ttn/go-utils vendor --- vendor/vendor.json | 48 +++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/vendor/vendor.json b/vendor/vendor.json index 900dbaeca..ce477a722 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -83,68 +83,68 @@ { "checksumSHA1": "x795Q87cyvAAeuxXxft5iwd1Los=", "path": "github.com/TheThingsNetwork/go-utils/backoff", - "revision": "9633d6a3fb4cce550ba1298effc32bed74d52c84", - "revisionTime": "2017-03-21T13:11:45Z" + "revision": "f81a36ea68c3a11be75c537d7be25603417b2fb5", + "revisionTime": "2017-03-22T07:18:41Z" }, { "checksumSHA1": "Dl7PzR7MCkAujv/tceQkrTzVq0U=", "path": "github.com/TheThingsNetwork/go-utils/encoding", - "revision": "9633d6a3fb4cce550ba1298effc32bed74d52c84", - "revisionTime": "2017-03-21T13:11:45Z" + "revision": "f81a36ea68c3a11be75c537d7be25603417b2fb5", + "revisionTime": "2017-03-22T07:18:41Z" }, { "checksumSHA1": "Q+Nny6VBPlH007VXteMBuMPYikI=", "path": "github.com/TheThingsNetwork/go-utils/grpc/interceptor", - "revision": "9633d6a3fb4cce550ba1298effc32bed74d52c84", - "revisionTime": "2017-03-21T13:11:45Z" + "revision": "f81a36ea68c3a11be75c537d7be25603417b2fb5", + "revisionTime": "2017-03-22T07:18:41Z" }, { - "checksumSHA1": "/0L+3YQZjPiYtK03Th/ThuUjNN4=", + "checksumSHA1": "nKzhtkIrxVFFJnN7qaiQPaDtw30=", "path": "github.com/TheThingsNetwork/go-utils/grpc/restartstream", - "revision": "9633d6a3fb4cce550ba1298effc32bed74d52c84", - "revisionTime": "2017-03-21T13:11:45Z" + "revision": "f81a36ea68c3a11be75c537d7be25603417b2fb5", + "revisionTime": "2017-03-22T07:18:41Z" }, { "checksumSHA1": "T7iFQUlCUAv4cJNDZC0//46Nbio=", "path": "github.com/TheThingsNetwork/go-utils/handlers/cli", - "revision": "9633d6a3fb4cce550ba1298effc32bed74d52c84", - "revisionTime": "2017-03-21T13:11:45Z" + "revision": "f81a36ea68c3a11be75c537d7be25603417b2fb5", + "revisionTime": "2017-03-22T07:18:41Z" }, { "checksumSHA1": "aXt7ZSqIfsHWBbJPgHFjqtyxyQ0=", "path": "github.com/TheThingsNetwork/go-utils/log", - "revision": "9633d6a3fb4cce550ba1298effc32bed74d52c84", - "revisionTime": "2017-03-21T13:11:45Z" + "revision": "f81a36ea68c3a11be75c537d7be25603417b2fb5", + "revisionTime": "2017-03-22T07:18:41Z" }, { "checksumSHA1": "RdI5upcV6MHSjr5Y9zogYvbeURw=", "path": "github.com/TheThingsNetwork/go-utils/log/apex", - "revision": "9633d6a3fb4cce550ba1298effc32bed74d52c84", - "revisionTime": "2017-03-21T13:11:45Z" + "revision": "f81a36ea68c3a11be75c537d7be25603417b2fb5", + "revisionTime": "2017-03-22T07:18:41Z" }, { "checksumSHA1": "sQ0vy3MCGY1WgK9xldn1V6pMeZk=", "path": "github.com/TheThingsNetwork/go-utils/log/grpc", - "revision": "9633d6a3fb4cce550ba1298effc32bed74d52c84", - "revisionTime": "2017-03-21T13:11:45Z" + "revision": "f81a36ea68c3a11be75c537d7be25603417b2fb5", + "revisionTime": "2017-03-22T07:18:41Z" }, { "checksumSHA1": "2/v0SMyHM5vgImOb1BEEDWeXZEY=", "path": "github.com/TheThingsNetwork/go-utils/pseudorandom", - "revision": "9633d6a3fb4cce550ba1298effc32bed74d52c84", - "revisionTime": "2017-03-21T13:11:45Z" + "revision": "f81a36ea68c3a11be75c537d7be25603417b2fb5", + "revisionTime": "2017-03-22T07:18:41Z" }, { "checksumSHA1": "iYa+qSqzqZwpmoibM8/1X+aC3sI=", "path": "github.com/TheThingsNetwork/go-utils/random", - "revision": "9633d6a3fb4cce550ba1298effc32bed74d52c84", - "revisionTime": "2017-03-21T13:11:45Z" + "revision": "f81a36ea68c3a11be75c537d7be25603417b2fb5", + "revisionTime": "2017-03-22T07:18:41Z" }, { - "checksumSHA1": "kLFTtAVcjZbHXybodGAqJ8wxflY=", + "checksumSHA1": "gqxUqqHQnCd8Nmdb4FU8vtxibag=", "path": "github.com/TheThingsNetwork/go-utils/roots", - "revision": "9633d6a3fb4cce550ba1298effc32bed74d52c84", - "revisionTime": "2017-03-21T13:11:45Z" + "revision": "f81a36ea68c3a11be75c537d7be25603417b2fb5", + "revisionTime": "2017-03-22T07:18:41Z" }, { "checksumSHA1": "EZ0pNaUAiIbJuT5c0Sew85egLgw=", From d67b6ac444aa9563c13ca44e30c03a26f0ac779b Mon Sep 17 00:00:00 2001 From: Hylke Visser Date: Wed, 22 Mar 2017 09:54:23 +0100 Subject: [PATCH 37/45] Generate random trace IDs --- api/trace/utils.go | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/api/trace/utils.go b/api/trace/utils.go index dbfc5d948..a131e3f02 100644 --- a/api/trace/utils.go +++ b/api/trace/utils.go @@ -4,12 +4,17 @@ package trace import ( + "encoding/base64" "fmt" "sort" "strings" "time" + + "github.com/TheThingsNetwork/go-utils/pseudorandom" ) +var rand = pseudorandom.New(time.Now().UnixNano()) + var _serviceID = "" var _serviceName = "" @@ -19,6 +24,27 @@ func SetComponent(serviceName, serviceID string) { _serviceID = serviceID } +// GetIDs gets the IDs of this Trace +func (m *Trace) GetIDs() (uids []string) { + if m == nil { + return + } + for _, p := range m.Parents { + uids = append(uids, p.GetIDs()...) + } + if m.Id != "" { + uids = append(uids, m.Id) + } + return +} + +// GenID generates a random ID +func (m *Trace) GenID() { + id := make([]byte, 16) + rand.FillBytes(id) + m.Id = base64.RawURLEncoding.EncodeToString(id) +} + // WithEvent returns a new Trace for the event and its metadata, with the original trace as its parent func (m *Trace) WithEvent(event string, keyvalue ...interface{}) *Trace { t := &Trace{ @@ -30,6 +56,9 @@ func (m *Trace) WithEvent(event string, keyvalue ...interface{}) *Trace { if m != nil { t.Parents = append(t.Parents, m) } + if len(t.GetIDs()) == 0 { + t.GenID() + } if len(keyvalue) > 0 { if len(keyvalue)%2 == 1 { panic("Got an odd number of key-value pairs") From 9cd63b03e8b500676da5090d1e8a144cd1d5f0df Mon Sep 17 00:00:00 2001 From: Hylke Visser Date: Wed, 22 Mar 2017 10:39:37 +0100 Subject: [PATCH 38/45] Send activations from Broker and Handler to Monitor --- api/monitor/monitor.go | 64 +++++++++++++++++++++++++++++++++++++ api/monitor/monitor_test.go | 24 ++++++++++++-- core/broker/activation.go | 3 ++ core/handler/activation.go | 4 +++ 4 files changed, 92 insertions(+), 3 deletions(-) diff --git a/api/monitor/monitor.go b/api/monitor/monitor.go index 0f659d3ba..f42d4c377 100644 --- a/api/monitor/monitor.go +++ b/api/monitor/monitor.go @@ -151,6 +151,9 @@ type gatewayStreams struct { } func (s *gatewayStreams) Send(msg interface{}) { + if msg == nil { + return + } s.mu.RLock() defer s.mu.RUnlock() switch msg := msg.(type) { @@ -163,6 +166,21 @@ func (s *gatewayStreams) Send(msg interface{}) { s.log.WithField("Monitor", serverName).Warn("UplinkMessage buffer full") } } + case *router.DeviceActivationRequest: + s.log.WithField("Monitors", len(s.uplink)).Debug("Sending DeviceActivationRequest->UplinkMessage to monitor") + for serverName, ch := range s.uplink { + select { + case ch <- &router.UplinkMessage{ + Payload: msg.Payload, + Message: msg.Message, + ProtocolMetadata: msg.ProtocolMetadata, + GatewayMetadata: msg.GatewayMetadata, + Trace: msg.Trace, + }: + default: + s.log.WithField("Monitor", serverName).Warn("UplinkMessage buffer full") + } + } case *router.DownlinkMessage: s.log.WithField("Monitors", len(s.downlink)).Debug("Sending DownlinkMessage to monitor") for serverName, ch := range s.downlink { @@ -353,6 +371,9 @@ type brokerStreams struct { } func (s *brokerStreams) Send(msg interface{}) { + if msg == nil { + return + } s.mu.RLock() defer s.mu.RUnlock() switch msg := msg.(type) { @@ -365,6 +386,26 @@ func (s *brokerStreams) Send(msg interface{}) { s.log.WithField("Monitor", serverName).Warn("DeduplicatedUplinkMessage buffer full") } } + case *broker.DeduplicatedDeviceActivationRequest: + s.log.WithField("Monitors", len(s.uplink)).Debug("Sending DeduplicatedDeviceActivationRequest->DeduplicatedUplinkMessage to monitor") + for serverName, ch := range s.uplink { + select { + case ch <- &broker.DeduplicatedUplinkMessage{ + Payload: msg.Payload, + Message: msg.Message, + DevEui: msg.DevEui, + AppEui: msg.AppEui, + AppId: msg.AppId, + DevId: msg.DevId, + ProtocolMetadata: msg.ProtocolMetadata, + GatewayMetadata: msg.GatewayMetadata, + ServerTime: msg.ServerTime, + Trace: msg.Trace, + }: + default: + s.log.WithField("Monitor", serverName).Warn("DeduplicatedUplinkMessage buffer full") + } + } case *broker.DownlinkMessage: s.log.WithField("Monitors", len(s.downlink)).Debug("Sending DownlinkMessage to monitor") for serverName, ch := range s.downlink { @@ -515,6 +556,9 @@ type handlerStreams struct { } func (s *handlerStreams) Send(msg interface{}) { + if msg == nil { + return + } s.mu.RLock() defer s.mu.RUnlock() switch msg := msg.(type) { @@ -527,6 +571,26 @@ func (s *handlerStreams) Send(msg interface{}) { s.log.WithField("Monitor", serverName).Warn("DeduplicatedUplinkMessage buffer full") } } + case *broker.DeduplicatedDeviceActivationRequest: + s.log.WithField("Monitors", len(s.uplink)).Debug("Sending DeduplicatedDeviceActivationRequest->DeduplicatedUplinkMessage to monitor") + for serverName, ch := range s.uplink { + select { + case ch <- &broker.DeduplicatedUplinkMessage{ + Payload: msg.Payload, + Message: msg.Message, + DevEui: msg.DevEui, + AppEui: msg.AppEui, + AppId: msg.AppId, + DevId: msg.DevId, + ProtocolMetadata: msg.ProtocolMetadata, + GatewayMetadata: msg.GatewayMetadata, + ServerTime: msg.ServerTime, + Trace: msg.Trace, + }: + default: + s.log.WithField("Monitor", serverName).Warn("DeduplicatedUplinkMessage buffer full") + } + } case *broker.DownlinkMessage: s.log.Debug("Sending DownlinkMessage to monitor") for serverName, ch := range s.downlink { diff --git a/api/monitor/monitor_test.go b/api/monitor/monitor_test.go index 915184b59..31690957b 100644 --- a/api/monitor/monitor_test.go +++ b/api/monitor/monitor_test.go @@ -59,6 +59,7 @@ func TestMonitor(t *testing.T) { time.Sleep(waitTime) for i := 0; i < 20; i++ { gtw.Send(&router.UplinkMessage{}) + gtw.Send(&router.DeviceActivationRequest{}) gtw.Send(&router.DownlinkMessage{}) gtw.Send(&gateway.Status{}) time.Sleep(time.Millisecond) @@ -67,7 +68,7 @@ func TestMonitor(t *testing.T) { gtw.Close() time.Sleep(waitTime) - a.So(server.metrics.uplinkMessages, ShouldEqual, 20) + a.So(server.metrics.uplinkMessages, ShouldEqual, 40) a.So(server.metrics.downlinkMessages, ShouldEqual, 20) a.So(server.metrics.gatewayStatuses, ShouldEqual, 20) @@ -76,16 +77,31 @@ func TestMonitor(t *testing.T) { brk := cli.NewBrokerStreams("test", "token") time.Sleep(waitTime) brk.Send(&broker.DeduplicatedUplinkMessage{}) + brk.Send(&broker.DeduplicatedDeviceActivationRequest{}) brk.Send(&broker.DownlinkMessage{}) time.Sleep(waitTime) brk.Close() time.Sleep(waitTime) - a.So(server.metrics.brokerUplinkMessages, ShouldEqual, 1) + a.So(server.metrics.brokerUplinkMessages, ShouldEqual, 2) a.So(server.metrics.brokerDownlinkMessages, ShouldEqual, 1) testLogger.Print(t) + hdl := cli.NewHandlerStreams("test", "token") + time.Sleep(waitTime) + hdl.Send(&broker.DeduplicatedUplinkMessage{}) + hdl.Send(&broker.DeduplicatedDeviceActivationRequest{}) + hdl.Send(&broker.DownlinkMessage{}) + time.Sleep(waitTime) + hdl.Close() + time.Sleep(waitTime) + + a.So(server.metrics.handlerUplinkMessages, ShouldEqual, 2) + a.So(server.metrics.handlerDownlinkMessages, ShouldEqual, 1) + + server.metrics.brokerUplinkMessages = 0 + cli.AddConn("test2", cli.serverConns[1].conn) brk = cli.NewBrokerStreams("test", "token") @@ -95,6 +111,8 @@ func TestMonitor(t *testing.T) { brk.Close() time.Sleep(waitTime) - a.So(server.metrics.brokerUplinkMessages, ShouldEqual, 3) + a.So(server.metrics.brokerUplinkMessages, ShouldEqual, 2) + + testLogger.Print(t) } diff --git a/core/broker/activation.go b/core/broker/activation.go index 23e7b27e2..8b1ff1138 100644 --- a/core/broker/activation.go +++ b/core/broker/activation.go @@ -43,6 +43,9 @@ func (b *broker) HandleActivation(activation *pb.DeviceActivationRequest) (res * } else { ctx.WithField("Duration", time.Now().Sub(start)).Info("Handled activation") } + if deduplicatedActivationRequest != nil && b.monitorStream != nil { + b.monitorStream.Send(deduplicatedActivationRequest) + } }() b.status.activations.Mark(1) diff --git a/core/handler/activation.go b/core/handler/activation.go index f271627cf..e148d7205 100644 --- a/core/handler/activation.go +++ b/core/handler/activation.go @@ -86,10 +86,14 @@ func (h *handler) HandleActivation(activation *pb_broker.DeduplicatedDeviceActiv ErrorEventData: types.ErrorEventData{Error: err.Error()}, }, } + activation.Trace = activation.Trace.WithEvent(trace.DropEvent, "reason", err) ctx.WithError(err).Warn("Could not handle activation") } else { ctx.WithField("Duration", time.Now().Sub(start)).Info("Handled activation") } + if activation != nil && h.monitorStream != nil { + h.monitorStream.Send(activation) + } }() h.status.activations.Mark(1) From 45a1796aeb49b1aeae43c6d675710d38f77494d8 Mon Sep 17 00:00:00 2001 From: Hylke Visser Date: Wed, 22 Mar 2017 10:49:27 +0100 Subject: [PATCH 39/45] Move router monitor forward out of gateway into defer --- core/router/activation.go | 7 ++++++- core/router/downlink.go | 19 +++++++++++++++++-- core/router/gateway/gateway.go | 12 ------------ core/router/gateway/schedule.go | 4 +--- core/router/gateway_status.go | 8 +++++++- core/router/uplink.go | 7 ++++++- 6 files changed, 37 insertions(+), 20 deletions(-) diff --git a/core/router/activation.go b/core/router/activation.go index 36dc81223..5b4b3b75a 100644 --- a/core/router/activation.go +++ b/core/router/activation.go @@ -15,12 +15,14 @@ import ( pb "github.com/TheThingsNetwork/ttn/api/router" "github.com/TheThingsNetwork/ttn/api/trace" "github.com/TheThingsNetwork/ttn/core/band" + "github.com/TheThingsNetwork/ttn/core/router/gateway" "github.com/TheThingsNetwork/ttn/utils/errors" ) func (r *router) HandleActivation(gatewayID string, activation *pb.DeviceActivationRequest) (res *pb.DeviceActivationResponse, err error) { ctx := r.Ctx.WithField("GatewayID", gatewayID).WithFields(fields.Get(activation)) start := time.Now() + var gateway *gateway.Gateway defer func() { if err != nil { activation.Trace = activation.Trace.WithEvent(trace.DropEvent, "reason", err) @@ -28,12 +30,15 @@ func (r *router) HandleActivation(gatewayID string, activation *pb.DeviceActivat } else { ctx.WithField("Duration", time.Now().Sub(start)).Info("Handled activation") } + if gateway != nil && gateway.MonitorStream != nil { + gateway.MonitorStream.Send(activation) + } }() r.status.activations.Mark(1) activation.Trace = activation.Trace.WithEvent(trace.ReceiveEvent, "gateway", gatewayID) - gateway := r.getGateway(gatewayID) + gateway = r.getGateway(gatewayID) gateway.LastSeen = time.Now() uplink := &pb.UplinkMessage{ diff --git a/core/router/downlink.go b/core/router/downlink.go index ac1754b6b..148fcd134 100644 --- a/core/router/downlink.go +++ b/core/router/downlink.go @@ -37,6 +37,11 @@ func (r *router) SubscribeDownlink(gatewayID string, subscriptionID string) (<-c for message := range fromSchedule { ctx.WithFields(fields.Get(message)).Debug("Send downlink") toGateway <- message + if gateway.MonitorStream != nil { + clone := *message // There can be multiple subscribers + clone.Trace = clone.Trace.WithEvent(trace.SendEvent) + gateway.MonitorStream.Send(&clone) + } } ctx.Debug("Deactivate downlink") close(toGateway) @@ -51,7 +56,16 @@ func (r *router) UnsubscribeDownlink(gatewayID string, subscriptionID string) er return nil } -func (r *router) HandleDownlink(downlink *pb_broker.DownlinkMessage) error { +func (r *router) HandleDownlink(downlink *pb_broker.DownlinkMessage) (err error) { + var gateway *gateway.Gateway + defer func() { + if err != nil { + downlink.Trace = downlink.Trace.WithEvent(trace.DropEvent, "reason", err) + if gateway != nil && gateway.MonitorStream != nil { + gateway.MonitorStream.Send(downlink) + } + } + }() r.status.downlink.Mark(1) downlink.Trace = downlink.Trace.WithEvent(trace.ReceiveEvent) @@ -70,7 +84,8 @@ func (r *router) HandleDownlink(downlink *pb_broker.DownlinkMessage) error { identifier = strings.TrimPrefix(option.Identifier, fmt.Sprintf("%s:", r.Component.Identity.Id)) } - return r.getGateway(downlink.DownlinkOption.GatewayId).HandleDownlink(identifier, downlinkMessage) + gateway = r.getGateway(downlink.DownlinkOption.GatewayId) + return gateway.HandleDownlink(identifier, downlinkMessage) } // buildDownlinkOption builds a DownlinkOption with default values diff --git a/core/router/gateway/gateway.go b/core/router/gateway/gateway.go index 412fc6bdc..e121d5d66 100644 --- a/core/router/gateway/gateway.go +++ b/core/router/gateway/gateway.go @@ -77,10 +77,6 @@ func (g *Gateway) HandleStatus(status *pb.Status) (err error) { return err } g.updateLastSeen() - if g.MonitorStream != nil { - clone := *status // Avoid race conditions - g.MonitorStream.Send(&clone) - } return nil } @@ -110,10 +106,6 @@ func (g *Gateway) HandleUplink(uplink *pb_router.UplinkMessage) (err error) { defer g.mu.RUnlock() uplink.GatewayMetadata.GatewayTrusted = g.authenticated uplink.GatewayMetadata.GatewayId = g.ID - if g.MonitorStream != nil { - clone := *uplink // Avoid race conditions - g.MonitorStream.Send(&clone) - } return nil } @@ -124,9 +116,5 @@ func (g *Gateway) HandleDownlink(identifier string, downlink *pb_router.Downlink return err } ctx.Debug("Scheduled downlink") - if g.MonitorStream != nil { - clone := *downlink // Avoid race conditions - g.MonitorStream.Send(&clone) - } return nil } diff --git a/core/router/gateway/schedule.go b/core/router/gateway/schedule.go index 5d0792b11..77a5891de 100644 --- a/core/router/gateway/schedule.go +++ b/core/router/gateway/schedule.go @@ -12,7 +12,6 @@ import ( ttnlog "github.com/TheThingsNetwork/go-utils/log" pb_lorawan "github.com/TheThingsNetwork/ttn/api/protocol/lorawan" router_pb "github.com/TheThingsNetwork/ttn/api/router" - "github.com/TheThingsNetwork/ttn/api/trace" "github.com/TheThingsNetwork/ttn/utils/errors" "github.com/TheThingsNetwork/ttn/utils/random" "github.com/TheThingsNetwork/ttn/utils/toa" @@ -194,7 +193,7 @@ func (s *schedule) Schedule(id string, downlink *router_pb.DownlinkMessage) erro go func() { waitTime := item.deadlineAt.Sub(time.Now()) ctx.WithField("Remaining", waitTime).Info("Scheduled downlink") - downlink.Trace = downlink.Trace.WithEvent("schedule") + downlink.Trace = downlink.Trace.WithEvent("schedule", "duration", waitTime) <-time.After(waitTime) s.RLock() defer s.RUnlock() @@ -250,7 +249,6 @@ func (s *schedule) Subscribe(subscriptionID string) <-chan *router_pb.DownlinkMe if s.gateway != nil && s.gateway.Utilization != nil { s.gateway.Utilization.AddTx(downlink) // FIXME: Issue #420 } - downlink.Trace = downlink.Trace.WithEvent(trace.SendEvent) s.downlinkSubscriptionsLock.RLock() for _, ch := range s.downlinkSubscriptions { select { diff --git a/core/router/gateway_status.go b/core/router/gateway_status.go index 7d3773936..eb47ac361 100644 --- a/core/router/gateway_status.go +++ b/core/router/gateway_status.go @@ -7,19 +7,25 @@ import ( "time" pb_gateway "github.com/TheThingsNetwork/ttn/api/gateway" + "github.com/TheThingsNetwork/ttn/core/router/gateway" ) func (r *router) HandleGatewayStatus(gatewayID string, status *pb_gateway.Status) (err error) { ctx := r.Ctx.WithField("GatewayID", gatewayID) start := time.Now() + var gateway *gateway.Gateway defer func() { if err != nil { ctx.WithError(err).Warn("Could not handle gateway status") } else { ctx.WithField("Duration", time.Now().Sub(start)).Info("Handled gateway status") } + if gateway != nil && gateway.MonitorStream != nil { + gateway.MonitorStream.Send(status) + } }() r.status.gatewayStatus.Mark(1) status.Router = r.Identity.Id - return r.getGateway(gatewayID).HandleStatus(status) + gateway = r.getGateway(gatewayID) + return gateway.HandleStatus(status) } diff --git a/core/router/uplink.go b/core/router/uplink.go index a8c615f61..0c6cd1cd2 100644 --- a/core/router/uplink.go +++ b/core/router/uplink.go @@ -12,6 +12,7 @@ import ( pb_lorawan "github.com/TheThingsNetwork/ttn/api/protocol/lorawan" pb "github.com/TheThingsNetwork/ttn/api/router" "github.com/TheThingsNetwork/ttn/api/trace" + "github.com/TheThingsNetwork/ttn/core/router/gateway" "github.com/TheThingsNetwork/ttn/core/types" "github.com/TheThingsNetwork/ttn/utils/errors" "github.com/brocaar/lorawan" @@ -20,11 +21,15 @@ import ( func (r *router) HandleUplink(gatewayID string, uplink *pb.UplinkMessage) (err error) { ctx := r.Ctx.WithField("GatewayID", gatewayID).WithFields(fields.Get(uplink)) start := time.Now() + var gateway *gateway.Gateway defer func() { if err != nil { uplink.Trace = uplink.Trace.WithEvent(trace.DropEvent, "reason", err) ctx.WithError(err).Warn("Could not handle uplink") } + if gateway != nil && gateway.MonitorStream != nil { + gateway.MonitorStream.Send(uplink) + } }() r.status.uplink.Mark(1) @@ -92,7 +97,7 @@ func (r *router) HandleUplink(gatewayID string, uplink *pb.UplinkMessage) (err e "FCnt": macPayload.FHDR.FCnt, }) - gateway := r.getGateway(gatewayID) + gateway = r.getGateway(gatewayID) if err = gateway.HandleUplink(uplink); err != nil { return err From 669f39684103cb0238c361dfbcea5e37b8112e6b Mon Sep 17 00:00:00 2001 From: Hylke Visser Date: Wed, 22 Mar 2017 10:49:37 +0100 Subject: [PATCH 40/45] Trace drop events in Handler --- core/handler/downlink.go | 1 + core/handler/uplink.go | 1 + 2 files changed, 2 insertions(+) diff --git a/core/handler/downlink.go b/core/handler/downlink.go index 889b36399..fb747a488 100644 --- a/core/handler/downlink.go +++ b/core/handler/downlink.go @@ -116,6 +116,7 @@ func (h *handler) HandleDownlink(appDownlink *types.DownlinkMessage, downlink *p }, } ctx.WithError(err).Warn("Could not handle downlink") + downlink.Trace = downlink.Trace.WithEvent(trace.DropEvent, "reason", err) } if downlink != nil && h.monitorStream != nil { h.monitorStream.Send(downlink) diff --git a/core/handler/uplink.go b/core/handler/uplink.go index f451a14c0..d91a05b89 100644 --- a/core/handler/uplink.go +++ b/core/handler/uplink.go @@ -28,6 +28,7 @@ func (h *handler) HandleUplink(uplink *pb_broker.DeduplicatedUplinkMessage) (err Data: types.ErrorEventData{Error: err.Error()}, } ctx.WithError(err).Warn("Could not handle uplink") + uplink.Trace = uplink.Trace.WithEvent(trace.DropEvent, "reason", err) } else { ctx.WithField("Duration", time.Now().Sub(start)).Info("Handled uplink") } From da00cf05d2a8e022d0feb7fe47703dc8565e38f4 Mon Sep 17 00:00:00 2001 From: Hylke Visser Date: Wed, 22 Mar 2017 11:14:05 +0100 Subject: [PATCH 41/45] Add MAC info to log fields --- api/fields/log_fields.go | 59 +++++++++++++++++++++++++++++++-- api/monitor/reference_server.go | 16 +++++---- 2 files changed, 66 insertions(+), 9 deletions(-) diff --git a/api/fields/log_fields.go b/api/fields/log_fields.go index 09c445681..7fb55fe9c 100644 --- a/api/fields/log_fields.go +++ b/api/fields/log_fields.go @@ -4,12 +4,18 @@ package fields import ( + "strings" + "github.com/TheThingsNetwork/go-utils/log" "github.com/TheThingsNetwork/ttn/api/gateway" "github.com/TheThingsNetwork/ttn/api/protocol" + "github.com/TheThingsNetwork/ttn/api/protocol/lorawan" "github.com/TheThingsNetwork/ttn/core/types" ) +// Debug mode +var Debug bool + type hasId interface { GetId() string } @@ -207,11 +213,20 @@ type hasPayload interface { } func fillMessage(m interface{}, f log.Fields) { + var payload []byte if m, ok := m.(hasPayload); ok { - f["PayloadSize"] = len(m.GetPayload()) + payload = m.GetPayload() + f["PayloadSize"] = len(payload) } if m, ok := m.(hasMessage); ok { - if m := m.GetMessage(); m != nil { + m := m.GetMessage() + if m == nil && Debug { + if msg, err := lorawan.MessageFromPHYPayloadBytes(payload); err == nil { + m = new(protocol.Message) + m.Protocol = &protocol.Message_Lorawan{Lorawan: &msg} + } + } + if m != nil { if lorawan := m.GetLorawan(); lorawan != nil { if mac := lorawan.GetMacPayload(); mac != nil { if v := mac.DevAddr; !v.IsEmpty() { @@ -226,6 +241,7 @@ func fillMessage(m interface{}, f log.Fields) { if v := mac.FCnt; v != 0 { f["Counter"] = v } + fillMAC(mac, f) } if join := lorawan.GetJoinRequestPayload(); join != nil { f["AppEUI"] = join.AppEui @@ -236,6 +252,45 @@ func fillMessage(m interface{}, f log.Fields) { } } +func fillMAC(m *lorawan.MACPayload, f log.Fields) { + var mac []string + if m.Ack { + mac = append(mac, "Ack") + } + if m.Adr { + mac = append(mac, "Adr") + } + if m.FPending { + mac = append(mac, "FPending") + } + if m.AdrAckReq { + mac = append(mac, "AdrAckReq") + } + for _, m := range m.FOpts { + switch m.Cid { + case 0x02: + mac = append(mac, "LinkCheck") + case 0x03: + mac = append(mac, "LinkADR") + case 0x04: + mac = append(mac, "DutyCycle") + case 0x05: + mac = append(mac, "RXParamSetup") + case 0x06: + mac = append(mac, "DevStatus") + case 0x07: + mac = append(mac, "NewChannel") + case 0x08: + mac = append(mac, "RXTimingSetup") + case 0x09: + mac = append(mac, "TXParamSetup") + case 0x0A: + mac = append(mac, "DLChannel") + } + } + f["MAC"] = strings.Join(mac, ",") +} + // Get a number of log fields for a message, if we're able to extract them func Get(m interface{}) log.Fields { fields := log.Fields{} diff --git a/api/monitor/reference_server.go b/api/monitor/reference_server.go index 6c87511be..c30407d80 100644 --- a/api/monitor/reference_server.go +++ b/api/monitor/reference_server.go @@ -11,6 +11,7 @@ import ( "github.com/TheThingsNetwork/go-utils/log" "github.com/TheThingsNetwork/ttn/api" "github.com/TheThingsNetwork/ttn/api/broker" + "github.com/TheThingsNetwork/ttn/api/fields" "github.com/TheThingsNetwork/ttn/api/gateway" "github.com/TheThingsNetwork/ttn/api/router" "github.com/TheThingsNetwork/ttn/utils/errors" @@ -19,6 +20,7 @@ import ( // NewReferenceMonitorServer creates a new reference monitor server func NewReferenceMonitorServer(bufferSize int) *ReferenceMonitorServer { + fields.Debug = true s := &ReferenceMonitorServer{ ctx: log.Get(), @@ -130,7 +132,7 @@ func (s *ReferenceMonitorServer) GatewayStatus(stream Monitor_GatewayStatusServe if err != nil { return err } - ctx.Info("Received GatewayStatus") + ctx.WithFields(fields.Get(msg)).Info("Received GatewayStatus") select { case s.gatewayStatuses <- msg: default: @@ -171,7 +173,7 @@ func (s *ReferenceMonitorServer) GatewayUplink(stream Monitor_GatewayUplinkServe if err != nil { return err } - ctx.Info("Received UplinkMessage") + ctx.WithFields(fields.Get(msg)).Info("Received UplinkMessage") select { case s.uplinkMessages <- msg: default: @@ -212,7 +214,7 @@ func (s *ReferenceMonitorServer) GatewayDownlink(stream Monitor_GatewayDownlinkS if err != nil { return err } - ctx.Info("Received DownlinkMessage") + ctx.WithFields(fields.Get(msg)).Info("Received DownlinkMessage") select { case s.downlinkMessages <- msg: default: @@ -267,7 +269,7 @@ func (s *ReferenceMonitorServer) BrokerUplink(stream Monitor_BrokerUplinkServer) if err != nil { return err } - ctx.Info("Received DeduplicatedUplinkMessage") + ctx.WithFields(fields.Get(msg)).Info("Received DeduplicatedUplinkMessage") select { case s.brokerUplinkMessages <- msg: default: @@ -308,7 +310,7 @@ func (s *ReferenceMonitorServer) BrokerDownlink(stream Monitor_BrokerDownlinkSer if err != nil { return err } - ctx.Info("Received DownlinkMessage") + ctx.WithFields(fields.Get(msg)).Info("Received DownlinkMessage") select { case s.brokerDownlinkMessages <- msg: default: @@ -363,7 +365,7 @@ func (s *ReferenceMonitorServer) HandlerUplink(stream Monitor_HandlerUplinkServe if err != nil { return err } - ctx.Info("Received DeduplicatedUplinkMessage") + ctx.WithFields(fields.Get(msg)).Info("Received DeduplicatedUplinkMessage") select { case s.handlerUplinkMessages <- msg: default: @@ -404,7 +406,7 @@ func (s *ReferenceMonitorServer) HandlerDownlink(stream Monitor_HandlerDownlinkS if err != nil { return err } - ctx.Info("Received DownlinkMessage") + ctx.WithFields(fields.Get(msg)).Info("Received DownlinkMessage") select { case s.handlerDownlinkMessages <- msg: default: From 8f623ea10988fbd14b845fa07f1ed7674232da23 Mon Sep 17 00:00:00 2001 From: Hylke Visser Date: Wed, 22 Mar 2017 12:53:19 +0100 Subject: [PATCH 42/45] Configure wait time for data stream startup --- api/broker/broker.go | 9 ++++++--- api/monitor/monitor.go | 5 +++-- api/router/router.go | 5 +++-- api/settings.go | 11 +++++++++++ 4 files changed, 23 insertions(+), 7 deletions(-) create mode 100644 api/settings.go diff --git a/api/broker/broker.go b/api/broker/broker.go index d194bc36b..81f788a9f 100644 --- a/api/broker/broker.go +++ b/api/broker/broker.go @@ -7,7 +7,6 @@ import ( "context" "io" "sync" - "time" "github.com/TheThingsNetwork/go-utils/grpc/restartstream" "github.com/TheThingsNetwork/go-utils/log" @@ -255,7 +254,9 @@ func (c *Client) NewRouterStreams(id string, token string) RouterStream { }(server) } - wg.WaitForMax(100 * time.Millisecond) + if api.WaitForStreams > 0 { + wg.WaitForMax(api.WaitForStreams) + } return s } @@ -406,7 +407,9 @@ func (c *Client) NewHandlerStreams(id string, token string) HandlerStream { }(server) } - wg.WaitForMax(100 * time.Millisecond) + if api.WaitForStreams > 0 { + wg.WaitForMax(api.WaitForStreams) + } return s } diff --git a/api/monitor/monitor.go b/api/monitor/monitor.go index f42d4c377..4da65ea4d 100644 --- a/api/monitor/monitor.go +++ b/api/monitor/monitor.go @@ -8,7 +8,6 @@ import ( "io" "strings" "sync" - "time" "github.com/TheThingsNetwork/go-utils/grpc/restartstream" "github.com/TheThingsNetwork/go-utils/log" @@ -355,7 +354,9 @@ func (c *Client) NewGatewayStreams(id string, token string) GenericStream { }(server) } - wg.WaitForMax(100 * time.Millisecond) + if api.WaitForStreams > 0 { + wg.WaitForMax(api.WaitForStreams) + } return s } diff --git a/api/router/router.go b/api/router/router.go index 607f2ae6e..439dbc272 100644 --- a/api/router/router.go +++ b/api/router/router.go @@ -7,7 +7,6 @@ import ( "context" "io" "sync" - "time" "github.com/TheThingsNetwork/go-utils/grpc/restartstream" "github.com/TheThingsNetwork/go-utils/log" @@ -306,7 +305,9 @@ func (c *Client) NewGatewayStreams(id string, token string) GenericStream { }(server) } - wg.WaitForMax(100 * time.Millisecond) + if api.WaitForStreams > 0 { + wg.WaitForMax(api.WaitForStreams) + } return s } diff --git a/api/settings.go b/api/settings.go new file mode 100644 index 000000000..983750b58 --- /dev/null +++ b/api/settings.go @@ -0,0 +1,11 @@ +// Copyright © 2017 The Things Network +// Use of this source code is governed by the MIT license that can be found in the LICENSE file. + +package api + +import ( + "time" +) + +// WaitForStreams indicates how long should be waited for a stream to start +var WaitForStreams = 100 * time.Millisecond From 6bd78edb01288e5f48683a153f12e9cb7bab932c Mon Sep 17 00:00:00 2001 From: Romeo Van Snick Date: Wed, 22 Mar 2017 14:43:49 +0100 Subject: [PATCH 43/45] Add command line flag to select access key when needed --- ttnctl/cmd/downlink.go | 12 +++++++++--- ttnctl/cmd/subscribe.go | 7 ++++++- ttnctl/util/mqtt.go | 20 +++++++++++++++----- 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/ttnctl/cmd/downlink.go b/ttnctl/cmd/downlink.go index 1f7cdb8de..d2f142983 100644 --- a/ttnctl/cmd/downlink.go +++ b/ttnctl/cmd/downlink.go @@ -29,9 +29,6 @@ $ ttnctl downlink test --json '{"led":"on"}' Run: func(cmd *cobra.Command, args []string) { assertArgsLength(cmd, args, 2, 2) - client := util.GetMQTT(ctx) - defer client.Disconnect() - appID := util.GetAppID(ctx) ctx = ctx.WithField("AppID", appID) @@ -56,6 +53,14 @@ $ ttnctl downlink test --json '{"led":"on"}' ctx.WithError(err).Fatal("Failed to read confirmed flag") } + accessKey, err := cmd.Flags().GetString("access-key") + if err != nil { + ctx.WithError(err).Fatal("Failed to read access-key flag") + } + + client := util.GetMQTT(ctx, accessKey) + defer client.Disconnect() + message := types.DownlinkMessage{ AppID: appID, DevID: devID, @@ -104,4 +109,5 @@ func init() { downlinkCmd.Flags().Int("fport", 1, "FPort for downlink") downlinkCmd.Flags().Bool("confirmed", false, "Confirmed downlink") downlinkCmd.Flags().Bool("json", false, "Provide the payload as JSON") + downlinkCmd.Flags().String("access-key", "", "The access key to use") } diff --git a/ttnctl/cmd/subscribe.go b/ttnctl/cmd/subscribe.go index 3b0e5eb47..d47ad4608 100644 --- a/ttnctl/cmd/subscribe.go +++ b/ttnctl/cmd/subscribe.go @@ -22,9 +22,13 @@ var subscribeCmd = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { assertArgsLength(cmd, args, 0, 0) + accessKey, err := cmd.Flags().GetString("access-key") + if err != nil { + ctx.WithError(err).Fatal("Failed to read access-key flag") + } util.GetAccount(ctx) - client := util.GetMQTT(ctx) + client := util.GetMQTT(ctx, accessKey) defer client.Disconnect() token := client.SubscribeActivations(func(client mqtt.Client, appID string, devID string, req types.Activation) { @@ -71,4 +75,5 @@ var subscribeCmd = &cobra.Command{ func init() { RootCmd.AddCommand(subscribeCmd) + subscribeCmd.Flags().String("access-key", "", "The access key to use") } diff --git a/ttnctl/util/mqtt.go b/ttnctl/util/mqtt.go index e36fed3f0..b3243aafc 100644 --- a/ttnctl/util/mqtt.go +++ b/ttnctl/util/mqtt.go @@ -15,8 +15,8 @@ import ( ) // GetMQTT connects a new MQTT clients with the specified credentials -func GetMQTT(ctx ttnlog.Interface) mqtt.Client { - username, password, err := getMQTTCredentials(ctx) +func GetMQTT(ctx ttnlog.Interface, accessKey string) mqtt.Client { + username, password, err := getMQTTCredentials(ctx, accessKey) if err != nil { ctx.WithError(err).Fatal("Failed to get MQTT credentials") } @@ -41,7 +41,7 @@ func GetMQTT(ctx ttnlog.Interface) mqtt.Client { return client } -func getMQTTCredentials(ctx ttnlog.Interface) (username string, password string, err error) { +func getMQTTCredentials(ctx ttnlog.Interface, accessKey string) (username string, password string, err error) { username = viper.GetString("mqtt-username") password = viper.GetString("mqtt-password") if username != "" { @@ -53,10 +53,10 @@ func getMQTTCredentials(ctx ttnlog.Interface) (username string, password string, return } - return getAppMQTTCredentials(ctx) + return getAppMQTTCredentials(ctx, accessKey) } -func getAppMQTTCredentials(ctx ttnlog.Interface) (string, string, error) { +func getAppMQTTCredentials(ctx ttnlog.Interface, accessKey string) (string, string, error) { appID := GetAppID(ctx) account := GetAccount(ctx) @@ -65,6 +65,16 @@ func getAppMQTTCredentials(ctx ttnlog.Interface) (string, string, error) { return "", "", err } + if accessKey != "" { + for _, key := range app.AccessKeys { + if key.Name == accessKey { + return appID, key.Key, nil + } + } + + return "", "", fmt.Errorf("Access key with name %s does not exist", accessKey) + } + var keyIdx int switch len(app.AccessKeys) { case 0: From 5e4eaab5f0595088a5b460a30ffc13b1026ded36 Mon Sep 17 00:00:00 2001 From: Hylke Visser Date: Wed, 22 Mar 2017 18:11:29 +0100 Subject: [PATCH 44/45] Send activation from router to handler before response --- core/router/activation.go | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/core/router/activation.go b/core/router/activation.go index 5b4b3b75a..5ebd7a1a8 100644 --- a/core/router/activation.go +++ b/core/router/activation.go @@ -4,6 +4,7 @@ package router import ( + "context" "fmt" "sync" "time" @@ -23,16 +24,19 @@ func (r *router) HandleActivation(gatewayID string, activation *pb.DeviceActivat ctx := r.Ctx.WithField("GatewayID", gatewayID).WithFields(fields.Get(activation)) start := time.Now() var gateway *gateway.Gateway + var forwarded bool defer func() { if err != nil { activation.Trace = activation.Trace.WithEvent(trace.DropEvent, "reason", err) - ctx.WithError(err).Warn("Could not handle activation") + if forwarded { + ctx.WithError(err).Debug("Did not handle activation") + } else if gateway != nil && gateway.MonitorStream != nil { + ctx.WithError(err).Warn("Could not handle activation") + gateway.MonitorStream.Send(activation) + } } else { ctx.WithField("Duration", time.Now().Sub(start)).Info("Handled activation") } - if gateway != nil && gateway.MonitorStream != nil { - gateway.MonitorStream.Send(activation) - } }() r.status.activations.Mark(1) @@ -116,6 +120,11 @@ func (r *router) HandleActivation(gatewayID string, activation *pb.DeviceActivat "brokers", len(brokers), ) + if gateway != nil && gateway.MonitorStream != nil { + forwarded = true + gateway.MonitorStream.Send(activation) + } + // Forward to all brokers and collect responses var wg sync.WaitGroup responses := make(chan *pb_broker.DeviceActivationResponse, len(brokers)) @@ -128,7 +137,9 @@ func (r *router) HandleActivation(gatewayID string, activation *pb.DeviceActivat // Do async request wg.Add(1) go func() { - res, err := broker.client.Activate(r.Component.GetContext(""), request) + ctx, cancel := context.WithTimeout(r.Component.GetContext(""), 5*time.Second) + defer cancel() + res, err := broker.client.Activate(ctx, request) if err == nil && res != nil { responses <- res } From 0f5917c0bc9190c87931541d00ddebccd428d29c Mon Sep 17 00:00:00 2001 From: Hylke Visser Date: Wed, 22 Mar 2017 18:11:48 +0100 Subject: [PATCH 45/45] Distinguish join and regular message in ref monitor --- api/monitor/reference_server.go | 42 ++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/api/monitor/reference_server.go b/api/monitor/reference_server.go index c30407d80..249e19084 100644 --- a/api/monitor/reference_server.go +++ b/api/monitor/reference_server.go @@ -173,7 +173,12 @@ func (s *ReferenceMonitorServer) GatewayUplink(stream Monitor_GatewayUplinkServe if err != nil { return err } - ctx.WithFields(fields.Get(msg)).Info("Received UplinkMessage") + msg.UnmarshalPayload() + if msg.GetMessage().GetLorawan().GetJoinRequestPayload() != nil { + ctx.WithFields(fields.Get(msg)).Info("Received ActivationRequest") + } else { + ctx.WithFields(fields.Get(msg)).Info("Received UplinkMessage") + } select { case s.uplinkMessages <- msg: default: @@ -214,7 +219,12 @@ func (s *ReferenceMonitorServer) GatewayDownlink(stream Monitor_GatewayDownlinkS if err != nil { return err } - ctx.WithFields(fields.Get(msg)).Info("Received DownlinkMessage") + msg.UnmarshalPayload() + if msg.GetMessage().GetLorawan().GetJoinAcceptPayload() != nil { + ctx.WithFields(fields.Get(msg)).Info("Received ActivationResponse") + } else { + ctx.WithFields(fields.Get(msg)).Info("Received DownlinkMessage") + } select { case s.downlinkMessages <- msg: default: @@ -269,7 +279,12 @@ func (s *ReferenceMonitorServer) BrokerUplink(stream Monitor_BrokerUplinkServer) if err != nil { return err } - ctx.WithFields(fields.Get(msg)).Info("Received DeduplicatedUplinkMessage") + msg.UnmarshalPayload() + if msg.GetMessage().GetLorawan().GetJoinRequestPayload() != nil { + ctx.WithFields(fields.Get(msg)).Info("Received DeduplicatedActivationRequest") + } else { + ctx.WithFields(fields.Get(msg)).Info("Received DeduplicatedUplinkMessage") + } select { case s.brokerUplinkMessages <- msg: default: @@ -310,7 +325,12 @@ func (s *ReferenceMonitorServer) BrokerDownlink(stream Monitor_BrokerDownlinkSer if err != nil { return err } - ctx.WithFields(fields.Get(msg)).Info("Received DownlinkMessage") + msg.UnmarshalPayload() + if msg.GetMessage().GetLorawan().GetJoinAcceptPayload() != nil { + ctx.WithFields(fields.Get(msg)).Info("Received ActivationResponse") + } else { + ctx.WithFields(fields.Get(msg)).Info("Received DownlinkMessage") + } select { case s.brokerDownlinkMessages <- msg: default: @@ -365,7 +385,12 @@ func (s *ReferenceMonitorServer) HandlerUplink(stream Monitor_HandlerUplinkServe if err != nil { return err } - ctx.WithFields(fields.Get(msg)).Info("Received DeduplicatedUplinkMessage") + msg.UnmarshalPayload() + if msg.GetMessage().GetLorawan().GetJoinRequestPayload() != nil { + ctx.WithFields(fields.Get(msg)).Info("Received DeduplicatedActivationRequest") + } else { + ctx.WithFields(fields.Get(msg)).Info("Received DeduplicatedUplinkMessage") + } select { case s.handlerUplinkMessages <- msg: default: @@ -406,7 +431,12 @@ func (s *ReferenceMonitorServer) HandlerDownlink(stream Monitor_HandlerDownlinkS if err != nil { return err } - ctx.WithFields(fields.Get(msg)).Info("Received DownlinkMessage") + msg.UnmarshalPayload() + if msg.GetMessage().GetLorawan().GetJoinAcceptPayload() != nil { + ctx.WithFields(fields.Get(msg)).Info("Received ActivationResponse") + } else { + ctx.WithFields(fields.Get(msg)).Info("Received DownlinkMessage") + } select { case s.handlerDownlinkMessages <- msg: default: