|
1 | 1 | package node |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "context" |
4 | 5 | "path/filepath" |
5 | 6 | "strings" |
6 | 7 | "time" |
7 | 8 |
|
8 | 9 | g "github.com/onsi/ginkgo/v2" |
9 | 10 | 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" |
10 | 16 | "k8s.io/apimachinery/pkg/util/wait" |
11 | 17 | e2e "k8s.io/kubernetes/test/e2e/framework" |
12 | 18 |
|
@@ -157,4 +163,126 @@ var _ = g.Describe("[sig-node] [Jira:Node/Kubelet] Kubelet, CRI-O, CPU manager", |
157 | 163 | e2e.Logf("/dev/fuse mount output: %s", output) |
158 | 164 | o.Expect(output).To(o.ContainSubstring("fuse"), "dev fuse is not mounted inside pod") |
159 | 165 | }) |
| 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 | + }) |
160 | 288 | }) |
0 commit comments