github.com/bilus/oya@v0.0.3-0.20190301162104-da4acbd394c6/pkg/raw/oyafile.go (about) 1 package raw 2 3 import ( 4 "bufio" 5 "bytes" 6 "fmt" 7 "io/ioutil" 8 "os" 9 "path" 10 "path/filepath" 11 12 "github.com/bilus/oya/pkg/secrets" 13 log "github.com/sirupsen/logrus" 14 yaml "gopkg.in/yaml.v2" 15 ) 16 17 const DefaultName = "Oyafile" 18 19 // Oyafile represents an unparsed Oyafile. 20 type Oyafile struct { 21 Path string // Path contains normalized absolute path. 22 RootDir string // RootDir is the absolute, normalized path to the project root directory. 23 file []byte // file contains Oyafile contents. 24 } 25 26 // DecodedOyafile is an Oyafile that has been loaded from YAML 27 // but hasn't been parsed yet. 28 type DecodedOyafile map[string]interface{} 29 30 func (o *DecodedOyafile) Merge(values map[string]interface{}) { 31 for k, v := range values { 32 (*o)[k] = v 33 } 34 } 35 36 func Load(oyafilePath, rootDir string) (*Oyafile, bool, error) { 37 raw, err := New(oyafilePath, rootDir) 38 if err != nil { 39 return nil, false, nil 40 } 41 return raw, true, nil 42 } 43 44 func LoadFromDir(dirPath, rootDir string) (*Oyafile, bool, error) { 45 oyafilePath := fullPath(dirPath, "") 46 fi, err := os.Stat(oyafilePath) 47 if err != nil { 48 if os.IsNotExist(err) { 49 return nil, false, nil 50 } 51 return nil, false, err 52 } 53 if fi.IsDir() { 54 return nil, false, nil 55 } 56 return Load(oyafilePath, rootDir) 57 } 58 59 func New(oyafilePath, rootDir string) (*Oyafile, error) { 60 file, err := ioutil.ReadFile(oyafilePath) 61 if err != nil { 62 return nil, err 63 } 64 return &Oyafile{ 65 Path: oyafilePath, 66 RootDir: rootDir, 67 file: file, 68 }, nil 69 } 70 71 func (raw *Oyafile) Decode() (DecodedOyafile, error) { 72 // YAML parser does not handle files without at least one node. 73 empty, err := isEmptyYAML(raw.Path) 74 if err != nil { 75 return nil, err 76 } 77 if empty { 78 return make(DecodedOyafile), nil 79 } 80 decodedOyafile, err := decodeYaml(raw.file) 81 if err != nil { 82 return nil, err 83 } 84 secs, err := secrets.Decrypt(raw.RootDir) 85 if err != nil { 86 if log.GetLevel() == log.DebugLevel { 87 log.Debug(fmt.Sprintf("Secrets could not be loaded at %v. %v", raw.RootDir, err)) 88 } 89 } else { 90 if len(secs) > 0 { 91 decodedSecrets, err := decodeYaml(secs) 92 if err != nil { 93 log.Warn(fmt.Sprintf("Secrets could not be loaded at %v. %v", raw.RootDir, err)) 94 } 95 decodedOyafile.Merge(decodedSecrets) 96 } 97 } 98 return decodedOyafile, nil 99 } 100 101 func decodeYaml(content []byte) (DecodedOyafile, error) { 102 reader := bytes.NewReader(content) 103 decoder := yaml.NewDecoder(reader) 104 var of DecodedOyafile 105 err := decoder.Decode(&of) 106 if err != nil { 107 return nil, err 108 } 109 return of, nil 110 } 111 112 func (raw *Oyafile) LookupKey(key string) (interface{}, bool, error) { 113 of, err := raw.Decode() 114 if err != nil { 115 return nil, false, err 116 } 117 val, ok := of[key] 118 return val, ok, nil 119 } 120 121 func (raw *Oyafile) IsRoot() (bool, error) { 122 _, hasProject, err := raw.LookupKey("Project") 123 if err != nil { 124 return false, err 125 } 126 127 rel, err := filepath.Rel(raw.RootDir, raw.Path) 128 if err != nil { 129 return false, err 130 } 131 return hasProject && rel == DefaultName, nil 132 } 133 134 // isEmptyYAML returns true if the Oyafile contains only blank characters 135 // or YAML comments. 136 func isEmptyYAML(oyafilePath string) (bool, error) { 137 file, err := os.Open(oyafilePath) 138 if err != nil { 139 return false, err 140 } 141 defer file.Close() 142 143 scanner := bufio.NewScanner(file) 144 for scanner.Scan() { 145 if isNode(scanner.Text()) { 146 return false, nil 147 } 148 } 149 150 return true, scanner.Err() 151 } 152 153 func isNode(line string) bool { 154 for _, c := range line { 155 switch c { 156 case '#': 157 return false 158 case ' ', '\t', '\n', '\f', '\r': 159 continue 160 default: 161 return true 162 } 163 } 164 return false 165 } 166 167 func fullPath(projectDir, name string) string { 168 if len(name) == 0 { 169 name = DefaultName 170 } 171 return path.Join(projectDir, name) 172 }