...

Source file src/k8s.io/kubernetes/pkg/apis/core/v1/conversion_test.go

Documentation: k8s.io/kubernetes/pkg/apis/core/v1

     1  /*
     2  Copyright 2015 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 v1_test
    18  
    19  import (
    20  	"encoding/json"
    21  	"math/rand"
    22  	"net/url"
    23  	"reflect"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/google/go-cmp/cmp"
    28  	appsv1 "k8s.io/api/apps/v1"
    29  	v1 "k8s.io/api/core/v1"
    30  	"k8s.io/apimachinery/pkg/api/apitesting/fuzzer"
    31  	apiequality "k8s.io/apimachinery/pkg/api/equality"
    32  	"k8s.io/apimachinery/pkg/api/resource"
    33  	metafuzzer "k8s.io/apimachinery/pkg/apis/meta/fuzzer"
    34  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    35  	"k8s.io/apimachinery/pkg/runtime"
    36  	"k8s.io/apimachinery/pkg/util/diff"
    37  	"k8s.io/kubernetes/pkg/api/legacyscheme"
    38  	apps "k8s.io/kubernetes/pkg/apis/apps"
    39  	"k8s.io/kubernetes/pkg/apis/core"
    40  	corefuzzer "k8s.io/kubernetes/pkg/apis/core/fuzzer"
    41  	corev1 "k8s.io/kubernetes/pkg/apis/core/v1"
    42  	utilpointer "k8s.io/utils/pointer"
    43  
    44  	// ensure types are installed
    45  	_ "k8s.io/kubernetes/pkg/apis/core/install"
    46  	// ensure types are installed corereplicationcontroller<->replicaset conversions
    47  	_ "k8s.io/kubernetes/pkg/apis/apps/install"
    48  )
    49  
    50  func TestPodLogOptions(t *testing.T) {
    51  	sinceSeconds := int64(1)
    52  	sinceTime := metav1.NewTime(time.Date(2000, 1, 1, 12, 34, 56, 0, time.UTC).Local())
    53  	tailLines := int64(2)
    54  	limitBytes := int64(3)
    55  
    56  	versionedLogOptions := &v1.PodLogOptions{
    57  		Container:    "mycontainer",
    58  		Follow:       true,
    59  		Previous:     true,
    60  		SinceSeconds: &sinceSeconds,
    61  		SinceTime:    &sinceTime,
    62  		Timestamps:   true,
    63  		TailLines:    &tailLines,
    64  		LimitBytes:   &limitBytes,
    65  	}
    66  	unversionedLogOptions := &core.PodLogOptions{
    67  		Container:    "mycontainer",
    68  		Follow:       true,
    69  		Previous:     true,
    70  		SinceSeconds: &sinceSeconds,
    71  		SinceTime:    &sinceTime,
    72  		Timestamps:   true,
    73  		TailLines:    &tailLines,
    74  		LimitBytes:   &limitBytes,
    75  	}
    76  	expectedParameters := url.Values{
    77  		"container":    {"mycontainer"},
    78  		"follow":       {"true"},
    79  		"previous":     {"true"},
    80  		"sinceSeconds": {"1"},
    81  		"sinceTime":    {"2000-01-01T12:34:56Z"},
    82  		"timestamps":   {"true"},
    83  		"tailLines":    {"2"},
    84  		"limitBytes":   {"3"},
    85  	}
    86  
    87  	codec := runtime.NewParameterCodec(legacyscheme.Scheme)
    88  
    89  	// unversioned -> query params
    90  	{
    91  		actualParameters, err := codec.EncodeParameters(unversionedLogOptions, v1.SchemeGroupVersion)
    92  		if err != nil {
    93  			t.Fatal(err)
    94  		}
    95  		if !reflect.DeepEqual(actualParameters, expectedParameters) {
    96  			t.Fatalf("Expected\n%#v\ngot\n%#v", expectedParameters, actualParameters)
    97  		}
    98  	}
    99  
   100  	// versioned -> query params
   101  	{
   102  		actualParameters, err := codec.EncodeParameters(versionedLogOptions, v1.SchemeGroupVersion)
   103  		if err != nil {
   104  			t.Fatal(err)
   105  		}
   106  		if !reflect.DeepEqual(actualParameters, expectedParameters) {
   107  			t.Fatalf("Expected\n%#v\ngot\n%#v", expectedParameters, actualParameters)
   108  		}
   109  	}
   110  
   111  	// query params -> versioned
   112  	{
   113  		convertedLogOptions := &v1.PodLogOptions{}
   114  		err := codec.DecodeParameters(expectedParameters, v1.SchemeGroupVersion, convertedLogOptions)
   115  		if err != nil {
   116  			t.Fatal(err)
   117  		}
   118  		if !reflect.DeepEqual(convertedLogOptions, versionedLogOptions) {
   119  			t.Fatalf("Unexpected deserialization:\n%s", diff.ObjectGoPrintSideBySide(versionedLogOptions, convertedLogOptions))
   120  		}
   121  	}
   122  
   123  	// query params -> unversioned
   124  	{
   125  		convertedLogOptions := &core.PodLogOptions{}
   126  		err := codec.DecodeParameters(expectedParameters, v1.SchemeGroupVersion, convertedLogOptions)
   127  		if err != nil {
   128  			t.Fatal(err)
   129  		}
   130  		if !reflect.DeepEqual(convertedLogOptions, unversionedLogOptions) {
   131  			t.Fatalf("Unexpected deserialization:\n%s", diff.ObjectGoPrintSideBySide(unversionedLogOptions, convertedLogOptions))
   132  		}
   133  	}
   134  }
   135  
   136  // TestPodSpecConversion tests that v1.ServiceAccount is an alias for
   137  // ServiceAccountName.
   138  func TestPodSpecConversion(t *testing.T) {
   139  	name, other := "foo", "bar"
   140  
   141  	// Test internal -> v1. Should have both alias (DeprecatedServiceAccount)
   142  	// and new field (ServiceAccountName).
   143  	i := &core.PodSpec{
   144  		ServiceAccountName: name,
   145  	}
   146  	v := v1.PodSpec{}
   147  	if err := legacyscheme.Scheme.Convert(i, &v, nil); err != nil {
   148  		t.Fatalf("unexpected error: %v", err)
   149  	}
   150  	if v.ServiceAccountName != name {
   151  		t.Fatalf("want v1.ServiceAccountName %q, got %q", name, v.ServiceAccountName)
   152  	}
   153  	if v.DeprecatedServiceAccount != name {
   154  		t.Fatalf("want v1.DeprecatedServiceAccount %q, got %q", name, v.DeprecatedServiceAccount)
   155  	}
   156  
   157  	// Test v1 -> internal. Either DeprecatedServiceAccount, ServiceAccountName,
   158  	// or both should translate to ServiceAccountName. ServiceAccountName wins
   159  	// if both are set.
   160  	testCases := []*v1.PodSpec{
   161  		// New
   162  		{ServiceAccountName: name},
   163  		// Alias
   164  		{DeprecatedServiceAccount: name},
   165  		// Both: same
   166  		{ServiceAccountName: name, DeprecatedServiceAccount: name},
   167  		// Both: different
   168  		{ServiceAccountName: name, DeprecatedServiceAccount: other},
   169  	}
   170  	for k, v := range testCases {
   171  		got := core.PodSpec{}
   172  		err := legacyscheme.Scheme.Convert(v, &got, nil)
   173  		if err != nil {
   174  			t.Fatalf("unexpected error for case %d: %v", k, err)
   175  		}
   176  		if got.ServiceAccountName != name {
   177  			t.Fatalf("want core.ServiceAccountName %q, got %q", name, got.ServiceAccountName)
   178  		}
   179  	}
   180  }
   181  
   182  func TestResourceListConversion(t *testing.T) {
   183  	bigMilliQuantity := resource.NewQuantity(resource.MaxMilliValue, resource.DecimalSI)
   184  	bigMilliQuantity.Add(resource.MustParse("12345m"))
   185  
   186  	tests := []struct {
   187  		input    v1.ResourceList
   188  		expected core.ResourceList
   189  	}{
   190  		{ // No changes necessary.
   191  			input: v1.ResourceList{
   192  				v1.ResourceMemory:  resource.MustParse("30M"),
   193  				v1.ResourceCPU:     resource.MustParse("100m"),
   194  				v1.ResourceStorage: resource.MustParse("1G"),
   195  			},
   196  			expected: core.ResourceList{
   197  				core.ResourceMemory:  resource.MustParse("30M"),
   198  				core.ResourceCPU:     resource.MustParse("100m"),
   199  				core.ResourceStorage: resource.MustParse("1G"),
   200  			},
   201  		},
   202  		{ // Nano-scale values should be rounded up to milli-scale.
   203  			input: v1.ResourceList{
   204  				v1.ResourceCPU:    resource.MustParse("3.000023m"),
   205  				v1.ResourceMemory: resource.MustParse("500.000050m"),
   206  			},
   207  			expected: core.ResourceList{
   208  				core.ResourceCPU:    resource.MustParse("4m"),
   209  				core.ResourceMemory: resource.MustParse("501m"),
   210  			},
   211  		},
   212  		{ // Large values should still be accurate.
   213  			input: v1.ResourceList{
   214  				v1.ResourceCPU:     bigMilliQuantity.DeepCopy(),
   215  				v1.ResourceStorage: bigMilliQuantity.DeepCopy(),
   216  			},
   217  			expected: core.ResourceList{
   218  				core.ResourceCPU:     bigMilliQuantity.DeepCopy(),
   219  				core.ResourceStorage: bigMilliQuantity.DeepCopy(),
   220  			},
   221  		},
   222  	}
   223  
   224  	for i, test := range tests {
   225  		output := core.ResourceList{}
   226  
   227  		// defaulting is a separate step from conversion that is applied when reading from the API or from etcd.
   228  		// perform that step explicitly.
   229  		corev1.SetDefaults_ResourceList(&test.input)
   230  
   231  		err := legacyscheme.Scheme.Convert(&test.input, &output, nil)
   232  		if err != nil {
   233  			t.Fatalf("unexpected error for case %d: %v", i, err)
   234  		}
   235  		if !apiequality.Semantic.DeepEqual(test.expected, output) {
   236  			t.Errorf("unexpected conversion for case %d: Expected\n%+v;\nGot\n%+v", i, test.expected, output)
   237  		}
   238  	}
   239  }
   240  
   241  func TestReplicationControllerConversion(t *testing.T) {
   242  	// If we start with a RC, we should always have round-trip fidelity.
   243  	inputs := []*v1.ReplicationController{
   244  		{
   245  			ObjectMeta: metav1.ObjectMeta{
   246  				Name:      "name",
   247  				Namespace: "namespace",
   248  			},
   249  			Spec: v1.ReplicationControllerSpec{
   250  				Replicas:        utilpointer.Int32(1),
   251  				MinReadySeconds: 32,
   252  				Selector:        map[string]string{"foo": "bar", "bar": "foo"},
   253  				Template: &v1.PodTemplateSpec{
   254  					ObjectMeta: metav1.ObjectMeta{
   255  						Labels: map[string]string{"foo": "bar", "bar": "foo"},
   256  					},
   257  					Spec: v1.PodSpec{
   258  						Containers: []v1.Container{
   259  							{
   260  								Name:  "container",
   261  								Image: "image",
   262  							},
   263  						},
   264  					},
   265  				},
   266  			},
   267  			Status: v1.ReplicationControllerStatus{
   268  				Replicas:             1,
   269  				FullyLabeledReplicas: 2,
   270  				ReadyReplicas:        3,
   271  				AvailableReplicas:    4,
   272  				ObservedGeneration:   5,
   273  				Conditions: []v1.ReplicationControllerCondition{
   274  					{
   275  						Type:               v1.ReplicationControllerReplicaFailure,
   276  						Status:             v1.ConditionTrue,
   277  						LastTransitionTime: metav1.NewTime(time.Unix(123456789, 0)),
   278  						Reason:             "Reason",
   279  						Message:            "Message",
   280  					},
   281  				},
   282  			},
   283  		},
   284  	}
   285  
   286  	// Add some fuzzed RCs.
   287  	apiObjectFuzzer := fuzzer.FuzzerFor(fuzzer.MergeFuzzerFuncs(metafuzzer.Funcs, corefuzzer.Funcs), rand.NewSource(152), legacyscheme.Codecs)
   288  	for i := 0; i < 100; i++ {
   289  		rc := &v1.ReplicationController{}
   290  		apiObjectFuzzer.Fuzz(rc)
   291  		// Sometimes the fuzzer decides to leave Spec.Template nil.
   292  		// We can't support that because Spec.Template is not a pointer in RS,
   293  		// so it will round-trip as non-nil but empty.
   294  		if rc.Spec.Template == nil {
   295  			rc.Spec.Template = &v1.PodTemplateSpec{}
   296  		}
   297  		// Sometimes the fuzzer decides to insert an empty label key.
   298  		// This doesn't round-trip properly because it's invalid.
   299  		if rc.Spec.Selector != nil {
   300  			delete(rc.Spec.Selector, "")
   301  		}
   302  		inputs = append(inputs, rc)
   303  	}
   304  
   305  	// Round-trip the input RCs before converting to RS.
   306  	for i := range inputs {
   307  		inputs[i] = roundTrip(t, inputs[i]).(*v1.ReplicationController)
   308  	}
   309  
   310  	for _, in := range inputs {
   311  		rs := &apps.ReplicaSet{}
   312  		// Use in.DeepCopy() to avoid sharing pointers with `in`.
   313  		if err := corev1.Convert_v1_ReplicationController_To_apps_ReplicaSet(in.DeepCopy(), rs, nil); err != nil {
   314  			t.Errorf("can't convert RC to RS: %v", err)
   315  			continue
   316  		}
   317  		// Round-trip RS before converting back to RC.
   318  		rs = roundTripRS(t, rs)
   319  		out := &v1.ReplicationController{}
   320  		if err := corev1.Convert_apps_ReplicaSet_To_v1_ReplicationController(rs, out, nil); err != nil {
   321  			t.Errorf("can't convert RS to RC: %v", err)
   322  			continue
   323  		}
   324  		if !apiequality.Semantic.DeepEqual(in, out) {
   325  			instr, _ := json.MarshalIndent(in, "", "  ")
   326  			outstr, _ := json.MarshalIndent(out, "", "  ")
   327  			t.Errorf("RC-RS conversion round-trip failed:\nin:\n%s\nout:\n%s", instr, outstr)
   328  		}
   329  	}
   330  }
   331  
   332  func roundTripRS(t *testing.T, rs *apps.ReplicaSet) *apps.ReplicaSet {
   333  	codec := legacyscheme.Codecs.LegacyCodec(appsv1.SchemeGroupVersion)
   334  	data, err := runtime.Encode(codec, rs)
   335  	if err != nil {
   336  		t.Errorf("%v\n %#v", err, rs)
   337  		return nil
   338  	}
   339  	obj2, err := runtime.Decode(codec, data)
   340  	if err != nil {
   341  		t.Errorf("%v\nData: %s\nSource: %#v", err, string(data), rs)
   342  		return nil
   343  	}
   344  	return obj2.(*apps.ReplicaSet)
   345  }
   346  
   347  func Test_core_PodStatus_to_v1_PodStatus(t *testing.T) {
   348  	// core to v1
   349  	testInputs := []core.PodStatus{
   350  		{
   351  			// one IP
   352  			PodIPs: []core.PodIP{
   353  				{
   354  					IP: "1.1.1.1",
   355  				},
   356  			},
   357  		},
   358  		{
   359  			// no ips
   360  			PodIPs: nil,
   361  		},
   362  		{
   363  			// list of ips
   364  			PodIPs: []core.PodIP{
   365  				{
   366  					IP: "1.1.1.1",
   367  				},
   368  				{
   369  					IP: "2000::",
   370  				},
   371  			},
   372  		},
   373  	}
   374  	for i, input := range testInputs {
   375  		v1PodStatus := v1.PodStatus{}
   376  		if err := corev1.Convert_core_PodStatus_To_v1_PodStatus(&input, &v1PodStatus, nil); nil != err {
   377  			t.Errorf("%v: Convert core.PodStatus to v1.PodStatus failed with error %v", i, err.Error())
   378  		}
   379  
   380  		if len(input.PodIPs) == 0 {
   381  			// no more work needed
   382  			continue
   383  		}
   384  		// Primary IP was not set..
   385  		if len(v1PodStatus.PodIP) == 0 {
   386  			t.Errorf("%v: Convert core.PodStatus to v1.PodStatus failed out.PodIP is empty, should be %v", i, v1PodStatus.PodIP)
   387  		}
   388  
   389  		// Primary should always == in.PodIPs[0].IP
   390  		if len(input.PodIPs) > 0 && v1PodStatus.PodIP != input.PodIPs[0].IP {
   391  			t.Errorf("%v: Convert core.PodStatus to v1.PodStatus failed out.PodIP != in.PodIP[0].IP expected %v found %v", i, input.PodIPs[0].IP, v1PodStatus.PodIP)
   392  		}
   393  		// match v1.PodIPs to core.PodIPs
   394  		for idx := range input.PodIPs {
   395  			if v1PodStatus.PodIPs[idx].IP != input.PodIPs[idx].IP {
   396  				t.Errorf("%v: Convert core.PodStatus to v1.PodStatus failed. Expected v1.PodStatus[%v]=%v but found %v", i, idx, input.PodIPs[idx].IP, v1PodStatus.PodIPs[idx].IP)
   397  			}
   398  		}
   399  	}
   400  }
   401  func Test_v1_PodStatus_to_core_PodStatus(t *testing.T) {
   402  	asymmetricInputs := []struct {
   403  		name string
   404  		in   v1.PodStatus
   405  		out  core.PodStatus
   406  	}{
   407  		{
   408  			name: "mismatched podIP",
   409  			in: v1.PodStatus{
   410  				PodIP: "1.1.2.1", // Older field takes precedence for compatibility with patch by older clients
   411  				PodIPs: []v1.PodIP{
   412  					{IP: "1.1.1.1"},
   413  					{IP: "2.2.2.2"},
   414  				},
   415  			},
   416  			out: core.PodStatus{
   417  				PodIPs: []core.PodIP{
   418  					{IP: "1.1.2.1"},
   419  				},
   420  			},
   421  		},
   422  		{
   423  			name: "matching podIP",
   424  			in: v1.PodStatus{
   425  				PodIP: "1.1.1.1",
   426  				PodIPs: []v1.PodIP{
   427  					{IP: "1.1.1.1"},
   428  					{IP: "2.2.2.2"},
   429  				},
   430  			},
   431  			out: core.PodStatus{
   432  				PodIPs: []core.PodIP{
   433  					{IP: "1.1.1.1"},
   434  					{IP: "2.2.2.2"},
   435  				},
   436  			},
   437  		},
   438  		{
   439  			name: "empty podIP",
   440  			in: v1.PodStatus{
   441  				PodIP: "",
   442  				PodIPs: []v1.PodIP{
   443  					{IP: "1.1.1.1"},
   444  					{IP: "2.2.2.2"},
   445  				},
   446  			},
   447  			out: core.PodStatus{
   448  				PodIPs: []core.PodIP{
   449  					{IP: "1.1.1.1"},
   450  					{IP: "2.2.2.2"},
   451  				},
   452  			},
   453  		},
   454  	}
   455  
   456  	// success
   457  	v1TestInputs := []v1.PodStatus{
   458  		// only Primary IP Provided
   459  		{
   460  			PodIP: "1.1.1.1",
   461  		},
   462  		{
   463  			// both are not provided
   464  			PodIP:  "",
   465  			PodIPs: nil,
   466  		},
   467  		// only list of IPs
   468  		{
   469  			PodIPs: []v1.PodIP{
   470  				{IP: "1.1.1.1"},
   471  				{IP: "2.2.2.2"},
   472  			},
   473  		},
   474  		// Both
   475  		{
   476  			PodIP: "1.1.1.1",
   477  			PodIPs: []v1.PodIP{
   478  				{IP: "1.1.1.1"},
   479  				{IP: "2.2.2.2"},
   480  			},
   481  		},
   482  		// v4 and v6
   483  		{
   484  			PodIP: "1.1.1.1",
   485  			PodIPs: []v1.PodIP{
   486  				{IP: "1.1.1.1"},
   487  				{IP: "::1"},
   488  			},
   489  		},
   490  		// v6 and v4
   491  		{
   492  			PodIP: "::1",
   493  			PodIPs: []v1.PodIP{
   494  				{IP: "::1"},
   495  				{IP: "1.1.1.1"},
   496  			},
   497  		},
   498  	}
   499  
   500  	// run asymmetric cases
   501  	for _, tc := range asymmetricInputs {
   502  		testInput := tc.in
   503  
   504  		corePodStatus := core.PodStatus{}
   505  		// convert..
   506  		if err := corev1.Convert_v1_PodStatus_To_core_PodStatus(&testInput, &corePodStatus, nil); err != nil {
   507  			t.Errorf("%s: Convert v1.PodStatus to core.PodStatus failed with error:%v for input %+v", tc.name, err.Error(), testInput)
   508  		}
   509  		if !reflect.DeepEqual(corePodStatus, tc.out) {
   510  			t.Errorf("%s: expected %#v, got %#v", tc.name, tc.out.PodIPs, corePodStatus.PodIPs)
   511  		}
   512  	}
   513  
   514  	// run ok cases
   515  	for i, testInput := range v1TestInputs {
   516  		corePodStatus := core.PodStatus{}
   517  		// convert..
   518  		if err := corev1.Convert_v1_PodStatus_To_core_PodStatus(&testInput, &corePodStatus, nil); err != nil {
   519  			t.Errorf("%v: Convert v1.PodStatus to core.PodStatus failed with error:%v for input %+v", i, err.Error(), testInput)
   520  		}
   521  
   522  		if len(testInput.PodIP) == 0 && len(testInput.PodIPs) == 0 {
   523  			continue //no more work needed
   524  		}
   525  
   526  		// List should have at least 1 IP == v1.PodIP || v1.PodIPs[0] (whichever provided)
   527  		if len(testInput.PodIP) > 0 && corePodStatus.PodIPs[0].IP != testInput.PodIP {
   528  			t.Errorf("%v: Convert v1.PodStatus to core.PodStatus failed. expected corePodStatus.PodIPs[0].ip=%v found %v", i, corePodStatus.PodIPs[0].IP, corePodStatus.PodIPs[0].IP)
   529  		}
   530  
   531  		// walk the list
   532  		for idx := range testInput.PodIPs {
   533  			if corePodStatus.PodIPs[idx].IP != testInput.PodIPs[idx].IP {
   534  				t.Errorf("%v: Convert v1.PodStatus to core.PodStatus failed core.PodIPs[%v]=%v expected %v", i, idx, corePodStatus.PodIPs[idx].IP, testInput.PodIPs[idx].IP)
   535  			}
   536  		}
   537  
   538  		// if input has a list of IPs
   539  		// then out put should have the same length
   540  		if len(testInput.PodIPs) > 0 && len(testInput.PodIPs) != len(corePodStatus.PodIPs) {
   541  			t.Errorf("%v: Convert v1.PodStatus to core.PodStatus failed len(core.PodIPs) != len(v1.PodStatus.PodIPs) [%v]=[%v]", i, len(corePodStatus.PodIPs), len(testInput.PodIPs))
   542  		}
   543  	}
   544  }
   545  
   546  func Test_core_NodeSpec_to_v1_NodeSpec(t *testing.T) {
   547  	// core to v1
   548  	testInputs := []core.NodeSpec{
   549  		{
   550  			PodCIDRs: []string{"10.0.0.0/24", "10.0.1.0/24"},
   551  		},
   552  		{
   553  			PodCIDRs: nil,
   554  		},
   555  		{
   556  			PodCIDRs: []string{"10.0.0.0/24"},
   557  		},
   558  		{
   559  			PodCIDRs: []string{"ace:cab:deca::/8"},
   560  		},
   561  		{
   562  			PodCIDRs: []string{"10.0.0.0/24", "ace:cab:deca::/8"},
   563  		},
   564  		{
   565  			PodCIDRs: []string{"ace:cab:deca::/8", "10.0.0.0/24"},
   566  		},
   567  	}
   568  
   569  	for i, testInput := range testInputs {
   570  		v1NodeSpec := v1.NodeSpec{}
   571  		// convert
   572  		if err := corev1.Convert_core_NodeSpec_To_v1_NodeSpec(&testInput, &v1NodeSpec, nil); nil != err {
   573  			t.Errorf("%v: Convert core.NodeSpec to v1.NodeSpec failed with error %v", i, err.Error())
   574  		}
   575  
   576  		if len(testInput.PodCIDRs) == 0 {
   577  			continue // no more work needed
   578  		}
   579  
   580  		// validate results
   581  		if v1NodeSpec.PodCIDR != testInput.PodCIDRs[0] {
   582  			t.Errorf("%v: Convert core.NodeSpec to v1.NodeSpec failed. Expected v1.PodCIDR=%v but found %v", i, testInput.PodCIDRs[0], v1NodeSpec.PodCIDR)
   583  		}
   584  
   585  		// match v1.PodIPs to core.PodIPs
   586  		for idx := range testInput.PodCIDRs {
   587  			if v1NodeSpec.PodCIDRs[idx] != testInput.PodCIDRs[idx] {
   588  				t.Errorf("%v: Convert core.NodeSpec to v1.NodeSpec failed. Expected v1.NodeSpec[%v]=%v but found %v", i, idx, testInput.PodCIDRs[idx], v1NodeSpec.PodCIDRs[idx])
   589  			}
   590  		}
   591  	}
   592  }
   593  
   594  func Test_v1_NodeSpec_to_core_NodeSpec(t *testing.T) {
   595  	asymmetricInputs := []struct {
   596  		name string
   597  		in   v1.NodeSpec
   598  		out  core.NodeSpec
   599  	}{
   600  		{
   601  			name: "mismatched podCIDR",
   602  			in: v1.NodeSpec{
   603  				PodCIDR:  "10.0.0.0/24",
   604  				PodCIDRs: []string{"10.0.1.0/24", "ace:cab:deca::/8"},
   605  			},
   606  			out: core.NodeSpec{
   607  				PodCIDRs: []string{"10.0.0.0/24"},
   608  			},
   609  		},
   610  		{
   611  			name: "unset podCIDR",
   612  			in: v1.NodeSpec{
   613  				PodCIDR:  "",
   614  				PodCIDRs: []string{"10.0.1.0/24", "ace:cab:deca::/8"},
   615  			},
   616  			out: core.NodeSpec{
   617  				PodCIDRs: []string{"10.0.1.0/24", "ace:cab:deca::/8"},
   618  			},
   619  		},
   620  		{
   621  			name: "matching podCIDR",
   622  			in: v1.NodeSpec{
   623  				PodCIDR:  "10.0.1.0/24",
   624  				PodCIDRs: []string{"10.0.1.0/24", "ace:cab:deca::/8"},
   625  			},
   626  			out: core.NodeSpec{
   627  				PodCIDRs: []string{"10.0.1.0/24", "ace:cab:deca::/8"},
   628  			},
   629  		},
   630  	}
   631  
   632  	testInputs := []v1.NodeSpec{
   633  		// cidr only - 4
   634  		{
   635  			PodCIDR: "10.0.1.0/24",
   636  		},
   637  		// cidr only - 6
   638  		{
   639  			PodCIDR: "ace:cab:deca::/8",
   640  		},
   641  		// Both are provided
   642  		{
   643  			PodCIDR:  "10.0.1.0/24",
   644  			PodCIDRs: []string{"10.0.1.0/24", "ace:cab:deca::/8"},
   645  		},
   646  		// list only
   647  		{
   648  			PodCIDRs: []string{"10.0.1.0/24", "ace:cab:deca::/8"},
   649  		},
   650  		// Both are provided 4,6
   651  		{
   652  			PodCIDR:  "10.0.1.0/24",
   653  			PodCIDRs: []string{"10.0.1.0/24", "ace:cab:deca::/8"},
   654  		},
   655  		// Both are provided 6,4
   656  		{
   657  			PodCIDR:  "ace:cab:deca::/8",
   658  			PodCIDRs: []string{"ace:cab:deca::/8", "10.0.1.0/24"},
   659  		},
   660  		// list only 4,6
   661  		{
   662  			PodCIDRs: []string{"10.0.1.0/24", "ace:cab:deca::/8"},
   663  		},
   664  		// list only 6,4
   665  		{
   666  			PodCIDRs: []string{"ace:cab:deca::/8", "10.0.1.0/24"},
   667  		},
   668  		// no cidr and no cidrs
   669  		{
   670  			PodCIDR:  "",
   671  			PodCIDRs: nil,
   672  		},
   673  	}
   674  
   675  	// run asymmetric cases
   676  	for _, tc := range asymmetricInputs {
   677  		testInput := tc.in
   678  
   679  		coreNodeSpec := core.NodeSpec{}
   680  		// convert..
   681  		if err := corev1.Convert_v1_NodeSpec_To_core_NodeSpec(&testInput, &coreNodeSpec, nil); err != nil {
   682  			t.Errorf("%s: Convert v1.NodeSpec to core.NodeSpec failed with error:%v for input %+v", tc.name, err.Error(), testInput)
   683  		}
   684  		if !reflect.DeepEqual(coreNodeSpec, tc.out) {
   685  			t.Errorf("%s: expected %#v, got %#v", tc.name, tc.out.PodCIDRs, coreNodeSpec.PodCIDRs)
   686  		}
   687  	}
   688  
   689  	for i, testInput := range testInputs {
   690  		coreNodeSpec := core.NodeSpec{}
   691  		if err := corev1.Convert_v1_NodeSpec_To_core_NodeSpec(&testInput, &coreNodeSpec, nil); err != nil {
   692  			t.Errorf("%v:Convert v1.NodeSpec to core.NodeSpec failed with error:%v", i, err.Error())
   693  		}
   694  		if len(testInput.PodCIDRs) == 0 && len(testInput.PodCIDR) == 0 {
   695  			continue // no more work needed
   696  		}
   697  		if len(testInput.PodCIDR) > 0 && coreNodeSpec.PodCIDRs[0] != testInput.PodCIDR {
   698  			t.Errorf("%v:Convert v1.NodeSpec to core.NodeSpec failed. expected coreNodeSpec.PodCIDRs[0]=%v found %v", i, testInput.PodCIDR, coreNodeSpec.PodCIDRs[0])
   699  		}
   700  		// match ip list
   701  		for idx := range testInput.PodCIDRs {
   702  			if coreNodeSpec.PodCIDRs[idx] != testInput.PodCIDRs[idx] {
   703  				t.Errorf("%v:Convert v1.NodeSpec to core.NodeSpec failed core.PodCIDRs[%v]=%v expected %v", i, idx, coreNodeSpec.PodCIDRs[idx], testInput.PodCIDRs[idx])
   704  			}
   705  		}
   706  	}
   707  }
   708  
   709  func TestConvert_v1_Pod_To_core_Pod(t *testing.T) {
   710  	type args struct {
   711  		in  *v1.Pod
   712  		out *core.Pod
   713  	}
   714  	tests := []struct {
   715  		name    string
   716  		args    args
   717  		wantErr bool
   718  		wantOut *core.Pod
   719  	}{
   720  		{
   721  			args: args{
   722  				in: &v1.Pod{
   723  					Spec: v1.PodSpec{
   724  						TerminationGracePeriodSeconds: utilpointer.Int64(-1),
   725  					},
   726  				},
   727  				out: &core.Pod{},
   728  			},
   729  			wantOut: &core.Pod{
   730  				Spec: core.PodSpec{
   731  					TerminationGracePeriodSeconds: utilpointer.Int64(1),
   732  					SecurityContext:               &core.PodSecurityContext{},
   733  				},
   734  			},
   735  		},
   736  	}
   737  	for _, tt := range tests {
   738  		t.Run(tt.name, func(t *testing.T) {
   739  			if err := corev1.Convert_v1_Pod_To_core_Pod(tt.args.in, tt.args.out, nil); (err != nil) != tt.wantErr {
   740  				t.Errorf("Convert_v1_Pod_To_core_Pod() error = %v, wantErr %v", err, tt.wantErr)
   741  			}
   742  			if diff := cmp.Diff(tt.args.out, tt.wantOut); diff != "" {
   743  				t.Errorf("Convert_v1_Pod_To_core_Pod() mismatch (-want +got):\n%s", diff)
   744  			}
   745  		})
   746  	}
   747  }
   748  
   749  func TestConvert_core_Pod_To_v1_Pod(t *testing.T) {
   750  	type args struct {
   751  		in  *core.Pod
   752  		out *v1.Pod
   753  	}
   754  	tests := []struct {
   755  		name    string
   756  		args    args
   757  		wantErr bool
   758  		wantOut *v1.Pod
   759  	}{
   760  		{
   761  			args: args{
   762  				in: &core.Pod{
   763  					Spec: core.PodSpec{
   764  						TerminationGracePeriodSeconds: utilpointer.Int64(-1),
   765  					},
   766  				},
   767  				out: &v1.Pod{},
   768  			},
   769  			wantOut: &v1.Pod{
   770  				Spec: v1.PodSpec{
   771  					TerminationGracePeriodSeconds: utilpointer.Int64(1),
   772  				},
   773  			},
   774  		},
   775  	}
   776  	for _, tt := range tests {
   777  		t.Run(tt.name, func(t *testing.T) {
   778  			if err := corev1.Convert_core_Pod_To_v1_Pod(tt.args.in, tt.args.out, nil); (err != nil) != tt.wantErr {
   779  				t.Errorf("Convert_core_Pod_To_v1_Pod() error = %v, wantErr %v", err, tt.wantErr)
   780  			}
   781  			if diff := cmp.Diff(tt.args.out, tt.wantOut); diff != "" {
   782  				t.Errorf("Convert_core_Pod_To_v1_Pod() mismatch (-want +got):\n%s", diff)
   783  			}
   784  		})
   785  	}
   786  }
   787  

View as plain text