github.com/dkishere/pop/v6@v6.103.1/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/dkishere/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 SetLogger(defaultLogger) 29 30 ap := os.Getenv("APP_PATH") 31 if ap != "" { 32 _ = AddLookupPaths(ap) 33 } 34 ap = os.Getenv("POP_PATH") 35 if ap != "" { 36 _ = AddLookupPaths(ap) 37 } 38 } 39 40 // LoadConfigFile loads a POP config file from the configured lookup paths 41 func LoadConfigFile() error { 42 path, err := findConfigPath() 43 if err != nil { 44 return err 45 } 46 Connections = map[string]*Connection{} 47 log(logging.Debug, "Loading config file from %s", path) 48 f, err := os.Open(path) 49 if err != nil { 50 return err 51 } 52 defer f.Close() 53 return LoadFrom(f) 54 } 55 56 // LookupPaths returns the current configuration lookup paths 57 func LookupPaths() []string { 58 return lookupPaths 59 } 60 61 // AddLookupPaths add paths to the current lookup paths list 62 func AddLookupPaths(paths ...string) error { 63 lookupPaths = append(paths, lookupPaths...) 64 return nil 65 } 66 67 func findConfigPath() (string, error) { 68 for _, p := range LookupPaths() { 69 path, _ := filepath.Abs(filepath.Join(p, ConfigName)) 70 if _, err := os.Stat(path); err == nil { 71 return path, err 72 } 73 } 74 return "", ErrConfigFileNotFound 75 } 76 77 // LoadFrom reads a configuration from the reader and sets up the connections 78 func LoadFrom(r io.Reader) error { 79 envy.Load() 80 deets, err := ParseConfig(r) 81 if err != nil { 82 return err 83 } 84 for n, d := range deets { 85 con, err := NewConnection(d) 86 if err != nil { 87 log(logging.Warn, "unable to load connection %s: %v", n, err) 88 continue 89 } 90 Connections[n] = con 91 } 92 return nil 93 } 94 95 // ParseConfig reads the pop config from the given io.Reader and returns 96 // the parsed ConnectionDetails map. 97 func ParseConfig(r io.Reader) (map[string]*ConnectionDetails, error) { 98 tmpl := template.New("test") 99 tmpl.Funcs(map[string]interface{}{ 100 "envOr": func(s1, s2 string) string { 101 return envy.Get(s1, s2) 102 }, 103 "env": func(s1 string) string { 104 return envy.Get(s1, "") 105 }, 106 }) 107 b, err := ioutil.ReadAll(r) 108 if err != nil { 109 return nil, err 110 } 111 t, err := tmpl.Parse(string(b)) 112 if err != nil { 113 return nil, fmt.Errorf("couldn't parse config template: %w", err) 114 } 115 116 var bb bytes.Buffer 117 err = t.Execute(&bb, nil) 118 if err != nil { 119 return nil, fmt.Errorf("couldn't execute config template: %w", err) 120 } 121 122 deets := map[string]*ConnectionDetails{} 123 err = yaml.Unmarshal(bb.Bytes(), &deets) 124 if err != nil { 125 return nil, fmt.Errorf("couldn't unmarshal config to yaml: %w", err) 126 } 127 return deets, nil 128 }