Merge pull request #100970 from apelisse/add-subresource-managedfields

Add subresource managedfields

Kubernetes-commit: 0f1d105f8d3e114f0bf47307513fe519a71351a2
This commit is contained in:
Kubernetes Publisher 2021-04-17 07:42:34 -07:00
commit 940c107184
15 changed files with 119 additions and 48 deletions

6
Godeps/Godeps.json generated
View File

@ -704,15 +704,15 @@
},
{
"ImportPath": "k8s.io/api",
"Rev": "86cef11b7287"
"Rev": "648b77825832"
},
{
"ImportPath": "k8s.io/apimachinery",
"Rev": "1ba67c107540"
"Rev": "8daf28983e6e"
},
{
"ImportPath": "k8s.io/client-go",
"Rev": "d974964d1226"
"Rev": "8c8fa70f7a2a"
},
{
"ImportPath": "k8s.io/component-base",

12
go.mod
View File

@ -42,9 +42,9 @@ require (
gopkg.in/natefinch/lumberjack.v2 v2.0.0
gopkg.in/square/go-jose.v2 v2.2.2
gopkg.in/yaml.v2 v2.4.0
k8s.io/api v0.0.0-20210416194706-86cef11b7287
k8s.io/apimachinery v0.0.0-20210415154527-1ba67c107540
k8s.io/client-go v0.0.0-20210416194932-d974964d1226
k8s.io/api v0.0.0-20210417155159-648b77825832
k8s.io/apimachinery v0.0.0-20210417144234-8daf28983e6e
k8s.io/client-go v0.0.0-20210417155731-8c8fa70f7a2a
k8s.io/component-base v0.0.0-20210412032905-a57cc3fac704
k8s.io/klog/v2 v2.8.0
k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7
@ -55,8 +55,8 @@ require (
)
replace (
k8s.io/api => k8s.io/api v0.0.0-20210416194706-86cef11b7287
k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20210415154527-1ba67c107540
k8s.io/client-go => k8s.io/client-go v0.0.0-20210416194932-d974964d1226
k8s.io/api => k8s.io/api v0.0.0-20210417155159-648b77825832
k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20210417144234-8daf28983e6e
k8s.io/client-go => k8s.io/client-go v0.0.0-20210417155731-8c8fa70f7a2a
k8s.io/component-base => k8s.io/component-base v0.0.0-20210412032905-a57cc3fac704
)

12
go.sum
View File

@ -602,12 +602,12 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3 h1:sXmLre5bzIR6ypkjXCDI3jHPssRhc8KD/Ome589sc3U=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
k8s.io/api v0.0.0-20210416194706-86cef11b7287 h1:nWl530sx2Lk0p+jyCnG2NcAZ/yO+6vd6MLe/QGTSXKg=
k8s.io/api v0.0.0-20210416194706-86cef11b7287/go.mod h1:DHqxvzZf7mMtXnGgywYgvLoyfA2SGsMd1vdGAEcDjbA=
k8s.io/apimachinery v0.0.0-20210415154527-1ba67c107540 h1:xFowz4QhYZAZ9cFw4BhLhTSkFSiYy6ArOL1oUAs15wI=
k8s.io/apimachinery v0.0.0-20210415154527-1ba67c107540/go.mod h1:CRK4uy8GKqwLtMa4Gj9i/B2EBjcqOsuvwP2xZJWUwmk=
k8s.io/client-go v0.0.0-20210416194932-d974964d1226 h1:4WX6yJ7a+0JxaxSm8ENdZRBCi0CRwMWLdOMEMjBs+ks=
k8s.io/client-go v0.0.0-20210416194932-d974964d1226/go.mod h1:RbsIgkosH5M8OkVp5z3OCH5jtynzbv79nvpmcUh94EM=
k8s.io/api v0.0.0-20210417155159-648b77825832 h1:No2ZlATFo1sNHtuP1p7SIWTkq5cYeyBrAWOT98xshjY=
k8s.io/api v0.0.0-20210417155159-648b77825832/go.mod h1:1rKdGusR4MnUa1eIY/WzLvM2oeduZr38Mmsgdft/2xg=
k8s.io/apimachinery v0.0.0-20210417144234-8daf28983e6e h1:fjczwJMl+07DTfpVAqAfprBYXxSMgsqpjVXfzaEa8k4=
k8s.io/apimachinery v0.0.0-20210417144234-8daf28983e6e/go.mod h1:CRK4uy8GKqwLtMa4Gj9i/B2EBjcqOsuvwP2xZJWUwmk=
k8s.io/client-go v0.0.0-20210417155731-8c8fa70f7a2a h1:akZ/DJ4KidjhpYDg2A7VsptVVwpcR4Fnu0W4vBcOX9U=
k8s.io/client-go v0.0.0-20210417155731-8c8fa70f7a2a/go.mod h1:NcSllaKuzG5G3cDEGxlLIN1HpxPHWykJaa67vGc32XY=
k8s.io/component-base v0.0.0-20210412032905-a57cc3fac704 h1:xstmd0kVbcrlTtk3MFlk3FKj0WXkJqnbaU2j3LzKxMI=
k8s.io/component-base v0.0.0-20210412032905-a57cc3fac704/go.mod h1:zEytEW+Y696JL4g97mo/bP6hzeY26r9zDRw2IFbc7fE=
k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=

View File

@ -28,16 +28,18 @@ import (
type buildManagerInfoManager struct {
fieldManager Manager
groupVersion schema.GroupVersion
subresource string
}
var _ Manager = &buildManagerInfoManager{}
// NewBuildManagerInfoManager creates a new Manager that converts the manager name into a unique identifier
// combining operation and version for update requests, and just operation for apply requests.
func NewBuildManagerInfoManager(f Manager, gv schema.GroupVersion) Manager {
func NewBuildManagerInfoManager(f Manager, gv schema.GroupVersion, subresource string) Manager {
return &buildManagerInfoManager{
fieldManager: f,
groupVersion: gv,
subresource: subresource,
}
}
@ -61,9 +63,10 @@ func (f *buildManagerInfoManager) Apply(liveObj, appliedObj runtime.Object, mana
func (f *buildManagerInfoManager) buildManagerInfo(prefix string, operation metav1.ManagedFieldsOperationType) (string, error) {
managerInfo := metav1.ManagedFieldsEntry{
Manager: prefix,
Operation: operation,
APIVersion: f.groupVersion.String(),
Manager: prefix,
Operation: operation,
APIVersion: f.groupVersion.String(),
Subresource: f.subresource,
}
if managerInfo.Manager == "" {
managerInfo.Manager = "unknown"

View File

@ -47,7 +47,7 @@ func (*fakeManager) Apply(_, _ runtime.Object, _ Managed, _ string, _ bool) (run
func TestCapManagersManagerMergesEntries(t *testing.T) {
f := NewTestFieldManager(schema.FromAPIVersionAndKind("v1", "Pod"),
false,
"",
func(m Manager) Manager {
return NewCapManagersManager(m, 3)
})
@ -113,7 +113,7 @@ func TestCapManagersManagerMergesEntries(t *testing.T) {
func TestCapUpdateManagers(t *testing.T) {
f := NewTestFieldManager(schema.FromAPIVersionAndKind("v1", "Pod"),
false,
"",
func(m Manager) Manager {
return NewCapManagersManager(m, 3)
})

View File

@ -66,39 +66,39 @@ type Manager interface {
// FieldManager updates the managed fields and merge applied
// configurations.
type FieldManager struct {
fieldManager Manager
ignoreManagedFieldsFromRequestObject bool
fieldManager Manager
subresource string
}
// NewFieldManager creates a new FieldManager that decodes, manages, then re-encodes managedFields
// on update and apply requests.
func NewFieldManager(f Manager, ignoreManagedFieldsFromRequestObject bool) *FieldManager {
return &FieldManager{fieldManager: f, ignoreManagedFieldsFromRequestObject: ignoreManagedFieldsFromRequestObject}
func NewFieldManager(f Manager, subresource string) *FieldManager {
return &FieldManager{fieldManager: f, subresource: subresource}
}
// NewDefaultFieldManager creates a new FieldManager that merges apply requests
// and update managed fields for other types of requests.
func NewDefaultFieldManager(typeConverter TypeConverter, objectConverter runtime.ObjectConvertor, objectDefaulter runtime.ObjectDefaulter, objectCreater runtime.ObjectCreater, kind schema.GroupVersionKind, hub schema.GroupVersion, ignoreManagedFieldsFromRequestObject bool, resetFields map[fieldpath.APIVersion]*fieldpath.Set) (*FieldManager, error) {
func NewDefaultFieldManager(typeConverter TypeConverter, objectConverter runtime.ObjectConvertor, objectDefaulter runtime.ObjectDefaulter, objectCreater runtime.ObjectCreater, kind schema.GroupVersionKind, hub schema.GroupVersion, subresource string, resetFields map[fieldpath.APIVersion]*fieldpath.Set) (*FieldManager, error) {
f, err := NewStructuredMergeManager(typeConverter, objectConverter, objectDefaulter, kind.GroupVersion(), hub, resetFields)
if err != nil {
return nil, fmt.Errorf("failed to create field manager: %v", err)
}
return newDefaultFieldManager(f, typeConverter, objectConverter, objectCreater, kind, ignoreManagedFieldsFromRequestObject), nil
return newDefaultFieldManager(f, typeConverter, objectConverter, objectCreater, kind, subresource), nil
}
// NewDefaultCRDFieldManager creates a new FieldManager specifically for
// CRDs. This allows for the possibility of fields which are not defined
// in models, as well as having no models defined at all.
func NewDefaultCRDFieldManager(typeConverter TypeConverter, objectConverter runtime.ObjectConvertor, objectDefaulter runtime.ObjectDefaulter, objectCreater runtime.ObjectCreater, kind schema.GroupVersionKind, hub schema.GroupVersion, ignoreManagedFieldsFromRequestObject bool, resetFields map[fieldpath.APIVersion]*fieldpath.Set) (_ *FieldManager, err error) {
func NewDefaultCRDFieldManager(typeConverter TypeConverter, objectConverter runtime.ObjectConvertor, objectDefaulter runtime.ObjectDefaulter, objectCreater runtime.ObjectCreater, kind schema.GroupVersionKind, hub schema.GroupVersion, subresource string, resetFields map[fieldpath.APIVersion]*fieldpath.Set) (_ *FieldManager, err error) {
f, err := NewCRDStructuredMergeManager(typeConverter, objectConverter, objectDefaulter, kind.GroupVersion(), hub, resetFields)
if err != nil {
return nil, fmt.Errorf("failed to create field manager: %v", err)
}
return newDefaultFieldManager(f, typeConverter, objectConverter, objectCreater, kind, ignoreManagedFieldsFromRequestObject), nil
return newDefaultFieldManager(f, typeConverter, objectConverter, objectCreater, kind, subresource), nil
}
// newDefaultFieldManager is a helper function which wraps a Manager with certain default logic.
func newDefaultFieldManager(f Manager, typeConverter TypeConverter, objectConverter runtime.ObjectConvertor, objectCreater runtime.ObjectCreater, kind schema.GroupVersionKind, ignoreManagedFieldsFromRequestObject bool) *FieldManager {
func newDefaultFieldManager(f Manager, typeConverter TypeConverter, objectConverter runtime.ObjectConvertor, objectCreater runtime.ObjectCreater, kind schema.GroupVersionKind, subresource string) *FieldManager {
return NewFieldManager(
NewLastAppliedUpdater(
NewLastAppliedManager(
@ -107,11 +107,11 @@ func newDefaultFieldManager(f Manager, typeConverter TypeConverter, objectConver
NewBuildManagerInfoManager(
NewManagedFieldsUpdater(
NewStripMetaManager(f),
), kind.GroupVersion(),
), kind.GroupVersion(), subresource,
), DefaultMaxUpdateManagers,
), objectCreater, kind, DefaultTrackOnCreateProbability,
), typeConverter, objectConverter, kind.GroupVersion()),
), ignoreManagedFieldsFromRequestObject,
), subresource,
)
}
@ -167,7 +167,8 @@ func emptyManagedFieldsOnErr(managed Managed, err error) (Managed, error) {
func (f *FieldManager) Update(liveObj, newObj runtime.Object, manager string) (object runtime.Object, err error) {
// First try to decode the managed fields provided in the update,
// This is necessary to allow directly updating managed fields.
managed, err := decodeLiveOrNew(liveObj, newObj, f.ignoreManagedFieldsFromRequestObject)
isSubresource := f.subresource != ""
managed, err := decodeLiveOrNew(liveObj, newObj, isSubresource)
if err != nil {
return newObj, nil
}

View File

@ -85,14 +85,14 @@ type TestFieldManager struct {
}
func NewDefaultTestFieldManager(gvk schema.GroupVersionKind) TestFieldManager {
return NewTestFieldManager(gvk, false, nil)
return NewTestFieldManager(gvk, "", nil)
}
func NewSubresourceTestFieldManager(gvk schema.GroupVersionKind) TestFieldManager {
return NewTestFieldManager(gvk, true, nil)
return NewTestFieldManager(gvk, "scale", nil)
}
func NewTestFieldManager(gvk schema.GroupVersionKind, ignoreManagedFieldsFromRequestObject bool, chainFieldManager func(Manager) Manager) TestFieldManager {
func NewTestFieldManager(gvk schema.GroupVersionKind, subresource string, chainFieldManager func(Manager) Manager) TestFieldManager {
m := NewFakeOpenAPIModels()
typeConverter := NewFakeTypeConverter(m)
converter := newVersionConverter(typeConverter, &fakeObjectConvertor{}, gvk.GroupVersion())
@ -118,7 +118,7 @@ func NewTestFieldManager(gvk schema.GroupVersionKind, ignoreManagedFieldsFromReq
NewBuildManagerInfoManager(
NewManagedFieldsUpdater(
NewStripMetaManager(f),
), gvk.GroupVersion(),
), gvk.GroupVersion(), subresource,
), &fakeObjectCreater{gvk: gvk}, gvk, DefaultTrackOnCreateProbability,
), typeConverter, objectConverter, gvk.GroupVersion(),
),
@ -127,7 +127,7 @@ func NewTestFieldManager(gvk schema.GroupVersionKind, ignoreManagedFieldsFromReq
f = chainFieldManager(f)
}
return TestFieldManager{
fieldManager: NewFieldManager(f, ignoreManagedFieldsFromRequestObject),
fieldManager: NewFieldManager(f, subresource),
apiVersion: gvk.GroupVersion().String(),
emptyObj: live,
liveObj: live.DeepCopyObject(),
@ -1266,7 +1266,7 @@ func TestUpdateViaSubresources(t *testing.T) {
},
})
// Check that managed fields cannot be changed via subresources
// Check that managed fields cannot be changed explicitly via subresources
expectedManager := "fieldmanager_test_subresource"
if err := f.Update(obj, expectedManager); err != nil {
t.Fatalf("failed to apply object: %v", err)

View File

@ -75,11 +75,15 @@ func printManager(manager string) string {
if err := json.Unmarshal([]byte(manager), encodedManager); err != nil {
return fmt.Sprintf("%q", manager)
}
managerStr := fmt.Sprintf("%q", encodedManager.Manager)
if encodedManager.Subresource != "" {
managerStr = fmt.Sprintf("%s with subresource %q", managerStr, encodedManager.Subresource)
}
if encodedManager.Operation == metav1.ManagedFieldsOperationUpdate {
if encodedManager.Time == nil {
return fmt.Sprintf("%q using %v", encodedManager.Manager, encodedManager.APIVersion)
return fmt.Sprintf("%s using %v", managerStr, encodedManager.APIVersion)
}
return fmt.Sprintf("%q using %v at %v", encodedManager.Manager, encodedManager.APIVersion, encodedManager.Time.UTC().Format(time.RFC3339))
return fmt.Sprintf("%s using %v at %v", managerStr, encodedManager.APIVersion, encodedManager.Time.UTC().Format(time.RFC3339))
}
return fmt.Sprintf("%q", encodedManager.Manager)
return managerStr
}

View File

@ -96,6 +96,31 @@ conflicts with "foo" using v1 at 2001-02-03T04:05:06Z:
},
},
},
{
conflict: merge.Conflicts{
merge.Conflict{
Manager: `{"manager":"foo","operation":"Update","subresource":"scale","apiVersion":"v1","time":"2001-02-03T04:05:06Z"}`,
Path: fieldpath.MakePathOrDie("spec", "replicas"),
},
},
expected: &errors.StatusError{
ErrStatus: metav1.Status{
Status: metav1.StatusFailure,
Code: http.StatusConflict,
Reason: metav1.StatusReasonConflict,
Details: &metav1.StatusDetails{
Causes: []metav1.StatusCause{
{
Type: metav1.CauseTypeFieldManagerConflict,
Message: `conflict with "foo" with subresource "scale" using v1 at 2001-02-03T04:05:06Z`,
Field: ".spec.replicas",
},
},
},
Message: `Apply failed with 1 conflict: conflict with "foo" with subresource "scale" using v1 at 2001-02-03T04:05:06Z: .spec.replicas`,
},
},
},
}
for _, tc := range testCases {
actual := internal.NewConflictError(tc.conflict)

View File

@ -213,7 +213,11 @@ func sortEncodedManagedFields(encodedManagedFields []metav1.ManagedFieldsEntry)
if p.Manager != q.Manager {
return p.Manager < q.Manager
}
return p.APIVersion < q.APIVersion
if p.APIVersion != q.APIVersion {
return p.APIVersion < q.APIVersion
}
return p.Subresource < q.Subresource
})
return encodedManagedFields, nil

View File

@ -258,6 +258,15 @@ func TestRoundTripManagedFields(t *testing.T) {
f:storage: {}
manager: foo
operation: Update
`,
`- apiVersion: v1
fieldsType: FieldsV1
fieldsV1:
f:spec:
f:replicas: {}
manager: foo
operation: Update
subresource: scale
`,
}
@ -313,6 +322,18 @@ time: "2001-02-03T04:05:06Z"
`,
expected: "{\"manager\":\"foo\",\"operation\":\"Apply\"}",
},
{
managedFieldsEntry: `
apiVersion: v1
fieldsV1:
f:apiVersion: {}
manager: foo
operation: Apply
subresource: scale
time: "2001-02-03T04:05:06Z"
`,
expected: "{\"manager\":\"foo\",\"operation\":\"Apply\",\"subresource\":\"scale\"}",
},
}
for _, test := range tests {
@ -462,6 +483,19 @@ func TestSortEncodedManagedFields(t *testing.T) {
{Manager: "c", Operation: metav1.ManagedFieldsOperationUpdate, Time: &metav1.Time{time.Date(2000, time.January, 0, 0, 0, 0, 1, time.UTC)}},
},
},
{
name: "entries with subresource field",
managedFields: []metav1.ManagedFieldsEntry{
{Manager: "a", Operation: metav1.ManagedFieldsOperationApply, Subresource: "status"},
{Manager: "a", Operation: metav1.ManagedFieldsOperationApply, Subresource: "scale"},
{Manager: "a", Operation: metav1.ManagedFieldsOperationApply},
},
expected: []metav1.ManagedFieldsEntry{
{Manager: "a", Operation: metav1.ManagedFieldsOperationApply},
{Manager: "a", Operation: metav1.ManagedFieldsOperationApply, Subresource: "scale"},
{Manager: "a", Operation: metav1.ManagedFieldsOperationApply, Subresource: "status"},
},
},
}
for _, test := range tests {

View File

@ -27,7 +27,7 @@ import (
func TestLastAppliedUpdater(t *testing.T) {
f := NewTestFieldManager(schema.FromAPIVersionAndKind("apps/v1", "Deployment"),
false,
"",
func(m Manager) Manager {
return NewLastAppliedUpdater(m)
})

View File

@ -27,7 +27,7 @@ import (
)
func TestNoManagedFieldsUpdateDoesntUpdateTime(t *testing.T) {
f := NewTestFieldManager(schema.FromAPIVersionAndKind("v1", "Pod"), false, nil)
f := NewTestFieldManager(schema.FromAPIVersionAndKind("v1", "Pod"), "", nil)
obj := &unstructured.Unstructured{Object: map[string]interface{}{}}
if err := yaml.Unmarshal([]byte(`{

View File

@ -42,7 +42,7 @@ func (f *fakeObjectCreater) New(_ schema.GroupVersionKind) (runtime.Object, erro
}
func TestNoUpdateBeforeFirstApply(t *testing.T) {
f := NewTestFieldManager(schema.FromAPIVersionAndKind("v1", "Pod"), false, func(m Manager) Manager {
f := NewTestFieldManager(schema.FromAPIVersionAndKind("v1", "Pod"), "", func(m Manager) Manager {
return NewSkipNonAppliedManager(
m,
&fakeObjectCreater{gvk: schema.GroupVersionKind{Version: "v1", Kind: "Pod"}},
@ -82,7 +82,7 @@ func TestNoUpdateBeforeFirstApply(t *testing.T) {
}
func TestUpdateBeforeFirstApply(t *testing.T) {
f := NewTestFieldManager(schema.FromAPIVersionAndKind("v1", "Pod"), false, func(m Manager) Manager {
f := NewTestFieldManager(schema.FromAPIVersionAndKind("v1", "Pod"), "", func(m Manager) Manager {
return NewSkipNonAppliedManager(
m,
&fakeObjectCreater{gvk: schema.GroupVersionKind{Version: "v1", Kind: "Pod"}},

View File

@ -607,7 +607,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
a.group.Creater,
fqKindToRegister,
reqScope.HubGroupVersion,
isSubresource,
subresource,
resetFields,
)
if err != nil {