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 }