Skip to content

Commit f6be5a1

Browse files
committed
migrate testcase 67564 from OTP to origin
1 parent 16bf93d commit f6be5a1

2 files changed

Lines changed: 152 additions & 0 deletions

File tree

test/extended/node/node_e2e/node.go

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
package node
22

33
import (
4+
"context"
45
"path/filepath"
56
"strings"
67
"time"
78

89
g "github.com/onsi/ginkgo/v2"
910
o "github.com/onsi/gomega"
11+
appsv1 "k8s.io/api/apps/v1"
12+
corev1 "k8s.io/api/core/v1"
13+
policyv1 "k8s.io/api/policy/v1"
14+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
15+
"k8s.io/apimachinery/pkg/util/intstr"
1016
"k8s.io/apimachinery/pkg/util/wait"
1117
e2e "k8s.io/kubernetes/test/e2e/framework"
1218

@@ -157,4 +163,126 @@ var _ = g.Describe("[sig-node] [Jira:Node/Kubelet] Kubelet, CRI-O, CPU manager",
157163
e2e.Logf("/dev/fuse mount output: %s", output)
158164
o.Expect(output).To(o.ContainSubstring("fuse"), "dev fuse is not mounted inside pod")
159165
})
166+
167+
//author: bgudi@redhat.com
168+
//migrated from openshift-tests-private
169+
//automates: https://issues.redhat.com/browse/OCPBUGS-15035
170+
g.It("[OTP] node's drain should block when PodDisruptionBudget minAvailable equals 100 percentage and selector is empty [Disruptive] [OCP-67564]", func() {
171+
ctx := context.Background()
172+
oc.SetupProject()
173+
namespace := oc.Namespace()
174+
175+
g.By("Create a deployment with 6 replicas")
176+
replicas := int32(6)
177+
deployment := &appsv1.Deployment{
178+
ObjectMeta: metav1.ObjectMeta{
179+
Name: "hello-openshift",
180+
Namespace: namespace,
181+
Labels: map[string]string{
182+
"app": "myapp",
183+
},
184+
},
185+
Spec: appsv1.DeploymentSpec{
186+
Replicas: &replicas,
187+
Selector: &metav1.LabelSelector{
188+
MatchLabels: map[string]string{
189+
"app": "myapp",
190+
},
191+
},
192+
Template: corev1.PodTemplateSpec{
193+
ObjectMeta: metav1.ObjectMeta{
194+
Name: "myapp",
195+
Labels: map[string]string{
196+
"app": "myapp",
197+
},
198+
},
199+
Spec: corev1.PodSpec{
200+
SecurityContext: &corev1.PodSecurityContext{
201+
RunAsNonRoot: &[]bool{true}[0],
202+
SeccompProfile: &corev1.SeccompProfile{
203+
Type: corev1.SeccompProfileTypeRuntimeDefault,
204+
},
205+
},
206+
Containers: []corev1.Container{
207+
{
208+
Name: "myapp",
209+
Image: "quay.io/openshifttest/hello-openshift@sha256:4200f438cf2e9446f6bcff9d67ceea1f69ed07a2f83363b7fb52529f7ddd8a83",
210+
SecurityContext: &corev1.SecurityContext{
211+
AllowPrivilegeEscalation: &[]bool{false}[0],
212+
Capabilities: &corev1.Capabilities{
213+
Drop: []corev1.Capability{"ALL"},
214+
},
215+
},
216+
},
217+
},
218+
},
219+
},
220+
},
221+
}
222+
_, err := oc.KubeClient().AppsV1().Deployments(namespace).Create(ctx, deployment, metav1.CreateOptions{})
223+
o.Expect(err).NotTo(o.HaveOccurred())
224+
defer oc.KubeClient().AppsV1().Deployments(namespace).Delete(ctx, "hello-openshift", metav1.DeleteOptions{})
225+
226+
g.By("Wait for deployment to be ready")
227+
err = wait.Poll(3*time.Second, 5*time.Minute, func() (bool, error) {
228+
deploy, pollErr := oc.KubeClient().AppsV1().Deployments(namespace).Get(ctx, "hello-openshift", metav1.GetOptions{})
229+
if pollErr != nil {
230+
e2e.Logf("Error getting deployment: %v", pollErr)
231+
return false, nil
232+
}
233+
if deploy.Status.ReadyReplicas == 6 {
234+
e2e.Logf("Deployment is ready with %d replicas", deploy.Status.ReadyReplicas)
235+
return true, nil
236+
}
237+
e2e.Logf("Waiting for deployment, ready replicas: %d/6", deploy.Status.ReadyReplicas)
238+
return false, nil
239+
})
240+
o.Expect(err).NotTo(o.HaveOccurred(), "deployment did not become ready")
241+
242+
g.By("Create PodDisruptionBudget with 100% minAvailable")
243+
pdb := &policyv1.PodDisruptionBudget{
244+
ObjectMeta: metav1.ObjectMeta{
245+
Name: "my-pdb",
246+
Namespace: namespace,
247+
},
248+
Spec: policyv1.PodDisruptionBudgetSpec{
249+
MinAvailable: &intstr.IntOrString{
250+
Type: intstr.String,
251+
StrVal: "100%",
252+
},
253+
Selector: &metav1.LabelSelector{},
254+
},
255+
}
256+
_, err = oc.KubeClient().PolicyV1().PodDisruptionBudgets(namespace).Create(ctx, pdb, metav1.CreateOptions{})
257+
o.Expect(err).NotTo(o.HaveOccurred())
258+
defer oc.KubeClient().PolicyV1().PodDisruptionBudgets(namespace).Delete(ctx, "my-pdb", metav1.DeleteOptions{})
259+
260+
g.By("Get a single worker node")
261+
workerNode := nodeutils.GetSingleWorkerNode(ctx, oc)
262+
e2e.Logf("Selected worker node: %s", workerNode)
263+
264+
g.By("Obtain the pods running on node " + workerNode)
265+
podsInWorker, err := oc.AsAdmin().WithoutNamespace().Run("get").Args("pods", "-n", namespace, "-o=jsonpath={.items[?(@.spec.nodeName=='"+workerNode+"')].metadata.name}").Output()
266+
o.Expect(err).NotTo(o.HaveOccurred())
267+
o.Expect(len(strings.Split(podsInWorker, " "))).Should(o.BeNumerically(">", 0))
268+
269+
g.By("Make sure that PDB's status is False")
270+
pdbStatus, err := oc.AsAdmin().WithoutNamespace().Run("get").Args("poddisruptionbudget", "my-pdb", "-n", namespace, "-o=jsonpath={.status.conditions[0].status}").Output()
271+
o.Expect(err).NotTo(o.HaveOccurred())
272+
o.Expect(strings.Contains(pdbStatus, "False")).Should(o.BeTrue())
273+
274+
g.By("Drain the node " + workerNode)
275+
defer nodeutils.WaitClusterOperatorAvailable(ctx, oc)
276+
defer oc.AsAdmin().WithoutNamespace().Run("adm").Args("uncordon", workerNode).Execute()
277+
278+
out, err := oc.AsAdmin().WithoutNamespace().Run("adm").Args("drain", workerNode, "--ignore-daemonsets", "--delete-emptydir-data", "--timeout=30s").Output()
279+
o.Expect(err).To(o.HaveOccurred(), "Drain operation should have been blocked but it wasn't")
280+
o.Expect(strings.Contains(out, "Cannot evict pod as it would violate the pod's disruption budget")).Should(o.BeTrue())
281+
o.Expect(strings.Contains(out, "There are pending nodes to be drained")).Should(o.BeTrue())
282+
283+
g.By("Verify that the pods were not drained from the node")
284+
podsAfterDrain, err := oc.AsAdmin().WithoutNamespace().Run("get").Args("pods", "-n", namespace, "-o=jsonpath={.items[?(@.spec.nodeName=='"+workerNode+"')].metadata.name}").Output()
285+
o.Expect(err).NotTo(o.HaveOccurred())
286+
o.Expect(podsInWorker).Should(o.BeIdenticalTo(podsAfterDrain))
287+
})
160288
})

test/extended/node/node_utils.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -741,3 +741,27 @@ func ensureDropInDirectoryExists(ctx context.Context, oc *exutil.CLI, dirPath st
741741

742742
return nil
743743
}
744+
745+
// GetSingleWorkerNode returns the name of a single worker node
746+
func GetSingleWorkerNode(ctx context.Context, oc *exutil.CLI) string {
747+
nodes, err := getNodesByLabel(ctx, oc, "node-role.kubernetes.io/worker")
748+
o.Expect(err).NotTo(o.HaveOccurred())
749+
o.Expect(nodes).NotTo(o.BeEmpty(), "No worker nodes found")
750+
framework.Logf("Worker Node Name is %v", nodes[0].Name)
751+
return nodes[0].Name
752+
}
753+
754+
// WaitClusterOperatorAvailable waits for all cluster operators to be available
755+
func WaitClusterOperatorAvailable(ctx context.Context, oc *exutil.CLI) {
756+
timeout := 120 * time.Minute
757+
758+
waitErr := wait.PollUntilContextTimeout(ctx, 10*time.Second, timeout, true, func(ctx context.Context) (bool, error) {
759+
availableCOStatus, err := oc.AsAdmin().WithoutNamespace().Run("get").Args("clusteroperator", "-o=jsonpath={.items[*].status.conditions[?(@.type==\"Available\")].status}").Output()
760+
if err != nil || strings.Contains(availableCOStatus, "False") {
761+
framework.Logf("Some Cluster Operator is still Unavailable")
762+
return false, nil
763+
}
764+
return true, nil
765+
})
766+
o.Expect(waitErr).NotTo(o.HaveOccurred(), "Some cluster operator is still unavailable after timeout")
767+
}

0 commit comments

Comments
 (0)