func run(c *cli.Context) error { ctx := context.Background() ctx, cancel := context.WithCancel(ctx) defer cancel() // parse the NetID var netID lorawan.NetID if err := netID.UnmarshalText([]byte(c.String("net-id"))); err != nil { log.Fatalf("NetID parse error: %s", err) } // get the band config if c.String("band") == "" { log.Fatalf("--band is undefined, valid options are: %s", strings.Join(bands, ", ")) } bandConfig, err := band.GetConfig(band.Name(c.String("band"))) if err != nil { log.Fatal(err) } common.Band = bandConfig log.WithFields(log.Fields{ "version": version, "net_id": netID.String(), "band": c.String("band"), "docs": "https://docs.loraserver.io/", }).Info("starting LoRa Server") // connect to the database log.Info("connecting to postgresql") db, err := storage.OpenDatabase(c.String("postgres-dsn")) if err != nil { log.Fatalf("database connection error: %s", err) } // setup redis pool log.Info("setup redis connection pool") rp := storage.NewRedisPool(c.String("redis-url")) // setup gateway backend gw, err := gateway.NewBackend(c.String("gw-mqtt-server"), c.String("gw-mqtt-username"), c.String("gw-mqtt-password")) if err != nil { log.Fatalf("gateway-backend setup failed: %s", err) } // setup application backend app, err := application.NewBackend(rp, c.String("app-mqtt-server"), c.String("app-mqtt-username"), c.String("app-mqtt-password")) if err != nil { log.Fatalf("application-backend setup failed: %s", err) } // setup controller backend ctrl, err := controller.NewBackend(rp, c.String("controller-mqtt-server"), c.String("controller-mqtt-username"), c.String("controller-mqtt-password")) if err != nil { log.Fatalf("controller-backend setup failed: %s", err) } // auto-migrate the database if c.Bool("db-automigrate") { log.Info("applying database migrations") m := &migrate.AssetMigrationSource{ Asset: migrations.Asset, AssetDir: migrations.AssetDir, Dir: "", } n, err := migrate.Exec(db.DB, "postgres", m, migrate.Up) if err != nil { log.Fatalf("applying migrations failed: %s", err) } log.WithField("count", n).Info("migrations applied") } lsCtx := loraserver.Context{ DB: db, RedisPool: rp, Gateway: gw, Application: app, Controller: ctrl, NetID: netID, } // start the loraserver server := loraserver.NewServer(lsCtx) if err := server.Start(); err != nil { log.Fatal(err) } // setup the grpc api go func() { server := api.GetGRPCServer(ctx, lsCtx) list, err := net.Listen("tcp", c.String("grpc-bind")) if err != nil { log.Fatalf("error creating gRPC listener: %s", err) } log.WithField("bind", c.String("grpc-bind")).Info("starting gRPC server") log.Fatal(server.Serve(list)) }() // setup the http server r := mux.NewRouter() // setup json api jsonHandler, err := api.GetJSONGateway(ctx, lsCtx, c.String("grpc-bind")) if err != nil { log.Fatalf("get json gateway error: %s", err) } log.WithField("path", "/api/v1").Info("registering api handler and documentation endpoint") r.HandleFunc("/api/v1", api.SwaggerHandlerFunc).Methods("get") r.PathPrefix("/api/v1/").Handler(jsonHandler) // setup static file server (for the gui) log.WithField("path", "/").Info("registering gui handler") r.PathPrefix("/").Handler(http.FileServer(&assetfs.AssetFS{ Asset: static.Asset, AssetDir: static.AssetDir, AssetInfo: static.AssetInfo, Prefix: "", })) // start the http server go func() { log.WithField("bind", c.String("http-bind")).Info("starting rest api / gui server") log.Fatal(http.ListenAndServe(c.String("http-bind"), r)) }() sigChan := make(chan os.Signal) exitChan := make(chan struct{}) signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM) log.WithField("signal", <-sigChan).Info("signal received") go func() { log.Warning("stopping loraserver") if err := server.Stop(); err != nil { log.Fatal(err) } exitChan <- struct{}{} }() select { case <-exitChan: case s := <-sigChan: log.WithField("signal", s).Info("signal received, stopping immediately") } return nil }
func run(c *cli.Context) { // parse the NetID var netID lorawan.NetID if err := netID.UnmarshalText([]byte(c.String("net-id"))); err != nil { log.Fatalf("NetID parse error: %s", err) } // get the band config if c.String("band") == "" { log.Fatalf("--band is undefined, valid options are: %s", strings.Join(bands, ", ")) } bandConfig, err := band.GetConfig(band.Name(c.String("band"))) if err != nil { log.Fatal(err) } loraserver.Band = bandConfig log.WithFields(log.Fields{ "version": version, "net_id": netID.String(), "band": c.String("band"), }).Info("starting LoRa Server") // connect to the database log.Info("connecting to postgresql") db, err := loraserver.OpenDatabase(c.String("postgres-dsn")) if err != nil { log.Fatalf("database connection error: %s", err) } // setup redis pool log.Info("setup redis connection pool") rp := loraserver.NewRedisPool(c.String("redis-url")) // setup gateway backend gw, err := gateway.NewBackend(c.String("gw-mqtt-server"), c.String("gw-mqtt-username"), c.String("gw-mqtt-password")) if err != nil { log.Fatalf("gateway-backend setup failed: %s", err) } // setup application backend app, err := application.NewBackend(rp, c.String("app-mqtt-server"), c.String("app-mqtt-username"), c.String("app-mqtt-password")) if err != nil { log.Fatalf("application-backend setup failed: %s", err) } // auto-migrate the database if c.Bool("db-automigrate") { log.Info("applying database migrations") m := &migrate.AssetMigrationSource{ Asset: migrations.Asset, AssetDir: migrations.AssetDir, Dir: "", } n, err := migrate.Exec(db.DB, "postgres", m, migrate.Up) if err != nil { log.Fatalf("applying migrations failed: %s", err) } log.WithField("count", n).Info("migrations applied") } ctx := loraserver.Context{ DB: db, RedisPool: rp, Gateway: gw, Application: app, NetID: netID, } // start the loraserver server := loraserver.NewServer(ctx) if err := server.Start(); err != nil { log.Fatal(err) } // setup json-rpc api handler apiHandler, err := loraserver.NewJSONRPCHandler( loraserver.NewApplicationAPI(ctx), loraserver.NewNodeAPI(ctx), loraserver.NewNodeSessionAPI(ctx), loraserver.NewChannelListAPI(ctx), loraserver.NewChannelAPI(ctx), ) if err != nil { log.Fatal(err) } log.WithField("path", "/rpc").Info("registering json-rpc handler") http.Handle("/rpc", apiHandler) // setup static file server (for the gui) log.WithField("path", "/").Info("registering gui handler") http.Handle("/", http.FileServer(&assetfs.AssetFS{ Asset: static.Asset, AssetDir: static.AssetDir, AssetInfo: static.AssetInfo, Prefix: "", })) // start the http server go func() { log.WithField("bind", c.String("http-bind")).Info("starting http server") log.Fatal(http.ListenAndServe(c.String("http-bind"), nil)) }() sigChan := make(chan os.Signal) exitChan := make(chan struct{}) signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM) log.WithField("signal", <-sigChan).Info("signal received") go func() { log.Warning("stopping loraserver") if err := server.Stop(); err != nil { log.Fatal(err) } exitChan <- struct{}{} }() select { case <-exitChan: case s := <-sigChan: log.WithField("signal", s).Info("signal received, stopping immediately") } }