// 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() }
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()) }
// 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) }
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 }
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 }
// GetRouter starts a connection with the router func GetRouter(ctx log.Interface) (*grpc.ClientConn, router.RouterClient) { 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() ctx.Info("Connected to Router") rtrClient := router.NewRouterClient(rtrConn) return rtrConn, rtrClient }
// 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 }
// 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 }
// 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") } } }
// 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") } }
// 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) }