admission_test.go 24 KB

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