@@ -1613,6 +1613,120 @@ var _ = Describe("Dataplane NodeSet Test", func() {
16131613 })
16141614 })
16151615
1616+ When ("A scoped deployment does not update SecretHashes on NodeSet" , func () {
1617+ var dataSourceSecretName types.NamespacedName
1618+ var hashTestServiceName types.NamespacedName
1619+
1620+ BeforeEach (func () {
1621+ dataSourceSecretName = types.NamespacedName {
1622+ Name : "test-datasource-secret" ,
1623+ Namespace : namespace ,
1624+ }
1625+ hashTestServiceName = types.NamespacedName {
1626+ Name : "hash-test-service" ,
1627+ Namespace : namespace ,
1628+ }
1629+
1630+ nodeSetSpec := DefaultDataPlaneNodeSetSpec ("edpm-compute" )
1631+ nodeSetSpec ["preProvisioned" ] = true
1632+ nodeSetSpec ["services" ] = []string {"hash-test-service" }
1633+
1634+ th .CreateSecret (dataSourceSecretName , map [string ][]byte {
1635+ "transport_url" : []byte ("rabbit://nova:old-password@rabbitmq:5672/" ),
1636+ })
1637+
1638+ DeferCleanup (th .DeleteInstance , CreateDataPlaneServiceFromSpec (hashTestServiceName , map [string ]interface {}{
1639+ "playbook" : "test" ,
1640+ "dataSources" : []map [string ]interface {}{
1641+ {
1642+ "secretRef" : map [string ]interface {}{
1643+ "name" : dataSourceSecretName .Name ,
1644+ },
1645+ },
1646+ },
1647+ }))
1648+
1649+ DeferCleanup (th .DeleteInstance , CreateNetConfig (dataplaneNetConfigName , DefaultNetConfigSpec ()))
1650+ DeferCleanup (th .DeleteInstance , CreateDNSMasq (dnsMasqName , DefaultDNSMasqSpec ()))
1651+ DeferCleanup (th .DeleteInstance , CreateDataplaneNodeSet (dataplaneNodeSetName , nodeSetSpec ))
1652+ DeferCleanup (th .DeleteInstance , CreateDataplaneDeployment (dataplaneDeploymentName , DefaultDataPlaneDeploymentSpec ()))
1653+ CreateSSHSecret (dataplaneSSHSecretName )
1654+ CreateCABundleSecret (caBundleSecretName )
1655+ SimulateDNSMasqComplete (dnsMasqName )
1656+ SimulateIPSetComplete (dataplaneNodeName )
1657+ SimulateDNSDataComplete (dataplaneNodeSetName )
1658+ })
1659+
1660+ It ("Should preserve SecretHashes when secret changes and scoped deployment completes" , func () {
1661+ // Complete the full deployment
1662+ Eventually (func (g Gomega ) {
1663+ ansibleeeName := types.NamespacedName {
1664+ Name : "hash-test-service-" + dataplaneDeploymentName .Name + "-" + dataplaneNodeSetName .Name ,
1665+ Namespace : namespace ,
1666+ }
1667+ ansibleEE := GetAnsibleee (ansibleeeName )
1668+ ansibleEE .Status .Succeeded = 1
1669+ g .Expect (th .K8sClient .Status ().Update (th .Ctx , ansibleEE )).To (Succeed ())
1670+ }, th .Timeout , th .Interval ).Should (Succeed ())
1671+
1672+ // Wait for SecretHashes to be populated on the nodeset
1673+ Eventually (func (g Gomega ) {
1674+ instance := GetDataplaneNodeSet (dataplaneNodeSetName )
1675+ g .Expect (instance .Status .SecretHashes ).ShouldNot (BeEmpty ())
1676+ g .Expect (instance .Status .SecretHashes ).Should (HaveKey (dataSourceSecretName .Name ))
1677+ }, th .Timeout , th .Interval ).Should (Succeed ())
1678+
1679+ // Capture the SecretHashes after full deployment
1680+ instance := GetDataplaneNodeSet (dataplaneNodeSetName )
1681+ savedSecretHashes := make (map [string ]string )
1682+ for k , v := range instance .Status .SecretHashes {
1683+ savedSecretHashes [k ] = v
1684+ }
1685+
1686+ // Modify the secret to simulate credential rotation
1687+ Eventually (func (g Gomega ) {
1688+ secret := & corev1.Secret {}
1689+ g .Expect (th .K8sClient .Get (th .Ctx , dataSourceSecretName , secret )).To (Succeed ())
1690+ secret .Data ["transport_url" ] = []byte ("rabbit://nova:new-rotated-password@rabbitmq:5672/" )
1691+ g .Expect (th .K8sClient .Update (th .Ctx , secret )).To (Succeed ())
1692+ }, th .Timeout , th .Interval ).Should (Succeed ())
1693+
1694+ // Create a scoped deployment with ServicesOverride
1695+ scopedDeploymentName := types.NamespacedName {
1696+ Name : "scoped-deployment" ,
1697+ Namespace : namespace ,
1698+ }
1699+ scopedSpec := DefaultDataPlaneDeploymentSpec ()
1700+ scopedSpec ["servicesOverride" ] = []string {"hash-test-service" }
1701+ DeferCleanup (th .DeleteInstance , CreateDataplaneDeployment (scopedDeploymentName , scopedSpec ))
1702+
1703+ // Complete the scoped deployment
1704+ Eventually (func (g Gomega ) {
1705+ ansibleeeName := types.NamespacedName {
1706+ Name : "hash-test-service-" + scopedDeploymentName .Name + "-" + dataplaneNodeSetName .Name ,
1707+ Namespace : namespace ,
1708+ }
1709+ ansibleEE := GetAnsibleee (ansibleeeName )
1710+ ansibleEE .Status .Succeeded = 1
1711+ g .Expect (th .K8sClient .Status ().Update (th .Ctx , ansibleEE )).To (Succeed ())
1712+ }, th .Timeout , th .Interval ).Should (Succeed ())
1713+
1714+ // Wait for scoped deployment to be processed
1715+ Eventually (func (g Gomega ) {
1716+ instance := GetDataplaneNodeSet (dataplaneNodeSetName )
1717+ g .Expect (instance .Status .DeploymentStatuses ).Should (HaveKey (scopedDeploymentName .Name ))
1718+ }, th .Timeout , th .Interval ).Should (Succeed ())
1719+
1720+ // servicesOverride deployments merge SecretHashes (all nodes
1721+ // were touched), so hashes should be updated.
1722+ Eventually (func (g Gomega ) {
1723+ instance := GetDataplaneNodeSet (dataplaneNodeSetName )
1724+ g .Expect (instance .Status .SecretHashes ).ShouldNot (BeEmpty ())
1725+ g .Expect (instance .Status .SecretHashes ).ShouldNot (Equal (savedSecretHashes ))
1726+ }, th .Timeout , th .Interval ).Should (Succeed ())
1727+ })
1728+ })
1729+
16161730 When ("Running deployments exist with completed deployment" , func () {
16171731 BeforeEach (func () {
16181732 nodeSetSpec := DefaultDataPlaneNodeSetSpec ("edpm-compute" )
0 commit comments