2020-06-10 07:58:42 -05:00
package setting
import (
"fmt"
"io/ioutil"
"os"
"regexp"
"sort"
"strings"
"gopkg.in/ini.v1"
)
type Expander interface {
SetupExpander ( file * ini . File ) error
Expand ( string ) ( string , error )
}
type registeredExpander struct {
name string
priority int64
expander Expander
}
var expanders = [ ] registeredExpander {
{
name : "env" ,
priority : - 10 ,
expander : envExpander { } ,
} ,
{
name : "file" ,
priority : - 5 ,
expander : fileExpander { } ,
} ,
}
func AddExpander ( name string , priority int64 , e Expander ) {
expanders = append ( expanders , registeredExpander {
name : name ,
priority : priority ,
expander : e ,
} )
}
var regex = regexp . MustCompile ( ` \$(|__\w+) { ([^}]+)} ` )
2022-04-27 15:29:15 -05:00
// Slightly hacky function to avoid code duplication. If this is eventually called in multiple places, consider refactoring or potentially adding more general helper functions to this package
func GetExpanderRegex ( ) * regexp . Regexp {
return regex
}
2020-06-10 07:58:42 -05:00
func expandConfig ( file * ini . File ) error {
sort . Slice ( expanders , func ( i , j int ) bool {
return expanders [ i ] . priority < expanders [ j ] . priority
} )
for _ , expander := range expanders {
err := expander . expander . SetupExpander ( file )
if err != nil {
return fmt . Errorf ( "got error during initilazation of expander '%s': %w" , expander . name , err )
}
for _ , section := range file . Sections ( ) {
for _ , key := range section . Keys ( ) {
updated , err := applyExpander ( key . Value ( ) , expander )
if err != nil {
return fmt . Errorf ( "got error while expanding %s.%s with expander '%s': %w" ,
section . Name ( ) ,
key . Name ( ) ,
expander . name ,
err )
}
key . SetValue ( updated )
}
}
}
return nil
}
func ExpandVar ( s string ) ( string , error ) {
for _ , expander := range expanders {
var err error
s , err = applyExpander ( s , expander )
if err != nil {
return "" , fmt . Errorf ( "got error while expanding expander %s: %w" , expander . name , err )
}
}
return s , nil
}
func applyExpander ( s string , e registeredExpander ) ( string , error ) {
matches := regex . FindAllStringSubmatch ( s , - 1 )
for _ , match := range matches {
if len ( match ) < 3 {
return "" , fmt . Errorf ( "regex error, got %d results back for match, expected 3" , len ( match ) )
}
_ , isEnv := e . expander . ( envExpander )
if match [ 1 ] == "__" + e . name || ( match [ 1 ] == "" && isEnv ) {
updated , err := e . expander . Expand ( match [ 2 ] )
if err != nil {
return "" , err
}
s = strings . Replace ( s , match [ 0 ] , updated , 1 )
}
}
return s , nil
}
type envExpander struct {
}
func ( e envExpander ) SetupExpander ( file * ini . File ) error {
return nil
}
func ( e envExpander ) Expand ( s string ) ( string , error ) {
envValue := os . Getenv ( s )
// if env variable is hostname and it is empty use os.Hostname as default
if s == "HOSTNAME" && envValue == "" {
return os . Hostname ( )
}
return os . Getenv ( s ) , nil
}
type fileExpander struct {
}
func ( e fileExpander ) SetupExpander ( file * ini . File ) error {
return nil
}
func ( e fileExpander ) Expand ( s string ) ( string , error ) {
_ , err := os . Stat ( s )
if err != nil {
return "" , err
}
2020-12-03 15:13:06 -06:00
// nolint:gosec
// We can ignore the gosec G304 warning on this one because `s` comes from configuration section keys
2020-06-10 07:58:42 -05:00
f , err := ioutil . ReadFile ( s )
if err != nil {
return "" , err
}
return strings . TrimSpace ( string ( f ) ) , nil
}