Skip to content

Commit 40c89a3

Browse files
committed
feat(state): add subscription installplan states
Add Subscription InstallPlan states and transition functions.
1 parent 0bb5691 commit 40c89a3

File tree

2 files changed

+1560
-36
lines changed

2 files changed

+1560
-36
lines changed

pkg/controller/operators/catalog/subscription/state.go

Lines changed: 245 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,11 @@ import (
66
corev1 "k8s.io/api/core/v1"
77
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
88
"k8s.io/apimachinery/pkg/types"
9-
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
109

1110
"github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
1211
clientv1alpha1 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned/typed/operators/v1alpha1"
12+
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/comparison"
1313
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/kubestate"
14-
"github.com/pkg/errors"
1514
)
1615

1716
// SubscriptionState describes subscription states.
@@ -57,7 +56,7 @@ type SubscriptionDeletedState interface {
5756

5857
// CatalogHealthState describes subscription states that represent a subscription with respect to catalog health.
5958
type CatalogHealthState interface {
60-
SubscriptionState
59+
SubscriptionExistsState
6160

6261
isCatalogHealthState()
6362

@@ -87,6 +86,61 @@ type CatalogUnhealthyState interface {
8786
isCatalogUnhealthyState()
8887
}
8988

89+
// InstallPlanState describes Subscription states with respect to an InstallPlan.
90+
type InstallPlanState interface {
91+
SubscriptionExistsState
92+
93+
isInstallPlanState()
94+
95+
CheckReference() InstallPlanState
96+
}
97+
98+
type NoInstallPlanReferencedState interface {
99+
InstallPlanState
100+
101+
isNoInstallPlanReferencedState()
102+
}
103+
104+
type InstallPlanReferencedState interface {
105+
InstallPlanState
106+
107+
isInstallPlanReferencedState()
108+
109+
InstallPlanNotFound(now *metav1.Time, client clientv1alpha1.SubscriptionInterface) (InstallPlanReferencedState, error)
110+
111+
CheckInstallPlanStatus(now *metav1.Time, client clientv1alpha1.SubscriptionInterface, status *v1alpha1.InstallPlanStatus) (InstallPlanReferencedState, error)
112+
}
113+
114+
type InstallPlanKnownState interface {
115+
InstallPlanReferencedState
116+
117+
isInstallPlanKnownState()
118+
}
119+
120+
type InstallPlanMissingState interface {
121+
InstallPlanKnownState
122+
123+
isInstallPlanMissingState()
124+
}
125+
126+
type InstallPlanPendingState interface {
127+
InstallPlanKnownState
128+
129+
isInstallPlanPendingState()
130+
}
131+
132+
type InstallPlanFailedState interface {
133+
InstallPlanKnownState
134+
135+
isInstallPlanFailedState()
136+
}
137+
138+
type InstallPlanInstalledState interface {
139+
InstallPlanKnownState
140+
141+
isInstallPlanInstalledState()
142+
}
143+
90144
type subscriptionState struct {
91145
kubestate.State
92146

@@ -236,7 +290,6 @@ func (c *catalogHealthState) UpdateHealth(now *metav1.Time, client clientv1alpha
236290
}
237291

238292
if !update && cond.Equals(in.Status.GetCondition(v1alpha1.SubscriptionCatalogSourcesUnhealthy)) {
239-
utilruntime.HandleError(errors.New("nothing has changed, returning same state"))
240293
// Nothing to do, transition to self
241294
return known, nil
242295
}
@@ -285,3 +338,191 @@ type catalogUnhealthyState struct {
285338
}
286339

287340
func (c *catalogUnhealthyState) isCatalogUnhealthyState() {}
341+
342+
type installPlanState struct {
343+
SubscriptionExistsState
344+
}
345+
346+
func (i *installPlanState) isInstallPlanState() {}
347+
348+
func (i *installPlanState) CheckReference() InstallPlanState {
349+
if i.Subscription().Status.InstallPlanRef != nil {
350+
return &installPlanReferencedState{
351+
InstallPlanState: i,
352+
}
353+
}
354+
355+
return &noInstallPlanReferencedState{
356+
InstallPlanState: i,
357+
}
358+
}
359+
360+
func newInstallPlanState(s SubscriptionExistsState) InstallPlanState {
361+
return &installPlanState{
362+
SubscriptionExistsState: s,
363+
}
364+
}
365+
366+
type noInstallPlanReferencedState struct {
367+
InstallPlanState
368+
}
369+
370+
func (n *noInstallPlanReferencedState) isNoInstallPlanReferencedState() {}
371+
372+
type installPlanReferencedState struct {
373+
InstallPlanState
374+
}
375+
376+
func (i *installPlanReferencedState) isInstallPlanReferencedState() {}
377+
378+
var hashEqual = comparison.NewHashEqualitor()
379+
380+
func (i *installPlanReferencedState) InstallPlanNotFound(now *metav1.Time, client clientv1alpha1.SubscriptionInterface) (InstallPlanReferencedState, error) {
381+
in := i.Subscription()
382+
out := in.DeepCopy()
383+
384+
// Remove pending and failed conditions
385+
out.Status.RemoveConditions(v1alpha1.SubscriptionInstallPlanPending, v1alpha1.SubscriptionInstallPlanFailed)
386+
387+
// Set missing condition to true
388+
missingCond := out.Status.GetCondition(v1alpha1.SubscriptionInstallPlanMissing)
389+
missingCond.Status = corev1.ConditionTrue
390+
missingCond.Reason = v1alpha1.ReferencedInstallPlanNotFound
391+
missingCond.LastTransitionTime = now
392+
out.Status.SetCondition(missingCond)
393+
394+
// Build missing state
395+
missingState := &installPlanMissingState{
396+
InstallPlanKnownState: &installPlanKnownState{
397+
InstallPlanReferencedState: i,
398+
},
399+
}
400+
401+
// Bail out if the conditions haven't changed (using select fields included in a hash)
402+
if hashEqual(out.Status.Conditions, in.Status.Conditions) {
403+
return missingState, nil
404+
}
405+
406+
// Update the Subscription
407+
out.Status.LastUpdated = *now
408+
updated, err := client.UpdateStatus(out)
409+
if err != nil {
410+
return i, err
411+
}
412+
413+
// Stuff updated Subscription into state
414+
missingState.setSubscription(updated)
415+
416+
return missingState, nil
417+
}
418+
419+
func (i *installPlanReferencedState) CheckInstallPlanStatus(now *metav1.Time, client clientv1alpha1.SubscriptionInterface, status *v1alpha1.InstallPlanStatus) (InstallPlanReferencedState, error) {
420+
in := i.Subscription()
421+
out := in.DeepCopy()
422+
423+
// Remove missing, pending, and failed conditions
424+
out.Status.RemoveConditions(v1alpha1.SubscriptionInstallPlanMissing, v1alpha1.SubscriptionInstallPlanPending, v1alpha1.SubscriptionInstallPlanFailed)
425+
426+
// Build and set the InstallPlan condition, if any
427+
cond := v1alpha1.SubscriptionCondition{
428+
Status: corev1.ConditionUnknown,
429+
LastTransitionTime: now,
430+
}
431+
432+
// TODO: Use InstallPlan conditions instead of phases
433+
// Get the status of the InstallPlan and create the appropriate condition and state
434+
var known InstallPlanKnownState
435+
switch phase := status.Phase; phase {
436+
case v1alpha1.InstallPlanPhaseNone:
437+
// Set reason and let the following case fill out the pending condition
438+
cond.Reason = v1alpha1.InstallPlanNotYetReconciled
439+
fallthrough
440+
case v1alpha1.InstallPlanPhasePlanning, v1alpha1.InstallPlanPhaseInstalling, v1alpha1.InstallPlanPhaseRequiresApproval:
441+
if cond.Reason == "" {
442+
cond.Reason = string(phase)
443+
}
444+
445+
cond.Type = v1alpha1.SubscriptionInstallPlanPending
446+
cond.Status = corev1.ConditionTrue
447+
out.Status.SetCondition(cond)
448+
449+
// Build pending state
450+
known = &installPlanPendingState{
451+
InstallPlanKnownState: &installPlanKnownState{
452+
InstallPlanReferencedState: i,
453+
},
454+
}
455+
case v1alpha1.InstallPlanPhaseFailed:
456+
// Attempt to project reason from failed InstallPlan condition
457+
if installedCond := status.GetCondition(v1alpha1.InstallPlanInstalled); installedCond.Status == corev1.ConditionFalse {
458+
cond.Reason = string(installedCond.Reason)
459+
} else {
460+
cond.Reason = v1alpha1.InstallPlanFailed
461+
}
462+
463+
cond.Type = v1alpha1.SubscriptionInstallPlanFailed
464+
cond.Status = corev1.ConditionTrue
465+
out.Status.SetCondition(cond)
466+
467+
// Build failed state
468+
known = &installPlanFailedState{
469+
InstallPlanKnownState: &installPlanKnownState{
470+
InstallPlanReferencedState: i,
471+
},
472+
}
473+
default:
474+
// Build installed state
475+
known = &installPlanInstalledState{
476+
InstallPlanKnownState: &installPlanKnownState{
477+
InstallPlanReferencedState: i,
478+
},
479+
}
480+
}
481+
482+
// Bail out if the conditions haven't changed (using select fields included in a hash)
483+
if hashEqual(out.Status.Conditions, in.Status.Conditions) {
484+
return known, nil
485+
}
486+
487+
// Update the Subscription
488+
out.Status.LastUpdated = *now
489+
updated, err := client.UpdateStatus(out)
490+
if err != nil {
491+
return i, err
492+
}
493+
494+
// Stuff updated Subscription into state
495+
known.setSubscription(updated)
496+
497+
return known, nil
498+
}
499+
500+
type installPlanKnownState struct {
501+
InstallPlanReferencedState
502+
}
503+
504+
func (i *installPlanKnownState) isInstallPlanKnownState() {}
505+
506+
type installPlanMissingState struct {
507+
InstallPlanKnownState
508+
}
509+
510+
func (i *installPlanMissingState) isInstallPlanMissingState() {}
511+
512+
type installPlanPendingState struct {
513+
InstallPlanKnownState
514+
}
515+
516+
func (i *installPlanPendingState) isInstallPlanPendingState() {}
517+
518+
type installPlanFailedState struct {
519+
InstallPlanKnownState
520+
}
521+
522+
func (i *installPlanFailedState) isInstallPlanFailedState() {}
523+
524+
type installPlanInstalledState struct {
525+
InstallPlanKnownState
526+
}
527+
528+
func (i *installPlanInstalledState) isInstallPlanInstalledState() {}

0 commit comments

Comments
 (0)