@@ -176,7 +176,9 @@ func (r *reconciler) ensureServiceMeshOperatorInstallPlan(ctx context.Context) (
176176}
177177
178178// currentInstallPlan returns the InstallPlan that describes installing the expected version of the GatewayAPI
179- // implementation, if one exists.
179+ // implementation. If no InstallPlan exists for the expected version, return the InstallPlan which replaces the currently installed one.
180+ // This InstallPlan is expected to advance the OSSM operator toward the next CSV in the upgrade graph,
181+ // assuming that the configured version is available further up the graph.
180182func (r * reconciler ) currentInstallPlan (ctx context.Context ) (bool , * operatorsv1alpha1.InstallPlan , error ) {
181183 _ , subscription , err := r .currentSubscription (ctx , operatorcontroller .ServiceMeshOperatorSubscriptionName ())
182184 if err != nil {
@@ -189,7 +191,7 @@ func (r *reconciler) currentInstallPlan(ctx context.Context) (bool, *operatorsv1
189191 if installPlans == nil || len (installPlans .Items ) == 0 {
190192 return false , nil , nil
191193 }
192- var currentInstallPlan * operatorsv1alpha1.InstallPlan
194+ var currentInstallPlan , nextInstallPlan * operatorsv1alpha1.InstallPlan
193195 multipleInstallPlans := false
194196 for _ , installPlan := range installPlans .Items {
195197 if len (installPlan .OwnerReferences ) == 0 || len (installPlan .Spec .ClusterServiceVersionNames ) == 0 {
@@ -209,6 +211,7 @@ func (r *reconciler) currentInstallPlan(ctx context.Context) (bool, *operatorsv1
209211 if installPlan .Status .Phase != operatorsv1alpha1 .InstallPlanPhaseRequiresApproval {
210212 continue
211213 }
214+ // Check whether InstallPlan implements the expected operator version.
212215 for _ , csvName := range installPlan .Spec .ClusterServiceVersionNames {
213216 if csvName == r .config .GatewayAPIOperatorVersion {
214217 // Keep the newest InstallPlan to return at the end of the loop.
@@ -223,10 +226,56 @@ func (r *reconciler) currentInstallPlan(ctx context.Context) (bool, *operatorsv1
223226 }
224227 }
225228 }
229+ // Check whether InstallPlan implements the next operator version in the upgrade graph.
230+ for _ , csvName := range installPlan .Spec .ClusterServiceVersionNames {
231+ // The definitions of InstalledCSV and CurrentCSV are non-trivial:
232+ //
233+ // - InstalledCSV represents the currently running CSV.
234+ // - CurrentCSV represents the version that "subscription is progressing to"
235+ // which practically means "the next CSV in the upgrade graph."
236+ //
237+ // - If InstalledCSV < CurrentCSV:
238+ // No CSV replacement is ongoing. InstalledCSV is the current version,
239+ // and CurrentCSV is the next one in the upgrade graph.
240+ // - If InstalledCSV == CurrentCSV:
241+ // One of the following scenarios is possible:
242+ // 1. CSV replacement is in progress "InstalledCSV-1" is being replaced with CurrentCSV.
243+ // 2. Installation of the first CSV is in progress.
244+ // 3. There is no "next CSV" in the upgrade graph, so CurrentCSV
245+ // cannot point to a future version.
246+ // CurrentCSV only becomes the next version once the replacement finishes,
247+ // and the next InstallPlan appears around the same time.
248+ //
249+ // The first condition (below) prevents setting the next InstallPlan while a replacement
250+ // or installation is ongoing, or when the end of the upgrade graph is reached.
251+ if subscription .Status .InstalledCSV != subscription .Status .CurrentCSV && csvName == subscription .Status .CurrentCSV {
252+ if nextInstallPlan == nil {
253+ nextInstallPlan = & installPlan
254+ break
255+ }
256+ }
257+ }
226258 }
227259 if multipleInstallPlans {
228260 log .Info (fmt .Sprintf ("found multiple valid InstallPlans. using %s because it's the newest" , currentInstallPlan .Name ))
229261 }
262+ // No InstallPlan with the expected operator version was found,
263+ // but the next one in the upgrade graph exists.
264+ // Return the next InstallPlan to continue the upgrade.
265+ if currentInstallPlan == nil && nextInstallPlan != nil {
266+ log .Info ("next install plan time" )
267+ // The condition below prevents approving an InstallPlan
268+ // that targets a version beyond the expected operator version.
269+ // This can happen when:
270+ // - InstallPlan with the expected version is complete (no approval needed).
271+ // - Newer versions exist in the upgrade graph.
272+ // The check ensures that the currently running CSV is different
273+ // from the expected version. Once they match, no further action is needed.
274+ if subscription .Status .InstalledCSV != r .config .GatewayAPIOperatorVersion {
275+ log .Info ("installplan with expected operator version was not found; proceedng with an intermedite installplan" , "name" , nextInstallPlan .Name , "csv" , subscription .Status .CurrentCSV )
276+ currentInstallPlan = nextInstallPlan
277+ }
278+ }
230279 return (currentInstallPlan != nil ), currentInstallPlan , nil
231280}
232281
0 commit comments