// NewGameHub creates a new GameHub for a given log contenxt. func NewGameHub(ctx log.Interface) GameHub { return GameHub{ Log: ctx.WithField("module", "GameHub"), games: make(map[string]Game), register: make(chan GameRegistrationRequest), } }
// GetHandlerManager gets a new HandlerManager for ttnctl func GetHandlerManager(ctx log.Interface, appID string) (*grpc.ClientConn, *handler.ManagerClient) { ctx.WithField("Handler", viper.GetString("handler-id")).Info("Discovering Handler...") dscConn, client := GetDiscovery(ctx) defer dscConn.Close() handlerAnnouncement, err := client.Get(GetContext(ctx), &discovery.GetRequest{ ServiceName: "handler", Id: viper.GetString("handler-id"), }) if err != nil { ctx.WithError(errors.FromGRPCError(err)).Fatal("Could not find Handler") } token := TokenForScope(ctx, scope.App(appID)) ctx.WithField("Handler", handlerAnnouncement.NetAddress).Info("Connecting with Handler...") hdlConn, err := handlerAnnouncement.Dial() if err != nil { ctx.WithError(err).Fatal("Could not connect to Handler") } managerClient, err := handler.NewManagerClient(hdlConn, token) if err != nil { ctx.WithError(err).Fatal("Could not create Handler Manager") } return hdlConn, managerClient }
// SetApp stores the app ID and app EUI preferences func SetApp(ctx log.Interface, appID string, appEUI types.AppEUI) { config := readData(appFilename) config[idKey] = appID config[euiKey] = appEUI.String() err := writeData(appFilename, config) if err != nil { ctx.WithError(err).Fatal("Could not save app preference") } }
// ForceRefreshToken forces a refresh of the access token func ForceRefreshToken(ctx log.Interface) { tokenSource := GetTokenSource(ctx).(*ttnctlTokenSource) token, err := tokenSource.Token() if err != nil { ctx.WithError(err).Fatal("Could not get access token") } token.Expiry = time.Now().Add(-1 * time.Second) tokenSource.source = oauth2.ReuseTokenSource(token, getAccountServerTokenSource(token)) tokenSource.Token() }
// NewGateway creates a new in-memory Gateway structure func NewGateway(ctx log.Interface, id string) *Gateway { ctx = ctx.WithField("GatewayID", id) return &Gateway{ ID: id, Status: NewStatusStore(), Utilization: NewUtilization(), Schedule: NewSchedule(ctx), Ctx: ctx, } }
// NewGameState creates a new game state given a logging context. func NewGameState(ctx log.Interface) GameState { return GameState{ Users: make(map[*User]bool), Shots: make(map[*Shot]bool), Log: ctx.WithField("module", "GameState"), UpdateInterval: DefaultStateUpdateLoopInterval, simulate: make(chan []Command), updateState: make(chan *GameState), } }
func saveToken(ctx log.Interface, token *oauth2.Token) { data, err := json.Marshal(token) if err != nil { ctx.WithError(err).Fatal("Could not save access token") } err = GetTokenCache().Set(tokenName(), data) if err != nil { ctx.WithError(err).Fatal("Could not save access token") } }
// GetAccount gets a new Account server client for ttnctl func GetAccount(ctx log.Interface) *account.Account { token, err := GetTokenSource(ctx).Token() if err != nil { ctx.WithError(err).Fatal("Could not get access token") } server := viper.GetString("auth-server") manager := GetTokenManager(token.AccessToken) return account.NewWithManager(server, token.AccessToken, manager).WithHeader("User-Agent", GetUserAgent()) }
// NewServer sets up a new server instance. func NewServer(ctx log.Interface, address, templateFilename string) Server { return Server{ Log: ctx.WithFields(log.Fields{ "module": "Server", "address": address, }), IndexTemplate: template.Must(template.ParseFiles(templateFilename)), Address: address, Hub: NewGameHub(ctx), } }
// NewUser creates a new user with a new Gopher to manage the user's new ws // connection. func NewUser(ctx log.Interface, ws *websocket.Conn) User { id := uuid.NewRandom().String()[:3] return User{ ID: id, Gopher: NewGopher(id, RandomCoordinates(boardSize)), Log: ctx.WithField("module", "User"), send: make(chan []byte, 256), ws: ws, } }
func TokenForScope(ctx log.Interface, scope string) string { token, err := GetTokenSource(ctx).Token() if err != nil { ctx.WithError(err).Fatal("Could not get token") } restricted, err := GetTokenManager(token.AccessToken).TokenForScope(scope) if err != nil { ctx.WithError(err).Fatal("Could not get correct rights") } return restricted }
// GetContext returns a new context func GetContext(ctx log.Interface, extraPairs ...string) context.Context { token, err := GetTokenSource(ctx).Token() if err != nil { ctx.WithError(err).Fatal("Could not get token") } md := metadata.Pairs( "id", GetID(), "service-name", "ttnctl", "service-version", fmt.Sprintf("%s-%s (%s)", viper.GetString("version"), viper.GetString("gitCommit"), viper.GetString("buildDate")), "token", token.AccessToken, ) return metadata.NewContext(context.Background(), md) }
// GetDiscovery gets the Discovery client for ttnctl func GetDiscovery(ctx log.Interface) (*grpc.ClientConn, discovery.DiscoveryClient) { path := path.Join(GetDataDir(), "/ca.cert") cert, err := ioutil.ReadFile(path) if err == nil && !api.RootCAs.AppendCertsFromPEM(cert) { ctx.Warnf("Could not add root certificates from %s", path) } conn, err := api.Dial(viper.GetString("discovery-address")) if err != nil { ctx.WithError(err).Fatal("Could not connect to Discovery server") } return conn, discovery.NewDiscoveryClient(conn) }
// GetAppID returns the AppID that must be set in the command options or config func GetAppID(ctx log.Interface) string { appID := viper.GetString("app-id") if appID == "" { appData := readData(appFilename) id, ok := appData[idKey].(string) if !ok { ctx.Fatal("Invalid appID in config file.") } appID = id } if appID == "" { ctx.Fatal("Missing AppID. You should select an application to use with \"ttnctl applications select\"") } return appID }
// NewGame creates a new game instance. func NewGame(ctx log.Interface, id string) Game { gs := NewGameState(ctx) cp := NewCommandProcessor(&gs) return Game{ Log: ctx.WithFields(log.Fields{ "module": "Game", "id": id, }), State: &gs, CommandProcessor: &cp, register: make(chan *User), unregister: make(chan *User), commands: make(chan Command), } }
// GetRouterManager starts a management connection with the router func GetRouterManager(ctx log.Interface) (*grpc.ClientConn, router.RouterManagerClient) { ctx.Info("Discovering Router...") dscConn, client := GetDiscovery(ctx) defer dscConn.Close() routerAnnouncement, err := client.Get(GetContext(ctx), &discovery.GetRequest{ ServiceName: "router", Id: viper.GetString("router-id"), }) if err != nil { ctx.WithError(errors.FromGRPCError(err)).Fatal("Could not get Router from Discovery") } ctx.Info("Connecting with Router...") rtrConn, err := routerAnnouncement.Dial() if err != nil { ctx.WithError(err).Fatal("Could not connect to Router") } ctx.Info("Connected to Router") return rtrConn, router.NewRouterManagerClient(rtrConn) }
func getStoredToken(ctx log.Interface) *oauth2.Token { tokenCache := GetTokenCache() data, err := tokenCache.Get(tokenName()) if err != nil { ctx.WithError(err).Fatal("Could not read stored token") } if data == nil { ctx.Fatal("No account information found. Please login with ttnctl user login [access code]") } token := &oauth2.Token{} err = json.Unmarshal(data, token) if err != nil { ctx.Fatal("Account information invalid. Please login with ttnctl user login [access code]") } return token }
// GetAppEUI returns the AppEUI that must be set in the command options or config func GetAppEUI(ctx log.Interface) types.AppEUI { appEUIString := viper.GetString("app-eui") if appEUIString == "" { appData := readData(appFilename) eui, ok := appData[euiKey].(string) if !ok { ctx.Fatal("Invalid AppEUI in config file") } appEUIString = eui } if appEUIString == "" { ctx.Fatal("Missing AppEUI. You should select an application to use with \"ttnctl applications select\"") } eui, err := types.ParseAppEUI(appEUIString) if err != nil { ctx.WithError(err).Fatal("Invalid AppEUI") } return eui }
func work(ctx log.Interface) (err error) { path := "Readme.md" defer ctx.WithField("path", path).Trace("opening").Stop(&err) _, err = os.Open(path) return }
func (h *handler) ConvertFromLoRaWAN(ctx log.Interface, ttnUp *pb_broker.DeduplicatedUplinkMessage, appUp *types.UplinkMessage) error { // Find Device dev, err := h.devices.Get(ttnUp.AppId, ttnUp.DevId) if err != nil { return err } // Check for LoRaWAN if lorawan := ttnUp.ProtocolMetadata.GetLorawan(); lorawan == nil { return errors.NewErrInvalidArgument("Activation", "does not contain LoRaWAN metadata") } // LoRaWAN: Unmarshal Uplink var phyPayload lorawan.PHYPayload err = phyPayload.UnmarshalBinary(ttnUp.Payload) if err != nil { return err } macPayload, ok := phyPayload.MACPayload.(*lorawan.MACPayload) if !ok { return errors.NewErrInvalidArgument("Uplink", "does not contain a MAC payload") } macPayload.FHDR.FCnt = ttnUp.ProtocolMetadata.GetLorawan().FCnt appUp.FCnt = macPayload.FHDR.FCnt ctx = ctx.WithField("FCnt", appUp.FCnt) // LoRaWAN: Validate MIC ok, err = phyPayload.ValidateMIC(lorawan.AES128Key(dev.NwkSKey)) if err != nil { return err } if !ok { return errors.NewErrNotFound("device that validates MIC") } // LoRaWAN: Decrypt if macPayload.FPort != nil && *macPayload.FPort != 0 && len(macPayload.FRMPayload) == 1 { appUp.FPort = *macPayload.FPort ctx = ctx.WithField("FCnt", appUp.FPort) if err := phyPayload.DecryptFRMPayload(lorawan.AES128Key(dev.AppSKey)); err != nil { return errors.NewErrInternal("Could not decrypt payload") } if len(macPayload.FRMPayload) == 1 { payload, ok := macPayload.FRMPayload[0].(*lorawan.DataPayload) if !ok { return errors.NewErrInvalidArgument("Uplink FRMPayload", "must be of type *lorawan.DataPayload") } appUp.PayloadRaw = payload.Bytes } } // LoRaWAN: Publish ACKs as events if macPayload.FHDR.FCtrl.ACK { h.mqttEvent <- &types.DeviceEvent{ AppID: appUp.AppID, DevID: appUp.DevID, Event: types.DownlinkAckEvent, } } return nil }
// SetApp stores the app EUI preference func SetAppEUI(ctx log.Interface, appEUI types.AppEUI) { err := setData(appFilename, euiKey, appEUI.String()) if err != nil { ctx.WithError(err).Fatal("Could not save app EUI") } }
// New creates a new Component func New(ctx log.Interface, serviceName string, announcedAddress string) (*Component, error) { go func() { memstats := new(runtime.MemStats) for range time.Tick(time.Minute) { runtime.ReadMemStats(memstats) ctx.WithFields(log.Fields{ "Goroutines": runtime.NumGoroutine(), "Memory": float64(memstats.Alloc) / 1000000, }).Debugf("Stats") } }() // Disable gRPC tracing // SEE: https://github.com/grpc/grpc-go/issues/695 grpc.EnableTracing = false component := &Component{ Config: ConfigFromViper(), Ctx: ctx, Identity: &pb_discovery.Announcement{ Id: viper.GetString("id"), Description: viper.GetString("description"), ServiceName: serviceName, ServiceVersion: fmt.Sprintf("%s-%s (%s)", viper.GetString("version"), viper.GetString("gitCommit"), viper.GetString("buildDate")), NetAddress: announcedAddress, Public: viper.GetBool("public"), }, AccessToken: viper.GetString("auth-token"), } if err := component.InitAuth(); err != nil { return nil, err } if serviceName != "discovery" && serviceName != "networkserver" { var err error component.Discovery, err = pb_discovery.NewClient( viper.GetString("discovery-address"), component.Identity, func() string { token, _ := component.BuildJWT() return token }, ) if err != nil { return nil, err } } if healthPort := viper.GetInt("health-port"); healthPort > 0 { http.HandleFunc("/healthz", func(w http.ResponseWriter, req *http.Request) { switch component.GetStatus() { case StatusHealthy: w.WriteHeader(200) w.Write([]byte("Status is HEALTHY")) return case StatusUnhealthy: w.WriteHeader(503) w.Write([]byte("Status is UNHEALTHY")) return } }) go http.ListenAndServe(fmt.Sprintf(":%d", healthPort), nil) } if monitors := viper.GetStringMapString("monitor-servers"); len(monitors) != 0 { component.Monitors = make(map[string]*pb_monitor.Client) for name, addr := range monitors { var err error component.Monitors[name], err = pb_monitor.NewClient(ctx.WithField("Monitor", name), addr) if err != nil { return nil, err } } } return component, nil }
// continue looking at dns entry for changes in config func configLoop(ctx log.Interface, cfgURL string) { ticker := time.Tick(15 * time.Second) for { select { case <-ticker: newIPs, err := dnscfg.Get(dnsAddr, ldPort) if err != nil { ctx.WithError(err).Error("dns lookup") continue } if len(newIPs) == 0 { ctx.Error("no ip addresses found") continue } oldIPs, err := httpcfg.Get(cfgURL) if err != nil { ctx.WithError(err).Error("getting config") continue } if eq(newIPs, oldIPs) { ctx.Info("config up to date") continue } err = httpcfg.Set(cfgURL, newIPs) if err != nil { ctx.WithError(err).Error("setting config") continue } ctx.WithField("ips", newIPs).Info("setting config") } } }
jsonHandler "github.com/apex/log/handlers/json" levelHandler "github.com/apex/log/handlers/level" multiHandler "github.com/apex/log/handlers/multi" "github.com/mitchellh/go-homedir" "github.com/spf13/cobra" "github.com/spf13/viper" "github.com/tj/go-elastic" "google.golang.org/grpc/grpclog" "gopkg.in/redis.v5" ) var cfgFile string var logFile *os.File var ctx log.Interface // RootCmd is executed when ttn is executed without a subcommand var RootCmd = &cobra.Command{ Use: "ttn", Short: "The Things Network's backend servers", Long: `ttn launches The Things Network's backend servers`, PersistentPreRun: func(cmd *cobra.Command, args []string) { var logLevel = log.InfoLevel if viper.GetBool("debug") { logLevel = log.DebugLevel } var logHandlers []log.Handler if !viper.GetBool("no-cli-logs") {
// Selfupdate runs a self-update for the current binary func Selfupdate(ctx log.Interface, component string) { if viper.GetString("gitBranch") == "unknown" { ctx.Infof("You are not using an official %s build. Not proceeding with the update", component) return } info, err := GetLatestInfo() if err != nil { ctx.WithError(err).Fatal("Could not get version information from the server") } if viper.GetString("gitCommit") == info.Commit { ctx.Info("The git commit of the build on the server is the same as yours") ctx.Info("Not proceeding with the update") return } if date, err := time.Parse(time.RFC3339, viper.GetString("buildDate")); err == nil { if date.Equal(info.Date) { ctx.Infof("You have the latest version of %s", component) ctx.Info("Nothing to update") return } if date.After(info.Date) { ctx.Infof("Your build is %s newer than the build on the server", date.Sub(info.Date)) ctx.Info("Not proceeding with the update") return } ctx.Infof("The build on the server is %s newer than yours", info.Date.Sub(date)) } ctx.Infof("Downloading the latest %s...", component) binary, err := GetLatest(component) if err != nil { ctx.WithError(err).Fatal("Could not download latest binary") } filename, err := osext.Executable() if err != nil { ctx.WithError(err).Fatal("Could not get path to local binary") } stat, err := os.Stat(filename) if err != nil { ctx.WithError(err).Fatal("Could not stat local binary") } ctx.Info("Replacing local binary...") if err := ioutil.WriteFile(filename+".new", binary, stat.Mode()); err != nil { ctx.WithError(err).Fatal("Could not write new binary to filesystem") } if err := os.Rename(filename, filename+".old"); err != nil { ctx.WithError(err).Fatal("Could not rename binary") } if err := os.Rename(filename+".new", filename); err != nil { ctx.WithError(err).Fatal("Could not rename binary") } ctx.Infof("Updated %s to the latest version", component) }