Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion tests/mdr-operator/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,21 @@ container or pod level). Only checks the `manager` container.
- **Standalone**: `ginkgo --label-filter="mdr" --focus="runs as non-root" ./tests/mdr-operator/...`
- **Pass criteria**: Pod runAsNonRoot=true; expected manager container exists; manager container runAsUser != 0; allowPrivilegeEscalation=false; readOnlyRootFilesystem=true; capabilities.drop=[ALL]; seccomp profile RuntimeDefault

## Negative Validation Tests

### 5. Verify MDRT With Invalid Values Is Rejected ([OCP-60889](https://polarion.engineering.redhat.com/polarion/#/project/OSE/workitem?id=OCP-60889))

Validates that the API server rejects MachineDeletionRemediationTemplate CRs
with invalid metadata. First attempts creation with a non-existent namespace
(`mdr-test-nonexistent-ns`), then with an invalid name (`-1-invalid-value`)
that violates RFC 1123 subdomain rules.

- **Operators**: MDR v0.7.0+
- **Cluster**: Any topology (MNO or SNO)
- **Environment**: Connected or disconnected
- **Standalone**: `ginkgo --label-filter="mdr" --focus="invalid values" ./tests/mdr-operator/...`
- **Pass criteria**: MDRT with non-existent namespace rejected with NotFound error; MDRT with invalid name rejected with Invalid error (k8serrors.IsInvalid)

## Destructive Tests -- NHC-Triggered Remediation

Tests that stop kubelet on a worker node, let NHC detect the unhealthy node
Expand All @@ -88,7 +103,7 @@ provider provisions a new VM. The node is re-created (new creation timestamp).
- At least 2 Ready worker nodes (target + spare for cluster schedulability)
- `KUBECONFIG` set with cluster-admin access

### 5. MDR Remediation with Condition Transitions ([OCP-66138](https://polarion.engineering.redhat.com/polarion/#/project/OSE/workitem?id=OCP-66138))
### 6. MDR Remediation with Condition Transitions ([OCP-66138](https://polarion.engineering.redhat.com/polarion/#/project/OSE/workitem?id=OCP-66138))

Stops kubelet on a worker node. NHC detects the unhealthy node and creates
an MDR CR via the MDR template. Verifies the MDR CR status conditions
Expand Down
9 changes: 9 additions & 0 deletions tests/mdr-operator/internal/mdrparams/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,13 @@ const (
// ConditionReasonRemediationStarted is the reason set on Processing and Succeeded
// conditions when remediation begins.
ConditionReasonRemediationStarted = "RemediationStarted"

// MDRTNegativeTestName is the MDRT name used in negative validation tests.
MDRTNegativeTestName = "mdr-negative-test-template"

// MDRTInvalidTestName is a name that violates RFC 1123 subdomain rules.
MDRTInvalidTestName = "-1-invalid-value"

// MDRTInvalidTestNamespace is a syntactically valid namespace that does not exist.
MDRTInvalidTestNamespace = "mdr-test-nonexistent-ns"
)
132 changes: 132 additions & 0 deletions tests/mdr-operator/tests/mdr_negative.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package tests

import (
"context"
"fmt"
"strings"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"

"github.com/rh-ecosystem-edge/eco-goinfra/pkg/deployment"
"github.com/rh-ecosystem-edge/eco-goinfra/pkg/pod"
"github.com/rh-ecosystem-edge/eco-goinfra/pkg/reportxml"

"github.com/medik8s/system-tests/tests/internal/helpers"
"github.com/medik8s/system-tests/tests/internal/labels"
. "github.com/medik8s/system-tests/tests/internal/medik8sinittools"
"github.com/medik8s/system-tests/tests/internal/medik8sparams"
"github.com/medik8s/system-tests/tests/mdr-operator/internal/mdrparams"

k8serrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

var _ = Describe(
"MDR Negative Validation tests",
Ordered,
ContinueOnFailure,
Label(labels.OperatorMDR, mdrparams.Label), func() {
BeforeAll(func() {
By("Verify MDR deployment is ready")

mdrDeployment, err := deployment.Pull(
APIClient, mdrparams.OperatorDeploymentName, medik8sparams.OperatorNs)
Expect(err).ToNot(HaveOccurred(), "Failed to get MDR deployment")
Comment thread
gamado marked this conversation as resolved.
Expect(mdrDeployment.IsReady(medik8sparams.DefaultTimeout)).To(BeTrue(),
Comment thread
gamado marked this conversation as resolved.
"MDR deployment is not Ready")

By("Pre-cleaning stale test resources from previous runs")

cleanupMDRT(mdrparams.MDRTNegativeTestName)
cleanupMDRT(mdrparams.MDRTInvalidTestName)
})

AfterAll(func() {
By("Cleaning up test MDRTs if unexpectedly created")

cleanupMDRT(mdrparams.MDRTNegativeTestName)
cleanupMDRT(mdrparams.MDRTInvalidTestName)

By("Verifying MDR controller pod is running")

Eventually(verifyMDRControllerRunning,
medik8sparams.DefaultTimeout, mdrparams.DefaultPollInterval).Should(Succeed(),
"MDR controller pod should be running after negative tests")
})

It("Verify MDRT with invalid values is rejected by API server",
reportxml.ID("60889"),
Label(labels.TierAcceptance,
labels.DisruptionNonDestructive, labels.PlatformAny,
labels.FrequencyPresubmit), func() {
var validationErrors []string

By("Creating MDRT with non-existent namespace")

mdrtInvalidNs := buildMDRT(mdrparams.MDRTNegativeTestName)
mdrtInvalidNs.SetNamespace(mdrparams.MDRTInvalidTestNamespace)

err := APIClient.Create(context.Background(), mdrtInvalidNs)
if err == nil {
DeferCleanup(func() {
By("Cleaning up unexpectedly created MDRT in non-existent namespace")
if delErr := APIClient.Delete(context.Background(), mdrtInvalidNs); delErr != nil && !k8serrors.IsNotFound(delErr) {
GinkgoWriter.Printf("Warning: failed to delete MDRT %s/%s: %v\n",
mdrtInvalidNs.GetNamespace(), mdrtInvalidNs.GetName(), delErr)
}
})
Comment thread
gamado marked this conversation as resolved.

validationErrors = append(validationErrors,
fmt.Sprintf("MDRT with namespace %q was unexpectedly created",
mdrparams.MDRTInvalidTestNamespace))
} else if !k8serrors.IsNotFound(err) {
validationErrors = append(validationErrors,
fmt.Sprintf("MDRT with namespace %q: expected NotFound error, got: %v",
mdrparams.MDRTInvalidTestNamespace, err))
}

By("Creating MDRT with name violating RFC 1123")

mdrtInvalidName := buildMDRT(mdrparams.MDRTInvalidTestName)

err = APIClient.Create(context.Background(), mdrtInvalidName)
if err == nil {
DeferCleanup(func() { cleanupMDRT(mdrtInvalidName.GetName()) })

validationErrors = append(validationErrors,
Comment thread
gamado marked this conversation as resolved.
fmt.Sprintf("MDRT with name %q was unexpectedly created",
mdrparams.MDRTInvalidTestName))
} else if !k8serrors.IsInvalid(err) {
validationErrors = append(validationErrors,
fmt.Sprintf("MDRT with name %q: expected Invalid error, got: %v",
mdrparams.MDRTInvalidTestName, err))
}

if len(validationErrors) > 0 {
Fail("MDRT negative validation failures:\n- " +
strings.Join(validationErrors, "\n- "))
}
})
})

func verifyMDRControllerRunning() error {
listOptions := metav1.ListOptions{
LabelSelector: mdrparams.OperatorControllerPodLabelSelector,
}

allPods, listErr := pod.List(APIClient, medik8sparams.OperatorNs, listOptions)
if listErr != nil {
return fmt.Errorf("failed to list MDR pods: %w", listErr)
}

mdrPods := helpers.FilterPodsByDeployment(allPods, mdrparams.OperatorDeploymentName)
runningCount := int32(len(helpers.FilterRunningPods(mdrPods)))

if runningCount != mdrparams.ExpectedReplicas {
return fmt.Errorf("expected %d running MDR pod(s), found %d",
mdrparams.ExpectedReplicas, runningCount)
}

return nil
}