github.com/manicqin/nomad@v0.9.5/helper/funcs.go (about)

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