defaults_test.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567
  1. /*
  2. Copyright 2017 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package v1beta2_test
  14. import (
  15. "reflect"
  16. "testing"
  17. appsv1beta2 "k8s.io/api/apps/v1beta2"
  18. "k8s.io/api/core/v1"
  19. apiequality "k8s.io/apimachinery/pkg/api/equality"
  20. "k8s.io/apimachinery/pkg/api/resource"
  21. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  22. "k8s.io/apimachinery/pkg/runtime"
  23. "k8s.io/apimachinery/pkg/util/intstr"
  24. "k8s.io/kubernetes/pkg/api/legacyscheme"
  25. _ "k8s.io/kubernetes/pkg/apis/apps/install"
  26. . "k8s.io/kubernetes/pkg/apis/apps/v1beta2"
  27. api "k8s.io/kubernetes/pkg/apis/core"
  28. _ "k8s.io/kubernetes/pkg/apis/core/install"
  29. utilpointer "k8s.io/utils/pointer"
  30. )
  31. func TestSetDefaultDaemonSetSpec(t *testing.T) {
  32. defaultLabels := map[string]string{"foo": "bar"}
  33. maxUnavailable := intstr.FromInt(1)
  34. period := int64(v1.DefaultTerminationGracePeriodSeconds)
  35. defaultTemplate := v1.PodTemplateSpec{
  36. Spec: v1.PodSpec{
  37. DNSPolicy: v1.DNSClusterFirst,
  38. RestartPolicy: v1.RestartPolicyAlways,
  39. SecurityContext: &v1.PodSecurityContext{},
  40. TerminationGracePeriodSeconds: &period,
  41. SchedulerName: api.DefaultSchedulerName,
  42. },
  43. ObjectMeta: metav1.ObjectMeta{
  44. Labels: defaultLabels,
  45. },
  46. }
  47. templateNoLabel := v1.PodTemplateSpec{
  48. Spec: v1.PodSpec{
  49. DNSPolicy: v1.DNSClusterFirst,
  50. RestartPolicy: v1.RestartPolicyAlways,
  51. SecurityContext: &v1.PodSecurityContext{},
  52. TerminationGracePeriodSeconds: &period,
  53. SchedulerName: api.DefaultSchedulerName,
  54. },
  55. }
  56. tests := []struct {
  57. original *appsv1beta2.DaemonSet
  58. expected *appsv1beta2.DaemonSet
  59. }{
  60. { // Labels change/defaulting test.
  61. original: &appsv1beta2.DaemonSet{
  62. Spec: appsv1beta2.DaemonSetSpec{
  63. Template: defaultTemplate,
  64. },
  65. },
  66. expected: &appsv1beta2.DaemonSet{
  67. ObjectMeta: metav1.ObjectMeta{
  68. Labels: defaultLabels,
  69. },
  70. Spec: appsv1beta2.DaemonSetSpec{
  71. Template: defaultTemplate,
  72. UpdateStrategy: appsv1beta2.DaemonSetUpdateStrategy{
  73. Type: appsv1beta2.RollingUpdateDaemonSetStrategyType,
  74. RollingUpdate: &appsv1beta2.RollingUpdateDaemonSet{
  75. MaxUnavailable: &maxUnavailable,
  76. },
  77. },
  78. RevisionHistoryLimit: utilpointer.Int32Ptr(10),
  79. },
  80. },
  81. },
  82. { // Labels change/defaulting test.
  83. original: &appsv1beta2.DaemonSet{
  84. ObjectMeta: metav1.ObjectMeta{
  85. Labels: map[string]string{
  86. "bar": "foo",
  87. },
  88. },
  89. Spec: appsv1beta2.DaemonSetSpec{
  90. Template: defaultTemplate,
  91. RevisionHistoryLimit: utilpointer.Int32Ptr(1),
  92. },
  93. },
  94. expected: &appsv1beta2.DaemonSet{
  95. ObjectMeta: metav1.ObjectMeta{
  96. Labels: map[string]string{
  97. "bar": "foo",
  98. },
  99. },
  100. Spec: appsv1beta2.DaemonSetSpec{
  101. Template: defaultTemplate,
  102. UpdateStrategy: appsv1beta2.DaemonSetUpdateStrategy{
  103. Type: appsv1beta2.RollingUpdateDaemonSetStrategyType,
  104. RollingUpdate: &appsv1beta2.RollingUpdateDaemonSet{
  105. MaxUnavailable: &maxUnavailable,
  106. },
  107. },
  108. RevisionHistoryLimit: utilpointer.Int32Ptr(1),
  109. },
  110. },
  111. },
  112. { // OnDeleteDaemonSetStrategyType update strategy.
  113. original: &appsv1beta2.DaemonSet{
  114. Spec: appsv1beta2.DaemonSetSpec{
  115. Template: templateNoLabel,
  116. UpdateStrategy: appsv1beta2.DaemonSetUpdateStrategy{
  117. Type: appsv1beta2.OnDeleteDaemonSetStrategyType,
  118. },
  119. },
  120. },
  121. expected: &appsv1beta2.DaemonSet{
  122. Spec: appsv1beta2.DaemonSetSpec{
  123. Template: templateNoLabel,
  124. UpdateStrategy: appsv1beta2.DaemonSetUpdateStrategy{
  125. Type: appsv1beta2.OnDeleteDaemonSetStrategyType,
  126. },
  127. RevisionHistoryLimit: utilpointer.Int32Ptr(10),
  128. },
  129. },
  130. },
  131. { // Custom unique label key.
  132. original: &appsv1beta2.DaemonSet{
  133. Spec: appsv1beta2.DaemonSetSpec{},
  134. },
  135. expected: &appsv1beta2.DaemonSet{
  136. Spec: appsv1beta2.DaemonSetSpec{
  137. Template: templateNoLabel,
  138. UpdateStrategy: appsv1beta2.DaemonSetUpdateStrategy{
  139. Type: appsv1beta2.RollingUpdateDaemonSetStrategyType,
  140. RollingUpdate: &appsv1beta2.RollingUpdateDaemonSet{
  141. MaxUnavailable: &maxUnavailable,
  142. },
  143. },
  144. RevisionHistoryLimit: utilpointer.Int32Ptr(10),
  145. },
  146. },
  147. },
  148. }
  149. for i, test := range tests {
  150. original := test.original
  151. expected := test.expected
  152. obj2 := roundTrip(t, runtime.Object(original))
  153. got, ok := obj2.(*appsv1beta2.DaemonSet)
  154. if !ok {
  155. t.Errorf("(%d) unexpected object: %v", i, got)
  156. t.FailNow()
  157. }
  158. if !apiequality.Semantic.DeepEqual(got.Spec, expected.Spec) {
  159. t.Errorf("(%d) got different than expected\ngot:\n\t%+v\nexpected:\n\t%+v", i, got.Spec, expected.Spec)
  160. }
  161. }
  162. }
  163. func TestSetDefaultStatefulSet(t *testing.T) {
  164. defaultLabels := map[string]string{"foo": "bar"}
  165. var defaultPartition int32 = 0
  166. var defaultReplicas int32 = 1
  167. period := int64(v1.DefaultTerminationGracePeriodSeconds)
  168. defaultTemplate := v1.PodTemplateSpec{
  169. Spec: v1.PodSpec{
  170. DNSPolicy: v1.DNSClusterFirst,
  171. RestartPolicy: v1.RestartPolicyAlways,
  172. SecurityContext: &v1.PodSecurityContext{},
  173. TerminationGracePeriodSeconds: &period,
  174. SchedulerName: api.DefaultSchedulerName,
  175. },
  176. ObjectMeta: metav1.ObjectMeta{
  177. Labels: defaultLabels,
  178. },
  179. }
  180. tests := []struct {
  181. original *appsv1beta2.StatefulSet
  182. expected *appsv1beta2.StatefulSet
  183. }{
  184. { // labels and default update strategy
  185. original: &appsv1beta2.StatefulSet{
  186. Spec: appsv1beta2.StatefulSetSpec{
  187. Template: defaultTemplate,
  188. },
  189. },
  190. expected: &appsv1beta2.StatefulSet{
  191. ObjectMeta: metav1.ObjectMeta{
  192. Labels: defaultLabels,
  193. },
  194. Spec: appsv1beta2.StatefulSetSpec{
  195. Replicas: &defaultReplicas,
  196. Template: defaultTemplate,
  197. PodManagementPolicy: appsv1beta2.OrderedReadyPodManagement,
  198. UpdateStrategy: appsv1beta2.StatefulSetUpdateStrategy{
  199. Type: appsv1beta2.RollingUpdateStatefulSetStrategyType,
  200. RollingUpdate: &appsv1beta2.RollingUpdateStatefulSetStrategy{
  201. Partition: &defaultPartition,
  202. },
  203. },
  204. RevisionHistoryLimit: utilpointer.Int32Ptr(10),
  205. },
  206. },
  207. },
  208. { // Alternate update strategy
  209. original: &appsv1beta2.StatefulSet{
  210. Spec: appsv1beta2.StatefulSetSpec{
  211. Template: defaultTemplate,
  212. UpdateStrategy: appsv1beta2.StatefulSetUpdateStrategy{
  213. Type: appsv1beta2.OnDeleteStatefulSetStrategyType,
  214. },
  215. },
  216. },
  217. expected: &appsv1beta2.StatefulSet{
  218. ObjectMeta: metav1.ObjectMeta{
  219. Labels: defaultLabels,
  220. },
  221. Spec: appsv1beta2.StatefulSetSpec{
  222. Replicas: &defaultReplicas,
  223. Template: defaultTemplate,
  224. PodManagementPolicy: appsv1beta2.OrderedReadyPodManagement,
  225. UpdateStrategy: appsv1beta2.StatefulSetUpdateStrategy{
  226. Type: appsv1beta2.OnDeleteStatefulSetStrategyType,
  227. },
  228. RevisionHistoryLimit: utilpointer.Int32Ptr(10),
  229. },
  230. },
  231. },
  232. { // Parallel pod management policy.
  233. original: &appsv1beta2.StatefulSet{
  234. Spec: appsv1beta2.StatefulSetSpec{
  235. Template: defaultTemplate,
  236. PodManagementPolicy: appsv1beta2.ParallelPodManagement,
  237. },
  238. },
  239. expected: &appsv1beta2.StatefulSet{
  240. ObjectMeta: metav1.ObjectMeta{
  241. Labels: defaultLabels,
  242. },
  243. Spec: appsv1beta2.StatefulSetSpec{
  244. Replicas: &defaultReplicas,
  245. Template: defaultTemplate,
  246. PodManagementPolicy: appsv1beta2.ParallelPodManagement,
  247. UpdateStrategy: appsv1beta2.StatefulSetUpdateStrategy{
  248. Type: appsv1beta2.RollingUpdateStatefulSetStrategyType,
  249. RollingUpdate: &appsv1beta2.RollingUpdateStatefulSetStrategy{
  250. Partition: &defaultPartition,
  251. },
  252. },
  253. RevisionHistoryLimit: utilpointer.Int32Ptr(10),
  254. },
  255. },
  256. },
  257. }
  258. for i, test := range tests {
  259. original := test.original
  260. expected := test.expected
  261. obj2 := roundTrip(t, runtime.Object(original))
  262. got, ok := obj2.(*appsv1beta2.StatefulSet)
  263. if !ok {
  264. t.Errorf("(%d) unexpected object: %v", i, got)
  265. t.FailNow()
  266. }
  267. if !apiequality.Semantic.DeepEqual(got.Spec, expected.Spec) {
  268. t.Errorf("(%d) got different than expected\ngot:\n\t%+v\nexpected:\n\t%+v", i, got.Spec, expected.Spec)
  269. }
  270. }
  271. }
  272. func TestSetDefaultDeployment(t *testing.T) {
  273. defaultIntOrString := intstr.FromString("25%")
  274. differentIntOrString := intstr.FromInt(5)
  275. period := int64(v1.DefaultTerminationGracePeriodSeconds)
  276. defaultTemplate := v1.PodTemplateSpec{
  277. Spec: v1.PodSpec{
  278. DNSPolicy: v1.DNSClusterFirst,
  279. RestartPolicy: v1.RestartPolicyAlways,
  280. SecurityContext: &v1.PodSecurityContext{},
  281. TerminationGracePeriodSeconds: &period,
  282. SchedulerName: api.DefaultSchedulerName,
  283. },
  284. }
  285. tests := []struct {
  286. original *appsv1beta2.Deployment
  287. expected *appsv1beta2.Deployment
  288. }{
  289. {
  290. original: &appsv1beta2.Deployment{},
  291. expected: &appsv1beta2.Deployment{
  292. Spec: appsv1beta2.DeploymentSpec{
  293. Replicas: utilpointer.Int32Ptr(1),
  294. Strategy: appsv1beta2.DeploymentStrategy{
  295. Type: appsv1beta2.RollingUpdateDeploymentStrategyType,
  296. RollingUpdate: &appsv1beta2.RollingUpdateDeployment{
  297. MaxSurge: &defaultIntOrString,
  298. MaxUnavailable: &defaultIntOrString,
  299. },
  300. },
  301. RevisionHistoryLimit: utilpointer.Int32Ptr(10),
  302. ProgressDeadlineSeconds: utilpointer.Int32Ptr(600),
  303. Template: defaultTemplate,
  304. },
  305. },
  306. },
  307. {
  308. original: &appsv1beta2.Deployment{
  309. Spec: appsv1beta2.DeploymentSpec{
  310. Replicas: utilpointer.Int32Ptr(5),
  311. Strategy: appsv1beta2.DeploymentStrategy{
  312. RollingUpdate: &appsv1beta2.RollingUpdateDeployment{
  313. MaxSurge: &differentIntOrString,
  314. },
  315. },
  316. },
  317. },
  318. expected: &appsv1beta2.Deployment{
  319. Spec: appsv1beta2.DeploymentSpec{
  320. Replicas: utilpointer.Int32Ptr(5),
  321. Strategy: appsv1beta2.DeploymentStrategy{
  322. Type: appsv1beta2.RollingUpdateDeploymentStrategyType,
  323. RollingUpdate: &appsv1beta2.RollingUpdateDeployment{
  324. MaxSurge: &differentIntOrString,
  325. MaxUnavailable: &defaultIntOrString,
  326. },
  327. },
  328. RevisionHistoryLimit: utilpointer.Int32Ptr(10),
  329. ProgressDeadlineSeconds: utilpointer.Int32Ptr(600),
  330. Template: defaultTemplate,
  331. },
  332. },
  333. },
  334. {
  335. original: &appsv1beta2.Deployment{
  336. Spec: appsv1beta2.DeploymentSpec{
  337. Replicas: utilpointer.Int32Ptr(3),
  338. Strategy: appsv1beta2.DeploymentStrategy{
  339. Type: appsv1beta2.RollingUpdateDeploymentStrategyType,
  340. RollingUpdate: nil,
  341. },
  342. },
  343. },
  344. expected: &appsv1beta2.Deployment{
  345. Spec: appsv1beta2.DeploymentSpec{
  346. Replicas: utilpointer.Int32Ptr(3),
  347. Strategy: appsv1beta2.DeploymentStrategy{
  348. Type: appsv1beta2.RollingUpdateDeploymentStrategyType,
  349. RollingUpdate: &appsv1beta2.RollingUpdateDeployment{
  350. MaxSurge: &defaultIntOrString,
  351. MaxUnavailable: &defaultIntOrString,
  352. },
  353. },
  354. RevisionHistoryLimit: utilpointer.Int32Ptr(10),
  355. ProgressDeadlineSeconds: utilpointer.Int32Ptr(600),
  356. Template: defaultTemplate,
  357. },
  358. },
  359. },
  360. {
  361. original: &appsv1beta2.Deployment{
  362. Spec: appsv1beta2.DeploymentSpec{
  363. Replicas: utilpointer.Int32Ptr(5),
  364. Strategy: appsv1beta2.DeploymentStrategy{
  365. Type: appsv1beta2.RecreateDeploymentStrategyType,
  366. },
  367. RevisionHistoryLimit: utilpointer.Int32Ptr(0),
  368. },
  369. },
  370. expected: &appsv1beta2.Deployment{
  371. Spec: appsv1beta2.DeploymentSpec{
  372. Replicas: utilpointer.Int32Ptr(5),
  373. Strategy: appsv1beta2.DeploymentStrategy{
  374. Type: appsv1beta2.RecreateDeploymentStrategyType,
  375. },
  376. RevisionHistoryLimit: utilpointer.Int32Ptr(0),
  377. ProgressDeadlineSeconds: utilpointer.Int32Ptr(600),
  378. Template: defaultTemplate,
  379. },
  380. },
  381. },
  382. {
  383. original: &appsv1beta2.Deployment{
  384. Spec: appsv1beta2.DeploymentSpec{
  385. Replicas: utilpointer.Int32Ptr(5),
  386. Strategy: appsv1beta2.DeploymentStrategy{
  387. Type: appsv1beta2.RecreateDeploymentStrategyType,
  388. },
  389. ProgressDeadlineSeconds: utilpointer.Int32Ptr(30),
  390. RevisionHistoryLimit: utilpointer.Int32Ptr(2),
  391. },
  392. },
  393. expected: &appsv1beta2.Deployment{
  394. Spec: appsv1beta2.DeploymentSpec{
  395. Replicas: utilpointer.Int32Ptr(5),
  396. Strategy: appsv1beta2.DeploymentStrategy{
  397. Type: appsv1beta2.RecreateDeploymentStrategyType,
  398. },
  399. ProgressDeadlineSeconds: utilpointer.Int32Ptr(30),
  400. RevisionHistoryLimit: utilpointer.Int32Ptr(2),
  401. Template: defaultTemplate,
  402. },
  403. },
  404. },
  405. }
  406. for _, test := range tests {
  407. original := test.original
  408. expected := test.expected
  409. obj2 := roundTrip(t, runtime.Object(original))
  410. got, ok := obj2.(*appsv1beta2.Deployment)
  411. if !ok {
  412. t.Errorf("unexpected object: %v", got)
  413. t.FailNow()
  414. }
  415. if !apiequality.Semantic.DeepEqual(got.Spec, expected.Spec) {
  416. t.Errorf("object mismatch!\nexpected:\n\t%+v\ngot:\n\t%+v", got.Spec, expected.Spec)
  417. }
  418. }
  419. }
  420. func TestDefaultDeploymentAvailability(t *testing.T) {
  421. d := roundTrip(t, runtime.Object(&appsv1beta2.Deployment{})).(*appsv1beta2.Deployment)
  422. maxUnavailable, err := intstr.GetValueFromIntOrPercent(d.Spec.Strategy.RollingUpdate.MaxUnavailable, int(*(d.Spec.Replicas)), false)
  423. if err != nil {
  424. t.Fatalf("unexpected error: %v", err)
  425. }
  426. if *(d.Spec.Replicas)-int32(maxUnavailable) <= 0 {
  427. t.Fatalf("the default value of maxUnavailable can lead to no active replicas during rolling update")
  428. }
  429. }
  430. func TestSetDefaultReplicaSetReplicas(t *testing.T) {
  431. tests := []struct {
  432. rs appsv1beta2.ReplicaSet
  433. expectReplicas int32
  434. }{
  435. {
  436. rs: appsv1beta2.ReplicaSet{
  437. Spec: appsv1beta2.ReplicaSetSpec{
  438. Template: v1.PodTemplateSpec{
  439. ObjectMeta: metav1.ObjectMeta{
  440. Labels: map[string]string{
  441. "foo": "bar",
  442. },
  443. },
  444. },
  445. },
  446. },
  447. expectReplicas: 1,
  448. },
  449. {
  450. rs: appsv1beta2.ReplicaSet{
  451. Spec: appsv1beta2.ReplicaSetSpec{
  452. Replicas: utilpointer.Int32Ptr(0),
  453. Template: v1.PodTemplateSpec{
  454. ObjectMeta: metav1.ObjectMeta{
  455. Labels: map[string]string{
  456. "foo": "bar",
  457. },
  458. },
  459. },
  460. },
  461. },
  462. expectReplicas: 0,
  463. },
  464. {
  465. rs: appsv1beta2.ReplicaSet{
  466. Spec: appsv1beta2.ReplicaSetSpec{
  467. Replicas: utilpointer.Int32Ptr(3),
  468. Template: v1.PodTemplateSpec{
  469. ObjectMeta: metav1.ObjectMeta{
  470. Labels: map[string]string{
  471. "foo": "bar",
  472. },
  473. },
  474. },
  475. },
  476. },
  477. expectReplicas: 3,
  478. },
  479. }
  480. for _, test := range tests {
  481. rs := &test.rs
  482. obj2 := roundTrip(t, runtime.Object(rs))
  483. rs2, ok := obj2.(*appsv1beta2.ReplicaSet)
  484. if !ok {
  485. t.Errorf("unexpected object: %v", rs2)
  486. t.FailNow()
  487. }
  488. if rs2.Spec.Replicas == nil {
  489. t.Errorf("unexpected nil Replicas")
  490. } else if test.expectReplicas != *rs2.Spec.Replicas {
  491. t.Errorf("expected: %d replicas, got: %d", test.expectReplicas, *rs2.Spec.Replicas)
  492. }
  493. }
  494. }
  495. func TestDefaultRequestIsNotSetForReplicaSet(t *testing.T) {
  496. s := v1.PodSpec{}
  497. s.Containers = []v1.Container{
  498. {
  499. Resources: v1.ResourceRequirements{
  500. Limits: v1.ResourceList{
  501. v1.ResourceCPU: resource.MustParse("100m"),
  502. },
  503. },
  504. },
  505. }
  506. rs := &appsv1beta2.ReplicaSet{
  507. Spec: appsv1beta2.ReplicaSetSpec{
  508. Replicas: utilpointer.Int32Ptr(3),
  509. Template: v1.PodTemplateSpec{
  510. ObjectMeta: metav1.ObjectMeta{
  511. Labels: map[string]string{
  512. "foo": "bar",
  513. },
  514. },
  515. Spec: s,
  516. },
  517. },
  518. }
  519. output := roundTrip(t, runtime.Object(rs))
  520. rs2 := output.(*appsv1beta2.ReplicaSet)
  521. defaultRequest := rs2.Spec.Template.Spec.Containers[0].Resources.Requests
  522. requestValue := defaultRequest[v1.ResourceCPU]
  523. if requestValue.String() != "0" {
  524. t.Errorf("Expected 0 request value, got: %s", requestValue.String())
  525. }
  526. }
  527. func roundTrip(t *testing.T, obj runtime.Object) runtime.Object {
  528. data, err := runtime.Encode(legacyscheme.Codecs.LegacyCodec(SchemeGroupVersion), obj)
  529. if err != nil {
  530. t.Errorf("%v\n %#v", err, obj)
  531. return nil
  532. }
  533. obj2, err := runtime.Decode(legacyscheme.Codecs.UniversalDecoder(), data)
  534. if err != nil {
  535. t.Errorf("%v\nData: %s\nSource: %#v", err, string(data), obj)
  536. return nil
  537. }
  538. obj3 := reflect.New(reflect.TypeOf(obj).Elem()).Interface().(runtime.Object)
  539. err = legacyscheme.Scheme.Convert(obj2, obj3, nil)
  540. if err != nil {
  541. t.Errorf("%v\nSource: %#v", err, obj2)
  542. return nil
  543. }
  544. return obj3
  545. }