Track ownership of deployments scale subresource
Kubernetes-commit: a9ea98b3b9272a7f7788a0d37891e4b13b9be38d
This commit is contained in:
		
							parent
							
								
									c883d6c994
								
							
						
					
					
						commit
						8df8282eaf
					
				| 
						 | 
				
			
			@ -0,0 +1,160 @@
 | 
			
		|||
/*
 | 
			
		||||
Copyright 2021 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 (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"k8s.io/apimachinery/pkg/api/meta"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime/schema"
 | 
			
		||||
	"k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal"
 | 
			
		||||
	"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	scaleGroupVersion   = schema.GroupVersion{Group: "autoscaling", Version: "v1"}
 | 
			
		||||
	replicasPathInScale = fieldpath.MakePathOrDie("spec", "replicas")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ResourcePathMappings maps a group/version to its replicas path. The
 | 
			
		||||
// assumption is that all the paths correspond to leaf fields.
 | 
			
		||||
type ResourcePathMappings map[string]fieldpath.Path
 | 
			
		||||
 | 
			
		||||
// ScaleHandler manages the conversion of managed fields between a main
 | 
			
		||||
// resource and the scale subresource
 | 
			
		||||
type ScaleHandler struct {
 | 
			
		||||
	parentEntries       []metav1.ManagedFieldsEntry
 | 
			
		||||
	defaultGroupVersion schema.GroupVersion
 | 
			
		||||
	mappings            ResourcePathMappings
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewScaleHandler creates a new ScaleHandler
 | 
			
		||||
func NewScaleHandler(parentEntries []metav1.ManagedFieldsEntry, defaultGroupVersion schema.GroupVersion, mappings ResourcePathMappings) *ScaleHandler {
 | 
			
		||||
	return &ScaleHandler{
 | 
			
		||||
		parentEntries:       parentEntries,
 | 
			
		||||
		defaultGroupVersion: defaultGroupVersion,
 | 
			
		||||
		mappings:            mappings,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ToSubresource filter the managed fields of the main resource and convert
 | 
			
		||||
// them so that they can be handled by scale.
 | 
			
		||||
// For the managed fields that have a replicas path it performs two changes:
 | 
			
		||||
// 1. APIVersion is changed to the APIVersion of the scale subresource
 | 
			
		||||
// 2. Replicas path of the main resource is transformed to the replicas path of
 | 
			
		||||
//    the scale subresource
 | 
			
		||||
func (h *ScaleHandler) ToSubresource() ([]metav1.ManagedFieldsEntry, error) {
 | 
			
		||||
	managed, err := DecodeManagedFields(h.parentEntries)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	f := fieldpath.ManagedFields{}
 | 
			
		||||
	t := map[string]*metav1.Time{}
 | 
			
		||||
	for manager, versionedSet := range managed.Fields() {
 | 
			
		||||
		path := h.mappings[string(versionedSet.APIVersion())]
 | 
			
		||||
		if versionedSet.Set().Has(path) {
 | 
			
		||||
			newVersionedSet := fieldpath.NewVersionedSet(
 | 
			
		||||
				fieldpath.NewSet(replicasPathInScale),
 | 
			
		||||
				fieldpath.APIVersion(scaleGroupVersion.String()),
 | 
			
		||||
				versionedSet.Applied(),
 | 
			
		||||
			)
 | 
			
		||||
 | 
			
		||||
			f[manager] = newVersionedSet
 | 
			
		||||
			t[manager] = managed.Times()[manager]
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return managedFieldsEntries(internal.NewManaged(f, t))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ToParent merges `scaleEntries` with the entries of the main resource and
 | 
			
		||||
// transforms them accordingly
 | 
			
		||||
func (h *ScaleHandler) ToParent(scaleEntries []metav1.ManagedFieldsEntry) ([]metav1.ManagedFieldsEntry, error) {
 | 
			
		||||
	decodedParentEntries, err := DecodeManagedFields(h.parentEntries)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	parentFields := decodedParentEntries.Fields()
 | 
			
		||||
 | 
			
		||||
	decodedScaleEntries, err := DecodeManagedFields(scaleEntries)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	scaleFields := decodedScaleEntries.Fields()
 | 
			
		||||
 | 
			
		||||
	f := fieldpath.ManagedFields{}
 | 
			
		||||
	t := map[string]*metav1.Time{}
 | 
			
		||||
 | 
			
		||||
	for manager, versionedSet := range parentFields {
 | 
			
		||||
		// Get the main resource "replicas" path
 | 
			
		||||
		path := h.mappings[string(versionedSet.APIVersion())]
 | 
			
		||||
 | 
			
		||||
		// If the parent entry does not have the replicas path, just keep it as it is
 | 
			
		||||
		if !versionedSet.Set().Has(path) {
 | 
			
		||||
			f[manager] = versionedSet
 | 
			
		||||
			t[manager] = decodedParentEntries.Times()[manager]
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if _, ok := scaleFields[manager]; !ok {
 | 
			
		||||
			// "Steal" the replicas path from the main resource entry
 | 
			
		||||
			newSet := versionedSet.Set().Difference(fieldpath.NewSet(path))
 | 
			
		||||
 | 
			
		||||
			if !newSet.Empty() {
 | 
			
		||||
				newVersionedSet := fieldpath.NewVersionedSet(
 | 
			
		||||
					newSet,
 | 
			
		||||
					versionedSet.APIVersion(),
 | 
			
		||||
					versionedSet.Applied(),
 | 
			
		||||
				)
 | 
			
		||||
				f[manager] = newVersionedSet
 | 
			
		||||
				t[manager] = decodedParentEntries.Times()[manager]
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			// Field wasn't stolen, let's keep the entry as it is.
 | 
			
		||||
			f[manager] = versionedSet
 | 
			
		||||
			t[manager] = decodedParentEntries.Times()[manager]
 | 
			
		||||
			delete(scaleFields, manager)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for manager, versionedSet := range scaleFields {
 | 
			
		||||
		newVersionedSet := fieldpath.NewVersionedSet(
 | 
			
		||||
			fieldpath.NewSet(h.mappings[h.defaultGroupVersion.String()]),
 | 
			
		||||
			fieldpath.APIVersion(h.defaultGroupVersion.String()),
 | 
			
		||||
			versionedSet.Applied(),
 | 
			
		||||
		)
 | 
			
		||||
		f[manager] = newVersionedSet
 | 
			
		||||
		t[manager] = decodedParentEntries.Times()[manager]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return managedFieldsEntries(internal.NewManaged(f, t))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func managedFieldsEntries(entries internal.ManagedInterface) ([]metav1.ManagedFieldsEntry, error) {
 | 
			
		||||
	obj := &unstructured.Unstructured{Object: map[string]interface{}{}}
 | 
			
		||||
	if err := internal.EncodeObjectManagedFields(obj, entries); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	accessor, err := meta.Accessor(obj)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(fmt.Sprintf("couldn't get accessor: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
	return accessor.GetManagedFields(), nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,684 @@
 | 
			
		|||
/*
 | 
			
		||||
Copyright 2021 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 (
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime/schema"
 | 
			
		||||
	"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestTransformManagedFieldsToSubresource(t *testing.T) {
 | 
			
		||||
	testTime, _ := time.ParseInLocation("2006-Jan-02", "2013-Feb-03", time.Local)
 | 
			
		||||
	managedFieldTime := v1.NewTime(testTime)
 | 
			
		||||
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		desc     string
 | 
			
		||||
		input    []metav1.ManagedFieldsEntry
 | 
			
		||||
		expected []metav1.ManagedFieldsEntry
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			desc: "filter one entry and transform it into a subresource entry",
 | 
			
		||||
			input: []metav1.ManagedFieldsEntry{
 | 
			
		||||
				{
 | 
			
		||||
					Manager:    "manager-1",
 | 
			
		||||
					Operation:  metav1.ManagedFieldsOperationApply,
 | 
			
		||||
					APIVersion: "apps/v1",
 | 
			
		||||
					FieldsType: "FieldsV1",
 | 
			
		||||
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:another-field":{}}}`)},
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Manager:    "manager-2",
 | 
			
		||||
					Operation:  metav1.ManagedFieldsOperationApply,
 | 
			
		||||
					APIVersion: "apps/v1",
 | 
			
		||||
					FieldsType: "FieldsV1",
 | 
			
		||||
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
 | 
			
		||||
					Time:       &managedFieldTime,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expected: []metav1.ManagedFieldsEntry{
 | 
			
		||||
				{
 | 
			
		||||
					Manager:    "manager-2",
 | 
			
		||||
					Operation:  metav1.ManagedFieldsOperationApply,
 | 
			
		||||
					APIVersion: "autoscaling/v1",
 | 
			
		||||
					FieldsType: "FieldsV1",
 | 
			
		||||
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
 | 
			
		||||
					Time:       &managedFieldTime,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			desc: "transform all entries",
 | 
			
		||||
			input: []metav1.ManagedFieldsEntry{
 | 
			
		||||
				{
 | 
			
		||||
					Manager:    "manager-1",
 | 
			
		||||
					Operation:  metav1.ManagedFieldsOperationApply,
 | 
			
		||||
					APIVersion: "apps/v1",
 | 
			
		||||
					FieldsType: "FieldsV1",
 | 
			
		||||
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Manager:    "manager-2",
 | 
			
		||||
					Operation:  metav1.ManagedFieldsOperationApply,
 | 
			
		||||
					APIVersion: "apps/v1",
 | 
			
		||||
					FieldsType: "FieldsV1",
 | 
			
		||||
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Manager:     "manager-3",
 | 
			
		||||
					Operation:   metav1.ManagedFieldsOperationApply,
 | 
			
		||||
					APIVersion:  "apps/v1",
 | 
			
		||||
					FieldsType:  "FieldsV1",
 | 
			
		||||
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
 | 
			
		||||
					Subresource: "scale",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expected: []metav1.ManagedFieldsEntry{
 | 
			
		||||
				{
 | 
			
		||||
					Manager:    "manager-1",
 | 
			
		||||
					Operation:  metav1.ManagedFieldsOperationApply,
 | 
			
		||||
					APIVersion: "autoscaling/v1",
 | 
			
		||||
					FieldsType: "FieldsV1",
 | 
			
		||||
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Manager:    "manager-2",
 | 
			
		||||
					Operation:  metav1.ManagedFieldsOperationApply,
 | 
			
		||||
					APIVersion: "autoscaling/v1",
 | 
			
		||||
					FieldsType: "FieldsV1",
 | 
			
		||||
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Manager:     "manager-3",
 | 
			
		||||
					Operation:   metav1.ManagedFieldsOperationApply,
 | 
			
		||||
					APIVersion:  "autoscaling/v1",
 | 
			
		||||
					FieldsType:  "FieldsV1",
 | 
			
		||||
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
 | 
			
		||||
					Subresource: "scale",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, test := range tests {
 | 
			
		||||
		handler := NewScaleHandler(
 | 
			
		||||
			test.input,
 | 
			
		||||
			schema.GroupVersion{Group: "apps", Version: "v1"},
 | 
			
		||||
			defaultMappings(),
 | 
			
		||||
		)
 | 
			
		||||
		subresourceEntries, err := handler.ToSubresource()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Fatalf("test %q - expected no error but got %v", test.desc, err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if !reflect.DeepEqual(subresourceEntries, test.expected) {
 | 
			
		||||
			t.Fatalf("test %q - expected output to be:\n%v\n\nbut got:\n%v", test.desc, test.expected, subresourceEntries)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestTransformingManagedFieldsToParent(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		desc        string
 | 
			
		||||
		parent      []metav1.ManagedFieldsEntry
 | 
			
		||||
		subresource []metav1.ManagedFieldsEntry
 | 
			
		||||
		expected    []metav1.ManagedFieldsEntry
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			desc: "different-managers: apply -> update",
 | 
			
		||||
			parent: []metav1.ManagedFieldsEntry{
 | 
			
		||||
				{
 | 
			
		||||
					Manager:    "test",
 | 
			
		||||
					Operation:  metav1.ManagedFieldsOperationApply,
 | 
			
		||||
					APIVersion: "apps/v1",
 | 
			
		||||
					FieldsType: "FieldsV1",
 | 
			
		||||
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{},"f:selector":{}}}`)},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			subresource: []metav1.ManagedFieldsEntry{
 | 
			
		||||
				{
 | 
			
		||||
					Manager:     "scale",
 | 
			
		||||
					Operation:   metav1.ManagedFieldsOperationUpdate,
 | 
			
		||||
					APIVersion:  "autoscaling/v1",
 | 
			
		||||
					FieldsType:  "FieldsV1",
 | 
			
		||||
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
 | 
			
		||||
					Subresource: "scale",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expected: []metav1.ManagedFieldsEntry{
 | 
			
		||||
				{
 | 
			
		||||
					Manager:    "test",
 | 
			
		||||
					Operation:  metav1.ManagedFieldsOperationApply,
 | 
			
		||||
					APIVersion: "apps/v1",
 | 
			
		||||
					FieldsType: "FieldsV1",
 | 
			
		||||
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:selector":{}}}`)},
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Manager:     "scale",
 | 
			
		||||
					Operation:   metav1.ManagedFieldsOperationUpdate,
 | 
			
		||||
					APIVersion:  "apps/v1",
 | 
			
		||||
					FieldsType:  "FieldsV1",
 | 
			
		||||
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
 | 
			
		||||
					Subresource: "scale",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			desc: "different-managers: apply -> apply",
 | 
			
		||||
			parent: []metav1.ManagedFieldsEntry{
 | 
			
		||||
				{
 | 
			
		||||
					Manager:    "test",
 | 
			
		||||
					Operation:  metav1.ManagedFieldsOperationApply,
 | 
			
		||||
					APIVersion: "apps/v1",
 | 
			
		||||
					FieldsType: "FieldsV1",
 | 
			
		||||
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{},"f:selector":{}}}`)},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			subresource: []metav1.ManagedFieldsEntry{
 | 
			
		||||
				{
 | 
			
		||||
					Manager:     "scale",
 | 
			
		||||
					Operation:   metav1.ManagedFieldsOperationApply,
 | 
			
		||||
					APIVersion:  "autoscaling/v1",
 | 
			
		||||
					FieldsType:  "FieldsV1",
 | 
			
		||||
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
 | 
			
		||||
					Subresource: "scale",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expected: []metav1.ManagedFieldsEntry{
 | 
			
		||||
				{
 | 
			
		||||
					Manager:     "scale",
 | 
			
		||||
					Operation:   metav1.ManagedFieldsOperationApply,
 | 
			
		||||
					APIVersion:  "apps/v1",
 | 
			
		||||
					FieldsType:  "FieldsV1",
 | 
			
		||||
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
 | 
			
		||||
					Subresource: "scale",
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Manager:    "test",
 | 
			
		||||
					Operation:  metav1.ManagedFieldsOperationApply,
 | 
			
		||||
					APIVersion: "apps/v1",
 | 
			
		||||
					FieldsType: "FieldsV1",
 | 
			
		||||
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:selector":{}}}`)},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			desc: "different-managers: update -> update",
 | 
			
		||||
			parent: []metav1.ManagedFieldsEntry{
 | 
			
		||||
				{
 | 
			
		||||
					Manager:    "test",
 | 
			
		||||
					Operation:  metav1.ManagedFieldsOperationUpdate,
 | 
			
		||||
					APIVersion: "apps/v1",
 | 
			
		||||
					FieldsType: "FieldsV1",
 | 
			
		||||
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{},"f:selector":{}}}`)},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			subresource: []metav1.ManagedFieldsEntry{
 | 
			
		||||
				{
 | 
			
		||||
					Manager:     "scale",
 | 
			
		||||
					Operation:   metav1.ManagedFieldsOperationUpdate,
 | 
			
		||||
					APIVersion:  "autoscaling/v1",
 | 
			
		||||
					FieldsType:  "FieldsV1",
 | 
			
		||||
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
 | 
			
		||||
					Subresource: "scale",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expected: []metav1.ManagedFieldsEntry{
 | 
			
		||||
				{
 | 
			
		||||
					Manager:     "scale",
 | 
			
		||||
					Operation:   metav1.ManagedFieldsOperationUpdate,
 | 
			
		||||
					APIVersion:  "apps/v1",
 | 
			
		||||
					FieldsType:  "FieldsV1",
 | 
			
		||||
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
 | 
			
		||||
					Subresource: "scale",
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Manager:    "test",
 | 
			
		||||
					Operation:  metav1.ManagedFieldsOperationUpdate,
 | 
			
		||||
					APIVersion: "apps/v1",
 | 
			
		||||
					FieldsType: "FieldsV1",
 | 
			
		||||
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:selector":{}}}`)},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			desc: "different-managers: update -> apply",
 | 
			
		||||
			parent: []metav1.ManagedFieldsEntry{
 | 
			
		||||
				{
 | 
			
		||||
					Manager:    "test",
 | 
			
		||||
					Operation:  metav1.ManagedFieldsOperationUpdate,
 | 
			
		||||
					APIVersion: "apps/v1",
 | 
			
		||||
					FieldsType: "FieldsV1",
 | 
			
		||||
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{},"f:selector":{}}}`)},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			subresource: []metav1.ManagedFieldsEntry{
 | 
			
		||||
				{
 | 
			
		||||
					Manager:     "scale",
 | 
			
		||||
					Operation:   metav1.ManagedFieldsOperationApply,
 | 
			
		||||
					APIVersion:  "autoscaling/v1",
 | 
			
		||||
					FieldsType:  "FieldsV1",
 | 
			
		||||
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
 | 
			
		||||
					Subresource: "scale",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expected: []metav1.ManagedFieldsEntry{
 | 
			
		||||
				{
 | 
			
		||||
					Manager:     "scale",
 | 
			
		||||
					Operation:   metav1.ManagedFieldsOperationApply,
 | 
			
		||||
					APIVersion:  "apps/v1",
 | 
			
		||||
					FieldsType:  "FieldsV1",
 | 
			
		||||
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
 | 
			
		||||
					Subresource: "scale",
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Manager:    "test",
 | 
			
		||||
					Operation:  metav1.ManagedFieldsOperationUpdate,
 | 
			
		||||
					APIVersion: "apps/v1",
 | 
			
		||||
					FieldsType: "FieldsV1",
 | 
			
		||||
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:selector":{}}}`)},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			desc: "same manager: apply -> apply",
 | 
			
		||||
			parent: []metav1.ManagedFieldsEntry{
 | 
			
		||||
				{
 | 
			
		||||
					Manager:    "test",
 | 
			
		||||
					Operation:  metav1.ManagedFieldsOperationApply,
 | 
			
		||||
					APIVersion: "apps/v1",
 | 
			
		||||
					FieldsType: "FieldsV1",
 | 
			
		||||
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{},"f:selector":{}}}`)},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			subresource: []metav1.ManagedFieldsEntry{
 | 
			
		||||
				{
 | 
			
		||||
					Manager:     "test",
 | 
			
		||||
					Operation:   metav1.ManagedFieldsOperationApply,
 | 
			
		||||
					APIVersion:  "autoscaling/v1",
 | 
			
		||||
					FieldsType:  "FieldsV1",
 | 
			
		||||
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
 | 
			
		||||
					Subresource: "scale",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expected: []metav1.ManagedFieldsEntry{
 | 
			
		||||
				{
 | 
			
		||||
					Manager:    "test",
 | 
			
		||||
					Operation:  metav1.ManagedFieldsOperationApply,
 | 
			
		||||
					APIVersion: "apps/v1",
 | 
			
		||||
					FieldsType: "FieldsV1",
 | 
			
		||||
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:selector":{}}}`)},
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Manager:     "test",
 | 
			
		||||
					Operation:   metav1.ManagedFieldsOperationApply,
 | 
			
		||||
					APIVersion:  "apps/v1",
 | 
			
		||||
					FieldsType:  "FieldsV1",
 | 
			
		||||
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
 | 
			
		||||
					Subresource: "scale",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			desc: "same manager: update -> update",
 | 
			
		||||
			parent: []metav1.ManagedFieldsEntry{
 | 
			
		||||
				{
 | 
			
		||||
					Manager:    "test",
 | 
			
		||||
					Operation:  metav1.ManagedFieldsOperationUpdate,
 | 
			
		||||
					APIVersion: "apps/v1",
 | 
			
		||||
					FieldsType: "FieldsV1",
 | 
			
		||||
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{},"f:selector":{}}}`)},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			subresource: []metav1.ManagedFieldsEntry{
 | 
			
		||||
				{
 | 
			
		||||
					Manager:     "test",
 | 
			
		||||
					Operation:   metav1.ManagedFieldsOperationUpdate,
 | 
			
		||||
					APIVersion:  "autoscaling/v1",
 | 
			
		||||
					FieldsType:  "FieldsV1",
 | 
			
		||||
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
 | 
			
		||||
					Subresource: "scale",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expected: []metav1.ManagedFieldsEntry{
 | 
			
		||||
				{
 | 
			
		||||
					Manager:    "test",
 | 
			
		||||
					Operation:  metav1.ManagedFieldsOperationUpdate,
 | 
			
		||||
					APIVersion: "apps/v1",
 | 
			
		||||
					FieldsType: "FieldsV1",
 | 
			
		||||
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:selector":{}}}`)},
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Manager:     "test",
 | 
			
		||||
					Operation:   metav1.ManagedFieldsOperationUpdate,
 | 
			
		||||
					APIVersion:  "apps/v1",
 | 
			
		||||
					FieldsType:  "FieldsV1",
 | 
			
		||||
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
 | 
			
		||||
					Subresource: "scale",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			desc: "same manager: update -> apply",
 | 
			
		||||
			parent: []metav1.ManagedFieldsEntry{
 | 
			
		||||
				{
 | 
			
		||||
					Manager:    "test",
 | 
			
		||||
					Operation:  metav1.ManagedFieldsOperationUpdate,
 | 
			
		||||
					APIVersion: "apps/v1",
 | 
			
		||||
					FieldsType: "FieldsV1",
 | 
			
		||||
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{},"f:selector":{}}}`)},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			subresource: []metav1.ManagedFieldsEntry{
 | 
			
		||||
				{
 | 
			
		||||
					Manager:     "test",
 | 
			
		||||
					Operation:   metav1.ManagedFieldsOperationApply,
 | 
			
		||||
					APIVersion:  "autoscaling/v1",
 | 
			
		||||
					FieldsType:  "FieldsV1",
 | 
			
		||||
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
 | 
			
		||||
					Subresource: "scale",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expected: []metav1.ManagedFieldsEntry{
 | 
			
		||||
				{
 | 
			
		||||
					Manager:     "test",
 | 
			
		||||
					Operation:   metav1.ManagedFieldsOperationApply,
 | 
			
		||||
					APIVersion:  "apps/v1",
 | 
			
		||||
					FieldsType:  "FieldsV1",
 | 
			
		||||
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
 | 
			
		||||
					Subresource: "scale",
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Manager:    "test",
 | 
			
		||||
					Operation:  metav1.ManagedFieldsOperationUpdate,
 | 
			
		||||
					APIVersion: "apps/v1",
 | 
			
		||||
					FieldsType: "FieldsV1",
 | 
			
		||||
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:selector":{}}}`)},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			desc: "same manager: apply -> update",
 | 
			
		||||
			parent: []metav1.ManagedFieldsEntry{
 | 
			
		||||
				{
 | 
			
		||||
					Manager:    "test",
 | 
			
		||||
					Operation:  metav1.ManagedFieldsOperationApply,
 | 
			
		||||
					APIVersion: "apps/v1",
 | 
			
		||||
					FieldsType: "FieldsV1",
 | 
			
		||||
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{},"f:selector":{}}}`)},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			subresource: []metav1.ManagedFieldsEntry{
 | 
			
		||||
				{
 | 
			
		||||
					Manager:     "test",
 | 
			
		||||
					Operation:   metav1.ManagedFieldsOperationUpdate,
 | 
			
		||||
					APIVersion:  "autoscaling/v1",
 | 
			
		||||
					FieldsType:  "FieldsV1",
 | 
			
		||||
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
 | 
			
		||||
					Subresource: "scale",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expected: []metav1.ManagedFieldsEntry{
 | 
			
		||||
				{
 | 
			
		||||
					Manager:    "test",
 | 
			
		||||
					Operation:  metav1.ManagedFieldsOperationApply,
 | 
			
		||||
					APIVersion: "apps/v1",
 | 
			
		||||
					FieldsType: "FieldsV1",
 | 
			
		||||
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:selector":{}}}`)},
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Manager:     "test",
 | 
			
		||||
					Operation:   metav1.ManagedFieldsOperationUpdate,
 | 
			
		||||
					APIVersion:  "apps/v1",
 | 
			
		||||
					FieldsType:  "FieldsV1",
 | 
			
		||||
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
 | 
			
		||||
					Subresource: "scale",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			desc: "subresource doesn't own the path anymore",
 | 
			
		||||
			parent: []metav1.ManagedFieldsEntry{
 | 
			
		||||
				{
 | 
			
		||||
					Manager:    "test",
 | 
			
		||||
					Operation:  metav1.ManagedFieldsOperationApply,
 | 
			
		||||
					APIVersion: "apps/v1",
 | 
			
		||||
					FieldsType: "FieldsV1",
 | 
			
		||||
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{},"f:selector":{}}}`)},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			subresource: []metav1.ManagedFieldsEntry{
 | 
			
		||||
				{
 | 
			
		||||
					Manager:     "scale",
 | 
			
		||||
					Operation:   metav1.ManagedFieldsOperationUpdate,
 | 
			
		||||
					APIVersion:  "autoscaling/v1",
 | 
			
		||||
					FieldsType:  "FieldsV1",
 | 
			
		||||
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:another":{}}}`)},
 | 
			
		||||
					Subresource: "scale",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expected: []metav1.ManagedFieldsEntry{
 | 
			
		||||
				{
 | 
			
		||||
					Manager:    "test",
 | 
			
		||||
					Operation:  metav1.ManagedFieldsOperationApply,
 | 
			
		||||
					APIVersion: "apps/v1",
 | 
			
		||||
					FieldsType: "FieldsV1",
 | 
			
		||||
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:selector":{}}}`)},
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Manager:     "scale",
 | 
			
		||||
					Operation:   metav1.ManagedFieldsOperationUpdate,
 | 
			
		||||
					APIVersion:  "apps/v1",
 | 
			
		||||
					FieldsType:  "FieldsV1",
 | 
			
		||||
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
 | 
			
		||||
					Subresource: "scale",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			desc: "Subresource steals all the fields of the parent resource",
 | 
			
		||||
			parent: []metav1.ManagedFieldsEntry{
 | 
			
		||||
				{
 | 
			
		||||
					Manager:    "test",
 | 
			
		||||
					Operation:  metav1.ManagedFieldsOperationApply,
 | 
			
		||||
					APIVersion: "apps/v1",
 | 
			
		||||
					FieldsType: "FieldsV1",
 | 
			
		||||
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			subresource: []metav1.ManagedFieldsEntry{
 | 
			
		||||
				{
 | 
			
		||||
					Manager:     "scale",
 | 
			
		||||
					Operation:   metav1.ManagedFieldsOperationUpdate,
 | 
			
		||||
					APIVersion:  "autoscaling/v1",
 | 
			
		||||
					FieldsType:  "FieldsV1",
 | 
			
		||||
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
 | 
			
		||||
					Subresource: "scale",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expected: []metav1.ManagedFieldsEntry{
 | 
			
		||||
				{
 | 
			
		||||
					Manager:     "scale",
 | 
			
		||||
					Operation:   metav1.ManagedFieldsOperationUpdate,
 | 
			
		||||
					APIVersion:  "apps/v1",
 | 
			
		||||
					FieldsType:  "FieldsV1",
 | 
			
		||||
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
 | 
			
		||||
					Subresource: "scale",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			desc: "apply without stealing",
 | 
			
		||||
			parent: []metav1.ManagedFieldsEntry{
 | 
			
		||||
				{
 | 
			
		||||
					Manager:    "test",
 | 
			
		||||
					Operation:  metav1.ManagedFieldsOperationApply,
 | 
			
		||||
					APIVersion: "apps/v1",
 | 
			
		||||
					FieldsType: "FieldsV1",
 | 
			
		||||
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{},"f:selector":{}}}`)},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			subresource: []metav1.ManagedFieldsEntry{
 | 
			
		||||
				{
 | 
			
		||||
					Manager:    "test",
 | 
			
		||||
					Operation:  metav1.ManagedFieldsOperationApply,
 | 
			
		||||
					APIVersion: "autoscaling/v1",
 | 
			
		||||
					FieldsType: "FieldsV1",
 | 
			
		||||
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Manager:     "test",
 | 
			
		||||
					Operation:   metav1.ManagedFieldsOperationApply,
 | 
			
		||||
					APIVersion:  "autoscaling/v1",
 | 
			
		||||
					FieldsType:  "FieldsV1",
 | 
			
		||||
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
 | 
			
		||||
					Subresource: "scale",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expected: []metav1.ManagedFieldsEntry{
 | 
			
		||||
				{
 | 
			
		||||
					Manager:    "test",
 | 
			
		||||
					Operation:  metav1.ManagedFieldsOperationApply,
 | 
			
		||||
					APIVersion: "apps/v1",
 | 
			
		||||
					FieldsType: "FieldsV1",
 | 
			
		||||
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{},"f:selector":{}}}`)},
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Manager:     "test",
 | 
			
		||||
					Operation:   metav1.ManagedFieldsOperationApply,
 | 
			
		||||
					APIVersion:  "apps/v1",
 | 
			
		||||
					FieldsType:  "FieldsV1",
 | 
			
		||||
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
 | 
			
		||||
					Subresource: "scale",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, test := range tests {
 | 
			
		||||
		t.Run(test.desc, func(t *testing.T) {
 | 
			
		||||
			handler := NewScaleHandler(
 | 
			
		||||
				test.parent,
 | 
			
		||||
				schema.GroupVersion{Group: "apps", Version: "v1"},
 | 
			
		||||
				defaultMappings(),
 | 
			
		||||
			)
 | 
			
		||||
			parentEntries, err := handler.ToParent(test.subresource)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				t.Fatalf("test: %q - expected no error but got %v", test.desc, err)
 | 
			
		||||
			}
 | 
			
		||||
			if !reflect.DeepEqual(parentEntries, test.expected) {
 | 
			
		||||
				t.Fatalf("test: %q - expected output to be:\n%v\n\nbut got:\n%v", test.desc, test.expected, parentEntries)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestTransformingManagedFieldsToParentMultiVersion(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		desc        string
 | 
			
		||||
		mappings    ResourcePathMappings
 | 
			
		||||
		parent      []metav1.ManagedFieldsEntry
 | 
			
		||||
		subresource []metav1.ManagedFieldsEntry
 | 
			
		||||
		expected    []metav1.ManagedFieldsEntry
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			desc: "multi-version",
 | 
			
		||||
			mappings: ResourcePathMappings{
 | 
			
		||||
				"apps/v1": fieldpath.MakePathOrDie("spec", "the-replicas"),
 | 
			
		||||
				"apps/v2": fieldpath.MakePathOrDie("spec", "not-the-replicas"),
 | 
			
		||||
			},
 | 
			
		||||
			parent: []metav1.ManagedFieldsEntry{
 | 
			
		||||
				{
 | 
			
		||||
					Manager:    "test",
 | 
			
		||||
					Operation:  metav1.ManagedFieldsOperationApply,
 | 
			
		||||
					APIVersion: "apps/v1",
 | 
			
		||||
					FieldsType: "FieldsV1",
 | 
			
		||||
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:the-replicas":{},"f:selector":{}}}`)},
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Manager:    "test-other",
 | 
			
		||||
					Operation:  metav1.ManagedFieldsOperationApply,
 | 
			
		||||
					APIVersion: "apps/v2",
 | 
			
		||||
					FieldsType: "FieldsV1",
 | 
			
		||||
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:not-the-replicas":{},"f:selector":{}}}`)},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			subresource: []metav1.ManagedFieldsEntry{
 | 
			
		||||
				{
 | 
			
		||||
					Manager:     "scale",
 | 
			
		||||
					Operation:   metav1.ManagedFieldsOperationUpdate,
 | 
			
		||||
					APIVersion:  "autoscaling/v1",
 | 
			
		||||
					FieldsType:  "FieldsV1",
 | 
			
		||||
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
 | 
			
		||||
					Subresource: "scale",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			expected: []metav1.ManagedFieldsEntry{
 | 
			
		||||
				{
 | 
			
		||||
					Manager:    "test",
 | 
			
		||||
					Operation:  metav1.ManagedFieldsOperationApply,
 | 
			
		||||
					APIVersion: "apps/v1",
 | 
			
		||||
					FieldsType: "FieldsV1",
 | 
			
		||||
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:selector":{}}}`)},
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Manager:    "test-other",
 | 
			
		||||
					Operation:  metav1.ManagedFieldsOperationApply,
 | 
			
		||||
					APIVersion: "apps/v2",
 | 
			
		||||
					FieldsType: "FieldsV1",
 | 
			
		||||
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:selector":{}}}`)},
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Manager:     "scale",
 | 
			
		||||
					Operation:   metav1.ManagedFieldsOperationUpdate,
 | 
			
		||||
					APIVersion:  "apps/v1",
 | 
			
		||||
					FieldsType:  "FieldsV1",
 | 
			
		||||
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:the-replicas":{}}}`)},
 | 
			
		||||
					Subresource: "scale",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, test := range tests {
 | 
			
		||||
		t.Run(test.desc, func(t *testing.T) {
 | 
			
		||||
			handler := NewScaleHandler(
 | 
			
		||||
				test.parent,
 | 
			
		||||
				schema.GroupVersion{Group: "apps", Version: "v1"},
 | 
			
		||||
				test.mappings,
 | 
			
		||||
			)
 | 
			
		||||
			parentEntries, err := handler.ToParent(test.subresource)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				t.Fatalf("test: %q - expected no error but got %v", test.desc, err)
 | 
			
		||||
			}
 | 
			
		||||
			if !reflect.DeepEqual(parentEntries, test.expected) {
 | 
			
		||||
				t.Fatalf("test: %q - expected output to be:\n%v\n\nbut got:\n%v", test.desc, test.expected, parentEntries)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func defaultMappings() ResourcePathMappings {
 | 
			
		||||
	return ResourcePathMappings{
 | 
			
		||||
		"apps/v1": fieldpath.MakePathOrDie("spec", "replicas"),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue