carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package appconfig
2
3import (
4 "context"
5 "crypto/sha256"
6 "encoding/hex"
7 "errors"
8 "fmt"
9
10 "github.com/carlosframework/platform/internal/hostwrite"
11 "github.com/carlosframework/platform/internal/registry"
12)
13
14var errHostWriteOptions = errors.New("appconfig: more than one hostwrite configuration")
15
16func hostWriteOption(options []hostwrite.Config) (hostwrite.Config, error) {
17 if len(options) > 1 {
18 return hostwrite.Config{}, errHostWriteOptions
19 }
20 if len(options) == 1 {
21 return options[0], nil
22 }
23 return hostwrite.Config{}, nil
24}
25
26func renderedIdentity(rendered []byte, defaultPresent, environmentPresent, instancePresent bool) hostwrite.Identity {
27 sum := sha256.Sum256(rendered)
28 return hostwrite.Identity{Config: fmt.Sprintf("sha256:%s;default=%t;environment=%t;instance=%t", hex.EncodeToString(sum[:]), defaultPresent, environmentPresent, instancePresent)}
29}
30
31func guarded(ctx context.Context, work hostwrite.Work, id hostwrite.Identity, fn func() error) error {
32 if ctx == nil || work == nil {
33 return hostwrite.ErrUnavailable
34 }
35 return work.With(ctx, id, fn)
36}
37
38type legacyHostWork struct{}
39
40func (legacyHostWork) With(_ context.Context, _ hostwrite.Identity, fn func() error) error {
41 return fn()
42}
43
44// Callbacks keeps legacy handlers for flag-off passes. Enabled passes require
45// the selected handlers, which must retain the selection until their final
46// effect. They run outside the config writer's flock.
47type Callbacks struct {
48 Restart func(string) error
49 Ack func(ConfigAck)
50 SelectedRestart hostwrite.Callback
51 SelectedAck func(context.Context, hostwrite.Selection, ConfigAck) error
52 // Delivered, when set, is told the revision of a route's config file
53 // each time this pass writes or removes it, before the restart callback
54 // runs: the zero-downtime scheduler's "delivered" revision (spec §8.1).
55 // It is never called for a shape whose process the platform does not
56 // restart (the TCP-addr shape), for a sidecar Managed does not name, or
57 // for a socket route Managed says no to. Nil keeps today's behaviour.
58 Delivered func(host, rev string)
59 // Managed, when set, names the routes the release scheduler moves itself
60 // (rollout.Managed, handed in because this package sits below the
61 // scheduler). It splits the pass the way the scheduler does (final
62 // review F7): a socket route it does not manage is restarted by
63 // Unmanaged, as config sync restarts it with the flag off, and is not
64 // reported to Delivered; a native sidecar it manages is reported to
65 // Delivered though config sync never restarts a sidecar, because the
66 // scheduler's config attempt is that sidecar's restart. Nil: Restart and
67 // Delivered serve every route, as before.
68 Managed func(rt registry.Route) bool
69 // Unmanaged is the restart a socket route outside Managed gets in
70 // Restart's place. Ignored while Managed is nil.
71 Unmanaged func(host string) error
72 enabled bool
73}
74
75// ErrDeliveredOnWake is a restart callback's answer for an instance that is
76// asleep and reads the new file when it next wakes: nothing was restarted
77// and nothing failed. Its ack carries Hibernating (delivered on wake), the
78// marker the unit pass sets for a hibernated unit (sync.go), with Restarted
79// false and no RestartError.
80var ErrDeliveredOnWake = errors.New("asleep: config delivered on wake")
81
82func (c Callbacks) restart(ctx context.Context, selected hostwrite.Selection) error {
83 if c.enabled {
84 if c.SelectedRestart == nil {
85 return hostwrite.ErrUnavailable
86 }
87 return c.SelectedRestart(ctx, selected)
88 }
89 if c.Restart == nil {
90 return nil
91 }
92 return c.Restart(selected.Host)
93}
94
95func (c Callbacks) acknowledge(ctx context.Context, selected hostwrite.Selection, a ConfigAck) error {
96 if c.enabled {
97 if c.SelectedAck == nil {
98 return hostwrite.ErrUnavailable
99 }
100 return c.SelectedAck(ctx, selected, a)
101 }
102 if c.Ack != nil {
103 c.Ack(a)
104 }
105 return nil
106}
107
108func configCallbacks(work hostwrite.Work, restart func(string) error, ack func(ConfigAck), options []Callbacks) Callbacks {
109 if len(options) != 0 {
110 return options[0]
111 }
112 _, legacy := work.(legacyHostWork)
113 return Callbacks{Restart: restart, Ack: ack, enabled: !legacy}
114}
115