diff --git a/hack/make-rules/test-cmd-util.sh b/hack/make-rules/test-cmd-util.sh index 138318f36f..e17cf4d7ff 100644 --- a/hack/make-rules/test-cmd-util.sh +++ b/hack/make-rules/test-cmd-util.sh @@ -43,6 +43,7 @@ IMAGE_DEPLOYMENT_R2="$IMAGE_NGINX" # deployment-revision2.yaml IMAGE_PERL="gcr.io/google-containers/perl" IMAGE_DAEMONSET_R1="gcr.io/google-containers/pause:2.0" IMAGE_DAEMONSET_R2="gcr.io/google-containers/pause:latest" +IMAGE_DAEMONSET_R2_2="gcr.io/google-containers/nginx:test-cmd" # rollingupdate-daemonset-rv2.yaml # Expose kubectl directly for readability PATH="${KUBE_OUTPUT_HOSTBIN}":$PATH @@ -2900,23 +2901,32 @@ run_daemonset_history_tests() { kubectl apply -f hack/testdata/rollingupdate-daemonset.yaml "${kube_flags[@]}" # Rollback to revision 1 - should be no-op kubectl rollout undo daemonset --to-revision=1 "${kube_flags[@]}" - kube::test::get_object_assert daemonset "{{range.items}}{{$daemonset_image_field}}:{{end}}" "${IMAGE_DAEMONSET_R1}:" + kube::test::get_object_assert daemonset "{{range.items}}{{$daemonset_image_field0}}:{{end}}" "${IMAGE_DAEMONSET_R1}:" + kube::test::get_object_assert daemonset "{{range.items}}{{$container_len}}{{end}}" "1" # Update the DaemonSet (revision 2) kubectl apply -f hack/testdata/rollingupdate-daemonset-rv2.yaml "${kube_flags[@]}" - kube::test::wait_object_assert daemonset "{{range.items}}{{$daemonset_image_field}}:{{end}}" "${IMAGE_DAEMONSET_R2}:" + kube::test::wait_object_assert daemonset "{{range.items}}{{$daemonset_image_field0}}:{{end}}" "${IMAGE_DAEMONSET_R2}:" + kube::test::wait_object_assert daemonset "{{range.items}}{{$daemonset_image_field1}}:{{end}}" "${IMAGE_DAEMONSET_R2_2}:" + kube::test::get_object_assert daemonset "{{range.items}}{{$container_len}}{{end}}" "2" # Rollback to revision 1 with dry-run - should be no-op kubectl rollout undo daemonset --dry-run=true "${kube_flags[@]}" - kube::test::get_object_assert daemonset "{{range.items}}{{$daemonset_image_field}}:{{end}}" "${IMAGE_DAEMONSET_R2}:" + kube::test::get_object_assert daemonset "{{range.items}}{{$daemonset_image_field0}}:{{end}}" "${IMAGE_DAEMONSET_R2}:" + kube::test::get_object_assert daemonset "{{range.items}}{{$daemonset_image_field1}}:{{end}}" "${IMAGE_DAEMONSET_R2_2}:" + kube::test::get_object_assert daemonset "{{range.items}}{{$container_len}}{{end}}" "2" # Rollback to revision 1 kubectl rollout undo daemonset --to-revision=1 "${kube_flags[@]}" - kube::test::wait_object_assert daemonset "{{range.items}}{{$daemonset_image_field}}:{{end}}" "${IMAGE_DAEMONSET_R1}:" + kube::test::wait_object_assert daemonset "{{range.items}}{{$daemonset_image_field0}}:{{end}}" "${IMAGE_DAEMONSET_R1}:" + kube::test::get_object_assert daemonset "{{range.items}}{{$container_len}}{{end}}" "1" # Rollback to revision 1000000 - should fail output_message=$(! kubectl rollout undo daemonset --to-revision=1000000 "${kube_flags[@]}" 2>&1) kube::test::if_has_string "${output_message}" "unable to find specified revision" - kube::test::get_object_assert daemonset "{{range.items}}{{$daemonset_image_field}}:{{end}}" "${IMAGE_DAEMONSET_R1}:" + kube::test::get_object_assert daemonset "{{range.items}}{{$daemonset_image_field0}}:{{end}}" "${IMAGE_DAEMONSET_R1}:" + kube::test::get_object_assert daemonset "{{range.items}}{{$container_len}}{{end}}" "1" # Rollback to last revision kubectl rollout undo daemonset "${kube_flags[@]}" - kube::test::wait_object_assert daemonset "{{range.items}}{{$daemonset_image_field}}:{{end}}" "${IMAGE_DAEMONSET_R2}:" + kube::test::wait_object_assert daemonset "{{range.items}}{{$daemonset_image_field0}}:{{end}}" "${IMAGE_DAEMONSET_R2}:" + kube::test::wait_object_assert daemonset "{{range.items}}{{$daemonset_image_field1}}:{{end}}" "${IMAGE_DAEMONSET_R2_2}:" + kube::test::get_object_assert daemonset "{{range.items}}{{$container_len}}{{end}}" "2" # Clean up kubectl delete -f hack/testdata/rollingupdate-daemonset.yaml "${kube_flags[@]}" } @@ -3153,7 +3163,9 @@ runTests() { pdb_min_available=".spec.minAvailable" pdb_max_unavailable=".spec.maxUnavailable" template_generation_field=".spec.templateGeneration" - daemonset_image_field="(index .spec.template.spec.containers 0).image" + container_len="(len .spec.template.spec.containers)" + daemonset_image_field0="(index .spec.template.spec.containers 0).image" + daemonset_image_field1="(index .spec.template.spec.containers 1).image" # Make sure "default" namespace exists. if kube::test::if_supports_resource "${namespaces}" ; then diff --git a/hack/testdata/rollingupdate-daemonset-rv2.yaml b/hack/testdata/rollingupdate-daemonset-rv2.yaml index dd5768d295..3214dcffe9 100644 --- a/hack/testdata/rollingupdate-daemonset-rv2.yaml +++ b/hack/testdata/rollingupdate-daemonset-rv2.yaml @@ -25,3 +25,5 @@ spec: containers: - name: kubernetes-pause image: gcr.io/google-containers/pause:latest + - name: app + image: gcr.io/google-containers/nginx:test-cmd diff --git a/pkg/controller/daemon/BUILD b/pkg/controller/daemon/BUILD index def942a003..3a185a253f 100644 --- a/pkg/controller/daemon/BUILD +++ b/pkg/controller/daemon/BUILD @@ -41,7 +41,6 @@ go_library( "//plugin/pkg/scheduler/algorithm/predicates:go_default_library", "//plugin/pkg/scheduler/schedulercache:go_default_library", "//vendor/github.com/golang/glog:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", diff --git a/pkg/controller/daemon/update.go b/pkg/controller/daemon/update.go index abe8987ce2..dfb5839b68 100644 --- a/pkg/controller/daemon/update.go +++ b/pkg/controller/daemon/update.go @@ -17,11 +17,12 @@ limitations under the License. package daemon import ( + "bytes" "fmt" "sort" "github.com/golang/glog" - apiequality "k8s.io/apimachinery/pkg/api/equality" + "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" @@ -103,7 +104,7 @@ func (dsc *DaemonSetsController) constructHistory(ds *extensions.DaemonSet) (cur } // Compare histories with ds to separate cur and old history found := false - found, err = Match(&ds.Spec.Template, history) + found, err = Match(ds, history) if err != nil { return nil, nil, err } @@ -294,24 +295,44 @@ func (dsc *DaemonSetsController) controlledHistories(ds *extensions.DaemonSet) ( return result, nil } -// Match check if ds template is semantically equal to the template stored in history -func Match(template *v1.PodTemplateSpec, history *apps.ControllerRevision) (bool, error) { - t, err := DecodeHistory(history) - return apiequality.Semantic.DeepEqual(template, t), err +// Match check if the given DaemonSet's template matches the template stored in the given history. +func Match(ds *extensions.DaemonSet, history *apps.ControllerRevision) (bool, error) { + patch, err := getPatch(ds) + if err != nil { + return false, err + } + return bytes.Equal(patch, history.Data.Raw), nil } -func DecodeHistory(history *apps.ControllerRevision) (*v1.PodTemplateSpec, error) { - template := v1.PodTemplateSpec{} - err := json.Unmarshal(history.Data.Raw, &template) - return &template, err -} +// getPatch returns a strategic merge patch that can be applied to restore a Daemonset to a +// previous version. If the returned error is nil the patch is valid. The current state that we save is just the +// PodSpecTemplate. We can modify this later to encompass more state (or less) and remain compatible with previously +// recorded patches. +func getPatch(ds *extensions.DaemonSet) ([]byte, error) { + dsBytes, err := json.Marshal(ds) + if err != nil { + return nil, err + } + var raw map[string]interface{} + err = json.Unmarshal(dsBytes, &raw) + if err != nil { + return nil, err + } + objCopy := make(map[string]interface{}) + specCopy := make(map[string]interface{}) -func encodeTemplate(template *v1.PodTemplateSpec) ([]byte, error) { - return json.Marshal(template) + // Create a patch of the DaemonSet that replaces spec.template + spec := raw["spec"].(map[string]interface{}) + template := spec["template"].(map[string]interface{}) + specCopy["template"] = template + template["$patch"] = "replace" + objCopy["spec"] = specCopy + patch, err := json.Marshal(objCopy) + return patch, err } func (dsc *DaemonSetsController) snapshot(ds *extensions.DaemonSet, revision int64) (*apps.ControllerRevision, error) { - encodedTemplate, err := encodeTemplate(&ds.Spec.Template) + patch, err := getPatch(ds) if err != nil { return nil, err } @@ -325,7 +346,7 @@ func (dsc *DaemonSetsController) snapshot(ds *extensions.DaemonSet, revision int Annotations: ds.Annotations, OwnerReferences: []metav1.OwnerReference{*newControllerRef(ds)}, }, - Data: runtime.RawExtension{Raw: encodedTemplate}, + Data: runtime.RawExtension{Raw: patch}, Revision: revision, } @@ -337,7 +358,7 @@ func (dsc *DaemonSetsController) snapshot(ds *extensions.DaemonSet, revision int return nil, getErr } // Check if we already created it - done, err := Match(&ds.Spec.Template, existedHistory) + done, err := Match(ds, existedHistory) if err != nil { return nil, err } diff --git a/pkg/kubectl/BUILD b/pkg/kubectl/BUILD index 11d890b89b..2ae2764cf0 100644 --- a/pkg/kubectl/BUILD +++ b/pkg/kubectl/BUILD @@ -154,7 +154,6 @@ go_library( "//vendor/github.com/golang/glog:go_default_library", "//vendor/github.com/spf13/cobra:go_default_library", "//vendor/github.com/spf13/pflag:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library", @@ -167,7 +166,9 @@ go_library( "//vendor/k8s.io/apimachinery/pkg/types:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/util/json:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/util/strategicpatch:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/uuid:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/validation:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library", diff --git a/pkg/kubectl/history.go b/pkg/kubectl/history.go index c058d5a301..3b7743dcaf 100644 --- a/pkg/kubectl/history.go +++ b/pkg/kubectl/history.go @@ -26,14 +26,17 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/json" + "k8s.io/apimachinery/pkg/util/strategicpatch" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/apis/apps" appsv1beta1 "k8s.io/kubernetes/pkg/apis/apps/v1beta1" "k8s.io/kubernetes/pkg/apis/extensions" + extensionsv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1" + externalclientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" "k8s.io/kubernetes/pkg/controller" - "k8s.io/kubernetes/pkg/controller/daemon" deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util" printersinternal "k8s.io/kubernetes/pkg/printers/internalversion" sliceutil "k8s.io/kubernetes/pkg/util/slice" @@ -149,13 +152,10 @@ type DaemonSetHistoryViewer struct { // ViewHistory returns a revision-to-history map as the revision history of a deployment // TODO: this should be a describer func (h *DaemonSetHistoryViewer) ViewHistory(namespace, name string, revision int64) (string, error) { - ds, err := h.c.Extensions().DaemonSets(namespace).Get(name, metav1.GetOptions{}) + versionedClient := versionedClientsetForDaemonSet(h.c) + ds, allHistory, err := controlledHistories(versionedClient, namespace, name) if err != nil { - return "", fmt.Errorf("failed to retrieve DaemonSet %s: %v", name, err) - } - allHistory, err := controlledHistories(h.c, ds) - if err != nil { - return "", fmt.Errorf("unable to find history controlled by DaemonSet %s: %v", ds.Name, err) + return "", fmt.Errorf("unable to find history controlled by DaemonSet %s: %v", name, err) } historyInfo := make(map[int64]*appsv1beta1.ControllerRevision) for _, history := range allHistory { @@ -173,11 +173,11 @@ func (h *DaemonSetHistoryViewer) ViewHistory(namespace, name string, revision in if !ok { return "", fmt.Errorf("unable to find the specified revision") } - template, err := daemon.DecodeHistory(history) + dsOfHistory, err := applyHistory(ds, history) if err != nil { - return "", fmt.Errorf("unable to decode history %s", history.Name) + return "", fmt.Errorf("unable to parse history %s", history.Name) } - return printTemplate(template) + return printTemplate(&dsOfHistory.Spec.Template) } // Print an overview of all Revisions @@ -255,17 +255,19 @@ func (h *StatefulSetHistoryViewer) ViewHistory(namespace, name string, revision } // controlledHistories returns all ControllerRevisions controlled by the given DaemonSet -// TODO: Use external version DaemonSet instead when #3955 is fixed -func controlledHistories(c clientset.Interface, ds *extensions.DaemonSet) ([]*appsv1beta1.ControllerRevision, error) { +func controlledHistories(c externalclientset.Interface, namespace, name string) (*extensionsv1beta1.DaemonSet, []*appsv1beta1.ControllerRevision, error) { + ds, err := c.ExtensionsV1beta1().DaemonSets(namespace).Get(name, metav1.GetOptions{}) + if err != nil { + return nil, nil, fmt.Errorf("failed to retrieve DaemonSet %s: %v", name, err) + } var result []*appsv1beta1.ControllerRevision selector, err := metav1.LabelSelectorAsSelector(ds.Spec.Selector) if err != nil { - return nil, err + return nil, nil, err } - versionedClient := versionedClientsetForDaemonSet(c) - historyList, err := versionedClient.AppsV1beta1().ControllerRevisions(ds.Namespace).List(metav1.ListOptions{LabelSelector: selector.String()}) + historyList, err := c.AppsV1beta1().ControllerRevisions(ds.Namespace).List(metav1.ListOptions{LabelSelector: selector.String()}) if err != nil { - return nil, err + return nil, nil, err } for i := range historyList.Items { history := historyList.Items[i] @@ -275,7 +277,29 @@ func controlledHistories(c clientset.Interface, ds *extensions.DaemonSet) ([]*ap } result = append(result, &history) } - return result, nil + return ds, result, nil +} + +// applyHistory returns a specific revision of DaemonSet by applying the given history to a copy of the given DaemonSet +func applyHistory(ds *extensionsv1beta1.DaemonSet, history *appsv1beta1.ControllerRevision) (*extensionsv1beta1.DaemonSet, error) { + obj, err := api.Scheme.New(ds.GroupVersionKind()) + if err != nil { + return nil, err + } + clone := obj.(*extensionsv1beta1.DaemonSet) + cloneBytes, err := json.Marshal(clone) + if err != nil { + return nil, err + } + patched, err := strategicpatch.StrategicMergePatch(cloneBytes, history.Data.Raw, clone) + if err != nil { + return nil, err + } + err = json.Unmarshal(patched, clone) + if err != nil { + return nil, err + } + return clone, nil } // TODO: copied here until this becomes a describer diff --git a/pkg/kubectl/rollback.go b/pkg/kubectl/rollback.go index b132655193..5efcf34a7c 100644 --- a/pkg/kubectl/rollback.go +++ b/pkg/kubectl/rollback.go @@ -24,10 +24,10 @@ import ( "sort" "syscall" - apiequality "k8s.io/apimachinery/pkg/api/equality" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/watch" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/v1" @@ -36,7 +36,6 @@ import ( "k8s.io/kubernetes/pkg/apis/extensions" externalextensions "k8s.io/kubernetes/pkg/apis/extensions/v1beta1" clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" - "k8s.io/kubernetes/pkg/client/retry" "k8s.io/kubernetes/pkg/controller/daemon" deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util" printersinternal "k8s.io/kubernetes/pkg/printers/internalversion" @@ -221,7 +220,8 @@ func (r *DaemonSetRollbacker) Rollback(obj runtime.Object, updatedAnnotations ma if !ok { return "", fmt.Errorf("passed object is not a DaemonSet: %#v", obj) } - allHistory, err := controlledHistories(r.c, ds) + versionedClient := versionedClientsetForDaemonSet(r.c) + versionedDS, allHistory, err := controlledHistories(versionedClient, ds.Namespace, ds.Name) if err != nil { return "", fmt.Errorf("unable to find history controlled by DaemonSet %s: %v", ds.Name, err) } @@ -249,55 +249,36 @@ func (r *DaemonSetRollbacker) Rollback(obj runtime.Object, updatedAnnotations ma return "", revisionNotFoundErr(toRevision) } - // Get the template of the history to rollback to - toTemplate, err := getInternalTemplate(toHistory) - if err != nil { - return "", err - } - if dryRun { + appliedDS, err := applyHistory(versionedDS, toHistory) + if err != nil { + return "", err + } content := bytes.NewBuffer([]byte{}) w := printersinternal.NewPrefixWriter(content) - printersinternal.DescribePodTemplate(toTemplate, w) + internalTemplate := &api.PodTemplateSpec{} + if err := v1.Convert_v1_PodTemplateSpec_To_api_PodTemplateSpec(&appliedDS.Spec.Template, internalTemplate, nil); err != nil { + return "", fmt.Errorf("failed to convert podtemplate while printing: %v", err) + } + printersinternal.DescribePodTemplate(internalTemplate, w) return fmt.Sprintf("will roll back to %s", content.String()), nil } - // Update DaemonSet template, and retry on conflict - skipUpdate := false - retryErr := retry.RetryOnConflict(retry.DefaultBackoff, func() error { - var err error - ds, err = r.c.Extensions().DaemonSets(ds.Namespace).Get(ds.Name, metav1.GetOptions{}) - if err != nil { - return err - } - if apiequality.Semantic.DeepEqual(toTemplate, &ds.Spec.Template) { - skipUpdate = true - return nil - } - ds.Spec.Template = *toTemplate - _, err = r.c.Extensions().DaemonSets(ds.Namespace).Update(ds) - return err - }) - if retryErr != nil { - return "", retryErr + // Skip if the revision already matches current DaemonSet + done, err := daemon.Match(versionedDS, toHistory) + if err != nil { + return "", err } - if skipUpdate { + if done { return fmt.Sprintf("%s (current template already matches revision %d)", rollbackSkipped, toRevision), nil } - return rollbackSuccess, nil -} + // Restore revision + if _, err = versionedClient.ExtensionsV1beta1().DaemonSets(ds.Namespace).Patch(ds.Name, types.StrategicMergePatchType, toHistory.Data.Raw); err != nil { + return "", fmt.Errorf("failed restoring revision %d: %v", toRevision, err) + } -func getInternalTemplate(toHistory *appsv1beta1.ControllerRevision) (*api.PodTemplateSpec, error) { - template, err := daemon.DecodeHistory(toHistory) - if err != nil { - return nil, err - } - internalTemplate := &api.PodTemplateSpec{} - if err := v1.Convert_v1_PodTemplateSpec_To_api_PodTemplateSpec(template, internalTemplate, nil); err != nil { - return nil, fmt.Errorf("failed to convert podtemplate, %v", err) - } - return internalTemplate, nil + return rollbackSuccess, nil } func revisionNotFoundErr(r int64) error { diff --git a/pkg/kubectl/versioned_client.go b/pkg/kubectl/versioned_client.go index d1b0481c40..e53004527f 100644 --- a/pkg/kubectl/versioned_client.go +++ b/pkg/kubectl/versioned_client.go @@ -39,6 +39,7 @@ func versionedClientsetForDaemonSet(internalClient internalclientset.Interface) return &externalclientset.Clientset{} } return &externalclientset.Clientset{ - AppsV1beta1Client: apps.New(internalClient.Apps().RESTClient()), + AppsV1beta1Client: apps.New(internalClient.Apps().RESTClient()), + ExtensionsV1beta1Client: extensions.New(internalClient.Extensions().RESTClient()), } } diff --git a/test/e2e/daemon_set.go b/test/e2e/daemon_set.go index 391287acb1..ef07f085d6 100644 --- a/test/e2e/daemon_set.go +++ b/test/e2e/daemon_set.go @@ -268,7 +268,7 @@ var _ = framework.KubeDescribe("Daemon set [Serial]", func() { // Check history and labels ds, err = c.Extensions().DaemonSets(ns).Get(ds.Name, metav1.GetOptions{}) Expect(err).NotTo(HaveOccurred()) - first := curHistory(listDaemonHistories(c, ns, label), &ds.Spec.Template) + first := curHistory(listDaemonHistories(c, ns, label), ds) firstHash := ds.Labels[extensions.DefaultDaemonSetUniqueLabelKey] Expect(first.Labels[extensions.DefaultDaemonSetUniqueLabelKey]).To(Equal(firstHash)) Expect(first.Revision).To(Equal(int64(1))) @@ -295,7 +295,7 @@ var _ = framework.KubeDescribe("Daemon set [Serial]", func() { // Check history and labels ds, err = c.Extensions().DaemonSets(ns).Get(ds.Name, metav1.GetOptions{}) Expect(err).NotTo(HaveOccurred()) - cur := curHistory(listDaemonHistories(c, ns, label), &ds.Spec.Template) + cur := curHistory(listDaemonHistories(c, ns, label), ds) curHash := ds.Labels[extensions.DefaultDaemonSetUniqueLabelKey] Expect(cur.Labels[extensions.DefaultDaemonSetUniqueLabelKey]).To(Equal(curHash)) Expect(cur.Revision).To(Equal(int64(2))) @@ -325,7 +325,7 @@ var _ = framework.KubeDescribe("Daemon set [Serial]", func() { // Check history and labels ds, err = c.Extensions().DaemonSets(ns).Get(ds.Name, metav1.GetOptions{}) Expect(err).NotTo(HaveOccurred()) - cur := curHistory(listDaemonHistories(c, ns, label), &ds.Spec.Template) + cur := curHistory(listDaemonHistories(c, ns, label), ds) hash := ds.Labels[extensions.DefaultDaemonSetUniqueLabelKey] Expect(cur.Labels[extensions.DefaultDaemonSetUniqueLabelKey]).To(Equal(hash)) Expect(cur.Revision).To(Equal(int64(1))) @@ -353,7 +353,7 @@ var _ = framework.KubeDescribe("Daemon set [Serial]", func() { // Check history and labels ds, err = c.Extensions().DaemonSets(ns).Get(ds.Name, metav1.GetOptions{}) Expect(err).NotTo(HaveOccurred()) - cur = curHistory(listDaemonHistories(c, ns, label), &ds.Spec.Template) + cur = curHistory(listDaemonHistories(c, ns, label), ds) hash = ds.Labels[extensions.DefaultDaemonSetUniqueLabelKey] Expect(cur.Labels[extensions.DefaultDaemonSetUniqueLabelKey]).To(Equal(hash)) Expect(cur.Revision).To(Equal(int64(2))) @@ -760,14 +760,14 @@ func listDaemonHistories(c clientset.Interface, ns string, label map[string]stri return historyList } -func curHistory(historyList *apps.ControllerRevisionList, template *v1.PodTemplateSpec) *apps.ControllerRevision { +func curHistory(historyList *apps.ControllerRevisionList, ds *extensions.DaemonSet) *apps.ControllerRevision { var curHistory *apps.ControllerRevision foundCurHistories := 0 for i := range historyList.Items { history := &historyList.Items[i] // Every history should have the hash label Expect(len(history.Labels[extensions.DefaultDaemonSetUniqueLabelKey])).To(BeNumerically(">", 0)) - match, err := daemon.Match(template, history) + match, err := daemon.Match(ds, history) Expect(err).NotTo(HaveOccurred()) if match { curHistory = history