diff --git a/apis/storage/v1alpha1/volume_types.go b/apis/storage/v1alpha1/volume_types.go index a5dce68f8..b119c1574 100644 --- a/apis/storage/v1alpha1/volume_types.go +++ b/apis/storage/v1alpha1/volume_types.go @@ -70,28 +70,23 @@ type ClaimReference struct { type VolumeStatus struct { // State represents the infrastructure state of a Volume. State VolumeState `json:"state,omitempty"` - // Conditions represents different status aspects of a Volume. - Conditions []VolumeCondition `json:"conditions,omitempty"` + // LastStateTransitionTime is the last time the State transitioned between values. + LastStateTransitionTime *metav1.Time `json:"lastStateTransitionTime,omitempty"` + + // Phase represents the binding phase of a Volume. + Phase VolumePhase `json:"phase,omitempty"` + // LastPhaseTransitionTime is the last time the Phase transitioned between values. + LastPhaseTransitionTime *metav1.Time `json:"lastPhaseTransitionTime,omitempty"` + // Access specifies how to access a Volume. // This is set by the volume provider when the volume is provisioned. Access *VolumeAccess `json:"access,omitempty"` } -const ( - // VolumeBoundReasonUnbound is used for any Volume that is not bound. - VolumeBoundReasonUnbound = "Unbound" - // VolumeBoundReasonPending is used for any Volume that is not available. - VolumeBoundReasonPending = "Pending" - // VolumeBoundReasonBound is used for any Volume that is bound. - VolumeBoundReasonBound = "Bound" -) - -// VolumePhase is the binding phase of a volume. +// VolumePhase represents the binding phase of a Volume. type VolumePhase string const ( - // VolumePhaseUnknown is used for any Volume for which it is unknown whether it can be used for binding. - VolumePhaseUnknown VolumePhase = "Unknown" // VolumePhaseUnbound is used for any Volume that not bound. VolumePhaseUnbound VolumePhase = "Unbound" // VolumePhasePending is used for any Volume that is currently awaiting binding. @@ -100,45 +95,10 @@ const ( VolumePhaseBound VolumePhase = "Bound" ) -func FindVolumeCondition(conditions []VolumeCondition, conditionType VolumeConditionType) (VolumeCondition, int) { - for i, condition := range conditions { - if condition.Type == conditionType { - return condition, i - } - } - return VolumeCondition{}, -1 -} - -func VolumePhaseFromBoundStatusAndReason(status corev1.ConditionStatus, reason string) VolumePhase { - switch { - case status == corev1.ConditionFalse && reason == VolumeBoundReasonPending: - return VolumePhasePending - case status == corev1.ConditionFalse && reason == VolumeBoundReasonUnbound: - return VolumePhaseUnbound - case status == corev1.ConditionTrue: - return VolumePhaseBound - default: - return VolumePhaseUnknown - } -} - -func GetVolumePhaseAndCondition(volume *Volume) (VolumePhase, VolumeCondition, int) { - cond, idx := FindVolumeCondition(volume.Status.Conditions, VolumeBound) - phase := VolumePhaseFromBoundStatusAndReason(cond.Status, cond.Reason) - return phase, cond, idx -} - -func GetVolumePhase(volume *Volume) VolumePhase { - phase, _, _ := GetVolumePhaseAndCondition(volume) - return phase -} - -// VolumeState is a possible state a volume can be in. +// VolumeState represents the infrastructure state of a Volume. type VolumeState string const ( - // VolumeStateUnknown reports whether a Volume is in an unknown state. - VolumeStateUnknown VolumeState = "Unknown" // VolumeStatePending reports whether a Volume is about to be ready. VolumeStatePending VolumeState = "Pending" // VolumeStateAvailable reports whether a Volume is available to be used. @@ -147,33 +107,6 @@ const ( VolumeStateError VolumeState = "Error" ) -// VolumeConditionType is a type a VolumeCondition can have. -type VolumeConditionType string - -const ( - // VolumeSynced represents the condition of a volume being synced with its backing resources - VolumeSynced VolumeConditionType = "Synced" - - // VolumeBound represents the binding state of a Volume. - VolumeBound VolumeConditionType = "Bound" -) - -// VolumeCondition is one of the conditions of a volume. -type VolumeCondition struct { - // Type is the type of the condition. - Type VolumeConditionType `json:"type"` - // Status is the status of the condition. - Status corev1.ConditionStatus `json:"status"` - // Reason is a machine-readable indication of why the condition is in a certain state. - Reason string `json:"reason"` - // Message is a human-readable explanation of why the condition has a certain reason / state. - Message string `json:"message"` - // ObservedGeneration represents the .metadata.generation that the condition was set based upon. - ObservedGeneration int64 `json:"observedGeneration,omitempty"` - // LastTransitionTime is the last time the status of a condition has transitioned from one state to another. - LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"` -} - // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +genclient diff --git a/apis/storage/v1alpha1/zz_generated.conversion.go b/apis/storage/v1alpha1/zz_generated.conversion.go index e04114970..6b888859d 100644 --- a/apis/storage/v1alpha1/zz_generated.conversion.go +++ b/apis/storage/v1alpha1/zz_generated.conversion.go @@ -129,16 +129,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*VolumeCondition)(nil), (*storage.VolumeCondition)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1alpha1_VolumeCondition_To_storage_VolumeCondition(a.(*VolumeCondition), b.(*storage.VolumeCondition), scope) - }); err != nil { - return err - } - if err := s.AddGeneratedConversionFunc((*storage.VolumeCondition)(nil), (*VolumeCondition)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_storage_VolumeCondition_To_v1alpha1_VolumeCondition(a.(*storage.VolumeCondition), b.(*VolumeCondition), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*VolumeList)(nil), (*storage.VolumeList)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1alpha1_VolumeList_To_storage_VolumeList(a.(*VolumeList), b.(*storage.VolumeList), scope) }); err != nil { @@ -444,36 +434,6 @@ func Convert_storage_VolumeClassList_To_v1alpha1_VolumeClassList(in *storage.Vol return autoConvert_storage_VolumeClassList_To_v1alpha1_VolumeClassList(in, out, s) } -func autoConvert_v1alpha1_VolumeCondition_To_storage_VolumeCondition(in *VolumeCondition, out *storage.VolumeCondition, s conversion.Scope) error { - out.Type = storage.VolumeConditionType(in.Type) - out.Status = corev1.ConditionStatus(in.Status) - out.Reason = in.Reason - out.Message = in.Message - out.ObservedGeneration = in.ObservedGeneration - out.LastTransitionTime = in.LastTransitionTime - return nil -} - -// Convert_v1alpha1_VolumeCondition_To_storage_VolumeCondition is an autogenerated conversion function. -func Convert_v1alpha1_VolumeCondition_To_storage_VolumeCondition(in *VolumeCondition, out *storage.VolumeCondition, s conversion.Scope) error { - return autoConvert_v1alpha1_VolumeCondition_To_storage_VolumeCondition(in, out, s) -} - -func autoConvert_storage_VolumeCondition_To_v1alpha1_VolumeCondition(in *storage.VolumeCondition, out *VolumeCondition, s conversion.Scope) error { - out.Type = VolumeConditionType(in.Type) - out.Status = corev1.ConditionStatus(in.Status) - out.Reason = in.Reason - out.Message = in.Message - out.ObservedGeneration = in.ObservedGeneration - out.LastTransitionTime = in.LastTransitionTime - return nil -} - -// Convert_storage_VolumeCondition_To_v1alpha1_VolumeCondition is an autogenerated conversion function. -func Convert_storage_VolumeCondition_To_v1alpha1_VolumeCondition(in *storage.VolumeCondition, out *VolumeCondition, s conversion.Scope) error { - return autoConvert_storage_VolumeCondition_To_v1alpha1_VolumeCondition(in, out, s) -} - func autoConvert_v1alpha1_VolumeList_To_storage_VolumeList(in *VolumeList, out *storage.VolumeList, s conversion.Scope) error { out.ListMeta = in.ListMeta out.Items = *(*[]storage.Volume)(unsafe.Pointer(&in.Items)) @@ -668,7 +628,9 @@ func Convert_storage_VolumeSpec_To_v1alpha1_VolumeSpec(in *storage.VolumeSpec, o func autoConvert_v1alpha1_VolumeStatus_To_storage_VolumeStatus(in *VolumeStatus, out *storage.VolumeStatus, s conversion.Scope) error { out.State = storage.VolumeState(in.State) - out.Conditions = *(*[]storage.VolumeCondition)(unsafe.Pointer(&in.Conditions)) + out.LastStateTransitionTime = (*v1.Time)(unsafe.Pointer(in.LastStateTransitionTime)) + out.Phase = storage.VolumePhase(in.Phase) + out.LastPhaseTransitionTime = (*v1.Time)(unsafe.Pointer(in.LastPhaseTransitionTime)) out.Access = (*storage.VolumeAccess)(unsafe.Pointer(in.Access)) return nil } @@ -680,7 +642,9 @@ func Convert_v1alpha1_VolumeStatus_To_storage_VolumeStatus(in *VolumeStatus, out func autoConvert_storage_VolumeStatus_To_v1alpha1_VolumeStatus(in *storage.VolumeStatus, out *VolumeStatus, s conversion.Scope) error { out.State = VolumeState(in.State) - out.Conditions = *(*[]VolumeCondition)(unsafe.Pointer(&in.Conditions)) + out.LastStateTransitionTime = (*v1.Time)(unsafe.Pointer(in.LastStateTransitionTime)) + out.Phase = VolumePhase(in.Phase) + out.LastPhaseTransitionTime = (*v1.Time)(unsafe.Pointer(in.LastPhaseTransitionTime)) out.Access = (*VolumeAccess)(unsafe.Pointer(in.Access)) return nil } diff --git a/apis/storage/v1alpha1/zz_generated.deepcopy.go b/apis/storage/v1alpha1/zz_generated.deepcopy.go index c933e20ab..41309d2ec 100644 --- a/apis/storage/v1alpha1/zz_generated.deepcopy.go +++ b/apis/storage/v1alpha1/zz_generated.deepcopy.go @@ -268,23 +268,6 @@ func (in *VolumeClassList) DeepCopyObject() runtime.Object { return nil } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *VolumeCondition) DeepCopyInto(out *VolumeCondition) { - *out = *in - in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeCondition. -func (in *VolumeCondition) DeepCopy() *VolumeCondition { - if in == nil { - return nil - } - out := new(VolumeCondition) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VolumeList) DeepCopyInto(out *VolumeList) { *out = *in @@ -501,12 +484,13 @@ func (in *VolumeSpec) DeepCopy() *VolumeSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VolumeStatus) DeepCopyInto(out *VolumeStatus) { *out = *in - if in.Conditions != nil { - in, out := &in.Conditions, &out.Conditions - *out = make([]VolumeCondition, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } + if in.LastStateTransitionTime != nil { + in, out := &in.LastStateTransitionTime, &out.LastStateTransitionTime + *out = (*in).DeepCopy() + } + if in.LastPhaseTransitionTime != nil { + in, out := &in.LastPhaseTransitionTime, &out.LastPhaseTransitionTime + *out = (*in).DeepCopy() } if in.Access != nil { in, out := &in.Access, &out.Access diff --git a/apis/storage/volume_types.go b/apis/storage/volume_types.go index 54d665689..9812df9e9 100644 --- a/apis/storage/volume_types.go +++ b/apis/storage/volume_types.go @@ -70,28 +70,23 @@ type ClaimReference struct { type VolumeStatus struct { // State represents the infrastructure state of a Volume. State VolumeState - // Conditions represents different status aspects of a Volume. - Conditions []VolumeCondition + // LastStateTransitionTime is the last time the State transitioned between values. + LastStateTransitionTime *metav1.Time + + // Phase represents the binding phase of a Volume. + Phase VolumePhase + // LastPhaseTransitionTime is the last time the Phase transitioned between values. + LastPhaseTransitionTime *metav1.Time + // Access specifies how to access a Volume. // This is set by the volume provider when the volume is provisioned. Access *VolumeAccess } -const ( - // VolumeBoundReasonUnbound is used for any Volume that is not bound. - VolumeBoundReasonUnbound = "Unbound" - // VolumeBoundReasonPending is used for any Volume that is not available. - VolumeBoundReasonPending = "Pending" - // VolumeBoundReasonBound is used for any Volume that is bound. - VolumeBoundReasonBound = "Bound" -) - -// VolumePhase is the binding phase of a volume. +// VolumePhase represents the binding phase of a Volume. type VolumePhase string const ( - // VolumePhaseUnknown is used for any Volume for which it is unknown whether it can be used for binding. - VolumePhaseUnknown VolumePhase = "Unknown" // VolumePhaseUnbound is used for any Volume that not bound. VolumePhaseUnbound VolumePhase = "Unbound" // VolumePhasePending is used for any Volume that is currently awaiting binding. @@ -100,45 +95,10 @@ const ( VolumePhaseBound VolumePhase = "Bound" ) -func FindVolumeCondition(conditions []VolumeCondition, conditionType VolumeConditionType) (VolumeCondition, int) { - for i, condition := range conditions { - if condition.Type == conditionType { - return condition, i - } - } - return VolumeCondition{}, -1 -} - -func VolumePhaseFromBoundStatusAndReason(status corev1.ConditionStatus, reason string) VolumePhase { - switch { - case status == corev1.ConditionFalse && reason == VolumeBoundReasonPending: - return VolumePhasePending - case status == corev1.ConditionFalse && reason == VolumeBoundReasonUnbound: - return VolumePhaseUnbound - case status == corev1.ConditionTrue: - return VolumePhaseBound - default: - return VolumePhaseUnknown - } -} - -func GetVolumePhaseAndCondition(volume *Volume) (VolumePhase, VolumeCondition, int) { - cond, idx := FindVolumeCondition(volume.Status.Conditions, VolumeBound) - phase := VolumePhaseFromBoundStatusAndReason(cond.Status, cond.Reason) - return phase, cond, idx -} - -func GetVolumePhase(volume *Volume) VolumePhase { - phase, _, _ := GetVolumePhaseAndCondition(volume) - return phase -} - -// VolumeState is a possible state a volume can be in. +// VolumeState represents the infrastructure state of a Volume. type VolumeState string const ( - // VolumeStateUnknown reports whether a Volume is in an unknown state. - VolumeStateUnknown VolumeState = "Unknown" // VolumeStateAvailable reports whether the volume is available to be used. VolumeStateAvailable VolumeState = "Available" // VolumeStatePending reports whether the volume is about to be ready. @@ -147,33 +107,6 @@ const ( VolumeStateError VolumeState = "Error" ) -// VolumeConditionType is a type a VolumeCondition can have. -type VolumeConditionType string - -const ( - // VolumeSynced represents the condition of a volume being synced with its backing resources - VolumeSynced VolumeConditionType = "Synced" - - // VolumeBound represents the binding state of a Volume. - VolumeBound VolumeConditionType = "Bound" -) - -// VolumeCondition is one of the conditions of a volume. -type VolumeCondition struct { - // Type is the type of the condition. - Type VolumeConditionType - // Status is the status of the condition. - Status corev1.ConditionStatus - // Reason is a machine-readable indication of why the condition is in a certain state. - Reason string - // Message is a human-readable explanation of why the condition has a certain reason / state. - Message string - // ObservedGeneration represents the .metadata.generation that the condition was set based upon. - ObservedGeneration int64 - // LastTransitionTime is the last time the status of a condition has transitioned from one state to another. - LastTransitionTime metav1.Time -} - // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +genclient diff --git a/apis/storage/zz_generated.deepcopy.go b/apis/storage/zz_generated.deepcopy.go index 631ba90db..78f2daf29 100644 --- a/apis/storage/zz_generated.deepcopy.go +++ b/apis/storage/zz_generated.deepcopy.go @@ -268,23 +268,6 @@ func (in *VolumeClassList) DeepCopyObject() runtime.Object { return nil } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *VolumeCondition) DeepCopyInto(out *VolumeCondition) { - *out = *in - in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeCondition. -func (in *VolumeCondition) DeepCopy() *VolumeCondition { - if in == nil { - return nil - } - out := new(VolumeCondition) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VolumeList) DeepCopyInto(out *VolumeList) { *out = *in @@ -501,12 +484,13 @@ func (in *VolumeSpec) DeepCopy() *VolumeSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VolumeStatus) DeepCopyInto(out *VolumeStatus) { *out = *in - if in.Conditions != nil { - in, out := &in.Conditions, &out.Conditions - *out = make([]VolumeCondition, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } + if in.LastStateTransitionTime != nil { + in, out := &in.LastStateTransitionTime, &out.LastStateTransitionTime + *out = (*in).DeepCopy() + } + if in.LastPhaseTransitionTime != nil { + in, out := &in.LastPhaseTransitionTime, &out.LastPhaseTransitionTime + *out = (*in).DeepCopy() } if in.Access != nil { in, out := &in.Access, &out.Access diff --git a/controllers/storage/volume_controller.go b/controllers/storage/volume_controller.go index e1ca9e9c4..38c66f141 100644 --- a/controllers/storage/volume_controller.go +++ b/controllers/storage/volume_controller.go @@ -23,10 +23,9 @@ import ( "github.com/go-logr/logr" "github.com/onmetal/controller-utils/clientutils" - "github.com/onmetal/controller-utils/conditionutils" storagev1alpha1 "github.com/onmetal/onmetal-api/apis/storage/v1alpha1" apiequality "github.com/onmetal/onmetal-api/equality" - corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" @@ -38,15 +37,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/source" ) -var ( - accessor = conditionutils.NewAccessor(conditionutils.AccessorOptions{ - Transition: &conditionutils.FieldsTransition{ - IncludeStatus: true, - IncludeReason: true, - }, - }) -) - // VolumeReconciler reconciles a Volume object type VolumeReconciler struct { client.Client @@ -83,22 +73,18 @@ func (r *VolumeReconciler) delete(ctx context.Context, log logr.Logger, volume * return ctrl.Result{}, nil } -func (r *VolumeReconciler) boundConditionTimedOut(cond storagev1alpha1.VolumeCondition) bool { - if cond.LastTransitionTime.IsZero() { +func (r *VolumeReconciler) phaseTransitionTimedOut(timestamp *metav1.Time) bool { + if timestamp.IsZero() { return false } - return cond.LastTransitionTime.Add(r.BoundTimeout).Before(time.Now()) + return timestamp.Add(r.BoundTimeout).Before(time.Now()) } func (r *VolumeReconciler) reconcile(ctx context.Context, log logr.Logger, volume *storagev1alpha1.Volume) (ctrl.Result, error) { log.V(1).Info("Reconciling volume") if volume.Spec.ClaimRef.Name == "" { log.V(1).Info("Volume is not bound and not referencing any claim") - if err := r.patchVolumeStatus(ctx, volume, storagev1alpha1.VolumeCondition{ - Status: corev1.ConditionFalse, - Reason: storagev1alpha1.VolumeBoundReasonUnbound, - Message: "Volume is not bound.", - }); err != nil { + if err := r.patchVolumeStatus(ctx, volume, storagev1alpha1.VolumePhaseUnbound); err != nil { return ctrl.Result{}, err } @@ -121,7 +107,8 @@ func (r *VolumeReconciler) reconcile(ctx context.Context, log logr.Logger, volum volumeClaimExists := err == nil validReferences := volumeClaimExists && r.validReferences(volume, volumeClaim) - volumePhase, boundCond, _ := storagev1alpha1.GetVolumePhaseAndCondition(volume) + volumePhase := volume.Status.Phase + volumePhaseLastTransitionTime := volume.Status.LastPhaseTransitionTime volumeState := volume.Status.State bindOK := volumeState == storagev1alpha1.VolumeStateAvailable && validReferences @@ -131,22 +118,18 @@ func (r *VolumeReconciler) reconcile(ctx context.Context, log logr.Logger, volum "ValidReferences", validReferences, "VolumeState", volumeState, "VolumePhase", volumePhase, - "VolumeBoundLastTransitionTime", boundCond.LastTransitionTime, + "VolumePhaseLastTransitionTime", volumePhaseLastTransitionTime, ) switch { case bindOK: log.V(1).Info("Setting volume to bound") - if err := r.patchVolumeStatus(ctx, volume, storagev1alpha1.VolumeCondition{ - Status: corev1.ConditionTrue, - Reason: storagev1alpha1.VolumeBoundReasonBound, - Message: "Successfully bound volume to claim.", - }); err != nil { + if err := r.patchVolumeStatus(ctx, volume, storagev1alpha1.VolumePhaseBound); err != nil { return ctrl.Result{}, fmt.Errorf("error binding volume: %w", err) } log.V(1).Info("Successfully set volume to bound.") return ctrl.Result{}, nil - case !bindOK && volumePhase == storagev1alpha1.VolumePhasePending && r.boundConditionTimedOut(boundCond): + case !bindOK && volumePhase == storagev1alpha1.VolumePhasePending && r.phaseTransitionTimedOut(volumePhaseLastTransitionTime): log.V(1).Info("Bind is not ok and timed out, releasing volume") if err := r.releaseVolume(ctx, volume); err != nil { return ctrl.Result{}, fmt.Errorf("error releasing volume: %w", err) @@ -156,11 +139,7 @@ func (r *VolumeReconciler) reconcile(ctx context.Context, log logr.Logger, volum return ctrl.Result{}, nil default: log.V(1).Info("Bind is not ok and not yet timed out, setting to pending") - if err := r.patchVolumeStatus(ctx, volume, storagev1alpha1.VolumeCondition{ - Status: corev1.ConditionFalse, - Reason: storagev1alpha1.VolumeBoundReasonPending, - Message: "Volume binding is pending.", - }); err != nil { + if err := r.patchVolumeStatus(ctx, volume, storagev1alpha1.VolumePhasePending); err != nil { return ctrl.Result{}, fmt.Errorf("error setting volume to pending: %w", err) } @@ -170,8 +149,7 @@ func (r *VolumeReconciler) reconcile(ctx context.Context, log logr.Logger, volum } func (r *VolumeReconciler) requeueAfterBoundTimeout(volume *storagev1alpha1.Volume) ctrl.Result { - _, cond, _ := storagev1alpha1.GetVolumePhaseAndCondition(volume) - boundTimeoutExpirationDuration := time.Until(cond.LastTransitionTime.Add(r.BoundTimeout)).Round(time.Second) + boundTimeoutExpirationDuration := time.Until(volume.Status.LastPhaseTransitionTime.Add(r.BoundTimeout)).Round(time.Second) if boundTimeoutExpirationDuration <= 0 { return ctrl.Result{Requeue: true} } @@ -194,12 +172,15 @@ func (r *VolumeReconciler) releaseVolume(ctx context.Context, volume *storagev1a return r.Patch(ctx, volume, client.MergeFrom(baseVolume)) } -func (r *VolumeReconciler) patchVolumeStatus(ctx context.Context, volume *storagev1alpha1.Volume, boundCond storagev1alpha1.VolumeCondition) error { +func (r *VolumeReconciler) patchVolumeStatus(ctx context.Context, volume *storagev1alpha1.Volume, phase storagev1alpha1.VolumePhase) error { + now := metav1.Now() volumeBase := volume.DeepCopy() - accessor.MustUpdateSlice(&volume.Status.Conditions, string(storagev1alpha1.VolumeBound), - conditionutils.UpdateFromCondition{Condition: boundCond}, - conditionutils.UpdateObserved(volume), - ) + + if volume.Status.Phase != phase { + volume.Status.LastPhaseTransitionTime = &now + } + volume.Status.Phase = phase + return r.Status().Patch(ctx, volume, client.MergeFrom(volumeBase)) } @@ -217,11 +198,9 @@ func (r *VolumeReconciler) SetupWithManager(mgr ctrl.Manager) error { builder.WithPredicates(predicate.Funcs{ UpdateFunc: func(event event.UpdateEvent) bool { oldVolume, newVolume := event.ObjectOld.(*storagev1alpha1.Volume), event.ObjectNew.(*storagev1alpha1.Volume) - oldPhase := storagev1alpha1.GetVolumePhase(oldVolume) - newPhase := storagev1alpha1.GetVolumePhase(newVolume) return !apiequality.Semantic.DeepEqual(oldVolume.Spec, newVolume.Spec) || oldVolume.Status.State != newVolume.Status.State || - oldPhase != newPhase + oldVolume.Status.Phase != newVolume.Status.Phase }, }), ). diff --git a/controllers/storage/volume_controller_test.go b/controllers/storage/volume_controller_test.go index e30b8c3e7..77dc98272 100644 --- a/controllers/storage/volume_controller_test.go +++ b/controllers/storage/volume_controller_test.go @@ -84,13 +84,13 @@ var _ = Describe("VolumeReconciler", func() { volumeKey := client.ObjectKeyFromObject(volume) Eventually(func(g Gomega) { Expect(k8sClient.Get(ctx, volumeKey, volume)).To(Succeed(), "failed to get volume") - g.Expect(storagev1alpha1.GetVolumePhase(volume)).To(Equal(storagev1alpha1.VolumePhaseBound)) + g.Expect(volume.Status.Phase).To(Equal(storagev1alpha1.VolumePhaseBound)) }).Should(Succeed()) By("making sure the volume stays bound") Consistently(func(g Gomega) { Expect(k8sClient.Get(ctx, volumeKey, volume)).To(Succeed(), "failed to get volume") - g.Expect(storagev1alpha1.GetVolumePhase(volume)).To(Equal(storagev1alpha1.VolumePhaseBound)) + g.Expect(volume.Status.Phase).To(Equal(storagev1alpha1.VolumePhaseBound)) }).Should(Succeed()) }) @@ -110,7 +110,7 @@ var _ = Describe("VolumeReconciler", func() { Eventually(func(g Gomega) { Expect(k8sClient.Get(ctx, volumeKey, volume)).To(Succeed(), "failed to get volume") g.Expect(volume.Spec.ClaimRef.Name).To(Equal(volumeClaim.Name)) - g.Expect(storagev1alpha1.GetVolumePhase(volume)).To(Equal(storagev1alpha1.VolumePhaseBound)) + g.Expect(volume.Status.Phase).To(Equal(storagev1alpha1.VolumePhaseBound)) }).Should(Succeed()) By("deleting the volume claim") @@ -119,7 +119,7 @@ var _ = Describe("VolumeReconciler", func() { By("waiting for the volume phase to become available") Eventually(func(g Gomega) { Expect(k8sClient.Get(ctx, volumeKey, volume)).To(Succeed(), "failed to get volume") - g.Expect(storagev1alpha1.GetVolumePhase(volume)).To(Equal(storagev1alpha1.VolumePhaseUnbound)) + g.Expect(volume.Status.Phase).To(Equal(storagev1alpha1.VolumePhaseUnbound)) g.Expect(volume.Spec.ClaimRef).To(BeZero()) }).Should(Succeed()) }) diff --git a/generated/openapi/api_violations.report b/generated/openapi/api_violations.report index 11c801853..fec49c38b 100644 --- a/generated/openapi/api_violations.report +++ b/generated/openapi/api_violations.report @@ -21,7 +21,6 @@ API rule violation: list_type_missing,github.com/onmetal/onmetal-api/apis/storag API rule violation: list_type_missing,github.com/onmetal/onmetal-api/apis/storage/v1alpha1,VolumePoolStatus,AvailableVolumeClasses API rule violation: list_type_missing,github.com/onmetal/onmetal-api/apis/storage/v1alpha1,VolumePoolStatus,Conditions API rule violation: list_type_missing,github.com/onmetal/onmetal-api/apis/storage/v1alpha1,VolumeSpec,Tolerations -API rule violation: list_type_missing,github.com/onmetal/onmetal-api/apis/storage/v1alpha1,VolumeStatus,Conditions API rule violation: list_type_missing,k8s.io/api/core/v1,AvoidPods,PreferAvoidPods API rule violation: list_type_missing,k8s.io/api/core/v1,Capabilities,Add API rule violation: list_type_missing,k8s.io/api/core/v1,Capabilities,Drop diff --git a/generated/openapi/zz_generated.openapi.go b/generated/openapi/zz_generated.openapi.go index eeb37cc84..06b37adcc 100644 --- a/generated/openapi/zz_generated.openapi.go +++ b/generated/openapi/zz_generated.openapi.go @@ -108,7 +108,6 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/onmetal/onmetal-api/apis/storage/v1alpha1.VolumeClaimStatus": schema_onmetal_api_apis_storage_v1alpha1_VolumeClaimStatus(ref), "github.com/onmetal/onmetal-api/apis/storage/v1alpha1.VolumeClass": schema_onmetal_api_apis_storage_v1alpha1_VolumeClass(ref), "github.com/onmetal/onmetal-api/apis/storage/v1alpha1.VolumeClassList": schema_onmetal_api_apis_storage_v1alpha1_VolumeClassList(ref), - "github.com/onmetal/onmetal-api/apis/storage/v1alpha1.VolumeCondition": schema_onmetal_api_apis_storage_v1alpha1_VolumeCondition(ref), "github.com/onmetal/onmetal-api/apis/storage/v1alpha1.VolumeList": schema_onmetal_api_apis_storage_v1alpha1_VolumeList(ref), "github.com/onmetal/onmetal-api/apis/storage/v1alpha1.VolumePool": schema_onmetal_api_apis_storage_v1alpha1_VolumePool(ref), "github.com/onmetal/onmetal-api/apis/storage/v1alpha1.VolumePoolCondition": schema_onmetal_api_apis_storage_v1alpha1_VolumePoolCondition(ref), @@ -3454,68 +3453,6 @@ func schema_onmetal_api_apis_storage_v1alpha1_VolumeClassList(ref common.Referen } } -func schema_onmetal_api_apis_storage_v1alpha1_VolumeCondition(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "VolumeCondition is one of the conditions of a volume.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "type": { - SchemaProps: spec.SchemaProps{ - Description: "Type is the type of the condition.", - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - "status": { - SchemaProps: spec.SchemaProps{ - Description: "Status is the status of the condition.", - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - "reason": { - SchemaProps: spec.SchemaProps{ - Description: "Reason is a machine-readable indication of why the condition is in a certain state.", - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - "message": { - SchemaProps: spec.SchemaProps{ - Description: "Message is a human-readable explanation of why the condition has a certain reason / state.", - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - "observedGeneration": { - SchemaProps: spec.SchemaProps{ - Description: "ObservedGeneration represents the .metadata.generation that the condition was set based upon.", - Type: []string{"integer"}, - Format: "int64", - }, - }, - "lastTransitionTime": { - SchemaProps: spec.SchemaProps{ - Description: "LastTransitionTime is the last time the status of a condition has transitioned from one state to another.", - Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), - }, - }, - }, - Required: []string{"type", "status", "reason", "message"}, - }, - }, - Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, - } -} - func schema_onmetal_api_apis_storage_v1alpha1_VolumeList(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -3942,18 +3879,23 @@ func schema_onmetal_api_apis_storage_v1alpha1_VolumeStatus(ref common.ReferenceC Format: "", }, }, - "conditions": { + "lastStateTransitionTime": { SchemaProps: spec.SchemaProps{ - Description: "Conditions represents different status aspects of a Volume.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("github.com/onmetal/onmetal-api/apis/storage/v1alpha1.VolumeCondition"), - }, - }, - }, + Description: "LastStateTransitionTime is the last time the State transitioned between values.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "phase": { + SchemaProps: spec.SchemaProps{ + Description: "Phase represents the binding phase of a Volume.", + Type: []string{"string"}, + Format: "", + }, + }, + "lastPhaseTransitionTime": { + SchemaProps: spec.SchemaProps{ + Description: "LastPhaseTransitionTime is the last time the Phase transitioned between values.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), }, }, "access": { @@ -3966,7 +3908,7 @@ func schema_onmetal_api_apis_storage_v1alpha1_VolumeStatus(ref common.ReferenceC }, }, Dependencies: []string{ - "github.com/onmetal/onmetal-api/apis/storage/v1alpha1.VolumeAccess", "github.com/onmetal/onmetal-api/apis/storage/v1alpha1.VolumeCondition"}, + "github.com/onmetal/onmetal-api/apis/storage/v1alpha1.VolumeAccess", "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, } } diff --git a/registry/storage/volume/storage/tableconvertor.go b/registry/storage/volume/storage/tableconvertor.go index fa2c443e4..903a95f63 100644 --- a/registry/storage/volume/storage/tableconvertor.go +++ b/registry/storage/volume/storage/tableconvertor.go @@ -75,7 +75,7 @@ func (c *convertor) ConvertToTable(ctx context.Context, obj runtime.Object, tabl } else { cells = append(cells, "") } - if phase := storage.GetVolumePhase(volume); phase != "" { + if phase := volume.Status.Phase; phase != "" { cells = append(cells, phase) } else { cells = append(cells, "")