diff --git a/pkg/controller/BUILD b/pkg/controller/BUILD index 56f0eb24cfc..6278fd367fd 100644 --- a/pkg/controller/BUILD +++ b/pkg/controller/BUILD @@ -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", diff --git a/pkg/controller/controller_utils.go b/pkg/controller/controller_utils.go index fb00a7567ad..ab94ee16696 100644 --- a/pkg/controller/controller_utils.go +++ b/pkg/controller/controller_utils.go @@ -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())) } diff --git a/pkg/controller/daemon/BUILD b/pkg/controller/daemon/BUILD index 310d748b2a8..1be65f07c9e 100644 --- a/pkg/controller/daemon/BUILD +++ b/pkg/controller/daemon/BUILD @@ -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", diff --git a/pkg/controller/daemon/daemon_controller_test.go b/pkg/controller/daemon/daemon_controller_test.go index 1a601479165..ff09e7ae94c 100644 --- a/pkg/controller/daemon/daemon_controller_test.go +++ b/pkg/controller/daemon/daemon_controller_test.go @@ -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 { diff --git a/pkg/controller/daemon/update.go b/pkg/controller/daemon/update.go index 6f1c1a9d3d2..3c1c57e2fc2 100644 --- a/pkg/controller/daemon/update.go +++ b/pkg/controller/daemon/update.go @@ -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, diff --git a/pkg/controller/deployment/BUILD b/pkg/controller/deployment/BUILD index 812166cba5c..1567169a168 100644 --- a/pkg/controller/deployment/BUILD +++ b/pkg/controller/deployment/BUILD @@ -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", diff --git a/pkg/controller/deployment/sync.go b/pkg/controller/deployment/sync.go index 9a1b705f640..63bd28ab18b 100644 --- a/pkg/controller/deployment/sync.go +++ b/pkg/controller/deployment/sync.go @@ -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, diff --git a/pkg/controller/deployment/util/hash_test.go b/pkg/controller/deployment/util/hash_test.go index 649626322cf..7604ba04290 100644 --- a/pkg/controller/deployment/util/hash_test.go +++ b/pkg/controller/deployment/util/hash_test.go @@ -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) diff --git a/pkg/controller/history/controller_history.go b/pkg/controller/history/controller_history.go index 5e4e8f95c61..d0ff9514253 100644 --- a/pkg/controller/history/controller_history.go +++ b/pkg/controller/history/controller_history.go @@ -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. diff --git a/test/integration/deployment/deployment_test.go b/test/integration/deployment/deployment_test.go index 7f475660923..53ae3967989 100644 --- a/test/integration/deployment/deployment_test.go +++ b/test/integration/deployment/deployment_test.go @@ -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 }