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  }