From c795a9eee4f73a98f11ae05164ac149aeb9faac6 Mon Sep 17 00:00:00 2001 From: Prachiti Talgulkar Date: Thu, 23 Jul 2026 12:32:07 +0530 Subject: [PATCH] Add TC 88940: Apply password only if changes exist --- test/extended-priv/mco_password.go | 102 +++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/test/extended-priv/mco_password.go b/test/extended-priv/mco_password.go index 8e0849fcea..e4a19da3a0 100644 --- a/test/extended-priv/mco_password.go +++ b/test/extended-priv/mco_password.go @@ -424,6 +424,108 @@ var _ = g.Describe("[sig-mco][Suite:openshift/machine-config-operator/disruptive checkAuthorizedKeyInNode(mcp.GetCoreOsNodesOrFail()[0], append(initialKeys, key)) logger.Infof("OK!\n") }) + + // https://issues.redhat.com/browse/OCPBUGS-83830 + g.It("[PolarionID:88940][OTP] Apply password only if changes exist [Disruptive]", func() { + var ( + mcName = "tc-88940-core-password-hash-change" + silentOC = oc.AsAdmin() + + password = exutil.GetRandomString() + passwordHash = OrFail[string](getHashPasswd(password)) + updatedPassword = password + "-2" + updatedPasswdHash = OrFail[string](getHashPasswd(updatedPassword)) + + _, sshPubKey = GenerateSSHKeyPairOrFail() + + pwdConfiguredLogMsg = "Password has been configured" + expectedReboot = false + ) + silentOC.NotShowInfo() + sshPubKey = strings.TrimSpace(sshPubKey) + + node := mcp.GetSortedNodesOrFail()[0] + mcc := NewController(oc.AsAdmin()).IgnoreLogsBeforeNowOrFail() + startTime := node.GetDateOrFail() + o.Expect(node.IgnoreEventsBeforeNow()).NotTo(o.HaveOccurred(), + "Error getting the latest event in node %s", node.GetName()) + + exutil.By("Apply MC with passwordHash for core user") + pwdUser := ign32PaswdUser{Name: user, PasswordHash: passwordHash} + mc := NewMachineConfig(silentOC, mcName, mcp.GetName()) + mc.parameters = []string{fmt.Sprintf(`PWDUSERS=[%s]`, MarshalOrFail(pwdUser))} + mc.skipWaitForMcp = true + + defer mc.DeleteWithWait() + mc.create() + mcp.waitForComplete() + logger.Infof("OK!\n") + + exutil.By("Check MCD logs to verify 'Password has been configured' is logged") + mcdLogs, err := node.GetMCDaemonLogs("") + o.Expect(err).NotTo(o.HaveOccurred(), "Error getting MCD logs for node %s", node.GetName()) + initialPasswordCount := strings.Count(mcdLogs, pwdConfiguredLogMsg) + o.Expect(initialPasswordCount).To(o.BeNumerically(">", 0), + "Expected '%s' in MCD logs after initial password application on node %s", pwdConfiguredLogMsg, node.GetName()) + logger.Infof("OK!\n") + + exutil.By("Check that drain and reboot are skipped for password-only change") + checkDrainAction(expectedReboot, node, mcc) + checkRebootAction(expectedReboot, node, startTime) + logger.Infof("OK!\n") + + exutil.By("Update the passwordHash in MC") + patchErr := mc.Patch("json", + fmt.Sprintf(`[{ "op": "replace", "path": "/spec/config/passwd/users/0/passwordHash", "value": "%s"}]`, updatedPasswdHash)) + o.Expect(patchErr).NotTo(o.HaveOccurred(), + "Error patching mc %s to update the 'core' user password", mc.GetName()) + mcp.waitForComplete() + logger.Infof("OK!\n") + + exutil.By("Check MCD logs to verify 'Password has been configured' is logged again after hash change") + mcdLogs, err = node.GetMCDaemonLogs("") + o.Expect(err).NotTo(o.HaveOccurred(), "Error getting MCD logs for node %s", node.GetName()) + updatedPasswordCount := strings.Count(mcdLogs, pwdConfiguredLogMsg) + o.Expect(updatedPasswordCount).To(o.BeNumerically(">", initialPasswordCount), + "Expected a new '%s' log entry after password hash change on node %s", pwdConfiguredLogMsg, node.GetName()) + logger.Infof("OK!\n") + + exutil.By("Get currently configured authorized keys before adding new SSH key") + currentMc, err := mcp.GetConfiguredMachineConfig() + o.Expect(err).NotTo(o.HaveOccurred(), "Error getting the current configuration for %s pool", mcp.GetName()) + initialKeys, err := currentMc.GetAuthorizedKeysByUserAsList("core") + o.Expect(err).NotTo(o.HaveOccurred(), "Error getting the current authorizedkeys for user 'core' in %s pool", mcp.GetName()) + logger.Infof("OK!\n") + + exutil.By("Add SSH key to MC without changing the password hash") + pwdUserWithSSH := ign32PaswdUser{Name: user, PasswordHash: updatedPasswdHash, SSHAuthorizedKeys: []string{sshPubKey}} + patchErr = mc.Patch("json", + fmt.Sprintf(`[{ "op": "replace", "path": "/spec/config/passwd/users", "value": [%s]}]`, MarshalOrFail(pwdUserWithSSH))) + o.Expect(patchErr).NotTo(o.HaveOccurred(), + "Error patching mc %s to add SSH key", mc.GetName()) + mcp.waitForComplete() + logger.Infof("OK!\n") + + exutil.By("Verify that the SSH key was correctly applied to the node") + checkAuthorizedKeyInNode(node, append(initialKeys, sshPubKey)) + logger.Infof("OK!\n") + + exutil.By("Check MCD logs to verify 'Password has been configured' is NOT logged for SSH-only change") + mcdLogs, err = node.GetMCDaemonLogs("") + o.Expect(err).NotTo(o.HaveOccurred(), "Error getting MCD logs for node %s", node.GetName()) + finalPasswordCount := strings.Count(mcdLogs, pwdConfiguredLogMsg) + o.Expect(finalPasswordCount).To(o.Equal(updatedPasswordCount), + "'%s' should NOT appear in MCD logs when only SSH keys are added (no password hash change) on node %s", + pwdConfiguredLogMsg, node.GetName()) + logger.Infof("OK!\n") + + exutil.By("Check events to make sure that drain and reboot events were not triggered") + nodeEvents, eErr := node.GetEvents() + o.Expect(eErr).ShouldNot(o.HaveOccurred(), "Error getting events for node %s", node.GetName()) + o.Expect(nodeEvents).NotTo(HaveEventsSequence("Drain"), "Error, a Drain event was triggered but it shouldn't") + o.Expect(nodeEvents).NotTo(HaveEventsSequence("Reboot"), "Error, a Reboot event was triggered but it shouldn't") + logger.Infof("OK!\n") + }) }) // getPasswdValidator returns the commands that need to be executed in an interactive expect shell to validate that a user can login