diff --git a/pkg/controller/daemon/update.go b/pkg/controller/daemon/update.go index abe8987ce29..bfa6e5eaac5 100644 --- a/pkg/controller/daemon/update.go +++ b/pkg/controller/daemon/update.go @@ -17,11 +17,11 @@ 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" @@ -38,6 +38,8 @@ import ( labelsutil "k8s.io/kubernetes/pkg/util/labels" ) +var patchCodec = api.Codecs.LegacyCodec(extensions.SchemeGroupVersion) + // rollingUpdate deletes old daemon set pods making sure that no more than // ds.Spec.UpdateStrategy.RollingUpdate.MaxUnavailable pods are unavailable func (dsc *DaemonSetsController) rollingUpdate(ds *extensions.DaemonSet, hash string) error { @@ -103,7 +105,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 +296,45 @@ 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{} + template := v1.PodTemplate{} err := json.Unmarshal(history.Data.Raw, &template) - return &template, err + return &template.Template, err } -func encodeTemplate(template *v1.PodTemplateSpec) ([]byte, error) { - return json.Marshal(template) +// 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) { + str, err := runtime.Encode(patchCodec, ds) + if err != nil { + return nil, err + } + var raw map[string]interface{} + json.Unmarshal([]byte(str), &raw) + objCopy := make(map[string]interface{}) + specCopy := make(map[string]interface{}) + 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 +348,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 +360,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 }