Commit dc2b0ee2 authored by Kubernetes Submit Queue's avatar Kubernetes Submit Queue Committed by GitHub

Merge pull request #43034 from enisoc/statefulset-patch

Automatic merge from submit-queue (batch tested with PRs 43034, 43066) Allow StatefulSet controller to PATCH Pods. **What this PR does / why we need it**: StatefulSet now needs the PATCH permission on Pods since it calls into ControllerRefManager to adopt and release. This adds the permission and the missing e2e test that should have caught this. **Which issue this PR fixes**: **Special notes for your reviewer**: This is based on #42925. **Release note**: ```release-note ``` cc @kubernetes/sig-apps-pr-reviews
parents f53ba558 53a6f440
...@@ -260,7 +260,7 @@ func init() { ...@@ -260,7 +260,7 @@ func init() {
rbac.NewRule("list", "watch").Groups(legacyGroup).Resources("pods").RuleOrDie(), rbac.NewRule("list", "watch").Groups(legacyGroup).Resources("pods").RuleOrDie(),
rbac.NewRule("get", "list", "watch").Groups(appsGroup).Resources("statefulsets").RuleOrDie(), rbac.NewRule("get", "list", "watch").Groups(appsGroup).Resources("statefulsets").RuleOrDie(),
rbac.NewRule("update").Groups(appsGroup).Resources("statefulsets/status").RuleOrDie(), rbac.NewRule("update").Groups(appsGroup).Resources("statefulsets/status").RuleOrDie(),
rbac.NewRule("get", "create", "delete", "update").Groups(legacyGroup).Resources("pods").RuleOrDie(), rbac.NewRule("get", "create", "delete", "update", "patch").Groups(legacyGroup).Resources("pods").RuleOrDie(),
rbac.NewRule("get", "create").Groups(legacyGroup).Resources("persistentvolumeclaims").RuleOrDie(), rbac.NewRule("get", "create").Groups(legacyGroup).Resources("persistentvolumeclaims").RuleOrDie(),
eventsRule(), eventsRule(),
}, },
......
...@@ -946,6 +946,7 @@ items: ...@@ -946,6 +946,7 @@ items:
- create - create
- delete - delete
- get - get
- patch
- update - update
- apiGroups: - apiGroups:
- "" - ""
......
...@@ -33,6 +33,7 @@ import ( ...@@ -33,6 +33,7 @@ import (
"k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/api/v1"
apps "k8s.io/kubernetes/pkg/apis/apps/v1beta1" apps "k8s.io/kubernetes/pkg/apis/apps/v1beta1"
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset" "k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
"k8s.io/kubernetes/pkg/controller"
"k8s.io/kubernetes/test/e2e/framework" "k8s.io/kubernetes/test/e2e/framework"
) )
...@@ -127,6 +128,90 @@ var _ = framework.KubeDescribe("StatefulSet", func() { ...@@ -127,6 +128,90 @@ var _ = framework.KubeDescribe("StatefulSet", func() {
framework.ExpectNoError(sst.ExecInStatefulPods(ss, cmd)) framework.ExpectNoError(sst.ExecInStatefulPods(ss, cmd))
}) })
It("should adopt matching orphans and release non-matching pods", func() {
By("Creating statefulset " + ssName + " in namespace " + ns)
*(ss.Spec.Replicas) = 1
framework.SetStatefulSetInitializedAnnotation(ss, "false")
// Replace ss with the one returned from Create() so it has the UID.
// Save Kind since it won't be populated in the returned ss.
kind := ss.Kind
ss, err := c.Apps().StatefulSets(ns).Create(ss)
Expect(err).NotTo(HaveOccurred())
ss.Kind = kind
sst := framework.NewStatefulSetTester(c)
By("Saturating stateful set " + ss.Name)
sst.Saturate(ss)
pods := sst.GetPodList(ss)
Expect(pods.Items).To(HaveLen(int(*ss.Spec.Replicas)))
By("Checking that stateful set pods are created with ControllerRef")
pod := pods.Items[0]
controllerRef := controller.GetControllerOf(&pod)
Expect(controllerRef).ToNot(BeNil())
Expect(controllerRef.Kind).To(Equal(ss.Kind))
Expect(controllerRef.Name).To(Equal(ss.Name))
Expect(controllerRef.UID).To(Equal(ss.UID))
By("Orphaning one of the stateful set's pods")
f.PodClient().Update(pod.Name, func(pod *v1.Pod) {
pod.OwnerReferences = nil
})
By("Checking that the stateful set readopts the pod")
Expect(framework.WaitForPodCondition(c, pod.Namespace, pod.Name, "adopted", framework.StatefulSetTimeout,
func(pod *v1.Pod) (bool, error) {
controllerRef := controller.GetControllerOf(pod)
if controllerRef == nil {
return false, nil
}
if controllerRef.Kind != ss.Kind || controllerRef.Name != ss.Name || controllerRef.UID != ss.UID {
return false, fmt.Errorf("pod has wrong controllerRef: %v", controllerRef)
}
return true, nil
},
)).To(Succeed(), "wait for pod %q to be readopted", pod.Name)
By("Removing the labels from one of the stateful set's pods")
prevLabels := pod.Labels
f.PodClient().Update(pod.Name, func(pod *v1.Pod) {
pod.Labels = nil
})
By("Checking that the stateful set releases the pod")
Expect(framework.WaitForPodCondition(c, pod.Namespace, pod.Name, "released", framework.StatefulSetTimeout,
func(pod *v1.Pod) (bool, error) {
controllerRef := controller.GetControllerOf(pod)
if controllerRef != nil {
return false, nil
}
return true, nil
},
)).To(Succeed(), "wait for pod %q to be released", pod.Name)
// If we don't do this, the test leaks the Pod and PVC.
By("Readding labels to the stateful set's pod")
f.PodClient().Update(pod.Name, func(pod *v1.Pod) {
pod.Labels = prevLabels
})
By("Checking that the stateful set readopts the pod")
Expect(framework.WaitForPodCondition(c, pod.Namespace, pod.Name, "adopted", framework.StatefulSetTimeout,
func(pod *v1.Pod) (bool, error) {
controllerRef := controller.GetControllerOf(pod)
if controllerRef == nil {
return false, nil
}
if controllerRef.Kind != ss.Kind || controllerRef.Name != ss.Name || controllerRef.UID != ss.UID {
return false, fmt.Errorf("pod has wrong controllerRef: %v", controllerRef)
}
return true, nil
},
)).To(Succeed(), "wait for pod %q to be readopted", pod.Name)
})
It("should not deadlock when a pod's predecessor fails", func() { It("should not deadlock when a pod's predecessor fails", func() {
By("Creating statefulset " + ssName + " in namespace " + ns) By("Creating statefulset " + ssName + " in namespace " + ns)
*(ss.Spec.Replicas) = 2 *(ss.Spec.Replicas) = 2
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment