Skip to content

Commit a0c928c

Browse files
committed
refactor: adjust verification logic to support upcoming registration loop
1 parent f5bf187 commit a0c928c

1 file changed

Lines changed: 68 additions & 47 deletions

File tree

internal/controller/domain_controller.go

Lines changed: 68 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,36 @@ func (r *DomainReconciler) Reconcile(ctx context.Context, req mcreconcile.Reques
6363
return ctrl.Result{}, client.IgnoreNotFound(err)
6464
}
6565

66+
logger.Info("reconciling domain")
67+
defer logger.Info("reconcile complete")
68+
69+
origStatus := domain.Status.DeepCopy()
70+
71+
// Delegate all verification work (including timers/backoff)
72+
nextVerification := r.reconcileVerification(ctx, domain)
73+
74+
// Persist status if changed
75+
if !equality.Semantic.DeepEqual(*origStatus, domain.Status) {
76+
if err := cl.GetClient().Status().Update(ctx, domain); err != nil {
77+
return ctrl.Result{}, fmt.Errorf("failed updating domain status: %w", err)
78+
}
79+
}
80+
81+
// If we have a future attempt scheduled, requeue for that time
82+
if !nextVerification.IsZero() {
83+
if remaining := nextVerification.Sub(r.timeNow()).Truncate(time.Second); remaining > 0 {
84+
return ctrl.Result{RequeueAfter: remaining}, nil
85+
}
86+
}
87+
88+
return ctrl.Result{}, nil
89+
}
90+
91+
// reconcileVerification contains the verification logic.
92+
// It mutates domain.Status and returns the next verification attempt time (if any).
93+
func (r *DomainReconciler) reconcileVerification(ctx context.Context, domain *networkingv1alpha.Domain) time.Time {
94+
logger := log.FromContext(ctx)
95+
6696
domainStatus := domain.Status.DeepCopy()
6797

6898
verifiedCondition := conditionutil.FindStatusConditionOrDefault(domainStatus.Conditions, &metav1.Condition{
@@ -92,8 +122,7 @@ func (r *DomainReconciler) Reconcile(ctx context.Context, req mcreconcile.Reques
92122
})
93123
verifiedHTTPCondition.ObservedGeneration = domain.Generation
94124

95-
logger.Info("reconciling domain")
96-
defer logger.Info("reconcile complete")
125+
var nextAttempt time.Time
97126

98127
if verifiedCondition.Status != metav1.ConditionTrue {
99128
logger.Info("domain ownership has not been verified.")
@@ -120,69 +149,62 @@ func (r *DomainReconciler) Reconcile(ctx context.Context, req mcreconcile.Reques
120149
verifiedDNSCondition.Message = "Update your DNS provider with record defined in `status.verification.dnsRecord`."
121150
verifiedHTTPCondition.Message = "Update your HTTP server with token defined in `status.verification.httpToken`."
122151
} else {
152+
// If we're not yet due, short-circuit
123153
if remaining := domainStatus.Verification.NextVerificationAttempt.Sub(r.timeNow()).Truncate(time.Second); remaining > 0 {
124154
logger.Info("not attempting another validation until remaining time elapsed", "remaining", remaining)
125-
// This exists because we want to communicate timing expectations to
126-
// users, but by doing so we update the status information which triggers
127-
// a reconcile, and without this check it could lead to many sequential
128-
// updates to the resource.
129-
//
130-
// We can't have this logic in a predicate, because we couldn't schedule a
131-
// future reconcile.
132-
return ctrl.Result{RequeueAfter: remaining}, nil
133-
}
134-
135-
initialAttempt := verifiedCondition.LastTransitionTime.Time
136-
elapsed := r.timeNow().Sub(initialAttempt)
137-
logger.Info("time elapsed since last transition time", "duration", elapsed)
138-
139-
requeueAfter := wait.Jitter(
140-
r.Config.DomainVerification.GetRetryInterval(elapsed),
141-
r.Config.DomainVerification.RetryJitterMaxFactor,
142-
)
155+
nextAttempt = r.timeNow().Add(remaining)
156+
} else {
157+
// Compute next backoff based on elapsed since last transition time
158+
initialAttempt := verifiedCondition.LastTransitionTime.Time
159+
elapsed := r.timeNow().Sub(initialAttempt)
160+
logger.Info("time elapsed since last transition time", "duration", elapsed)
161+
162+
requeueAfter := wait.Jitter(
163+
r.Config.DomainVerification.GetRetryInterval(elapsed),
164+
r.Config.DomainVerification.RetryJitterMaxFactor,
165+
)
143166

144-
// Note that this will result in a reconcile to be enqueued immediately
145-
// upon updating the status. The delayed requeue for attempting lookups
146-
// is enforced above.
147-
domainStatus.Verification.NextVerificationAttempt = metav1.NewTime(r.timeNow().Add(requeueAfter))
167+
// Set the next attempt on status (this intentionally triggers an immediate reconcile via status update)
168+
domainStatus.Verification.NextVerificationAttempt = metav1.NewTime(r.timeNow().Add(requeueAfter))
169+
nextAttempt = domainStatus.Verification.NextVerificationAttempt.Time
148170

149-
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
150-
defer cancel()
171+
// Perform verification attempts now
172+
attemptCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
173+
defer cancel()
151174

152-
r.attemptDNSVerification(ctx, domainStatus, verifiedDNSCondition)
175+
r.attemptDNSVerification(attemptCtx, domainStatus, verifiedDNSCondition)
153176

154-
if verifiedDNSCondition.Status != metav1.ConditionTrue {
155-
r.attemptHTTPVerification(ctx, domainStatus, verifiedHTTPCondition)
156-
}
177+
if verifiedDNSCondition.Status != metav1.ConditionTrue {
178+
r.attemptHTTPVerification(attemptCtx, domainStatus, verifiedHTTPCondition)
179+
}
157180

158-
if verifiedDNSCondition.Status == metav1.ConditionTrue || verifiedHTTPCondition.Status == metav1.ConditionTrue {
159-
verifiedCondition.Status = metav1.ConditionTrue
160-
verifiedCondition.Reason = networkingv1alpha.DomainReasonVerified
161-
verifiedCondition.Message = "Domain verification successful"
181+
if verifiedDNSCondition.Status == metav1.ConditionTrue || verifiedHTTPCondition.Status == metav1.ConditionTrue {
182+
verifiedCondition.Status = metav1.ConditionTrue
183+
verifiedCondition.Reason = networkingv1alpha.DomainReasonVerified
184+
verifiedCondition.Message = "Domain verification successful"
162185

163-
// Clear out verification info and unnecessary conditions
164-
domainStatus.Verification = nil
165-
apimeta.RemoveStatusCondition(&domainStatus.Conditions, networkingv1alpha.DomainConditionVerifiedDNS)
166-
apimeta.RemoveStatusCondition(&domainStatus.Conditions, networkingv1alpha.DomainConditionVerifiedHTTP)
186+
// Clear verification scaffolding and sub-conditions
187+
domainStatus.Verification = nil
188+
apimeta.RemoveStatusCondition(&domainStatus.Conditions, networkingv1alpha.DomainConditionVerifiedDNS)
189+
apimeta.RemoveStatusCondition(&domainStatus.Conditions, networkingv1alpha.DomainConditionVerifiedHTTP)
190+
// When verified, no future verification timer is needed
191+
nextAttempt = time.Time{}
192+
}
167193
}
168194
}
169195
}
170196

197+
// Update conditions
171198
apimeta.SetStatusCondition(&domainStatus.Conditions, *verifiedCondition)
172-
173199
if verifiedCondition.Status == metav1.ConditionFalse {
174200
apimeta.SetStatusCondition(&domainStatus.Conditions, *verifiedDNSCondition)
175201
apimeta.SetStatusCondition(&domainStatus.Conditions, *verifiedHTTPCondition)
176202
}
177203

178-
if !equality.Semantic.DeepEqual(domain.Status, domainStatus) {
179-
domain.Status = *domainStatus
180-
if statusErr := cl.GetClient().Status().Update(ctx, domain); statusErr != nil {
181-
return ctrl.Result{}, fmt.Errorf("failed updating domain status: %w", statusErr)
182-
}
183-
}
204+
// Commit the staged status back
205+
domain.Status = *domainStatus
184206

185-
return ctrl.Result{}, nil
207+
return nextAttempt
186208
}
187209

188210
func (r *DomainReconciler) attemptDNSVerification(
@@ -271,7 +293,6 @@ func (r *DomainReconciler) attemptHTTPVerification(
271293
verifiedHTTPCondition.Reason = networkingv1alpha.DomainReasonVerificationUnexpectedResponse
272294
verifiedHTTPCondition.Message = fmt.Sprintf("unexpected status code from HTTP token endpoint. HTTP %d", httpResponse.StatusCode)
273295
}
274-
275296
}
276297

277298
// defaultHTTPGet is the default HTTP GET implementation for verification

0 commit comments

Comments
 (0)