github.com/spotmaxtech/k8s-apimachinery-v0260@v0.0.1/pkg/util/managedfields/extract_test.go (about)

     1  /*
     2  Copyright 2021 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package managedfields
    18  
    19  import (
    20  	"testing"
    21  
    22  	"github.com/google/go-cmp/cmp"
    23  	"sigs.k8s.io/structured-merge-diff/v4/typed"
    24  
    25  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/api/equality"
    26  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/api/meta"
    27  	metav1 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/apis/meta/v1"
    28  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/apis/meta/v1/unstructured"
    29  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/runtime"
    30  	runtimeschema "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/runtime/schema"
    31  )
    32  
    33  func TestExtractInto(t *testing.T) {
    34  	one := int32(1)
    35  	parser, err := typed.NewParser(schemaYAML)
    36  	if err != nil {
    37  		t.Fatalf("Failed to parse schema: %v", err)
    38  	}
    39  	cases := []struct {
    40  		name          string
    41  		obj           runtime.Object
    42  		objType       typed.ParseableType
    43  		managedFields []metav1.ManagedFieldsEntry // written to object before test is run
    44  		fieldManager  string
    45  		expectedOut   interface{}
    46  		subresource   string
    47  	}{
    48  		{
    49  			name:    "unstructured, no matching manager",
    50  			obj:     &unstructured.Unstructured{Object: map[string]interface{}{"spec": map[string]interface{}{"replicas": 1}}},
    51  			objType: parser.Type("io.k8s.api.apps.v1.Deployment"),
    52  			managedFields: []metav1.ManagedFieldsEntry{
    53  				applyFieldsEntry("mgr999", `{ "f:spec": { "f:replicas": {}}}`, ""),
    54  			},
    55  			fieldManager: "mgr1",
    56  			expectedOut:  map[string]interface{}{},
    57  		},
    58  		{
    59  			name:    "unstructured, one manager",
    60  			obj:     &unstructured.Unstructured{Object: map[string]interface{}{"spec": map[string]interface{}{"replicas": 1}}},
    61  			objType: parser.Type("io.k8s.api.apps.v1.Deployment"),
    62  			managedFields: []metav1.ManagedFieldsEntry{
    63  				applyFieldsEntry("mgr1", `{ "f:spec": { "f:replicas": {}}}`, ""),
    64  			},
    65  			fieldManager: "mgr1",
    66  			expectedOut:  map[string]interface{}{"spec": map[string]interface{}{"replicas": 1}},
    67  		},
    68  		{
    69  			name:    "unstructured, multiple manager",
    70  			obj:     &unstructured.Unstructured{Object: map[string]interface{}{"spec": map[string]interface{}{"paused": true}}},
    71  			objType: parser.Type("io.k8s.api.apps.v1.Deployment"),
    72  			managedFields: []metav1.ManagedFieldsEntry{
    73  				applyFieldsEntry("mgr1", `{ "f:spec": { "f:replicas": {}}}`, ""),
    74  				applyFieldsEntry("mgr2", `{ "f:spec": { "f:paused": {}}}`, ""),
    75  			},
    76  			fieldManager: "mgr2",
    77  			expectedOut:  map[string]interface{}{"spec": map[string]interface{}{"paused": true}},
    78  		},
    79  		{
    80  			name:    "structured, no matching manager",
    81  			obj:     &fakeDeployment{Spec: fakeDeploymentSpec{Replicas: &one}},
    82  			objType: parser.Type("io.k8s.api.apps.v1.Deployment"),
    83  			managedFields: []metav1.ManagedFieldsEntry{
    84  				applyFieldsEntry("mgr999", `{ "f:spec": { "f:replicas": {}}}`, ""),
    85  			},
    86  			fieldManager: "mgr1",
    87  			expectedOut:  map[string]interface{}{},
    88  		},
    89  		{
    90  			name:    "structured, one manager",
    91  			obj:     &fakeDeployment{Spec: fakeDeploymentSpec{Replicas: &one}},
    92  			objType: parser.Type("io.k8s.api.apps.v1.Deployment"),
    93  			managedFields: []metav1.ManagedFieldsEntry{
    94  				applyFieldsEntry("mgr1", `{ "f:spec": { "f:replicas": {}}}`, ""),
    95  			},
    96  			fieldManager: "mgr1",
    97  			expectedOut:  map[string]interface{}{"spec": map[string]interface{}{"replicas": int64(1)}},
    98  		},
    99  		{
   100  			name:    "structured, multiple manager",
   101  			obj:     &fakeDeployment{Spec: fakeDeploymentSpec{Replicas: &one, Paused: true}},
   102  			objType: parser.Type("io.k8s.api.apps.v1.Deployment"),
   103  			managedFields: []metav1.ManagedFieldsEntry{
   104  				applyFieldsEntry("mgr1", `{ "f:spec": { "f:replicas": {}}}`, ""),
   105  				applyFieldsEntry("mgr2", `{ "f:spec": { "f:paused": {}}}`, ""),
   106  			},
   107  			fieldManager: "mgr2",
   108  			expectedOut:  map[string]interface{}{"spec": map[string]interface{}{"paused": true}},
   109  		},
   110  		{
   111  			name:    "subresource",
   112  			obj:     &fakeDeployment{Status: fakeDeploymentStatus{Replicas: &one}},
   113  			objType: parser.Type("io.k8s.api.apps.v1.Deployment"),
   114  			managedFields: []metav1.ManagedFieldsEntry{
   115  				applyFieldsEntry("mgr1", `{ "f:status": { "f:replicas": {}}}`, "status"),
   116  			},
   117  			fieldManager: "mgr1",
   118  			expectedOut:  map[string]interface{}{"status": map[string]interface{}{"replicas": int64(1)}},
   119  			subresource:  "status",
   120  		},
   121  	}
   122  	for _, tc := range cases {
   123  		t.Run(tc.name, func(t *testing.T) {
   124  			out := map[string]interface{}{}
   125  			accessor, err := meta.Accessor(tc.obj)
   126  			if err != nil {
   127  				t.Fatalf("Error accessing object: %v", err)
   128  			}
   129  			accessor.SetManagedFields(tc.managedFields)
   130  			err = ExtractInto(tc.obj, tc.objType, tc.fieldManager, &out, tc.subresource)
   131  			if err != nil {
   132  				t.Fatalf("Unexpected extract error: %v", err)
   133  			}
   134  			if !equality.Semantic.DeepEqual(out, tc.expectedOut) {
   135  				t.Fatalf("Expected output did not match actual output: %s", cmp.Diff(out, tc.expectedOut))
   136  			}
   137  		})
   138  	}
   139  }
   140  
   141  func applyFieldsEntry(fieldManager string, fieldsJSON string, subresource string) metav1.ManagedFieldsEntry {
   142  	return metav1.ManagedFieldsEntry{
   143  		Manager:     fieldManager,
   144  		Operation:   metav1.ManagedFieldsOperationApply,
   145  		APIVersion:  "v1",
   146  		FieldsType:  "FieldsV1",
   147  		FieldsV1:    &metav1.FieldsV1{Raw: []byte(fieldsJSON)},
   148  		Subresource: subresource,
   149  	}
   150  }
   151  
   152  type fakeDeployment struct {
   153  	metav1.ObjectMeta `json:"metadata,omitempty"`
   154  	Spec              fakeDeploymentSpec   `json:"spec"`
   155  	Status            fakeDeploymentStatus `json:"status"`
   156  }
   157  
   158  type fakeDeploymentSpec struct {
   159  	Replicas *int32 `json:"replicas"`
   160  	Paused   bool   `json:"paused,omitempty"`
   161  }
   162  
   163  type fakeDeploymentStatus struct {
   164  	Replicas *int32 `json:"replicas"`
   165  }
   166  
   167  func (o *fakeDeployment) GetObjectMeta() metav1.ObjectMeta {
   168  	return o.ObjectMeta
   169  }
   170  func (o *fakeDeployment) GetObjectKind() runtimeschema.ObjectKind {
   171  	return runtimeschema.EmptyObjectKind
   172  }
   173  func (o *fakeDeployment) DeepCopyObject() runtime.Object {
   174  	return o
   175  }
   176  
   177  // trimmed up schema for test purposes
   178  const schemaYAML = typed.YAMLObject(`types:
   179  - name: io.k8s.api.apps.v1.Deployment
   180    map:
   181      fields:
   182      - name: apiVersion
   183        type:
   184          scalar: string
   185      - name: kind
   186        type:
   187          scalar: string
   188      - name: metadata
   189        type:
   190          namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta
   191      - name: spec
   192        type:
   193          namedType: io.k8s.api.apps.v1.DeploymentSpec
   194      - name: status
   195        type:
   196          namedType: io.k8s.api.apps.v1.DeploymentStatus
   197  - name: io.k8s.api.apps.v1.DeploymentSpec
   198    map:
   199      fields:
   200      - name: paused
   201        type:
   202          scalar: boolean
   203      - name: replicas
   204        type:
   205          scalar: numeric
   206  - name: io.k8s.api.apps.v1.DeploymentStatus
   207    map:
   208      fields:
   209      - name: replicas
   210        type:
   211          scalar: numeric
   212  - name: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta
   213    map:
   214      fields:
   215      - name: creationTimestamp
   216        type:
   217          namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time
   218      - name: managedFields
   219        type:
   220          list:
   221            elementType:
   222              namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ManagedFieldsEntry
   223            elementRelationship: atomic
   224  - name: io.k8s.apimachinery.pkg.apis.meta.v1.ManagedFieldsEntry
   225    map:
   226      fields:
   227      - name: apiVersion
   228        type:
   229          scalar: string
   230      - name: fieldsType
   231        type:
   232          scalar: string
   233      - name: fieldsV1
   234        type:
   235          namedType: io.k8s.apimachinery.pkg.apis.meta.v1.FieldsV1
   236      - name: manager
   237        type:
   238          scalar: string
   239      - name: operation
   240        type:
   241          scalar: string
   242      - name: time
   243        type:
   244          namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time
   245      - name: subresource
   246        type:
   247          scalar: string
   248  - name: io.k8s.apimachinery.pkg.apis.meta.v1.FieldsV1
   249    map:
   250      elementType:
   251        scalar: untyped
   252        list:
   253          elementType:
   254            namedType: __untyped_atomic_
   255          elementRelationship: atomic
   256        map:
   257          elementType:
   258            namedType: __untyped_atomic_
   259          elementRelationship: atomic
   260  - name: io.k8s.apimachinery.pkg.apis.meta.v1.Time
   261    scalar: untyped
   262  - name: __untyped_atomic_
   263    scalar: untyped
   264    list:
   265      elementType:
   266        namedType: __untyped_atomic_
   267      elementRelationship: atomic
   268    map:
   269      elementType:
   270        namedType: __untyped_atomic_
   271      elementRelationship: atomic
   272  `)