Files
paralus/pkg/hasher/hash.go
2022-03-21 12:28:27 +05:30

149 lines
3.5 KiB
Go

package hasher
import (
"bytes"
"crypto/sha256"
"fmt"
"sort"
jsoniter "github.com/json-iterator/go"
"github.com/speps/go-hashids"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
k8sapijson "sigs.k8s.io/kustomize/pseudo/k8s/apimachinery/pkg/runtime/serializer/json"
)
var hd *hashids.HashID
const (
// ObjectHash is the hash of the object processed by Rafay
ObjectHash = "rafay.dev/object-hash"
)
func init() {
hd, _ = hashids.NewWithData(&hashids.HashIDData{
Alphabet: "abcdefghijklmnopqrstuvwxyz1234567890",
Salt: "***REMOVED***",
MinLength: 7,
})
}
// IDFromString returns new RafayID for hash id string
func IDFromString(hashID string) (int64, error) {
ids, err := hd.DecodeInt64WithError(hashID)
if err != nil {
return -1, err
}
if len(ids) < 1 {
return -1, fmt.Errorf("no ids could be constructed from string %s", hashID)
}
return ids[0], nil
}
// HashFromInt64 returns new RafayID for hash id string
func HashFromInt64(id int64) (string, error) {
stringHash, err := hd.EncodeInt64([]int64{id})
if err != nil {
return "", err
}
return stringHash, nil
}
// HashFromHex returns new Hash for uuid string
func HashFromHex(id string) (string, error) {
stringHash, err := hd.EncodeHex(id)
if err != nil {
return "", err
}
return stringHash, nil
}
// HashFromHex returns new Hash for uuid string
func IDFromHash(hash string) (string, error) {
id, err := hd.DecodeHex(hash)
if err != nil {
return "", err
}
return id, nil
}
var json = k8sapijson.CaseSensitiveJsonIterator()
// GetHash returns the hash of spec/data for a kubernetes style object
func GetHash(o interface{}) (string, error) {
var b []byte
var err error
b, err = json.Marshal(o)
if err != nil {
return "", err
}
bb := new(bytes.Buffer)
iter := jsoniter.ParseBytes(json, b)
for field := iter.ReadObject(); field != ""; field = iter.ReadObject() {
switch field {
case "data", "binaryData", "spec":
bb.Write(iter.SkipAndReturnBytes())
default:
iter.Skip()
}
}
h := sha256.New()
h.Write(bb.Bytes())
return fmt.Sprintf("%x", h.Sum(nil)), nil
}
//Add adds object hash to rutime object
func Add(o metav1.Object) error {
hash, err := GetHash(o)
if err != nil {
return err
}
annotations := o.GetAnnotations()
if annotations == nil {
annotations = make(map[string]string)
}
annotations[ObjectHash] = hash
o.SetAnnotations(annotations)
return nil
}
// Node Hash should take Labels into the hash calculation since it can be
// set from either side: From core or from cluster
func GetNodeHashFrom(labels map[string]string, taints []v1.Taint, unscheduleable bool) (string, error) {
//add sorted labels
labelsKeys := make([]string, 0)
for k := range labels {
labelsKeys = append(labelsKeys, k)
}
sort.Strings(labelsKeys)
finalLabelsAsString := ""
for _, k := range labelsKeys {
finalLabelsAsString += fmt.Sprintf("%s:%s,", k, labels[k])
}
//add sorted taints
taintKeys := make([]string, 0)
taintMap := make(map[string]v1.Taint)
for _, taint := range taints {
taintKeys = append(taintKeys, taint.Key)
taintMap[taint.Key] = taint
}
sort.Strings(taintKeys)
finalTaintsAsString := ""
for _, k := range taintKeys {
finalTaintsAsString += fmt.Sprintf("%s:%s:%s,", k, taintMap[k].Value, taintMap[k].Effect)
}
finalHashString := fmt.Sprintf("labels:%s,taints:%s,unschedulable:%v", finalLabelsAsString, finalTaintsAsString, unscheduleable)
h := sha256.New()
h.Write([]byte(finalHashString))
return fmt.Sprintf("%x", h.Sum(nil)), nil
}