util_test.go 61 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771
  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 pod
  14. import (
  15. "fmt"
  16. "reflect"
  17. "strings"
  18. "testing"
  19. "k8s.io/apimachinery/pkg/api/resource"
  20. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  21. "k8s.io/apimachinery/pkg/util/diff"
  22. "k8s.io/apimachinery/pkg/util/sets"
  23. "k8s.io/apimachinery/pkg/util/validation/field"
  24. utilfeature "k8s.io/apiserver/pkg/util/feature"
  25. featuregatetesting "k8s.io/component-base/featuregate/testing"
  26. api "k8s.io/kubernetes/pkg/apis/core"
  27. "k8s.io/kubernetes/pkg/features"
  28. "k8s.io/kubernetes/pkg/security/apparmor"
  29. )
  30. func TestPodSecrets(t *testing.T) {
  31. // Stub containing all possible secret references in a pod.
  32. // The names of the referenced secrets match struct paths detected by reflection.
  33. pod := &api.Pod{
  34. Spec: api.PodSpec{
  35. Containers: []api.Container{{
  36. EnvFrom: []api.EnvFromSource{{
  37. SecretRef: &api.SecretEnvSource{
  38. LocalObjectReference: api.LocalObjectReference{
  39. Name: "Spec.Containers[*].EnvFrom[*].SecretRef"}}}},
  40. Env: []api.EnvVar{{
  41. ValueFrom: &api.EnvVarSource{
  42. SecretKeyRef: &api.SecretKeySelector{
  43. LocalObjectReference: api.LocalObjectReference{
  44. Name: "Spec.Containers[*].Env[*].ValueFrom.SecretKeyRef"}}}}}}},
  45. ImagePullSecrets: []api.LocalObjectReference{{
  46. Name: "Spec.ImagePullSecrets"}},
  47. InitContainers: []api.Container{{
  48. EnvFrom: []api.EnvFromSource{{
  49. SecretRef: &api.SecretEnvSource{
  50. LocalObjectReference: api.LocalObjectReference{
  51. Name: "Spec.InitContainers[*].EnvFrom[*].SecretRef"}}}},
  52. Env: []api.EnvVar{{
  53. ValueFrom: &api.EnvVarSource{
  54. SecretKeyRef: &api.SecretKeySelector{
  55. LocalObjectReference: api.LocalObjectReference{
  56. Name: "Spec.InitContainers[*].Env[*].ValueFrom.SecretKeyRef"}}}}}}},
  57. Volumes: []api.Volume{{
  58. VolumeSource: api.VolumeSource{
  59. AzureFile: &api.AzureFileVolumeSource{
  60. SecretName: "Spec.Volumes[*].VolumeSource.AzureFile.SecretName"}}}, {
  61. VolumeSource: api.VolumeSource{
  62. CephFS: &api.CephFSVolumeSource{
  63. SecretRef: &api.LocalObjectReference{
  64. Name: "Spec.Volumes[*].VolumeSource.CephFS.SecretRef"}}}}, {
  65. VolumeSource: api.VolumeSource{
  66. Cinder: &api.CinderVolumeSource{
  67. SecretRef: &api.LocalObjectReference{
  68. Name: "Spec.Volumes[*].VolumeSource.Cinder.SecretRef"}}}}, {
  69. VolumeSource: api.VolumeSource{
  70. FlexVolume: &api.FlexVolumeSource{
  71. SecretRef: &api.LocalObjectReference{
  72. Name: "Spec.Volumes[*].VolumeSource.FlexVolume.SecretRef"}}}}, {
  73. VolumeSource: api.VolumeSource{
  74. Projected: &api.ProjectedVolumeSource{
  75. Sources: []api.VolumeProjection{{
  76. Secret: &api.SecretProjection{
  77. LocalObjectReference: api.LocalObjectReference{
  78. Name: "Spec.Volumes[*].VolumeSource.Projected.Sources[*].Secret"}}}}}}}, {
  79. VolumeSource: api.VolumeSource{
  80. RBD: &api.RBDVolumeSource{
  81. SecretRef: &api.LocalObjectReference{
  82. Name: "Spec.Volumes[*].VolumeSource.RBD.SecretRef"}}}}, {
  83. VolumeSource: api.VolumeSource{
  84. Secret: &api.SecretVolumeSource{
  85. SecretName: "Spec.Volumes[*].VolumeSource.Secret.SecretName"}}}, {
  86. VolumeSource: api.VolumeSource{
  87. Secret: &api.SecretVolumeSource{
  88. SecretName: "Spec.Volumes[*].VolumeSource.Secret"}}}, {
  89. VolumeSource: api.VolumeSource{
  90. ScaleIO: &api.ScaleIOVolumeSource{
  91. SecretRef: &api.LocalObjectReference{
  92. Name: "Spec.Volumes[*].VolumeSource.ScaleIO.SecretRef"}}}}, {
  93. VolumeSource: api.VolumeSource{
  94. ISCSI: &api.ISCSIVolumeSource{
  95. SecretRef: &api.LocalObjectReference{
  96. Name: "Spec.Volumes[*].VolumeSource.ISCSI.SecretRef"}}}}, {
  97. VolumeSource: api.VolumeSource{
  98. StorageOS: &api.StorageOSVolumeSource{
  99. SecretRef: &api.LocalObjectReference{
  100. Name: "Spec.Volumes[*].VolumeSource.StorageOS.SecretRef"}}}}, {
  101. VolumeSource: api.VolumeSource{
  102. CSI: &api.CSIVolumeSource{
  103. NodePublishSecretRef: &api.LocalObjectReference{
  104. Name: "Spec.Volumes[*].VolumeSource.CSI.NodePublishSecretRef"}}}}},
  105. },
  106. }
  107. extractedNames := sets.NewString()
  108. VisitPodSecretNames(pod, func(name string) bool {
  109. extractedNames.Insert(name)
  110. return true
  111. })
  112. // excludedSecretPaths holds struct paths to fields with "secret" in the name that are not actually references to secret API objects
  113. excludedSecretPaths := sets.NewString(
  114. "Spec.Volumes[*].VolumeSource.CephFS.SecretFile",
  115. )
  116. // expectedSecretPaths holds struct paths to fields with "secret" in the name that are references to secret API objects.
  117. // every path here should be represented as an example in the Pod stub above, with the secret name set to the path.
  118. expectedSecretPaths := sets.NewString(
  119. "Spec.Containers[*].EnvFrom[*].SecretRef",
  120. "Spec.Containers[*].Env[*].ValueFrom.SecretKeyRef",
  121. "Spec.ImagePullSecrets",
  122. "Spec.InitContainers[*].EnvFrom[*].SecretRef",
  123. "Spec.InitContainers[*].Env[*].ValueFrom.SecretKeyRef",
  124. "Spec.Volumes[*].VolumeSource.AzureFile.SecretName",
  125. "Spec.Volumes[*].VolumeSource.CephFS.SecretRef",
  126. "Spec.Volumes[*].VolumeSource.Cinder.SecretRef",
  127. "Spec.Volumes[*].VolumeSource.FlexVolume.SecretRef",
  128. "Spec.Volumes[*].VolumeSource.Projected.Sources[*].Secret",
  129. "Spec.Volumes[*].VolumeSource.RBD.SecretRef",
  130. "Spec.Volumes[*].VolumeSource.Secret",
  131. "Spec.Volumes[*].VolumeSource.Secret.SecretName",
  132. "Spec.Volumes[*].VolumeSource.ScaleIO.SecretRef",
  133. "Spec.Volumes[*].VolumeSource.ISCSI.SecretRef",
  134. "Spec.Volumes[*].VolumeSource.StorageOS.SecretRef",
  135. "Spec.Volumes[*].VolumeSource.CSI.NodePublishSecretRef",
  136. )
  137. secretPaths := collectResourcePaths(t, "secret", nil, "", reflect.TypeOf(&api.Pod{}))
  138. secretPaths = secretPaths.Difference(excludedSecretPaths)
  139. if missingPaths := expectedSecretPaths.Difference(secretPaths); len(missingPaths) > 0 {
  140. t.Logf("Missing expected secret paths:\n%s", strings.Join(missingPaths.List(), "\n"))
  141. t.Error("Missing expected secret paths. Verify VisitPodSecretNames() is correctly finding the missing paths, then correct expectedSecretPaths")
  142. }
  143. if extraPaths := secretPaths.Difference(expectedSecretPaths); len(extraPaths) > 0 {
  144. t.Logf("Extra secret paths:\n%s", strings.Join(extraPaths.List(), "\n"))
  145. t.Error("Extra fields with 'secret' in the name found. Verify VisitPodSecretNames() is including these fields if appropriate, then correct expectedSecretPaths")
  146. }
  147. if missingNames := expectedSecretPaths.Difference(extractedNames); len(missingNames) > 0 {
  148. t.Logf("Missing expected secret names:\n%s", strings.Join(missingNames.List(), "\n"))
  149. t.Error("Missing expected secret names. Verify the pod stub above includes these references, then verify VisitPodSecretNames() is correctly finding the missing names")
  150. }
  151. if extraNames := extractedNames.Difference(expectedSecretPaths); len(extraNames) > 0 {
  152. t.Logf("Extra secret names:\n%s", strings.Join(extraNames.List(), "\n"))
  153. t.Error("Extra secret names extracted. Verify VisitPodSecretNames() is correctly extracting secret names")
  154. }
  155. }
  156. // collectResourcePaths traverses the object, computing all the struct paths that lead to fields with resourcename in the name.
  157. func collectResourcePaths(t *testing.T, resourcename string, path *field.Path, name string, tp reflect.Type) sets.String {
  158. resourcename = strings.ToLower(resourcename)
  159. resourcePaths := sets.NewString()
  160. if tp.Kind() == reflect.Ptr {
  161. resourcePaths.Insert(collectResourcePaths(t, resourcename, path, name, tp.Elem()).List()...)
  162. return resourcePaths
  163. }
  164. if strings.Contains(strings.ToLower(name), resourcename) {
  165. resourcePaths.Insert(path.String())
  166. }
  167. switch tp.Kind() {
  168. case reflect.Ptr:
  169. resourcePaths.Insert(collectResourcePaths(t, resourcename, path, name, tp.Elem()).List()...)
  170. case reflect.Struct:
  171. // ObjectMeta is generic and therefore should never have a field with a specific resource's name;
  172. // it contains cycles so it's easiest to just skip it.
  173. if name == "ObjectMeta" {
  174. break
  175. }
  176. for i := 0; i < tp.NumField(); i++ {
  177. field := tp.Field(i)
  178. resourcePaths.Insert(collectResourcePaths(t, resourcename, path.Child(field.Name), field.Name, field.Type).List()...)
  179. }
  180. case reflect.Interface:
  181. t.Errorf("cannot find %s fields in interface{} field %s", resourcename, path.String())
  182. case reflect.Map:
  183. resourcePaths.Insert(collectResourcePaths(t, resourcename, path.Key("*"), "", tp.Elem()).List()...)
  184. case reflect.Slice:
  185. resourcePaths.Insert(collectResourcePaths(t, resourcename, path.Key("*"), "", tp.Elem()).List()...)
  186. default:
  187. // all primitive types
  188. }
  189. return resourcePaths
  190. }
  191. func TestPodConfigmaps(t *testing.T) {
  192. // Stub containing all possible ConfigMap references in a pod.
  193. // The names of the referenced ConfigMaps match struct paths detected by reflection.
  194. pod := &api.Pod{
  195. Spec: api.PodSpec{
  196. Containers: []api.Container{{
  197. EnvFrom: []api.EnvFromSource{{
  198. ConfigMapRef: &api.ConfigMapEnvSource{
  199. LocalObjectReference: api.LocalObjectReference{
  200. Name: "Spec.Containers[*].EnvFrom[*].ConfigMapRef"}}}},
  201. Env: []api.EnvVar{{
  202. ValueFrom: &api.EnvVarSource{
  203. ConfigMapKeyRef: &api.ConfigMapKeySelector{
  204. LocalObjectReference: api.LocalObjectReference{
  205. Name: "Spec.Containers[*].Env[*].ValueFrom.ConfigMapKeyRef"}}}}}}},
  206. InitContainers: []api.Container{{
  207. EnvFrom: []api.EnvFromSource{{
  208. ConfigMapRef: &api.ConfigMapEnvSource{
  209. LocalObjectReference: api.LocalObjectReference{
  210. Name: "Spec.InitContainers[*].EnvFrom[*].ConfigMapRef"}}}},
  211. Env: []api.EnvVar{{
  212. ValueFrom: &api.EnvVarSource{
  213. ConfigMapKeyRef: &api.ConfigMapKeySelector{
  214. LocalObjectReference: api.LocalObjectReference{
  215. Name: "Spec.InitContainers[*].Env[*].ValueFrom.ConfigMapKeyRef"}}}}}}},
  216. Volumes: []api.Volume{{
  217. VolumeSource: api.VolumeSource{
  218. Projected: &api.ProjectedVolumeSource{
  219. Sources: []api.VolumeProjection{{
  220. ConfigMap: &api.ConfigMapProjection{
  221. LocalObjectReference: api.LocalObjectReference{
  222. Name: "Spec.Volumes[*].VolumeSource.Projected.Sources[*].ConfigMap"}}}}}}}, {
  223. VolumeSource: api.VolumeSource{
  224. ConfigMap: &api.ConfigMapVolumeSource{
  225. LocalObjectReference: api.LocalObjectReference{
  226. Name: "Spec.Volumes[*].VolumeSource.ConfigMap"}}}}},
  227. },
  228. }
  229. extractedNames := sets.NewString()
  230. VisitPodConfigmapNames(pod, func(name string) bool {
  231. extractedNames.Insert(name)
  232. return true
  233. })
  234. // expectedPaths holds struct paths to fields with "ConfigMap" in the name that are references to ConfigMap API objects.
  235. // every path here should be represented as an example in the Pod stub above, with the ConfigMap name set to the path.
  236. expectedPaths := sets.NewString(
  237. "Spec.Containers[*].EnvFrom[*].ConfigMapRef",
  238. "Spec.Containers[*].Env[*].ValueFrom.ConfigMapKeyRef",
  239. "Spec.InitContainers[*].EnvFrom[*].ConfigMapRef",
  240. "Spec.InitContainers[*].Env[*].ValueFrom.ConfigMapKeyRef",
  241. "Spec.Volumes[*].VolumeSource.Projected.Sources[*].ConfigMap",
  242. "Spec.Volumes[*].VolumeSource.ConfigMap",
  243. )
  244. collectPaths := collectResourcePaths(t, "ConfigMap", nil, "", reflect.TypeOf(&api.Pod{}))
  245. if missingPaths := expectedPaths.Difference(collectPaths); len(missingPaths) > 0 {
  246. t.Logf("Missing expected paths:\n%s", strings.Join(missingPaths.List(), "\n"))
  247. t.Error("Missing expected paths. Verify VisitPodConfigmapNames() is correctly finding the missing paths, then correct expectedPaths")
  248. }
  249. if extraPaths := collectPaths.Difference(expectedPaths); len(extraPaths) > 0 {
  250. t.Logf("Extra paths:\n%s", strings.Join(extraPaths.List(), "\n"))
  251. t.Error("Extra fields with resource in the name found. Verify VisitPodConfigmapNames() is including these fields if appropriate, then correct expectedPaths")
  252. }
  253. if missingNames := expectedPaths.Difference(extractedNames); len(missingNames) > 0 {
  254. t.Logf("Missing expected names:\n%s", strings.Join(missingNames.List(), "\n"))
  255. t.Error("Missing expected names. Verify the pod stub above includes these references, then verify VisitPodConfigmapNames() is correctly finding the missing names")
  256. }
  257. if extraNames := extractedNames.Difference(expectedPaths); len(extraNames) > 0 {
  258. t.Logf("Extra names:\n%s", strings.Join(extraNames.List(), "\n"))
  259. t.Error("Extra names extracted. Verify VisitPodConfigmapNames() is correctly extracting resource names")
  260. }
  261. }
  262. func TestDropAlphaVolumeDevices(t *testing.T) {
  263. podWithVolumeDevices := func() *api.Pod {
  264. return &api.Pod{
  265. Spec: api.PodSpec{
  266. RestartPolicy: api.RestartPolicyNever,
  267. Containers: []api.Container{
  268. {
  269. Name: "container1",
  270. Image: "testimage",
  271. VolumeDevices: []api.VolumeDevice{
  272. {
  273. Name: "myvolume",
  274. DevicePath: "/usr/test",
  275. },
  276. },
  277. },
  278. },
  279. InitContainers: []api.Container{
  280. {
  281. Name: "container1",
  282. Image: "testimage",
  283. VolumeDevices: []api.VolumeDevice{
  284. {
  285. Name: "myvolume",
  286. DevicePath: "/usr/test",
  287. },
  288. },
  289. },
  290. },
  291. Volumes: []api.Volume{
  292. {
  293. Name: "myvolume",
  294. VolumeSource: api.VolumeSource{
  295. HostPath: &api.HostPathVolumeSource{
  296. Path: "/dev/xvdc",
  297. },
  298. },
  299. },
  300. },
  301. },
  302. }
  303. }
  304. podWithoutVolumeDevices := func() *api.Pod {
  305. return &api.Pod{
  306. Spec: api.PodSpec{
  307. RestartPolicy: api.RestartPolicyNever,
  308. Containers: []api.Container{
  309. {
  310. Name: "container1",
  311. Image: "testimage",
  312. },
  313. },
  314. InitContainers: []api.Container{
  315. {
  316. Name: "container1",
  317. Image: "testimage",
  318. },
  319. },
  320. Volumes: []api.Volume{
  321. {
  322. Name: "myvolume",
  323. VolumeSource: api.VolumeSource{
  324. HostPath: &api.HostPathVolumeSource{
  325. Path: "/dev/xvdc",
  326. },
  327. },
  328. },
  329. },
  330. },
  331. }
  332. }
  333. podInfo := []struct {
  334. description string
  335. hasVolumeDevices bool
  336. pod func() *api.Pod
  337. }{
  338. {
  339. description: "has VolumeDevices",
  340. hasVolumeDevices: true,
  341. pod: podWithVolumeDevices,
  342. },
  343. {
  344. description: "does not have VolumeDevices",
  345. hasVolumeDevices: false,
  346. pod: podWithoutVolumeDevices,
  347. },
  348. {
  349. description: "is nil",
  350. hasVolumeDevices: false,
  351. pod: func() *api.Pod { return nil },
  352. },
  353. }
  354. for _, enabled := range []bool{true, false} {
  355. for _, oldPodInfo := range podInfo {
  356. for _, newPodInfo := range podInfo {
  357. oldPodHasVolumeDevices, oldPod := oldPodInfo.hasVolumeDevices, oldPodInfo.pod()
  358. newPodHasVolumeDevices, newPod := newPodInfo.hasVolumeDevices, newPodInfo.pod()
  359. if newPod == nil {
  360. continue
  361. }
  362. t.Run(fmt.Sprintf("feature enabled=%v, old pod %v, new pod %v", enabled, oldPodInfo.description, newPodInfo.description), func(t *testing.T) {
  363. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.BlockVolume, enabled)()
  364. var oldPodSpec *api.PodSpec
  365. if oldPod != nil {
  366. oldPodSpec = &oldPod.Spec
  367. }
  368. dropDisabledFields(&newPod.Spec, nil, oldPodSpec, nil)
  369. // old pod should never be changed
  370. if !reflect.DeepEqual(oldPod, oldPodInfo.pod()) {
  371. t.Errorf("old pod changed: %v", diff.ObjectReflectDiff(oldPod, oldPodInfo.pod()))
  372. }
  373. switch {
  374. case enabled || oldPodHasVolumeDevices:
  375. // new pod should not be changed if the feature is enabled, or if the old pod had VolumeDevices
  376. if !reflect.DeepEqual(newPod, newPodInfo.pod()) {
  377. t.Errorf("new pod changed: %v", diff.ObjectReflectDiff(newPod, newPodInfo.pod()))
  378. }
  379. case newPodHasVolumeDevices:
  380. // new pod should be changed
  381. if reflect.DeepEqual(newPod, newPodInfo.pod()) {
  382. t.Errorf("new pod was not changed")
  383. }
  384. // new pod should not have VolumeDevices
  385. if !reflect.DeepEqual(newPod, podWithoutVolumeDevices()) {
  386. t.Errorf("new pod had VolumeDevices: %v", diff.ObjectReflectDiff(newPod, podWithoutVolumeDevices()))
  387. }
  388. default:
  389. // new pod should not need to be changed
  390. if !reflect.DeepEqual(newPod, newPodInfo.pod()) {
  391. t.Errorf("new pod changed: %v", diff.ObjectReflectDiff(newPod, newPodInfo.pod()))
  392. }
  393. }
  394. })
  395. }
  396. }
  397. }
  398. }
  399. func TestDropSubPath(t *testing.T) {
  400. podWithSubpaths := func() *api.Pod {
  401. return &api.Pod{
  402. Spec: api.PodSpec{
  403. RestartPolicy: api.RestartPolicyNever,
  404. Containers: []api.Container{{Name: "container1", Image: "testimage", VolumeMounts: []api.VolumeMount{{Name: "a", SubPath: "foo"}, {Name: "a", SubPath: "foo2"}, {Name: "a", SubPath: "foo3"}}}},
  405. InitContainers: []api.Container{{Name: "container1", Image: "testimage", VolumeMounts: []api.VolumeMount{{Name: "a", SubPath: "foo"}, {Name: "a", SubPath: "foo2"}}}},
  406. Volumes: []api.Volume{{Name: "a", VolumeSource: api.VolumeSource{HostPath: &api.HostPathVolumeSource{Path: "/dev/xvdc"}}}},
  407. },
  408. }
  409. }
  410. podWithoutSubpaths := func() *api.Pod {
  411. return &api.Pod{
  412. Spec: api.PodSpec{
  413. RestartPolicy: api.RestartPolicyNever,
  414. Containers: []api.Container{{Name: "container1", Image: "testimage", VolumeMounts: []api.VolumeMount{{Name: "a", SubPath: ""}, {Name: "a", SubPath: ""}, {Name: "a", SubPath: ""}}}},
  415. InitContainers: []api.Container{{Name: "container1", Image: "testimage", VolumeMounts: []api.VolumeMount{{Name: "a", SubPath: ""}, {Name: "a", SubPath: ""}}}},
  416. Volumes: []api.Volume{{Name: "a", VolumeSource: api.VolumeSource{HostPath: &api.HostPathVolumeSource{Path: "/dev/xvdc"}}}},
  417. },
  418. }
  419. }
  420. podInfo := []struct {
  421. description string
  422. hasSubpaths bool
  423. pod func() *api.Pod
  424. }{
  425. {
  426. description: "has subpaths",
  427. hasSubpaths: true,
  428. pod: podWithSubpaths,
  429. },
  430. {
  431. description: "does not have subpaths",
  432. hasSubpaths: false,
  433. pod: podWithoutSubpaths,
  434. },
  435. {
  436. description: "is nil",
  437. hasSubpaths: false,
  438. pod: func() *api.Pod { return nil },
  439. },
  440. }
  441. for _, enabled := range []bool{true, false} {
  442. for _, oldPodInfo := range podInfo {
  443. for _, newPodInfo := range podInfo {
  444. oldPodHasSubpaths, oldPod := oldPodInfo.hasSubpaths, oldPodInfo.pod()
  445. newPodHasSubpaths, newPod := newPodInfo.hasSubpaths, newPodInfo.pod()
  446. if newPod == nil {
  447. continue
  448. }
  449. t.Run(fmt.Sprintf("feature enabled=%v, old pod %v, new pod %v", enabled, oldPodInfo.description, newPodInfo.description), func(t *testing.T) {
  450. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeSubpath, enabled)()
  451. var oldPodSpec *api.PodSpec
  452. if oldPod != nil {
  453. oldPodSpec = &oldPod.Spec
  454. }
  455. dropDisabledFields(&newPod.Spec, nil, oldPodSpec, nil)
  456. // old pod should never be changed
  457. if !reflect.DeepEqual(oldPod, oldPodInfo.pod()) {
  458. t.Errorf("old pod changed: %v", diff.ObjectReflectDiff(oldPod, oldPodInfo.pod()))
  459. }
  460. switch {
  461. case enabled || oldPodHasSubpaths:
  462. // new pod should not be changed if the feature is enabled, or if the old pod had subpaths
  463. if !reflect.DeepEqual(newPod, newPodInfo.pod()) {
  464. t.Errorf("new pod changed: %v", diff.ObjectReflectDiff(newPod, newPodInfo.pod()))
  465. }
  466. case newPodHasSubpaths:
  467. // new pod should be changed
  468. if reflect.DeepEqual(newPod, newPodInfo.pod()) {
  469. t.Errorf("new pod was not changed")
  470. }
  471. // new pod should not have subpaths
  472. if !reflect.DeepEqual(newPod, podWithoutSubpaths()) {
  473. t.Errorf("new pod had subpaths: %v", diff.ObjectReflectDiff(newPod, podWithoutSubpaths()))
  474. }
  475. default:
  476. // new pod should not need to be changed
  477. if !reflect.DeepEqual(newPod, newPodInfo.pod()) {
  478. t.Errorf("new pod changed: %v", diff.ObjectReflectDiff(newPod, newPodInfo.pod()))
  479. }
  480. }
  481. })
  482. }
  483. }
  484. }
  485. }
  486. func TestDropRuntimeClass(t *testing.T) {
  487. runtimeClassName := "some_container_engine"
  488. podWithoutRuntimeClass := func() *api.Pod {
  489. return &api.Pod{
  490. Spec: api.PodSpec{
  491. RuntimeClassName: nil,
  492. },
  493. }
  494. }
  495. podWithRuntimeClass := func() *api.Pod {
  496. return &api.Pod{
  497. Spec: api.PodSpec{
  498. RuntimeClassName: &runtimeClassName,
  499. },
  500. }
  501. }
  502. podInfo := []struct {
  503. description string
  504. hasPodRuntimeClassName bool
  505. pod func() *api.Pod
  506. }{
  507. {
  508. description: "pod Without RuntimeClassName",
  509. hasPodRuntimeClassName: false,
  510. pod: podWithoutRuntimeClass,
  511. },
  512. {
  513. description: "pod With RuntimeClassName",
  514. hasPodRuntimeClassName: true,
  515. pod: podWithRuntimeClass,
  516. },
  517. {
  518. description: "is nil",
  519. hasPodRuntimeClassName: false,
  520. pod: func() *api.Pod { return nil },
  521. },
  522. }
  523. for _, enabled := range []bool{true, false} {
  524. for _, oldPodInfo := range podInfo {
  525. for _, newPodInfo := range podInfo {
  526. oldPodHasRuntimeClassName, oldPod := oldPodInfo.hasPodRuntimeClassName, oldPodInfo.pod()
  527. newPodHasRuntimeClassName, newPod := newPodInfo.hasPodRuntimeClassName, newPodInfo.pod()
  528. if newPod == nil {
  529. continue
  530. }
  531. t.Run(fmt.Sprintf("feature enabled=%v, old pod %v, new pod %v", enabled, oldPodInfo.description, newPodInfo.description), func(t *testing.T) {
  532. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.RuntimeClass, enabled)()
  533. var oldPodSpec *api.PodSpec
  534. if oldPod != nil {
  535. oldPodSpec = &oldPod.Spec
  536. }
  537. dropDisabledFields(&newPod.Spec, nil, oldPodSpec, nil)
  538. // old pod should never be changed
  539. if !reflect.DeepEqual(oldPod, oldPodInfo.pod()) {
  540. t.Errorf("old pod changed: %v", diff.ObjectReflectDiff(oldPod, oldPodInfo.pod()))
  541. }
  542. switch {
  543. case enabled || oldPodHasRuntimeClassName:
  544. // new pod should not be changed if the feature is enabled, or if the old pod had RuntimeClass
  545. if !reflect.DeepEqual(newPod, newPodInfo.pod()) {
  546. t.Errorf("new pod changed: %v", diff.ObjectReflectDiff(newPod, newPodInfo.pod()))
  547. }
  548. case newPodHasRuntimeClassName:
  549. // new pod should be changed
  550. if reflect.DeepEqual(newPod, newPodInfo.pod()) {
  551. t.Errorf("new pod was not changed")
  552. }
  553. // new pod should not have RuntimeClass
  554. if !reflect.DeepEqual(newPod, podWithoutRuntimeClass()) {
  555. t.Errorf("new pod had PodRuntimeClassName: %v", diff.ObjectReflectDiff(newPod, podWithoutRuntimeClass()))
  556. }
  557. default:
  558. // new pod should not need to be changed
  559. if !reflect.DeepEqual(newPod, newPodInfo.pod()) {
  560. t.Errorf("new pod changed: %v", diff.ObjectReflectDiff(newPod, newPodInfo.pod()))
  561. }
  562. }
  563. })
  564. }
  565. }
  566. }
  567. }
  568. func TestDropProcMount(t *testing.T) {
  569. procMount := api.UnmaskedProcMount
  570. defaultProcMount := api.DefaultProcMount
  571. podWithProcMount := func() *api.Pod {
  572. return &api.Pod{
  573. Spec: api.PodSpec{
  574. RestartPolicy: api.RestartPolicyNever,
  575. Containers: []api.Container{{Name: "container1", Image: "testimage", SecurityContext: &api.SecurityContext{ProcMount: &procMount}}},
  576. InitContainers: []api.Container{{Name: "container1", Image: "testimage", SecurityContext: &api.SecurityContext{ProcMount: &procMount}}},
  577. },
  578. }
  579. }
  580. podWithDefaultProcMount := func() *api.Pod {
  581. return &api.Pod{
  582. Spec: api.PodSpec{
  583. RestartPolicy: api.RestartPolicyNever,
  584. Containers: []api.Container{{Name: "container1", Image: "testimage", SecurityContext: &api.SecurityContext{ProcMount: &defaultProcMount}}},
  585. InitContainers: []api.Container{{Name: "container1", Image: "testimage", SecurityContext: &api.SecurityContext{ProcMount: &defaultProcMount}}},
  586. },
  587. }
  588. }
  589. podWithoutProcMount := func() *api.Pod {
  590. return &api.Pod{
  591. Spec: api.PodSpec{
  592. RestartPolicy: api.RestartPolicyNever,
  593. Containers: []api.Container{{Name: "container1", Image: "testimage", SecurityContext: &api.SecurityContext{ProcMount: nil}}},
  594. InitContainers: []api.Container{{Name: "container1", Image: "testimage", SecurityContext: &api.SecurityContext{ProcMount: nil}}},
  595. },
  596. }
  597. }
  598. podInfo := []struct {
  599. description string
  600. hasProcMount bool
  601. pod func() *api.Pod
  602. }{
  603. {
  604. description: "has ProcMount",
  605. hasProcMount: true,
  606. pod: podWithProcMount,
  607. },
  608. {
  609. description: "has default ProcMount",
  610. hasProcMount: false,
  611. pod: podWithDefaultProcMount,
  612. },
  613. {
  614. description: "does not have ProcMount",
  615. hasProcMount: false,
  616. pod: podWithoutProcMount,
  617. },
  618. {
  619. description: "is nil",
  620. hasProcMount: false,
  621. pod: func() *api.Pod { return nil },
  622. },
  623. }
  624. for _, enabled := range []bool{true, false} {
  625. for _, oldPodInfo := range podInfo {
  626. for _, newPodInfo := range podInfo {
  627. oldPodHasProcMount, oldPod := oldPodInfo.hasProcMount, oldPodInfo.pod()
  628. newPodHasProcMount, newPod := newPodInfo.hasProcMount, newPodInfo.pod()
  629. if newPod == nil {
  630. continue
  631. }
  632. t.Run(fmt.Sprintf("feature enabled=%v, old pod %v, new pod %v", enabled, oldPodInfo.description, newPodInfo.description), func(t *testing.T) {
  633. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ProcMountType, enabled)()
  634. var oldPodSpec *api.PodSpec
  635. if oldPod != nil {
  636. oldPodSpec = &oldPod.Spec
  637. }
  638. dropDisabledFields(&newPod.Spec, nil, oldPodSpec, nil)
  639. // old pod should never be changed
  640. if !reflect.DeepEqual(oldPod, oldPodInfo.pod()) {
  641. t.Errorf("old pod changed: %v", diff.ObjectReflectDiff(oldPod, oldPodInfo.pod()))
  642. }
  643. switch {
  644. case enabled || oldPodHasProcMount:
  645. // new pod should not be changed if the feature is enabled, or if the old pod had ProcMount
  646. if !reflect.DeepEqual(newPod, newPodInfo.pod()) {
  647. t.Errorf("new pod changed: %v", diff.ObjectReflectDiff(newPod, newPodInfo.pod()))
  648. }
  649. case newPodHasProcMount:
  650. // new pod should be changed
  651. if reflect.DeepEqual(newPod, newPodInfo.pod()) {
  652. t.Errorf("new pod was not changed")
  653. }
  654. // new pod should not have ProcMount
  655. if procMountInUse(&newPod.Spec) {
  656. t.Errorf("new pod had ProcMount: %#v", &newPod.Spec)
  657. }
  658. default:
  659. // new pod should not need to be changed
  660. if !reflect.DeepEqual(newPod, newPodInfo.pod()) {
  661. t.Errorf("new pod changed: %v", diff.ObjectReflectDiff(newPod, newPodInfo.pod()))
  662. }
  663. }
  664. })
  665. }
  666. }
  667. }
  668. }
  669. func TestDropPodPriority(t *testing.T) {
  670. podPriority := int32(1000)
  671. podWithoutPriority := func() *api.Pod {
  672. return &api.Pod{
  673. Spec: api.PodSpec{
  674. Priority: nil,
  675. PriorityClassName: "",
  676. },
  677. }
  678. }
  679. podWithPriority := func() *api.Pod {
  680. return &api.Pod{
  681. Spec: api.PodSpec{
  682. Priority: &podPriority,
  683. PriorityClassName: "",
  684. },
  685. }
  686. }
  687. podWithPriorityClassOnly := func() *api.Pod {
  688. return &api.Pod{
  689. Spec: api.PodSpec{
  690. Priority: nil,
  691. PriorityClassName: "HighPriorityClass",
  692. },
  693. }
  694. }
  695. podWithBothPriorityFields := func() *api.Pod {
  696. return &api.Pod{
  697. Spec: api.PodSpec{
  698. Priority: &podPriority,
  699. PriorityClassName: "HighPriorityClass",
  700. },
  701. }
  702. }
  703. podInfo := []struct {
  704. description string
  705. hasPodPriority bool
  706. pod func() *api.Pod
  707. }{
  708. {
  709. description: "pod With no PodPriority fields set",
  710. hasPodPriority: false,
  711. pod: podWithoutPriority,
  712. },
  713. {
  714. description: "feature disabled and pod With PodPriority field set but class name not set",
  715. hasPodPriority: true,
  716. pod: podWithPriority,
  717. },
  718. {
  719. description: "feature disabled and pod With PodPriority ClassName field set but PortPriority not set",
  720. hasPodPriority: true,
  721. pod: podWithPriorityClassOnly,
  722. },
  723. {
  724. description: "feature disabled and pod With both PodPriority ClassName and PodPriority fields set",
  725. hasPodPriority: true,
  726. pod: podWithBothPriorityFields,
  727. },
  728. {
  729. description: "is nil",
  730. hasPodPriority: false,
  731. pod: func() *api.Pod { return nil },
  732. },
  733. }
  734. for _, enabled := range []bool{true, false} {
  735. for _, oldPodInfo := range podInfo {
  736. for _, newPodInfo := range podInfo {
  737. oldPodHasPodPriority, oldPod := oldPodInfo.hasPodPriority, oldPodInfo.pod()
  738. newPodHasPodPriority, newPod := newPodInfo.hasPodPriority, newPodInfo.pod()
  739. if newPod == nil {
  740. continue
  741. }
  742. t.Run(fmt.Sprintf("feature enabled=%v, old pod %v, new pod %v", enabled, oldPodInfo.description, newPodInfo.description), func(t *testing.T) {
  743. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodPriority, enabled)()
  744. var oldPodSpec *api.PodSpec
  745. if oldPod != nil {
  746. oldPodSpec = &oldPod.Spec
  747. }
  748. dropDisabledFields(&newPod.Spec, nil, oldPodSpec, nil)
  749. // old pod should never be changed
  750. if !reflect.DeepEqual(oldPod, oldPodInfo.pod()) {
  751. t.Errorf("old pod changed: %v", diff.ObjectReflectDiff(oldPod, oldPodInfo.pod()))
  752. }
  753. switch {
  754. case enabled || oldPodHasPodPriority:
  755. // new pod should not be changed if the feature is enabled, or if the old pod had PodPriority
  756. if !reflect.DeepEqual(newPod, newPodInfo.pod()) {
  757. t.Errorf("new pod changed: %v", diff.ObjectReflectDiff(newPod, newPodInfo.pod()))
  758. }
  759. case newPodHasPodPriority:
  760. // new pod should be changed
  761. if reflect.DeepEqual(newPod, newPodInfo.pod()) {
  762. t.Errorf("new pod was not changed")
  763. }
  764. // new pod should not have PodPriority
  765. if !reflect.DeepEqual(newPod, podWithoutPriority()) {
  766. t.Errorf("new pod had PodPriority: %v", diff.ObjectReflectDiff(newPod, podWithoutPriority()))
  767. }
  768. default:
  769. // new pod should not need to be changed
  770. if !reflect.DeepEqual(newPod, newPodInfo.pod()) {
  771. t.Errorf("new pod changed: %v", diff.ObjectReflectDiff(newPod, newPodInfo.pod()))
  772. }
  773. }
  774. })
  775. }
  776. }
  777. }
  778. }
  779. func TestDropEmptyDirSizeLimit(t *testing.T) {
  780. sizeLimit := resource.MustParse("1Gi")
  781. podWithEmptyDirSizeLimit := func() *api.Pod {
  782. return &api.Pod{
  783. Spec: api.PodSpec{
  784. RestartPolicy: api.RestartPolicyNever,
  785. Volumes: []api.Volume{
  786. {
  787. Name: "a",
  788. VolumeSource: api.VolumeSource{
  789. EmptyDir: &api.EmptyDirVolumeSource{
  790. Medium: "memory",
  791. SizeLimit: &sizeLimit,
  792. },
  793. },
  794. },
  795. },
  796. },
  797. }
  798. }
  799. podWithoutEmptyDirSizeLimit := func() *api.Pod {
  800. return &api.Pod{
  801. Spec: api.PodSpec{
  802. RestartPolicy: api.RestartPolicyNever,
  803. Volumes: []api.Volume{
  804. {
  805. Name: "a",
  806. VolumeSource: api.VolumeSource{
  807. EmptyDir: &api.EmptyDirVolumeSource{
  808. Medium: "memory",
  809. },
  810. },
  811. },
  812. },
  813. },
  814. }
  815. }
  816. podInfo := []struct {
  817. description string
  818. hasEmptyDirSizeLimit bool
  819. pod func() *api.Pod
  820. }{
  821. {
  822. description: "has EmptyDir Size Limit",
  823. hasEmptyDirSizeLimit: true,
  824. pod: podWithEmptyDirSizeLimit,
  825. },
  826. {
  827. description: "does not have EmptyDir Size Limit",
  828. hasEmptyDirSizeLimit: false,
  829. pod: podWithoutEmptyDirSizeLimit,
  830. },
  831. {
  832. description: "is nil",
  833. hasEmptyDirSizeLimit: false,
  834. pod: func() *api.Pod { return nil },
  835. },
  836. }
  837. for _, enabled := range []bool{true, false} {
  838. for _, oldPodInfo := range podInfo {
  839. for _, newPodInfo := range podInfo {
  840. oldPodHasEmptyDirSizeLimit, oldPod := oldPodInfo.hasEmptyDirSizeLimit, oldPodInfo.pod()
  841. newPodHasEmptyDirSizeLimit, newPod := newPodInfo.hasEmptyDirSizeLimit, newPodInfo.pod()
  842. if newPod == nil {
  843. continue
  844. }
  845. t.Run(fmt.Sprintf("feature enabled=%v, old pod %v, new pod %v", enabled, oldPodInfo.description, newPodInfo.description), func(t *testing.T) {
  846. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.LocalStorageCapacityIsolation, enabled)()
  847. var oldPodSpec *api.PodSpec
  848. if oldPod != nil {
  849. oldPodSpec = &oldPod.Spec
  850. }
  851. dropDisabledFields(&newPod.Spec, nil, oldPodSpec, nil)
  852. // old pod should never be changed
  853. if !reflect.DeepEqual(oldPod, oldPodInfo.pod()) {
  854. t.Errorf("old pod changed: %v", diff.ObjectReflectDiff(oldPod, oldPodInfo.pod()))
  855. }
  856. switch {
  857. case enabled || oldPodHasEmptyDirSizeLimit:
  858. // new pod should not be changed if the feature is enabled, or if the old pod had EmptyDir SizeLimit
  859. if !reflect.DeepEqual(newPod, newPodInfo.pod()) {
  860. t.Errorf("new pod changed: %v", diff.ObjectReflectDiff(newPod, newPodInfo.pod()))
  861. }
  862. case newPodHasEmptyDirSizeLimit:
  863. // new pod should be changed
  864. if reflect.DeepEqual(newPod, newPodInfo.pod()) {
  865. t.Errorf("new pod was not changed")
  866. }
  867. // new pod should not have EmptyDir SizeLimit
  868. if !reflect.DeepEqual(newPod, podWithoutEmptyDirSizeLimit()) {
  869. t.Errorf("new pod had EmptyDir SizeLimit: %v", diff.ObjectReflectDiff(newPod, podWithoutEmptyDirSizeLimit()))
  870. }
  871. default:
  872. // new pod should not need to be changed
  873. if !reflect.DeepEqual(newPod, newPodInfo.pod()) {
  874. t.Errorf("new pod changed: %v", diff.ObjectReflectDiff(newPod, newPodInfo.pod()))
  875. }
  876. }
  877. })
  878. }
  879. }
  880. }
  881. }
  882. func TestDropPodShareProcessNamespace(t *testing.T) {
  883. podWithShareProcessNamespace := func() *api.Pod {
  884. return &api.Pod{
  885. Spec: api.PodSpec{
  886. SecurityContext: &api.PodSecurityContext{
  887. ShareProcessNamespace: &[]bool{true}[0],
  888. },
  889. },
  890. }
  891. }
  892. podWithoutShareProcessNamespace := func() *api.Pod {
  893. return &api.Pod{
  894. Spec: api.PodSpec{
  895. SecurityContext: &api.PodSecurityContext{},
  896. },
  897. }
  898. }
  899. podWithoutSecurityContext := func() *api.Pod {
  900. return &api.Pod{
  901. Spec: api.PodSpec{},
  902. }
  903. }
  904. podInfo := []struct {
  905. description string
  906. hasShareProcessNamespace bool
  907. pod func() *api.Pod
  908. }{
  909. {
  910. description: "has ShareProcessNamespace",
  911. hasShareProcessNamespace: true,
  912. pod: podWithShareProcessNamespace,
  913. },
  914. {
  915. description: "does not have ShareProcessNamespace",
  916. hasShareProcessNamespace: false,
  917. pod: podWithoutShareProcessNamespace,
  918. },
  919. {
  920. description: "does not have SecurityContext",
  921. hasShareProcessNamespace: false,
  922. pod: podWithoutSecurityContext,
  923. },
  924. {
  925. description: "is nil",
  926. hasShareProcessNamespace: false,
  927. pod: func() *api.Pod { return nil },
  928. },
  929. }
  930. for _, enabled := range []bool{true, false} {
  931. for _, oldPodInfo := range podInfo {
  932. for _, newPodInfo := range podInfo {
  933. oldPodHasShareProcessNamespace, oldPod := oldPodInfo.hasShareProcessNamespace, oldPodInfo.pod()
  934. newPodHasShareProcessNamespace, newPod := newPodInfo.hasShareProcessNamespace, newPodInfo.pod()
  935. if newPod == nil {
  936. continue
  937. }
  938. t.Run(fmt.Sprintf("feature enabled=%v, old pod %v, new pod %v", enabled, oldPodInfo.description, newPodInfo.description), func(t *testing.T) {
  939. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodShareProcessNamespace, enabled)()
  940. var oldPodSpec *api.PodSpec
  941. if oldPod != nil {
  942. oldPodSpec = &oldPod.Spec
  943. }
  944. dropDisabledFields(&newPod.Spec, nil, oldPodSpec, nil)
  945. // old pod should never be changed
  946. if !reflect.DeepEqual(oldPod, oldPodInfo.pod()) {
  947. t.Errorf("old pod changed: %v", diff.ObjectReflectDiff(oldPod, oldPodInfo.pod()))
  948. }
  949. switch {
  950. case enabled || oldPodHasShareProcessNamespace:
  951. // new pod should not be changed if the feature is enabled, or if the old pod had ShareProcessNamespace set
  952. if !reflect.DeepEqual(newPod, newPodInfo.pod()) {
  953. t.Errorf("new pod changed: %v", diff.ObjectReflectDiff(newPod, newPodInfo.pod()))
  954. }
  955. case newPodHasShareProcessNamespace:
  956. // new pod should be changed
  957. if reflect.DeepEqual(newPod, newPodInfo.pod()) {
  958. t.Errorf("new pod was not changed")
  959. }
  960. // new pod should not have ShareProcessNamespace
  961. if !reflect.DeepEqual(newPod, podWithoutShareProcessNamespace()) {
  962. t.Errorf("new pod had ShareProcessNamespace: %v", diff.ObjectReflectDiff(newPod, podWithoutShareProcessNamespace()))
  963. }
  964. default:
  965. // new pod should not need to be changed
  966. if !reflect.DeepEqual(newPod, newPodInfo.pod()) {
  967. t.Errorf("new pod changed: %v", diff.ObjectReflectDiff(newPod, newPodInfo.pod()))
  968. }
  969. }
  970. })
  971. }
  972. }
  973. }
  974. }
  975. func TestDropAppArmor(t *testing.T) {
  976. podWithAppArmor := func() *api.Pod {
  977. return &api.Pod{
  978. ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{"a": "1", apparmor.ContainerAnnotationKeyPrefix + "foo": "default"}},
  979. Spec: api.PodSpec{},
  980. }
  981. }
  982. podWithoutAppArmor := func() *api.Pod {
  983. return &api.Pod{
  984. ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{"a": "1"}},
  985. Spec: api.PodSpec{},
  986. }
  987. }
  988. podInfo := []struct {
  989. description string
  990. hasAppArmor bool
  991. pod func() *api.Pod
  992. }{
  993. {
  994. description: "has AppArmor",
  995. hasAppArmor: true,
  996. pod: podWithAppArmor,
  997. },
  998. {
  999. description: "does not have AppArmor",
  1000. hasAppArmor: false,
  1001. pod: podWithoutAppArmor,
  1002. },
  1003. {
  1004. description: "is nil",
  1005. hasAppArmor: false,
  1006. pod: func() *api.Pod { return nil },
  1007. },
  1008. }
  1009. for _, enabled := range []bool{true, false} {
  1010. for _, oldPodInfo := range podInfo {
  1011. for _, newPodInfo := range podInfo {
  1012. oldPodHasAppArmor, oldPod := oldPodInfo.hasAppArmor, oldPodInfo.pod()
  1013. newPodHasAppArmor, newPod := newPodInfo.hasAppArmor, newPodInfo.pod()
  1014. if newPod == nil {
  1015. continue
  1016. }
  1017. t.Run(fmt.Sprintf("feature enabled=%v, old pod %v, new pod %v", enabled, oldPodInfo.description, newPodInfo.description), func(t *testing.T) {
  1018. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.AppArmor, enabled)()
  1019. DropDisabledPodFields(newPod, oldPod)
  1020. // old pod should never be changed
  1021. if !reflect.DeepEqual(oldPod, oldPodInfo.pod()) {
  1022. t.Errorf("old pod changed: %v", diff.ObjectReflectDiff(oldPod, oldPodInfo.pod()))
  1023. }
  1024. switch {
  1025. case enabled || oldPodHasAppArmor:
  1026. // new pod should not be changed if the feature is enabled, or if the old pod had AppArmor
  1027. if !reflect.DeepEqual(newPod, newPodInfo.pod()) {
  1028. t.Errorf("new pod changed: %v", diff.ObjectReflectDiff(newPod, newPodInfo.pod()))
  1029. }
  1030. case newPodHasAppArmor:
  1031. // new pod should be changed
  1032. if reflect.DeepEqual(newPod, newPodInfo.pod()) {
  1033. t.Errorf("new pod was not changed")
  1034. }
  1035. // new pod should not have AppArmor
  1036. if !reflect.DeepEqual(newPod, podWithoutAppArmor()) {
  1037. t.Errorf("new pod had EmptyDir SizeLimit: %v", diff.ObjectReflectDiff(newPod, podWithoutAppArmor()))
  1038. }
  1039. default:
  1040. // new pod should not need to be changed
  1041. if !reflect.DeepEqual(newPod, newPodInfo.pod()) {
  1042. t.Errorf("new pod changed: %v", diff.ObjectReflectDiff(newPod, newPodInfo.pod()))
  1043. }
  1044. }
  1045. })
  1046. }
  1047. }
  1048. }
  1049. }
  1050. func TestDropTokenRequestProjection(t *testing.T) {
  1051. podWithoutTRProjection := func() *api.Pod {
  1052. return &api.Pod{
  1053. Spec: api.PodSpec{
  1054. Volumes: []api.Volume{{
  1055. VolumeSource: api.VolumeSource{
  1056. Projected: &api.ProjectedVolumeSource{
  1057. Sources: []api.VolumeProjection{{
  1058. ServiceAccountToken: nil,
  1059. }},
  1060. }}},
  1061. },
  1062. },
  1063. }
  1064. }
  1065. podWithoutProjectedVolumeSource := func() *api.Pod {
  1066. return &api.Pod{
  1067. Spec: api.PodSpec{
  1068. Volumes: []api.Volume{
  1069. {VolumeSource: api.VolumeSource{
  1070. ConfigMap: &api.ConfigMapVolumeSource{},
  1071. }},
  1072. },
  1073. },
  1074. }
  1075. }
  1076. podWithTRProjection := func() *api.Pod {
  1077. return &api.Pod{
  1078. Spec: api.PodSpec{
  1079. Volumes: []api.Volume{{
  1080. VolumeSource: api.VolumeSource{
  1081. Projected: &api.ProjectedVolumeSource{
  1082. Sources: []api.VolumeProjection{{
  1083. ServiceAccountToken: &api.ServiceAccountTokenProjection{
  1084. Audience: "api",
  1085. ExpirationSeconds: 3600,
  1086. Path: "token",
  1087. }},
  1088. }},
  1089. },
  1090. },
  1091. },
  1092. }}
  1093. }
  1094. podInfo := []struct {
  1095. description string
  1096. hasTRProjection bool
  1097. pod func() *api.Pod
  1098. }{
  1099. {
  1100. description: "has TokenRequestProjection",
  1101. hasTRProjection: true,
  1102. pod: podWithTRProjection,
  1103. },
  1104. {
  1105. description: "does not have TokenRequestProjection",
  1106. hasTRProjection: false,
  1107. pod: podWithoutTRProjection,
  1108. },
  1109. {
  1110. description: "does not have ProjectedVolumeSource",
  1111. hasTRProjection: false,
  1112. pod: podWithoutProjectedVolumeSource,
  1113. },
  1114. {
  1115. description: "is nil",
  1116. hasTRProjection: false,
  1117. pod: func() *api.Pod { return nil },
  1118. },
  1119. }
  1120. for _, enabled := range []bool{true, false} {
  1121. for _, oldPodInfo := range podInfo {
  1122. for _, newPodInfo := range podInfo {
  1123. oldPodhasTRProjection, oldPod := oldPodInfo.hasTRProjection, oldPodInfo.pod()
  1124. newPodhasTRProjection, newPod := newPodInfo.hasTRProjection, newPodInfo.pod()
  1125. if newPod == nil {
  1126. continue
  1127. }
  1128. t.Run(fmt.Sprintf("feature enabled=%v, old pod %v, new pod %v", enabled, oldPodInfo.description, newPodInfo.description), func(t *testing.T) {
  1129. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.TokenRequestProjection, enabled)()
  1130. var oldPodSpec *api.PodSpec
  1131. if oldPod != nil {
  1132. oldPodSpec = &oldPod.Spec
  1133. }
  1134. dropDisabledFields(&newPod.Spec, nil, oldPodSpec, nil)
  1135. // old pod should never be changed
  1136. if !reflect.DeepEqual(oldPod, oldPodInfo.pod()) {
  1137. t.Errorf("old pod changed: %v", diff.ObjectReflectDiff(oldPod, oldPodInfo.pod()))
  1138. }
  1139. switch {
  1140. case enabled || oldPodhasTRProjection:
  1141. if !reflect.DeepEqual(newPod, newPodInfo.pod()) {
  1142. t.Errorf("new pod changed: %v", diff.ObjectReflectDiff(newPod, newPodInfo.pod()))
  1143. }
  1144. case newPodhasTRProjection:
  1145. // new pod should be changed
  1146. if reflect.DeepEqual(newPod, newPodInfo.pod()) {
  1147. t.Errorf("%v", oldPod)
  1148. t.Errorf("%v", newPod)
  1149. t.Errorf("new pod was not changed")
  1150. }
  1151. if !reflect.DeepEqual(newPod, podWithoutTRProjection()) {
  1152. t.Errorf("new pod had Tokenrequestprojection: %v", diff.ObjectReflectDiff(newPod, podWithoutTRProjection()))
  1153. }
  1154. default:
  1155. // new pod should not need to be changed
  1156. if !reflect.DeepEqual(newPod, newPodInfo.pod()) {
  1157. t.Errorf("new pod changed: %v", diff.ObjectReflectDiff(newPod, newPodInfo.pod()))
  1158. }
  1159. }
  1160. })
  1161. }
  1162. }
  1163. }
  1164. }
  1165. func TestDropRunAsGroup(t *testing.T) {
  1166. group := func() *int64 {
  1167. testGroup := int64(1000)
  1168. return &testGroup
  1169. }
  1170. defaultProcMount := api.DefaultProcMount
  1171. defaultSecurityContext := func() *api.SecurityContext {
  1172. return &api.SecurityContext{ProcMount: &defaultProcMount}
  1173. }
  1174. securityContextWithRunAsGroup := func() *api.SecurityContext {
  1175. return &api.SecurityContext{ProcMount: &defaultProcMount, RunAsGroup: group()}
  1176. }
  1177. podWithoutRunAsGroup := func() *api.Pod {
  1178. return &api.Pod{
  1179. Spec: api.PodSpec{
  1180. RestartPolicy: api.RestartPolicyNever,
  1181. SecurityContext: &api.PodSecurityContext{},
  1182. Containers: []api.Container{{Name: "container1", Image: "testimage", SecurityContext: defaultSecurityContext()}},
  1183. InitContainers: []api.Container{{Name: "initContainer1", Image: "testimage", SecurityContext: defaultSecurityContext()}},
  1184. },
  1185. }
  1186. }
  1187. podWithRunAsGroupInPod := func() *api.Pod {
  1188. return &api.Pod{
  1189. Spec: api.PodSpec{
  1190. RestartPolicy: api.RestartPolicyNever,
  1191. SecurityContext: &api.PodSecurityContext{RunAsGroup: group()},
  1192. Containers: []api.Container{{Name: "container1", Image: "testimage", SecurityContext: defaultSecurityContext()}},
  1193. InitContainers: []api.Container{{Name: "initContainer1", Image: "testimage", SecurityContext: defaultSecurityContext()}},
  1194. },
  1195. }
  1196. }
  1197. podWithRunAsGroupInContainers := func() *api.Pod {
  1198. return &api.Pod{
  1199. Spec: api.PodSpec{
  1200. RestartPolicy: api.RestartPolicyNever,
  1201. SecurityContext: &api.PodSecurityContext{},
  1202. Containers: []api.Container{{Name: "container1", Image: "testimage", SecurityContext: securityContextWithRunAsGroup()}},
  1203. InitContainers: []api.Container{{Name: "initContainer1", Image: "testimage", SecurityContext: defaultSecurityContext()}},
  1204. },
  1205. }
  1206. }
  1207. podWithRunAsGroupInInitContainers := func() *api.Pod {
  1208. return &api.Pod{
  1209. Spec: api.PodSpec{
  1210. RestartPolicy: api.RestartPolicyNever,
  1211. SecurityContext: &api.PodSecurityContext{},
  1212. Containers: []api.Container{{Name: "container1", Image: "testimage", SecurityContext: defaultSecurityContext()}},
  1213. InitContainers: []api.Container{{Name: "initContainer1", Image: "testimage", SecurityContext: securityContextWithRunAsGroup()}},
  1214. },
  1215. }
  1216. }
  1217. podInfo := []struct {
  1218. description string
  1219. hasRunAsGroup bool
  1220. pod func() *api.Pod
  1221. }{
  1222. {
  1223. description: "have RunAsGroup in Pod",
  1224. hasRunAsGroup: true,
  1225. pod: podWithRunAsGroupInPod,
  1226. },
  1227. {
  1228. description: "have RunAsGroup in Container",
  1229. hasRunAsGroup: true,
  1230. pod: podWithRunAsGroupInContainers,
  1231. },
  1232. {
  1233. description: "have RunAsGroup in InitContainer",
  1234. hasRunAsGroup: true,
  1235. pod: podWithRunAsGroupInInitContainers,
  1236. },
  1237. {
  1238. description: "does not have RunAsGroup",
  1239. hasRunAsGroup: false,
  1240. pod: podWithoutRunAsGroup,
  1241. },
  1242. {
  1243. description: "is nil",
  1244. hasRunAsGroup: false,
  1245. pod: func() *api.Pod { return nil },
  1246. },
  1247. }
  1248. for _, enabled := range []bool{true, false} {
  1249. for _, oldPodInfo := range podInfo {
  1250. for _, newPodInfo := range podInfo {
  1251. oldPodHasRunAsGroup, oldPod := oldPodInfo.hasRunAsGroup, oldPodInfo.pod()
  1252. newPodHasRunAsGroup, newPod := newPodInfo.hasRunAsGroup, newPodInfo.pod()
  1253. if newPod == nil {
  1254. continue
  1255. }
  1256. t.Run(fmt.Sprintf("feature enabled=%v, old pod %v, new pod %v", enabled, oldPodInfo.description, newPodInfo.description), func(t *testing.T) {
  1257. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.RunAsGroup, enabled)()
  1258. var oldPodSpec *api.PodSpec
  1259. if oldPod != nil {
  1260. oldPodSpec = &oldPod.Spec
  1261. }
  1262. dropDisabledFields(&newPod.Spec, nil, oldPodSpec, nil)
  1263. // old pod should never be changed
  1264. if !reflect.DeepEqual(oldPod, oldPodInfo.pod()) {
  1265. t.Errorf("old pod changed: %v", diff.ObjectReflectDiff(oldPod, oldPodInfo.pod()))
  1266. }
  1267. switch {
  1268. case enabled || oldPodHasRunAsGroup:
  1269. // new pod should not be changed if the feature is enabled, or if the old pod had RunAsGroup
  1270. if !reflect.DeepEqual(newPod, newPodInfo.pod()) {
  1271. t.Errorf("new pod changed: %v", diff.ObjectReflectDiff(newPod, newPodInfo.pod()))
  1272. }
  1273. case newPodHasRunAsGroup:
  1274. // new pod should be changed
  1275. if reflect.DeepEqual(newPod, newPodInfo.pod()) {
  1276. t.Errorf("%v", oldPod)
  1277. t.Errorf("%v", newPod)
  1278. t.Errorf("new pod was not changed")
  1279. }
  1280. // new pod should not have RunAsGroup
  1281. if !reflect.DeepEqual(newPod, podWithoutRunAsGroup()) {
  1282. t.Errorf("new pod had RunAsGroup: %v", diff.ObjectReflectDiff(newPod, podWithoutRunAsGroup()))
  1283. }
  1284. default:
  1285. // new pod should not need to be changed
  1286. if !reflect.DeepEqual(newPod, newPodInfo.pod()) {
  1287. t.Errorf("new pod changed: %v", diff.ObjectReflectDiff(newPod, newPodInfo.pod()))
  1288. }
  1289. }
  1290. })
  1291. }
  1292. }
  1293. }
  1294. }
  1295. func TestDropGMSAFields(t *testing.T) {
  1296. defaultContainerSecurityContextFactory := func() *api.SecurityContext {
  1297. defaultProcMount := api.DefaultProcMount
  1298. return &api.SecurityContext{ProcMount: &defaultProcMount}
  1299. }
  1300. podWithoutWindowsOptionsFactory := func() *api.Pod {
  1301. return &api.Pod{
  1302. Spec: api.PodSpec{
  1303. RestartPolicy: api.RestartPolicyNever,
  1304. SecurityContext: &api.PodSecurityContext{},
  1305. Containers: []api.Container{{Name: "container1", Image: "testimage", SecurityContext: defaultContainerSecurityContextFactory()}},
  1306. InitContainers: []api.Container{{Name: "initContainer1", Image: "testimage", SecurityContext: defaultContainerSecurityContextFactory()}},
  1307. },
  1308. }
  1309. }
  1310. type podFactoryInfo struct {
  1311. description string
  1312. hasGMSAField bool
  1313. // this factory should generate the input pod whose spec will be fed to dropDisabledFields
  1314. podFactory func() *api.Pod
  1315. // this factory should generate the expected pod after the GMSA fields have been dropped
  1316. // we can't just use podWithoutWindowsOptionsFactory as is for this, since in some cases
  1317. // we'll be left with a WindowsSecurityContextOptions struct with no GMSA field set, as opposed
  1318. // to a nil pointer in the pod generated by podWithoutWindowsOptionsFactory
  1319. // if this field is not set, it will default to the podFactory
  1320. strippedPodFactory func() *api.Pod
  1321. }
  1322. podFactoryInfos := []podFactoryInfo{
  1323. {
  1324. description: "does not have any GMSA field set",
  1325. hasGMSAField: false,
  1326. podFactory: podWithoutWindowsOptionsFactory,
  1327. },
  1328. {
  1329. description: "has a pod-level WindowsSecurityContextOptions struct with no GMSA field set",
  1330. hasGMSAField: false,
  1331. podFactory: func() *api.Pod {
  1332. pod := podWithoutWindowsOptionsFactory()
  1333. pod.Spec.SecurityContext.WindowsOptions = &api.WindowsSecurityContextOptions{}
  1334. return pod
  1335. },
  1336. },
  1337. {
  1338. description: "has a WindowsSecurityContextOptions struct with no GMSA field set on a container",
  1339. hasGMSAField: false,
  1340. podFactory: func() *api.Pod {
  1341. pod := podWithoutWindowsOptionsFactory()
  1342. pod.Spec.Containers[0].SecurityContext.WindowsOptions = &api.WindowsSecurityContextOptions{}
  1343. return pod
  1344. },
  1345. },
  1346. {
  1347. description: "has a WindowsSecurityContextOptions struct with no GMSA field set on an init container",
  1348. hasGMSAField: false,
  1349. podFactory: func() *api.Pod {
  1350. pod := podWithoutWindowsOptionsFactory()
  1351. pod.Spec.InitContainers[0].SecurityContext.WindowsOptions = &api.WindowsSecurityContextOptions{}
  1352. return pod
  1353. },
  1354. },
  1355. {
  1356. description: "is nil",
  1357. hasGMSAField: false,
  1358. podFactory: func() *api.Pod { return nil },
  1359. },
  1360. }
  1361. toPtr := func(s string) *string {
  1362. return &s
  1363. }
  1364. addGMSACredentialSpecName := func(windowsOptions *api.WindowsSecurityContextOptions) {
  1365. windowsOptions.GMSACredentialSpecName = toPtr("dummy-gmsa-cred-spec-name")
  1366. }
  1367. addGMSACredentialSpec := func(windowsOptions *api.WindowsSecurityContextOptions) {
  1368. windowsOptions.GMSACredentialSpec = toPtr("dummy-gmsa-cred-spec-contents")
  1369. }
  1370. addBothGMSAFields := func(windowsOptions *api.WindowsSecurityContextOptions) {
  1371. addGMSACredentialSpecName(windowsOptions)
  1372. addGMSACredentialSpec(windowsOptions)
  1373. }
  1374. for fieldName, windowsOptionsTransformingFunc := range map[string]func(*api.WindowsSecurityContextOptions){
  1375. "GMSACredentialSpecName field": addGMSACredentialSpecName,
  1376. "GMSACredentialSpec field": addGMSACredentialSpec,
  1377. "both GMSA fields": addBothGMSAFields,
  1378. } {
  1379. // yes, these variables are indeed needed for the closure to work
  1380. // properly, please do NOT remove them
  1381. name := fieldName
  1382. transformingFunc := windowsOptionsTransformingFunc
  1383. windowsOptionsWithGMSAFieldFactory := func() *api.WindowsSecurityContextOptions {
  1384. windowsOptions := &api.WindowsSecurityContextOptions{}
  1385. transformingFunc(windowsOptions)
  1386. return windowsOptions
  1387. }
  1388. podFactoryInfos = append(podFactoryInfos,
  1389. podFactoryInfo{
  1390. description: fmt.Sprintf("has %s in Pod", name),
  1391. hasGMSAField: true,
  1392. podFactory: func() *api.Pod {
  1393. pod := podWithoutWindowsOptionsFactory()
  1394. pod.Spec.SecurityContext.WindowsOptions = windowsOptionsWithGMSAFieldFactory()
  1395. return pod
  1396. },
  1397. strippedPodFactory: func() *api.Pod {
  1398. pod := podWithoutWindowsOptionsFactory()
  1399. pod.Spec.SecurityContext.WindowsOptions = &api.WindowsSecurityContextOptions{}
  1400. return pod
  1401. },
  1402. },
  1403. podFactoryInfo{
  1404. description: fmt.Sprintf("has %s in Container", name),
  1405. hasGMSAField: true,
  1406. podFactory: func() *api.Pod {
  1407. pod := podWithoutWindowsOptionsFactory()
  1408. pod.Spec.Containers[0].SecurityContext.WindowsOptions = windowsOptionsWithGMSAFieldFactory()
  1409. return pod
  1410. },
  1411. strippedPodFactory: func() *api.Pod {
  1412. pod := podWithoutWindowsOptionsFactory()
  1413. pod.Spec.Containers[0].SecurityContext.WindowsOptions = &api.WindowsSecurityContextOptions{}
  1414. return pod
  1415. },
  1416. },
  1417. podFactoryInfo{
  1418. description: fmt.Sprintf("has %s in InitContainer", name),
  1419. hasGMSAField: true,
  1420. podFactory: func() *api.Pod {
  1421. pod := podWithoutWindowsOptionsFactory()
  1422. pod.Spec.InitContainers[0].SecurityContext.WindowsOptions = windowsOptionsWithGMSAFieldFactory()
  1423. return pod
  1424. },
  1425. strippedPodFactory: func() *api.Pod {
  1426. pod := podWithoutWindowsOptionsFactory()
  1427. pod.Spec.InitContainers[0].SecurityContext.WindowsOptions = &api.WindowsSecurityContextOptions{}
  1428. return pod
  1429. },
  1430. })
  1431. }
  1432. for _, enabled := range []bool{true, false} {
  1433. for _, oldPodFactoryInfo := range podFactoryInfos {
  1434. for _, newPodFactoryInfo := range podFactoryInfos {
  1435. newPodHasGMSAField, newPod := newPodFactoryInfo.hasGMSAField, newPodFactoryInfo.podFactory()
  1436. if newPod == nil {
  1437. continue
  1438. }
  1439. oldPodHasGMSAField, oldPod := oldPodFactoryInfo.hasGMSAField, oldPodFactoryInfo.podFactory()
  1440. t.Run(fmt.Sprintf("feature enabled=%v, old pod %s, new pod %s", enabled, oldPodFactoryInfo.description, newPodFactoryInfo.description), func(t *testing.T) {
  1441. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.WindowsGMSA, enabled)()
  1442. var oldPodSpec *api.PodSpec
  1443. if oldPod != nil {
  1444. oldPodSpec = &oldPod.Spec
  1445. }
  1446. dropDisabledFields(&newPod.Spec, nil, oldPodSpec, nil)
  1447. // old pod should never be changed
  1448. if !reflect.DeepEqual(oldPod, oldPodFactoryInfo.podFactory()) {
  1449. t.Errorf("old pod changed: %v", diff.ObjectReflectDiff(oldPod, oldPodFactoryInfo.podFactory()))
  1450. }
  1451. switch {
  1452. case enabled || oldPodHasGMSAField:
  1453. // new pod should not be changed if the feature is enabled, or if the old pod had any GMSA field set
  1454. if !reflect.DeepEqual(newPod, newPodFactoryInfo.podFactory()) {
  1455. t.Errorf("new pod changed: %v", diff.ObjectReflectDiff(newPod, newPodFactoryInfo.podFactory()))
  1456. }
  1457. case newPodHasGMSAField:
  1458. // new pod should be changed
  1459. if reflect.DeepEqual(newPod, newPodFactoryInfo.podFactory()) {
  1460. t.Errorf("%v", oldPod)
  1461. t.Errorf("%v", newPod)
  1462. t.Errorf("new pod was not changed")
  1463. }
  1464. // new pod should not have any GMSA field set
  1465. var expectedStrippedPod *api.Pod
  1466. if newPodFactoryInfo.strippedPodFactory == nil {
  1467. expectedStrippedPod = newPodFactoryInfo.podFactory()
  1468. } else {
  1469. expectedStrippedPod = newPodFactoryInfo.strippedPodFactory()
  1470. }
  1471. if !reflect.DeepEqual(newPod, expectedStrippedPod) {
  1472. t.Errorf("new pod had some GMSA field set: %v", diff.ObjectReflectDiff(newPod, expectedStrippedPod))
  1473. }
  1474. default:
  1475. // new pod should not need to be changed
  1476. if !reflect.DeepEqual(newPod, newPodFactoryInfo.podFactory()) {
  1477. t.Errorf("new pod changed: %v", diff.ObjectReflectDiff(newPod, newPodFactoryInfo.podFactory()))
  1478. }
  1479. }
  1480. })
  1481. }
  1482. }
  1483. }
  1484. }
  1485. func TestDropPodSysctls(t *testing.T) {
  1486. podWithSysctls := func() *api.Pod {
  1487. return &api.Pod{
  1488. Spec: api.PodSpec{
  1489. SecurityContext: &api.PodSecurityContext{
  1490. Sysctls: []api.Sysctl{{Name: "test", Value: "value"}},
  1491. },
  1492. },
  1493. }
  1494. }
  1495. podWithoutSysctls := func() *api.Pod {
  1496. return &api.Pod{
  1497. Spec: api.PodSpec{
  1498. SecurityContext: &api.PodSecurityContext{},
  1499. },
  1500. }
  1501. }
  1502. podWithoutSecurityContext := func() *api.Pod {
  1503. return &api.Pod{
  1504. Spec: api.PodSpec{},
  1505. }
  1506. }
  1507. podInfo := []struct {
  1508. description string
  1509. hasSysctls bool
  1510. pod func() *api.Pod
  1511. }{
  1512. {
  1513. description: "has Sysctls",
  1514. hasSysctls: true,
  1515. pod: podWithSysctls,
  1516. },
  1517. {
  1518. description: "does not have Sysctls",
  1519. hasSysctls: false,
  1520. pod: podWithoutSysctls,
  1521. },
  1522. {
  1523. description: "does not have SecurityContext",
  1524. hasSysctls: false,
  1525. pod: podWithoutSecurityContext,
  1526. },
  1527. {
  1528. description: "is nil",
  1529. hasSysctls: false,
  1530. pod: func() *api.Pod { return nil },
  1531. },
  1532. }
  1533. for _, enabled := range []bool{true, false} {
  1534. for _, oldPodInfo := range podInfo {
  1535. for _, newPodInfo := range podInfo {
  1536. oldPodHasSysctls, oldPod := oldPodInfo.hasSysctls, oldPodInfo.pod()
  1537. newPodHasSysctls, newPod := newPodInfo.hasSysctls, newPodInfo.pod()
  1538. if newPod == nil {
  1539. continue
  1540. }
  1541. t.Run(fmt.Sprintf("feature enabled=%v, old pod %v, new pod %v", enabled, oldPodInfo.description, newPodInfo.description), func(t *testing.T) {
  1542. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.Sysctls, enabled)()
  1543. var oldPodSpec *api.PodSpec
  1544. if oldPod != nil {
  1545. oldPodSpec = &oldPod.Spec
  1546. }
  1547. dropDisabledFields(&newPod.Spec, nil, oldPodSpec, nil)
  1548. // old pod should never be changed
  1549. if !reflect.DeepEqual(oldPod, oldPodInfo.pod()) {
  1550. t.Errorf("old pod changed: %v", diff.ObjectReflectDiff(oldPod, oldPodInfo.pod()))
  1551. }
  1552. switch {
  1553. case enabled || oldPodHasSysctls:
  1554. // new pod should not be changed if the feature is enabled, or if the old pod had Sysctls set
  1555. if !reflect.DeepEqual(newPod, newPodInfo.pod()) {
  1556. t.Errorf("new pod changed: %v", diff.ObjectReflectDiff(newPod, newPodInfo.pod()))
  1557. }
  1558. case newPodHasSysctls:
  1559. // new pod should be changed
  1560. if reflect.DeepEqual(newPod, newPodInfo.pod()) {
  1561. t.Errorf("new pod was not changed")
  1562. }
  1563. // new pod should not have Sysctls
  1564. if !reflect.DeepEqual(newPod, podWithoutSysctls()) {
  1565. t.Errorf("new pod had Sysctls: %v", diff.ObjectReflectDiff(newPod, podWithoutSysctls()))
  1566. }
  1567. default:
  1568. // new pod should not need to be changed
  1569. if !reflect.DeepEqual(newPod, newPodInfo.pod()) {
  1570. t.Errorf("new pod changed: %v", diff.ObjectReflectDiff(newPod, newPodInfo.pod()))
  1571. }
  1572. }
  1573. })
  1574. }
  1575. }
  1576. }
  1577. }
  1578. func TestDropSubPathExpr(t *testing.T) {
  1579. podWithSubpaths := func() *api.Pod {
  1580. return &api.Pod{
  1581. Spec: api.PodSpec{
  1582. RestartPolicy: api.RestartPolicyNever,
  1583. Containers: []api.Container{{Name: "container1", Image: "testimage", VolumeMounts: []api.VolumeMount{{Name: "a", SubPathExpr: "foo"}, {Name: "a", SubPathExpr: "foo2"}, {Name: "a", SubPathExpr: "foo3"}}}},
  1584. InitContainers: []api.Container{{Name: "container1", Image: "testimage", VolumeMounts: []api.VolumeMount{{Name: "a", SubPathExpr: "foo"}, {Name: "a", SubPathExpr: "foo2"}}}},
  1585. Volumes: []api.Volume{{Name: "a", VolumeSource: api.VolumeSource{HostPath: &api.HostPathVolumeSource{Path: "/dev/xvdc"}}}},
  1586. },
  1587. }
  1588. }
  1589. podWithoutSubpaths := func() *api.Pod {
  1590. return &api.Pod{
  1591. Spec: api.PodSpec{
  1592. RestartPolicy: api.RestartPolicyNever,
  1593. Containers: []api.Container{{Name: "container1", Image: "testimage", VolumeMounts: []api.VolumeMount{{Name: "a", SubPathExpr: ""}, {Name: "a", SubPathExpr: ""}, {Name: "a", SubPathExpr: ""}}}},
  1594. InitContainers: []api.Container{{Name: "container1", Image: "testimage", VolumeMounts: []api.VolumeMount{{Name: "a", SubPathExpr: ""}, {Name: "a", SubPathExpr: ""}}}},
  1595. Volumes: []api.Volume{{Name: "a", VolumeSource: api.VolumeSource{HostPath: &api.HostPathVolumeSource{Path: "/dev/xvdc"}}}},
  1596. },
  1597. }
  1598. }
  1599. podInfo := []struct {
  1600. description string
  1601. hasSubpaths bool
  1602. pod func() *api.Pod
  1603. }{
  1604. {
  1605. description: "has subpaths",
  1606. hasSubpaths: true,
  1607. pod: podWithSubpaths,
  1608. },
  1609. {
  1610. description: "does not have subpaths",
  1611. hasSubpaths: false,
  1612. pod: podWithoutSubpaths,
  1613. },
  1614. {
  1615. description: "is nil",
  1616. hasSubpaths: false,
  1617. pod: func() *api.Pod { return nil },
  1618. },
  1619. }
  1620. for _, enabled := range []bool{true, false} {
  1621. for _, oldPodInfo := range podInfo {
  1622. for _, newPodInfo := range podInfo {
  1623. oldPodHasSubpaths, oldPod := oldPodInfo.hasSubpaths, oldPodInfo.pod()
  1624. newPodHasSubpaths, newPod := newPodInfo.hasSubpaths, newPodInfo.pod()
  1625. if newPod == nil {
  1626. continue
  1627. }
  1628. t.Run(fmt.Sprintf("feature enabled=%v, old pod %v, new pod %v", enabled, oldPodInfo.description, newPodInfo.description), func(t *testing.T) {
  1629. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeSubpathEnvExpansion, enabled)()
  1630. var oldPodSpec *api.PodSpec
  1631. if oldPod != nil {
  1632. oldPodSpec = &oldPod.Spec
  1633. }
  1634. dropDisabledFields(&newPod.Spec, nil, oldPodSpec, nil)
  1635. // old pod should never be changed
  1636. if !reflect.DeepEqual(oldPod, oldPodInfo.pod()) {
  1637. t.Errorf("old pod changed: %v", diff.ObjectReflectDiff(oldPod, oldPodInfo.pod()))
  1638. }
  1639. switch {
  1640. case enabled || oldPodHasSubpaths:
  1641. // new pod should not be changed if the feature is enabled, or if the old pod had subpaths
  1642. if !reflect.DeepEqual(newPod, newPodInfo.pod()) {
  1643. t.Errorf("new pod changed: %v", diff.ObjectReflectDiff(newPod, newPodInfo.pod()))
  1644. }
  1645. case newPodHasSubpaths:
  1646. // new pod should be changed
  1647. if reflect.DeepEqual(newPod, newPodInfo.pod()) {
  1648. t.Errorf("new pod was not changed")
  1649. }
  1650. // new pod should not have subpaths
  1651. if !reflect.DeepEqual(newPod, podWithoutSubpaths()) {
  1652. t.Errorf("new pod had subpaths: %v", diff.ObjectReflectDiff(newPod, podWithoutSubpaths()))
  1653. }
  1654. default:
  1655. // new pod should not need to be changed
  1656. if !reflect.DeepEqual(newPod, newPodInfo.pod()) {
  1657. t.Errorf("new pod changed: %v", diff.ObjectReflectDiff(newPod, newPodInfo.pod()))
  1658. }
  1659. }
  1660. })
  1661. }
  1662. }
  1663. }
  1664. }