mirror of
https://github.com/kubernetes/client-go.git
synced 2025-06-27 15:39:39 +00:00
dont expose internal methods in implementatoin
Kubernetes-commit: fe2b5d00f2c1f16636638acd10b8d640e6de22c9
This commit is contained in:
parent
c364b639fb
commit
90ef078c30
@ -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
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user