github.com/solongordon/pop@v4.10.0+incompatible/config.go (about)

     1  package pop
     2  
     3  import (
     4  	"bytes"
     5  	"io"
     6  	"io/ioutil"
     7  	"os"
     8  	"path/filepath"
     9  	"text/template"
    10  
    11  	"github.com/gobuffalo/envy"
    12  	"github.com/gobuffalo/pop/logging"
    13  	"github.com/pkg/errors"
    14  	yaml "gopkg.in/yaml.v2"
    15  )
    16  
    17  // ErrConfigFileNotFound is returned when the pop config file can't be found,
    18  // after looking for it.
    19  var ErrConfigFileNotFound = errors.New("unable to find pop config file")
    20  
    21  var lookupPaths = []string{"", "./config", "/config", "../", "../config", "../..", "../../config"}
    22  
    23  // ConfigName is the name of the YAML databases config file
    24  var ConfigName = "database.yml"
    25  
    26  func init() {
    27  	SetLogger(defaultLogger)
    28  
    29  	ap := os.Getenv("APP_PATH")
    30  	if ap != "" {
    31  		_ = AddLookupPaths(ap)
    32  	}
    33  	ap = os.Getenv("POP_PATH")
    34  	if ap != "" {
    35  		_ = AddLookupPaths(ap)
    36  	}
    37  }
    38  
    39  // LoadConfigFile loads a POP config file from the configured lookup paths
    40  func LoadConfigFile() error {
    41  	path, err := findConfigPath()
    42  	if err != nil {
    43  		return errors.WithStack(err)
    44  	}
    45  	Connections = map[string]*Connection{}
    46  	log(logging.Debug, "Loading config file from %s", path)
    47  	f, err := os.Open(path)
    48  	if err != nil {
    49  		return errors.WithStack(err)
    50  	}
    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, errors.WithStack(err)
   108  	}
   109  	t, err := tmpl.Parse(string(b))
   110  	if err != nil {
   111  		return nil, errors.Wrap(err, "couldn't parse config template")
   112  	}
   113  
   114  	var bb bytes.Buffer
   115  	err = t.Execute(&bb, nil)
   116  	if err != nil {
   117  		return nil, errors.Wrap(err, "couldn't execute config template")
   118  	}
   119  
   120  	deets := map[string]*ConnectionDetails{}
   121  	err = yaml.Unmarshal(bb.Bytes(), &deets)
   122  	return deets, errors.Wrap(err, "couldn't unmarshal config to yaml")
   123  }