Merge pull request #47075 from janetkuo/ds-history-patch
Automatic merge from submit-queue Change what is stored in DaemonSet history `.data` **What this PR does / why we need it**: In DaemonSet history `.data`, store a strategic merge patch that can be applied to restore a DaemonSet. Only PodSpecTemplate is saved. This will become consistent with the data stored in StatefulSet history. Before this fix, a serialized pod template is stored in `.data`; however, seriazlized pod template isn't a `runtime.RawExtension`, and caused problems when controllers try to patch the history's controller ref. **Which issue this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close that issue when PR gets merged)*: fixes #47008 **Special notes for your reviewer**: @kubernetes/sig-apps-bugs @erictune @kow3ns @kargakis @lukaszo @mengqiy **Release note**: ```release-note NONE ```
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user