Skip to content

Commit 3beaa2c

Browse files
Fix TestKubeletConfigRemediation issue
1 parent 30241be commit 3beaa2c

File tree

3 files changed

+140
-24
lines changed

3 files changed

+140
-24
lines changed

tests/e2e/framework/common.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -894,6 +894,88 @@ func (f *Framework) restoreNodeLabelsForPool(n string) error {
894894
return nil
895895
}
896896

897+
// WaitForMachineConfigPoolUpdated waits for a MachineConfigPool to complete an update cycle.
898+
// It first waits for the Updated condition to become False (indicating update/reboot started),
899+
// then waits for it to become True again (indicating update/reboot completed).
900+
func (f *Framework) WaitForMachineConfigPoolUpdated(poolName string) error {
901+
if f.Platform == "rosa" {
902+
log.Printf("Bypassing MachineConfigPool update check on %s", f.Platform)
903+
return nil
904+
}
905+
906+
// First, get the initial state
907+
pool := mcfgv1.MachineConfigPool{}
908+
err := f.Client.Get(context.TODO(), types.NamespacedName{Name: poolName}, &pool)
909+
if err != nil {
910+
return fmt.Errorf("failed to find Machine Config Pool %s: %w", poolName, err)
911+
}
912+
913+
// Check initial state of MachineConfigPoolUpdated condition
914+
initialUpdated := false
915+
for _, c := range pool.Status.Conditions {
916+
if c.Type == mcfgv1.MachineConfigPoolUpdated {
917+
initialUpdated = (c.Status == core.ConditionTrue)
918+
break
919+
}
920+
}
921+
922+
// If the pool is already updated (True), wait for it to start updating (False)
923+
if initialUpdated {
924+
log.Printf("Machine Config Pool %s is currently updated. Waiting for update to start...\n", poolName)
925+
err = wait.PollImmediate(machineOperationRetryInterval, machineOperationTimeout, func() (bool, error) {
926+
pool := mcfgv1.MachineConfigPool{}
927+
err := f.Client.Get(context.TODO(), types.NamespacedName{Name: poolName}, &pool)
928+
if err != nil {
929+
log.Printf("failed to find Machine Config Pool %s\n", poolName)
930+
return false, err
931+
}
932+
933+
for _, c := range pool.Status.Conditions {
934+
if c.Type == mcfgv1.MachineConfigPoolUpdated {
935+
if c.Status == core.ConditionFalse {
936+
log.Printf("Machine Config Pool %s update started (Updated=False)\n", poolName)
937+
return true, nil
938+
}
939+
break
940+
}
941+
}
942+
943+
log.Printf("Machine Config Pool %s still updated, waiting for update to start...\n", poolName)
944+
return false, nil
945+
})
946+
if err != nil {
947+
return fmt.Errorf("failed waiting for Machine Config Pool %s to start updating: %w", poolName, err)
948+
}
949+
} else {
950+
log.Printf("Machine Config Pool %s is already updating (Updated=False). Waiting for update to complete...\n", poolName)
951+
}
952+
953+
// Now wait for the update to complete (Updated=True)
954+
err = wait.PollImmediate(machineOperationRetryInterval, machineOperationTimeout, func() (bool, error) {
955+
pool := mcfgv1.MachineConfigPool{}
956+
err := f.Client.Get(context.TODO(), types.NamespacedName{Name: poolName}, &pool)
957+
if err != nil {
958+
log.Printf("failed to find Machine Config Pool %s\n", poolName)
959+
return false, err
960+
}
961+
962+
for _, c := range pool.Status.Conditions {
963+
if c.Type == mcfgv1.MachineConfigPoolUpdated && c.Status == core.ConditionTrue {
964+
return true, nil
965+
}
966+
}
967+
968+
log.Printf("Machine Config Pool %s has not finished updating yet... retrying\n", poolName)
969+
return false, nil
970+
})
971+
if err != nil {
972+
return fmt.Errorf("failed waiting for Machine Config Pool %s to be updated: %w", poolName, err)
973+
}
974+
975+
log.Printf("Machine Config Pool %s is updated\n", poolName)
976+
return nil
977+
}
978+
897979
func (f *Framework) getNodesForPool(p *mcfgv1.MachineConfigPool) (core.NodeList, error) {
898980
var nodeList core.NodeList
899981
opts := &dynclient.ListOptions{
@@ -1048,6 +1130,25 @@ func (f *Framework) WaitForScanSettingBindingStatus(namespace, name string, targ
10481130
return nil
10491131
}
10501132

1133+
func (f *Framework) WaitForComplianceSuiteDeletion(name, namespace string) error {
1134+
suiteKey := types.NamespacedName{Name: name, Namespace: namespace}
1135+
suite := &compv1alpha1.ComplianceSuite{}
1136+
err := wait.Poll(RetryInterval, 30*time.Second, func() (bool, error) {
1137+
err := f.Client.Get(context.TODO(), suiteKey, suite)
1138+
if apierrors.IsNotFound(err) {
1139+
return true, nil
1140+
}
1141+
if err != nil {
1142+
return false, err
1143+
}
1144+
return false, nil
1145+
})
1146+
if err != nil {
1147+
return fmt.Errorf("ComplianceSuite %s may not have been fully cleaned up: %w", name, err)
1148+
}
1149+
return nil
1150+
}
1151+
10511152
func (f *Framework) AssertProfileInRuleAnnotation(r *compv1alpha1.Rule, expectedProfileId string) bool {
10521153
if r.Annotations == nil {
10531154
return false

tests/e2e/framework/main_entry.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,11 @@ func (f *Framework) TearDown() error {
168168
if err != nil {
169169
return err
170170
}
171+
// Wait for worker pool to be updated after nodes are unlabeled
172+
err = f.WaitForMachineConfigPoolUpdated(workerPoolName)
173+
if err != nil {
174+
return err
175+
}
171176
err = f.cleanUpMachineConfigPool("e2e")
172177
if err != nil {
173178
return err

tests/e2e/serial/main_test.go

Lines changed: 34 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1569,47 +1569,32 @@ func TestVariableTemplate(t *testing.T) {
15691569

15701570
func TestKubeletConfigRemediation(t *testing.T) {
15711571
f := framework.Global
1572-
var baselineImage = fmt.Sprintf("%s:%s", brokenContentImagePath, "new_kubeletconfig")
1573-
const requiredRule = "kubelet-enable-streaming-connections"
1574-
pbName := framework.GetObjNameFromTest(t)
1575-
prefixName := func(profName, ruleBaseName string) string { return profName + "-" + ruleBaseName }
1576-
1577-
ocpPb, err := f.CreateProfileBundle(pbName, baselineImage, framework.OcpContentFile)
1578-
if err != nil {
1579-
t.Fatal(err)
1580-
}
1581-
defer f.Client.Delete(context.TODO(), ocpPb)
1582-
if err := f.WaitForProfileBundleStatus(pbName, compv1alpha1.DataStreamValid); err != nil {
1583-
t.Fatal(err)
1584-
}
1585-
1586-
// Check that if the rule we are going to test is there
1587-
requiredRuleName := prefixName(pbName, requiredRule)
1588-
requiredVersionRuleName := prefixName(pbName, "version-detect-in-ocp")
1589-
requiredVariableName := prefixName(pbName, "var-streaming-connection-timeouts")
1590-
suiteName := "kubelet-remediation-test-suite-node"
1572+
suiteName := framework.GetObjNameFromTest(t) + "-kubelet-remediation"
15911573

15921574
tp := &compv1alpha1.TailoredProfile{
15931575
ObjectMeta: metav1.ObjectMeta{
15941576
Name: suiteName,
15951577
Namespace: f.OperatorNamespace,
1578+
Annotations: map[string]string{
1579+
compv1alpha1.ProductTypeAnnotation: "Node",
1580+
},
15961581
},
15971582
Spec: compv1alpha1.TailoredProfileSpec{
15981583
Title: "kubelet-remediation-test-node",
15991584
Description: "A test tailored profile to test kubelet remediation",
16001585
EnableRules: []compv1alpha1.RuleReferenceSpec{
16011586
{
1602-
Name: requiredRuleName,
1587+
Name: "ocp4-kubelet-enable-streaming-connections",
16031588
Rationale: "To be tested",
16041589
},
16051590
{
1606-
Name: requiredVersionRuleName,
1591+
Name: "ocp4-version-detect-in-ocp",
16071592
Rationale: "To be tested",
16081593
},
16091594
},
16101595
SetValues: []compv1alpha1.VariableValueSpec{
16111596
{
1612-
Name: requiredVariableName,
1597+
Name: "ocp4-var-streaming-connection-timeouts",
16131598
Rationale: "Value to be set",
16141599
Value: "8h0m0s",
16151600
},
@@ -1641,11 +1626,21 @@ func TestKubeletConfigRemediation(t *testing.T) {
16411626
},
16421627
}
16431628

1644-
err = f.Client.Create(context.TODO(), ssb, nil)
1629+
err := f.Client.Create(context.TODO(), ssb, nil)
16451630
if err != nil {
16461631
t.Fatal(err)
16471632
}
1648-
defer f.Client.Delete(context.TODO(), ssb)
1633+
// Cleanup: Delete ScanSettingBinding first, then wait for ComplianceSuite to be cascade-deleted
1634+
// This ensures proper cleanup before the next test runs
1635+
defer func() {
1636+
if err := f.Client.Delete(context.TODO(), ssb); err != nil {
1637+
t.Logf("Failed to delete ScanSettingBinding: %v", err)
1638+
return
1639+
}
1640+
if err := f.WaitForComplianceSuiteDeletion(suiteName, f.OperatorNamespace); err != nil {
1641+
t.Logf("ComplianceSuite cleanup warning: %v", err)
1642+
}
1643+
}()
16491644

16501645
// Ensure that all the scans in the suite have finished and are marked as Done
16511646
err = f.WaitForSuiteScansStatus(f.OperatorNamespace, suiteName, compv1alpha1.PhaseDone, compv1alpha1.ResultNonCompliant)
@@ -1664,6 +1659,21 @@ func TestKubeletConfigRemediation(t *testing.T) {
16641659
t.Fatal(err)
16651660
}
16661661

1662+
// Cleanup: Unapply the remediation before test ends to prevent leaving nodes in a modified state
1663+
// This ensures the cluster is reset after the test completes
1664+
defer func() {
1665+
// Finally clean up by removing the remediation and waiting for the nodes to reboot one more time
1666+
err = f.UnApplyRemediationAndCheck(f.OperatorNamespace, remName, "worker")
1667+
if err != nil {
1668+
t.Fatal(err)
1669+
}
1670+
1671+
err = f.WaitForNodesToBeReady()
1672+
if err != nil {
1673+
t.Fatalf("failed waiting for nodes to reboot after unapplying MachineConfig: %s", err)
1674+
}
1675+
}()
1676+
16671677
err = f.ReRunScan(scanName, f.OperatorNamespace)
16681678
if err != nil {
16691679
t.Fatal(err)

0 commit comments

Comments
 (0)