gc_admission_test.go 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625
  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 gc
  14. import (
  15. "context"
  16. "fmt"
  17. "strings"
  18. "testing"
  19. appsv1 "k8s.io/api/apps/v1"
  20. corev1 "k8s.io/api/core/v1"
  21. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  22. "k8s.io/apimachinery/pkg/runtime"
  23. "k8s.io/apimachinery/pkg/runtime/schema"
  24. "k8s.io/apiserver/pkg/admission"
  25. "k8s.io/apiserver/pkg/admission/initializer"
  26. "k8s.io/apiserver/pkg/authentication/user"
  27. "k8s.io/apiserver/pkg/authorization/authorizer"
  28. fakediscovery "k8s.io/client-go/discovery/fake"
  29. "k8s.io/client-go/restmapper"
  30. coretesting "k8s.io/client-go/testing"
  31. api "k8s.io/kubernetes/pkg/apis/core"
  32. kubeadmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
  33. )
  34. type fakeAuthorizer struct{}
  35. func (fakeAuthorizer) Authorize(ctx context.Context, a authorizer.Attributes) (authorizer.Decision, string, error) {
  36. username := a.GetUser().GetName()
  37. if username == "non-deleter" {
  38. if a.GetVerb() == "delete" {
  39. return authorizer.DecisionNoOpinion, "", nil
  40. }
  41. if a.GetVerb() == "update" && a.GetSubresource() == "finalizers" {
  42. return authorizer.DecisionNoOpinion, "", nil
  43. }
  44. return authorizer.DecisionAllow, "", nil
  45. }
  46. if username == "non-pod-deleter" {
  47. if a.GetVerb() == "delete" && a.GetResource() == "pods" {
  48. return authorizer.DecisionNoOpinion, "", nil
  49. }
  50. if a.GetVerb() == "update" && a.GetResource() == "pods" && a.GetSubresource() == "finalizers" {
  51. return authorizer.DecisionNoOpinion, "", nil
  52. }
  53. return authorizer.DecisionAllow, "", nil
  54. }
  55. if username == "non-rc-deleter" {
  56. if a.GetVerb() == "delete" && a.GetResource() == "replicationcontrollers" {
  57. return authorizer.DecisionNoOpinion, "", nil
  58. }
  59. if a.GetVerb() == "update" && a.GetResource() == "replicationcontrollers" && a.GetSubresource() == "finalizers" {
  60. return authorizer.DecisionNoOpinion, "", nil
  61. }
  62. return authorizer.DecisionAllow, "", nil
  63. }
  64. if username == "non-node-deleter" {
  65. if a.GetVerb() == "delete" && a.GetResource() == "nodes" {
  66. return authorizer.DecisionNoOpinion, "", nil
  67. }
  68. if a.GetVerb() == "update" && a.GetResource() == "nodes" && a.GetSubresource() == "finalizers" {
  69. return authorizer.DecisionNoOpinion, "", nil
  70. }
  71. return authorizer.DecisionAllow, "", nil
  72. }
  73. return authorizer.DecisionAllow, "", nil
  74. }
  75. // newGCPermissionsEnforcement returns the admission controller configured for testing.
  76. func newGCPermissionsEnforcement() (*gcPermissionsEnforcement, error) {
  77. // the pods/status endpoint is ignored by this plugin since old kubelets
  78. // corrupt them. the pod status strategy ensures status updates cannot mutate
  79. // ownerRef.
  80. whiteList := []whiteListItem{
  81. {
  82. groupResource: schema.GroupResource{Resource: "pods"},
  83. subresource: "status",
  84. },
  85. }
  86. gcAdmit := &gcPermissionsEnforcement{
  87. Handler: admission.NewHandler(admission.Create, admission.Update),
  88. whiteList: whiteList,
  89. }
  90. genericPluginInitializer := initializer.New(nil, nil, fakeAuthorizer{}, nil)
  91. fakeDiscoveryClient := &fakediscovery.FakeDiscovery{Fake: &coretesting.Fake{}}
  92. fakeDiscoveryClient.Resources = []*metav1.APIResourceList{
  93. {
  94. GroupVersion: corev1.SchemeGroupVersion.String(),
  95. APIResources: []metav1.APIResource{
  96. {Name: "nodes", Namespaced: false, Kind: "Node"},
  97. {Name: "pods", Namespaced: true, Kind: "Pod"},
  98. {Name: "replicationcontrollers", Namespaced: true, Kind: "ReplicationController"},
  99. },
  100. },
  101. {
  102. GroupVersion: appsv1.SchemeGroupVersion.String(),
  103. APIResources: []metav1.APIResource{
  104. {Name: "daemonsets", Namespaced: true, Kind: "DaemonSet"},
  105. },
  106. },
  107. }
  108. restMapperRes, err := restmapper.GetAPIGroupResources(fakeDiscoveryClient)
  109. if err != nil {
  110. return nil, fmt.Errorf("unexpected error while constructing resource list from fake discovery client: %v", err)
  111. }
  112. restMapper := restmapper.NewDiscoveryRESTMapper(restMapperRes)
  113. pluginInitializer := kubeadmission.NewPluginInitializer(nil, restMapper, nil)
  114. initializersChain := admission.PluginInitializers{}
  115. initializersChain = append(initializersChain, genericPluginInitializer)
  116. initializersChain = append(initializersChain, pluginInitializer)
  117. initializersChain.Initialize(gcAdmit)
  118. return gcAdmit, nil
  119. }
  120. func TestGCAdmission(t *testing.T) {
  121. expectNoError := func(err error) bool {
  122. return err == nil
  123. }
  124. expectCantSetOwnerRefError := func(err error) bool {
  125. if err == nil {
  126. return false
  127. }
  128. return strings.Contains(err.Error(), "cannot set an ownerRef on a resource you can't delete")
  129. }
  130. tests := []struct {
  131. name string
  132. username string
  133. resource schema.GroupVersionResource
  134. subresource string
  135. oldObj runtime.Object
  136. newObj runtime.Object
  137. checkError func(error) bool
  138. }{
  139. {
  140. name: "super-user, create, no objectref change",
  141. username: "super",
  142. resource: api.SchemeGroupVersion.WithResource("pods"),
  143. newObj: &api.Pod{},
  144. checkError: expectNoError,
  145. },
  146. {
  147. name: "super-user, create, objectref change",
  148. username: "super",
  149. resource: api.SchemeGroupVersion.WithResource("pods"),
  150. newObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}},
  151. checkError: expectNoError,
  152. },
  153. {
  154. name: "non-deleter, create, no objectref change",
  155. username: "non-deleter",
  156. resource: api.SchemeGroupVersion.WithResource("pods"),
  157. newObj: &api.Pod{},
  158. checkError: expectNoError,
  159. },
  160. {
  161. name: "non-deleter, create, objectref change",
  162. username: "non-deleter",
  163. resource: api.SchemeGroupVersion.WithResource("pods"),
  164. newObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}},
  165. checkError: expectNoError,
  166. },
  167. {
  168. name: "non-pod-deleter, create, no objectref change",
  169. username: "non-pod-deleter",
  170. resource: api.SchemeGroupVersion.WithResource("pods"),
  171. newObj: &api.Pod{},
  172. checkError: expectNoError,
  173. },
  174. {
  175. name: "non-pod-deleter, create, objectref change",
  176. username: "non-pod-deleter",
  177. resource: api.SchemeGroupVersion.WithResource("pods"),
  178. newObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}},
  179. checkError: expectNoError,
  180. },
  181. {
  182. name: "non-pod-deleter, create, objectref change, but not a pod",
  183. username: "non-pod-deleter",
  184. resource: api.SchemeGroupVersion.WithResource("not-pods"),
  185. newObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}},
  186. checkError: expectNoError,
  187. },
  188. {
  189. name: "super-user, update, no objectref change",
  190. username: "super",
  191. resource: api.SchemeGroupVersion.WithResource("pods"),
  192. oldObj: &api.Pod{},
  193. newObj: &api.Pod{},
  194. checkError: expectNoError,
  195. },
  196. {
  197. name: "super-user, update, no objectref change two",
  198. username: "super",
  199. resource: api.SchemeGroupVersion.WithResource("pods"),
  200. oldObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}},
  201. newObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}},
  202. checkError: expectNoError,
  203. },
  204. {
  205. name: "super-user, update, objectref change",
  206. username: "super",
  207. resource: api.SchemeGroupVersion.WithResource("pods"),
  208. oldObj: &api.Pod{},
  209. newObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}},
  210. checkError: expectNoError,
  211. },
  212. {
  213. name: "non-deleter, update, no objectref change",
  214. username: "non-deleter",
  215. resource: api.SchemeGroupVersion.WithResource("pods"),
  216. oldObj: &api.Pod{},
  217. newObj: &api.Pod{},
  218. checkError: expectNoError,
  219. },
  220. {
  221. name: "non-deleter, update, no objectref change two",
  222. username: "non-deleter",
  223. resource: api.SchemeGroupVersion.WithResource("pods"),
  224. oldObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}},
  225. newObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}},
  226. checkError: expectNoError,
  227. },
  228. {
  229. name: "non-deleter, update, objectref change",
  230. username: "non-deleter",
  231. resource: api.SchemeGroupVersion.WithResource("pods"),
  232. oldObj: &api.Pod{},
  233. newObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}},
  234. checkError: expectCantSetOwnerRefError,
  235. },
  236. {
  237. name: "non-deleter, update, objectref change two",
  238. username: "non-deleter",
  239. resource: api.SchemeGroupVersion.WithResource("pods"),
  240. oldObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}},
  241. newObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}, {Name: "second"}}}},
  242. checkError: expectCantSetOwnerRefError,
  243. },
  244. {
  245. name: "non-pod-deleter, update, no objectref change",
  246. username: "non-pod-deleter",
  247. resource: api.SchemeGroupVersion.WithResource("pods"),
  248. oldObj: &api.Pod{},
  249. newObj: &api.Pod{},
  250. checkError: expectNoError,
  251. },
  252. {
  253. name: "non-pod-deleter, update status, objectref change",
  254. username: "non-pod-deleter",
  255. resource: api.SchemeGroupVersion.WithResource("pods"),
  256. subresource: "status",
  257. oldObj: &api.Pod{},
  258. newObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}},
  259. checkError: expectNoError,
  260. },
  261. {
  262. name: "non-pod-deleter, update, objectref change",
  263. username: "non-pod-deleter",
  264. resource: api.SchemeGroupVersion.WithResource("pods"),
  265. oldObj: &api.Pod{},
  266. newObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}},
  267. checkError: expectCantSetOwnerRefError,
  268. },
  269. {
  270. name: "non-pod-deleter, update, objectref change, but not a pod",
  271. username: "non-pod-deleter",
  272. resource: api.SchemeGroupVersion.WithResource("not-pods"),
  273. oldObj: &api.Pod{},
  274. newObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}},
  275. checkError: expectNoError,
  276. },
  277. }
  278. for _, tc := range tests {
  279. t.Run(tc.name, func(t *testing.T) {
  280. gcAdmit, err := newGCPermissionsEnforcement()
  281. if err != nil {
  282. t.Error(err)
  283. }
  284. operation := admission.Create
  285. var options runtime.Object = &metav1.CreateOptions{}
  286. if tc.oldObj != nil {
  287. operation = admission.Update
  288. options = &metav1.UpdateOptions{}
  289. }
  290. user := &user.DefaultInfo{Name: tc.username}
  291. attributes := admission.NewAttributesRecord(tc.newObj, tc.oldObj, schema.GroupVersionKind{}, metav1.NamespaceDefault, "foo", tc.resource, tc.subresource, operation, options, false, user)
  292. err = gcAdmit.Validate(context.TODO(), attributes, nil)
  293. if !tc.checkError(err) {
  294. t.Errorf("unexpected err: %v", err)
  295. }
  296. })
  297. }
  298. }
  299. func TestBlockOwnerDeletionAdmission(t *testing.T) {
  300. podWithOwnerRefs := func(refs ...metav1.OwnerReference) *api.Pod {
  301. var refSlice []metav1.OwnerReference
  302. refSlice = append(refSlice, refs...)
  303. return &api.Pod{
  304. ObjectMeta: metav1.ObjectMeta{
  305. OwnerReferences: refSlice,
  306. },
  307. }
  308. }
  309. getTrueVar := func() *bool {
  310. ret := true
  311. return &ret
  312. }
  313. getFalseVar := func() *bool {
  314. ret := false
  315. return &ret
  316. }
  317. blockRC1 := metav1.OwnerReference{
  318. APIVersion: "v1",
  319. Kind: "ReplicationController",
  320. Name: "rc1",
  321. BlockOwnerDeletion: getTrueVar(),
  322. }
  323. blockRC2 := metav1.OwnerReference{
  324. APIVersion: "v1",
  325. Kind: "ReplicationController",
  326. Name: "rc2",
  327. BlockOwnerDeletion: getTrueVar(),
  328. }
  329. notBlockRC1 := metav1.OwnerReference{
  330. APIVersion: "v1",
  331. Kind: "ReplicationController",
  332. Name: "rc1",
  333. BlockOwnerDeletion: getFalseVar(),
  334. }
  335. notBlockRC2 := metav1.OwnerReference{
  336. APIVersion: "v1",
  337. Kind: "ReplicationController",
  338. Name: "rc2",
  339. BlockOwnerDeletion: getFalseVar(),
  340. }
  341. nilBlockRC1 := metav1.OwnerReference{
  342. APIVersion: "v1",
  343. Kind: "ReplicationController",
  344. Name: "rc1",
  345. }
  346. nilBlockRC2 := metav1.OwnerReference{
  347. APIVersion: "v1",
  348. Kind: "ReplicationController",
  349. Name: "rc2",
  350. }
  351. blockDS1 := metav1.OwnerReference{
  352. APIVersion: "apps/v1",
  353. Kind: "DaemonSet",
  354. Name: "ds1",
  355. BlockOwnerDeletion: getTrueVar(),
  356. }
  357. notBlockDS1 := metav1.OwnerReference{
  358. APIVersion: "apps/v1",
  359. Kind: "DaemonSet",
  360. Name: "ds1",
  361. BlockOwnerDeletion: getFalseVar(),
  362. }
  363. blockNode := metav1.OwnerReference{
  364. APIVersion: "v1",
  365. Kind: "Node",
  366. Name: "node1",
  367. BlockOwnerDeletion: getTrueVar(),
  368. }
  369. notBlockNode := metav1.OwnerReference{
  370. APIVersion: "v1",
  371. Kind: "Node",
  372. Name: "node",
  373. BlockOwnerDeletion: getFalseVar(),
  374. }
  375. nilBlockNode := metav1.OwnerReference{
  376. APIVersion: "v1",
  377. Kind: "Node",
  378. Name: "node",
  379. }
  380. expectNoError := func(err error) bool {
  381. return err == nil
  382. }
  383. expectCantSetBlockOwnerDeletionError := func(err error) bool {
  384. if err == nil {
  385. return false
  386. }
  387. return strings.Contains(err.Error(), "cannot set blockOwnerDeletion if an ownerReference refers to a resource you can't set finalizers on")
  388. }
  389. tests := []struct {
  390. name string
  391. username string
  392. resource schema.GroupVersionResource
  393. subresource string
  394. oldObj runtime.Object
  395. newObj runtime.Object
  396. checkError func(error) bool
  397. }{
  398. // cases for create
  399. {
  400. name: "super-user, create, no ownerReferences",
  401. username: "super",
  402. resource: api.SchemeGroupVersion.WithResource("pods"),
  403. newObj: podWithOwnerRefs(),
  404. checkError: expectNoError,
  405. },
  406. {
  407. name: "super-user, create, all ownerReferences have blockOwnerDeletion=false",
  408. username: "super",
  409. resource: api.SchemeGroupVersion.WithResource("pods"),
  410. newObj: podWithOwnerRefs(notBlockRC1, notBlockRC2),
  411. checkError: expectNoError,
  412. },
  413. {
  414. name: "super-user, create, some ownerReferences have blockOwnerDeletion=true",
  415. username: "super",
  416. resource: api.SchemeGroupVersion.WithResource("pods"),
  417. newObj: podWithOwnerRefs(blockRC1, blockRC2, blockNode),
  418. checkError: expectNoError,
  419. },
  420. {
  421. name: "non-rc-deleter, create, no ownerReferences",
  422. username: "non-rc-deleter",
  423. resource: api.SchemeGroupVersion.WithResource("pods"),
  424. newObj: podWithOwnerRefs(),
  425. checkError: expectNoError,
  426. },
  427. {
  428. name: "non-rc-deleter, create, all ownerReferences have blockOwnerDeletion=false or nil",
  429. username: "non-rc-deleter",
  430. resource: api.SchemeGroupVersion.WithResource("pods"),
  431. newObj: podWithOwnerRefs(notBlockRC1, nilBlockRC2),
  432. checkError: expectNoError,
  433. },
  434. {
  435. name: "non-node-deleter, create, all ownerReferences have blockOwnerDeletion=false",
  436. username: "non-node-deleter",
  437. resource: api.SchemeGroupVersion.WithResource("pods"),
  438. newObj: podWithOwnerRefs(notBlockNode),
  439. checkError: expectNoError,
  440. },
  441. {
  442. name: "non-rc-deleter, create, some ownerReferences have blockOwnerDeletion=true",
  443. username: "non-rc-deleter",
  444. resource: api.SchemeGroupVersion.WithResource("pods"),
  445. newObj: podWithOwnerRefs(blockRC1, notBlockRC2),
  446. checkError: expectCantSetBlockOwnerDeletionError,
  447. },
  448. {
  449. name: "non-rc-deleter, create, some ownerReferences have blockOwnerDeletion=true, but are pointing to daemonset",
  450. username: "non-rc-deleter",
  451. resource: api.SchemeGroupVersion.WithResource("pods"),
  452. newObj: podWithOwnerRefs(blockDS1),
  453. checkError: expectNoError,
  454. },
  455. {
  456. name: "non-node-deleter, create, some ownerReferences have blockOwnerDeletion=true",
  457. username: "non-node-deleter",
  458. resource: api.SchemeGroupVersion.WithResource("pods"),
  459. newObj: podWithOwnerRefs(blockNode),
  460. checkError: expectCantSetBlockOwnerDeletionError,
  461. },
  462. // cases are for update
  463. {
  464. name: "super-user, update, no ownerReferences change blockOwnerDeletion",
  465. username: "super",
  466. resource: api.SchemeGroupVersion.WithResource("pods"),
  467. oldObj: podWithOwnerRefs(nilBlockRC1, nilBlockNode),
  468. newObj: podWithOwnerRefs(notBlockRC1, notBlockNode),
  469. checkError: expectNoError,
  470. },
  471. {
  472. name: "super-user, update, some ownerReferences change to blockOwnerDeletion=true",
  473. username: "super",
  474. resource: api.SchemeGroupVersion.WithResource("pods"),
  475. oldObj: podWithOwnerRefs(notBlockRC1, notBlockNode),
  476. newObj: podWithOwnerRefs(blockRC1, blockNode),
  477. checkError: expectNoError,
  478. },
  479. {
  480. name: "super-user, update, add new ownerReferences with blockOwnerDeletion=true",
  481. username: "super",
  482. resource: api.SchemeGroupVersion.WithResource("pods"),
  483. oldObj: podWithOwnerRefs(),
  484. newObj: podWithOwnerRefs(blockRC1, blockNode),
  485. checkError: expectNoError,
  486. },
  487. {
  488. name: "non-rc-deleter, update, no ownerReferences change blockOwnerDeletion",
  489. username: "non-rc-deleter",
  490. resource: api.SchemeGroupVersion.WithResource("pods"),
  491. oldObj: podWithOwnerRefs(nilBlockRC1),
  492. newObj: podWithOwnerRefs(notBlockRC1),
  493. checkError: expectNoError,
  494. },
  495. {
  496. name: "non-rc-deleter, update, some ownerReferences change from blockOwnerDeletion=false to true",
  497. username: "non-rc-deleter",
  498. resource: api.SchemeGroupVersion.WithResource("pods"),
  499. oldObj: podWithOwnerRefs(notBlockRC1),
  500. newObj: podWithOwnerRefs(blockRC1),
  501. checkError: expectCantSetBlockOwnerDeletionError,
  502. },
  503. {
  504. name: "non-rc-deleter, update, some ownerReferences change from blockOwnerDeletion=nil to true",
  505. username: "non-rc-deleter",
  506. resource: api.SchemeGroupVersion.WithResource("pods"),
  507. oldObj: podWithOwnerRefs(nilBlockRC1),
  508. newObj: podWithOwnerRefs(blockRC1),
  509. checkError: expectCantSetBlockOwnerDeletionError,
  510. },
  511. {
  512. name: "non-node-deleter, update, some ownerReferences change from blockOwnerDeletion=nil to true",
  513. username: "non-node-deleter",
  514. resource: api.SchemeGroupVersion.WithResource("pods"),
  515. oldObj: podWithOwnerRefs(nilBlockNode),
  516. newObj: podWithOwnerRefs(blockNode),
  517. checkError: expectCantSetBlockOwnerDeletionError,
  518. },
  519. {
  520. name: "non-rc-deleter, update, some ownerReferences change from blockOwnerDeletion=true to false",
  521. username: "non-rc-deleter",
  522. resource: api.SchemeGroupVersion.WithResource("pods"),
  523. oldObj: podWithOwnerRefs(blockRC1),
  524. newObj: podWithOwnerRefs(notBlockRC1),
  525. checkError: expectNoError,
  526. },
  527. {
  528. name: "non-node-deleter, update, some ownerReferences change from blockOwnerDeletion=true to false",
  529. username: "non-node-deleter",
  530. resource: api.SchemeGroupVersion.WithResource("pods"),
  531. oldObj: podWithOwnerRefs(blockNode),
  532. newObj: podWithOwnerRefs(notBlockNode),
  533. checkError: expectNoError,
  534. },
  535. {
  536. name: "non-rc-deleter, update, some ownerReferences change blockOwnerDeletion, but all such references are to daemonset",
  537. username: "non-rc-deleter",
  538. resource: api.SchemeGroupVersion.WithResource("pods"),
  539. oldObj: podWithOwnerRefs(notBlockDS1),
  540. newObj: podWithOwnerRefs(blockDS1),
  541. checkError: expectNoError,
  542. },
  543. {
  544. name: "non-rc-deleter, update, add new ownerReferences with blockOwnerDeletion=nil or false",
  545. username: "non-rc-deleter",
  546. resource: api.SchemeGroupVersion.WithResource("pods"),
  547. oldObj: podWithOwnerRefs(),
  548. newObj: podWithOwnerRefs(notBlockRC1, nilBlockRC2),
  549. checkError: expectNoError,
  550. },
  551. {
  552. name: "non-rc-deleter, update, add new ownerReferences with blockOwnerDeletion=true",
  553. username: "non-rc-deleter",
  554. resource: api.SchemeGroupVersion.WithResource("pods"),
  555. oldObj: podWithOwnerRefs(),
  556. newObj: podWithOwnerRefs(blockRC1),
  557. checkError: expectCantSetBlockOwnerDeletionError,
  558. },
  559. {
  560. name: "non-rc-deleter, update, add new ownerReferences with blockOwnerDeletion=true, but the references are to daemonset",
  561. username: "non-rc-deleter",
  562. resource: api.SchemeGroupVersion.WithResource("pods"),
  563. oldObj: podWithOwnerRefs(),
  564. newObj: podWithOwnerRefs(blockDS1),
  565. checkError: expectNoError,
  566. },
  567. {
  568. name: "non-node-deleter, update, add ownerReferences with blockOwnerDeletion=true",
  569. username: "non-node-deleter",
  570. resource: api.SchemeGroupVersion.WithResource("pods"),
  571. oldObj: podWithOwnerRefs(),
  572. newObj: podWithOwnerRefs(blockNode),
  573. checkError: expectCantSetBlockOwnerDeletionError,
  574. },
  575. }
  576. gcAdmit, err := newGCPermissionsEnforcement()
  577. if err != nil {
  578. t.Error(err)
  579. }
  580. for _, tc := range tests {
  581. operation := admission.Create
  582. var options runtime.Object = &metav1.CreateOptions{}
  583. if tc.oldObj != nil {
  584. operation = admission.Update
  585. options = &metav1.UpdateOptions{}
  586. }
  587. user := &user.DefaultInfo{Name: tc.username}
  588. attributes := admission.NewAttributesRecord(tc.newObj, tc.oldObj, schema.GroupVersionKind{}, metav1.NamespaceDefault, "foo", tc.resource, tc.subresource, operation, options, false, user)
  589. err := gcAdmit.Validate(context.TODO(), attributes, nil)
  590. if !tc.checkError(err) {
  591. t.Errorf("%v: unexpected err: %v", tc.name, err)
  592. }
  593. }
  594. }