Merge pull request #499 from negz/conditionedstatus

Expose fewer condition types by leveraging status and reason.
This commit is contained in:
Nic Cope 2019-05-31 16:13:54 -07:00 committed by GitHub
commit 54bc0da57d
48 changed files with 1044 additions and 350 deletions

View File

@ -253,7 +253,7 @@ type NodeGroupConfigurationSpec struct {
// ReplicationGroupStatus defines the observed state of ReplicationGroup
type ReplicationGroupStatus struct {
corev1alpha1.ConditionedStatus
corev1alpha1.DeprecatedConditionedStatus
corev1alpha1.BindingStatusPhase
State string `json:"state,omitempty"`
Message string `json:"message,omitempty"`

View File

@ -164,7 +164,7 @@ func (in *ReplicationGroupSpec) DeepCopy() *ReplicationGroupSpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ReplicationGroupStatus) DeepCopyInto(out *ReplicationGroupStatus) {
*out = *in
in.ConditionedStatus.DeepCopyInto(&out.ConditionedStatus)
in.DeprecatedConditionedStatus.DeepCopyInto(&out.DeprecatedConditionedStatus)
out.BindingStatusPhase = in.BindingStatusPhase
if in.MemberClusters != nil {
in, out := &in.MemberClusters, &out.MemberClusters

View File

@ -221,7 +221,7 @@ type WorkerNodesSpec struct {
// EKSClusterStatus schema of the status of eks cluster
type EKSClusterStatus struct {
corev1alpha1.ConditionedStatus
corev1alpha1.DeprecatedConditionedStatus
corev1alpha1.BindingStatusPhase
// State of the cluster (see status constants above)

View File

@ -140,7 +140,7 @@ func (in *EKSClusterSpec) DeepCopy() *EKSClusterSpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *EKSClusterStatus) DeepCopyInto(out *EKSClusterStatus) {
*out = *in
in.ConditionedStatus.DeepCopyInto(&out.ConditionedStatus)
in.DeprecatedConditionedStatus.DeepCopyInto(&out.DeprecatedConditionedStatus)
out.BindingStatusPhase = in.BindingStatusPhase
out.ConnectionSecretRef = in.ConnectionSecretRef
return

View File

@ -80,7 +80,7 @@ const (
// RDSInstanceStatus defines the observed state of RDSInstance
type RDSInstanceStatus struct {
corev1alpha1.ConditionedStatus
corev1alpha1.DeprecatedConditionedStatus
corev1alpha1.BindingStatusPhase
State string `json:"state,omitempty"`
Message string `json:"message,omitempty"`

View File

@ -125,7 +125,7 @@ func (in *RDSInstanceSpec) DeepCopy() *RDSInstanceSpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *RDSInstanceStatus) DeepCopyInto(out *RDSInstanceStatus) {
*out = *in
in.ConditionedStatus.DeepCopyInto(&out.ConditionedStatus)
in.DeprecatedConditionedStatus.DeepCopyInto(&out.DeprecatedConditionedStatus)
out.BindingStatusPhase = in.BindingStatusPhase
return
}

View File

@ -61,7 +61,7 @@ type S3BucketSpec struct {
// S3BucketStatus defines the observed state of S3Bucket
type S3BucketStatus struct {
corev1alpha1.ConditionedStatus
corev1alpha1.DeprecatedConditionedStatus
corev1alpha1.BindingStatusPhase
Message string `json:"message,omitempty"`
ProviderID string `json:"providerID,omitempty"` // the external ID to identify this resource in the cloud provider
@ -200,7 +200,7 @@ func (b *S3Bucket) OwnerReference() metav1.OwnerReference {
// IsAvailable for usage/binding
func (b *S3Bucket) IsAvailable() bool {
return b.Status.IsCondition(corev1alpha1.Ready)
return b.Status.IsDeprecatedCondition(corev1alpha1.DeprecatedReady)
}
// IsBound returns true if this bucket is bound to a resource claim.

View File

@ -127,7 +127,7 @@ func (in *S3BucketSpec) DeepCopy() *S3BucketSpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *S3BucketStatus) DeepCopyInto(out *S3BucketStatus) {
*out = *in
in.ConditionedStatus.DeepCopyInto(&out.ConditionedStatus)
in.DeprecatedConditionedStatus.DeepCopyInto(&out.DeprecatedConditionedStatus)
out.BindingStatusPhase = in.BindingStatusPhase
out.ConnectionSecretRef = in.ConnectionSecretRef
return

View File

@ -38,7 +38,7 @@ type ProviderSpec struct {
// ProviderStatus represents the status of an AWS Provider.
type ProviderStatus struct {
corev1alpha1.ConditionedStatus
corev1alpha1.DeprecatedConditionedStatus
}
// +genclient

View File

@ -104,7 +104,7 @@ func (in *ProviderSpec) DeepCopy() *ProviderSpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ProviderStatus) DeepCopyInto(out *ProviderStatus) {
*out = *in
in.ConditionedStatus.DeepCopyInto(&out.ConditionedStatus)
in.DeprecatedConditionedStatus.DeepCopyInto(&out.DeprecatedConditionedStatus)
return
}

View File

@ -129,7 +129,7 @@ type SKUSpec struct {
// RedisStatus defines the observed state of Redis
type RedisStatus struct {
corev1alpha1.ConditionedStatus
corev1alpha1.DeprecatedConditionedStatus
corev1alpha1.BindingStatusPhase
State string `json:"state,omitempty"`
Message string `json:"message,omitempty"`

View File

@ -124,7 +124,7 @@ func (in *RedisSpec) DeepCopy() *RedisSpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *RedisStatus) DeepCopyInto(out *RedisStatus) {
*out = *in
in.ConditionedStatus.DeepCopyInto(&out.ConditionedStatus)
in.DeprecatedConditionedStatus.DeepCopyInto(&out.DeprecatedConditionedStatus)
out.BindingStatusPhase = in.BindingStatusPhase
return
}

View File

@ -76,7 +76,7 @@ type AKSClusterSpec struct {
// AKSClusterStatus is the status for AKS cluster resources
type AKSClusterStatus struct {
corev1alpha1.ConditionedStatus
corev1alpha1.DeprecatedConditionedStatus
corev1alpha1.BindingStatusPhase
// ClusterName is the name of the cluster as registered with the cloud provider
ClusterName string `json:"clusterName,omitempty"`

View File

@ -125,7 +125,7 @@ func (in *AKSClusterSpec) DeepCopy() *AKSClusterSpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AKSClusterStatus) DeepCopyInto(out *AKSClusterStatus) {
*out = *in
in.ConditionedStatus.DeepCopyInto(&out.ConditionedStatus)
in.DeprecatedConditionedStatus.DeepCopyInto(&out.DeprecatedConditionedStatus)
out.BindingStatusPhase = in.BindingStatusPhase
return
}

View File

@ -125,7 +125,7 @@ type SQLServerSpec struct {
// SQLServerStatus defines the observed state of SQLServer
type SQLServerStatus struct {
corev1alpha1.ConditionedStatus
corev1alpha1.DeprecatedConditionedStatus
corev1alpha1.BindingStatusPhase
State string `json:"state,omitempty"`
Message string `json:"message,omitempty"`

View File

@ -195,7 +195,7 @@ func (in *SQLServerSpec) DeepCopy() *SQLServerSpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SQLServerStatus) DeepCopyInto(out *SQLServerStatus) {
*out = *in
in.ConditionedStatus.DeepCopyInto(&out.ConditionedStatus)
in.DeprecatedConditionedStatus.DeepCopyInto(&out.DeprecatedConditionedStatus)
out.BindingStatusPhase = in.BindingStatusPhase
return
}

View File

@ -114,14 +114,14 @@ func (ta *MockAccount) WithSpecStorageAccountSpec(spec *v1alpha1.StorageAccountS
return ta
}
// WithStatusCondition sets status condition
func (ta *MockAccount) WithStatusCondition(c corev1alpha1.Condition) *MockAccount {
ta.Status.ConditionedStatus.SetCondition(c)
// WithStatusDeprecatedCondition sets status condition
func (ta *MockAccount) WithStatusDeprecatedCondition(c corev1alpha1.DeprecatedCondition) *MockAccount {
ta.Status.DeprecatedConditionedStatus.SetDeprecatedCondition(c)
return ta
}
// WithStatusFailedCondition sets and activates Failed condition
func (ta *MockAccount) WithStatusFailedCondition(reason, msg string) *MockAccount {
// WithStatusFailedDeprecatedCondition sets and activates Failed condition
func (ta *MockAccount) WithStatusFailedDeprecatedCondition(reason, msg string) *MockAccount {
ta.Status.SetFailed(reason, msg)
return ta
}

View File

@ -129,20 +129,20 @@ func (tc *MockContainer) WithStatusSetBound(bound bool) *MockContainer {
return tc
}
// WithFailedCondition sets status failed condition
func (tc *MockContainer) WithFailedCondition(reason, msg string) *MockContainer {
// WithFailedDeprecatedCondition sets status failed condition
func (tc *MockContainer) WithFailedDeprecatedCondition(reason, msg string) *MockContainer {
tc.Status.SetFailed(reason, msg)
return tc
}
// WithUnsetAllConditions resets all status conditions
func (tc *MockContainer) WithUnsetAllConditions() *MockContainer {
tc.Status.UnsetAllConditions()
// WithUnsetAllDeprecatedConditions resets all status conditions
func (tc *MockContainer) WithUnsetAllDeprecatedConditions() *MockContainer {
tc.Status.UnsetAllDeprecatedConditions()
return tc
}
// WithReadyCondition sets status ready condition
func (tc *MockContainer) WithReadyCondition() *MockContainer {
// WithReadyDeprecatedCondition sets status ready condition
func (tc *MockContainer) WithReadyDeprecatedCondition() *MockContainer {
tc.Status.SetReady()
return tc
}

View File

@ -52,7 +52,7 @@ type AccountSpec struct {
type AccountStatus struct {
*StorageAccountStatus `json:"accountStatus,inline"`
corev1alpha1.ConditionedStatus
corev1alpha1.DeprecatedConditionedStatus
corev1alpha1.BindingStatusPhase
ConnectionSecretRef corev1.LocalObjectReference `json:"connectionSecretRef,omitempty"`
}
@ -167,7 +167,7 @@ type ContainerSpec struct {
// ContainerStatus sub-resource for Container object
type ContainerStatus struct {
corev1alpha1.ConditionedStatus
corev1alpha1.DeprecatedConditionedStatus
corev1alpha1.BindingStatusPhase
ConnectionSecretRef corev1.LocalObjectReference `json:"connectionSecretRef,omitempty"`
Name string `json:"name,omitempty"`

View File

@ -215,7 +215,7 @@ func TestAccount_IsAvailable(t *testing.T) {
bReadyAndFailed.Status.SetFailed("", "")
bNotReadyAndFailed := bReadyAndFailed
bNotReadyAndFailed.Status.UnsetCondition(v1alpha1.Ready)
bNotReadyAndFailed.Status.UnsetDeprecatedCondition(v1alpha1.DeprecatedReady)
tests := []struct {
name string
@ -416,7 +416,7 @@ func TestContainer_IsAvailable(t *testing.T) {
bReadyAndFailed.Status.SetFailed("", "")
bNotReadyAndFailed := bReadyAndFailed
bNotReadyAndFailed.Status.UnsetCondition(v1alpha1.Ready)
bNotReadyAndFailed.Status.UnsetDeprecatedCondition(v1alpha1.DeprecatedReady)
tests := []struct {
name string

View File

@ -126,7 +126,7 @@ func (in *AccountStatus) DeepCopyInto(out *AccountStatus) {
*out = new(StorageAccountStatus)
(*in).DeepCopyInto(*out)
}
in.ConditionedStatus.DeepCopyInto(&out.ConditionedStatus)
in.DeprecatedConditionedStatus.DeepCopyInto(&out.DeprecatedConditionedStatus)
out.BindingStatusPhase = in.BindingStatusPhase
out.ConnectionSecretRef = in.ConnectionSecretRef
return
@ -240,7 +240,7 @@ func (in *ContainerSpec) DeepCopy() *ContainerSpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ContainerStatus) DeepCopyInto(out *ContainerStatus) {
*out = *in
in.ConditionedStatus.DeepCopyInto(&out.ConditionedStatus)
in.DeprecatedConditionedStatus.DeepCopyInto(&out.DeprecatedConditionedStatus)
out.BindingStatusPhase = in.BindingStatusPhase
out.ConnectionSecretRef = in.ConnectionSecretRef
return

View File

@ -35,7 +35,7 @@ type ProviderSpec struct {
// ProviderStatus is the status for this provider
type ProviderStatus struct {
corev1alpha1.ConditionedStatus
corev1alpha1.DeprecatedConditionedStatus
}
// +genclient
@ -86,7 +86,7 @@ type ResourceGroupSpec struct {
// ResourceGroupStatus is the status for this resource group
type ResourceGroupStatus struct {
Name string `json:"name"`
corev1alpha1.ConditionedStatus
corev1alpha1.DeprecatedConditionedStatus
}
// +genclient

View File

@ -105,7 +105,7 @@ func (in *ProviderSpec) DeepCopy() *ProviderSpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ProviderStatus) DeepCopyInto(out *ProviderStatus) {
*out = *in
in.ConditionedStatus.DeepCopyInto(&out.ConditionedStatus)
in.DeprecatedConditionedStatus.DeepCopyInto(&out.DeprecatedConditionedStatus)
return
}
@ -210,7 +210,7 @@ func (in *ResourceGroupSpec) DeepCopy() *ResourceGroupSpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ResourceGroupStatus) DeepCopyInto(out *ResourceGroupStatus) {
*out = *in
in.ConditionedStatus.DeepCopyInto(&out.ConditionedStatus)
in.DeprecatedConditionedStatus.DeepCopyInto(&out.DeprecatedConditionedStatus)
return
}

View File

@ -136,7 +136,7 @@ type WorkloadSpec struct {
// WorkloadStatus represents the status of a workload.
type WorkloadStatus struct {
corev1alpha1.ConditionedStatus
corev1alpha1.DeprecatedConditionedStatus
Cluster *corev1.ObjectReference `json:"clusterRef,omitempty"`
appsv1.DeploymentStatus `json:"deployment,omitempty"`

View File

@ -232,7 +232,7 @@ func (in *WorkloadSpec) DeepCopy() *WorkloadSpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WorkloadStatus) DeepCopyInto(out *WorkloadStatus) {
*out = *in
in.ConditionedStatus.DeepCopyInto(&out.ConditionedStatus)
in.DeprecatedConditionedStatus.DeepCopyInto(&out.DeprecatedConditionedStatus)
if in.Cluster != nil {
in, out := &in.Cluster, &out.Cluster
*out = new(v1.ObjectReference)

View File

@ -17,41 +17,65 @@ limitations under the License.
package v1alpha1
import (
"sort"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// ConditionType type for possible conditions the resource could be in.
// A ConditionType represents a condition a resource could be in.
type ConditionType string
// Resource conditions.
// Condition types.
const (
// Pending means that the resource create request has been received and is waiting to be fulfilled.
Pending ConditionType = "Pending"
// Creating means that the resource create request has been accepted and the resource is in
// the process of being created.
Creating ConditionType = "Creating"
// Deleting means that the resource is in the process of being deleted.
Deleting ConditionType = "Deleting"
// Failed means that the resource is in a failure state, for example it failed to be created.
Failed ConditionType = "Failed"
// Ready means that the resource creation has been successful and the resource is ready to
// accept requests and perform operations.
// Ready managed resources are believed to be ready to handle work.
Ready ConditionType = "Ready"
// Synced managed resources are believed to be in sync with the Kubernetes
// resources that manage their lifecycle.
Synced ConditionType = "Synced"
)
// Condition contains details for the current condition of this pod.
// A ConditionReason represents the reason a resource is in a condition.
type ConditionReason string
// Reasons a resource is or is not ready.
const (
Available ConditionReason = "Managed resource is available for use"
Unavailable ConditionReason = "Managed resource is not available for use"
Creating ConditionReason = "Managed resource is being created"
Deleting ConditionReason = "Managed resource is being deleted"
)
// Reasons a resource is or is not synced.
const (
ReconcileSuccess ConditionReason = "Successfully reconciled managed resource"
ReconcileError ConditionReason = "Encountered an error during managed resource reconciliation"
)
// 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
// a managed resource at any point in time.
Type ConditionType
// Status of this condition; is it currently True, False, or Unknown?
Status corev1.ConditionStatus
// LastTransitionTime is the last time this condition transitioned from one
// status to another.
LastTransitionTime metav1.Time
Reason string
// A Reason for this condition's last transition from one status to another.
Reason ConditionReason
// A Message containing details about this condition's last transition from
// one status to another, if any.
Message string
}
// Equal returns true if the condition is identical to the supplied condition,
// ignoring the LastTransitionTime. github.com/go-test/deep uses this method to
// test equality.
// ignoring the LastTransitionTime.
func (c Condition) Equal(other Condition) bool {
return c.Type == other.Type &&
c.Status == other.Status &&
@ -59,139 +83,174 @@ func (c Condition) Equal(other Condition) bool {
c.Message == other.Message
}
// Conditionable defines set of functionality to operate on Conditions
type Conditionable interface {
Condition(ConditionType) *Condition
SetCondition(Condition)
RemoveCondition(ConditionType)
UnsetCondition(ConditionType)
UnsetAllConditions()
}
// NOTE(negz): Conditions are implemented as a slice rather than a map to comply
// with Kubernetes API conventions. Ideally we'd comply by using a map that
// marshalled to a JSON array, but doing so confuses the CRD schema generator.
// https://github.com/kubernetes/community/blob/9bf8cd/contributors/devel/sig-architecture/api-conventions.md#lists-of-named-subobjects-preferred-over-maps
// ConditionedStatus defines the observed state of RDS resource
// A ConditionedStatus reflects the observed status of a managed resource. Only
// one condition of each type may exist. Do not manipulate Conditions directly -
// use the Set method.
type ConditionedStatus struct {
// Conditions indicate state for particular aspects of a CustomResourceDefinition
// Conditions of the managed resource.
Conditions []Condition
}
// Condition returns a provider condition with the provided type if it exists.
func (c *ConditionedStatus) Condition(conditionType ConditionType) *Condition {
for i := range c.Conditions {
// This loop is written this way (as opposed to for i, cnd := range...)
// to avoid returning a pointer to a range variable whose content will
// change as the loop iterates.
// https://github.com/kyoh86/scopelint#whats-this
cnd := c.Conditions[i]
if cnd.Type == conditionType {
return &cnd
// NewConditionedStatus returns a new conditioned status with all condition
// types with unknown statuses.
func NewConditionedStatus() *ConditionedStatus {
return &ConditionedStatus{Conditions: []Condition{
{Type: Ready, Status: corev1.ConditionUnknown, LastTransitionTime: metav1.Now()},
{Type: Synced, Status: corev1.ConditionUnknown, LastTransitionTime: metav1.Now()},
}}
}
// Equal returns true if the status is identical to the supplied status,
// ignoring the LastTransitionTimes and order of statuses.
func (s *ConditionedStatus) Equal(other *ConditionedStatus) bool {
if s == nil || other == nil {
return s == nil && other == nil
}
if len(other.Conditions) != len(s.Conditions) {
return false
}
sc := make([]Condition, len(s.Conditions))
copy(sc, s.Conditions)
oc := make([]Condition, len(other.Conditions))
copy(oc, other.Conditions)
// We should not have more than one condition of each type.
sort.Slice(sc, func(i, j int) bool { return sc[i].Type < sc[j].Type })
sort.Slice(oc, func(i, j int) bool { return oc[i].Type < oc[j].Type })
for i := range sc {
if !sc[i].Equal(oc[i]) {
return false
}
}
return nil
return true
}
// IsCondition of provided type is present and set to true
func (c *ConditionedStatus) IsCondition(ctype ConditionType) bool {
condition := c.Condition(ctype)
return condition != nil && condition.Status == corev1.ConditionTrue
}
// IsReady returns true if the status is currently ready.
func (c *ConditionedStatus) IsReady() bool {
return c.IsCondition(Ready)
}
// IsFailed returns true if the status is currently failed.
func (c *ConditionedStatus) IsFailed() bool {
return c.IsCondition(Failed)
}
// SetCondition adds/replaces the given condition in the credentials controller status.
func (c *ConditionedStatus) SetCondition(condition Condition) {
current := c.Condition(condition.Type)
if current != nil && current.Equal(condition) {
return
}
newConditions := FilterOutCondition(c.Conditions, condition.Type)
newConditions = append(newConditions, condition)
c.Conditions = newConditions
}
// SetFailed set failed as an active condition
func (c *ConditionedStatus) SetFailed(reason, msg string) {
c.SetCondition(NewCondition(Failed, reason, msg))
}
// SetReady set ready as an active condition
func (c *ConditionedStatus) SetReady() {
c.SetCondition(NewCondition(Ready, "", ""))
}
// SetCreating set creating as an active condition
func (c *ConditionedStatus) SetCreating() {
c.SetCondition(NewCondition(Creating, "", ""))
}
// SetPending set pending as an active condition
func (c *ConditionedStatus) SetPending() {
c.SetCondition(NewCondition(Pending, "", ""))
}
// SetDeleting set deleting as an active condition
func (c *ConditionedStatus) SetDeleting() {
c.SetCondition(NewCondition(Deleting, "", ""))
}
// UnsetCondition set condition status to false with the given type - if found.
func (c *ConditionedStatus) UnsetCondition(conditionType ConditionType) {
current := c.Condition(conditionType)
if current != nil && current.Status == corev1.ConditionTrue {
current.Status = corev1.ConditionFalse
c.SetCondition(*current)
}
}
// UnsetAllConditions set conditions status to false on all conditions
func (c *ConditionedStatus) UnsetAllConditions() {
for i := range c.Conditions {
c.Conditions[i].Status = corev1.ConditionFalse
}
}
// RemoveCondition removes the condition with the provided type from the credentials controller status.
func (c *ConditionedStatus) RemoveCondition(condType ConditionType) {
c.Conditions = FilterOutCondition(c.Conditions, condType)
}
// RemoveAllConditions removes all condition entries
func (c *ConditionedStatus) RemoveAllConditions() {
c.Conditions = []Condition{}
}
// NewCondition creates a new resource condition.
func NewCondition(condType ConditionType, reason, msg string) Condition {
return Condition{
Type: condType,
Status: corev1.ConditionTrue,
LastTransitionTime: metav1.Now(),
Reason: reason,
Message: msg,
}
}
// NewReadyCondition sets and activates Ready status condition
func NewReadyCondition() Condition {
return NewCondition(Ready, "", "")
}
// FilterOutCondition returns a new slice of credentials controller conditions
// without conditions with the provided type.
func FilterOutCondition(conditions []Condition, condType ConditionType) []Condition {
var newConditions []Condition // nolint:prealloc
for _, c := range conditions {
if c.Type == condType {
// Set 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.
func (s *ConditionedStatus) Set(c ...Condition) {
for _, new := range c {
exists := false
for i, existing := range s.Conditions {
if existing.Type != new.Type {
continue
}
newConditions = append(newConditions, c)
if existing.Equal(new) {
exists = true
continue
}
s.Conditions[i] = new
exists = true
}
if !exists {
s.Conditions = append(s.Conditions, new)
}
}
return newConditions
}
// SetCreating indicates that Crossplane has an up-to-date view of the managed
// resource, which is currently being created.
func (s *ConditionedStatus) SetCreating() {
s.Set(
Condition{
Type: Ready,
Status: corev1.ConditionFalse,
LastTransitionTime: metav1.Now(),
Reason: Creating,
},
Condition{
Type: Synced,
Status: corev1.ConditionTrue,
LastTransitionTime: metav1.Now(),
Reason: ReconcileSuccess,
},
)
}
// SetDeleting indicates that Crossplane has an up-to-date view of the managed
// resource, which is currently being deleted.
func (s *ConditionedStatus) SetDeleting() {
s.Set(
Condition{
Type: Ready,
Status: corev1.ConditionFalse,
LastTransitionTime: metav1.Now(),
Reason: Deleting,
},
Condition{
Type: Synced,
Status: corev1.ConditionTrue,
LastTransitionTime: metav1.Now(),
Reason: ReconcileSuccess,
},
)
}
// SetAvailable indicates that Crossplane has an up-to-date view of the managed
// resource, which is currently observed to be available for use.
func (s *ConditionedStatus) SetAvailable() {
s.Set(
Condition{
Type: Ready,
Status: corev1.ConditionTrue,
LastTransitionTime: metav1.Now(),
Reason: Available,
},
Condition{
Type: Synced,
Status: corev1.ConditionTrue,
LastTransitionTime: metav1.Now(),
Reason: ReconcileSuccess,
},
)
}
// SetUnavailable indicates that Crossplane has an up-to-date view of the
// managed resource, which is not currently available for use. SetUnavailable
// should be called only when Crossplane expects the managed resource to be
// available but knows it is not, for example because its API reports it is
// unhealthy.
func (s *ConditionedStatus) SetUnavailable() {
s.Set(
Condition{
Type: Ready,
Status: corev1.ConditionFalse,
LastTransitionTime: metav1.Now(),
Reason: Unavailable,
},
Condition{
Type: Synced,
Status: corev1.ConditionTrue,
LastTransitionTime: metav1.Now(),
Reason: ReconcileSuccess,
},
)
}
// ReconcileError indicates that Crossplane encountered an error while
// reconciling the managed resource. This could mean Crossplane was unable to
// update the managed resource to reflect its desired state, or that Crossplane
// was unable to determine the current actual state of the managed resource.
func (s *ConditionedStatus) ReconcileError(err error) {
s.Set(
Condition{
Type: Synced,
Status: corev1.ConditionFalse,
LastTransitionTime: metav1.Now(),
Reason: ReconcileError,
Message: err.Error(),
},
)
}

View File

@ -19,25 +19,372 @@ package v1alpha1
import (
"testing"
. "github.com/onsi/gomega"
"github.com/google/go-cmp/cmp"
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func TestConditionedStatus_UnsetAllConditions(t *testing.T) {
g := NewGomegaWithT(t)
func TestConditionEqual(t *testing.T) {
cases := map[string]struct {
a Condition
b Condition
want bool
}{
"IdenticalIgnoringTimestamp": {
a: Condition{Type: Ready, Status: corev1.ConditionTrue, Reason: Available, Message: "cool", LastTransitionTime: metav1.Now()},
b: Condition{Type: Ready, Status: corev1.ConditionTrue, Reason: Available, Message: "cool", LastTransitionTime: metav1.Now()},
want: true,
},
"DifferentType": {
a: Condition{Type: Ready, Status: corev1.ConditionTrue, Reason: Available, Message: "cool", LastTransitionTime: metav1.Now()},
b: Condition{Type: Synced, Status: corev1.ConditionTrue, Reason: Available, Message: "cool", LastTransitionTime: metav1.Now()},
want: false,
},
"DifferentStatus": {
a: Condition{Type: Ready, Status: corev1.ConditionTrue, Reason: Available, Message: "cool", LastTransitionTime: metav1.Now()},
b: Condition{Type: Ready, Status: corev1.ConditionFalse, Reason: Available, Message: "cool", LastTransitionTime: metav1.Now()},
want: false,
},
"DifferentReason": {
a: Condition{Type: Ready, Status: corev1.ConditionFalse, Reason: Creating, Message: "cool", LastTransitionTime: metav1.Now()},
b: Condition{Type: Ready, Status: corev1.ConditionFalse, Reason: Deleting, Message: "cool", LastTransitionTime: metav1.Now()},
want: false,
},
"DifferentMessage": {
a: Condition{Type: Ready, Status: corev1.ConditionFalse, Reason: Creating, Message: "cool", LastTransitionTime: metav1.Now()},
b: Condition{Type: Ready, Status: corev1.ConditionFalse, Reason: Creating, Message: "uncool", LastTransitionTime: metav1.Now()},
want: false,
},
}
cs := &ConditionedStatus{}
cs.SetReady()
g.Expect(cs.IsReady()).To(BeTrue())
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
got := tc.a.Equal(tc.b)
cs.UnsetAllConditions()
g.Expect(cs.IsReady()).To(BeFalse())
cs.SetFailed("foo", "bar")
g.Expect(cs.IsFailed()).To(BeTrue())
g.Expect(cs.IsReady()).To(BeFalse())
cs.UnsetAllConditions()
cs.SetReady()
g.Expect(cs.IsFailed()).To(BeFalse())
g.Expect(cs.IsReady()).To(BeTrue())
if diff := cmp.Diff(tc.want, got); diff != "" {
t.Errorf("a.Equal(b): -want, +got:\n%s", diff)
}
})
}
}
func TestConditionedStatusEqual(t *testing.T) {
ready := Condition{
Type: Ready,
Status: corev1.ConditionTrue,
LastTransitionTime: metav1.Now(),
}
synced := Condition{
Type: Synced,
Status: corev1.ConditionTrue,
LastTransitionTime: metav1.Now(),
}
cases := map[string]struct {
a *ConditionedStatus
b *ConditionedStatus
want bool
}{
"Identical": {
a: &ConditionedStatus{Conditions: []Condition{ready, synced}},
b: &ConditionedStatus{Conditions: []Condition{ready, synced}},
want: true,
},
"IdenticalExceptOrder": {
a: &ConditionedStatus{Conditions: []Condition{ready, synced}},
b: &ConditionedStatus{Conditions: []Condition{synced, ready}},
want: true,
},
"DifferentLength": {
a: &ConditionedStatus{Conditions: []Condition{ready, synced}},
b: &ConditionedStatus{Conditions: []Condition{synced}},
want: false,
},
"DifferentCondition": {
a: &ConditionedStatus{Conditions: []Condition{ready, synced}},
b: &ConditionedStatus{Conditions: []Condition{
ready,
{
Type: Synced,
Status: corev1.ConditionTrue,
LastTransitionTime: metav1.Now(),
Message: "I'm different!",
},
}},
want: false,
},
"AIsNil": {
a: nil,
b: &ConditionedStatus{Conditions: []Condition{synced}},
want: false,
},
"BIsNil": {
a: &ConditionedStatus{Conditions: []Condition{synced}},
b: nil,
want: false,
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
got := tc.a.Equal(tc.b)
if diff := cmp.Diff(tc.want, got); diff != "" {
t.Errorf("a.Equal(b): -want, +got:\n%s", diff)
}
})
}
}
func TestSet(t *testing.T) {
ready := Condition{
Type: Ready,
Status: corev1.ConditionTrue,
LastTransitionTime: metav1.Now(),
}
synced := Condition{
Type: Synced,
Status: corev1.ConditionTrue,
LastTransitionTime: metav1.Now(),
}
cases := map[string]struct {
cs *ConditionedStatus
c []Condition
want *ConditionedStatus
}{
"TypeDoesNotExist": {
cs: &ConditionedStatus{Conditions: []Condition{synced}},
c: []Condition{ready},
want: &ConditionedStatus{Conditions: []Condition{ready, synced}},
},
"TypeIsIdentical": {
cs: &ConditionedStatus{Conditions: []Condition{ready}},
c: []Condition{ready},
want: &ConditionedStatus{Conditions: []Condition{ready}},
},
"TypeIsDifferent": {
cs: &ConditionedStatus{Conditions: []Condition{{
Type: Ready,
Reason: ConditionReason("imdifferent!"),
}}},
c: []Condition{ready},
want: &ConditionedStatus{Conditions: []Condition{ready}},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
tc.cs.Set(tc.c...)
got := tc.cs
if diff := cmp.Diff(tc.want, got); diff != "" {
t.Errorf("tc.cs.Set(...): -want, +got:\n%s", diff)
}
})
}
}
func TestSetCreating(t *testing.T) {
creating := NewConditionedStatus()
creating.SetCreating()
errored := NewConditionedStatus()
errored.SetCreating()
errored.ReconcileError(errors.New("boom"))
cases := map[string]struct {
cs *ConditionedStatus
want *ConditionedStatus
}{
"CurrentlyUnknown": {
cs: NewConditionedStatus(),
want: creating,
},
"CurrentlyCreating": {
cs: creating,
want: creating,
},
"CurrentlyReconcileError": {
cs: errored,
want: creating,
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
tc.cs.SetCreating()
got := tc.cs
if diff := cmp.Diff(tc.want, got); diff != "" {
t.Errorf("tc.cs.SetCreating(): -want, +got:\n%s", diff)
}
})
}
}
func TestSetDeleting(t *testing.T) {
deleting := NewConditionedStatus()
deleting.SetDeleting()
creating := NewConditionedStatus()
creating.SetCreating()
available := NewConditionedStatus()
available.SetAvailable()
errored := NewConditionedStatus()
errored.SetDeleting()
errored.ReconcileError(errors.New("boom"))
cases := map[string]struct {
cs *ConditionedStatus
want *ConditionedStatus
}{
"CurrentlyCreating": {
cs: creating,
want: deleting,
},
"CurrentlyDeleting": {
cs: deleting,
want: deleting,
},
"CurrentlyAvailable": {
cs: available,
want: deleting,
},
"CurrentlyReconcileError": {
cs: errored,
want: deleting,
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
tc.cs.SetDeleting()
got := tc.cs
if diff := cmp.Diff(tc.want, got); diff != "" {
t.Errorf("tc.cs.SetDeleting(): -want, +got:\n%s", diff)
}
})
}
}
func TestSetAvailable(t *testing.T) {
creating := NewConditionedStatus()
creating.SetCreating()
available := NewConditionedStatus()
available.SetAvailable()
errored := NewConditionedStatus()
errored.SetAvailable()
errored.ReconcileError(errors.New("boom"))
cases := map[string]struct {
cs *ConditionedStatus
want *ConditionedStatus
}{
"CurrentlyCreating": {
cs: creating,
want: available,
},
"CurrentlyAvailable": {
cs: available,
want: available,
},
"CurrentlyReconcileError": {
cs: errored,
want: available,
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
tc.cs.SetAvailable()
got := tc.cs
if diff := cmp.Diff(tc.want, got); diff != "" {
t.Errorf("tc.cs.SetAvailable(): -want, +got:\n%s", diff)
}
})
}
}
func TestSetUnavailable(t *testing.T) {
available := NewConditionedStatus()
available.SetAvailable()
unavailable := NewConditionedStatus()
unavailable.SetUnavailable()
errored := NewConditionedStatus()
errored.SetAvailable()
errored.ReconcileError(errors.New("boom"))
cases := map[string]struct {
cs *ConditionedStatus
want *ConditionedStatus
}{
"CurrentlyAvailable": {
cs: available,
want: unavailable,
},
"CurrentlyUnavailable": {
cs: unavailable,
want: unavailable,
},
"CurrentlyReconcileError": {
cs: errored,
want: unavailable,
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
tc.cs.SetUnavailable()
got := tc.cs
if diff := cmp.Diff(tc.want, got); diff != "" {
t.Errorf("tc.cs.SetUnavailable(): -want, +got:\n%s", diff)
}
})
}
}
func TestReconcileError(t *testing.T) {
err := errors.New("boom")
available := NewConditionedStatus()
available.SetAvailable()
errored := NewConditionedStatus()
errored.SetAvailable()
errored.ReconcileError(errors.New("boom"))
cases := map[string]struct {
cs *ConditionedStatus
want *ConditionedStatus
}{
"CurrentlyAvailable": {
cs: available,
want: errored,
},
"CurrentlyReconcileError": {
cs: errored,
want: errored,
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
tc.cs.ReconcileError(err)
got := tc.cs
if diff := cmp.Diff(tc.want, got); diff != "" {
t.Errorf("tc.cs.ReconcileError(...): -want, +got:\n%s", diff)
}
})
}
}

View File

@ -1,129 +0,0 @@
/*
Copyright 2018 The Crossplane Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha1
import (
"fmt"
"github.com/onsi/gomega"
"github.com/onsi/gomega/types"
)
// AssertConditions verifies the given expected conditions against the given actual conditions.
// This is helpful for unit tests since the Condition type has a timestamp that makes full
// object equal comparisons difficult.
// TODO: consider to use ConditionMatcher or ConditionedStatusMatcher instead
func AssertConditions(g *gomega.GomegaWithT, expected []Condition, actual ConditionedStatus) {
for _, ec := range expected {
// find the condition of the matching type, it should exist
ac := actual.Condition(ec.Type)
g.Expect(ac).NotTo(gomega.BeNil())
g.Expect(*ac).To(MatchCondition(ec))
}
}
// ConditionMatcher is a gomega matcher for Conditions.
// +k8s:deepcopy-gen=false
type ConditionMatcher struct {
expected interface{}
}
// Match returns true if the underlying condition matches the supplied one.
func (cm *ConditionMatcher) Match(actual interface{}) (success bool, err error) {
e, ok := cm.expected.(Condition)
if !ok {
return false, fmt.Errorf("expected value is not a Condition: %v", cm.expected)
}
a, ok := actual.(Condition)
if !ok {
return false, fmt.Errorf("actual value is not a Condition: %v", actual)
}
return e.Equal(a), nil
}
// FailureMessage is printed when conditions do not match.
func (cm *ConditionMatcher) FailureMessage(actual interface{}) (message string) {
return fmt.Sprintf("Expected\n\t%#v\nto match, actual\n\t%#v", cm.expected, actual)
}
// NegatedFailureMessage is printed when conditions match unexpectedly.
func (cm *ConditionMatcher) NegatedFailureMessage(actual interface{}) (message string) {
return fmt.Sprintf("Expected\n\t%#v\nnot to match, actual\n\t%#v", cm.expected, actual)
}
// MatchCondition returns a new gomga matcher for Conditions.
func MatchCondition(expected interface{}) types.GomegaMatcher {
return &ConditionMatcher{
expected: expected,
}
}
// ConditionedStatusMatcher is a gomega matcher for ConditionedStatuses.
// +k8s:deepcopy-gen=false
type ConditionedStatusMatcher struct {
expected interface{}
}
// Match returns true if the underlying conditioned status matches the supplied
// one.
func (csm *ConditionedStatusMatcher) Match(actual interface{}) (success bool, err error) {
e, ok := csm.expected.(ConditionedStatus)
if !ok {
return false, fmt.Errorf("expected value is not a ConditionedStatus: %v", csm.expected)
}
a, ok := actual.(ConditionedStatus)
if !ok {
return false, fmt.Errorf("actual value is not a ConditionedStatus: %v", actual)
}
if len(e.Conditions) != len(a.Conditions) {
return false, nil
}
for _, ce := range e.Conditions {
ca := a.Condition(ce.Type)
if ca == nil {
return false, nil
}
cm := &ConditionMatcher{ce}
ok, err := cm.Match(*ca)
if !ok {
return false, err
}
}
return true, nil
}
// FailureMessage is printed when conditioned statuses do not match.
func (csm *ConditionedStatusMatcher) FailureMessage(actual interface{}) (message string) {
return fmt.Sprintf("Expected\n\t%#v\nto mach, actual\n\t%#v", csm.expected, actual)
}
// NegatedFailureMessage is printed when conditioned statuses match
// unexpectedly.
func (csm *ConditionedStatusMatcher) NegatedFailureMessage(actual interface{}) (message string) {
return fmt.Sprintf("Expected\n\t%#v\nnot to mach, actual\n\t%#v", csm.expected, actual)
}
// MatchConditionedStatus returns a new gomega matcher for conditioned statuses.
func MatchConditionedStatus(expected interface{}) types.GomegaMatcher {
return &ConditionedStatusMatcher{
expected: expected,
}
}

View File

@ -0,0 +1,205 @@
/*
Copyright 2018 The Crossplane Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha1
import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// DeprecatedConditionType type for possible conditions the resource could be in.
type DeprecatedConditionType string
// Deprecated resource conditions. Please use the undeprecated equivalents for
// new APIs.
const (
// DeprecatedPending means that the resource create request has been
// received and is waiting to be fulfilled.
DeprecatedPending DeprecatedConditionType = "Pending"
// DeprecatedCreating means that the resource create request has been
// accepted and the resource is in the process of being created.
DeprecatedCreating DeprecatedConditionType = "Creating"
// DeprecatedDeleting means that the resource is in the process of being deleted.
DeprecatedDeleting DeprecatedConditionType = "Deleting"
// DeprecatedFailed means that the resource is in a failure state, for example it failed to be created.
DeprecatedFailed DeprecatedConditionType = "Failed"
// DeprecatedReady means that the resource creation has been successful and the resource is ready to
// accept requests and perform operations.
DeprecatedReady DeprecatedConditionType = "Ready"
)
// DeprecatedCondition contains details for the current condition of a managed
// resource. Please use Condition instead for new APIs.
type DeprecatedCondition struct {
Type DeprecatedConditionType
Status corev1.ConditionStatus
LastTransitionTime metav1.Time
Reason string
Message string
}
// Equal returns true if the condition is identical to the supplied condition,
// ignoring the LastTransitionTime. github.com/go-test/deep uses this method to
// test equality.
func (c DeprecatedCondition) Equal(other DeprecatedCondition) bool {
return c.Type == other.Type &&
c.Status == other.Status &&
c.Reason == other.Reason &&
c.Message == other.Message
}
// DeprecatedConditionable defines set of functionality to operate on Conditions
type DeprecatedConditionable interface {
DeprecatedCondition(DeprecatedConditionType) *DeprecatedCondition
SetDeprecatedCondition(DeprecatedCondition)
RemoveDeprecatedCondition(DeprecatedConditionType)
UnsetDeprecatedCondition(DeprecatedConditionType)
UnsetAllDeprecatedConditions()
}
// DeprecatedConditionedStatus reflects the observed state of a managed
// resource. Please use ConditionedStatus for new resources.
type DeprecatedConditionedStatus struct {
// Conditions indicate state for particular aspects of a CustomResourceDefinition
Conditions []DeprecatedCondition
}
// DeprecatedCondition returns a provider condition with the provided type if it exists.
func (c *DeprecatedConditionedStatus) DeprecatedCondition(conditionType DeprecatedConditionType) *DeprecatedCondition {
for i := range c.Conditions {
// This loop is written this way (as opposed to for i, cnd := range...)
// to avoid returning a pointer to a range variable whose content will
// change as the loop iterates.
// https://github.com/kyoh86/scopelint#whats-this
cnd := c.Conditions[i]
if cnd.Type == conditionType {
return &cnd
}
}
return nil
}
// IsDeprecatedCondition of provided type is present and set to true
func (c *DeprecatedConditionedStatus) IsDeprecatedCondition(ctype DeprecatedConditionType) bool {
condition := c.DeprecatedCondition(ctype)
return condition != nil && condition.Status == corev1.ConditionTrue
}
// IsReady returns true if the status is currently ready.
func (c *DeprecatedConditionedStatus) IsReady() bool {
return c.IsDeprecatedCondition(DeprecatedReady)
}
// IsFailed returns true if the status is currently failed.
func (c *DeprecatedConditionedStatus) IsFailed() bool {
return c.IsDeprecatedCondition(DeprecatedFailed)
}
// SetDeprecatedCondition adds/replaces the given condition in the credentials controller status.
func (c *DeprecatedConditionedStatus) SetDeprecatedCondition(condition DeprecatedCondition) {
current := c.DeprecatedCondition(condition.Type)
if current != nil && current.Equal(condition) {
return
}
newConditions := FilterOutDeprecatedCondition(c.Conditions, condition.Type)
newConditions = append(newConditions, condition)
c.Conditions = newConditions
}
// SetFailed set failed as an active condition
func (c *DeprecatedConditionedStatus) SetFailed(reason, msg string) {
c.SetDeprecatedCondition(NewDeprecatedCondition(DeprecatedFailed, reason, msg))
}
// SetReady set ready as an active condition
func (c *DeprecatedConditionedStatus) SetReady() {
c.SetDeprecatedCondition(NewDeprecatedCondition(DeprecatedReady, "", ""))
}
// SetCreating set creating as an active condition
func (c *DeprecatedConditionedStatus) SetCreating() {
c.SetDeprecatedCondition(NewDeprecatedCondition(DeprecatedCreating, "", ""))
}
// SetPending set pending as an active condition
func (c *DeprecatedConditionedStatus) SetPending() {
c.SetDeprecatedCondition(NewDeprecatedCondition(DeprecatedPending, "", ""))
}
// SetDeleting set deleting as an active condition
func (c *DeprecatedConditionedStatus) SetDeleting() {
c.SetDeprecatedCondition(NewDeprecatedCondition(DeprecatedDeleting, "", ""))
}
// UnsetDeprecatedCondition set condition status to false with the given type - if found.
func (c *DeprecatedConditionedStatus) UnsetDeprecatedCondition(conditionType DeprecatedConditionType) {
current := c.DeprecatedCondition(conditionType)
if current != nil && current.Status == corev1.ConditionTrue {
current.Status = corev1.ConditionFalse
c.SetDeprecatedCondition(*current)
}
}
// UnsetAllDeprecatedConditions set conditions status to false on all conditions
func (c *DeprecatedConditionedStatus) UnsetAllDeprecatedConditions() {
for i := range c.Conditions {
c.Conditions[i].Status = corev1.ConditionFalse
}
}
// RemoveCondition removes the condition with the provided type from the credentials controller status.
func (c *DeprecatedConditionedStatus) RemoveCondition(condType DeprecatedConditionType) {
c.Conditions = FilterOutDeprecatedCondition(c.Conditions, condType)
}
// RemoveAllConditions removes all condition entries
func (c *DeprecatedConditionedStatus) RemoveAllConditions() {
c.Conditions = []DeprecatedCondition{}
}
// NewDeprecatedCondition creates a new resource condition.
func NewDeprecatedCondition(condType DeprecatedConditionType, reason, msg string) DeprecatedCondition {
return DeprecatedCondition{
Type: condType,
Status: corev1.ConditionTrue,
LastTransitionTime: metav1.Now(),
Reason: reason,
Message: msg,
}
}
// NewReadyDeprecatedCondition sets and activates Ready status condition
func NewReadyDeprecatedCondition() DeprecatedCondition {
return NewDeprecatedCondition(DeprecatedReady, "", "")
}
// FilterOutDeprecatedCondition returns a new slice of credentials controller conditions
// without conditions with the provided type.
func FilterOutDeprecatedCondition(conditions []DeprecatedCondition, condType DeprecatedConditionType) []DeprecatedCondition {
var newConditions []DeprecatedCondition // nolint:prealloc
for _, c := range conditions {
if c.Type == condType {
continue
}
newConditions = append(newConditions, c)
}
return newConditions
}

View File

@ -0,0 +1,43 @@
/*
Copyright 2018 The Crossplane Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha1
import (
"testing"
. "github.com/onsi/gomega"
)
func TestDeprecatedConditionedStatus_UnsetAllDeprecatedConditions(t *testing.T) {
g := NewGomegaWithT(t)
cs := &DeprecatedConditionedStatus{}
cs.SetReady()
g.Expect(cs.IsReady()).To(BeTrue())
cs.UnsetAllDeprecatedConditions()
g.Expect(cs.IsReady()).To(BeFalse())
cs.SetFailed("foo", "bar")
g.Expect(cs.IsFailed()).To(BeTrue())
g.Expect(cs.IsReady()).To(BeFalse())
cs.UnsetAllDeprecatedConditions()
cs.SetReady()
g.Expect(cs.IsFailed()).To(BeFalse())
g.Expect(cs.IsReady()).To(BeTrue())
}

View File

@ -0,0 +1,129 @@
/*
Copyright 2018 The Crossplane Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha1
import (
"fmt"
"github.com/onsi/gomega"
"github.com/onsi/gomega/types"
)
// AssertConditions verifies the given expected conditions against the given actual conditions.
// This is helpful for unit tests since the DeprecatedCondition type has a timestamp that makes full
// object equal comparisons difficult.
// TODO: consider to use DeprecatedConditionMatcher or DeprecatedConditionedStatusMatcher instead
func AssertConditions(g *gomega.GomegaWithT, expected []DeprecatedCondition, actual DeprecatedConditionedStatus) {
for _, ec := range expected {
// find the condition of the matching type, it should exist
ac := actual.DeprecatedCondition(ec.Type)
g.Expect(ac).NotTo(gomega.BeNil())
g.Expect(*ac).To(MatchDeprecatedCondition(ec))
}
}
// DeprecatedConditionMatcher is a gomega matcher for Conditions.
// +k8s:deepcopy-gen=false
type DeprecatedConditionMatcher struct {
expected interface{}
}
// Match returns true if the underlying condition matches the supplied one.
func (cm *DeprecatedConditionMatcher) Match(actual interface{}) (success bool, err error) {
e, ok := cm.expected.(DeprecatedCondition)
if !ok {
return false, fmt.Errorf("expected value is not a DeprecatedCondition: %v", cm.expected)
}
a, ok := actual.(DeprecatedCondition)
if !ok {
return false, fmt.Errorf("actual value is not a DeprecatedCondition: %v", actual)
}
return e.Equal(a), nil
}
// FailureMessage is printed when conditions do not match.
func (cm *DeprecatedConditionMatcher) FailureMessage(actual interface{}) (message string) {
return fmt.Sprintf("Expected\n\t%#v\nto match, actual\n\t%#v", cm.expected, actual)
}
// NegatedFailureMessage is printed when conditions match unexpectedly.
func (cm *DeprecatedConditionMatcher) NegatedFailureMessage(actual interface{}) (message string) {
return fmt.Sprintf("Expected\n\t%#v\nnot to match, actual\n\t%#v", cm.expected, actual)
}
// MatchDeprecatedCondition returns a new gomga matcher for Conditions.
func MatchDeprecatedCondition(expected interface{}) types.GomegaMatcher {
return &DeprecatedConditionMatcher{
expected: expected,
}
}
// DeprecatedConditionedStatusMatcher is a gomega matcher for DeprecatedConditionedStatuses.
// +k8s:deepcopy-gen=false
type DeprecatedConditionedStatusMatcher struct {
expected interface{}
}
// Match returns true if the underlying conditioned status matches the supplied
// one.
func (csm *DeprecatedConditionedStatusMatcher) Match(actual interface{}) (success bool, err error) {
e, ok := csm.expected.(DeprecatedConditionedStatus)
if !ok {
return false, fmt.Errorf("expected value is not a DeprecatedConditionedStatus: %v", csm.expected)
}
a, ok := actual.(DeprecatedConditionedStatus)
if !ok {
return false, fmt.Errorf("actual value is not a DeprecatedConditionedStatus: %v", actual)
}
if len(e.Conditions) != len(a.Conditions) {
return false, nil
}
for _, ce := range e.Conditions {
ca := a.DeprecatedCondition(ce.Type)
if ca == nil {
return false, nil
}
cm := &DeprecatedConditionMatcher{ce}
ok, err := cm.Match(*ca)
if !ok {
return false, err
}
}
return true, nil
}
// FailureMessage is printed when conditioned statuses do not match.
func (csm *DeprecatedConditionedStatusMatcher) FailureMessage(actual interface{}) (message string) {
return fmt.Sprintf("Expected\n\t%#v\nto mach, actual\n\t%#v", csm.expected, actual)
}
// NegatedFailureMessage is printed when conditioned statuses match
// unexpectedly.
func (csm *DeprecatedConditionedStatusMatcher) NegatedFailureMessage(actual interface{}) (message string) {
return fmt.Sprintf("Expected\n\t%#v\nnot to mach, actual\n\t%#v", csm.expected, actual)
}
// MatchDeprecatedConditionedStatus returns a new gomega matcher for conditioned statuses.
func MatchDeprecatedConditionedStatus(expected interface{}) types.GomegaMatcher {
return &DeprecatedConditionedStatusMatcher{
expected: expected,
}
}

View File

@ -128,7 +128,7 @@ func (r *ResourceClass) ObjectReference() *corev1.ObjectReference {
// ResourceClaimStatus represents the status of a resource claim
type ResourceClaimStatus struct {
ConditionedStatus
DeprecatedConditionedStatus
BindingStatusPhase
// Provisioner is the driver that was used to provision the concrete resource

View File

@ -79,10 +79,50 @@ func (in *ConditionedStatus) DeepCopy() *ConditionedStatus {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DeprecatedCondition) DeepCopyInto(out *DeprecatedCondition) {
*out = *in
in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeprecatedCondition.
func (in *DeprecatedCondition) DeepCopy() *DeprecatedCondition {
if in == nil {
return nil
}
out := new(DeprecatedCondition)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DeprecatedConditionedStatus) DeepCopyInto(out *DeprecatedConditionedStatus) {
*out = *in
if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions
*out = make([]DeprecatedCondition, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeprecatedConditionedStatus.
func (in *DeprecatedConditionedStatus) DeepCopy() *DeprecatedConditionedStatus {
if in == nil {
return nil
}
out := new(DeprecatedConditionedStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ResourceClaimStatus) DeepCopyInto(out *ResourceClaimStatus) {
*out = *in
in.ConditionedStatus.DeepCopyInto(&out.ConditionedStatus)
in.DeprecatedConditionedStatus.DeepCopyInto(&out.DeprecatedConditionedStatus)
out.BindingStatusPhase = in.BindingStatusPhase
out.CredentialsSecretRef = in.CredentialsSecretRef
return

View File

@ -76,7 +76,7 @@ type ExtensionRequestSpec struct {
// ExtensionRequestStatus defines the observed state of ExtensionRequest
type ExtensionRequestStatus struct {
corev1alpha1.ConditionedStatus
corev1alpha1.DeprecatedConditionedStatus
InstallJob *corev1.ObjectReference `json:"installJob,omitempty"`
ExtensionRecord *corev1.ObjectReference `json:"extensionRecord,omitempty"`
}
@ -130,7 +130,7 @@ type ExtensionSpec struct {
// ExtensionStatus defines the observed state of Extension
type ExtensionStatus struct {
corev1alpha1.ConditionedStatus
corev1alpha1.DeprecatedConditionedStatus
ControllerRef *corev1.ObjectReference `json:"controllerRef,omitempty"`
}

View File

@ -288,7 +288,7 @@ func (in *ExtensionRequestSpec) DeepCopy() *ExtensionRequestSpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ExtensionRequestStatus) DeepCopyInto(out *ExtensionRequestStatus) {
*out = *in
in.ConditionedStatus.DeepCopyInto(&out.ConditionedStatus)
in.DeprecatedConditionedStatus.DeepCopyInto(&out.DeprecatedConditionedStatus)
if in.InstallJob != nil {
in, out := &in.InstallJob, &out.InstallJob
*out = new(corev1.ObjectReference)
@ -335,7 +335,7 @@ func (in *ExtensionSpec) DeepCopy() *ExtensionSpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ExtensionStatus) DeepCopyInto(out *ExtensionStatus) {
*out = *in
in.ConditionedStatus.DeepCopyInto(&out.ConditionedStatus)
in.DeprecatedConditionedStatus.DeepCopyInto(&out.DeprecatedConditionedStatus)
if in.ControllerRef != nil {
in, out := &in.ControllerRef, &out.ControllerRef
*out = new(corev1.ObjectReference)

View File

@ -111,7 +111,7 @@ type CloudMemorystoreInstanceSpec struct {
// CloudMemorystoreInstanceStatus defines the observed state of CloudMemorystoreInstance
type CloudMemorystoreInstanceStatus struct {
corev1alpha1.ConditionedStatus
corev1alpha1.DeprecatedConditionedStatus
corev1alpha1.BindingStatusPhase
State string `json:"state,omitempty"`
Message string `json:"message,omitempty"`

View File

@ -123,7 +123,7 @@ func (in *CloudMemorystoreInstanceSpec) DeepCopy() *CloudMemorystoreInstanceSpec
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CloudMemorystoreInstanceStatus) DeepCopyInto(out *CloudMemorystoreInstanceStatus) {
*out = *in
in.ConditionedStatus.DeepCopyInto(&out.ConditionedStatus)
in.DeprecatedConditionedStatus.DeepCopyInto(&out.DeprecatedConditionedStatus)
out.BindingStatusPhase = in.BindingStatusPhase
return
}

View File

@ -100,7 +100,7 @@ type GKEClusterSpec struct {
// GKEClusterStatus represents the status of a GKE cluster.
type GKEClusterStatus struct {
v1alpha1.ConditionedStatus
v1alpha1.DeprecatedConditionedStatus
v1alpha1.BindingStatusPhase
ClusterName string `json:"clusterName"`
Endpoint string `json:"endpoint"`

View File

@ -162,7 +162,7 @@ func (in *GKEClusterSpec) DeepCopy() *GKEClusterSpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *GKEClusterStatus) DeepCopyInto(out *GKEClusterStatus) {
*out = *in
in.ConditionedStatus.DeepCopyInto(&out.ConditionedStatus)
in.DeprecatedConditionedStatus.DeepCopyInto(&out.DeprecatedConditionedStatus)
out.BindingStatusPhase = in.BindingStatusPhase
return
}

View File

@ -75,7 +75,7 @@ type CloudsqlInstanceSpec struct {
// CloudsqlInstanceStatus defines the observed state of CloudsqlInstance
type CloudsqlInstanceStatus struct {
corev1alpha1.ConditionedStatus
corev1alpha1.DeprecatedConditionedStatus
corev1alpha1.BindingStatusPhase
State string `json:"state,omitempty"`
Message string `json:"message,omitempty"`

View File

@ -116,7 +116,7 @@ func (in *CloudsqlInstanceSpec) DeepCopy() *CloudsqlInstanceSpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CloudsqlInstanceStatus) DeepCopyInto(out *CloudsqlInstanceStatus) {
*out = *in
in.ConditionedStatus.DeepCopyInto(&out.ConditionedStatus)
in.DeprecatedConditionedStatus.DeepCopyInto(&out.DeprecatedConditionedStatus)
out.BindingStatusPhase = in.BindingStatusPhase
return
}

View File

@ -784,7 +784,7 @@ type BucketSpec struct {
type BucketStatus struct {
BucketOutputAttrs `json:"attributes"`
corev1alpha1.ConditionedStatus
corev1alpha1.DeprecatedConditionedStatus
corev1alpha1.BindingStatusPhase
ConnectionSecretRef corev1.LocalObjectReference `json:"connectionSecretRef,omitempty"`
}

View File

@ -1067,7 +1067,7 @@ func TestBucket_IsAvailable(t *testing.T) {
bReadyAndFailed.Status.SetFailed("", "")
bNotReadyAndFailed := bReadyAndFailed
bNotReadyAndFailed.Status.UnsetCondition(v1alpha1.Ready)
bNotReadyAndFailed.Status.UnsetDeprecatedCondition(v1alpha1.DeprecatedReady)
tests := []struct {
name string

View File

@ -246,7 +246,7 @@ func (in *BucketSpecAttrs) DeepCopy() *BucketSpecAttrs {
func (in *BucketStatus) DeepCopyInto(out *BucketStatus) {
*out = *in
in.BucketOutputAttrs.DeepCopyInto(&out.BucketOutputAttrs)
in.ConditionedStatus.DeepCopyInto(&out.ConditionedStatus)
in.DeprecatedConditionedStatus.DeepCopyInto(&out.DeprecatedConditionedStatus)
out.BindingStatusPhase = in.BindingStatusPhase
out.ConnectionSecretRef = in.ConnectionSecretRef
return

View File

@ -39,7 +39,7 @@ type ProviderSpec struct {
// ProviderStatus represents the status of a GCP Provider.
type ProviderStatus struct {
corev1alpha1.ConditionedStatus
corev1alpha1.DeprecatedConditionedStatus
}
// +genclient

View File

@ -109,7 +109,7 @@ func (in *ProviderSpec) DeepCopy() *ProviderSpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ProviderStatus) DeepCopyInto(out *ProviderStatus) {
*out = *in
in.ConditionedStatus.DeepCopyInto(&out.ConditionedStatus)
in.DeprecatedConditionedStatus.DeepCopyInto(&out.DeprecatedConditionedStatus)
return
}

View File

@ -79,7 +79,7 @@ type KubernetesApplicationResourceTemplate struct {
// KubernetesApplicationStatus represents the status of a Kubernetes
// application.
type KubernetesApplicationStatus struct {
corev1alpha1.ConditionedStatus
corev1alpha1.DeprecatedConditionedStatus
// State of the application.
State KubernetesApplicationState `json:"state,omitempty"`
@ -181,7 +181,7 @@ func (s *RemoteStatus) UnmarshalJSON(data []byte) error {
// KubernetesApplicationResourceStatus represents the status of a Kubernetes
// application resource.
type KubernetesApplicationResourceStatus struct {
corev1alpha1.ConditionedStatus
corev1alpha1.DeprecatedConditionedStatus
// State of the application.
State KubernetesApplicationResourceState `json:"state,omitempty"`

View File

@ -177,7 +177,7 @@ func (in *KubernetesApplicationResourceSpec) DeepCopy() *KubernetesApplicationRe
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *KubernetesApplicationResourceStatus) DeepCopyInto(out *KubernetesApplicationResourceStatus) {
*out = *in
in.ConditionedStatus.DeepCopyInto(&out.ConditionedStatus)
in.DeprecatedConditionedStatus.DeepCopyInto(&out.DeprecatedConditionedStatus)
if in.Cluster != nil {
in, out := &in.Cluster, &out.Cluster
*out = new(v1.ObjectReference)
@ -255,7 +255,7 @@ func (in *KubernetesApplicationSpec) DeepCopy() *KubernetesApplicationSpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *KubernetesApplicationStatus) DeepCopyInto(out *KubernetesApplicationStatus) {
*out = *in
in.ConditionedStatus.DeepCopyInto(&out.ConditionedStatus)
in.DeprecatedConditionedStatus.DeepCopyInto(&out.DeprecatedConditionedStatus)
if in.Cluster != nil {
in, out := &in.Cluster, &out.Cluster
*out = new(v1.ObjectReference)