@@ 24,13 24,17 @@ import (
// wrapper, on the other hand, allows them to be updated upon receiving a signal.
type Secrets struct {
path mpath.Path
- appliers map[string]func(string)
+ appliers map[string]Applier
}
+// An Applier is a function that does something with a secret value that’s just
+// been loaded.
+type Applier func(secretValue string)error
+
// New returns a new Secrets handler with the given path. New does not read the
// secrets when called by itself.
func New(path mpath.Path) *Secrets {
- return &Secrets{path, make(map[string]func(string))}
+ return &Secrets{path, make(map[string]Applier)}
}
// Register registers a new secret applier, pairing a secret name with a function
@@ 42,7 46,7 @@ func New(path mpath.Path) *Secrets {
// making them thread-safe for THIS reason; however, you will almost certainly
// want to synchronise access to the memory that holds each secret behind a
// sync.Mutex or sync.RWMutex to prevent it from being read while being updated.
-func (s *Secrets) Register(secretName string, applier func(secretValue string)) {
+func (s *Secrets) Register(secretName string, applier Applier) {
s.appliers[secretName] = applier
}
@@ 65,6 69,7 @@ func (s *Secrets) Read() {
scanner := bufio.NewScanner(file)
linum := 0
secretsApplied := 0
+ secretsFailed := 0
for scanner.Scan() {
line := scanner.Text()
linum += 1
@@ 76,8 81,13 @@ func (s *Secrets) Read() {
}
if applier, matches := s.appliers[secretName]; matches {
- applier(secretValue)
- secretsApplied++
+ err := applier(secretValue)
+ if err != nil {
+ log.Printf("Failed to set secret %q (line #%d): %v", secretName, linum, err)
+ secretsFailed++
+ } else {
+ secretsApplied++
+ }
} else {
log.Printf("Unknown secret %q (line #%d) in secrets file", secretName, linum)
}
@@ 86,15 96,27 @@ func (s *Secrets) Read() {
if err := scanner.Err(); err != nil {
log.Printf("Failed to read from secrets file: %v", err)
} else if secretsApplied == 0 {
- log.Println("No secrets applied.")
+ log.Printf("No secrets applied (%d failed)", secretsFailed)
} else if secretsApplied == 1 {
- log.Println("1 secret applied.")
+ log.Printf("1 secret applied (%d failed)", secretsFailed)
} else {
- log.Printf("%d secrets applied.", secretsApplied)
+ log.Printf("%d secrets applied (%d failed)", secretsApplied, secretsFailed)
+ }
+}
+
+// Infallibly converts a function that always succeeds to a function that always
+// returns a nil error.
+func Infallibly[T any](input func(T)) func(T)error {
+ return func(secretValue T) error {
+ input(secretValue)
+ return nil
}
}
+//goland:noinspection GoUnusedConst
const SIGUSR1 = syscall.SIGUSR1
+//goland:noinspection GoUnusedConst
+const SIGUSR2 = syscall.SIGUSR2
// ListenForReload sets up the signal listener for the OS signal with the given
// value, wherein upon receiving a signal, runs Read.