github.com/bigcommerce/nomad@v0.9.3-bc/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  // CompareMapStringString returns true if the maps are equivalent. A nil and
   194  // empty map are considered not equal.
   195  func CompareMapStringString(a, b map[string]string) bool {
   196  	if a == nil || b == nil {
   197  		return a == nil && b == nil
   198  	}
   199  
   200  	if len(a) != len(b) {
   201  		return false
   202  	}
   203  
   204  	for k, v := range a {
   205  		v2, ok := b[k]
   206  		if !ok {
   207  			return false
   208  		}
   209  		if v != v2 {
   210  			return false
   211  		}
   212  	}
   213  
   214  	// Already compared all known values in a so only test that keys from b
   215  	// exist in a
   216  	for k := range b {
   217  		if _, ok := a[k]; !ok {
   218  			return false
   219  		}
   220  	}
   221  
   222  	return true
   223  }
   224  
   225  // Helpers for copying generic structures.
   226  func CopyMapStringString(m map[string]string) map[string]string {
   227  	l := len(m)
   228  	if l == 0 {
   229  		return nil
   230  	}
   231  
   232  	c := make(map[string]string, l)
   233  	for k, v := range m {
   234  		c[k] = v
   235  	}
   236  	return c
   237  }
   238  
   239  func CopyMapStringStruct(m map[string]struct{}) map[string]struct{} {
   240  	l := len(m)
   241  	if l == 0 {
   242  		return nil
   243  	}
   244  
   245  	c := make(map[string]struct{}, l)
   246  	for k := range m {
   247  		c[k] = struct{}{}
   248  	}
   249  	return c
   250  }
   251  
   252  func CopyMapStringInt(m map[string]int) map[string]int {
   253  	l := len(m)
   254  	if l == 0 {
   255  		return nil
   256  	}
   257  
   258  	c := make(map[string]int, l)
   259  	for k, v := range m {
   260  		c[k] = v
   261  	}
   262  	return c
   263  }
   264  
   265  func CopyMapStringFloat64(m map[string]float64) map[string]float64 {
   266  	l := len(m)
   267  	if l == 0 {
   268  		return nil
   269  	}
   270  
   271  	c := make(map[string]float64, l)
   272  	for k, v := range m {
   273  		c[k] = v
   274  	}
   275  	return c
   276  }
   277  
   278  // CopyMapStringSliceString copies a map of strings to string slices such as
   279  // http.Header
   280  func CopyMapStringSliceString(m map[string][]string) map[string][]string {
   281  	l := len(m)
   282  	if l == 0 {
   283  		return nil
   284  	}
   285  
   286  	c := make(map[string][]string, l)
   287  	for k, v := range m {
   288  		c[k] = CopySliceString(v)
   289  	}
   290  	return c
   291  }
   292  
   293  func CopySliceString(s []string) []string {
   294  	l := len(s)
   295  	if l == 0 {
   296  		return nil
   297  	}
   298  
   299  	c := make([]string, l)
   300  	for i, v := range s {
   301  		c[i] = v
   302  	}
   303  	return c
   304  }
   305  
   306  func CopySliceInt(s []int) []int {
   307  	l := len(s)
   308  	if l == 0 {
   309  		return nil
   310  	}
   311  
   312  	c := make([]int, l)
   313  	for i, v := range s {
   314  		c[i] = v
   315  	}
   316  	return c
   317  }
   318  
   319  // CleanEnvVar replaces all occurrences of illegal characters in an environment
   320  // variable with the specified byte.
   321  func CleanEnvVar(s string, r byte) string {
   322  	b := []byte(s)
   323  	for i, c := range b {
   324  		switch {
   325  		case c == '_':
   326  		case c == '.':
   327  		case c >= 'a' && c <= 'z':
   328  		case c >= 'A' && c <= 'Z':
   329  		case i > 0 && c >= '0' && c <= '9':
   330  		default:
   331  			// Replace!
   332  			b[i] = r
   333  		}
   334  	}
   335  	return string(b)
   336  }
   337  
   338  func CheckHCLKeys(node ast.Node, valid []string) error {
   339  	var list *ast.ObjectList
   340  	switch n := node.(type) {
   341  	case *ast.ObjectList:
   342  		list = n
   343  	case *ast.ObjectType:
   344  		list = n.List
   345  	default:
   346  		return fmt.Errorf("cannot check HCL keys of type %T", n)
   347  	}
   348  
   349  	validMap := make(map[string]struct{}, len(valid))
   350  	for _, v := range valid {
   351  		validMap[v] = struct{}{}
   352  	}
   353  
   354  	var result error
   355  	for _, item := range list.Items {
   356  		key := item.Keys[0].Token.Value().(string)
   357  		if _, ok := validMap[key]; !ok {
   358  			result = multierror.Append(result, fmt.Errorf(
   359  				"invalid key: %s", key))
   360  		}
   361  	}
   362  
   363  	return result
   364  }