@@ -19,6 +19,7 @@ import (
1919 "encoding/json"
2020 "fmt"
2121 "os"
22+ "time"
2223
2324 . "github.com/onsi/ginkgo/v2" //revive:disable:dot-imports
2425 . "github.com/onsi/gomega" //revive:disable:dot-imports
@@ -1613,6 +1614,119 @@ var _ = Describe("Dataplane NodeSet Test", func() {
16131614 })
16141615 })
16151616
1617+ When ("A scoped deployment does not update SecretHashes on NodeSet" , func () {
1618+ var dataSourceSecretName types.NamespacedName
1619+ var hashTestServiceName types.NamespacedName
1620+
1621+ BeforeEach (func () {
1622+ dataSourceSecretName = types.NamespacedName {
1623+ Name : "test-datasource-secret" ,
1624+ Namespace : namespace ,
1625+ }
1626+ hashTestServiceName = types.NamespacedName {
1627+ Name : "hash-test-service" ,
1628+ Namespace : namespace ,
1629+ }
1630+
1631+ nodeSetSpec := DefaultDataPlaneNodeSetSpec ("edpm-compute" )
1632+ nodeSetSpec ["preProvisioned" ] = true
1633+ nodeSetSpec ["services" ] = []string {"hash-test-service" }
1634+
1635+ th .CreateSecret (dataSourceSecretName , map [string ][]byte {
1636+ "transport_url" : []byte ("rabbit://nova:old-password@rabbitmq:5672/" ),
1637+ })
1638+
1639+ DeferCleanup (th .DeleteInstance , CreateDataPlaneServiceFromSpec (hashTestServiceName , map [string ]interface {}{
1640+ "playbook" : "test" ,
1641+ "dataSources" : []map [string ]interface {}{
1642+ {
1643+ "secretRef" : map [string ]interface {}{
1644+ "name" : dataSourceSecretName .Name ,
1645+ },
1646+ },
1647+ },
1648+ }))
1649+
1650+ DeferCleanup (th .DeleteInstance , CreateNetConfig (dataplaneNetConfigName , DefaultNetConfigSpec ()))
1651+ DeferCleanup (th .DeleteInstance , CreateDNSMasq (dnsMasqName , DefaultDNSMasqSpec ()))
1652+ DeferCleanup (th .DeleteInstance , CreateDataplaneNodeSet (dataplaneNodeSetName , nodeSetSpec ))
1653+ DeferCleanup (th .DeleteInstance , CreateDataplaneDeployment (dataplaneDeploymentName , DefaultDataPlaneDeploymentSpec ()))
1654+ CreateSSHSecret (dataplaneSSHSecretName )
1655+ CreateCABundleSecret (caBundleSecretName )
1656+ SimulateDNSMasqComplete (dnsMasqName )
1657+ SimulateIPSetComplete (dataplaneNodeName )
1658+ SimulateDNSDataComplete (dataplaneNodeSetName )
1659+ })
1660+
1661+ It ("Should preserve SecretHashes when secret changes and scoped deployment completes" , func () {
1662+ // Complete the full deployment
1663+ Eventually (func (g Gomega ) {
1664+ ansibleeeName := types.NamespacedName {
1665+ Name : "hash-test-service-" + dataplaneDeploymentName .Name + "-" + dataplaneNodeSetName .Name ,
1666+ Namespace : namespace ,
1667+ }
1668+ ansibleEE := GetAnsibleee (ansibleeeName )
1669+ ansibleEE .Status .Succeeded = 1
1670+ g .Expect (th .K8sClient .Status ().Update (th .Ctx , ansibleEE )).To (Succeed ())
1671+ }, th .Timeout , th .Interval ).Should (Succeed ())
1672+
1673+ // Wait for SecretHashes to be populated on the nodeset
1674+ Eventually (func (g Gomega ) {
1675+ instance := GetDataplaneNodeSet (dataplaneNodeSetName )
1676+ g .Expect (instance .Status .SecretHashes ).ShouldNot (BeEmpty ())
1677+ g .Expect (instance .Status .SecretHashes ).Should (HaveKey (dataSourceSecretName .Name ))
1678+ }, th .Timeout , th .Interval ).Should (Succeed ())
1679+
1680+ // Capture the SecretHashes after full deployment
1681+ instance := GetDataplaneNodeSet (dataplaneNodeSetName )
1682+ savedSecretHashes := make (map [string ]string )
1683+ for k , v := range instance .Status .SecretHashes {
1684+ savedSecretHashes [k ] = v
1685+ }
1686+
1687+ // Modify the secret to simulate credential rotation
1688+ Eventually (func (g Gomega ) {
1689+ secret := & corev1.Secret {}
1690+ g .Expect (th .K8sClient .Get (th .Ctx , dataSourceSecretName , secret )).To (Succeed ())
1691+ secret .Data ["transport_url" ] = []byte ("rabbit://nova:new-rotated-password@rabbitmq:5672/" )
1692+ g .Expect (th .K8sClient .Update (th .Ctx , secret )).To (Succeed ())
1693+ }, th .Timeout , th .Interval ).Should (Succeed ())
1694+
1695+ // Create a scoped deployment with ServicesOverride
1696+ scopedDeploymentName := types.NamespacedName {
1697+ Name : "scoped-deployment" ,
1698+ Namespace : namespace ,
1699+ }
1700+ scopedSpec := DefaultDataPlaneDeploymentSpec ()
1701+ scopedSpec ["servicesOverride" ] = []string {"hash-test-service" }
1702+ DeferCleanup (th .DeleteInstance , CreateDataplaneDeployment (scopedDeploymentName , scopedSpec ))
1703+
1704+ // Complete the scoped deployment
1705+ Eventually (func (g Gomega ) {
1706+ ansibleeeName := types.NamespacedName {
1707+ Name : "hash-test-service-" + scopedDeploymentName .Name + "-" + dataplaneNodeSetName .Name ,
1708+ Namespace : namespace ,
1709+ }
1710+ ansibleEE := GetAnsibleee (ansibleeeName )
1711+ ansibleEE .Status .Succeeded = 1
1712+ g .Expect (th .K8sClient .Status ().Update (th .Ctx , ansibleEE )).To (Succeed ())
1713+ }, th .Timeout , th .Interval ).Should (Succeed ())
1714+
1715+ // Wait for scoped deployment to be processed
1716+ Eventually (func (g Gomega ) {
1717+ instance := GetDataplaneNodeSet (dataplaneNodeSetName )
1718+ g .Expect (instance .Status .DeploymentStatuses ).Should (HaveKey (scopedDeploymentName .Name ))
1719+ }, th .Timeout , th .Interval ).Should (Succeed ())
1720+
1721+ // SecretHashes should still have the old hashes from the full deployment,
1722+ // not the new hashes from the scoped deployment
1723+ Consistently (func (g Gomega ) {
1724+ instance := GetDataplaneNodeSet (dataplaneNodeSetName )
1725+ g .Expect (instance .Status .SecretHashes ).Should (Equal (savedSecretHashes ))
1726+ }, 5 * time .Second , 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