mirror of https://github.com/k3s-io/k3s
Merge pull request #66094 from janetkuo/hash-safe-encode
Automatic merge from submit-queue (batch tested with PRs 66094, 65676). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. Safe encode template hash value to make it consistent with resource name **What this PR does / why we need it**: **Which issue(s) this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close the issue(s) when PR gets merged)*: Fixes #66093 **Special notes for your reviewer**: It's safe to change the function that generates template hash label, because this value is only used when creating a resource and never updated or compared. Therefore, it won't break existing workloads after k8s upgrade/downgrade. Note that we've changed hash before when introducing hash collision avoidance mechanism. @kubernetes/sig-apps-pr-reviews **Release note**: ```release-note NONE ```pull/8/head
commit
0200dd08b0
|
@ -72,6 +72,7 @@ go_library(
|
|||
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/clock:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/errors:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/rand:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/strategicpatch:go_default_library",
|
||||
|
|
|
@ -33,6 +33,7 @@ import (
|
|||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/clock"
|
||||
"k8s.io/apimachinery/pkg/util/rand"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/util/strategicpatch"
|
||||
|
@ -1033,8 +1034,10 @@ func WaitForCacheSync(controllerName string, stopCh <-chan struct{}, cacheSyncs
|
|||
return true
|
||||
}
|
||||
|
||||
// ComputeHash returns a hash value calculated from pod template and a collisionCount to avoid hash collision
|
||||
func ComputeHash(template *v1.PodTemplateSpec, collisionCount *int32) uint32 {
|
||||
// ComputeHash returns a hash value calculated from pod template and
|
||||
// a collisionCount to avoid hash collision. The hash will be safe encoded to
|
||||
// avoid bad words.
|
||||
func ComputeHash(template *v1.PodTemplateSpec, collisionCount *int32) string {
|
||||
podTemplateSpecHasher := fnv.New32a()
|
||||
hashutil.DeepHashObject(podTemplateSpecHasher, *template)
|
||||
|
||||
|
@ -1045,5 +1048,5 @@ func ComputeHash(template *v1.PodTemplateSpec, collisionCount *int32) uint32 {
|
|||
podTemplateSpecHasher.Write(collisionCountBytes)
|
||||
}
|
||||
|
||||
return podTemplateSpecHasher.Sum32()
|
||||
return rand.SafeEncodeString(fmt.Sprint(podTemplateSpecHasher.Sum32()))
|
||||
}
|
||||
|
|
|
@ -36,7 +36,6 @@ go_library(
|
|||
"//staging/src/k8s.io/apimachinery/pkg/util/errors:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/json:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/rand:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
|
|
|
@ -167,7 +167,7 @@ func newPod(podName string, nodeName string, label map[string]string, ds *apps.D
|
|||
var podSpec v1.PodSpec
|
||||
// Copy pod spec from DaemonSet template, or use a default one if DaemonSet is nil
|
||||
if ds != nil {
|
||||
hash := fmt.Sprint(controller.ComputeHash(&ds.Spec.Template, ds.Status.CollisionCount))
|
||||
hash := controller.ComputeHash(&ds.Spec.Template, ds.Status.CollisionCount)
|
||||
newLabels = labelsutil.CloneAndAddLabel(label, apps.DefaultDaemonSetUniqueLabelKey, hash)
|
||||
podSpec = ds.Spec.Template.Spec
|
||||
} else {
|
||||
|
|
|
@ -31,7 +31,6 @@ import (
|
|||
"k8s.io/apimachinery/pkg/runtime"
|
||||
intstrutil "k8s.io/apimachinery/pkg/util/intstr"
|
||||
"k8s.io/apimachinery/pkg/util/json"
|
||||
"k8s.io/apimachinery/pkg/util/rand"
|
||||
podutil "k8s.io/kubernetes/pkg/api/v1/pod"
|
||||
"k8s.io/kubernetes/pkg/controller"
|
||||
"k8s.io/kubernetes/pkg/controller/daemon/util"
|
||||
|
@ -316,8 +315,8 @@ func (dsc *DaemonSetsController) snapshot(ds *apps.DaemonSet, revision int64) (*
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hash := fmt.Sprint(controller.ComputeHash(&ds.Spec.Template, ds.Status.CollisionCount))
|
||||
name := ds.Name + "-" + rand.SafeEncodeString(hash)
|
||||
hash := controller.ComputeHash(&ds.Spec.Template, ds.Status.CollisionCount)
|
||||
name := ds.Name + "-" + hash
|
||||
history := &apps.ControllerRevision{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
|
|
|
@ -29,7 +29,6 @@ go_library(
|
|||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/rand:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/informers/apps/v1:go_default_library",
|
||||
|
|
|
@ -27,7 +27,6 @@ import (
|
|||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/rand"
|
||||
"k8s.io/kubernetes/pkg/controller"
|
||||
deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util"
|
||||
labelsutil "k8s.io/kubernetes/pkg/util/labels"
|
||||
|
@ -181,7 +180,7 @@ func (dc *DeploymentController) getNewReplicaSet(d *apps.Deployment, rsList, old
|
|||
|
||||
// new ReplicaSet does not exist, create one.
|
||||
newRSTemplate := *d.Spec.Template.DeepCopy()
|
||||
podTemplateSpecHash := fmt.Sprintf("%d", controller.ComputeHash(&newRSTemplate, d.Status.CollisionCount))
|
||||
podTemplateSpecHash := controller.ComputeHash(&newRSTemplate, d.Status.CollisionCount)
|
||||
newRSTemplate.Labels = labelsutil.CloneAndAddLabel(d.Spec.Template.Labels, apps.DefaultDeploymentUniqueLabelKey, podTemplateSpecHash)
|
||||
// Add podTemplateHash label to selector.
|
||||
newRSSelector := labelsutil.CloneSelectorAndAddLabel(d.Spec.Selector, apps.DefaultDeploymentUniqueLabelKey, podTemplateSpecHash)
|
||||
|
@ -190,7 +189,7 @@ func (dc *DeploymentController) getNewReplicaSet(d *apps.Deployment, rsList, old
|
|||
newRS := apps.ReplicaSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
// Make the name deterministic, to ensure idempotence
|
||||
Name: d.Name + "-" + rand.SafeEncodeString(podTemplateSpecHash),
|
||||
Name: d.Name + "-" + podTemplateSpecHash,
|
||||
Namespace: d.Namespace,
|
||||
OwnerReferences: []metav1.OwnerReference{*metav1.NewControllerRef(d, controllerKind)},
|
||||
Labels: newRSTemplate.Labels,
|
||||
|
|
|
@ -105,7 +105,7 @@ var podSpec string = `
|
|||
`
|
||||
|
||||
func TestPodTemplateSpecHash(t *testing.T) {
|
||||
seenHashes := make(map[uint32]int)
|
||||
seenHashes := make(map[string]int)
|
||||
|
||||
for i := 0; i < 1000; i++ {
|
||||
specJson := strings.Replace(podSpec, "@@VERSION@@", strconv.Itoa(i), 1)
|
||||
|
|
|
@ -47,12 +47,12 @@ const ControllerRevisionHashLabel = "controller.kubernetes.io/hash"
|
|||
|
||||
// ControllerRevisionName returns the Name for a ControllerRevision in the form prefix-hash. If the length
|
||||
// of prefix is greater than 223 bytes, it is truncated to allow for a name that is no larger than 253 bytes.
|
||||
func ControllerRevisionName(prefix string, hash uint32) string {
|
||||
func ControllerRevisionName(prefix string, hash string) string {
|
||||
if len(prefix) > 223 {
|
||||
prefix = prefix[:223]
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s-%s", prefix, rand.SafeEncodeString(strconv.FormatInt(int64(hash), 10)))
|
||||
return fmt.Sprintf("%s-%s", prefix, hash)
|
||||
}
|
||||
|
||||
// NewControllerRevision returns a ControllerRevision with a ControllerRef pointing to parent and indicating that
|
||||
|
@ -91,13 +91,13 @@ func NewControllerRevision(parent metav1.Object,
|
|||
}
|
||||
hash := HashControllerRevision(cr, collisionCount)
|
||||
cr.Name = ControllerRevisionName(parent.GetName(), hash)
|
||||
cr.Labels[ControllerRevisionHashLabel] = strconv.FormatInt(int64(hash), 10)
|
||||
cr.Labels[ControllerRevisionHashLabel] = hash
|
||||
return cr, nil
|
||||
}
|
||||
|
||||
// HashControllerRevision hashes the contents of revision's Data using FNV hashing. If probe is not nil, the byte value
|
||||
// of probe is added written to the hash as well.
|
||||
func HashControllerRevision(revision *apps.ControllerRevision, probe *int32) uint32 {
|
||||
// of probe is added written to the hash as well. The returned hash will be a safe encoded string to avoid bad words.
|
||||
func HashControllerRevision(revision *apps.ControllerRevision, probe *int32) string {
|
||||
hf := fnv.New32()
|
||||
if len(revision.Data.Raw) > 0 {
|
||||
hf.Write(revision.Data.Raw)
|
||||
|
@ -108,8 +108,7 @@ func HashControllerRevision(revision *apps.ControllerRevision, probe *int32) uin
|
|||
if probe != nil {
|
||||
hf.Write([]byte(strconv.FormatInt(int64(*probe), 10)))
|
||||
}
|
||||
return hf.Sum32()
|
||||
|
||||
return rand.SafeEncodeString(fmt.Sprint(hf.Sum32()))
|
||||
}
|
||||
|
||||
// SortControllerRevisions sorts revisions by their Revision.
|
||||
|
|
|
@ -681,6 +681,10 @@ func checkRSHashLabels(rs *apps.ReplicaSet) (string, error) {
|
|||
return "", fmt.Errorf("unexpected replicaset %s missing required pod-template-hash labels", rs.Name)
|
||||
}
|
||||
|
||||
if !strings.HasSuffix(rs.Name, hash) {
|
||||
return "", fmt.Errorf("unexpected replicaset %s name suffix doesn't match hash %s", rs.Name, hash)
|
||||
}
|
||||
|
||||
return hash, nil
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue