util_test.go 66 KB

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