admission_test.go 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891
  1. /*
  2. Copyright 2016 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 podpreset
  14. import (
  15. "context"
  16. "fmt"
  17. "reflect"
  18. "testing"
  19. fuzz "github.com/google/gofuzz"
  20. corev1 "k8s.io/api/core/v1"
  21. settingsv1alpha1 "k8s.io/api/settings/v1alpha1"
  22. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  23. "k8s.io/apimachinery/pkg/runtime"
  24. "k8s.io/apimachinery/pkg/util/diff"
  25. kadmission "k8s.io/apiserver/pkg/admission"
  26. admissiontesting "k8s.io/apiserver/pkg/admission/testing"
  27. "k8s.io/apiserver/pkg/authentication/user"
  28. "k8s.io/client-go/informers"
  29. "k8s.io/client-go/kubernetes/fake"
  30. settingsv1alpha1listers "k8s.io/client-go/listers/settings/v1alpha1"
  31. api "k8s.io/kubernetes/pkg/apis/core"
  32. "k8s.io/kubernetes/pkg/controller"
  33. )
  34. func TestMergeEnv(t *testing.T) {
  35. tests := map[string]struct {
  36. orig []api.EnvVar
  37. mod []corev1.EnvVar
  38. result []api.EnvVar
  39. shouldFail bool
  40. }{
  41. "empty original": {
  42. mod: []corev1.EnvVar{{Name: "abc", Value: "value2"}, {Name: "ABC", Value: "value3"}},
  43. result: []api.EnvVar{{Name: "abc", Value: "value2"}, {Name: "ABC", Value: "value3"}},
  44. shouldFail: false,
  45. },
  46. "good merge": {
  47. orig: []api.EnvVar{{Name: "abcd", Value: "value2"}, {Name: "hello", Value: "value3"}},
  48. mod: []corev1.EnvVar{{Name: "abc", Value: "value2"}, {Name: "ABC", Value: "value3"}},
  49. result: []api.EnvVar{{Name: "abcd", Value: "value2"}, {Name: "hello", Value: "value3"}, {Name: "abc", Value: "value2"}, {Name: "ABC", Value: "value3"}},
  50. shouldFail: false,
  51. },
  52. "conflict": {
  53. orig: []api.EnvVar{{Name: "abc", Value: "value3"}},
  54. mod: []corev1.EnvVar{{Name: "abc", Value: "value2"}, {Name: "ABC", Value: "value3"}},
  55. shouldFail: true,
  56. },
  57. "one is exact same": {
  58. orig: []api.EnvVar{{Name: "abc", Value: "value2"}, {Name: "hello", Value: "value3"}},
  59. mod: []corev1.EnvVar{{Name: "abc", Value: "value2"}, {Name: "ABC", Value: "value3"}},
  60. result: []api.EnvVar{{Name: "abc", Value: "value2"}, {Name: "hello", Value: "value3"}, {Name: "ABC", Value: "value3"}},
  61. shouldFail: false,
  62. },
  63. }
  64. for name, test := range tests {
  65. result, err := mergeEnv(
  66. test.orig,
  67. []*settingsv1alpha1.PodPreset{{Spec: settingsv1alpha1.PodPresetSpec{Env: test.mod}}},
  68. )
  69. if test.shouldFail && err == nil {
  70. t.Fatalf("expected test %q to fail but got nil", name)
  71. }
  72. if !test.shouldFail && err != nil {
  73. t.Fatalf("test %q failed: %v", name, err)
  74. }
  75. if !reflect.DeepEqual(test.result, result) {
  76. t.Fatalf("results were not equal for test %q: got %#v; expected: %#v", name, result, test.result)
  77. }
  78. }
  79. }
  80. func TestMergeEnvFrom(t *testing.T) {
  81. tests := map[string]struct {
  82. orig []api.EnvFromSource
  83. mod []corev1.EnvFromSource
  84. result []api.EnvFromSource
  85. shouldFail bool
  86. }{
  87. "empty original": {
  88. mod: []corev1.EnvFromSource{
  89. {
  90. ConfigMapRef: &corev1.ConfigMapEnvSource{
  91. LocalObjectReference: corev1.LocalObjectReference{Name: "abc"},
  92. },
  93. },
  94. {
  95. Prefix: "pre_",
  96. ConfigMapRef: &corev1.ConfigMapEnvSource{
  97. LocalObjectReference: corev1.LocalObjectReference{Name: "abc"},
  98. },
  99. },
  100. },
  101. result: []api.EnvFromSource{
  102. {
  103. ConfigMapRef: &api.ConfigMapEnvSource{
  104. LocalObjectReference: api.LocalObjectReference{Name: "abc"},
  105. },
  106. },
  107. {
  108. Prefix: "pre_",
  109. ConfigMapRef: &api.ConfigMapEnvSource{
  110. LocalObjectReference: api.LocalObjectReference{Name: "abc"},
  111. },
  112. },
  113. },
  114. shouldFail: false,
  115. },
  116. "good merge": {
  117. orig: []api.EnvFromSource{
  118. {
  119. ConfigMapRef: &api.ConfigMapEnvSource{
  120. LocalObjectReference: api.LocalObjectReference{Name: "thing"},
  121. },
  122. },
  123. },
  124. mod: []corev1.EnvFromSource{
  125. {
  126. ConfigMapRef: &corev1.ConfigMapEnvSource{
  127. LocalObjectReference: corev1.LocalObjectReference{Name: "abc"},
  128. },
  129. },
  130. {
  131. Prefix: "pre_",
  132. ConfigMapRef: &corev1.ConfigMapEnvSource{
  133. LocalObjectReference: corev1.LocalObjectReference{Name: "abc"},
  134. },
  135. },
  136. },
  137. result: []api.EnvFromSource{
  138. {
  139. ConfigMapRef: &api.ConfigMapEnvSource{
  140. LocalObjectReference: api.LocalObjectReference{Name: "thing"},
  141. },
  142. },
  143. {
  144. ConfigMapRef: &api.ConfigMapEnvSource{
  145. LocalObjectReference: api.LocalObjectReference{Name: "abc"},
  146. },
  147. },
  148. {
  149. Prefix: "pre_",
  150. ConfigMapRef: &api.ConfigMapEnvSource{
  151. LocalObjectReference: api.LocalObjectReference{Name: "abc"},
  152. },
  153. },
  154. },
  155. shouldFail: false,
  156. },
  157. }
  158. for name, test := range tests {
  159. result, err := mergeEnvFrom(
  160. test.orig,
  161. []*settingsv1alpha1.PodPreset{{Spec: settingsv1alpha1.PodPresetSpec{EnvFrom: test.mod}}},
  162. )
  163. if test.shouldFail && err == nil {
  164. t.Fatalf("expected test %q to fail but got nil", name)
  165. }
  166. if !test.shouldFail && err != nil {
  167. t.Fatalf("test %q failed: %v", name, err)
  168. }
  169. if !reflect.DeepEqual(test.result, result) {
  170. t.Fatalf("results were not equal for test %q: got %#v; expected: %#v", name, result, test.result)
  171. }
  172. }
  173. }
  174. func TestMergeVolumeMounts(t *testing.T) {
  175. tests := map[string]struct {
  176. orig []api.VolumeMount
  177. mod []corev1.VolumeMount
  178. result []api.VolumeMount
  179. shouldFail bool
  180. }{
  181. "empty original": {
  182. mod: []corev1.VolumeMount{
  183. {
  184. Name: "simply-mounted-volume",
  185. MountPath: "/opt/",
  186. },
  187. },
  188. result: []api.VolumeMount{
  189. {
  190. Name: "simply-mounted-volume",
  191. MountPath: "/opt/",
  192. },
  193. },
  194. shouldFail: false,
  195. },
  196. "good merge": {
  197. mod: []corev1.VolumeMount{
  198. {
  199. Name: "simply-mounted-volume",
  200. MountPath: "/opt/",
  201. },
  202. },
  203. orig: []api.VolumeMount{
  204. {
  205. Name: "etc-volume",
  206. MountPath: "/etc/",
  207. },
  208. },
  209. result: []api.VolumeMount{
  210. {
  211. Name: "etc-volume",
  212. MountPath: "/etc/",
  213. },
  214. {
  215. Name: "simply-mounted-volume",
  216. MountPath: "/opt/",
  217. },
  218. },
  219. shouldFail: false,
  220. },
  221. "conflict": {
  222. mod: []corev1.VolumeMount{
  223. {
  224. Name: "simply-mounted-volume",
  225. MountPath: "/opt/",
  226. },
  227. {
  228. Name: "etc-volume",
  229. MountPath: "/things/",
  230. },
  231. },
  232. orig: []api.VolumeMount{
  233. {
  234. Name: "etc-volume",
  235. MountPath: "/etc/",
  236. },
  237. },
  238. shouldFail: true,
  239. },
  240. "conflict on mount path": {
  241. mod: []corev1.VolumeMount{
  242. {
  243. Name: "simply-mounted-volume",
  244. MountPath: "/opt/",
  245. },
  246. {
  247. Name: "things-volume",
  248. MountPath: "/etc/",
  249. },
  250. },
  251. orig: []api.VolumeMount{
  252. {
  253. Name: "etc-volume",
  254. MountPath: "/etc/",
  255. },
  256. },
  257. shouldFail: true,
  258. },
  259. "one is exact same": {
  260. mod: []corev1.VolumeMount{
  261. {
  262. Name: "simply-mounted-volume",
  263. MountPath: "/opt/",
  264. },
  265. {
  266. Name: "etc-volume",
  267. MountPath: "/etc/",
  268. },
  269. },
  270. orig: []api.VolumeMount{
  271. {
  272. Name: "etc-volume",
  273. MountPath: "/etc/",
  274. },
  275. },
  276. result: []api.VolumeMount{
  277. {
  278. Name: "etc-volume",
  279. MountPath: "/etc/",
  280. },
  281. {
  282. Name: "simply-mounted-volume",
  283. MountPath: "/opt/",
  284. },
  285. },
  286. shouldFail: false,
  287. },
  288. }
  289. for name, test := range tests {
  290. result, err := mergeVolumeMounts(
  291. test.orig,
  292. []*settingsv1alpha1.PodPreset{{Spec: settingsv1alpha1.PodPresetSpec{VolumeMounts: test.mod}}},
  293. )
  294. if test.shouldFail && err == nil {
  295. t.Fatalf("expected test %q to fail but got nil", name)
  296. }
  297. if !test.shouldFail && err != nil {
  298. t.Fatalf("test %q failed: %v", name, err)
  299. }
  300. if !reflect.DeepEqual(test.result, result) {
  301. t.Fatalf("results were not equal for test %q: got %#v; expected: %#v", name, result, test.result)
  302. }
  303. }
  304. }
  305. func TestMergeVolumes(t *testing.T) {
  306. tests := map[string]struct {
  307. orig []api.Volume
  308. mod []corev1.Volume
  309. result []api.Volume
  310. shouldFail bool
  311. }{
  312. "empty original": {
  313. mod: []corev1.Volume{
  314. {Name: "vol", VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}}},
  315. {Name: "vol2", VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}}},
  316. },
  317. result: []api.Volume{
  318. {Name: "vol", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}},
  319. {Name: "vol2", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}},
  320. },
  321. shouldFail: false,
  322. },
  323. "good merge": {
  324. orig: []api.Volume{
  325. {Name: "vol3", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}},
  326. {Name: "vol4", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}},
  327. },
  328. mod: []corev1.Volume{
  329. {Name: "vol", VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}}},
  330. {Name: "vol2", VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}}},
  331. },
  332. result: []api.Volume{
  333. {Name: "vol3", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}},
  334. {Name: "vol4", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}},
  335. {Name: "vol", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}},
  336. {Name: "vol2", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}},
  337. },
  338. shouldFail: false,
  339. },
  340. "conflict": {
  341. orig: []api.Volume{
  342. {Name: "vol3", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}},
  343. {Name: "vol4", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}},
  344. },
  345. mod: []corev1.Volume{
  346. {Name: "vol3", VolumeSource: corev1.VolumeSource{HostPath: &corev1.HostPathVolumeSource{Path: "/etc/apparmor.d"}}},
  347. {Name: "vol2", VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}}},
  348. },
  349. shouldFail: true,
  350. },
  351. "one is exact same": {
  352. orig: []api.Volume{
  353. {Name: "vol3", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}},
  354. {Name: "vol4", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}},
  355. },
  356. mod: []corev1.Volume{
  357. {Name: "vol3", VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}}},
  358. {Name: "vol2", VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}}},
  359. },
  360. result: []api.Volume{
  361. {Name: "vol3", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}},
  362. {Name: "vol4", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}},
  363. {Name: "vol2", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}},
  364. },
  365. shouldFail: false,
  366. },
  367. }
  368. for name, test := range tests {
  369. result, err := mergeVolumes(
  370. test.orig,
  371. []*settingsv1alpha1.PodPreset{{Spec: settingsv1alpha1.PodPresetSpec{Volumes: test.mod}}},
  372. )
  373. if test.shouldFail && err == nil {
  374. t.Fatalf("expected test %q to fail but got nil", name)
  375. }
  376. if !test.shouldFail && err != nil {
  377. t.Fatalf("test %q failed: %v", name, err)
  378. }
  379. if !reflect.DeepEqual(test.result, result) {
  380. t.Fatalf("results were not equal for test %q: got %#v; expected: %#v", name, result, test.result)
  381. }
  382. }
  383. }
  384. // NewTestAdmission provides an admission plugin with test implementations of internal structs. It uses
  385. // an authorizer that always returns true.
  386. func NewTestAdmission(lister settingsv1alpha1listers.PodPresetLister, objects ...runtime.Object) kadmission.MutationInterface {
  387. // Build a test client that the admission plugin can use to look up the service account missing from its cache
  388. client := fake.NewSimpleClientset(objects...)
  389. return &Plugin{
  390. client: client,
  391. Handler: kadmission.NewHandler(kadmission.Create),
  392. lister: lister,
  393. }
  394. }
  395. func TestAdmitConflictWithDifferentNamespaceShouldDoNothing(t *testing.T) {
  396. containerName := "container"
  397. pod := &api.Pod{
  398. ObjectMeta: metav1.ObjectMeta{
  399. Name: "mypod",
  400. Namespace: "namespace",
  401. Labels: map[string]string{
  402. "security": "S2",
  403. },
  404. },
  405. Spec: api.PodSpec{
  406. Containers: []api.Container{
  407. {
  408. Name: containerName,
  409. Env: []api.EnvVar{{Name: "abc", Value: "value2"}, {Name: "ABC", Value: "value3"}},
  410. },
  411. },
  412. },
  413. }
  414. pip := &settingsv1alpha1.PodPreset{
  415. ObjectMeta: metav1.ObjectMeta{
  416. Name: "hello",
  417. Namespace: "othernamespace",
  418. },
  419. Spec: settingsv1alpha1.PodPresetSpec{
  420. Selector: metav1.LabelSelector{
  421. MatchExpressions: []metav1.LabelSelectorRequirement{
  422. {
  423. Key: "security",
  424. Operator: metav1.LabelSelectorOpIn,
  425. Values: []string{"S2"},
  426. },
  427. },
  428. },
  429. Env: []corev1.EnvVar{{Name: "abc", Value: "value"}, {Name: "ABC", Value: "value"}},
  430. },
  431. }
  432. err := admitPod(t, pod, pip)
  433. if err != nil {
  434. t.Fatal(err)
  435. }
  436. }
  437. func TestAdmitConflictWithNonMatchingLabelsShouldNotError(t *testing.T) {
  438. containerName := "container"
  439. pod := &api.Pod{
  440. ObjectMeta: metav1.ObjectMeta{
  441. Name: "mypod",
  442. Namespace: "namespace",
  443. Labels: map[string]string{
  444. "security": "S2",
  445. },
  446. },
  447. Spec: api.PodSpec{
  448. Containers: []api.Container{
  449. {
  450. Name: containerName,
  451. Env: []api.EnvVar{{Name: "abc", Value: "value2"}, {Name: "ABC", Value: "value3"}},
  452. },
  453. },
  454. },
  455. }
  456. pip := &settingsv1alpha1.PodPreset{
  457. ObjectMeta: metav1.ObjectMeta{
  458. Name: "hello",
  459. Namespace: "namespace",
  460. },
  461. Spec: settingsv1alpha1.PodPresetSpec{
  462. Selector: metav1.LabelSelector{
  463. MatchExpressions: []metav1.LabelSelectorRequirement{
  464. {
  465. Key: "security",
  466. Operator: metav1.LabelSelectorOpIn,
  467. Values: []string{"S3"},
  468. },
  469. },
  470. },
  471. Env: []corev1.EnvVar{{Name: "abc", Value: "value"}, {Name: "ABC", Value: "value"}},
  472. },
  473. }
  474. err := admitPod(t, pod, pip)
  475. if err != nil {
  476. t.Fatal(err)
  477. }
  478. }
  479. func TestAdmitConflictShouldNotModifyPod(t *testing.T) {
  480. containerName := "container"
  481. pod := &api.Pod{
  482. ObjectMeta: metav1.ObjectMeta{
  483. Name: "mypod",
  484. Namespace: "namespace",
  485. Labels: map[string]string{
  486. "security": "S2",
  487. },
  488. },
  489. Spec: api.PodSpec{
  490. Containers: []api.Container{
  491. {
  492. Name: containerName,
  493. Env: []api.EnvVar{{Name: "abc", Value: "value2"}, {Name: "ABC", Value: "value3"}},
  494. },
  495. },
  496. },
  497. }
  498. origPod := *pod
  499. pip := &settingsv1alpha1.PodPreset{
  500. ObjectMeta: metav1.ObjectMeta{
  501. Name: "hello",
  502. Namespace: "namespace",
  503. },
  504. Spec: settingsv1alpha1.PodPresetSpec{
  505. Selector: metav1.LabelSelector{
  506. MatchExpressions: []metav1.LabelSelectorRequirement{
  507. {
  508. Key: "security",
  509. Operator: metav1.LabelSelectorOpIn,
  510. Values: []string{"S2"},
  511. },
  512. },
  513. },
  514. Env: []corev1.EnvVar{{Name: "abc", Value: "value"}, {Name: "ABC", Value: "value"}},
  515. },
  516. }
  517. err := admitPod(t, pod, pip)
  518. if err != nil {
  519. t.Fatal(err)
  520. }
  521. if !reflect.DeepEqual(&origPod, pod) {
  522. t.Fatalf("pod should not get modified in case of conflict origPod: %+v got: %+v", &origPod, pod)
  523. }
  524. }
  525. func TestAdmit(t *testing.T) {
  526. containerName := "container"
  527. pod := &api.Pod{
  528. ObjectMeta: metav1.ObjectMeta{
  529. Name: "mypod",
  530. Namespace: "namespace",
  531. Labels: map[string]string{
  532. "security": "S2",
  533. },
  534. },
  535. Spec: api.PodSpec{
  536. Containers: []api.Container{
  537. {
  538. Name: containerName,
  539. Env: []api.EnvVar{{Name: "abc", Value: "value2"}, {Name: "ABCD", Value: "value3"}},
  540. },
  541. },
  542. },
  543. }
  544. pip := &settingsv1alpha1.PodPreset{
  545. ObjectMeta: metav1.ObjectMeta{
  546. Name: "hello",
  547. Namespace: "namespace",
  548. },
  549. Spec: settingsv1alpha1.PodPresetSpec{
  550. Selector: metav1.LabelSelector{
  551. MatchExpressions: []metav1.LabelSelectorRequirement{
  552. {
  553. Key: "security",
  554. Operator: metav1.LabelSelectorOpIn,
  555. Values: []string{"S2"},
  556. },
  557. },
  558. },
  559. Volumes: []corev1.Volume{{Name: "vol", VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}}}},
  560. Env: []corev1.EnvVar{{Name: "abcd", Value: "value"}, {Name: "ABC", Value: "value"}},
  561. EnvFrom: []corev1.EnvFromSource{
  562. {
  563. ConfigMapRef: &corev1.ConfigMapEnvSource{
  564. LocalObjectReference: corev1.LocalObjectReference{Name: "abc"},
  565. },
  566. },
  567. {
  568. Prefix: "pre_",
  569. ConfigMapRef: &corev1.ConfigMapEnvSource{
  570. LocalObjectReference: corev1.LocalObjectReference{Name: "abc"},
  571. },
  572. },
  573. },
  574. },
  575. }
  576. err := admitPod(t, pod, pip)
  577. if err != nil {
  578. t.Fatal(err)
  579. }
  580. }
  581. func TestAdmitMirrorPod(t *testing.T) {
  582. containerName := "container"
  583. mirrorPod := &api.Pod{
  584. ObjectMeta: metav1.ObjectMeta{
  585. Name: "mypod",
  586. Namespace: "namespace",
  587. Labels: map[string]string{
  588. "security": "S2",
  589. },
  590. Annotations: map[string]string{api.MirrorPodAnnotationKey: "mirror"},
  591. },
  592. Spec: api.PodSpec{
  593. Containers: []api.Container{
  594. {
  595. Name: containerName,
  596. },
  597. },
  598. },
  599. }
  600. pip := &settingsv1alpha1.PodPreset{
  601. ObjectMeta: metav1.ObjectMeta{
  602. Name: "hello",
  603. Namespace: "namespace",
  604. },
  605. Spec: settingsv1alpha1.PodPresetSpec{
  606. Selector: metav1.LabelSelector{
  607. MatchExpressions: []metav1.LabelSelectorRequirement{
  608. {
  609. Key: "security",
  610. Operator: metav1.LabelSelectorOpIn,
  611. Values: []string{"S2"},
  612. },
  613. },
  614. },
  615. Volumes: []corev1.Volume{{Name: "vol", VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}}}},
  616. Env: []corev1.EnvVar{{Name: "abcd", Value: "value"}, {Name: "ABC", Value: "value"}},
  617. EnvFrom: []corev1.EnvFromSource{
  618. {
  619. ConfigMapRef: &corev1.ConfigMapEnvSource{
  620. LocalObjectReference: corev1.LocalObjectReference{Name: "abc"},
  621. },
  622. },
  623. {
  624. Prefix: "pre_",
  625. ConfigMapRef: &corev1.ConfigMapEnvSource{
  626. LocalObjectReference: corev1.LocalObjectReference{Name: "abc"},
  627. },
  628. },
  629. },
  630. },
  631. }
  632. if err := admitPod(t, mirrorPod, pip); err != nil {
  633. t.Fatal(err)
  634. }
  635. container := mirrorPod.Spec.Containers[0]
  636. if len(mirrorPod.Spec.Volumes) != 0 ||
  637. len(container.VolumeMounts) != 0 ||
  638. len(container.Env) != 0 ||
  639. len(container.EnvFrom) != 0 {
  640. t.Fatalf("mirror pod is updated by PodPreset admission:\n\tVolumes got %d, expected 0\n\tVolumeMounts go %d, expected 0\n\tEnv got, %d expected 0\n\tEnvFrom got %d, expected 0", len(mirrorPod.Spec.Volumes), len(container.VolumeMounts), len(container.Env), len(container.EnvFrom))
  641. }
  642. }
  643. func TestExclusionNoAdmit(t *testing.T) {
  644. containerName := "container"
  645. pod := &api.Pod{
  646. ObjectMeta: metav1.ObjectMeta{
  647. Name: "mypod",
  648. Namespace: "namespace",
  649. Labels: map[string]string{
  650. "security": "S2",
  651. },
  652. Annotations: map[string]string{
  653. api.PodPresetOptOutAnnotationKey: "true",
  654. },
  655. },
  656. Spec: api.PodSpec{
  657. Containers: []api.Container{
  658. {
  659. Name: containerName,
  660. Env: []api.EnvVar{{Name: "abc", Value: "value2"}, {Name: "ABCD", Value: "value3"}},
  661. },
  662. },
  663. },
  664. }
  665. pip := &settingsv1alpha1.PodPreset{
  666. ObjectMeta: metav1.ObjectMeta{
  667. Name: "hello",
  668. Namespace: "namespace",
  669. },
  670. Spec: settingsv1alpha1.PodPresetSpec{
  671. Selector: metav1.LabelSelector{
  672. MatchExpressions: []metav1.LabelSelectorRequirement{
  673. {
  674. Key: "security",
  675. Operator: metav1.LabelSelectorOpIn,
  676. Values: []string{"S2"},
  677. },
  678. },
  679. },
  680. Volumes: []corev1.Volume{{Name: "vol", VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}}}},
  681. Env: []corev1.EnvVar{{Name: "abcd", Value: "value"}, {Name: "ABC", Value: "value"}},
  682. EnvFrom: []corev1.EnvFromSource{
  683. {
  684. ConfigMapRef: &corev1.ConfigMapEnvSource{
  685. LocalObjectReference: corev1.LocalObjectReference{Name: "abc"},
  686. },
  687. },
  688. {
  689. Prefix: "pre_",
  690. ConfigMapRef: &corev1.ConfigMapEnvSource{
  691. LocalObjectReference: corev1.LocalObjectReference{Name: "abc"},
  692. },
  693. },
  694. },
  695. },
  696. }
  697. originalPod := pod.DeepCopy()
  698. err := admitPod(t, pod, pip)
  699. if err != nil {
  700. t.Fatal(err)
  701. }
  702. // verify PodSpec has not been mutated
  703. if !reflect.DeepEqual(pod, originalPod) {
  704. t.Fatalf("Expected pod spec of '%v' to be unchanged", pod.Name)
  705. }
  706. }
  707. func TestAdmitEmptyPodNamespace(t *testing.T) {
  708. containerName := "container"
  709. pod := &api.Pod{
  710. ObjectMeta: metav1.ObjectMeta{
  711. Name: "mypod",
  712. Labels: map[string]string{
  713. "security": "S2",
  714. },
  715. },
  716. Spec: api.PodSpec{
  717. Containers: []api.Container{
  718. {
  719. Name: containerName,
  720. Env: []api.EnvVar{{Name: "abc", Value: "value2"}, {Name: "ABCD", Value: "value3"}},
  721. },
  722. },
  723. },
  724. }
  725. pip := &settingsv1alpha1.PodPreset{
  726. ObjectMeta: metav1.ObjectMeta{
  727. Name: "hello",
  728. Namespace: "different", // (pod will be submitted to namespace 'namespace')
  729. },
  730. Spec: settingsv1alpha1.PodPresetSpec{
  731. Selector: metav1.LabelSelector{
  732. MatchExpressions: []metav1.LabelSelectorRequirement{
  733. {
  734. Key: "security",
  735. Operator: metav1.LabelSelectorOpIn,
  736. Values: []string{"S2"},
  737. },
  738. },
  739. },
  740. Volumes: []corev1.Volume{{Name: "vol", VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}}}},
  741. Env: []corev1.EnvVar{{Name: "abcd", Value: "value"}, {Name: "ABC", Value: "value"}},
  742. EnvFrom: []corev1.EnvFromSource{
  743. {
  744. ConfigMapRef: &corev1.ConfigMapEnvSource{
  745. LocalObjectReference: corev1.LocalObjectReference{Name: "abc"},
  746. },
  747. },
  748. {
  749. Prefix: "pre_",
  750. ConfigMapRef: &corev1.ConfigMapEnvSource{
  751. LocalObjectReference: corev1.LocalObjectReference{Name: "abc"},
  752. },
  753. },
  754. },
  755. },
  756. }
  757. originalPod := pod.DeepCopy()
  758. err := admitPod(t, pod, pip)
  759. if err != nil {
  760. t.Fatal(err)
  761. }
  762. // verify PodSpec has not been mutated
  763. if !reflect.DeepEqual(pod, originalPod) {
  764. t.Fatalf("pod should not get modified in case of emptyNamespace origPod: %+v got: %+v", originalPod, pod)
  765. }
  766. }
  767. func admitPod(t *testing.T, pod *api.Pod, pip *settingsv1alpha1.PodPreset) error {
  768. informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc())
  769. store := informerFactory.Settings().V1alpha1().PodPresets().Informer().GetStore()
  770. store.Add(pip)
  771. plugin := admissiontesting.WithReinvocationTesting(t, NewTestAdmission(informerFactory.Settings().V1alpha1().PodPresets().Lister()))
  772. attrs := kadmission.NewAttributesRecord(
  773. pod,
  774. nil,
  775. api.Kind("Pod").WithVersion("version"),
  776. "namespace",
  777. "",
  778. api.Resource("pods").WithVersion("version"),
  779. "",
  780. kadmission.Create,
  781. &metav1.CreateOptions{},
  782. false,
  783. &user.DefaultInfo{},
  784. )
  785. err := plugin.Admit(context.TODO(), attrs, nil)
  786. if err != nil {
  787. return err
  788. }
  789. return nil
  790. }
  791. func TestEnvFromMergeKey(t *testing.T) {
  792. f := fuzz.New()
  793. for i := 0; i < 100; i++ {
  794. t.Run(fmt.Sprintf("Run %d/100", i), func(t *testing.T) {
  795. orig := api.EnvFromSource{}
  796. f.Fuzz(&orig)
  797. clone := api.EnvFromSource{}
  798. f.Fuzz(&clone)
  799. key := newEnvFromMergeKey(orig)
  800. // copy all key fields into the clone so it only differs by fields not from the key
  801. clone.Prefix = key.prefix
  802. if orig.ConfigMapRef == nil {
  803. clone.ConfigMapRef = nil
  804. } else {
  805. if clone.ConfigMapRef == nil {
  806. clone.ConfigMapRef = &api.ConfigMapEnvSource{
  807. LocalObjectReference: api.LocalObjectReference{},
  808. }
  809. }
  810. clone.ConfigMapRef.Name = key.configMapRefName
  811. }
  812. if orig.SecretRef == nil {
  813. clone.SecretRef = nil
  814. } else {
  815. if clone.SecretRef == nil {
  816. clone.SecretRef = &api.SecretEnvSource{
  817. LocalObjectReference: api.LocalObjectReference{},
  818. }
  819. }
  820. clone.SecretRef.Name = key.secretRefName
  821. }
  822. // zero out known non-identifying fields
  823. for _, e := range []api.EnvFromSource{orig, clone} {
  824. if e.ConfigMapRef != nil {
  825. e.ConfigMapRef.Optional = nil
  826. }
  827. if e.SecretRef != nil {
  828. e.SecretRef.Optional = nil
  829. }
  830. }
  831. if !reflect.DeepEqual(orig, clone) {
  832. t.Errorf("expected all but known non-identifying fields for envFrom to be in envFromMergeKey but found unaccounted for differences, diff:\n%s", diff.ObjectReflectDiff(orig, clone))
  833. }
  834. })
  835. }
  836. }