Merge pull request #88875 from apelisse/apply-no-op
SSA: Applying same object twice should be a no-op the second time Kubernetes-commit: 71ad0a90205c12e8ebbe6872db30097026916659
This commit is contained in:
commit
ea2fe0c9c4
|
|
@ -588,19 +588,19 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/api",
|
||||
"Rev": "0a52b7486422"
|
||||
"Rev": "510bcd53e1cf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery",
|
||||
"Rev": "ac276fc34867"
|
||||
"Rev": "2b7fa1cb5395"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/client-go",
|
||||
"Rev": "425ea3e5d030"
|
||||
"Rev": "5194bac86967"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/component-base",
|
||||
"Rev": "2cff9454e9d1"
|
||||
"Rev": "dae26a37dccb"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/gengo",
|
||||
|
|
@ -624,7 +624,7 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "sigs.k8s.io/structured-merge-diff/v3",
|
||||
"Rev": "5e70324e7c1c"
|
||||
"Rev": "v3.0.0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "sigs.k8s.io/yaml",
|
||||
|
|
|
|||
18
go.mod
18
go.mod
|
|
@ -43,23 +43,23 @@ require (
|
|||
gopkg.in/square/go-jose.v2 v2.2.2
|
||||
gopkg.in/yaml.v2 v2.2.8
|
||||
gotest.tools v2.2.0+incompatible // indirect
|
||||
k8s.io/api v0.0.0-20200306151724-0a52b7486422
|
||||
k8s.io/apimachinery v0.0.0-20200306202157-ac276fc34867
|
||||
k8s.io/client-go v0.0.0-20200307002527-425ea3e5d030
|
||||
k8s.io/component-base v0.0.0-20200305122702-2cff9454e9d1
|
||||
k8s.io/api v0.0.0-20200307122242-510bcd53e1cf
|
||||
k8s.io/apimachinery v0.0.0-20200307122051-2b7fa1cb5395
|
||||
k8s.io/client-go v0.0.0-20200307122516-5194bac86967
|
||||
k8s.io/component-base v0.0.0-20200307123030-dae26a37dccb
|
||||
k8s.io/klog v1.0.0
|
||||
k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c
|
||||
k8s.io/utils v0.0.0-20200229041039-0a110f9eb7ab
|
||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7
|
||||
sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200207200219-5e70324e7c1c
|
||||
sigs.k8s.io/structured-merge-diff/v3 v3.0.0
|
||||
sigs.k8s.io/yaml v1.2.0
|
||||
)
|
||||
|
||||
replace (
|
||||
golang.org/x/sys => golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a // pinned to release-branch.go1.13
|
||||
golang.org/x/tools => golang.org/x/tools v0.0.0-20190821162956-65e3620a7ae7 // pinned to release-branch.go1.13
|
||||
k8s.io/api => k8s.io/api v0.0.0-20200306151724-0a52b7486422
|
||||
k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20200306202157-ac276fc34867
|
||||
k8s.io/client-go => k8s.io/client-go v0.0.0-20200307002527-425ea3e5d030
|
||||
k8s.io/component-base => k8s.io/component-base v0.0.0-20200305122702-2cff9454e9d1
|
||||
k8s.io/api => k8s.io/api v0.0.0-20200307122242-510bcd53e1cf
|
||||
k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20200307122051-2b7fa1cb5395
|
||||
k8s.io/client-go => k8s.io/client-go v0.0.0-20200307122516-5194bac86967
|
||||
k8s.io/component-base => k8s.io/component-base v0.0.0-20200307123030-dae26a37dccb
|
||||
)
|
||||
|
|
|
|||
12
go.sum
12
go.sum
|
|
@ -337,10 +337,10 @@ gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81
|
|||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
k8s.io/api v0.0.0-20200306151724-0a52b7486422/go.mod h1:4bHpvwcObcdIFmIlnwDdJznMrytQbQCBpJvQgh+FnrA=
|
||||
k8s.io/apimachinery v0.0.0-20200306202157-ac276fc34867/go.mod h1:5X8oEhnd931nEg6/Nkumo00nT6ZsCLp2h7Xwd7Ym6P4=
|
||||
k8s.io/client-go v0.0.0-20200307002527-425ea3e5d030/go.mod h1:9W6NBDAP22mJEhrKYXmPsL6dTQqczIp6+4GZcpWDlKs=
|
||||
k8s.io/component-base v0.0.0-20200305122702-2cff9454e9d1/go.mod h1:3RwNWXDYjUOPPeNNQE+7cPPoWAnnL/938q4q+jnBTNk=
|
||||
k8s.io/api v0.0.0-20200307122242-510bcd53e1cf/go.mod h1:4LcBtPb1OoMEmTvQLypv2jGrtMEK8ZRCPQyc+uOSi4o=
|
||||
k8s.io/apimachinery v0.0.0-20200307122051-2b7fa1cb5395/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA=
|
||||
k8s.io/client-go v0.0.0-20200307122516-5194bac86967/go.mod h1:Ys+cjVY1SiB17qdMz+qc3ItvCfnYLsgVt49tSUmvwTo=
|
||||
k8s.io/component-base v0.0.0-20200307123030-dae26a37dccb/go.mod h1:P8U3GgtuxIyAFY/al0ojd2AP/ifHmYMZBx3HFO3N0es=
|
||||
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
|
|
@ -353,8 +353,8 @@ k8s.io/utils v0.0.0-20200229041039-0a110f9eb7ab/go.mod h1:sZAwmy6armz5eXlNoLmJcl
|
|||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7 h1:uuHDyjllyzRyCIvvn0OBjiRB0SgBZGqHNYAmjR7fO50=
|
||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT7lCHcxMU+mDHEm+nx46H4zuuHZkDP6icnhu0=
|
||||
sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
|
||||
sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200207200219-5e70324e7c1c h1:xQP7F7Lntt2dtYmg12WPQHObOrAyPHlMWP1JVSa79GM=
|
||||
sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200207200219-5e70324e7c1c/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
|
||||
sigs.k8s.io/structured-merge-diff/v3 v3.0.0 h1:dOmIZBMfhcHS09XZkMyUgkq5trg3/jRyJYFZUiaOp8E=
|
||||
sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
|
||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@ func NewDefaultCRDFieldManager(models openapiproto.Models, objectConverter runti
|
|||
// newDefaultFieldManager is a helper function which wraps a Manager with certain default logic.
|
||||
func newDefaultFieldManager(f Manager, objectCreater runtime.ObjectCreater, kind schema.GroupVersionKind) *FieldManager {
|
||||
f = NewStripMetaManager(f)
|
||||
f = NewManagedFieldsUpdater(f)
|
||||
f = NewBuildManagerInfoManager(f, kind.GroupVersion())
|
||||
f = NewCapManagersManager(f, DefaultMaxUpdateManagers)
|
||||
f = NewProbabilisticSkipNonAppliedManager(f, objectCreater, kind, DefaultTrackOnCreateProbability)
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import (
|
|||
"io/ioutil"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
|
@ -104,6 +105,7 @@ func NewTestFieldManager(gvk schema.GroupVersionKind) TestFieldManager {
|
|||
live.SetKind(gvk.Kind)
|
||||
live.SetAPIVersion(gvk.GroupVersion().String())
|
||||
f = fieldmanager.NewStripMetaManager(f)
|
||||
f = fieldmanager.NewManagedFieldsUpdater(f)
|
||||
f = fieldmanager.NewBuildManagerInfoManager(f, gvk.GroupVersion())
|
||||
return TestFieldManager{
|
||||
fieldManager: f,
|
||||
|
|
@ -732,3 +734,52 @@ func TestApplySuccessWithNoManagedFields(t *testing.T) {
|
|||
t.Fatalf("failed to apply object: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Run an update and apply, and make sure that nothing has changed.
|
||||
func TestNoOpChanges(t *testing.T) {
|
||||
f := NewTestFieldManager(schema.FromAPIVersionAndKind("v1", "Pod"))
|
||||
|
||||
obj := &unstructured.Unstructured{Object: map[string]interface{}{}}
|
||||
if err := yaml.Unmarshal([]byte(`{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Pod",
|
||||
"metadata": {
|
||||
"labels": {
|
||||
"a": "b"
|
||||
},
|
||||
}
|
||||
}`), &obj.Object); err != nil {
|
||||
t.Fatalf("error decoding YAML: %v", err)
|
||||
}
|
||||
if err := f.Apply(obj, "fieldmanager_test_apply", false); err != nil {
|
||||
t.Fatalf("failed to apply object: %v", err)
|
||||
}
|
||||
before := f.liveObj.DeepCopyObject()
|
||||
// Wait to make sure the timestamp is different
|
||||
time.Sleep(time.Second)
|
||||
// Applying with a different fieldmanager will create an entry..
|
||||
if err := f.Apply(obj, "fieldmanager_test_apply_other", false); err != nil {
|
||||
t.Fatalf("failed to update object: %v", err)
|
||||
}
|
||||
if reflect.DeepEqual(before, f.liveObj) {
|
||||
t.Fatalf("Applying no-op apply with new manager didn't change object: \n%v", f.liveObj)
|
||||
}
|
||||
before = f.liveObj.DeepCopyObject()
|
||||
// Wait to make sure the timestamp is different
|
||||
time.Sleep(time.Second)
|
||||
if err := f.Update(obj, "fieldmanager_test_update"); err != nil {
|
||||
t.Fatalf("failed to update object: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(before, f.liveObj) {
|
||||
t.Fatalf("No-op update has changed the object:\n%v\n---\n%v", before, f.liveObj)
|
||||
}
|
||||
before = f.liveObj.DeepCopyObject()
|
||||
// Wait to make sure the timestamp is different
|
||||
time.Sleep(time.Second)
|
||||
if err := f.Apply(obj, "fieldmanager_test_apply", true); err != nil {
|
||||
t.Fatalf("failed to re-apply object: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(before, f.liveObj) {
|
||||
t.Fatalf("No-op apply has changed the object:\n%v\n---\n%v", before, f.liveObj)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
Copyright 2020 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package fieldmanager
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"sigs.k8s.io/structured-merge-diff/v3/fieldpath"
|
||||
)
|
||||
|
||||
type managedFieldsUpdater struct {
|
||||
fieldManager Manager
|
||||
}
|
||||
|
||||
var _ Manager = &managedFieldsUpdater{}
|
||||
|
||||
// NewManagedFieldsUpdater is responsible for updating the managedfields
|
||||
// in the object, updating the time of the operation as necessary. For
|
||||
// updates, it uses a hard-coded manager to detect if things have
|
||||
// changed, and swaps back the correct manager after the operation is
|
||||
// done.
|
||||
func NewManagedFieldsUpdater(fieldManager Manager) Manager {
|
||||
return &managedFieldsUpdater{
|
||||
fieldManager: fieldManager,
|
||||
}
|
||||
}
|
||||
|
||||
// Update implements Manager.
|
||||
func (f *managedFieldsUpdater) Update(liveObj, newObj runtime.Object, managed Managed, manager string) (runtime.Object, Managed, error) {
|
||||
self := "current-operation"
|
||||
object, managed, err := f.fieldManager.Update(liveObj, newObj, managed, self)
|
||||
if err != nil {
|
||||
return object, managed, err
|
||||
}
|
||||
|
||||
// If the current operation took any fields from anything, it means the object changed,
|
||||
// so update the timestamp of the managedFieldsEntry and merge with any previous updates from the same manager
|
||||
if vs, ok := managed.Fields()[self]; ok {
|
||||
delete(managed.Fields(), self)
|
||||
|
||||
managed.Times()[manager] = &metav1.Time{Time: time.Now().UTC()}
|
||||
if previous, ok := managed.Fields()[manager]; ok {
|
||||
managed.Fields()[manager] = fieldpath.NewVersionedSet(vs.Set().Union(previous.Set()), vs.APIVersion(), vs.Applied())
|
||||
} else {
|
||||
managed.Fields()[manager] = vs
|
||||
}
|
||||
}
|
||||
|
||||
return object, managed, nil
|
||||
}
|
||||
|
||||
// Apply implements Manager.
|
||||
func (f *managedFieldsUpdater) Apply(liveObj, appliedObj runtime.Object, managed Managed, fieldManager string, force bool) (runtime.Object, Managed, error) {
|
||||
formerManaged := managed.Fields().Copy()
|
||||
object, managed, err := f.fieldManager.Apply(liveObj, appliedObj, managed, fieldManager, force)
|
||||
if err != nil {
|
||||
return object, managed, err
|
||||
}
|
||||
if object != nil || !managed.Fields().Equals(formerManaged) {
|
||||
managed.Times()[fieldManager] = &metav1.Time{Time: time.Now().UTC()}
|
||||
}
|
||||
if object == nil {
|
||||
object = liveObj
|
||||
}
|
||||
return object, managed, nil
|
||||
}
|
||||
|
|
@ -22,7 +22,6 @@ import (
|
|||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal"
|
||||
|
|
@ -116,26 +115,12 @@ func (f *structuredMergeManager) Update(liveObj, newObj runtime.Object, managed
|
|||
apiVersion := fieldpath.APIVersion(f.groupVersion.String())
|
||||
|
||||
// TODO(apelisse) use the first return value when unions are implemented
|
||||
self := "current-operation"
|
||||
_, managedFields, err := f.updater.Update(liveObjTyped, newObjTyped, apiVersion, managed.Fields(), self)
|
||||
_, managedFields, err := f.updater.Update(liveObjTyped, newObjTyped, apiVersion, managed.Fields(), manager)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to update ManagedFields: %v", err)
|
||||
}
|
||||
managed = internal.NewManaged(managedFields, managed.Times())
|
||||
|
||||
// If the current operation took any fields from anything, it means the object changed,
|
||||
// so update the timestamp of the managedFieldsEntry and merge with any previous updates from the same manager
|
||||
if vs, ok := managed.Fields()[self]; ok {
|
||||
delete(managed.Fields(), self)
|
||||
|
||||
managed.Times()[manager] = &metav1.Time{Time: time.Now().UTC()}
|
||||
if previous, ok := managed.Fields()[manager]; ok {
|
||||
managed.Fields()[manager] = fieldpath.NewVersionedSet(vs.Set().Union(previous.Set()), vs.APIVersion(), vs.Applied())
|
||||
} else {
|
||||
managed.Fields()[manager] = vs
|
||||
}
|
||||
}
|
||||
|
||||
return newObj, managed, nil
|
||||
}
|
||||
|
||||
|
|
@ -182,8 +167,9 @@ func (f *structuredMergeManager) Apply(liveObj, patchObj runtime.Object, managed
|
|||
}
|
||||
managed = internal.NewManaged(managedFields, managed.Times())
|
||||
|
||||
// Update the time in the managedFieldsEntry for this operation
|
||||
managed.Times()[manager] = &metav1.Time{Time: time.Now().UTC()}
|
||||
if newObjTyped == nil {
|
||||
return nil, managed, nil
|
||||
}
|
||||
|
||||
newObj, err := f.typeConverter.TypedToObject(newObjTyped)
|
||||
if err != nil {
|
||||
|
|
|
|||
Loading…
Reference in New Issue