package process import ( "os" "time" "git.eeqj.de/sneak/feta/database" "git.eeqj.de/sneak/feta/ingester" "git.eeqj.de/sneak/feta/locator" "git.eeqj.de/sneak/feta/manager" "git.eeqj.de/sneak/feta/storage" _ "github.com/jinzhu/gorm/dialects/sqlite" "github.com/k0kubun/pp" "github.com/mattn/go-isatty" "github.com/rs/zerolog" "github.com/rs/zerolog/log" "github.com/spf13/viper" ) // required for orm // CLIEntry is the main entrypoint for the feta process from the cli func CLIEntry(version string, buildarch string) int { f := new(Feta) f.version = version f.buildarch = buildarch f.configure() f.setupLogging() f.setupDatabase() return f.runForever() } // Feta is the main structure/process of this app type Feta struct { version string buildarch string locator *locator.InstanceLocator manager *manager.InstanceManager ingester *ingester.TootIngester api *Server dbm *database.Manager startup time.Time } func (f *Feta) configure() { viper.SetConfigName("feta") viper.SetConfigType("yaml") viper.AddConfigPath("/etc/feta") // path to look for the config file in viper.AddConfigPath("$HOME/.config/feta") // call multiple times to add many search paths viper.SetEnvPrefix("FETA") viper.AutomaticEnv() viper.SetDefault("Debug", false) viper.SetDefault("TootsToDisk", false) viper.SetDefault("TootsToDB", true) viper.SetDefault("HostDiscoveryParallelism", 5) viper.SetDefault("FSStorageLocation", os.ExpandEnv("$HOME/Library/ApplicationSupport/feta/tootarchive.d")) viper.SetDefault("DBStorageLocation", os.ExpandEnv("$HOME/Library/ApplicationSupport/feta/feta.state.db")) viper.SetDefault("LogReportInterval", time.Second*10) if err := viper.ReadInConfig(); err != nil { if _, ok := err.(viper.ConfigFileNotFoundError); ok { // Config file not found; ignore error if desired } else { // Config file was found but another error was produced log.Panic(). Err(err). Msg("cannot read config file") } } if viper.GetBool("debug") { pp.Print(viper.AllSettings()) } } func (f *Feta) identify() { log.Info(). Str("version", f.version). Str("buildarch", f.buildarch). Msg("starting") } func (f *Feta) setupDatabase() { f.dbm = database.New() } func (f *Feta) setupLogging() { log.Logger = log.With().Caller().Logger() tty := isatty.IsTerminal(os.Stdin.Fd()) || isatty.IsCygwinTerminal(os.Stdin.Fd()) if tty { out := zerolog.NewConsoleWriter( func(w *zerolog.ConsoleWriter) { // Customize time format w.TimeFormat = time.RFC3339 }, ) log.Logger = log.Output(out) } // always log in UTC zerolog.TimestampFunc = func() time.Time { return time.Now().UTC() } zerolog.SetGlobalLevel(zerolog.InfoLevel) if viper.GetBool("debug") { zerolog.SetGlobalLevel(zerolog.DebugLevel) } f.identify() } func (f *Feta) uptime() time.Duration { return time.Since(f.startup) } func (f *Feta) runForever() int { f.startup = time.Now() // FIXME move this channel creation into the manager's constructor // and add getters/setters on the manager/locator newInstanceHostnameNotifications := make(chan string) f.locator = locator.New() f.manager = manager.New(f.dbm) f.ingester = ingester.NewTootIngester() home := os.Getenv("HOME") if home == "" { panic("can't find home directory") } // TODO make the ingester support multiple storage backends simultaneously if viper.GetBool("TootsToDB") { f.ingester.SetStorageBackend(f.dbm) } else if viper.GetBool("TootsToDisk") { diskBackend := storage.NewTootFSStorage(viper.GetString("FSStorageLocation")) f.ingester.SetStorageBackend(diskBackend) } else { log.Info().Msg("toots will not be saved to disk") } f.api = new(Server) f.api.SetFeta(f) // api needs to get to us to access data f.locator.SetInstanceNotificationChannel(newInstanceHostnameNotifications) f.manager.SetInstanceNotificationChannel(newInstanceHostnameNotifications) f.manager.SetTootDestination(f.ingester.GetDeliveryChannel()) // ingester goroutine: go f.ingester.Ingest() // locator goroutine: go f.locator.Locate() // manager goroutine: go f.manager.Manage() go f.api.Serve() // this goroutine (main) does nothing until we handle signals // FIXME(sneak) for { time.Sleep(1 * time.Second) } return 0 }