Skip to content

Commit 7e97b4b

Browse files
authored
Merge pull request kubernetes#114868 from apelisse/private-internal-managers
fieldmanager: Make internal managers private
2 parents cf81822 + ad65b25 commit 7e97b4b

29 files changed

+597
-419
lines changed

staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -679,7 +679,7 @@ func (r *crdHandler) getOrCreateServingInfoFor(uid types.UID, name string) (*crd
679679
openAPIModels = nil
680680
}
681681

682-
var typeConverter fieldmanager.TypeConverter = fieldmanager.DeducedTypeConverter{}
682+
var typeConverter fieldmanager.TypeConverter = fieldmanager.NewDeducedTypeConverter()
683683
if openAPIModels != nil {
684684
typeConverter, err = fieldmanager.NewTypeConverter(openAPIModels, crd.Spec.PreserveUnknownFields)
685685
if err != nil {

staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/admission.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222

2323
"k8s.io/apimachinery/pkg/api/meta"
2424
"k8s.io/apiserver/pkg/admission"
25+
"k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal"
2526
"k8s.io/apiserver/pkg/warning"
2627
)
2728

@@ -70,7 +71,7 @@ func (admit *managedFieldsValidatingAdmissionController) Admit(ctx context.Conte
7071
return err
7172
}
7273
managedFieldsAfterAdmission := objectMeta.GetManagedFields()
73-
if _, err := DecodeManagedFields(managedFieldsAfterAdmission); err != nil {
74+
if _, err := internal.DecodeManagedFields(managedFieldsAfterAdmission); err != nil {
7475
objectMeta.SetManagedFields(managedFieldsBeforeAdmission)
7576
warning.AddWarning(ctx, "",
7677
fmt.Sprintf(InvalidManagedFieldsAfterMutatingAdmissionWarningFormat,

staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/fieldmanager.go

Lines changed: 9 additions & 216 deletions
Original file line numberDiff line numberDiff line change
@@ -18,247 +18,40 @@ package fieldmanager
1818

1919
import (
2020
"fmt"
21-
"reflect"
22-
"time"
2321

24-
"k8s.io/apimachinery/pkg/api/meta"
2522
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2623
"k8s.io/apimachinery/pkg/runtime"
2724
"k8s.io/apimachinery/pkg/runtime/schema"
2825
"k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal"
29-
"k8s.io/klog/v2"
3026
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
31-
"sigs.k8s.io/structured-merge-diff/v4/merge"
3227
)
3328

34-
// DefaultMaxUpdateManagers defines the default maximum retained number of managedFields entries from updates
35-
// if the number of update managers exceeds this, the oldest entries will be merged until the number is below the maximum.
36-
// TODO(jennybuckley): Determine if this is really the best value. Ideally we wouldn't unnecessarily merge too many entries.
37-
const DefaultMaxUpdateManagers int = 10
38-
39-
// DefaultTrackOnCreateProbability defines the default probability that the field management of an object
40-
// starts being tracked from the object's creation, instead of from the first time the object is applied to.
41-
const DefaultTrackOnCreateProbability float32 = 1
42-
43-
var atMostEverySecond = internal.NewAtMostEvery(time.Second)
44-
45-
// Managed groups a fieldpath.ManagedFields together with the timestamps associated with each operation.
46-
type Managed interface {
47-
// Fields gets the fieldpath.ManagedFields.
48-
Fields() fieldpath.ManagedFields
49-
50-
// Times gets the timestamps associated with each operation.
51-
Times() map[string]*metav1.Time
52-
}
53-
54-
// Manager updates the managed fields and merges applied configurations.
55-
type Manager interface {
56-
// Update is used when the object has already been merged (non-apply
57-
// use-case), and simply updates the managed fields in the output
58-
// object.
59-
// * `liveObj` is not mutated by this function
60-
// * `newObj` may be mutated by this function
61-
// Returns the new object with managedFields removed, and the object's new
62-
// proposed managedFields separately.
63-
Update(liveObj, newObj runtime.Object, managed Managed, manager string) (runtime.Object, Managed, error)
64-
65-
// Apply is used when server-side apply is called, as it merges the
66-
// object and updates the managed fields.
67-
// * `liveObj` is not mutated by this function
68-
// * `newObj` may be mutated by this function
69-
// Returns the new object with managedFields removed, and the object's new
70-
// proposed managedFields separately.
71-
Apply(liveObj, appliedObj runtime.Object, managed Managed, fieldManager string, force bool) (runtime.Object, Managed, error)
72-
}
73-
74-
// FieldManager updates the managed fields and merge applied
29+
// FieldManager updates the managed fields and merges applied
7530
// configurations.
76-
type FieldManager struct {
77-
fieldManager Manager
78-
subresource string
79-
}
80-
81-
// NewFieldManager creates a new FieldManager that decodes, manages, then re-encodes managedFields
82-
// on update and apply requests.
83-
func NewFieldManager(f Manager, subresource string) *FieldManager {
84-
return &FieldManager{fieldManager: f, subresource: subresource}
85-
}
31+
type FieldManager = internal.FieldManager
8632

8733
// NewDefaultFieldManager creates a new FieldManager that merges apply requests
8834
// and update managed fields for other types of requests.
8935
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) {
90-
f, err := NewStructuredMergeManager(typeConverter, objectConverter, objectDefaulter, kind.GroupVersion(), hub, resetFields)
36+
f, err := internal.NewStructuredMergeManager(typeConverter, objectConverter, objectDefaulter, kind.GroupVersion(), hub, resetFields)
9137
if err != nil {
9238
return nil, fmt.Errorf("failed to create field manager: %v", err)
9339
}
94-
return newDefaultFieldManager(f, typeConverter, objectConverter, objectCreater, kind, subresource), nil
40+
return internal.NewDefaultFieldManager(f, typeConverter, objectConverter, objectCreater, kind, subresource), nil
9541
}
9642

9743
// NewDefaultCRDFieldManager creates a new FieldManager specifically for
9844
// CRDs. This allows for the possibility of fields which are not defined
9945
// in models, as well as having no models defined at all.
10046
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) {
101-
f, err := NewCRDStructuredMergeManager(typeConverter, objectConverter, objectDefaulter, kind.GroupVersion(), hub, resetFields)
47+
f, err := internal.NewCRDStructuredMergeManager(typeConverter, objectConverter, objectDefaulter, kind.GroupVersion(), hub, resetFields)
10248
if err != nil {
10349
return nil, fmt.Errorf("failed to create field manager: %v", err)
10450
}
105-
return newDefaultFieldManager(f, typeConverter, objectConverter, objectCreater, kind, subresource), nil
106-
}
107-
108-
// newDefaultFieldManager is a helper function which wraps a Manager with certain default logic.
109-
func newDefaultFieldManager(f Manager, typeConverter TypeConverter, objectConverter runtime.ObjectConvertor, objectCreater runtime.ObjectCreater, kind schema.GroupVersionKind, subresource string) *FieldManager {
110-
return NewFieldManager(
111-
NewLastAppliedUpdater(
112-
NewLastAppliedManager(
113-
NewProbabilisticSkipNonAppliedManager(
114-
NewCapManagersManager(
115-
NewBuildManagerInfoManager(
116-
NewManagedFieldsUpdater(
117-
NewStripMetaManager(f),
118-
), kind.GroupVersion(), subresource,
119-
), DefaultMaxUpdateManagers,
120-
), objectCreater, kind, DefaultTrackOnCreateProbability,
121-
), typeConverter, objectConverter, kind.GroupVersion()),
122-
), subresource,
123-
)
124-
}
125-
126-
// DecodeManagedFields converts ManagedFields from the wire format (api format)
127-
// to the format used by sigs.k8s.io/structured-merge-diff
128-
func DecodeManagedFields(encodedManagedFields []metav1.ManagedFieldsEntry) (Managed, error) {
129-
return internal.DecodeManagedFields(encodedManagedFields)
130-
}
131-
132-
func decodeLiveOrNew(liveObj, newObj runtime.Object, ignoreManagedFieldsFromRequestObject bool) (Managed, error) {
133-
liveAccessor, err := meta.Accessor(liveObj)
134-
if err != nil {
135-
return nil, err
136-
}
137-
138-
// We take the managedFields of the live object in case the request tries to
139-
// manually set managedFields via a subresource.
140-
if ignoreManagedFieldsFromRequestObject {
141-
return emptyManagedFieldsOnErr(DecodeManagedFields(liveAccessor.GetManagedFields()))
142-
}
143-
144-
// If the object doesn't have metadata, we should just return without trying to
145-
// set the managedFields at all, so creates/updates/patches will work normally.
146-
newAccessor, err := meta.Accessor(newObj)
147-
if err != nil {
148-
return nil, err
149-
}
150-
151-
if isResetManagedFields(newAccessor.GetManagedFields()) {
152-
return internal.NewEmptyManaged(), nil
153-
}
154-
155-
// If the managed field is empty or we failed to decode it,
156-
// let's try the live object. This is to prevent clients who
157-
// don't understand managedFields from deleting it accidentally.
158-
managed, err := DecodeManagedFields(newAccessor.GetManagedFields())
159-
if err != nil || len(managed.Fields()) == 0 {
160-
return emptyManagedFieldsOnErr(DecodeManagedFields(liveAccessor.GetManagedFields()))
161-
}
162-
return managed, nil
163-
}
164-
165-
func emptyManagedFieldsOnErr(managed Managed, err error) (Managed, error) {
166-
if err != nil {
167-
return internal.NewEmptyManaged(), nil
168-
}
169-
return managed, nil
51+
return internal.NewDefaultFieldManager(f, typeConverter, objectConverter, objectCreater, kind, subresource), nil
17052
}
17153

172-
// Update is used when the object has already been merged (non-apply
173-
// use-case), and simply updates the managed fields in the output
174-
// object.
175-
func (f *FieldManager) Update(liveObj, newObj runtime.Object, manager string) (object runtime.Object, err error) {
176-
// First try to decode the managed fields provided in the update,
177-
// This is necessary to allow directly updating managed fields.
178-
isSubresource := f.subresource != ""
179-
managed, err := decodeLiveOrNew(liveObj, newObj, isSubresource)
180-
if err != nil {
181-
return newObj, nil
182-
}
183-
184-
internal.RemoveObjectManagedFields(newObj)
185-
186-
if object, managed, err = f.fieldManager.Update(liveObj, newObj, managed, manager); err != nil {
187-
return nil, err
188-
}
189-
190-
if err = internal.EncodeObjectManagedFields(object, managed); err != nil {
191-
return nil, fmt.Errorf("failed to encode managed fields: %v", err)
192-
}
193-
194-
return object, nil
195-
}
196-
197-
// UpdateNoErrors is the same as Update, but it will not return
198-
// errors. If an error happens, the object is returned with
199-
// managedFields cleared.
200-
func (f *FieldManager) UpdateNoErrors(liveObj, newObj runtime.Object, manager string) runtime.Object {
201-
obj, err := f.Update(liveObj, newObj, manager)
202-
if err != nil {
203-
atMostEverySecond.Do(func() {
204-
ns, name := "unknown", "unknown"
205-
if accessor, err := meta.Accessor(newObj); err == nil {
206-
ns = accessor.GetNamespace()
207-
name = accessor.GetName()
208-
}
209-
210-
klog.ErrorS(err, "[SHOULD NOT HAPPEN] failed to update managedFields", "VersionKind",
211-
newObj.GetObjectKind().GroupVersionKind(), "namespace", ns, "name", name)
212-
})
213-
// Explicitly remove managedFields on failure, so that
214-
// we can't have garbage in it.
215-
internal.RemoveObjectManagedFields(newObj)
216-
return newObj
217-
}
218-
return obj
219-
}
220-
221-
// Returns true if the managedFields indicate that the user is trying to
222-
// reset the managedFields, i.e. if the list is non-nil but empty, or if
223-
// the list has one empty item.
224-
func isResetManagedFields(managedFields []metav1.ManagedFieldsEntry) bool {
225-
if len(managedFields) == 0 {
226-
return managedFields != nil
227-
}
228-
229-
if len(managedFields) == 1 {
230-
return reflect.DeepEqual(managedFields[0], metav1.ManagedFieldsEntry{})
231-
}
232-
233-
return false
234-
}
235-
236-
// Apply is used when server-side apply is called, as it merges the
237-
// object and updates the managed fields.
238-
func (f *FieldManager) Apply(liveObj, appliedObj runtime.Object, manager string, force bool) (object runtime.Object, err error) {
239-
// If the object doesn't have metadata, apply isn't allowed.
240-
accessor, err := meta.Accessor(liveObj)
241-
if err != nil {
242-
return nil, fmt.Errorf("couldn't get accessor: %v", err)
243-
}
244-
245-
// Decode the managed fields in the live object, since it isn't allowed in the patch.
246-
managed, err := DecodeManagedFields(accessor.GetManagedFields())
247-
if err != nil {
248-
return nil, fmt.Errorf("failed to decode managed fields: %v", err)
249-
}
250-
251-
object, managed, err = f.fieldManager.Apply(liveObj, appliedObj, managed, manager, force)
252-
if err != nil {
253-
if conflicts, ok := err.(merge.Conflicts); ok {
254-
return nil, internal.NewConflictError(conflicts)
255-
}
256-
return nil, err
257-
}
258-
259-
if err = internal.EncodeObjectManagedFields(object, managed); err != nil {
260-
return nil, fmt.Errorf("failed to encode managed fields: %v", err)
261-
}
262-
263-
return object, nil
54+
func ValidateManagedFields(encodedManagedFields []metav1.ManagedFieldsEntry) error {
55+
_, err := internal.DecodeManagedFields(encodedManagedFields)
56+
return err
26457
}

staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/fieldmanagertest/testfieldmanager.go

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"k8s.io/apimachinery/pkg/runtime"
2727
"k8s.io/apimachinery/pkg/runtime/schema"
2828
"k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager"
29+
"k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal"
2930
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
3031
"sigs.k8s.io/structured-merge-diff/v4/merge"
3132
"sigs.k8s.io/structured-merge-diff/v4/typed"
@@ -95,7 +96,7 @@ func (f *fakeObjectCreater) New(gvk schema.GroupVersionKind) (runtime.Object, er
9596
// to specify either a sub-resource, or a set of modified Manager to
9697
// test them specifically.
9798
type TestFieldManager struct {
98-
fieldManager *fieldmanager.FieldManager
99+
fieldManager *internal.FieldManager
99100
apiVersion string
100101
emptyObj runtime.Object
101102
liveObj runtime.Object
@@ -108,10 +109,10 @@ func NewDefaultTestFieldManager(typeConverter fieldmanager.TypeConverter, gvk sc
108109
}
109110

110111
// NewTestFieldManager creates a new manager for the given GVK.
111-
func NewTestFieldManager(typeConverter fieldmanager.TypeConverter, gvk schema.GroupVersionKind, subresource string, chainFieldManager func(fieldmanager.Manager) fieldmanager.Manager) TestFieldManager {
112+
func NewTestFieldManager(typeConverter fieldmanager.TypeConverter, gvk schema.GroupVersionKind, subresource string, chainFieldManager func(internal.Manager) internal.Manager) TestFieldManager {
112113
apiVersion := fieldpath.APIVersion(gvk.GroupVersion().String())
113114
objectConverter := &fakeObjectConvertor{sameVersionConverter{}, apiVersion}
114-
f, err := fieldmanager.NewStructuredMergeManager(
115+
f, err := internal.NewStructuredMergeManager(
115116
typeConverter,
116117
objectConverter,
117118
&fakeObjectDefaulter{},
@@ -125,22 +126,26 @@ func NewTestFieldManager(typeConverter fieldmanager.TypeConverter, gvk schema.Gr
125126
live := &unstructured.Unstructured{}
126127
live.SetKind(gvk.Kind)
127128
live.SetAPIVersion(gvk.GroupVersion().String())
128-
f = fieldmanager.NewLastAppliedUpdater(
129-
fieldmanager.NewLastAppliedManager(
130-
fieldmanager.NewProbabilisticSkipNonAppliedManager(
131-
fieldmanager.NewBuildManagerInfoManager(
132-
fieldmanager.NewManagedFieldsUpdater(
133-
fieldmanager.NewStripMetaManager(f),
129+
// This is different from `internal.NewDefaultFieldManager` because:
130+
// 1. We don't want to create a `internal.FieldManager`
131+
// 2. We don't want to use the CapManager that is tested separately with
132+
// a smaller than the default cap.
133+
f = internal.NewLastAppliedUpdater(
134+
internal.NewLastAppliedManager(
135+
internal.NewProbabilisticSkipNonAppliedManager(
136+
internal.NewBuildManagerInfoManager(
137+
internal.NewManagedFieldsUpdater(
138+
internal.NewStripMetaManager(f),
134139
), gvk.GroupVersion(), subresource,
135-
), NewFakeObjectCreater(), gvk, fieldmanager.DefaultTrackOnCreateProbability,
140+
), NewFakeObjectCreater(), gvk, internal.DefaultTrackOnCreateProbability,
136141
), typeConverter, objectConverter, gvk.GroupVersion(),
137142
),
138143
)
139144
if chainFieldManager != nil {
140145
f = chainFieldManager(f)
141146
}
142147
return TestFieldManager{
143-
fieldManager: fieldmanager.NewFieldManager(f, subresource),
148+
fieldManager: internal.NewFieldManager(f, subresource),
144149
apiVersion: gvk.GroupVersion().String(),
145150
emptyObj: live,
146151
liveObj: live.DeepCopyObject(),

staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/buildmanagerinfo.go renamed to staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/buildmanagerinfo.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,14 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
package fieldmanager
17+
package internal
1818

1919
import (
2020
"fmt"
2121

2222
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2323
"k8s.io/apimachinery/pkg/runtime"
2424
"k8s.io/apimachinery/pkg/runtime/schema"
25-
"k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal"
2625
)
2726

2827
type buildManagerInfoManager struct {
@@ -71,5 +70,5 @@ func (f *buildManagerInfoManager) buildManagerInfo(prefix string, operation meta
7170
if managerInfo.Manager == "" {
7271
managerInfo.Manager = "unknown"
7372
}
74-
return internal.BuildManagerIdentifier(&managerInfo)
73+
return BuildManagerIdentifier(&managerInfo)
7574
}

staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/capmanagers.go renamed to staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/capmanagers.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,14 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
package fieldmanager
17+
package internal
1818

1919
import (
2020
"fmt"
2121
"sort"
2222

2323
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2424
"k8s.io/apimachinery/pkg/runtime"
25-
"k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal"
2625
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
2726
)
2827

@@ -100,7 +99,7 @@ func (f *capManagersManager) capUpdateManagers(managed Managed) (newManaged Mana
10099

101100
// Create a new manager identifier for the versioned bucket entry.
102101
// The version for this manager comes from the version of the update being merged into the bucket.
103-
bucket, err := internal.BuildManagerIdentifier(&metav1.ManagedFieldsEntry{
102+
bucket, err := BuildManagerIdentifier(&metav1.ManagedFieldsEntry{
104103
Manager: f.oldUpdatesManagerName,
105104
Operation: metav1.ManagedFieldsOperationUpdate,
106105
APIVersion: version,

0 commit comments

Comments
 (0)