github.com/adityamillind98/nomad@v0.11.8/helper/funcs.go (about)

     1  package helper
     2  
     3  import (
     4  	"crypto/sha512"
     5  	"fmt"
     6  	"path/filepath"
     7  	"reflect"
     8  	"regexp"
     9  	"strings"
    10  	"time"
    11  
    12  	multierror "github.com/hashicorp/go-multierror"
    13  	"github.com/hashicorp/hcl/hcl/ast"
    14  )
    15  
    16  // validUUID is used to check if a given string looks like a UUID
    17  var validUUID = regexp.MustCompile(`(?i)^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$`)
    18  
    19  // validInterpVarKey matches valid dotted variable names for interpolation. The
    20  // string must begin with one or more non-dot characters which may be followed
    21  // by sequences containing a dot followed by a one or more non-dot characters.
    22  var validInterpVarKey = regexp.MustCompile(`^[^.]+(\.[^.]+)*$`)
    23  
    24  // IsUUID returns true if the given string is a valid UUID.
    25  func IsUUID(str string) bool {
    26  	const uuidLen = 36
    27  	if len(str) != uuidLen {
    28  		return false
    29  	}
    30  
    31  	return validUUID.MatchString(str)
    32  }
    33  
    34  // IsValidInterpVariable returns true if a valid dotted variable names for
    35  // interpolation. The string must begin with one or more non-dot characters
    36  // which may be followed by sequences containing a dot followed by a one or more
    37  // non-dot characters.
    38  func IsValidInterpVariable(str string) bool {
    39  	return validInterpVarKey.MatchString(str)
    40  }
    41  
    42  // HashUUID takes an input UUID and returns a hashed version of the UUID to
    43  // ensure it is well distributed.
    44  func HashUUID(input string) (output string, hashed bool) {
    45  	if !IsUUID(input) {
    46  		return "", false
    47  	}
    48  
    49  	// Hash the input
    50  	buf := sha512.Sum512([]byte(input))
    51  	output = fmt.Sprintf("%08x-%04x-%04x-%04x-%12x",
    52  		buf[0:4],
    53  		buf[4:6],
    54  		buf[6:8],
    55  		buf[8:10],
    56  		buf[10:16])
    57  
    58  	return output, true
    59  }
    60  
    61  // boolToPtr returns the pointer to a boolean
    62  func BoolToPtr(b bool) *bool {
    63  	return &b
    64  }
    65  
    66  // IntToPtr returns the pointer to an int
    67  func IntToPtr(i int) *int {
    68  	return &i
    69  }
    70  
    71  // Int8ToPtr returns the pointer to an int8
    72  func Int8ToPtr(i int8) *int8 {
    73  	return &i
    74  }
    75  
    76  // Int64ToPtr returns the pointer to an int
    77  func Int64ToPtr(i int64) *int64 {
    78  	return &i
    79  }
    80  
    81  // Uint64ToPtr returns the pointer to an uint64
    82  func Uint64ToPtr(u uint64) *uint64 {
    83  	return &u
    84  }
    85  
    86  // UintToPtr returns the pointer to an uint
    87  func UintToPtr(u uint) *uint {
    88  	return &u
    89  }
    90  
    91  // StringToPtr returns the pointer to a string
    92  func StringToPtr(str string) *string {
    93  	return &str
    94  }
    95  
    96  // TimeToPtr returns the pointer to a time stamp
    97  func TimeToPtr(t time.Duration) *time.Duration {
    98  	return &t
    99  }
   100  
   101  // Float64ToPtr returns the pointer to an float64
   102  func Float64ToPtr(f float64) *float64 {
   103  	return &f
   104  }
   105  
   106  func IntMin(a, b int) int {
   107  	if a < b {
   108  		return a
   109  	}
   110  	return b
   111  }
   112  
   113  func IntMax(a, b int) int {
   114  	if a > b {
   115  		return a
   116  	}
   117  	return b
   118  }
   119  
   120  func Uint64Max(a, b uint64) uint64 {
   121  	if a > b {
   122  		return a
   123  	}
   124  	return b
   125  }
   126  
   127  // MapStringStringSliceValueSet returns the set of values in a map[string][]string
   128  func MapStringStringSliceValueSet(m map[string][]string) []string {
   129  	set := make(map[string]struct{})
   130  	for _, slice := range m {
   131  		for _, v := range slice {
   132  			set[v] = struct{}{}
   133  		}
   134  	}
   135  
   136  	flat := make([]string, 0, len(set))
   137  	for k := range set {
   138  		flat = append(flat, k)
   139  	}
   140  	return flat
   141  }
   142  
   143  func SliceStringToSet(s []string) map[string]struct{} {
   144  	m := make(map[string]struct{}, (len(s)+1)/2)
   145  	for _, k := range s {
   146  		m[k] = struct{}{}
   147  	}
   148  	return m
   149  }
   150  
   151  // SliceStringIsSubset returns whether the smaller set of strings is a subset of
   152  // the larger. If the smaller slice is not a subset, the offending elements are
   153  // returned.
   154  func SliceStringIsSubset(larger, smaller []string) (bool, []string) {
   155  	largerSet := make(map[string]struct{}, len(larger))
   156  	for _, l := range larger {
   157  		largerSet[l] = struct{}{}
   158  	}
   159  
   160  	subset := true
   161  	var offending []string
   162  	for _, s := range smaller {
   163  		if _, ok := largerSet[s]; !ok {
   164  			subset = false
   165  			offending = append(offending, s)
   166  		}
   167  	}
   168  
   169  	return subset, offending
   170  }
   171  
   172  func SliceSetDisjoint(first, second []string) (bool, []string) {
   173  	contained := make(map[string]struct{}, len(first))
   174  	for _, k := range first {
   175  		contained[k] = struct{}{}
   176  	}
   177  
   178  	offending := make(map[string]struct{})
   179  	for _, k := range second {
   180  		if _, ok := contained[k]; ok {
   181  			offending[k] = struct{}{}
   182  		}
   183  	}
   184  
   185  	if len(offending) == 0 {
   186  		return true, nil
   187  	}
   188  
   189  	flattened := make([]string, 0, len(offending))
   190  	for k := range offending {
   191  		flattened = append(flattened, k)
   192  	}
   193  	return false, flattened
   194  }
   195  
   196  // CompareSliceSetString returns true if the slices contain the same strings.
   197  // Order is ignored. The slice may be copied but is never altered. The slice is
   198  // assumed to be a set. Multiple instances of an entry are treated the same as
   199  // a single instance.
   200  func CompareSliceSetString(a, b []string) bool {
   201  	n := len(a)
   202  	if n != len(b) {
   203  		return false
   204  	}
   205  
   206  	// Copy a into a map and compare b against it
   207  	amap := make(map[string]struct{}, n)
   208  	for i := range a {
   209  		amap[a[i]] = struct{}{}
   210  	}
   211  
   212  	for i := range b {
   213  		if _, ok := amap[b[i]]; !ok {
   214  			return false
   215  		}
   216  	}
   217  
   218  	return true
   219  }
   220  
   221  // CompareMapStringString returns true if the maps are equivalent. A nil and
   222  // empty map are considered not equal.
   223  func CompareMapStringString(a, b map[string]string) bool {
   224  	if a == nil || b == nil {
   225  		return a == nil && b == nil
   226  	}
   227  
   228  	if len(a) != len(b) {
   229  		return false
   230  	}
   231  
   232  	for k, v := range a {
   233  		v2, ok := b[k]
   234  		if !ok {
   235  			return false
   236  		}
   237  		if v != v2 {
   238  			return false
   239  		}
   240  	}
   241  
   242  	// Already compared all known values in a so only test that keys from b
   243  	// exist in a
   244  	for k := range b {
   245  		if _, ok := a[k]; !ok {
   246  			return false
   247  		}
   248  	}
   249  
   250  	return true
   251  }
   252  
   253  // Helpers for copying generic structures.
   254  func CopyMapStringString(m map[string]string) map[string]string {
   255  	l := len(m)
   256  	if l == 0 {
   257  		return nil
   258  	}
   259  
   260  	c := make(map[string]string, l)
   261  	for k, v := range m {
   262  		c[k] = v
   263  	}
   264  	return c
   265  }
   266  
   267  func CopyMapStringStruct(m map[string]struct{}) map[string]struct{} {
   268  	l := len(m)
   269  	if l == 0 {
   270  		return nil
   271  	}
   272  
   273  	c := make(map[string]struct{}, l)
   274  	for k := range m {
   275  		c[k] = struct{}{}
   276  	}
   277  	return c
   278  }
   279  
   280  func CopyMapStringInt(m map[string]int) map[string]int {
   281  	l := len(m)
   282  	if l == 0 {
   283  		return nil
   284  	}
   285  
   286  	c := make(map[string]int, l)
   287  	for k, v := range m {
   288  		c[k] = v
   289  	}
   290  	return c
   291  }
   292  
   293  func CopyMapStringFloat64(m map[string]float64) map[string]float64 {
   294  	l := len(m)
   295  	if l == 0 {
   296  		return nil
   297  	}
   298  
   299  	c := make(map[string]float64, l)
   300  	for k, v := range m {
   301  		c[k] = v
   302  	}
   303  	return c
   304  }
   305  
   306  // CopyMapStringSliceString copies a map of strings to string slices such as
   307  // http.Header
   308  func CopyMapStringSliceString(m map[string][]string) map[string][]string {
   309  	l := len(m)
   310  	if l == 0 {
   311  		return nil
   312  	}
   313  
   314  	c := make(map[string][]string, l)
   315  	for k, v := range m {
   316  		c[k] = CopySliceString(v)
   317  	}
   318  	return c
   319  }
   320  
   321  func CopySliceString(s []string) []string {
   322  	l := len(s)
   323  	if l == 0 {
   324  		return nil
   325  	}
   326  
   327  	c := make([]string, l)
   328  	for i, v := range s {
   329  		c[i] = v
   330  	}
   331  	return c
   332  }
   333  
   334  func CopySliceInt(s []int) []int {
   335  	l := len(s)
   336  	if l == 0 {
   337  		return nil
   338  	}
   339  
   340  	c := make([]int, l)
   341  	for i, v := range s {
   342  		c[i] = v
   343  	}
   344  	return c
   345  }
   346  
   347  // CleanEnvVar replaces all occurrences of illegal characters in an environment
   348  // variable with the specified byte.
   349  func CleanEnvVar(s string, r byte) string {
   350  	b := []byte(s)
   351  	for i, c := range b {
   352  		switch {
   353  		case c == '_':
   354  		case c == '.':
   355  		case c >= 'a' && c <= 'z':
   356  		case c >= 'A' && c <= 'Z':
   357  		case i > 0 && c >= '0' && c <= '9':
   358  		default:
   359  			// Replace!
   360  			b[i] = r
   361  		}
   362  	}
   363  	return string(b)
   364  }
   365  
   366  func CheckHCLKeys(node ast.Node, valid []string) error {
   367  	var list *ast.ObjectList
   368  	switch n := node.(type) {
   369  	case *ast.ObjectList:
   370  		list = n
   371  	case *ast.ObjectType:
   372  		list = n.List
   373  	default:
   374  		return fmt.Errorf("cannot check HCL keys of type %T", n)
   375  	}
   376  
   377  	validMap := make(map[string]struct{}, len(valid))
   378  	for _, v := range valid {
   379  		validMap[v] = struct{}{}
   380  	}
   381  
   382  	var result error
   383  	for _, item := range list.Items {
   384  		key := item.Keys[0].Token.Value().(string)
   385  		if _, ok := validMap[key]; !ok {
   386  			result = multierror.Append(result, fmt.Errorf(
   387  				"invalid key: %s", key))
   388  		}
   389  	}
   390  
   391  	return result
   392  }
   393  
   394  // UnusedKeys returns a pretty-printed error if any `hcl:",unusedKeys"` is not empty
   395  func UnusedKeys(obj interface{}) error {
   396  	val := reflect.ValueOf(obj)
   397  	if val.Kind() == reflect.Ptr {
   398  		val = reflect.Indirect(val)
   399  	}
   400  	return unusedKeysImpl([]string{}, val)
   401  }
   402  
   403  func unusedKeysImpl(path []string, val reflect.Value) error {
   404  	stype := val.Type()
   405  	for i := 0; i < stype.NumField(); i++ {
   406  		ftype := stype.Field(i)
   407  		fval := val.Field(i)
   408  		tags := strings.Split(ftype.Tag.Get("hcl"), ",")
   409  		name := tags[0]
   410  		tags = tags[1:]
   411  
   412  		if fval.Kind() == reflect.Ptr {
   413  			fval = reflect.Indirect(fval)
   414  		}
   415  
   416  		// struct? recurse. Add the struct's key to the path
   417  		if fval.Kind() == reflect.Struct {
   418  			err := unusedKeysImpl(append([]string{name}, path...), fval)
   419  			if err != nil {
   420  				return err
   421  			}
   422  			continue
   423  		}
   424  
   425  		// Search the hcl tags for "unusedKeys"
   426  		unusedKeys := false
   427  		for _, p := range tags {
   428  			if p == "unusedKeys" {
   429  				unusedKeys = true
   430  				break
   431  			}
   432  		}
   433  
   434  		if unusedKeys {
   435  			ks, ok := fval.Interface().([]string)
   436  			if ok && len(ks) != 0 {
   437  				ps := ""
   438  				if len(path) > 0 {
   439  					ps = strings.Join(path, ".") + " "
   440  				}
   441  				return fmt.Errorf("%sunexpected keys %s",
   442  					ps,
   443  					strings.Join(ks, ", "))
   444  			}
   445  		}
   446  	}
   447  	return nil
   448  }
   449  
   450  // RemoveEqualFold removes the first string that EqualFold matches. It updates xs in place
   451  func RemoveEqualFold(xs *[]string, search string) {
   452  	sl := *xs
   453  	for i, x := range sl {
   454  		if strings.EqualFold(x, search) {
   455  			sl = append(sl[:i], sl[i+1:]...)
   456  			if len(sl) == 0 {
   457  				*xs = nil
   458  			} else {
   459  				*xs = sl
   460  			}
   461  			return
   462  		}
   463  	}
   464  }
   465  
   466  // PathEscapesSandbox returns whether previously cleaned path inside the
   467  // sandbox directory (typically this will be the allocation directory)
   468  // escapes.
   469  func PathEscapesSandbox(sandboxDir, path string) bool {
   470  	rel, err := filepath.Rel(sandboxDir, path)
   471  	if err != nil {
   472  		return true
   473  	}
   474  	if strings.HasPrefix(rel, "..") {
   475  		return true
   476  	}
   477  	return false
   478  }