dont expose internal methods in implementatoin

Kubernetes-commit: fe2b5d00f2c1f16636638acd10b8d640e6de22c9
This commit is contained in:
Alexander Zielenski 2022-08-23 16:12:47 -07:00 committed by Kubernetes Publisher
parent c364b639fb
commit 90ef078c30

View File

@ -1,13 +1,12 @@
package csaupgrade package csaupgrade
import ( import (
"encoding/json" "bytes"
"fmt" "fmt"
"k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath" "sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
@ -20,8 +19,8 @@ import (
// subResource - Name of subresource used for api calls or empty string for main resource // subResource - Name of subresource used for api calls or empty string for main resource
func UpgradeManagedFields( func UpgradeManagedFields(
obj runtime.Object, obj runtime.Object,
csaManager string, csaManagerName string,
ssaManager string, ssaManagerName string,
subResource string, subResource string,
) (runtime.Object, error) { ) (runtime.Object, error) {
accessor, err := meta.Accessor(obj) accessor, err := meta.Accessor(obj)
@ -29,106 +28,130 @@ func UpgradeManagedFields(
return nil, fmt.Errorf("error accessing object metadata: %w", err) return nil, fmt.Errorf("error accessing object metadata: %w", err)
} }
managed, error := fieldmanager.DecodeManagedFields(accessor.GetManagedFields()) // Create managed fields clone since we modify the values
if error != nil { var managedFields []metav1.ManagedFieldsEntry
return nil, fmt.Errorf("failed to decode managed fields: %w", error) managedFields = append(managedFields, accessor.GetManagedFields()...)
}
// If SSA manager exists:
// find CSA manager of same version, union. discard the rest
// Else SSA manager does not exist:
// find most recent CSA manager. convert to Apply operation
ssaIdentifier, err := fieldmanager.BuildManagerIdentifier(&metav1.ManagedFieldsEntry{ // Locate SSA manager
Manager: ssaManager, ssaManagerIndex, ssaManagerExists := findFirstIndex(managedFields,
Operation: metav1.ManagedFieldsOperationApply, func(entry metav1.ManagedFieldsEntry) bool {
Subresource: subResource, return entry.Manager == ssaManagerName &&
}) entry.Operation == metav1.ManagedFieldsOperationApply &&
if err != nil { entry.Subresource == ""
return nil, fmt.Errorf("failed to build manager identifier for ssa manager") })
}
ssaMan, ssaExists := managed.Fields()[ssaIdentifier] if ssaManagerExists {
ssaManager := managedFields[ssaManagerIndex]
// Collect all relevant CSA managers before operating on them // find Update manager of same APIVersion, union ssa fields with it.
csaManagers := map[string]fieldpath.VersionedSet{} // discard all other Update managers of the same name
for name, entry := range managed.Fields() { csaManagerIndex, csaManagerExists := findFirstIndex(managedFields,
if entry.Applied() { func(entry metav1.ManagedFieldsEntry) bool {
// Not interested in SSA managed fields entries return entry.Manager == csaManagerName &&
continue entry.Operation == metav1.ManagedFieldsOperationUpdate &&
} entry.Subresource == "" &&
entry.APIVersion == ssaManager.APIVersion
})
// Manager string is a JSON representation of encoded entry if csaManagerExists {
// Pull manager name and subresource from it csaManager := managedFields[csaManagerIndex]
encodedVersionedSet := &metav1.ManagedFieldsEntry{}
err = json.Unmarshal([]byte(name), encodedVersionedSet)
if err != nil {
return nil, fmt.Errorf("error unmarshalling manager identifier %v: %v", name, err)
}
if encodedVersionedSet.Manager != csaManager || // Union the csa manager with the existing SSA manager
encodedVersionedSet.Subresource != subResource { ssaFieldSet, err := fieldsToSet(*ssaManager.FieldsV1)
continue if err != nil {
} return nil, fmt.Errorf("failed to convert fields to set: %w", err)
csaManagers[name] = entry
}
if len(csaManagers) == 0 {
return obj, nil
}
if ssaExists {
for name, entry := range csaManagers {
if entry.APIVersion() == ssaMan.APIVersion() {
// Merge entries if they are compatible versions
ssaMan = fieldpath.NewVersionedSet(
ssaMan.Set().Union(entry.Set()),
entry.APIVersion(),
true,
)
managed.Fields()[ssaIdentifier] = ssaMan
} }
// Discard entry in all cases: csaFieldSet, err := fieldsToSet(*csaManager.FieldsV1)
// if it has the wrong version we discard since managed fields versions if err != nil {
// cannot be converted return nil, fmt.Errorf("failed to convert fields to set: %w", err)
// if it has the correct version its fields were moved into the }
// ssaManager's fieldSet
delete(managed.Fields(), name) combinedFieldSet := ssaFieldSet.Union(&csaFieldSet)
combinedFieldSetEncoded, err := setToFields(*combinedFieldSet)
if err != nil {
return nil, fmt.Errorf("failed to encode field set: %w", err)
}
managedFields[ssaManagerIndex].FieldsV1 = &combinedFieldSetEncoded
} }
} else { } else {
// Loop through sorted CSA managers. Take the first one we care about // SSA manager does not exist. Find the most recent matching CSA manager,
firstName := "" // convert it to an SSA manager.
for _, entry := range accessor.GetManagedFields() { //
if entry.Manager == csaManager && // (find first index, since managed fields are sorted so that most recent is
entry.Subresource == subResource && // first in the list)
entry.Operation == metav1.ManagedFieldsOperationUpdate { csaManagerIndex, csaManagerExists := findFirstIndex(managedFields, func(entry metav1.ManagedFieldsEntry) bool {
return entry.Manager == csaManagerName && entry.Operation == metav1.ManagedFieldsOperationUpdate && entry.Subresource == ""
})
if len(firstName) == 0 { if !csaManagerExists {
ident, err := fieldmanager.BuildManagerIdentifier(&entry) // There are no CSA managers that need to be converted. Nothing to do
if err != nil { // Return early
return nil, fmt.Errorf("failed to build manager identifier: %w", err) return obj, nil
}
firstName = ident
break
}
}
} }
managed.Fields()[ssaIdentifier] = csaManagers[firstName] // Convert the entry to apply operation
managedFields[csaManagerIndex].Operation = metav1.ManagedFieldsOperationApply
for name := range csaManagers { managedFields[csaManagerIndex].Manager = ssaManagerName
delete(managed.Fields(), name)
}
} }
now := metav1.Now() // Create version of managed fields which has no CSA managers with the given name
managed.Times()[ssaIdentifier] = &now filteredManagers := filter(managedFields, func(entry metav1.ManagedFieldsEntry) bool {
return !(entry.Manager == csaManagerName &&
entry.Operation == metav1.ManagedFieldsOperationUpdate &&
entry.Subresource == "")
})
copied := obj.DeepCopyObject() copied := obj.DeepCopyObject()
if err := fieldmanager.EncodeObjectManagedFields(copied, managed); err != nil { copiedAccessor, err := meta.Accessor(copied)
return nil, err if err != nil {
return nil, fmt.Errorf("failed to get meta accessor for copied object: %w", err)
} }
copiedAccessor.SetManagedFields(filteredManagers)
return copied, nil return copied, nil
} }
func findFirstIndex[T any](
collection []T,
predicate func(T) bool,
) (int, bool) {
for idx, entry := range collection {
if predicate(entry) {
return idx, true
}
}
return -1, false
}
func filter[T any](
collection []T,
predicate func(T) bool,
) []T {
result := make([]T, 0, len(collection))
for _, value := range collection {
if predicate(value) {
result = append(result, value)
}
}
if len(result) == 0 {
return nil
}
return result
}
// FieldsToSet creates a set paths from an input trie of fields
func fieldsToSet(f metav1.FieldsV1) (s fieldpath.Set, err error) {
err = s.FromJSON(bytes.NewReader(f.Raw))
return s, err
}
// SetToFields creates a trie of fields from an input set of paths
func setToFields(s fieldpath.Set) (f metav1.FieldsV1, err error) {
f.Raw, err = s.ToJSON()
return f, err
}