github.com/dnephin/dobi@v0.15.0/config/config.go (about) 1 package config 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "path/filepath" 7 "sort" 8 "strings" 9 10 "github.com/dnephin/configtf" 11 pth "github.com/dnephin/configtf/path" 12 "github.com/dnephin/dobi/logging" 13 "github.com/dnephin/dobi/tasks/task" 14 log "github.com/sirupsen/logrus" 15 ) 16 17 // Config is a data object for a full config file 18 type Config struct { 19 FilePath string 20 Meta *MetaConfig 21 Resources map[string]Resource 22 WorkingDir string 23 } 24 25 // NewConfig returns a new Config object 26 func NewConfig() *Config { 27 return &Config{ 28 Resources: make(map[string]Resource), 29 Meta: &MetaConfig{}, 30 } 31 } 32 33 func (c *Config) add(name string, resource Resource) error { 34 if c.contains(name) { 35 return fmt.Errorf("duplicate resource name %q", name) 36 } 37 c.Resources[name] = resource 38 return nil 39 } 40 41 func (c *Config) contains(name string) bool { 42 _, exists := c.Resources[name] 43 return exists 44 } 45 46 // Sorted returns the list of resource names in alphabetical sort order 47 func (c *Config) Sorted() []string { 48 names := []string{} 49 for name := range c.Resources { 50 names = append(names, name) 51 } 52 sort.Strings(names) 53 return names 54 } 55 56 // Load a configuration from a filename 57 func Load(filename string) (*Config, error) { 58 fmtError := func(err error) error { 59 return fmt.Errorf("failed to load config from %q: %s", filename, err) 60 } 61 62 config, err := loadConfig(filename) 63 if err != nil { 64 return nil, fmtError(err) 65 } 66 67 absPath, err := filepath.Abs(filename) 68 if err != nil { 69 return nil, fmtError(err) 70 } 71 config.WorkingDir = filepath.Dir(absPath) 72 config.FilePath = absPath 73 74 if err = validate(config); err != nil { 75 return nil, fmtError(err) 76 } 77 return config, nil 78 } 79 80 func loadConfig(filename string) (*Config, error) { 81 data, err := ioutil.ReadFile(filename) 82 if err != nil { 83 return nil, err 84 } 85 config, err := LoadFromBytes(data) 86 if err != nil { 87 return nil, err 88 } 89 logging.Log.WithFields(log.Fields{"filename": filename}).Debug("Configuration loaded") 90 return config, nil 91 } 92 93 // validate validates all the resources in the config 94 func validate(config *Config) error { 95 for name, resource := range config.Resources { 96 path := pth.NewPath(name) 97 98 if err := configtf.ValidateFields(path, resource); err != nil { 99 return err 100 } 101 if err := validateResourcesExist(path, config, resource.Dependencies()); err != nil { 102 return err 103 } 104 if err := resource.Validate(path, config); err != nil { 105 return err 106 } 107 } 108 return config.Meta.Validate(config) 109 } 110 111 // validateResourcesExist checks that the list of resources is defined in the 112 // config and returns an error if a resources is not defined. 113 func validateResourcesExist(path pth.Path, c *Config, names []string) error { 114 missing := []string{} 115 for _, name := range names { 116 resource := task.ParseName(name).Resource() 117 if _, ok := c.Resources[resource]; !ok { 118 missing = append(missing, resource) 119 } 120 } 121 if len(missing) != 0 { 122 return pth.Errorf(path, "missing dependencies: %s", strings.Join(missing, ", ")) 123 } 124 return nil 125 }