1package setting 2 3import ( 4 "fmt" 5 "io/ioutil" 6 "os" 7 "regexp" 8 "sort" 9 "strings" 10 11 "gopkg.in/ini.v1" 12) 13 14type Expander interface { 15 SetupExpander(file *ini.File) error 16 Expand(string) (string, error) 17} 18 19type registeredExpander struct { 20 name string 21 priority int64 22 expander Expander 23} 24 25var expanders = []registeredExpander{ 26 { 27 name: "env", 28 priority: -10, 29 expander: envExpander{}, 30 }, 31 { 32 name: "file", 33 priority: -5, 34 expander: fileExpander{}, 35 }, 36} 37 38func AddExpander(name string, priority int64, e Expander) { 39 expanders = append(expanders, registeredExpander{ 40 name: name, 41 priority: priority, 42 expander: e, 43 }) 44} 45 46var regex = regexp.MustCompile(`\$(|__\w+){([^}]+)}`) 47 48func expandConfig(file *ini.File) error { 49 sort.Slice(expanders, func(i, j int) bool { 50 return expanders[i].priority < expanders[j].priority 51 }) 52 53 for _, expander := range expanders { 54 err := expander.expander.SetupExpander(file) 55 if err != nil { 56 return fmt.Errorf("got error during initilazation of expander '%s': %w", expander.name, err) 57 } 58 59 for _, section := range file.Sections() { 60 for _, key := range section.Keys() { 61 updated, err := applyExpander(key.Value(), expander) 62 if err != nil { 63 return fmt.Errorf("got error while expanding %s.%s with expander '%s': %w", 64 section.Name(), 65 key.Name(), 66 expander.name, 67 err) 68 } 69 70 key.SetValue(updated) 71 } 72 } 73 } 74 return nil 75} 76 77func ExpandVar(s string) (string, error) { 78 for _, expander := range expanders { 79 var err error 80 s, err = applyExpander(s, expander) 81 if err != nil { 82 return "", fmt.Errorf("got error while expanding expander %s: %w", expander.name, err) 83 } 84 } 85 return s, nil 86} 87 88func applyExpander(s string, e registeredExpander) (string, error) { 89 matches := regex.FindAllStringSubmatch(s, -1) 90 91 for _, match := range matches { 92 if len(match) < 3 { 93 return "", fmt.Errorf("regex error, got %d results back for match, expected 3", len(match)) 94 } 95 96 _, isEnv := e.expander.(envExpander) 97 if match[1] == "__"+e.name || (match[1] == "" && isEnv) { 98 updated, err := e.expander.Expand(match[2]) 99 if err != nil { 100 return "", err 101 } 102 103 s = strings.Replace(s, match[0], updated, 1) 104 } 105 } 106 107 return s, nil 108} 109 110type envExpander struct { 111} 112 113func (e envExpander) SetupExpander(file *ini.File) error { 114 return nil 115} 116 117func (e envExpander) Expand(s string) (string, error) { 118 envValue := os.Getenv(s) 119 120 // if env variable is hostname and it is empty use os.Hostname as default 121 if s == "HOSTNAME" && envValue == "" { 122 return os.Hostname() 123 } 124 125 return os.Getenv(s), nil 126} 127 128type fileExpander struct { 129} 130 131func (e fileExpander) SetupExpander(file *ini.File) error { 132 return nil 133} 134 135func (e fileExpander) Expand(s string) (string, error) { 136 _, err := os.Stat(s) 137 if err != nil { 138 return "", err 139 } 140 141 // nolint:gosec 142 // We can ignore the gosec G304 warning on this one because `s` comes from configuration section keys 143 f, err := ioutil.ReadFile(s) 144 if err != nil { 145 return "", err 146 } 147 148 return strings.TrimSpace(string(f)), nil 149} 150