mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-10-31 05:40:42 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			302 lines
		
	
	
		
			8.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			302 lines
		
	
	
		
			8.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package guid
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"crypto/rand"
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"net"
 | |
| 	"strings"
 | |
| 	"sync"
 | |
| 	"time"
 | |
| )
 | |
| 
 | |
| // GUID is a unique identifier designed to virtually guarantee non-conflict between values generated
 | |
| // across a distributed system.
 | |
| type GUID struct {
 | |
| 	timeHighAndVersion      uint16
 | |
| 	timeMid                 uint16
 | |
| 	timeLow                 uint32
 | |
| 	clockSeqHighAndReserved uint8
 | |
| 	clockSeqLow             uint8
 | |
| 	node                    [6]byte
 | |
| }
 | |
| 
 | |
| // Format enumerates the values that are supported by Parse and Format
 | |
| type Format string
 | |
| 
 | |
| // These constants define the possible string formats available via this implementation of Guid.
 | |
| const (
 | |
| 	FormatB       Format = "B" // {00000000-0000-0000-0000-000000000000}
 | |
| 	FormatD       Format = "D" // 00000000-0000-0000-0000-000000000000
 | |
| 	FormatN       Format = "N" // 00000000000000000000000000000000
 | |
| 	FormatP       Format = "P" // (00000000-0000-0000-0000-000000000000)
 | |
| 	FormatX       Format = "X" // {0x00000000,0x0000,0x0000,{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}
 | |
| 	FormatDefault Format = FormatD
 | |
| )
 | |
| 
 | |
| // CreationStrategy enumerates the values that are supported for populating the bits of a new Guid.
 | |
| type CreationStrategy string
 | |
| 
 | |
| // These constants define the possible creation strategies available via this implementation of Guid.
 | |
| const (
 | |
| 	CreationStrategyVersion1 CreationStrategy = "version1"
 | |
| 	CreationStrategyVersion2 CreationStrategy = "version2"
 | |
| 	CreationStrategyVersion3 CreationStrategy = "version3"
 | |
| 	CreationStrategyVersion4 CreationStrategy = "version4"
 | |
| 	CreationStrategyVersion5 CreationStrategy = "version5"
 | |
| )
 | |
| 
 | |
| var emptyGUID GUID
 | |
| 
 | |
| // NewGUID generates and returns a new globally unique identifier
 | |
| func NewGUID() GUID {
 | |
| 	result, err := version4()
 | |
| 	if err != nil {
 | |
| 		panic(err) //Version 4 (pseudo-random GUID) doesn't use anything that could fail.
 | |
| 	}
 | |
| 	return result
 | |
| }
 | |
| 
 | |
| var knownStrategies = map[CreationStrategy]func() (GUID, error){
 | |
| 	CreationStrategyVersion1: version1,
 | |
| 	CreationStrategyVersion4: version4,
 | |
| }
 | |
| 
 | |
| // NewGUIDs generates and returns a new globally unique identifier that conforms to the given strategy.
 | |
| func NewGUIDs(strategy CreationStrategy) (GUID, error) {
 | |
| 	if creator, present := knownStrategies[strategy]; present {
 | |
| 		result, err := creator()
 | |
| 		return result, err
 | |
| 	}
 | |
| 	return emptyGUID, errors.New("Unsupported CreationStrategy")
 | |
| }
 | |
| 
 | |
| // Empty returns a copy of the default and empty GUID.
 | |
| func Empty() GUID {
 | |
| 	return emptyGUID
 | |
| }
 | |
| 
 | |
| var knownFormats = map[Format]string{
 | |
| 	FormatN: "%08x%04x%04x%02x%02x%02x%02x%02x%02x%02x%02x",
 | |
| 	FormatD: "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
 | |
| 	FormatB: "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
 | |
| 	FormatP: "(%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x)",
 | |
| 	FormatX: "{0x%08x,0x%04x,0x%04x,{0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x}}",
 | |
| }
 | |
| 
 | |
| // MarshalJSON writes a GUID as a JSON string.
 | |
| func (guid GUID) MarshalJSON() (marshaled []byte, err error) {
 | |
| 	buf := bytes.Buffer{}
 | |
| 
 | |
| 	_, err = buf.WriteRune('"')
 | |
| 	buf.WriteString(guid.String())
 | |
| 	buf.WriteRune('"')
 | |
| 
 | |
| 	marshaled = buf.Bytes()
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // Parse instantiates a GUID from a text representation of the same GUID.
 | |
| // This is the inverse of function family String()
 | |
| func Parse(value string) (GUID, error) {
 | |
| 	var guid GUID
 | |
| 	for _, fullFormat := range knownFormats {
 | |
| 		parity, err := fmt.Sscanf(
 | |
| 			value,
 | |
| 			fullFormat,
 | |
| 			&guid.timeLow,
 | |
| 			&guid.timeMid,
 | |
| 			&guid.timeHighAndVersion,
 | |
| 			&guid.clockSeqHighAndReserved,
 | |
| 			&guid.clockSeqLow,
 | |
| 			&guid.node[0],
 | |
| 			&guid.node[1],
 | |
| 			&guid.node[2],
 | |
| 			&guid.node[3],
 | |
| 			&guid.node[4],
 | |
| 			&guid.node[5])
 | |
| 		if parity == 11 && err == nil {
 | |
| 			return guid, err
 | |
| 		}
 | |
| 	}
 | |
| 	return emptyGUID, fmt.Errorf("\"%s\" is not in a recognized format", value)
 | |
| }
 | |
| 
 | |
| // String returns a text representation of a GUID in the default format.
 | |
| func (guid GUID) String() string {
 | |
| 	return guid.Stringf(FormatDefault)
 | |
| }
 | |
| 
 | |
| // Stringf returns a text representation of a GUID that conforms to the specified format.
 | |
| // If an unrecognized format is provided, the empty string is returned.
 | |
| func (guid GUID) Stringf(format Format) string {
 | |
| 	if format == "" {
 | |
| 		format = FormatDefault
 | |
| 	}
 | |
| 	fullFormat, present := knownFormats[format]
 | |
| 	if !present {
 | |
| 		return ""
 | |
| 	}
 | |
| 	return fmt.Sprintf(
 | |
| 		fullFormat,
 | |
| 		guid.timeLow,
 | |
| 		guid.timeMid,
 | |
| 		guid.timeHighAndVersion,
 | |
| 		guid.clockSeqHighAndReserved,
 | |
| 		guid.clockSeqLow,
 | |
| 		guid.node[0],
 | |
| 		guid.node[1],
 | |
| 		guid.node[2],
 | |
| 		guid.node[3],
 | |
| 		guid.node[4],
 | |
| 		guid.node[5])
 | |
| }
 | |
| 
 | |
| // UnmarshalJSON parses a GUID from a JSON string token.
 | |
| func (guid *GUID) UnmarshalJSON(marshaled []byte) (err error) {
 | |
| 	if len(marshaled) < 2 {
 | |
| 		err = errors.New("JSON GUID must be surrounded by quotes")
 | |
| 		return
 | |
| 	}
 | |
| 	stripped := marshaled[1 : len(marshaled)-1]
 | |
| 	*guid, err = Parse(string(stripped))
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // Version reads a GUID to parse which mechanism of generating GUIDS was employed.
 | |
| // Values returned here are documented in rfc4122.txt.
 | |
| func (guid GUID) Version() uint {
 | |
| 	return uint(guid.timeHighAndVersion >> 12)
 | |
| }
 | |
| 
 | |
| var unixToGregorianOffset = time.Date(1970, 01, 01, 0, 0, 00, 0, time.UTC).Sub(time.Date(1582, 10, 15, 0, 0, 0, 0, time.UTC))
 | |
| 
 | |
| // getRFC4122Time returns a 60-bit count of 100-nanosecond intervals since 00:00:00.00 October 15th, 1582
 | |
| func getRFC4122Time() int64 {
 | |
| 	currentTime := time.Now().UTC().Add(unixToGregorianOffset).UnixNano()
 | |
| 	currentTime /= 100
 | |
| 	return currentTime & 0x0FFFFFFFFFFFFFFF
 | |
| }
 | |
| 
 | |
| var clockSeqVal uint16
 | |
| var clockSeqKey sync.Mutex
 | |
| 
 | |
| func getClockSequence() (uint16, error) {
 | |
| 	clockSeqKey.Lock()
 | |
| 	defer clockSeqKey.Unlock()
 | |
| 
 | |
| 	if 0 == clockSeqVal {
 | |
| 		var temp [2]byte
 | |
| 		if parity, err := rand.Read(temp[:]); !(2 == parity && nil == err) {
 | |
| 			return 0, err
 | |
| 		}
 | |
| 		clockSeqVal = uint16(temp[0])<<8 | uint16(temp[1])
 | |
| 	}
 | |
| 	clockSeqVal++
 | |
| 	return clockSeqVal, nil
 | |
| }
 | |
| 
 | |
| func getMACAddress() (mac [6]byte, err error) {
 | |
| 	var hostNICs []net.Interface
 | |
| 
 | |
| 	hostNICs, err = net.Interfaces()
 | |
| 	if err != nil {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	for _, nic := range hostNICs {
 | |
| 		var parity int
 | |
| 
 | |
| 		parity, err = fmt.Sscanf(
 | |
| 			strings.ToLower(nic.HardwareAddr.String()),
 | |
| 			"%02x:%02x:%02x:%02x:%02x:%02x",
 | |
| 			&mac[0],
 | |
| 			&mac[1],
 | |
| 			&mac[2],
 | |
| 			&mac[3],
 | |
| 			&mac[4],
 | |
| 			&mac[5])
 | |
| 
 | |
| 		if parity == len(mac) {
 | |
| 			return
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	err = fmt.Errorf("No suitable address found")
 | |
| 
 | |
| 	return
 | |
| }
 | |
| 
 | |
| func version1() (result GUID, err error) {
 | |
| 	var localMAC [6]byte
 | |
| 	var clockSeq uint16
 | |
| 
 | |
| 	currentTime := getRFC4122Time()
 | |
| 
 | |
| 	result.timeLow = uint32(currentTime)
 | |
| 	result.timeMid = uint16(currentTime >> 32)
 | |
| 	result.timeHighAndVersion = uint16(currentTime >> 48)
 | |
| 	if err = result.setVersion(1); err != nil {
 | |
| 		return emptyGUID, err
 | |
| 	}
 | |
| 
 | |
| 	if localMAC, err = getMACAddress(); nil != err {
 | |
| 		if parity, err := rand.Read(localMAC[:]); !(len(localMAC) != parity && err == nil) {
 | |
| 			return emptyGUID, err
 | |
| 		}
 | |
| 		localMAC[0] |= 0x1
 | |
| 	}
 | |
| 	copy(result.node[:], localMAC[:])
 | |
| 
 | |
| 	if clockSeq, err = getClockSequence(); nil != err {
 | |
| 		return emptyGUID, err
 | |
| 	}
 | |
| 
 | |
| 	result.clockSeqLow = uint8(clockSeq)
 | |
| 	result.clockSeqHighAndReserved = uint8(clockSeq >> 8)
 | |
| 
 | |
| 	result.setReservedBits()
 | |
| 
 | |
| 	return
 | |
| }
 | |
| 
 | |
| func version4() (GUID, error) {
 | |
| 	var retval GUID
 | |
| 	var bits [10]byte
 | |
| 
 | |
| 	if parity, err := rand.Read(bits[:]); !(len(bits) == parity && err == nil) {
 | |
| 		return emptyGUID, err
 | |
| 	}
 | |
| 	retval.timeHighAndVersion |= uint16(bits[0]) | uint16(bits[1])<<8
 | |
| 	retval.timeMid |= uint16(bits[2]) | uint16(bits[3])<<8
 | |
| 	retval.timeLow |= uint32(bits[4]) | uint32(bits[5])<<8 | uint32(bits[6])<<16 | uint32(bits[7])<<24
 | |
| 	retval.clockSeqHighAndReserved = uint8(bits[8])
 | |
| 	retval.clockSeqLow = uint8(bits[9])
 | |
| 
 | |
| 	//Randomly set clock-sequence, reserved, and node
 | |
| 	if written, err := rand.Read(retval.node[:]); !(nil == err && written == len(retval.node)) {
 | |
| 		retval = emptyGUID
 | |
| 		return retval, err
 | |
| 	}
 | |
| 
 | |
| 	if err := retval.setVersion(4); nil != err {
 | |
| 		return emptyGUID, err
 | |
| 	}
 | |
| 	retval.setReservedBits()
 | |
| 
 | |
| 	return retval, nil
 | |
| }
 | |
| 
 | |
| func (guid *GUID) setVersion(version uint16) error {
 | |
| 	if version > 5 || version == 0 {
 | |
| 		return fmt.Errorf("While setting GUID version, unsupported version: %d", version)
 | |
| 	}
 | |
| 	guid.timeHighAndVersion = (guid.timeHighAndVersion & 0x0fff) | version<<12
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (guid *GUID) setReservedBits() {
 | |
| 	guid.clockSeqHighAndReserved = (guid.clockSeqHighAndReserved & 0x3f) | 0x80
 | |
| }
 |