github.com/hpcng/singularity@v3.1.1+incompatible/internal/pkg/runtime/engines/config/parser.go (about) 1 // Copyright (c) 2018, Sylabs Inc. All rights reserved. 2 // This software is licensed under a 3-clause BSD license. Please consult the 3 // LICENSE.md file distributed with the sources of this project regarding your 4 // rights to use or distribute this software. 5 6 package config 7 8 import ( 9 "fmt" 10 "io" 11 "io/ioutil" 12 "os" 13 "reflect" 14 "regexp" 15 "strconv" 16 "strings" 17 "text/template" 18 ) 19 20 // Parser parses configuration found in the file with the specified path. 21 func Parser(filepath string, f interface{}) error { 22 var err error 23 var c *os.File 24 var b []byte 25 directives := make(map[string][]string) 26 27 if filepath != "" { 28 c, err = os.Open(filepath) 29 if err != nil { 30 return err 31 } 32 b, err = ioutil.ReadAll(c) 33 if err != nil { 34 return err 35 } 36 37 c.Close() 38 } 39 40 r, err := regexp.Compile(`(?m)^\s*([a-zA-Z _]+)\s*=\s*(.*)$`) 41 if err != nil { 42 return fmt.Errorf("regex compilation failed") 43 } 44 45 for _, match := range r.FindAllSubmatch(b, -1) { 46 if match != nil { 47 key := strings.TrimSpace(string(match[1])) 48 val := strings.TrimSpace(string(match[2])) 49 directives[key] = append(directives[key], val) 50 } 51 } 52 53 val := reflect.ValueOf(f).Elem() 54 55 // Iterate over the fields of f and handle each type 56 for i := 0; i < val.NumField(); i++ { 57 valueField := val.Field(i) 58 typeField := val.Type().Field(i) 59 def := typeField.Tag.Get("default") 60 dir := typeField.Tag.Get("directive") 61 authorized := strings.Split(typeField.Tag.Get("authorized"), ",") 62 63 switch typeField.Type.Kind() { 64 case reflect.Bool: 65 found := false 66 if directives[dir] != nil { 67 for _, a := range authorized { 68 if a == directives[dir][0] { 69 if a == "yes" { 70 valueField.SetBool(true) 71 } else { 72 valueField.SetBool(false) 73 } 74 found = true 75 break 76 } 77 } 78 if found == false { 79 return fmt.Errorf("value authorized for directive '%s' are %s", dir, authorized) 80 } 81 } else { 82 if def == "yes" { 83 valueField.SetBool(true) 84 } else { 85 valueField.SetBool(false) 86 } 87 } 88 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 89 var n int64 90 var err error 91 92 if directives[dir] != nil && directives[dir][0] != "" { 93 n, err = strconv.ParseInt(directives[dir][0], 0, 64) 94 } else { 95 n, err = strconv.ParseInt(def, 0, 64) 96 } 97 if err != nil { 98 return err 99 } 100 valueField.SetInt(n) 101 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 102 var n uint64 103 var err error 104 105 if directives[dir] != nil && directives[dir][0] != "" { 106 n, err = strconv.ParseUint(directives[dir][0], 0, 64) 107 } else { 108 n, err = strconv.ParseUint(def, 0, 64) 109 } 110 if err != nil { 111 return err 112 } 113 valueField.SetUint(n) 114 case reflect.String: 115 found := false 116 if directives[dir] != nil { 117 // To allow for string fields which are intended to be set to *any* value, we must 118 // handle the case where authorized isn't set (implies any value is acceptable) 119 if len(authorized) == 1 && authorized[0] == "" { 120 valueField.SetString(directives[dir][0]) 121 found = true 122 } else { 123 for _, a := range authorized { 124 if a == directives[dir][0] { 125 valueField.SetString(a) 126 found = true 127 break 128 } 129 } 130 } 131 if found == false { 132 return fmt.Errorf("value authorized for directive '%s' are %s", dir, authorized) 133 } 134 } else { 135 valueField.SetString(def) 136 } 137 case reflect.Slice: 138 l := len(directives[dir]) 139 switch valueField.Interface().(type) { 140 case []string: 141 if l == 1 { 142 s := strings.Split(directives[dir][0], ",") 143 l = len(s) 144 if l != 1 { 145 directives[dir] = s 146 } 147 } else if (l == 0 || c == nil) && def != "" { 148 s := strings.Split(def, ",") 149 l = len(s) 150 directives[dir] = s 151 } 152 v := reflect.MakeSlice(typeField.Type, l, l) 153 valueField.Set(v) 154 t := valueField.Interface().([]string) 155 for i, val := range directives[dir] { 156 t[i] = strings.TrimSpace(val) 157 } 158 } 159 } 160 } 161 return nil 162 } 163 164 // Generate executes the template stored at fpath on object f 165 func Generate(out io.Writer, tmplpath string, f interface{}) error { 166 t, err := template.ParseFiles(tmplpath) 167 if err != nil { 168 return err 169 } 170 171 if err := t.Execute(out, f); err != nil { 172 return fmt.Errorf("unable to execute template at %s on %v: %v", tmplpath, f, err) 173 } 174 175 return nil 176 }