apply_test.go 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603
  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. "context"
  16. "encoding/json"
  17. "flag"
  18. "fmt"
  19. "net/http"
  20. "net/http/httptest"
  21. "reflect"
  22. "strings"
  23. "testing"
  24. "time"
  25. "sigs.k8s.io/yaml"
  26. v1 "k8s.io/api/core/v1"
  27. apierrors "k8s.io/apimachinery/pkg/api/errors"
  28. "k8s.io/apimachinery/pkg/api/meta"
  29. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  30. "k8s.io/apimachinery/pkg/runtime/schema"
  31. "k8s.io/apimachinery/pkg/types"
  32. genericfeatures "k8s.io/apiserver/pkg/features"
  33. utilfeature "k8s.io/apiserver/pkg/util/feature"
  34. "k8s.io/client-go/kubernetes"
  35. clientset "k8s.io/client-go/kubernetes"
  36. restclient "k8s.io/client-go/rest"
  37. featuregatetesting "k8s.io/component-base/featuregate/testing"
  38. "k8s.io/kubernetes/pkg/master"
  39. "k8s.io/kubernetes/test/integration/framework"
  40. )
  41. func setup(t testing.TB, groupVersions ...schema.GroupVersion) (*httptest.Server, clientset.Interface, framework.CloseFunc) {
  42. opts := framework.MasterConfigOptions{EtcdOptions: framework.DefaultEtcdOptions()}
  43. opts.EtcdOptions.DefaultStorageMediaType = "application/vnd.kubernetes.protobuf"
  44. masterConfig := framework.NewIntegrationTestMasterConfigWithOptions(&opts)
  45. if len(groupVersions) > 0 {
  46. resourceConfig := master.DefaultAPIResourceConfigSource()
  47. resourceConfig.EnableVersions(groupVersions...)
  48. masterConfig.ExtraConfig.APIResourceConfigSource = resourceConfig
  49. }
  50. masterConfig.GenericConfig.OpenAPIConfig = framework.DefaultOpenAPIConfig()
  51. _, s, closeFn := framework.RunAMaster(masterConfig)
  52. clientSet, err := clientset.NewForConfig(&restclient.Config{Host: s.URL, QPS: -1})
  53. if err != nil {
  54. t.Fatalf("Error in create clientset: %v", err)
  55. }
  56. return s, clientSet, closeFn
  57. }
  58. // TestApplyAlsoCreates makes sure that PATCH requests with the apply content type
  59. // will create the object if it doesn't already exist
  60. // 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.
  61. func TestApplyAlsoCreates(t *testing.T) {
  62. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ServerSideApply, true)()
  63. _, client, closeFn := setup(t)
  64. defer closeFn()
  65. testCases := []struct {
  66. resource string
  67. name string
  68. body string
  69. }{
  70. {
  71. resource: "pods",
  72. name: "test-pod",
  73. body: `{
  74. "apiVersion": "v1",
  75. "kind": "Pod",
  76. "metadata": {
  77. "name": "test-pod"
  78. },
  79. "spec": {
  80. "containers": [{
  81. "name": "test-container",
  82. "image": "test-image"
  83. }]
  84. }
  85. }`,
  86. }, {
  87. resource: "services",
  88. name: "test-svc",
  89. body: `{
  90. "apiVersion": "v1",
  91. "kind": "Service",
  92. "metadata": {
  93. "name": "test-svc"
  94. },
  95. "spec": {
  96. "ports": [{
  97. "port": 8080,
  98. "protocol": "UDP"
  99. }]
  100. }
  101. }`,
  102. },
  103. }
  104. for _, tc := range testCases {
  105. _, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  106. Namespace("default").
  107. Resource(tc.resource).
  108. Name(tc.name).
  109. Param("fieldManager", "apply_test").
  110. Body([]byte(tc.body)).
  111. Do(context.TODO()).
  112. Get()
  113. if err != nil {
  114. t.Fatalf("Failed to create object using Apply patch: %v", err)
  115. }
  116. _, err = client.CoreV1().RESTClient().Get().Namespace("default").Resource(tc.resource).Name(tc.name).Do(context.TODO()).Get()
  117. if err != nil {
  118. t.Fatalf("Failed to retrieve object: %v", err)
  119. }
  120. // Test that we can re apply with a different field manager and don't get conflicts
  121. _, err = client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  122. Namespace("default").
  123. Resource(tc.resource).
  124. Name(tc.name).
  125. Param("fieldManager", "apply_test_2").
  126. Body([]byte(tc.body)).
  127. Do(context.TODO()).
  128. Get()
  129. if err != nil {
  130. t.Fatalf("Failed to re-apply object using Apply patch: %v", err)
  131. }
  132. }
  133. }
  134. // TestNoOpUpdateSameResourceVersion makes sure that PUT requests which change nothing
  135. // will not change the resource version (no write to etcd is done)
  136. func TestNoOpUpdateSameResourceVersion(t *testing.T) {
  137. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ServerSideApply, true)()
  138. _, client, closeFn := setup(t)
  139. defer closeFn()
  140. podName := "no-op"
  141. podResource := "pods"
  142. podBytes := []byte(`{
  143. "apiVersion": "v1",
  144. "kind": "Pod",
  145. "metadata": {
  146. "name": "` + podName + `",
  147. "labels": {
  148. "a": "one",
  149. "c": "two",
  150. "b": "three"
  151. }
  152. },
  153. "spec": {
  154. "containers": [{
  155. "name": "test-container-a",
  156. "image": "test-image-one"
  157. },{
  158. "name": "test-container-c",
  159. "image": "test-image-two"
  160. },{
  161. "name": "test-container-b",
  162. "image": "test-image-three"
  163. }]
  164. }
  165. }`)
  166. _, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  167. Namespace("default").
  168. Param("fieldManager", "apply_test").
  169. Resource(podResource).
  170. Name(podName).
  171. Body(podBytes).
  172. Do(context.TODO()).
  173. Get()
  174. if err != nil {
  175. t.Fatalf("Failed to create object: %v", err)
  176. }
  177. // Sleep for one second to make sure that the times of each update operation is different.
  178. time.Sleep(1 * time.Second)
  179. createdObject, err := client.CoreV1().RESTClient().Get().Namespace("default").Resource(podResource).Name(podName).Do(context.TODO()).Get()
  180. if err != nil {
  181. t.Fatalf("Failed to retrieve created object: %v", err)
  182. }
  183. createdAccessor, err := meta.Accessor(createdObject)
  184. if err != nil {
  185. t.Fatalf("Failed to get meta accessor for created object: %v", err)
  186. }
  187. createdBytes, err := json.MarshalIndent(createdObject, "\t", "\t")
  188. if err != nil {
  189. t.Fatalf("Failed to marshal created object: %v", err)
  190. }
  191. // Test that we can put the same object and don't change the RV
  192. _, err = client.CoreV1().RESTClient().Put().
  193. Namespace("default").
  194. Resource(podResource).
  195. Name(podName).
  196. Body(createdBytes).
  197. Do(context.TODO()).
  198. Get()
  199. if err != nil {
  200. t.Fatalf("Failed to apply no-op update: %v", err)
  201. }
  202. updatedObject, err := client.CoreV1().RESTClient().Get().Namespace("default").Resource(podResource).Name(podName).Do(context.TODO()).Get()
  203. if err != nil {
  204. t.Fatalf("Failed to retrieve updated object: %v", err)
  205. }
  206. updatedAccessor, err := meta.Accessor(updatedObject)
  207. if err != nil {
  208. t.Fatalf("Failed to get meta accessor for updated object: %v", err)
  209. }
  210. updatedBytes, err := json.MarshalIndent(updatedObject, "\t", "\t")
  211. if err != nil {
  212. t.Fatalf("Failed to marshal updated object: %v", err)
  213. }
  214. if createdAccessor.GetResourceVersion() != updatedAccessor.GetResourceVersion() {
  215. t.Fatalf("Expected same resource version to be %v but got: %v\nold object:\n%v\nnew object:\n%v",
  216. createdAccessor.GetResourceVersion(),
  217. updatedAccessor.GetResourceVersion(),
  218. string(createdBytes),
  219. string(updatedBytes),
  220. )
  221. }
  222. }
  223. // TestCreateOnApplyFailsWithUID makes sure that PATCH requests with the apply content type
  224. // will not create the object if it doesn't already exist and it specifies a UID
  225. func TestCreateOnApplyFailsWithUID(t *testing.T) {
  226. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ServerSideApply, true)()
  227. _, client, closeFn := setup(t)
  228. defer closeFn()
  229. _, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  230. Namespace("default").
  231. Resource("pods").
  232. Name("test-pod-uid").
  233. Param("fieldManager", "apply_test").
  234. Body([]byte(`{
  235. "apiVersion": "v1",
  236. "kind": "Pod",
  237. "metadata": {
  238. "name": "test-pod-uid",
  239. "uid": "88e00824-7f0e-11e8-94a1-c8d3ffb15800"
  240. },
  241. "spec": {
  242. "containers": [{
  243. "name": "test-container",
  244. "image": "test-image"
  245. }]
  246. }
  247. }`)).
  248. Do(context.TODO()).
  249. Get()
  250. if !apierrors.IsConflict(err) {
  251. t.Fatalf("Expected conflict error but got: %v", err)
  252. }
  253. }
  254. func TestApplyUpdateApplyConflictForced(t *testing.T) {
  255. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ServerSideApply, true)()
  256. _, client, closeFn := setup(t)
  257. defer closeFn()
  258. obj := []byte(`{
  259. "apiVersion": "apps/v1",
  260. "kind": "Deployment",
  261. "metadata": {
  262. "name": "deployment",
  263. "labels": {"app": "nginx"}
  264. },
  265. "spec": {
  266. "replicas": 3,
  267. "selector": {
  268. "matchLabels": {
  269. "app": "nginx"
  270. }
  271. },
  272. "template": {
  273. "metadata": {
  274. "labels": {
  275. "app": "nginx"
  276. }
  277. },
  278. "spec": {
  279. "containers": [{
  280. "name": "nginx",
  281. "image": "nginx:latest"
  282. }]
  283. }
  284. }
  285. }
  286. }`)
  287. _, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  288. AbsPath("/apis/apps/v1").
  289. Namespace("default").
  290. Resource("deployments").
  291. Name("deployment").
  292. Param("fieldManager", "apply_test").
  293. Body(obj).Do(context.TODO()).Get()
  294. if err != nil {
  295. t.Fatalf("Failed to create object using Apply patch: %v", err)
  296. }
  297. _, err = client.CoreV1().RESTClient().Patch(types.MergePatchType).
  298. AbsPath("/apis/apps/v1").
  299. Namespace("default").
  300. Resource("deployments").
  301. Name("deployment").
  302. Body([]byte(`{"spec":{"replicas": 5}}`)).Do(context.TODO()).Get()
  303. if err != nil {
  304. t.Fatalf("Failed to patch object: %v", err)
  305. }
  306. _, err = client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  307. AbsPath("/apis/apps/v1").
  308. Namespace("default").
  309. Resource("deployments").
  310. Name("deployment").
  311. Param("fieldManager", "apply_test").
  312. Body([]byte(obj)).Do(context.TODO()).Get()
  313. if err == nil {
  314. t.Fatalf("Expecting to get conflicts when applying object")
  315. }
  316. status, ok := err.(*apierrors.StatusError)
  317. if !ok {
  318. t.Fatalf("Expecting to get conflicts as API error")
  319. }
  320. if len(status.Status().Details.Causes) < 1 {
  321. t.Fatalf("Expecting to get at least one conflict when applying object, got: %v", status.Status().Details.Causes)
  322. }
  323. _, err = client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  324. AbsPath("/apis/apps/v1").
  325. Namespace("default").
  326. Resource("deployments").
  327. Name("deployment").
  328. Param("force", "true").
  329. Param("fieldManager", "apply_test").
  330. Body([]byte(obj)).Do(context.TODO()).Get()
  331. if err != nil {
  332. t.Fatalf("Failed to apply object with force: %v", err)
  333. }
  334. }
  335. // TestApplyGroupsManySeparateUpdates tests that when many different managers update the same object,
  336. // the number of managedFields entries will only grow to a certain size.
  337. func TestApplyGroupsManySeparateUpdates(t *testing.T) {
  338. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ServerSideApply, true)()
  339. _, client, closeFn := setup(t)
  340. defer closeFn()
  341. obj := []byte(`{
  342. "apiVersion": "admissionregistration.k8s.io/v1",
  343. "kind": "ValidatingWebhookConfiguration",
  344. "metadata": {
  345. "name": "webhook",
  346. "labels": {"applier":"true"},
  347. },
  348. }`)
  349. object, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  350. AbsPath("/apis/admissionregistration.k8s.io/v1").
  351. Resource("validatingwebhookconfigurations").
  352. Name("webhook").
  353. Param("fieldManager", "apply_test").
  354. Body(obj).Do(context.TODO()).Get()
  355. if err != nil {
  356. t.Fatalf("Failed to create object using Apply patch: %v", err)
  357. }
  358. for i := 0; i < 20; i++ {
  359. unique := fmt.Sprintf("updater%v", i)
  360. version := "v1"
  361. if i%2 == 0 {
  362. version = "v1beta1"
  363. }
  364. object, err = client.CoreV1().RESTClient().Patch(types.MergePatchType).
  365. AbsPath("/apis/admissionregistration.k8s.io/"+version).
  366. Resource("validatingwebhookconfigurations").
  367. Name("webhook").
  368. Param("fieldManager", unique).
  369. Body([]byte(`{"metadata":{"labels":{"` + unique + `":"new"}}}`)).Do(context.TODO()).Get()
  370. if err != nil {
  371. t.Fatalf("Failed to patch object: %v", err)
  372. }
  373. }
  374. accessor, err := meta.Accessor(object)
  375. if err != nil {
  376. t.Fatalf("Failed to get meta accessor: %v", err)
  377. }
  378. // Expect 11 entries, because the cap for update entries is 10, and 1 apply entry
  379. if actual, expected := len(accessor.GetManagedFields()), 11; actual != expected {
  380. if b, err := json.MarshalIndent(object, "\t", "\t"); err == nil {
  381. t.Fatalf("Object expected to contain %v entries in managedFields, but got %v:\n%v", expected, actual, string(b))
  382. } else {
  383. t.Fatalf("Object expected to contain %v entries in managedFields, but got %v: error marshalling object: %v", expected, actual, err)
  384. }
  385. }
  386. // Expect the first entry to have the manager name "apply_test"
  387. if actual, expected := accessor.GetManagedFields()[0].Manager, "apply_test"; actual != expected {
  388. t.Fatalf("Expected first manager to be named %v but got %v", expected, actual)
  389. }
  390. // Expect the second entry to have the manager name "ancient-changes"
  391. if actual, expected := accessor.GetManagedFields()[1].Manager, "ancient-changes"; actual != expected {
  392. t.Fatalf("Expected first manager to be named %v but got %v", expected, actual)
  393. }
  394. }
  395. // TestApplyManagedFields makes sure that managedFields api does not change
  396. func TestApplyManagedFields(t *testing.T) {
  397. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ServerSideApply, true)()
  398. _, client, closeFn := setup(t)
  399. defer closeFn()
  400. _, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  401. Namespace("default").
  402. Resource("configmaps").
  403. Name("test-cm").
  404. Param("fieldManager", "apply_test").
  405. Body([]byte(`{
  406. "apiVersion": "v1",
  407. "kind": "ConfigMap",
  408. "metadata": {
  409. "name": "test-cm",
  410. "namespace": "default",
  411. "labels": {
  412. "test-label": "test"
  413. }
  414. },
  415. "data": {
  416. "key": "value"
  417. }
  418. }`)).
  419. Do(context.TODO()).
  420. Get()
  421. if err != nil {
  422. t.Fatalf("Failed to create object using Apply patch: %v", err)
  423. }
  424. _, err = client.CoreV1().RESTClient().Patch(types.MergePatchType).
  425. Namespace("default").
  426. Resource("configmaps").
  427. Name("test-cm").
  428. Param("fieldManager", "updater").
  429. Body([]byte(`{"data":{"new-key": "value"}}`)).Do(context.TODO()).Get()
  430. if err != nil {
  431. t.Fatalf("Failed to patch object: %v", err)
  432. }
  433. // Sleep for one second to make sure that the times of each update operation is different.
  434. // This will let us check that update entries with the same manager name are grouped together,
  435. // and that the most recent update time is recorded in the grouped entry.
  436. time.Sleep(1 * time.Second)
  437. _, err = client.CoreV1().RESTClient().Patch(types.MergePatchType).
  438. Namespace("default").
  439. Resource("configmaps").
  440. Name("test-cm").
  441. Param("fieldManager", "updater").
  442. Body([]byte(`{"data":{"key": "new value"}}`)).Do(context.TODO()).Get()
  443. if err != nil {
  444. t.Fatalf("Failed to patch object: %v", err)
  445. }
  446. object, err := client.CoreV1().RESTClient().Get().Namespace("default").Resource("configmaps").Name("test-cm").Do(context.TODO()).Get()
  447. if err != nil {
  448. t.Fatalf("Failed to retrieve object: %v", err)
  449. }
  450. accessor, err := meta.Accessor(object)
  451. if err != nil {
  452. t.Fatalf("Failed to get meta accessor: %v", err)
  453. }
  454. actual, err := json.MarshalIndent(object, "\t", "\t")
  455. if err != nil {
  456. t.Fatalf("Failed to marshal object: %v", err)
  457. }
  458. expected := []byte(`{
  459. "metadata": {
  460. "name": "test-cm",
  461. "namespace": "default",
  462. "selfLink": "` + accessor.GetSelfLink() + `",
  463. "uid": "` + string(accessor.GetUID()) + `",
  464. "resourceVersion": "` + accessor.GetResourceVersion() + `",
  465. "creationTimestamp": "` + accessor.GetCreationTimestamp().UTC().Format(time.RFC3339) + `",
  466. "labels": {
  467. "test-label": "test"
  468. },
  469. "managedFields": [
  470. {
  471. "manager": "apply_test",
  472. "operation": "Apply",
  473. "apiVersion": "v1",
  474. "time": "` + accessor.GetManagedFields()[0].Time.UTC().Format(time.RFC3339) + `",
  475. "fieldsType": "FieldsV1",
  476. "fieldsV1": {
  477. "f:metadata": {
  478. "f:labels": {
  479. "f:test-label": {}
  480. }
  481. }
  482. }
  483. },
  484. {
  485. "manager": "updater",
  486. "operation": "Update",
  487. "apiVersion": "v1",
  488. "time": "` + accessor.GetManagedFields()[1].Time.UTC().Format(time.RFC3339) + `",
  489. "fieldsType": "FieldsV1",
  490. "fieldsV1": {
  491. "f:data": {
  492. "f:key": {},
  493. "f:new-key": {}
  494. }
  495. }
  496. }
  497. ]
  498. },
  499. "data": {
  500. "key": "new value",
  501. "new-key": "value"
  502. }
  503. }`)
  504. if string(expected) != string(actual) {
  505. t.Fatalf("Expected:\n%v\nGot:\n%v", string(expected), string(actual))
  506. }
  507. if accessor.GetManagedFields()[0].Time.UTC().Format(time.RFC3339) == accessor.GetManagedFields()[1].Time.UTC().Format(time.RFC3339) {
  508. t.Fatalf("Expected times to be different but got:\n%v", string(actual))
  509. }
  510. }
  511. // TestApplyRemovesEmptyManagedFields there are no empty managers in managedFields
  512. func TestApplyRemovesEmptyManagedFields(t *testing.T) {
  513. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ServerSideApply, true)()
  514. _, client, closeFn := setup(t)
  515. defer closeFn()
  516. obj := []byte(`{
  517. "apiVersion": "v1",
  518. "kind": "ConfigMap",
  519. "metadata": {
  520. "name": "test-cm",
  521. "namespace": "default"
  522. }
  523. }`)
  524. _, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  525. Namespace("default").
  526. Resource("configmaps").
  527. Name("test-cm").
  528. Param("fieldManager", "apply_test").
  529. Body(obj).
  530. Do(context.TODO()).
  531. Get()
  532. if err != nil {
  533. t.Fatalf("Failed to create object using Apply patch: %v", err)
  534. }
  535. _, err = client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  536. Namespace("default").
  537. Resource("configmaps").
  538. Name("test-cm").
  539. Param("fieldManager", "apply_test").
  540. Body(obj).Do(context.TODO()).Get()
  541. if err != nil {
  542. t.Fatalf("Failed to patch object: %v", err)
  543. }
  544. object, err := client.CoreV1().RESTClient().Get().Namespace("default").Resource("configmaps").Name("test-cm").Do(context.TODO()).Get()
  545. if err != nil {
  546. t.Fatalf("Failed to retrieve object: %v", err)
  547. }
  548. accessor, err := meta.Accessor(object)
  549. if err != nil {
  550. t.Fatalf("Failed to get meta accessor: %v", err)
  551. }
  552. if managed := accessor.GetManagedFields(); managed != nil {
  553. t.Fatalf("Object contains unexpected managedFields: %v", managed)
  554. }
  555. }
  556. func TestApplyRequiresFieldManager(t *testing.T) {
  557. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ServerSideApply, true)()
  558. _, client, closeFn := setup(t)
  559. defer closeFn()
  560. obj := []byte(`{
  561. "apiVersion": "v1",
  562. "kind": "ConfigMap",
  563. "metadata": {
  564. "name": "test-cm",
  565. "namespace": "default"
  566. }
  567. }`)
  568. _, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  569. Namespace("default").
  570. Resource("configmaps").
  571. Name("test-cm").
  572. Body(obj).
  573. Do(context.TODO()).
  574. Get()
  575. if err == nil {
  576. t.Fatalf("Apply should fail to create without fieldManager")
  577. }
  578. _, err = client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  579. Namespace("default").
  580. Resource("configmaps").
  581. Name("test-cm").
  582. Param("fieldManager", "apply_test").
  583. Body(obj).
  584. Do(context.TODO()).
  585. Get()
  586. if err != nil {
  587. t.Fatalf("Apply failed to create with fieldManager: %v", err)
  588. }
  589. }
  590. // TestApplyRemoveContainerPort removes a container port from a deployment
  591. func TestApplyRemoveContainerPort(t *testing.T) {
  592. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ServerSideApply, true)()
  593. _, client, closeFn := setup(t)
  594. defer closeFn()
  595. obj := []byte(`{
  596. "apiVersion": "apps/v1",
  597. "kind": "Deployment",
  598. "metadata": {
  599. "name": "deployment",
  600. "labels": {"app": "nginx"}
  601. },
  602. "spec": {
  603. "replicas": 3,
  604. "selector": {
  605. "matchLabels": {
  606. "app": "nginx"
  607. }
  608. },
  609. "template": {
  610. "metadata": {
  611. "labels": {
  612. "app": "nginx"
  613. }
  614. },
  615. "spec": {
  616. "containers": [{
  617. "name": "nginx",
  618. "image": "nginx:latest",
  619. "ports": [{
  620. "containerPort": 80,
  621. "protocol": "TCP"
  622. }]
  623. }]
  624. }
  625. }
  626. }
  627. }`)
  628. _, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  629. AbsPath("/apis/apps/v1").
  630. Namespace("default").
  631. Resource("deployments").
  632. Name("deployment").
  633. Param("fieldManager", "apply_test").
  634. Body(obj).Do(context.TODO()).Get()
  635. if err != nil {
  636. t.Fatalf("Failed to create object using Apply patch: %v", err)
  637. }
  638. obj = []byte(`{
  639. "apiVersion": "apps/v1",
  640. "kind": "Deployment",
  641. "metadata": {
  642. "name": "deployment",
  643. "labels": {"app": "nginx"}
  644. },
  645. "spec": {
  646. "replicas": 3,
  647. "selector": {
  648. "matchLabels": {
  649. "app": "nginx"
  650. }
  651. },
  652. "template": {
  653. "metadata": {
  654. "labels": {
  655. "app": "nginx"
  656. }
  657. },
  658. "spec": {
  659. "containers": [{
  660. "name": "nginx",
  661. "image": "nginx:latest"
  662. }]
  663. }
  664. }
  665. }
  666. }`)
  667. _, err = client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  668. AbsPath("/apis/apps/v1").
  669. Namespace("default").
  670. Resource("deployments").
  671. Name("deployment").
  672. Param("fieldManager", "apply_test").
  673. Body(obj).Do(context.TODO()).Get()
  674. if err != nil {
  675. t.Fatalf("Failed to remove container port using Apply patch: %v", err)
  676. }
  677. deployment, err := client.AppsV1().Deployments("default").Get(context.TODO(), "deployment", metav1.GetOptions{})
  678. if err != nil {
  679. t.Fatalf("Failed to retrieve object: %v", err)
  680. }
  681. if len(deployment.Spec.Template.Spec.Containers[0].Ports) > 0 {
  682. t.Fatalf("Expected no container ports but got: %v, object: \n%#v", deployment.Spec.Template.Spec.Containers[0].Ports, deployment)
  683. }
  684. }
  685. // TestApplyFailsWithVersionMismatch ensures that a version mismatch between the
  686. // patch object and the live object will error
  687. func TestApplyFailsWithVersionMismatch(t *testing.T) {
  688. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ServerSideApply, true)()
  689. _, client, closeFn := setup(t)
  690. defer closeFn()
  691. obj := []byte(`{
  692. "apiVersion": "apps/v1",
  693. "kind": "Deployment",
  694. "metadata": {
  695. "name": "deployment",
  696. "labels": {"app": "nginx"}
  697. },
  698. "spec": {
  699. "replicas": 3,
  700. "selector": {
  701. "matchLabels": {
  702. "app": "nginx"
  703. }
  704. },
  705. "template": {
  706. "metadata": {
  707. "labels": {
  708. "app": "nginx"
  709. }
  710. },
  711. "spec": {
  712. "containers": [{
  713. "name": "nginx",
  714. "image": "nginx:latest"
  715. }]
  716. }
  717. }
  718. }
  719. }`)
  720. _, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  721. AbsPath("/apis/apps/v1").
  722. Namespace("default").
  723. Resource("deployments").
  724. Name("deployment").
  725. Param("fieldManager", "apply_test").
  726. Body(obj).Do(context.TODO()).Get()
  727. if err != nil {
  728. t.Fatalf("Failed to create object using Apply patch: %v", err)
  729. }
  730. obj = []byte(`{
  731. "apiVersion": "extensions/v1beta",
  732. "kind": "Deployment",
  733. "metadata": {
  734. "name": "deployment",
  735. "labels": {"app": "nginx"}
  736. },
  737. "spec": {
  738. "replicas": 100,
  739. "selector": {
  740. "matchLabels": {
  741. "app": "nginx"
  742. }
  743. },
  744. "template": {
  745. "metadata": {
  746. "labels": {
  747. "app": "nginx"
  748. }
  749. },
  750. "spec": {
  751. "containers": [{
  752. "name": "nginx",
  753. "image": "nginx:latest"
  754. }]
  755. }
  756. }
  757. }
  758. }`)
  759. _, err = client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  760. AbsPath("/apis/apps/v1").
  761. Namespace("default").
  762. Resource("deployments").
  763. Name("deployment").
  764. Param("fieldManager", "apply_test").
  765. Body([]byte(obj)).Do(context.TODO()).Get()
  766. if err == nil {
  767. t.Fatalf("Expecting to get version mismatch when applying object")
  768. }
  769. status, ok := err.(*apierrors.StatusError)
  770. if !ok {
  771. t.Fatalf("Expecting to get version mismatch as API error")
  772. }
  773. if status.Status().Code != http.StatusBadRequest {
  774. t.Fatalf("expected status code to be %d but was %d", http.StatusBadRequest, status.Status().Code)
  775. }
  776. }
  777. // TestApplyConvertsManagedFieldsVersion checks that the apply
  778. // converts the API group-version in the field manager
  779. func TestApplyConvertsManagedFieldsVersion(t *testing.T) {
  780. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ServerSideApply, true)()
  781. _, client, closeFn := setup(t)
  782. defer closeFn()
  783. obj := []byte(`{
  784. "apiVersion": "apps/v1",
  785. "kind": "Deployment",
  786. "metadata": {
  787. "name": "deployment",
  788. "labels": {"app": "nginx"},
  789. "managedFields": [
  790. {
  791. "manager": "sidecar_controller",
  792. "operation": "Apply",
  793. "apiVersion": "extensions/v1beta1",
  794. "fieldsV1": {
  795. "f:metadata": {
  796. "f:labels": {
  797. "f:sidecar_version": {}
  798. }
  799. },
  800. "f:spec": {
  801. "f:template": {
  802. "f: spec": {
  803. "f:containers": {
  804. "k:{\"name\":\"sidecar\"}": {
  805. ".": {},
  806. "f:image": {}
  807. }
  808. }
  809. }
  810. }
  811. }
  812. }
  813. }
  814. ]
  815. },
  816. "spec": {
  817. "selector": {
  818. "matchLabels": {
  819. "app": "nginx"
  820. }
  821. },
  822. "template": {
  823. "metadata": {
  824. "labels": {
  825. "app": "nginx"
  826. }
  827. },
  828. "spec": {
  829. "containers": [{
  830. "name": "nginx",
  831. "image": "nginx:latest"
  832. }]
  833. }
  834. }
  835. }
  836. }`)
  837. _, err := client.CoreV1().RESTClient().Post().
  838. AbsPath("/apis/apps/v1").
  839. Namespace("default").
  840. Resource("deployments").
  841. Body(obj).Do(context.TODO()).Get()
  842. if err != nil {
  843. t.Fatalf("Failed to create object: %v", err)
  844. }
  845. obj = []byte(`{
  846. "apiVersion": "apps/v1",
  847. "kind": "Deployment",
  848. "metadata": {
  849. "name": "deployment",
  850. "labels": {"sidecar_version": "release"}
  851. },
  852. "spec": {
  853. "template": {
  854. "spec": {
  855. "containers": [{
  856. "name": "sidecar",
  857. "image": "sidecar:latest"
  858. }]
  859. }
  860. }
  861. }
  862. }`)
  863. _, err = client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  864. AbsPath("/apis/apps/v1").
  865. Namespace("default").
  866. Resource("deployments").
  867. Name("deployment").
  868. Param("fieldManager", "sidecar_controller").
  869. Body([]byte(obj)).Do(context.TODO()).Get()
  870. if err != nil {
  871. t.Fatalf("Failed to apply object: %v", err)
  872. }
  873. object, err := client.AppsV1().Deployments("default").Get(context.TODO(), "deployment", metav1.GetOptions{})
  874. if err != nil {
  875. t.Fatalf("Failed to retrieve object: %v", err)
  876. }
  877. accessor, err := meta.Accessor(object)
  878. if err != nil {
  879. t.Fatalf("Failed to get meta accessor: %v", err)
  880. }
  881. managed := accessor.GetManagedFields()
  882. if len(managed) != 2 {
  883. t.Fatalf("Expected 2 field managers, but got managed fields: %v", managed)
  884. }
  885. var actual *metav1.ManagedFieldsEntry
  886. for i := range managed {
  887. entry := &managed[i]
  888. if entry.Manager == "sidecar_controller" && entry.APIVersion == "apps/v1" {
  889. actual = entry
  890. }
  891. }
  892. if actual == nil {
  893. 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)
  894. }
  895. expected := &metav1.ManagedFieldsEntry{
  896. Manager: "sidecar_controller",
  897. Operation: metav1.ManagedFieldsOperationApply,
  898. APIVersion: "apps/v1",
  899. Time: actual.Time,
  900. FieldsType: "FieldsV1",
  901. FieldsV1: &metav1.FieldsV1{
  902. Raw: []byte(`{"f:metadata":{"f:labels":{"f:sidecar_version":{}}},"f:spec":{"f:template":{"f:spec":{"f:containers":{"k:{\"name\":\"sidecar\"}":{".":{},"f:image":{},"f:name":{}}}}}}}`),
  903. },
  904. }
  905. if !reflect.DeepEqual(actual, expected) {
  906. t.Fatalf("expected:\n%v\nbut got:\n%v", expected, actual)
  907. }
  908. }
  909. // TestClearManagedFieldsWithMergePatch verifies it's possible to clear the managedFields
  910. func TestClearManagedFieldsWithMergePatch(t *testing.T) {
  911. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ServerSideApply, true)()
  912. _, client, closeFn := setup(t)
  913. defer closeFn()
  914. _, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  915. Namespace("default").
  916. Resource("configmaps").
  917. Name("test-cm").
  918. Param("fieldManager", "apply_test").
  919. Body([]byte(`{
  920. "apiVersion": "v1",
  921. "kind": "ConfigMap",
  922. "metadata": {
  923. "name": "test-cm",
  924. "namespace": "default",
  925. "labels": {
  926. "test-label": "test"
  927. }
  928. },
  929. "data": {
  930. "key": "value"
  931. }
  932. }`)).
  933. Do(context.TODO()).
  934. Get()
  935. if err != nil {
  936. t.Fatalf("Failed to create object using Apply patch: %v", err)
  937. }
  938. _, err = client.CoreV1().RESTClient().Patch(types.MergePatchType).
  939. Namespace("default").
  940. Resource("configmaps").
  941. Name("test-cm").
  942. Body([]byte(`{"metadata":{"managedFields": [{}]}}`)).Do(context.TODO()).Get()
  943. if err != nil {
  944. t.Fatalf("Failed to patch object: %v", err)
  945. }
  946. object, err := client.CoreV1().RESTClient().Get().Namespace("default").Resource("configmaps").Name("test-cm").Do(context.TODO()).Get()
  947. if err != nil {
  948. t.Fatalf("Failed to retrieve object: %v", err)
  949. }
  950. accessor, err := meta.Accessor(object)
  951. if err != nil {
  952. t.Fatalf("Failed to get meta accessor: %v", err)
  953. }
  954. if managedFields := accessor.GetManagedFields(); len(managedFields) != 0 {
  955. t.Fatalf("Failed to clear managedFields, got: %v", managedFields)
  956. }
  957. }
  958. // TestClearManagedFieldsWithStrategicMergePatch verifies it's possible to clear the managedFields
  959. func TestClearManagedFieldsWithStrategicMergePatch(t *testing.T) {
  960. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ServerSideApply, true)()
  961. _, client, closeFn := setup(t)
  962. defer closeFn()
  963. _, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  964. Namespace("default").
  965. Resource("configmaps").
  966. Name("test-cm").
  967. Param("fieldManager", "apply_test").
  968. Body([]byte(`{
  969. "apiVersion": "v1",
  970. "kind": "ConfigMap",
  971. "metadata": {
  972. "name": "test-cm",
  973. "namespace": "default",
  974. "labels": {
  975. "test-label": "test"
  976. }
  977. },
  978. "data": {
  979. "key": "value"
  980. }
  981. }`)).
  982. Do(context.TODO()).
  983. Get()
  984. if err != nil {
  985. t.Fatalf("Failed to create object using Apply patch: %v", err)
  986. }
  987. _, err = client.CoreV1().RESTClient().Patch(types.StrategicMergePatchType).
  988. Namespace("default").
  989. Resource("configmaps").
  990. Name("test-cm").
  991. Body([]byte(`{"metadata":{"managedFields": [{}]}}`)).Do(context.TODO()).Get()
  992. if err != nil {
  993. t.Fatalf("Failed to patch object: %v", err)
  994. }
  995. object, err := client.CoreV1().RESTClient().Get().Namespace("default").Resource("configmaps").Name("test-cm").Do(context.TODO()).Get()
  996. if err != nil {
  997. t.Fatalf("Failed to retrieve object: %v", err)
  998. }
  999. accessor, err := meta.Accessor(object)
  1000. if err != nil {
  1001. t.Fatalf("Failed to get meta accessor: %v", err)
  1002. }
  1003. if managedFields := accessor.GetManagedFields(); len(managedFields) != 0 {
  1004. t.Fatalf("Failed to clear managedFields, got: %v", managedFields)
  1005. }
  1006. if labels := accessor.GetLabels(); len(labels) < 1 {
  1007. t.Fatalf("Expected other fields to stay untouched, got: %v", object)
  1008. }
  1009. }
  1010. // TestClearManagedFieldsWithJSONPatch verifies it's possible to clear the managedFields
  1011. func TestClearManagedFieldsWithJSONPatch(t *testing.T) {
  1012. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ServerSideApply, true)()
  1013. _, client, closeFn := setup(t)
  1014. defer closeFn()
  1015. _, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  1016. Namespace("default").
  1017. Resource("configmaps").
  1018. Name("test-cm").
  1019. Param("fieldManager", "apply_test").
  1020. Body([]byte(`{
  1021. "apiVersion": "v1",
  1022. "kind": "ConfigMap",
  1023. "metadata": {
  1024. "name": "test-cm",
  1025. "namespace": "default",
  1026. "labels": {
  1027. "test-label": "test"
  1028. }
  1029. },
  1030. "data": {
  1031. "key": "value"
  1032. }
  1033. }`)).
  1034. Do(context.TODO()).
  1035. Get()
  1036. if err != nil {
  1037. t.Fatalf("Failed to create object using Apply patch: %v", err)
  1038. }
  1039. _, err = client.CoreV1().RESTClient().Patch(types.JSONPatchType).
  1040. Namespace("default").
  1041. Resource("configmaps").
  1042. Name("test-cm").
  1043. Body([]byte(`[{"op": "replace", "path": "/metadata/managedFields", "value": [{}]}]`)).Do(context.TODO()).Get()
  1044. if err != nil {
  1045. t.Fatalf("Failed to patch object: %v", err)
  1046. }
  1047. object, err := client.CoreV1().RESTClient().Get().Namespace("default").Resource("configmaps").Name("test-cm").Do(context.TODO()).Get()
  1048. if err != nil {
  1049. t.Fatalf("Failed to retrieve object: %v", err)
  1050. }
  1051. accessor, err := meta.Accessor(object)
  1052. if err != nil {
  1053. t.Fatalf("Failed to get meta accessor: %v", err)
  1054. }
  1055. if managedFields := accessor.GetManagedFields(); len(managedFields) != 0 {
  1056. t.Fatalf("Failed to clear managedFields, got: %v", managedFields)
  1057. }
  1058. }
  1059. // TestClearManagedFieldsWithUpdate verifies it's possible to clear the managedFields
  1060. func TestClearManagedFieldsWithUpdate(t *testing.T) {
  1061. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ServerSideApply, true)()
  1062. _, client, closeFn := setup(t)
  1063. defer closeFn()
  1064. _, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  1065. Namespace("default").
  1066. Resource("configmaps").
  1067. Name("test-cm").
  1068. Param("fieldManager", "apply_test").
  1069. Body([]byte(`{
  1070. "apiVersion": "v1",
  1071. "kind": "ConfigMap",
  1072. "metadata": {
  1073. "name": "test-cm",
  1074. "namespace": "default",
  1075. "labels": {
  1076. "test-label": "test"
  1077. }
  1078. },
  1079. "data": {
  1080. "key": "value"
  1081. }
  1082. }`)).
  1083. Do(context.TODO()).
  1084. Get()
  1085. if err != nil {
  1086. t.Fatalf("Failed to create object using Apply patch: %v", err)
  1087. }
  1088. _, err = client.CoreV1().RESTClient().Put().
  1089. Namespace("default").
  1090. Resource("configmaps").
  1091. Name("test-cm").
  1092. Body([]byte(`{
  1093. "apiVersion": "v1",
  1094. "kind": "ConfigMap",
  1095. "metadata": {
  1096. "name": "test-cm",
  1097. "namespace": "default",
  1098. "managedFields": [{}],
  1099. "labels": {
  1100. "test-label": "test"
  1101. }
  1102. },
  1103. "data": {
  1104. "key": "value"
  1105. }
  1106. }`)).Do(context.TODO()).Get()
  1107. if err != nil {
  1108. t.Fatalf("Failed to patch object: %v", err)
  1109. }
  1110. object, err := client.CoreV1().RESTClient().Get().Namespace("default").Resource("configmaps").Name("test-cm").Do(context.TODO()).Get()
  1111. if err != nil {
  1112. t.Fatalf("Failed to retrieve object: %v", err)
  1113. }
  1114. accessor, err := meta.Accessor(object)
  1115. if err != nil {
  1116. t.Fatalf("Failed to get meta accessor: %v", err)
  1117. }
  1118. if managedFields := accessor.GetManagedFields(); len(managedFields) != 0 {
  1119. t.Fatalf("Failed to clear managedFields, got: %v", managedFields)
  1120. }
  1121. if labels := accessor.GetLabels(); len(labels) < 1 {
  1122. t.Fatalf("Expected other fields to stay untouched, got: %v", object)
  1123. }
  1124. }
  1125. var podBytes = []byte(`
  1126. apiVersion: v1
  1127. kind: Pod
  1128. metadata:
  1129. labels:
  1130. app: some-app
  1131. plugin1: some-value
  1132. plugin2: some-value
  1133. plugin3: some-value
  1134. plugin4: some-value
  1135. name: some-name
  1136. namespace: default
  1137. ownerReferences:
  1138. - apiVersion: apps/v1
  1139. blockOwnerDeletion: true
  1140. controller: true
  1141. kind: ReplicaSet
  1142. name: some-name
  1143. uid: 0a9d2b9e-779e-11e7-b422-42010a8001be
  1144. spec:
  1145. containers:
  1146. - args:
  1147. - one
  1148. - two
  1149. - three
  1150. - four
  1151. - five
  1152. - six
  1153. - seven
  1154. - eight
  1155. - nine
  1156. env:
  1157. - name: VAR_3
  1158. valueFrom:
  1159. secretKeyRef:
  1160. key: some-other-key
  1161. name: some-oher-name
  1162. - name: VAR_2
  1163. valueFrom:
  1164. secretKeyRef:
  1165. key: other-key
  1166. name: other-name
  1167. - name: VAR_1
  1168. valueFrom:
  1169. secretKeyRef:
  1170. key: some-key
  1171. name: some-name
  1172. image: some-image-name
  1173. imagePullPolicy: IfNotPresent
  1174. name: some-name
  1175. resources:
  1176. requests:
  1177. cpu: "0"
  1178. terminationMessagePath: /dev/termination-log
  1179. terminationMessagePolicy: File
  1180. volumeMounts:
  1181. - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
  1182. name: default-token-hu5jz
  1183. readOnly: true
  1184. dnsPolicy: ClusterFirst
  1185. nodeName: node-name
  1186. priority: 0
  1187. restartPolicy: Always
  1188. schedulerName: default-scheduler
  1189. securityContext: {}
  1190. serviceAccount: default
  1191. serviceAccountName: default
  1192. terminationGracePeriodSeconds: 30
  1193. tolerations:
  1194. - effect: NoExecute
  1195. key: node.kubernetes.io/not-ready
  1196. operator: Exists
  1197. tolerationSeconds: 300
  1198. - effect: NoExecute
  1199. key: node.kubernetes.io/unreachable
  1200. operator: Exists
  1201. tolerationSeconds: 300
  1202. volumes:
  1203. - name: default-token-hu5jz
  1204. secret:
  1205. defaultMode: 420
  1206. secretName: default-token-hu5jz
  1207. status:
  1208. conditions:
  1209. - lastProbeTime: null
  1210. lastTransitionTime: "2019-07-08T09:31:18Z"
  1211. status: "True"
  1212. type: Initialized
  1213. - lastProbeTime: null
  1214. lastTransitionTime: "2019-07-08T09:41:59Z"
  1215. status: "True"
  1216. type: Ready
  1217. - lastProbeTime: null
  1218. lastTransitionTime: null
  1219. status: "True"
  1220. type: ContainersReady
  1221. - lastProbeTime: null
  1222. lastTransitionTime: "2019-07-08T09:31:18Z"
  1223. status: "True"
  1224. type: PodScheduled
  1225. containerStatuses:
  1226. - containerID: docker://885e82a1ed0b7356541bb410a0126921ac42439607c09875cd8097dd5d7b5376
  1227. image: some-image-name
  1228. imageID: docker-pullable://some-image-id
  1229. lastState:
  1230. terminated:
  1231. containerID: docker://d57290f9e00fad626b20d2dd87a3cf69bbc22edae07985374f86a8b2b4e39565
  1232. exitCode: 255
  1233. finishedAt: "2019-07-08T09:39:09Z"
  1234. reason: Error
  1235. startedAt: "2019-07-08T09:38:54Z"
  1236. name: name
  1237. ready: true
  1238. restartCount: 6
  1239. state:
  1240. running:
  1241. startedAt: "2019-07-08T09:41:59Z"
  1242. hostIP: 10.0.0.1
  1243. phase: Running
  1244. podIP: 10.0.0.1
  1245. qosClass: BestEffort
  1246. startTime: "2019-07-08T09:31:18Z"
  1247. `)
  1248. func decodePod(podBytes []byte) v1.Pod {
  1249. pod := v1.Pod{}
  1250. err := yaml.Unmarshal(podBytes, &pod)
  1251. if err != nil {
  1252. panic(err)
  1253. }
  1254. return pod
  1255. }
  1256. func encodePod(pod v1.Pod) []byte {
  1257. podBytes, err := yaml.Marshal(pod)
  1258. if err != nil {
  1259. panic(err)
  1260. }
  1261. return podBytes
  1262. }
  1263. func BenchmarkNoServerSideApply(b *testing.B) {
  1264. defer featuregatetesting.SetFeatureGateDuringTest(b, utilfeature.DefaultFeatureGate, genericfeatures.ServerSideApply, false)()
  1265. _, client, closeFn := setup(b)
  1266. defer closeFn()
  1267. flag.Lookup("v").Value.Set("0")
  1268. benchAll(b, client, decodePod(podBytes))
  1269. }
  1270. func getPodSizeWhenEnabled(b *testing.B, pod v1.Pod) int {
  1271. return len(getPodBytesWhenEnabled(b, pod, "application/vnd.kubernetes.protobuf"))
  1272. }
  1273. func getPodBytesWhenEnabled(b *testing.B, pod v1.Pod, format string) []byte {
  1274. defer featuregatetesting.SetFeatureGateDuringTest(b, utilfeature.DefaultFeatureGate, genericfeatures.ServerSideApply, true)()
  1275. _, client, closeFn := setup(b)
  1276. defer closeFn()
  1277. flag.Lookup("v").Value.Set("0")
  1278. pod.Name = "size-pod"
  1279. podB, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  1280. Name(pod.Name).
  1281. Namespace("default").
  1282. Param("fieldManager", "apply_test").
  1283. Resource("pods").
  1284. SetHeader("Accept", format).
  1285. Body(encodePod(pod)).DoRaw(context.TODO())
  1286. if err != nil {
  1287. b.Fatalf("Failed to create object: %#v", err)
  1288. }
  1289. return podB
  1290. }
  1291. func BenchmarkNoServerSideApplyButSameSize(b *testing.B) {
  1292. pod := decodePod(podBytes)
  1293. ssaPodSize := getPodSizeWhenEnabled(b, pod)
  1294. defer featuregatetesting.SetFeatureGateDuringTest(b, utilfeature.DefaultFeatureGate, genericfeatures.ServerSideApply, false)()
  1295. _, client, closeFn := setup(b)
  1296. defer closeFn()
  1297. flag.Lookup("v").Value.Set("0")
  1298. pod.Name = "size-pod"
  1299. noSSAPod, err := client.CoreV1().RESTClient().Post().
  1300. Namespace("default").
  1301. Resource("pods").
  1302. SetHeader("Content-Type", "application/yaml").
  1303. SetHeader("Accept", "application/vnd.kubernetes.protobuf").
  1304. Body(encodePod(pod)).DoRaw(context.TODO())
  1305. if err != nil {
  1306. b.Fatalf("Failed to create object: %v", err)
  1307. }
  1308. ssaDiff := ssaPodSize - len(noSSAPod)
  1309. fmt.Printf("Without SSA: %v bytes, With SSA: %v bytes, Difference: %v bytes\n", len(noSSAPod), ssaPodSize, ssaDiff)
  1310. annotations := pod.GetAnnotations()
  1311. builder := strings.Builder{}
  1312. for i := 0; i < ssaDiff; i++ {
  1313. builder.WriteByte('0')
  1314. }
  1315. if annotations == nil {
  1316. annotations = map[string]string{}
  1317. }
  1318. annotations["x-ssa-difference"] = builder.String()
  1319. pod.SetAnnotations(annotations)
  1320. benchAll(b, client, pod)
  1321. }
  1322. func BenchmarkServerSideApply(b *testing.B) {
  1323. podBytesWhenEnabled := getPodBytesWhenEnabled(b, decodePod(podBytes), "application/yaml")
  1324. defer featuregatetesting.SetFeatureGateDuringTest(b, utilfeature.DefaultFeatureGate, genericfeatures.ServerSideApply, true)()
  1325. _, client, closeFn := setup(b)
  1326. defer closeFn()
  1327. flag.Lookup("v").Value.Set("0")
  1328. benchAll(b, client, decodePod(podBytesWhenEnabled))
  1329. }
  1330. func benchAll(b *testing.B, client kubernetes.Interface, pod v1.Pod) {
  1331. // Make sure pod is ready to post
  1332. pod.ObjectMeta.CreationTimestamp = metav1.Time{}
  1333. pod.ObjectMeta.ResourceVersion = ""
  1334. pod.ObjectMeta.UID = ""
  1335. pod.ObjectMeta.SelfLink = ""
  1336. // Create pod for repeated-updates
  1337. pod.Name = "repeated-pod"
  1338. _, err := client.CoreV1().RESTClient().Post().
  1339. Namespace("default").
  1340. Resource("pods").
  1341. SetHeader("Content-Type", "application/yaml").
  1342. Body(encodePod(pod)).Do(context.TODO()).Get()
  1343. if err != nil {
  1344. b.Fatalf("Failed to create object: %v", err)
  1345. }
  1346. b.Run("List1", benchListPod(client, pod, 1))
  1347. b.Run("List20", benchListPod(client, pod, 20))
  1348. b.Run("List200", benchListPod(client, pod, 200))
  1349. b.Run("List2000", benchListPod(client, pod, 2000))
  1350. b.Run("RepeatedUpdates", benchRepeatedUpdate(client, "repeated-pod"))
  1351. b.Run("Post1", benchPostPod(client, pod, 1))
  1352. b.Run("Post10", benchPostPod(client, pod, 10))
  1353. b.Run("Post50", benchPostPod(client, pod, 50))
  1354. }
  1355. func benchPostPod(client kubernetes.Interface, pod v1.Pod, parallel int) func(*testing.B) {
  1356. return func(b *testing.B) {
  1357. b.ResetTimer()
  1358. b.ReportAllocs()
  1359. for i := 0; i < b.N; i++ {
  1360. c := make(chan error)
  1361. for j := 0; j < parallel; j++ {
  1362. j := j
  1363. i := i
  1364. go func(pod v1.Pod) {
  1365. pod.Name = fmt.Sprintf("post%d-%d-%d-%d", parallel, b.N, j, i)
  1366. _, err := client.CoreV1().RESTClient().Post().
  1367. Namespace("default").
  1368. Resource("pods").
  1369. SetHeader("Content-Type", "application/yaml").
  1370. Body(encodePod(pod)).Do(context.TODO()).Get()
  1371. c <- err
  1372. }(pod)
  1373. }
  1374. for j := 0; j < parallel; j++ {
  1375. err := <-c
  1376. if err != nil {
  1377. b.Fatal(err)
  1378. }
  1379. }
  1380. close(c)
  1381. }
  1382. }
  1383. }
  1384. func createNamespace(client kubernetes.Interface, name string) error {
  1385. namespace := v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: name}}
  1386. namespaceBytes, err := yaml.Marshal(namespace)
  1387. if err != nil {
  1388. return fmt.Errorf("Failed to marshal namespace: %v", err)
  1389. }
  1390. _, err = client.CoreV1().RESTClient().Get().
  1391. Resource("namespaces").
  1392. SetHeader("Content-Type", "application/yaml").
  1393. Body(namespaceBytes).Do(context.TODO()).Get()
  1394. if err != nil {
  1395. return fmt.Errorf("Failed to create namespace: %v", err)
  1396. }
  1397. return nil
  1398. }
  1399. func benchListPod(client kubernetes.Interface, pod v1.Pod, num int) func(*testing.B) {
  1400. return func(b *testing.B) {
  1401. namespace := fmt.Sprintf("get-%d-%d", num, b.N)
  1402. if err := createNamespace(client, namespace); err != nil {
  1403. b.Fatal(err)
  1404. }
  1405. // Create pods
  1406. for i := 0; i < num; i++ {
  1407. pod.Name = fmt.Sprintf("get-%d-%d", b.N, i)
  1408. pod.Namespace = namespace
  1409. _, err := client.CoreV1().RESTClient().Post().
  1410. Namespace(namespace).
  1411. Resource("pods").
  1412. SetHeader("Content-Type", "application/yaml").
  1413. Body(encodePod(pod)).Do(context.TODO()).Get()
  1414. if err != nil {
  1415. b.Fatalf("Failed to create object: %v", err)
  1416. }
  1417. }
  1418. b.ResetTimer()
  1419. b.ReportAllocs()
  1420. for i := 0; i < b.N; i++ {
  1421. _, err := client.CoreV1().RESTClient().Get().
  1422. Namespace(namespace).
  1423. Resource("pods").
  1424. SetHeader("Accept", "application/vnd.kubernetes.protobuf").
  1425. Do(context.TODO()).Get()
  1426. if err != nil {
  1427. b.Fatalf("Failed to patch object: %v", err)
  1428. }
  1429. }
  1430. }
  1431. }
  1432. func benchRepeatedUpdate(client kubernetes.Interface, podName string) func(*testing.B) {
  1433. return func(b *testing.B) {
  1434. b.ResetTimer()
  1435. b.ReportAllocs()
  1436. for i := 0; i < b.N; i++ {
  1437. _, err := client.CoreV1().RESTClient().Patch(types.JSONPatchType).
  1438. Namespace("default").
  1439. Resource("pods").
  1440. Name(podName).
  1441. Body([]byte(fmt.Sprintf(`[{"op": "replace", "path": "/spec/containers/0/image", "value": "image%d"}]`, i))).Do(context.TODO()).Get()
  1442. if err != nil {
  1443. b.Fatalf("Failed to patch object: %v", err)
  1444. }
  1445. }
  1446. }
  1447. }