apply_test.go 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889
  1. /*
  2. Copyright 2018 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 apiserver
  14. import (
  15. "encoding/json"
  16. "net/http"
  17. "net/http/httptest"
  18. "reflect"
  19. "testing"
  20. "time"
  21. "k8s.io/apimachinery/pkg/api/errors"
  22. "k8s.io/apimachinery/pkg/api/meta"
  23. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  24. "k8s.io/apimachinery/pkg/runtime/schema"
  25. "k8s.io/apimachinery/pkg/types"
  26. genericfeatures "k8s.io/apiserver/pkg/features"
  27. utilfeature "k8s.io/apiserver/pkg/util/feature"
  28. clientset "k8s.io/client-go/kubernetes"
  29. restclient "k8s.io/client-go/rest"
  30. featuregatetesting "k8s.io/component-base/featuregate/testing"
  31. "k8s.io/kubernetes/pkg/master"
  32. "k8s.io/kubernetes/test/integration/framework"
  33. )
  34. func setup(t *testing.T, groupVersions ...schema.GroupVersion) (*httptest.Server, clientset.Interface, framework.CloseFunc) {
  35. masterConfig := framework.NewIntegrationTestMasterConfig()
  36. if len(groupVersions) > 0 {
  37. resourceConfig := master.DefaultAPIResourceConfigSource()
  38. resourceConfig.EnableVersions(groupVersions...)
  39. masterConfig.ExtraConfig.APIResourceConfigSource = resourceConfig
  40. }
  41. masterConfig.GenericConfig.OpenAPIConfig = framework.DefaultOpenAPIConfig()
  42. _, s, closeFn := framework.RunAMaster(masterConfig)
  43. clientSet, err := clientset.NewForConfig(&restclient.Config{Host: s.URL})
  44. if err != nil {
  45. t.Fatalf("Error in create clientset: %v", err)
  46. }
  47. return s, clientSet, closeFn
  48. }
  49. // TestApplyAlsoCreates makes sure that PATCH requests with the apply content type
  50. // will create the object if it doesn't already exist
  51. // TODO: make a set of test cases in an easy-to-consume place (separate package?) so it's easy to test in both integration and e2e.
  52. func TestApplyAlsoCreates(t *testing.T) {
  53. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ServerSideApply, true)()
  54. _, client, closeFn := setup(t)
  55. defer closeFn()
  56. testCases := []struct {
  57. resource string
  58. name string
  59. body string
  60. }{
  61. {
  62. resource: "pods",
  63. name: "test-pod",
  64. body: `{
  65. "apiVersion": "v1",
  66. "kind": "Pod",
  67. "metadata": {
  68. "name": "test-pod"
  69. },
  70. "spec": {
  71. "containers": [{
  72. "name": "test-container",
  73. "image": "test-image"
  74. }]
  75. }
  76. }`,
  77. }, {
  78. resource: "services",
  79. name: "test-svc",
  80. body: `{
  81. "apiVersion": "v1",
  82. "kind": "Service",
  83. "metadata": {
  84. "name": "test-svc"
  85. },
  86. "spec": {
  87. "ports": [{
  88. "port": 8080,
  89. "protocol": "UDP"
  90. }]
  91. }
  92. }`,
  93. },
  94. }
  95. for _, tc := range testCases {
  96. _, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  97. Namespace("default").
  98. Resource(tc.resource).
  99. Name(tc.name).
  100. Param("fieldManager", "apply_test").
  101. Body([]byte(tc.body)).
  102. Do().
  103. Get()
  104. if err != nil {
  105. t.Fatalf("Failed to create object using Apply patch: %v", err)
  106. }
  107. _, err = client.CoreV1().RESTClient().Get().Namespace("default").Resource(tc.resource).Name(tc.name).Do().Get()
  108. if err != nil {
  109. t.Fatalf("Failed to retrieve object: %v", err)
  110. }
  111. // Test that we can re apply with a different field manager and don't get conflicts
  112. _, err = client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  113. Namespace("default").
  114. Resource(tc.resource).
  115. Name(tc.name).
  116. Param("fieldManager", "apply_test_2").
  117. Body([]byte(tc.body)).
  118. Do().
  119. Get()
  120. if err != nil {
  121. t.Fatalf("Failed to re-apply object using Apply patch: %v", err)
  122. }
  123. }
  124. }
  125. // TestCreateOnApplyFailsWithUID makes sure that PATCH requests with the apply content type
  126. // will not create the object if it doesn't already exist and it specifies a UID
  127. func TestCreateOnApplyFailsWithUID(t *testing.T) {
  128. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ServerSideApply, true)()
  129. _, client, closeFn := setup(t)
  130. defer closeFn()
  131. _, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  132. Namespace("default").
  133. Resource("pods").
  134. Name("test-pod-uid").
  135. Param("fieldManager", "apply_test").
  136. Body([]byte(`{
  137. "apiVersion": "v1",
  138. "kind": "Pod",
  139. "metadata": {
  140. "name": "test-pod-uid",
  141. "uid": "88e00824-7f0e-11e8-94a1-c8d3ffb15800"
  142. },
  143. "spec": {
  144. "containers": [{
  145. "name": "test-container",
  146. "image": "test-image"
  147. }]
  148. }
  149. }`)).
  150. Do().
  151. Get()
  152. if !errors.IsConflict(err) {
  153. t.Fatalf("Expected conflict error but got: %v", err)
  154. }
  155. }
  156. func TestApplyUpdateApplyConflictForced(t *testing.T) {
  157. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ServerSideApply, true)()
  158. _, client, closeFn := setup(t)
  159. defer closeFn()
  160. obj := []byte(`{
  161. "apiVersion": "apps/v1",
  162. "kind": "Deployment",
  163. "metadata": {
  164. "name": "deployment",
  165. "labels": {"app": "nginx"}
  166. },
  167. "spec": {
  168. "replicas": 3,
  169. "selector": {
  170. "matchLabels": {
  171. "app": "nginx"
  172. }
  173. },
  174. "template": {
  175. "metadata": {
  176. "labels": {
  177. "app": "nginx"
  178. }
  179. },
  180. "spec": {
  181. "containers": [{
  182. "name": "nginx",
  183. "image": "nginx:latest"
  184. }]
  185. }
  186. }
  187. }
  188. }`)
  189. _, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  190. AbsPath("/apis/apps/v1").
  191. Namespace("default").
  192. Resource("deployments").
  193. Name("deployment").
  194. Param("fieldManager", "apply_test").
  195. Body(obj).Do().Get()
  196. if err != nil {
  197. t.Fatalf("Failed to create object using Apply patch: %v", err)
  198. }
  199. _, err = client.CoreV1().RESTClient().Patch(types.MergePatchType).
  200. AbsPath("/apis/apps/v1").
  201. Namespace("default").
  202. Resource("deployments").
  203. Name("deployment").
  204. Body([]byte(`{"spec":{"replicas": 5}}`)).Do().Get()
  205. if err != nil {
  206. t.Fatalf("Failed to patch object: %v", err)
  207. }
  208. _, err = client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  209. AbsPath("/apis/apps/v1").
  210. Namespace("default").
  211. Resource("deployments").
  212. Name("deployment").
  213. Param("fieldManager", "apply_test").
  214. Body([]byte(obj)).Do().Get()
  215. if err == nil {
  216. t.Fatalf("Expecting to get conflicts when applying object")
  217. }
  218. status, ok := err.(*errors.StatusError)
  219. if !ok {
  220. t.Fatalf("Expecting to get conflicts as API error")
  221. }
  222. if len(status.Status().Details.Causes) < 1 {
  223. t.Fatalf("Expecting to get at least one conflict when applying object, got: %v", status.Status().Details.Causes)
  224. }
  225. _, err = client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  226. AbsPath("/apis/apps/v1").
  227. Namespace("default").
  228. Resource("deployments").
  229. Name("deployment").
  230. Param("force", "true").
  231. Param("fieldManager", "apply_test").
  232. Body([]byte(obj)).Do().Get()
  233. if err != nil {
  234. t.Fatalf("Failed to apply object with force: %v", err)
  235. }
  236. }
  237. // TestApplyManagedFields makes sure that managedFields api does not change
  238. func TestApplyManagedFields(t *testing.T) {
  239. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ServerSideApply, true)()
  240. _, client, closeFn := setup(t)
  241. defer closeFn()
  242. _, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  243. Namespace("default").
  244. Resource("configmaps").
  245. Name("test-cm").
  246. Param("fieldManager", "apply_test").
  247. Body([]byte(`{
  248. "apiVersion": "v1",
  249. "kind": "ConfigMap",
  250. "metadata": {
  251. "name": "test-cm",
  252. "namespace": "default",
  253. "labels": {
  254. "test-label": "test"
  255. }
  256. },
  257. "data": {
  258. "key": "value"
  259. }
  260. }`)).
  261. Do().
  262. Get()
  263. if err != nil {
  264. t.Fatalf("Failed to create object using Apply patch: %v", err)
  265. }
  266. _, err = client.CoreV1().RESTClient().Patch(types.MergePatchType).
  267. Namespace("default").
  268. Resource("configmaps").
  269. Name("test-cm").
  270. Param("fieldManager", "updater").
  271. Body([]byte(`{"data":{"key": "new value"}}`)).Do().Get()
  272. if err != nil {
  273. t.Fatalf("Failed to patch object: %v", err)
  274. }
  275. object, err := client.CoreV1().RESTClient().Get().Namespace("default").Resource("configmaps").Name("test-cm").Do().Get()
  276. if err != nil {
  277. t.Fatalf("Failed to retrieve object: %v", err)
  278. }
  279. accessor, err := meta.Accessor(object)
  280. if err != nil {
  281. t.Fatalf("Failed to get meta accessor: %v", err)
  282. }
  283. actual, err := json.MarshalIndent(object, "\t", "\t")
  284. if err != nil {
  285. t.Fatalf("Failed to marshal object: %v", err)
  286. }
  287. expected := []byte(`{
  288. "metadata": {
  289. "name": "test-cm",
  290. "namespace": "default",
  291. "selfLink": "` + accessor.GetSelfLink() + `",
  292. "uid": "` + string(accessor.GetUID()) + `",
  293. "resourceVersion": "` + accessor.GetResourceVersion() + `",
  294. "creationTimestamp": "` + accessor.GetCreationTimestamp().UTC().Format(time.RFC3339) + `",
  295. "labels": {
  296. "test-label": "test"
  297. },
  298. "managedFields": [
  299. {
  300. "manager": "apply_test",
  301. "operation": "Apply",
  302. "apiVersion": "v1",
  303. "fields": {
  304. "f:metadata": {
  305. "f:labels": {
  306. "f:test-label": {}
  307. }
  308. }
  309. }
  310. },
  311. {
  312. "manager": "updater",
  313. "operation": "Update",
  314. "apiVersion": "v1",
  315. "time": "` + accessor.GetManagedFields()[1].Time.UTC().Format(time.RFC3339) + `",
  316. "fields": {
  317. "f:data": {
  318. "f:key": {}
  319. }
  320. }
  321. }
  322. ]
  323. },
  324. "data": {
  325. "key": "new value"
  326. }
  327. }`)
  328. if string(expected) != string(actual) {
  329. t.Fatalf("Expected:\n%v\nGot:\n%v", string(expected), string(actual))
  330. }
  331. }
  332. // TestApplyRemovesEmptyManagedFields there are no empty managers in managedFields
  333. func TestApplyRemovesEmptyManagedFields(t *testing.T) {
  334. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ServerSideApply, true)()
  335. _, client, closeFn := setup(t)
  336. defer closeFn()
  337. obj := []byte(`{
  338. "apiVersion": "v1",
  339. "kind": "ConfigMap",
  340. "metadata": {
  341. "name": "test-cm",
  342. "namespace": "default"
  343. }
  344. }`)
  345. _, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  346. Namespace("default").
  347. Resource("configmaps").
  348. Name("test-cm").
  349. Param("fieldManager", "apply_test").
  350. Body(obj).
  351. Do().
  352. Get()
  353. if err != nil {
  354. t.Fatalf("Failed to create object using Apply patch: %v", err)
  355. }
  356. _, err = client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  357. Namespace("default").
  358. Resource("configmaps").
  359. Name("test-cm").
  360. Param("fieldManager", "apply_test").
  361. Body(obj).Do().Get()
  362. if err != nil {
  363. t.Fatalf("Failed to patch object: %v", err)
  364. }
  365. object, err := client.CoreV1().RESTClient().Get().Namespace("default").Resource("configmaps").Name("test-cm").Do().Get()
  366. if err != nil {
  367. t.Fatalf("Failed to retrieve object: %v", err)
  368. }
  369. accessor, err := meta.Accessor(object)
  370. if err != nil {
  371. t.Fatalf("Failed to get meta accessor: %v", err)
  372. }
  373. if managed := accessor.GetManagedFields(); managed != nil {
  374. t.Fatalf("Object contains unexpected managedFields: %v", managed)
  375. }
  376. }
  377. func TestApplyRequiresFieldManager(t *testing.T) {
  378. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ServerSideApply, true)()
  379. _, client, closeFn := setup(t)
  380. defer closeFn()
  381. obj := []byte(`{
  382. "apiVersion": "v1",
  383. "kind": "ConfigMap",
  384. "metadata": {
  385. "name": "test-cm",
  386. "namespace": "default"
  387. }
  388. }`)
  389. _, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  390. Namespace("default").
  391. Resource("configmaps").
  392. Name("test-cm").
  393. Body(obj).
  394. Do().
  395. Get()
  396. if err == nil {
  397. t.Fatalf("Apply should fail to create without fieldManager")
  398. }
  399. _, err = client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  400. Namespace("default").
  401. Resource("configmaps").
  402. Name("test-cm").
  403. Param("fieldManager", "apply_test").
  404. Body(obj).
  405. Do().
  406. Get()
  407. if err != nil {
  408. t.Fatalf("Apply failed to create with fieldManager: %v", err)
  409. }
  410. }
  411. // TestApplyRemoveContainerPort removes a container port from a deployment
  412. func TestApplyRemoveContainerPort(t *testing.T) {
  413. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ServerSideApply, true)()
  414. _, client, closeFn := setup(t)
  415. defer closeFn()
  416. obj := []byte(`{
  417. "apiVersion": "apps/v1",
  418. "kind": "Deployment",
  419. "metadata": {
  420. "name": "deployment",
  421. "labels": {"app": "nginx"}
  422. },
  423. "spec": {
  424. "replicas": 3,
  425. "selector": {
  426. "matchLabels": {
  427. "app": "nginx"
  428. }
  429. },
  430. "template": {
  431. "metadata": {
  432. "labels": {
  433. "app": "nginx"
  434. }
  435. },
  436. "spec": {
  437. "containers": [{
  438. "name": "nginx",
  439. "image": "nginx:latest",
  440. "ports": [{
  441. "containerPort": 80,
  442. "protocol": "TCP"
  443. }]
  444. }]
  445. }
  446. }
  447. }
  448. }`)
  449. _, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  450. AbsPath("/apis/apps/v1").
  451. Namespace("default").
  452. Resource("deployments").
  453. Name("deployment").
  454. Param("fieldManager", "apply_test").
  455. Body(obj).Do().Get()
  456. if err != nil {
  457. t.Fatalf("Failed to create object using Apply patch: %v", err)
  458. }
  459. obj = []byte(`{
  460. "apiVersion": "apps/v1",
  461. "kind": "Deployment",
  462. "metadata": {
  463. "name": "deployment",
  464. "labels": {"app": "nginx"}
  465. },
  466. "spec": {
  467. "replicas": 3,
  468. "selector": {
  469. "matchLabels": {
  470. "app": "nginx"
  471. }
  472. },
  473. "template": {
  474. "metadata": {
  475. "labels": {
  476. "app": "nginx"
  477. }
  478. },
  479. "spec": {
  480. "containers": [{
  481. "name": "nginx",
  482. "image": "nginx:latest"
  483. }]
  484. }
  485. }
  486. }
  487. }`)
  488. _, err = client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  489. AbsPath("/apis/apps/v1").
  490. Namespace("default").
  491. Resource("deployments").
  492. Name("deployment").
  493. Param("fieldManager", "apply_test").
  494. Body(obj).Do().Get()
  495. if err != nil {
  496. t.Fatalf("Failed to remove container port using Apply patch: %v", err)
  497. }
  498. deployment, err := client.AppsV1().Deployments("default").Get("deployment", metav1.GetOptions{})
  499. if err != nil {
  500. t.Fatalf("Failed to retrieve object: %v", err)
  501. }
  502. if len(deployment.Spec.Template.Spec.Containers[0].Ports) > 0 {
  503. t.Fatalf("Expected no container ports but got: %v", deployment.Spec.Template.Spec.Containers[0].Ports)
  504. }
  505. }
  506. // TestApplyFailsWithVersionMismatch ensures that a version mismatch between the
  507. // patch object and the live object will error
  508. func TestApplyFailsWithVersionMismatch(t *testing.T) {
  509. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ServerSideApply, true)()
  510. _, client, closeFn := setup(t)
  511. defer closeFn()
  512. obj := []byte(`{
  513. "apiVersion": "apps/v1",
  514. "kind": "Deployment",
  515. "metadata": {
  516. "name": "deployment",
  517. "labels": {"app": "nginx"}
  518. },
  519. "spec": {
  520. "replicas": 3,
  521. "selector": {
  522. "matchLabels": {
  523. "app": "nginx"
  524. }
  525. },
  526. "template": {
  527. "metadata": {
  528. "labels": {
  529. "app": "nginx"
  530. }
  531. },
  532. "spec": {
  533. "containers": [{
  534. "name": "nginx",
  535. "image": "nginx:latest"
  536. }]
  537. }
  538. }
  539. }
  540. }`)
  541. _, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  542. AbsPath("/apis/apps/v1").
  543. Namespace("default").
  544. Resource("deployments").
  545. Name("deployment").
  546. Param("fieldManager", "apply_test").
  547. Body(obj).Do().Get()
  548. if err != nil {
  549. t.Fatalf("Failed to create object using Apply patch: %v", err)
  550. }
  551. obj = []byte(`{
  552. "apiVersion": "extensions/v1beta",
  553. "kind": "Deployment",
  554. "metadata": {
  555. "name": "deployment",
  556. "labels": {"app": "nginx"}
  557. },
  558. "spec": {
  559. "replicas": 100,
  560. "selector": {
  561. "matchLabels": {
  562. "app": "nginx"
  563. }
  564. },
  565. "template": {
  566. "metadata": {
  567. "labels": {
  568. "app": "nginx"
  569. }
  570. },
  571. "spec": {
  572. "containers": [{
  573. "name": "nginx",
  574. "image": "nginx:latest"
  575. }]
  576. }
  577. }
  578. }
  579. }`)
  580. _, err = client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  581. AbsPath("/apis/apps/v1").
  582. Namespace("default").
  583. Resource("deployments").
  584. Name("deployment").
  585. Param("fieldManager", "apply_test").
  586. Body([]byte(obj)).Do().Get()
  587. if err == nil {
  588. t.Fatalf("Expecting to get version mismatch when applying object")
  589. }
  590. status, ok := err.(*errors.StatusError)
  591. if !ok {
  592. t.Fatalf("Expecting to get version mismatch as API error")
  593. }
  594. if status.Status().Code != http.StatusBadRequest {
  595. t.Fatalf("expected status code to be %d but was %d", http.StatusBadRequest, status.Status().Code)
  596. }
  597. }
  598. // TestApplyConvertsManagedFieldsVersion checks that the apply
  599. // converts the API group-version in the field manager
  600. func TestApplyConvertsManagedFieldsVersion(t *testing.T) {
  601. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ServerSideApply, true)()
  602. _, client, closeFn := setup(t)
  603. defer closeFn()
  604. obj := []byte(`{
  605. "apiVersion": "apps/v1",
  606. "kind": "Deployment",
  607. "metadata": {
  608. "name": "deployment",
  609. "labels": {"app": "nginx"},
  610. "managedFields": [
  611. {
  612. "manager": "sidecar_controller",
  613. "operation": "Apply",
  614. "apiVersion": "extensions/v1beta1",
  615. "fields": {
  616. "f:metadata": {
  617. "f:labels": {
  618. "f:sidecar_version": {}
  619. }
  620. },
  621. "f:spec": {
  622. "f:template": {
  623. "f: spec": {
  624. "f:containers": {
  625. "k:{\"name\":\"sidecar\"}": {
  626. ".": {},
  627. "f:image": {}
  628. }
  629. }
  630. }
  631. }
  632. }
  633. }
  634. }
  635. ]
  636. },
  637. "spec": {
  638. "selector": {
  639. "matchLabels": {
  640. "app": "nginx"
  641. }
  642. },
  643. "template": {
  644. "metadata": {
  645. "labels": {
  646. "app": "nginx"
  647. }
  648. },
  649. "spec": {
  650. "containers": [{
  651. "name": "nginx",
  652. "image": "nginx:latest"
  653. }]
  654. }
  655. }
  656. }
  657. }`)
  658. _, err := client.CoreV1().RESTClient().Post().
  659. AbsPath("/apis/apps/v1").
  660. Namespace("default").
  661. Resource("deployments").
  662. Body(obj).Do().Get()
  663. if err != nil {
  664. t.Fatalf("Failed to create object: %v", err)
  665. }
  666. obj = []byte(`{
  667. "apiVersion": "apps/v1",
  668. "kind": "Deployment",
  669. "metadata": {
  670. "name": "deployment",
  671. "labels": {"sidecar_version": "release"}
  672. },
  673. "spec": {
  674. "template": {
  675. "spec": {
  676. "containers": [{
  677. "name": "sidecar",
  678. "image": "sidecar:latest"
  679. }]
  680. }
  681. }
  682. }
  683. }`)
  684. _, err = client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  685. AbsPath("/apis/apps/v1").
  686. Namespace("default").
  687. Resource("deployments").
  688. Name("deployment").
  689. Param("fieldManager", "sidecar_controller").
  690. Body([]byte(obj)).Do().Get()
  691. if err != nil {
  692. t.Fatalf("Failed to apply object: %v", err)
  693. }
  694. object, err := client.AppsV1().Deployments("default").Get("deployment", metav1.GetOptions{})
  695. if err != nil {
  696. t.Fatalf("Failed to retrieve object: %v", err)
  697. }
  698. accessor, err := meta.Accessor(object)
  699. if err != nil {
  700. t.Fatalf("Failed to get meta accessor: %v", err)
  701. }
  702. managed := accessor.GetManagedFields()
  703. if len(managed) != 2 {
  704. t.Fatalf("Expected 2 field managers, but got managed fields: %v", managed)
  705. }
  706. var actual *metav1.ManagedFieldsEntry
  707. for i := range managed {
  708. entry := &managed[i]
  709. if entry.Manager == "sidecar_controller" && entry.APIVersion == "apps/v1" {
  710. actual = entry
  711. }
  712. }
  713. if actual == nil {
  714. t.Fatalf("Expected managed fields to contain entry with manager '%v' with converted api version '%v', but got managed fields:\n%v", "sidecar_controller", "apps/v1", managed)
  715. }
  716. expected := &metav1.ManagedFieldsEntry{
  717. Manager: "sidecar_controller",
  718. Operation: metav1.ManagedFieldsOperationApply,
  719. APIVersion: "apps/v1",
  720. Time: actual.Time,
  721. Fields: &metav1.Fields{
  722. Map: map[string]metav1.Fields{
  723. "f:metadata": {
  724. Map: map[string]metav1.Fields{
  725. "f:labels": {
  726. Map: map[string]metav1.Fields{
  727. "f:sidecar_version": {Map: map[string]metav1.Fields{}},
  728. },
  729. },
  730. },
  731. },
  732. "f:spec": {
  733. Map: map[string]metav1.Fields{
  734. "f:template": {
  735. Map: map[string]metav1.Fields{
  736. "f:spec": {
  737. Map: map[string]metav1.Fields{
  738. "f:containers": {
  739. Map: map[string]metav1.Fields{
  740. "k:{\"name\":\"sidecar\"}": {
  741. Map: map[string]metav1.Fields{
  742. ".": {Map: map[string]metav1.Fields{}},
  743. "f:image": {Map: map[string]metav1.Fields{}},
  744. "f:name": {Map: map[string]metav1.Fields{}},
  745. },
  746. },
  747. },
  748. },
  749. },
  750. },
  751. },
  752. },
  753. },
  754. },
  755. },
  756. },
  757. }
  758. if !reflect.DeepEqual(actual, expected) {
  759. t.Fatalf("expected:\n%v\nbut got:\n%v", expected, actual)
  760. }
  761. }
  762. // TestClearManagedFields verifies it's possible to clear the managedFields
  763. func TestClearManagedFields(t *testing.T) {
  764. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ServerSideApply, true)()
  765. _, client, closeFn := setup(t)
  766. defer closeFn()
  767. _, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  768. Namespace("default").
  769. Resource("configmaps").
  770. Name("test-cm").
  771. Param("fieldManager", "apply_test").
  772. Body([]byte(`{
  773. "apiVersion": "v1",
  774. "kind": "ConfigMap",
  775. "metadata": {
  776. "name": "test-cm",
  777. "namespace": "default",
  778. "labels": {
  779. "test-label": "test"
  780. }
  781. },
  782. "data": {
  783. "key": "value"
  784. }
  785. }`)).
  786. Do().
  787. Get()
  788. if err != nil {
  789. t.Fatalf("Failed to create object using Apply patch: %v", err)
  790. }
  791. _, err = client.CoreV1().RESTClient().Patch(types.MergePatchType).
  792. Namespace("default").
  793. Resource("configmaps").
  794. Name("test-cm").
  795. Body([]byte(`{"metadata":{"managedFields": [{}]}}`)).Do().Get()
  796. if err != nil {
  797. t.Fatalf("Failed to patch object: %v", err)
  798. }
  799. object, err := client.CoreV1().RESTClient().Get().Namespace("default").Resource("configmaps").Name("test-cm").Do().Get()
  800. if err != nil {
  801. t.Fatalf("Failed to retrieve object: %v", err)
  802. }
  803. accessor, err := meta.Accessor(object)
  804. if err != nil {
  805. t.Fatalf("Failed to get meta accessor: %v", err)
  806. }
  807. if managedFields := accessor.GetManagedFields(); len(managedFields) != 0 {
  808. t.Fatalf("Failed to clear managedFields, got: %v", managedFields)
  809. }
  810. }