github.com/Accefy/pop@v0.0.0-20230428174248-e9f677eab5b9/config.go (about) 1 package pop 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "io" 8 "io/ioutil" 9 "os" 10 "path/filepath" 11 "text/template" 12 13 "github.com/gobuffalo/envy" 14 "github.com/gobuffalo/pop/v6/logging" 15 "gopkg.in/yaml.v2" 16 ) 17 18 // ErrConfigFileNotFound is returned when the pop config file can't be found, 19 // after looking for it. 20 var ErrConfigFileNotFound = errors.New("unable to find pop config file") 21 22 var lookupPaths = []string{"", "./config", "/config", "../", "../config", "../..", "../../config"} 23 24 // ConfigName is the name of the YAML databases config file 25 var ConfigName = "database.yml" 26 27 func init() { 28 ap := os.Getenv("APP_PATH") 29 if ap != "" { 30 _ = AddLookupPaths(ap) 31 } 32 ap = os.Getenv("POP_PATH") 33 if ap != "" { 34 _ = AddLookupPaths(ap) 35 } 36 } 37 38 // LoadConfigFile loads a POP config file from the configured lookup paths 39 func LoadConfigFile() error { 40 path, err := findConfigPath() 41 if err != nil { 42 return err 43 } 44 Connections = map[string]*Connection{} 45 log(logging.Debug, "Loading config file from %s", path) 46 f, err := os.Open(path) 47 if err != nil { 48 return err 49 } 50 defer f.Close() 51 return LoadFrom(f) 52 } 53 54 // LookupPaths returns the current configuration lookup paths 55 func LookupPaths() []string { 56 return lookupPaths 57 } 58 59 // AddLookupPaths add paths to the current lookup paths list 60 func AddLookupPaths(paths ...string) error { 61 lookupPaths = append(paths, lookupPaths...) 62 return nil 63 } 64 65 func findConfigPath() (string, error) { 66 for _, p := range LookupPaths() { 67 path, _ := filepath.Abs(filepath.Join(p, ConfigName)) 68 if _, err := os.Stat(path); err == nil { 69 return path, err 70 } 71 } 72 return "", ErrConfigFileNotFound 73 } 74 75 // LoadFrom reads a configuration from the reader and sets up the connections 76 func LoadFrom(r io.Reader) error { 77 envy.Load() 78 deets, err := ParseConfig(r) 79 if err != nil { 80 return err 81 } 82 for n, d := range deets { 83 con, err := NewConnection(d) 84 if err != nil { 85 log(logging.Warn, "unable to load connection %s: %v", n, err) 86 continue 87 } 88 Connections[n] = con 89 } 90 return nil 91 } 92 93 // ParseConfig reads the pop config from the given io.Reader and returns 94 // the parsed ConnectionDetails map. 95 func ParseConfig(r io.Reader) (map[string]*ConnectionDetails, error) { 96 tmpl := template.New("test") 97 tmpl.Funcs(map[string]interface{}{ 98 "envOr": func(s1, s2 string) string { 99 return envy.Get(s1, s2) 100 }, 101 "env": func(s1 string) string { 102 return envy.Get(s1, "") 103 }, 104 }) 105 b, err := ioutil.ReadAll(r) 106 if err != nil { 107 return nil, err 108 } 109 t, err := tmpl.Parse(string(b)) 110 if err != nil { 111 return nil, fmt.Errorf("couldn't parse config template: %w", err) 112 } 113 114 var bb bytes.Buffer 115 err = t.Execute(&bb, nil) 116 if err != nil { 117 return nil, fmt.Errorf("couldn't execute config template: %w", err) 118 } 119 120 deets := map[string]*ConnectionDetails{} 121 err = yaml.Unmarshal(bb.Bytes(), &deets) 122 if err != nil { 123 return nil, fmt.Errorf("couldn't unmarshal config to yaml: %w", err) 124 } 125 return deets, nil 126 }