package configsource import ( "context" "errors" "fmt" "strings" "tug/internal/platform/config" "tug/internal/platform/configsrc" ) const ( hotSessionTTL = "session.ttl" hotAllowedHD = "google.allowed_hd" hotInviteTTL = "invite.ttl" hotDefaultGroups = "invite.default_groups" ) func (c *Controller) Step(ctx context.Context) error { c.stepMu.Lock() defer c.stepMu.Unlock() if c.restarting { return nil } if !c.git { c.stepFile(ctx) return nil } k, err := c.cfg.Store.ConfigCommit(ctx) if err != nil { return err } res, err := c.cfg.Source.Step(ctx, c.input(k, 0)) if err != nil { return err } c.noteFetched(res) c.noteRepaired(res) switch res.Outcome { case configsrc.Refused: c.refused(ctx, res.Kind, res.Head, res.Cause, nil) case configsrc.Accepted: cand, ok := c.candidate(ctx, res) if !ok { return nil } if err := c.advance(ctx, k, res.Head); err != nil { if nothingMoved(err) { return nil } return err } c.apply(ctx, cand, res.Head) } return nil } func (c *Controller) stepFile(ctx context.Context) { cand, err := config.Load(c.cfg.ConfigPath, c.cfg.SecretsPath) if err != nil { attrs := []any{"kind", configsrc.KindInvalid, "file", c.cfg.ConfigPath} if lines := problemLines(err); lines != nil { attrs = append(attrs, "problems", lines) } c.logger.Warn("config refused", attrs...) return } c.apply(ctx, cand, "") } func (c *Controller) apply(ctx context.Context, cand *config.Config, commit string) { ch := config.Diff(c.running, cand) if len(ch.Restart) > 0 { c.recordApplied(ctx, commit, ch) c.requestRestart(ch.Restart) return } if err := c.applyHot(ctx, cand, ch.Hot); err != nil { c.logger.Error("config hot apply failed", "hot", ch.Hot, "err", err) c.recordApplied(ctx, commit, ch) c.requestRestart(ch.Hot) return } c.running = cand c.recordApplied(ctx, commit, ch) c.setCommit(commit) } func (c *Controller) applyHot(ctx context.Context, cand *config.Config, hot []string) error { set := c.setters() session, hd, groups := false, false, false for _, key := range hot { switch { case key == hotSessionTTL, strings.HasPrefix(key, "app[") && strings.HasSuffix(key, "].session_ttl"): session = true case key == hotAllowedHD: hd = true case key == hotInviteTTL: case key == hotDefaultGroups: groups = true default: return fmt.Errorf("configsource: no setter applies %s", key) } } if session { if set.SessionTTLs == nil { return errors.New("configsource: no session setter is bound") } if err := set.SessionTTLs(cand.Session.TTL, config.HostTTLs(cand.Apps)); err != nil { return err } } if hd { if set.AllowedHD == nil || cand.Google == nil { return errors.New("configsource: no allowed_hd setter is bound") } set.AllowedHD(cand.Google.AllowedHD) } if groups { if set.DefaultGroups == nil { return errors.New("configsource: no default_groups setter is bound") } if err := set.DefaultGroups(ctx, cand.Invite.DefaultGroups, cand.ReferencedGroups()); err != nil { return err } } return nil } func (c *Controller) requestRestart(keys []string) { c.restarting = true c.logger.Info("restart required", "restart", keys) c.cfg.Restart(keys) }