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}