...
1 package configx
2
3 import (
4 "context"
5 "errors"
6 "fmt"
7 "io"
8 "os"
9
10 "github.com/spf13/pflag"
11
12 "github.com/ory/jsonschema/v3"
13 "github.com/ory/x/logrusx"
14
15 "github.com/knadh/koanf"
16
17 "github.com/ory/x/watcherx"
18 )
19
20 type (
21 OptionModifier func(p *Provider)
22 )
23
24 func WithContext(ctx context.Context) OptionModifier {
25 return func(p *Provider) {
26 p.originalContext = ctx
27 for _, o := range ConfigOptionsFromContext(ctx) {
28 o(p)
29 }
30 }
31 }
32
33 func WithConfigFiles(files ...string) OptionModifier {
34 return func(p *Provider) {
35 p.files = append(p.files, files...)
36 }
37 }
38
39 func WithImmutables(immutables ...string) OptionModifier {
40 return func(p *Provider) {
41 p.immutables = append(p.immutables, immutables...)
42 }
43 }
44
45 func WithFlags(flags *pflag.FlagSet) OptionModifier {
46 return func(p *Provider) {
47 p.flags = flags
48 }
49 }
50
51 func WithLogger(l *logrusx.Logger) OptionModifier {
52 return func(p *Provider) {
53 p.logger = l
54 }
55 }
56
57 func SkipValidation() OptionModifier {
58 return func(p *Provider) {
59 p.skipValidation = true
60 }
61 }
62
63 func WithValue(key string, value interface{}) OptionModifier {
64 return func(p *Provider) {
65 p.forcedValues = append(p.forcedValues, tuple{Key: key, Value: value})
66 }
67 }
68
69 func WithValues(values map[string]interface{}) OptionModifier {
70 return func(p *Provider) {
71 for key, value := range values {
72 p.forcedValues = append(p.forcedValues, tuple{Key: key, Value: value})
73 }
74 }
75 }
76
77 func WithBaseValues(values map[string]interface{}) OptionModifier {
78 return func(p *Provider) {
79 for key, value := range values {
80 p.baseValues = append(p.baseValues, tuple{Key: key, Value: value})
81 }
82 }
83 }
84
85 func OmitKeysFromTracing(keys ...string) OptionModifier {
86 return func(p *Provider) {
87 p.excludeFieldsFromTracing = keys
88 }
89 }
90
91 func AttachWatcher(watcher func(event watcherx.Event, err error)) OptionModifier {
92 return func(p *Provider) {
93 p.onChanges = append(p.onChanges, watcher)
94 }
95 }
96
97 func WithLogrusWatcher(l *logrusx.Logger) OptionModifier {
98 return AttachWatcher(LogrusWatcher(l))
99 }
100
101 func LogrusWatcher(l *logrusx.Logger) func(e watcherx.Event, err error) {
102 return func(e watcherx.Event, err error) {
103 l.WithField("file", e.Source()).
104 WithField("event", e.String()).
105 WithField("event_type", fmt.Sprintf("%T", e)).
106 Info("A change to a configuration file was detected.")
107
108 if et := new(jsonschema.ValidationError); errors.As(err, &et) {
109 l.WithField("event", fmt.Sprintf("%#v", et)).
110 Errorf("The changed configuration is invalid and could not be loaded. Rolling back to the last working configuration revision. Please address the validation errors before restarting the process.")
111 } else if et := new(ImmutableError); errors.As(err, &et) {
112 l.WithError(err).
113 WithField("key", et.Key).
114 WithField("old_value", fmt.Sprintf("%v", et.From)).
115 WithField("new_value", fmt.Sprintf("%v", et.To)).
116 Errorf("A configuration value marked as immutable has changed. Rolling back to the last working configuration revision. To reload the values please restart the process.")
117 } else if err != nil {
118 l.WithError(err).Errorf("An error occurred while watching config file %s", e.Source())
119 } else {
120 l.WithField("file", e.Source()).
121 WithField("event", e).
122 WithField("event_type", fmt.Sprintf("%T", e)).
123 Info("Configuration change processed successfully.")
124 }
125 }
126 }
127
128 func WithStderrValidationReporter() OptionModifier {
129 return func(p *Provider) {
130 p.onValidationError = func(k *koanf.Koanf, err error) {
131 p.printHumanReadableValidationErrors(k, os.Stderr, err)
132 }
133 }
134 }
135
136 func WithStandardValidationReporter(w io.Writer) OptionModifier {
137 return func(p *Provider) {
138 p.onValidationError = func(k *koanf.Koanf, err error) {
139 p.printHumanReadableValidationErrors(k, w, err)
140 }
141 }
142 }
143
View as plain text