mirror of
https://github.com/grafana/grafana.git
synced 2024-12-01 13:09:22 -06:00
95 lines
2.6 KiB
Go
95 lines
2.6 KiB
Go
package util
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"math/rand"
|
|
"regexp"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/bwmarrin/snowflake"
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
const MaxUIDLength = 40
|
|
|
|
var uidrand = rand.New(rand.NewSource(time.Now().UnixNano()))
|
|
var alphaRunes = []rune("abcdefghijklmnopqrstuvwxyz")
|
|
var hexLetters = []rune("abcdef")
|
|
|
|
var (
|
|
ErrUIDTooLong = fmt.Errorf("UID is longer than %d symbols", MaxUIDLength)
|
|
ErrUIDFormatInvalid = errors.New("invalid format of UID. Only letters, numbers, '-' and '_' are allowed")
|
|
ErrUIDEmpty = fmt.Errorf("UID is empty")
|
|
)
|
|
|
|
// We want to protect our number generator as they are not thread safe. Not using
|
|
// the mutex could result in panics in certain cases where UIDs would be generated
|
|
// at the same time.
|
|
var mtx sync.Mutex
|
|
|
|
// Legacy UID pattern
|
|
var validUIDCharPattern = `a-zA-Z0-9\-\_`
|
|
var validUIDPattern = regexp.MustCompile(`^[` + validUIDCharPattern + `]*$`).MatchString
|
|
|
|
// IsValidShortUID checks if short unique identifier contains valid characters
|
|
// NOTE: future Grafana UIDs will need conform to https://github.com/kubernetes/apimachinery/blob/master/pkg/util/validation/validation.go#L43
|
|
func IsValidShortUID(uid string) bool {
|
|
return validUIDPattern(uid)
|
|
}
|
|
|
|
// IsShortUIDTooLong checks if short unique identifier is too long
|
|
func IsShortUIDTooLong(uid string) bool {
|
|
return len(uid) > MaxUIDLength
|
|
}
|
|
|
|
var node *snowflake.Node
|
|
|
|
// GenerateShortUID will generate a UUID that can also be a k8s name
|
|
// it is guaranteed to have a character as the first letter
|
|
// This UID will be a valid k8s name
|
|
func GenerateShortUID() string {
|
|
mtx.Lock()
|
|
defer mtx.Unlock()
|
|
|
|
if node == nil {
|
|
// ignoring the error happens when input outside 0-1023
|
|
node, _ = snowflake.NewNode(rand.Int63n(1024))
|
|
}
|
|
|
|
// Use UUIDs if snowflake failed (should be never)
|
|
if node == nil {
|
|
uid, err := uuid.NewRandom()
|
|
if err != nil {
|
|
// This should never happen... but this seems better than a panic
|
|
for i := range uid {
|
|
uid[i] = byte(uidrand.Intn(255))
|
|
}
|
|
}
|
|
uuid := uid.String()
|
|
if rune(uuid[0]) < rune('a') {
|
|
uuid = string(hexLetters[uidrand.Intn(len(hexLetters))]) + uuid[1:]
|
|
}
|
|
return uuid
|
|
}
|
|
|
|
return string(hexLetters[uidrand.Intn(len(hexLetters))]) + // start with a letter
|
|
node.Generate().Base36() +
|
|
string(hexLetters[uidrand.Intn(len(hexLetters))]) // a bit more entropy
|
|
}
|
|
|
|
// ValidateUID checks the format and length of the string and returns error if it does not pass the condition
|
|
func ValidateUID(uid string) error {
|
|
if len(uid) == 0 {
|
|
return ErrUIDEmpty
|
|
}
|
|
if IsShortUIDTooLong(uid) {
|
|
return ErrUIDTooLong
|
|
}
|
|
if !IsValidShortUID(uid) {
|
|
return ErrUIDFormatInvalid
|
|
}
|
|
return nil
|
|
}
|