diff --git a/api/core/v1alpha2/virtual_disk.go b/api/core/v1alpha2/virtual_disk.go index 0bf8eb8b0f..7e9a8576d6 100644 --- a/api/core/v1alpha2/virtual_disk.go +++ b/api/core/v1alpha2/virtual_disk.go @@ -97,6 +97,9 @@ type VirtualDiskStatsCreationDuration struct { // Waiting time for dependent resources. // +nullable WaitingForDependencies *metav1.Duration `json:"waitingForDependencies,omitempty"` + // Waiting time in WaitForFirstConsumer phase. + // +nullable + WaitingForFirstConsumer *metav1.Duration `json:"waitingForFirstConsumer,omitempty"` // Duration of the loading into DVCR. // +nullable DVCRProvisioning *metav1.Duration `json:"dvcrProvisioning,omitempty"` diff --git a/api/core/v1alpha2/zz_generated.deepcopy.go b/api/core/v1alpha2/zz_generated.deepcopy.go index 8c6001319b..658bce8efe 100644 --- a/api/core/v1alpha2/zz_generated.deepcopy.go +++ b/api/core/v1alpha2/zz_generated.deepcopy.go @@ -1624,6 +1624,11 @@ func (in *VirtualDiskStatsCreationDuration) DeepCopyInto(out *VirtualDiskStatsCr *out = new(v1.Duration) **out = **in } + if in.WaitingForFirstConsumer != nil { + in, out := &in.WaitingForFirstConsumer, &out.WaitingForFirstConsumer + *out = new(v1.Duration) + **out = **in + } if in.DVCRProvisioning != nil { in, out := &in.DVCRProvisioning, &out.DVCRProvisioning *out = new(v1.Duration) diff --git a/crds/doc-ru-virtualdisks.yaml b/crds/doc-ru-virtualdisks.yaml index af3143b254..c24e41c64d 100644 --- a/crds/doc-ru-virtualdisks.yaml +++ b/crds/doc-ru-virtualdisks.yaml @@ -163,6 +163,9 @@ spec: waitingForDependencies: description: | Длительность ожидания зависимостей для создания виртуального диска. + waitingForFirstConsumer: + description: | + Длительность ожидания в фазе `WaitForFirstConsumer`. dvcrProvisioning: description: | Длительность загрузки в Deckhouse Virtualization Container Registry (DVCR). diff --git a/crds/virtualdisks.yaml b/crds/virtualdisks.yaml index 6f720ded92..e7be5d6457 100644 --- a/crds/virtualdisks.yaml +++ b/crds/virtualdisks.yaml @@ -460,6 +460,10 @@ spec: description: Waiting time for dependent resources. nullable: true type: string + waitingForFirstConsumer: + description: Waiting time in WaitForFirstConsumer phase. + nullable: true + type: string type: object type: object storageClassName: diff --git a/images/virtualization-artifact/pkg/controller/vd/internal/source/sources.go b/images/virtualization-artifact/pkg/controller/vd/internal/source/sources.go index b01c4b8579..b7e3efec17 100644 --- a/images/virtualization-artifact/pkg/controller/vd/internal/source/sources.go +++ b/images/virtualization-artifact/pkg/controller/vd/internal/source/sources.go @@ -175,6 +175,7 @@ func setPhaseConditionForPVCProvisioningDisk( switch { case err == nil: if dv == nil { + addWaitingForFirstConsumerDuration(vd, vdcondition.Provisioning.String()) vd.Status.Phase = v1alpha2.DiskProvisioning cb. Status(metav1.ConditionFalse). @@ -193,6 +194,7 @@ func setPhaseConditionForPVCProvisioningDisk( return nil } + addWaitingForFirstConsumerDuration(vd, vdcondition.Provisioning.String()) vd.Status.Phase = v1alpha2.DiskProvisioning cb. Status(metav1.ConditionFalse). @@ -200,6 +202,7 @@ func setPhaseConditionForPVCProvisioningDisk( Message("Import is in the process of provisioning to PVC.") return nil case errors.Is(err, service.ErrDataVolumeNotRunning): + addWaitingForFirstConsumerDuration(vd, vdcondition.ProvisioningFailed.String()) vd.Status.Phase = v1alpha2.DiskFailed cb. Status(metav1.ConditionFalse). @@ -211,6 +214,31 @@ func setPhaseConditionForPVCProvisioningDisk( } } +func addWaitingForFirstConsumerDuration(vd *v1alpha2.VirtualDisk, nextReason string) { + readyCondition, ok := conditions.GetCondition(vdcondition.ReadyType, vd.Status.Conditions) + if !ok || readyCondition.Reason != vdcondition.WaitingForFirstConsumer.String() || nextReason == vdcondition.WaitingForFirstConsumer.String() { + return + } + + if readyCondition.LastTransitionTime.IsZero() { + return + } + + wffcDuration := time.Since(readyCondition.LastTransitionTime.Time).Truncate(time.Second) + if wffcDuration <= 0 { + return + } + + if vd.Status.Stats.CreationDuration.WaitingForFirstConsumer == nil { + vd.Status.Stats.CreationDuration.WaitingForFirstConsumer = &metav1.Duration{ + Duration: wffcDuration, + } + return + } + + vd.Status.Stats.CreationDuration.WaitingForFirstConsumer.Duration += wffcDuration +} + func setPhaseConditionFromPodError( ctx context.Context, podErr error, diff --git a/images/virtualization-artifact/pkg/controller/vd/internal/stats.go b/images/virtualization-artifact/pkg/controller/vd/internal/stats.go index b2532f2bff..e9e56f3d33 100644 --- a/images/virtualization-artifact/pkg/controller/vd/internal/stats.go +++ b/images/virtualization-artifact/pkg/controller/vd/internal/stats.go @@ -73,6 +73,9 @@ func (h StatsHandler) Handle(ctx context.Context, vd *v1alpha2.VirtualDisk) (rec if vd.Status.Stats.CreationDuration.WaitingForDependencies != nil { duration -= vd.Status.Stats.CreationDuration.WaitingForDependencies.Duration } + if vd.Status.Stats.CreationDuration.WaitingForFirstConsumer != nil { + duration -= vd.Status.Stats.CreationDuration.WaitingForFirstConsumer.Duration + } vd.Status.Stats.CreationDuration.TotalProvisioning = &metav1.Duration{ Duration: duration,