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  }