/* Copyright 2020 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. */ // This code is directly lifted from the kubefed codebase. // For reference: // https://github.com/kubernetes-sigs/kubefed/blob/master/pkg/controller/sync/dispatch/retain_test.go#L91-L292 package lifted import ( "reflect" "testing" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/utils/ptr" ) // +lifted:source=https://github.com/kubernetes-sigs/kubefed/blob/master/pkg/controller/sync/dispatch/retain_test.go#L91-L174 // +lifted:changed func TestRetainHealthCheckNodePortInServiceFields(t *testing.T) { tests := []struct { name string desiredObj *unstructured.Unstructured clusterObj *unstructured.Unstructured retainSucceed bool expectedValue *int64 }{ { "cluster object has no healthCheckNodePort", &unstructured.Unstructured{ Object: map[string]interface{}{}, }, &unstructured.Unstructured{ Object: map[string]interface{}{}, }, true, nil, }, { "cluster object has invalid healthCheckNodePort", &unstructured.Unstructured{ Object: map[string]interface{}{}, }, &unstructured.Unstructured{ Object: map[string]interface{}{ "spec": map[string]interface{}{ "healthCheckNodePort": "invalid string", }, }, }, false, nil, }, { "cluster object has healthCheckNodePort 0", &unstructured.Unstructured{ Object: map[string]interface{}{}, }, &unstructured.Unstructured{ Object: map[string]interface{}{ "spec": map[string]interface{}{ "healthCheckNodePort": int64(0), }, }, }, true, nil, }, { "cluster object has healthCheckNodePort 1000", &unstructured.Unstructured{ Object: map[string]interface{}{}, }, &unstructured.Unstructured{ Object: map[string]interface{}{ "spec": map[string]interface{}{ "healthCheckNodePort": int64(1000), }, }, }, true, ptr.To[int64](1000), }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { if _, err := RetainServiceFields(test.desiredObj, test.clusterObj); (err == nil) != test.retainSucceed { t.Fatalf("test %s fails: unexpected returned error %v", test.name, err) } currentValue, ok, err := unstructured.NestedInt64(test.desiredObj.Object, "spec", "healthCheckNodePort") if err != nil { t.Fatalf("test %s fails: %v", test.name, err) } if !ok && test.expectedValue != nil { t.Fatalf("test %s fails: expect specified healthCheckNodePort but not found", test.name) } if ok && (test.expectedValue == nil || *test.expectedValue != currentValue) { t.Fatalf("test %s fails: unexpected current healthCheckNodePort %d", test.name, currentValue) } }) } } // +lifted:source=https://github.com/kubernetes-sigs/kubefed/blob/master/pkg/controller/sync/dispatch/retain_test.go#L176-L292 // +lifted:changed func TestRetainClusterIPInServiceFields(t *testing.T) { tests := []struct { name string desiredObj *unstructured.Unstructured clusterObj *unstructured.Unstructured retainSucceed bool expectedClusterIPValue *string }{ { "cluster object has no clusterIP", &unstructured.Unstructured{ Object: map[string]interface{}{}, }, &unstructured.Unstructured{ Object: map[string]interface{}{}, }, true, nil, }, { "cluster object has invalid clusterIP", &unstructured.Unstructured{ Object: map[string]interface{}{}, }, &unstructured.Unstructured{ Object: map[string]interface{}{ "spec": map[string]interface{}{ "clusterIP": -1000, }, }, }, false, nil, }, { "cluster object has valid clusterIP", &unstructured.Unstructured{ Object: map[string]interface{}{}, }, &unstructured.Unstructured{ Object: map[string]interface{}{ "spec": map[string]interface{}{ "clusterIP": "1.2.3.4", }, }, }, true, ptr.To("1.2.3.4"), }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { if _, err := RetainServiceFields(test.desiredObj, test.clusterObj); (err == nil) != test.retainSucceed { t.Fatalf("test %s fails: unexpected returned error %v", test.name, err) } currentClusterIPValue, ok, err := unstructured.NestedString(test.desiredObj.Object, "spec", "clusterIP") if err != nil { t.Fatalf("test %s fails: %v", test.name, err) } if !ok && test.expectedClusterIPValue != nil { t.Fatalf("test %s fails: expect specified clusterIP but not found", test.name) } if ok && (test.expectedClusterIPValue == nil || *test.expectedClusterIPValue != currentClusterIPValue) { t.Fatalf("test %s fails: unexpected current clusterIP %s", test.name, currentClusterIPValue) } }) } } func TestRetainServiceAccountFields(t *testing.T) { tests := []struct { name string desiredObj *unstructured.Unstructured observedObj *unstructured.Unstructured expectedErr bool expectedSecretsValue []interface{} }{ { name: "neither desired or observed service account has the secrets field", desiredObj: &unstructured.Unstructured{ Object: map[string]interface{}{}, }, observedObj: &unstructured.Unstructured{ Object: map[string]interface{}{}, }, expectedErr: false, expectedSecretsValue: nil, }, { name: "both desired and observed service account have the same secrets field", desiredObj: &unstructured.Unstructured{ Object: map[string]interface{}{ "secrets": []interface{}{ map[string]interface{}{ "name": "test", }, }, }, }, observedObj: &unstructured.Unstructured{ Object: map[string]interface{}{ "secrets": []interface{}{ map[string]interface{}{ "name": "test", }, }, }, }, expectedErr: false, expectedSecretsValue: []interface{}{ map[string]interface{}{ "name": "test", }, }, }, { name: "desired and observed service account have the different secrets field", desiredObj: &unstructured.Unstructured{ Object: map[string]interface{}{ "secrets": []interface{}{ map[string]interface{}{ "name": "test", }, }, }, }, observedObj: &unstructured.Unstructured{ Object: map[string]interface{}{ "secrets": []interface{}{ map[string]interface{}{ "name": "test", }, map[string]interface{}{ "name": "test-token", }, }, }, }, expectedErr: false, expectedSecretsValue: []interface{}{ map[string]interface{}{ "name": "test", }, map[string]interface{}{ "name": "test-token", }, }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { desiredObj, err := RetainServiceAccountFields(test.desiredObj, test.observedObj) if (err != nil) != test.expectedErr { t.Fatalf("unexpected returned error %v", err) } if err == nil { currentSecretValue, ok, err := unstructured.NestedSlice(desiredObj.Object, SecretsField) if err != nil { t.Fatalf("failed to get the secrets field from the serviceaccount, err is: %v", err) } if !ok && test.expectedSecretsValue != nil { t.Fatalf("expect specified secrets %s but not found", test.expectedSecretsValue) } if ok && !reflect.DeepEqual(test.expectedSecretsValue, currentSecretValue) { t.Fatalf("expect specified secrets %s but get %s", test.expectedSecretsValue, currentSecretValue) } } }) } }