Raw
  1package configsource
  2
  3import (
  4	"context"
  5	"errors"
  6	"fmt"
  7	"strings"
  8
  9	"tug/internal/platform/config"
 10	"tug/internal/platform/configsrc"
 11)
 12
 13const (
 14	hotSessionTTL    = "session.ttl"
 15	hotAllowedHD     = "google.allowed_hd"
 16	hotInviteTTL     = "invite.ttl"
 17	hotDefaultGroups = "invite.default_groups"
 18)
 19
 20func (c *Controller) Step(ctx context.Context) error {
 21	c.stepMu.Lock()
 22	defer c.stepMu.Unlock()
 23	if c.restarting {
 24		return nil
 25	}
 26	if !c.git {
 27		c.stepFile(ctx)
 28		return nil
 29	}
 30	k, err := c.cfg.Store.ConfigCommit(ctx)
 31	if err != nil {
 32		return err
 33	}
 34	res, err := c.cfg.Source.Step(ctx, c.input(k, 0))
 35	if err != nil {
 36		return err
 37	}
 38	c.noteFetched(res)
 39	c.noteRepaired(res)
 40	switch res.Outcome {
 41	case configsrc.Refused:
 42		c.refused(ctx, res.Kind, res.Head, res.Cause, nil)
 43	case configsrc.Accepted:
 44		cand, ok := c.candidate(ctx, res)
 45		if !ok {
 46			return nil
 47		}
 48		if err := c.advance(ctx, k, res.Head); err != nil {
 49			if nothingMoved(err) {
 50				return nil
 51			}
 52			return err
 53		}
 54		c.apply(ctx, cand, res.Head)
 55	}
 56	return nil
 57}
 58
 59func (c *Controller) stepFile(ctx context.Context) {
 60	cand, err := config.Load(c.cfg.ConfigPath, c.cfg.SecretsPath)
 61	if err != nil {
 62		attrs := []any{"kind", configsrc.KindInvalid, "file", c.cfg.ConfigPath}
 63		if lines := problemLines(err); lines != nil {
 64			attrs = append(attrs, "problems", lines)
 65		}
 66		c.logger.Warn("config refused", attrs...)
 67		return
 68	}
 69	c.apply(ctx, cand, "")
 70}
 71
 72func (c *Controller) apply(ctx context.Context, cand *config.Config, commit string) {
 73	ch := config.Diff(c.running, cand)
 74	if len(ch.Restart) > 0 {
 75		c.recordApplied(ctx, commit, ch)
 76		c.requestRestart(ch.Restart)
 77		return
 78	}
 79	if err := c.applyHot(ctx, cand, ch.Hot); err != nil {
 80		c.logger.Error("config hot apply failed", "hot", ch.Hot, "err", err)
 81		c.recordApplied(ctx, commit, ch)
 82		c.requestRestart(ch.Hot)
 83		return
 84	}
 85	c.running = cand
 86	c.recordApplied(ctx, commit, ch)
 87	c.setCommit(commit)
 88}
 89
 90func (c *Controller) applyHot(ctx context.Context, cand *config.Config, hot []string) error {
 91	set := c.setters()
 92	session, hd, groups := false, false, false
 93	for _, key := range hot {
 94		switch {
 95		case key == hotSessionTTL, strings.HasPrefix(key, "app[") && strings.HasSuffix(key, "].session_ttl"):
 96			session = true
 97		case key == hotAllowedHD:
 98			hd = true
 99		case key == hotInviteTTL:
100		case key == hotDefaultGroups:
101			groups = true
102		default:
103			return fmt.Errorf("configsource: no setter applies %s", key)
104		}
105	}
106	if session {
107		if set.SessionTTLs == nil {
108			return errors.New("configsource: no session setter is bound")
109		}
110		if err := set.SessionTTLs(cand.Session.TTL, config.HostTTLs(cand.Apps)); err != nil {
111			return err
112		}
113	}
114	if hd {
115		if set.AllowedHD == nil || cand.Google == nil {
116			return errors.New("configsource: no allowed_hd setter is bound")
117		}
118		set.AllowedHD(cand.Google.AllowedHD)
119	}
120	if groups {
121		if set.DefaultGroups == nil {
122			return errors.New("configsource: no default_groups setter is bound")
123		}
124		if err := set.DefaultGroups(ctx, cand.Invite.DefaultGroups, cand.ReferencedGroups()); err != nil {
125			return err
126		}
127	}
128	return nil
129}
130
131func (c *Controller) requestRestart(keys []string) {
132	c.restarting = true
133	c.logger.Info("restart required", "restart", keys)
134	c.cfg.Restart(keys)
135}