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 }