Skip to content

Commit d666db5

Browse files
committed
Don't reconcile a failed deployment
Don't reconcile a failed deployment after the required number of retries if there are changes in the nodeset or watched resources. Signed-off-by: rabi <[email protected]>
1 parent 61d1832 commit d666db5

File tree

2 files changed

+133
-0
lines changed

2 files changed

+133
-0
lines changed

controllers/dataplane/openstackdataplanedeployment_controller.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,20 @@ func (r *OpenStackDataPlaneDeploymentReconciler) Reconcile(ctx context.Context,
109109
return ctrl.Result{}, nil
110110
}
111111

112+
// If the deployment has failed with backoff limit exceeded, do not reconcile
113+
// even if nodesets or other watched resources change. The deployment is in a
114+
// terminal failure state and should not be retried.
115+
if instance.Status.Conditions != nil {
116+
deploymentCondition := instance.Status.Conditions.Get(condition.DeploymentReadyCondition)
117+
if deploymentCondition != nil &&
118+
deploymentCondition.Severity == condition.SeverityError &&
119+
deploymentCondition.Reason == condition.JobReasonBackoffLimitExceeded {
120+
Log.Info("Deployment has failed with backoff limit exceeded, skipping reconciliation",
121+
"deployment", instance.Name)
122+
return ctrl.Result{}, nil
123+
}
124+
}
125+
112126
// initialize status if Conditions is nil, but do not reset if it already
113127
// exists
114128
isNewInstance := instance.Status.Conditions == nil
@@ -344,6 +358,9 @@ func (r *OpenStackDataPlaneDeploymentReconciler) Reconcile(ctx context.Context,
344358
severity,
345359
condition.DeploymentReadyErrorMessage,
346360
deploymentErrMsg)
361+
if backoffLimitReached {
362+
return ctrl.Result{}, nil
363+
}
347364
return ctrl.Result{}, fmt.Errorf("%s", deploymentErrMsg)
348365
}
349366

tests/functional/dataplane/openstackdataplanedeployment_controller_test.go

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1793,4 +1793,120 @@ var _ = Describe("Dataplane Deployment Test", func() {
17931793

17941794
})
17951795

1796+
When("A dataplaneDeployment fails with backoff limit exceeded", func() {
1797+
BeforeEach(func() {
1798+
CreateSSHSecret(dataplaneSSHSecretName)
1799+
CreateCABundleSecret(caBundleSecretName)
1800+
DeferCleanup(th.DeleteInstance, th.CreateSecret(neutronOvnMetadataSecretName, map[string][]byte{
1801+
"fake_keys": []byte("blih"),
1802+
}))
1803+
DeferCleanup(th.DeleteInstance, th.CreateSecret(novaNeutronMetadataSecretName, map[string][]byte{
1804+
"fake_keys": []byte("blih"),
1805+
}))
1806+
DeferCleanup(th.DeleteInstance, th.CreateSecret(novaCellComputeConfigSecretName, map[string][]byte{
1807+
"fake_keys": []byte("blih"),
1808+
}))
1809+
DeferCleanup(th.DeleteInstance, th.CreateSecret(novaMigrationSSHKey, map[string][]byte{
1810+
"ssh-privatekey": []byte("fake-ssh-private-key"),
1811+
"ssh-publickey": []byte("fake-ssh-public-key"),
1812+
}))
1813+
DeferCleanup(th.DeleteInstance, th.CreateSecret(ceilometerConfigSecretName, map[string][]byte{
1814+
"fake_keys": []byte("blih"),
1815+
}))
1816+
CreateDataplaneService(dataplaneServiceName, false)
1817+
CreateDataplaneService(dataplaneGlobalServiceName, true)
1818+
1819+
DeferCleanup(th.DeleteService, dataplaneServiceName)
1820+
DeferCleanup(th.DeleteService, dataplaneGlobalServiceName)
1821+
DeferCleanup(th.DeleteInstance, CreateNetConfig(dataplaneNetConfigName, DefaultNetConfigSpec()))
1822+
DeferCleanup(th.DeleteInstance, CreateDNSMasq(dnsMasqName, DefaultDNSMasqSpec()))
1823+
SimulateDNSMasqComplete(dnsMasqName)
1824+
DeferCleanup(th.DeleteInstance, CreateDataplaneNodeSet(dataplaneNodeSetName, DefaultDataPlaneNodeSetSpec(dataplaneNodeSetName.Name)))
1825+
SimulateIPSetComplete(dataplaneNodeName)
1826+
SimulateDNSDataComplete(dataplaneNodeSetName)
1827+
DeferCleanup(th.DeleteInstance, CreateDataplaneDeployment(dataplaneDeploymentName, DefaultDataPlaneDeploymentSpec()))
1828+
})
1829+
1830+
It("should not reconcile after failure", func() {
1831+
// Directly set the deployment status to backoff limit exceeded
1832+
// This simulates a deployment that has failed after retries
1833+
Eventually(func(g Gomega) {
1834+
deployment := GetDataplaneDeployment(dataplaneDeploymentName)
1835+
1836+
// Initialize conditions if not present
1837+
if deployment.Status.Conditions == nil {
1838+
deployment.Status.Conditions = condition.Conditions{}
1839+
}
1840+
1841+
// Initialize NodeSetConditions if not present
1842+
if deployment.Status.NodeSetConditions == nil {
1843+
deployment.Status.NodeSetConditions = make(map[string]condition.Conditions)
1844+
}
1845+
1846+
// Set the deployment condition to backoff limit exceeded
1847+
deployment.Status.Conditions.MarkFalse(
1848+
condition.DeploymentReadyCondition,
1849+
condition.JobReasonBackoffLimitExceeded,
1850+
condition.SeverityError,
1851+
condition.DeploymentReadyErrorMessage,
1852+
"Simulated backoff limit exceeded for testing")
1853+
1854+
deployment.Status.Deployed = false
1855+
deployment.Status.ObservedGeneration = deployment.Generation
1856+
1857+
g.Expect(th.K8sClient.Status().Update(th.Ctx, deployment)).To(Succeed())
1858+
}, th.Timeout, th.Interval).Should(Succeed())
1859+
1860+
// Verify the deployment has the correct failure condition
1861+
deployment := GetDataplaneDeployment(dataplaneDeploymentName)
1862+
deploymentCondition := deployment.Status.Conditions.Get(condition.DeploymentReadyCondition)
1863+
Expect(deploymentCondition).ToNot(BeNil())
1864+
Expect(string(deploymentCondition.Severity)).To(Equal(string(condition.SeverityError)))
1865+
Expect(string(deploymentCondition.Reason)).To(Equal(string(condition.JobReasonBackoffLimitExceeded)))
1866+
1867+
// Verify the status is not deployed
1868+
deployment = GetDataplaneDeployment(dataplaneDeploymentName)
1869+
Expect(deployment.Status.Deployed).To(BeFalse())
1870+
1871+
// Store the current ObservedGeneration and condition timestamp
1872+
originalGeneration := deployment.Status.ObservedGeneration
1873+
deploymentCondition = deployment.Status.Conditions.Get(condition.DeploymentReadyCondition)
1874+
originalTransitionTime := deploymentCondition.LastTransitionTime
1875+
1876+
// Trigger reconciliation by updating the deployment annotation
1877+
// This should normally trigger reconciliation, but should be blocked by our early return check
1878+
Eventually(func(g Gomega) {
1879+
deployment := GetDataplaneDeployment(dataplaneDeploymentName)
1880+
if deployment.Annotations == nil {
1881+
deployment.Annotations = make(map[string]string)
1882+
}
1883+
deployment.Annotations["test-annotation"] = "trigger-reconcile"
1884+
g.Expect(th.K8sClient.Update(th.Ctx, deployment)).To(Succeed())
1885+
}, th.Timeout, th.Interval).Should(Succeed())
1886+
1887+
// Wait a bit to ensure reconciliation would have been triggered if it was going to
1888+
th.Logger.Info("Waiting to ensure no reconciliation occurs...")
1889+
1890+
// Verify the deployment status hasn't changed after annotation update
1891+
// The ObservedGeneration and condition LastTransitionTime should remain the same
1892+
Consistently(func(g Gomega) {
1893+
deployment := GetDataplaneDeployment(dataplaneDeploymentName)
1894+
g.Expect(deployment.Status.ObservedGeneration).To(Equal(originalGeneration))
1895+
deploymentCondition := deployment.Status.Conditions.Get(condition.DeploymentReadyCondition)
1896+
g.Expect(deploymentCondition).ToNot(BeNil())
1897+
g.Expect(string(deploymentCondition.Severity)).To(Equal(string(condition.SeverityError)))
1898+
g.Expect(string(deploymentCondition.Reason)).To(Equal(string(condition.JobReasonBackoffLimitExceeded)))
1899+
g.Expect(deploymentCondition.LastTransitionTime).To(Equal(originalTransitionTime))
1900+
g.Expect(deployment.Status.Deployed).To(BeFalse())
1901+
}, "5s", "1s").Should(Succeed())
1902+
1903+
th.ExpectCondition(
1904+
dataplaneDeploymentName,
1905+
ConditionGetterFunc(DataplaneDeploymentConditionGetter),
1906+
condition.DeploymentReadyCondition,
1907+
corev1.ConditionFalse,
1908+
)
1909+
})
1910+
})
1911+
17961912
})

0 commit comments

Comments
 (0)