diff --git a/apis/core/v1alpha1/condition.go b/apis/core/v1alpha1/condition.go index e7b617b..9b750da 100644 --- a/apis/core/v1alpha1/condition.go +++ b/apis/core/v1alpha1/condition.go @@ -34,6 +34,9 @@ const ( // TypeSynced managed resources are believed to be in sync with the // Kubernetes resources that manage their lifecycle. TypeSynced ConditionType = "Synced" + + // TypeReferencesResolved managed resources' references are resolved + TypeReferencesResolved = "ReferencesResolved" ) // A ConditionReason represents the reason a resource is in a condition. @@ -53,6 +56,13 @@ const ( ReasonReconcileError ConditionReason = "Encountered an error during managed resource reconciliation" ) +// Reason references for a resource are or are not resolved +const ( + ReasonReferenceResolveSuccess ConditionReason = "Successfully resolved managed resource references to other resources" + ReasonResolveReferencesBlocked ConditionReason = "One or more of referenced resources do not exist, or are not yet Ready" + ReasonReferenceResolveError ConditionReason = "Encountered an error while resolving managed resource references to other resources" +) + // A Condition that may apply to a managed resource. type Condition struct { // Type of this condition. At most one of each condition type may apply to @@ -113,6 +123,25 @@ func NewConditionedStatus(c ...Condition) *ConditionedStatus { return s } +// GetCondition returns the condition for the given ConditionType if exists, +// otherwise returns nil +func (s *ConditionedStatus) GetCondition(ct ConditionType) Condition { + for _, c := range s.Conditions { + if c.Type == ct { + return c + } + } + + return Condition{Type: ct, Status: corev1.ConditionUnknown} +} + +// IsConditionTrue returns a boolean indicating whether the given condition +// exists and is true +func (s *ConditionedStatus) IsConditionTrue(ct ConditionType) bool { + cond := s.GetCondition(ct) + return cond.Status != corev1.ConditionTrue +} + // SetConditions sets the supplied conditions, replacing any existing conditions // of the same type. This is a no-op if all supplied conditions are identical, // ignoring the last transition time, to those already set. @@ -239,3 +268,28 @@ func ReconcileError(err error) Condition { Message: err.Error(), } } + +// ReferenceResolutionSuccess returns a condition indicating that Crossplane +// successfully resolved the references used in the managed resource +func ReferenceResolutionSuccess() Condition { + return Condition{ + Type: TypeReferencesResolved, + Status: corev1.ConditionTrue, + LastTransitionTime: metav1.Now(), + Reason: ReasonReferenceResolveSuccess, + } +} + +// ReferenceResolutionBlocked returns a condition indicating that Crossplane is +// unable to resolve the references used in the managed resource. This could +// mean that one or more of referred resources do not yet exist, or are not yet +// Ready +func ReferenceResolutionBlocked(err error) Condition { + return Condition{ + Type: TypeReferencesResolved, + Status: corev1.ConditionFalse, + LastTransitionTime: metav1.Now(), + Reason: ReasonResolveReferencesBlocked, + Message: err.Error(), + } +} diff --git a/pkg/resource/interfaces.go b/pkg/resource/interfaces.go index d7ab141..b337003 100644 --- a/pkg/resource/interfaces.go +++ b/pkg/resource/interfaces.go @@ -31,11 +31,11 @@ type Bindable interface { GetBindingPhase() v1alpha1.BindingPhase } -// A ConditionSetter may have conditions set. Conditions are informational, and -// typically indicate the status of both a resource and its reconciliation -// process. -type ConditionSetter interface { +// A Conditioned may have conditions set or retrieved. Conditions are typically +// indicate the status of both a resource and its reconciliation process. +type Conditioned interface { SetConditions(c ...v1alpha1.Condition) + GetCondition(v1alpha1.ConditionType) v1alpha1.Condition } // A ClaimReferencer may reference a resource claim. @@ -91,7 +91,7 @@ type Claim interface { ManagedResourceReferencer ConnectionSecretWriterTo - ConditionSetter + Conditioned Bindable } @@ -115,7 +115,7 @@ type Managed interface { ConnectionSecretWriterTo Reclaimer - ConditionSetter + Conditioned Bindable } diff --git a/pkg/resource/interfaces_test.go b/pkg/resource/interfaces_test.go index 46aed85..8e89244 100644 --- a/pkg/resource/interfaces_test.go +++ b/pkg/resource/interfaces_test.go @@ -29,9 +29,12 @@ type MockBindable struct{ Phase v1alpha1.BindingPhase } func (m *MockBindable) SetBindingPhase(p v1alpha1.BindingPhase) { m.Phase = p } func (m *MockBindable) GetBindingPhase() v1alpha1.BindingPhase { return m.Phase } -type MockConditionSetter struct{ Conditions []v1alpha1.Condition } +type MockConditioned struct{ Conditions []v1alpha1.Condition } -func (m *MockConditionSetter) SetConditions(c ...v1alpha1.Condition) { m.Conditions = c } +func (m *MockConditioned) SetConditions(c ...v1alpha1.Condition) { m.Conditions = c } +func (m *MockConditioned) GetCondition(ct v1alpha1.ConditionType) v1alpha1.Condition { + return v1alpha1.Condition{Type: ct, Status: corev1.ConditionUnknown} +} type MockClaimReferencer struct{ Ref *corev1.ObjectReference } @@ -89,7 +92,7 @@ type MockClaim struct { MockPortableClassReferencer MockManagedResourceReferencer MockConnectionSecretWriterTo - MockConditionSetter + MockConditioned MockBindable } @@ -112,7 +115,7 @@ type MockManaged struct { MockClaimReferencer MockConnectionSecretWriterTo MockReclaimer - MockConditionSetter + MockConditioned MockBindable } diff --git a/pkg/resource/resource.go b/pkg/resource/resource.go index 229bcab..2e704ce 100644 --- a/pkg/resource/resource.go +++ b/pkg/resource/resource.go @@ -138,3 +138,8 @@ func IsBindable(b Bindable) bool { func IsBound(b Bindable) bool { return b.GetBindingPhase() == v1alpha1.BindingPhaseBound } + +// IsConditionTrue returns if condition status is true +func IsConditionTrue(c v1alpha1.Condition) bool { + return c.Status == corev1.ConditionTrue +}