package main import ( "context" "errors" "fmt" "io" "log/slog" "path/filepath" "tug/internal/platform/config" "tug/internal/platform/configsrc" "tug/internal/service/admin" "tug/internal/service/configsource" "tug/internal/service/invite" "tug/internal/storage" ) const ( configSourceGit = "git" exitRestart = 3 exitUsage = 64 exitConfig = 78 ) type configSource struct { ctl *configsource.Controller hup chan struct{} restart chan []string } func startConfigSource(ctx context.Context, boot *config.Config, configPath, stateDir string, store *storage.Store, logger *slog.Logger, stderr io.Writer) (*configSource, *config.Config, int) { cs := &configSource{hup: make(chan struct{}, 1), restart: make(chan []string, 1)} cc := configsource.Config{ ConfigPath: configPath, SecretsPath: filepath.Join(stateDir, "secrets.env"), Bootstrap: boot, Store: store, Logger: logger, Restart: cs.requestRestart, } if boot.Source == configSourceGit { userbaseTransports() src, err := configsrc.Open(stateDir) if err != nil { logger.Error("config source failed", "err", err) return nil, nil, 1 } cc.Source = src } ctl, err := configsource.New(cc) if err != nil { logger.Error("config source failed", "err", err) return nil, nil, 1 } cfg, err := ctl.Start(ctx) if err != nil { return nil, nil, configExit(err, logger, stderr) } cs.ctl = ctl return cs, cfg, 0 } func configExit(err error, logger *slog.Logger, stderr io.Writer) int { var problems *config.Error switch { case errors.Is(err, configsource.ErrRefused) && errors.As(err, &problems): fmt.Fprintln(stderr, problems.Error()) return exitConfig case errors.Is(err, configsource.ErrRefused): logger.Error("config refused at start", "err", err) return exitConfig case errors.Is(err, configsource.ErrUnavailable): logger.Error("config unavailable at start", "err", err) return 1 } logger.Error("config start failed", "err", err) return 1 } func (c *configSource) requestRestart(keys []string) { if keys == nil { keys = []string{} } select { case c.restart <- keys: default: } } func (c *configSource) serve() func() { ctx, cancel := context.WithCancel(context.Background()) done := make(chan struct{}) go func() { defer close(done) c.ctl.Run(ctx, c.hup) }() return func() { cancel() <-done } } func defaultGroupsSetter(invites *invite.Service, admins *admin.Service) func(context.Context, []string, []string) error { return func(ctx context.Context, defaults, referenced []string) error { if err := invites.SetDefaultGroups(ctx, defaults); err != nil { return err } return admins.SetDefaultGroups(defaults, referenced) } }