diff --git a/images/dvcr-artifact/pkg/uploader/uploader.go b/images/dvcr-artifact/pkg/uploader/uploader.go index e3fb281761..5c67e21de0 100644 --- a/images/dvcr-artifact/pkg/uploader/uploader.go +++ b/images/dvcr-artifact/pkg/uploader/uploader.go @@ -280,6 +280,11 @@ func (app *uploadServerApp) healthzHandler(w http.ResponseWriter, _ *http.Reques } func (app *uploadServerApp) validateShouldHandleRequest(w http.ResponseWriter, r *http.Request) bool { + if r.Method == http.MethodGet { + w.WriteHeader(http.StatusOK) + return false + } + if r.Method != http.MethodPost && r.Method != http.MethodPut { w.WriteHeader(http.StatusNotFound) return false diff --git a/images/virtualization-artifact/pkg/common/ingress/ingress.go b/images/virtualization-artifact/pkg/common/ingress/ingress.go index 28cb3a97e3..3e7407fdaf 100644 --- a/images/virtualization-artifact/pkg/common/ingress/ingress.go +++ b/images/virtualization-artifact/pkg/common/ingress/ingress.go @@ -34,3 +34,8 @@ func MakeOwnerReference(ing *netv1.Ingress) metav1.OwnerReference { Controller: &isController, } } + +// IsIngressReady returns true if the load-balancer has at least one ingress point in the status; otherwise, it returns false. +func IsIngressReady(ing *netv1.Ingress) bool { + return len(ing.Status.LoadBalancer.Ingress) != 0 +} diff --git a/images/virtualization-artifact/pkg/common/pod/pod.go b/images/virtualization-artifact/pkg/common/pod/pod.go index f7836d4c57..a80b2416c6 100644 --- a/images/virtualization-artifact/pkg/common/pod/pod.go +++ b/images/virtualization-artifact/pkg/common/pod/pod.go @@ -103,6 +103,17 @@ func IsPodStarted(pod *corev1.Pod) bool { return true } +// IsPodReady returns true if the pod's `Ready` condition status is true; otherwise, it returns false. +func IsPodReady(pod *corev1.Pod) bool { + for _, c := range pod.Status.Conditions { + if c.Type == corev1.PodReady && c.Status == corev1.ConditionTrue { + return true + } + } + + return false +} + // IsPodComplete returns true if a Pod is in 'Succeeded' phase, false if not. func IsPodComplete(pod *corev1.Pod) bool { return pod != nil && pod.Status.Phase == corev1.PodSucceeded diff --git a/images/virtualization-artifact/pkg/controller/cvi/internal/source/interfaces.go b/images/virtualization-artifact/pkg/controller/cvi/internal/source/interfaces.go index 8a7592cb04..893f4312ac 100644 --- a/images/virtualization-artifact/pkg/controller/cvi/internal/source/interfaces.go +++ b/images/virtualization-artifact/pkg/controller/cvi/internal/source/interfaces.go @@ -66,7 +66,7 @@ type Stat interface { GetDVCRImageName(pod *corev1.Pod) string GetDownloadSpeed(ownerUID types.UID, pod *corev1.Pod) *v1alpha2.StatusSpeed GetProgress(ownerUID types.UID, pod *corev1.Pod, prevProgress string, opts ...service.GetProgressOption) string - IsUploaderReady(pod *corev1.Pod, svc *corev1.Service, ing *netv1.Ingress) bool + IsUploaderReady(svc *corev1.Service, ing *netv1.Ingress) (bool, error) IsUploadStarted(ownerUID types.UID, pod *corev1.Pod) bool CheckPod(pod *corev1.Pod) error } diff --git a/images/virtualization-artifact/pkg/controller/cvi/internal/source/mock.go b/images/virtualization-artifact/pkg/controller/cvi/internal/source/mock.go index 6cd2eb625d..06172be85e 100644 --- a/images/virtualization-artifact/pkg/controller/cvi/internal/source/mock.go +++ b/images/virtualization-artifact/pkg/controller/cvi/internal/source/mock.go @@ -1141,7 +1141,7 @@ var _ Stat = &StatMock{} // IsUploadStartedFunc: func(ownerUID types.UID, pod *corev1.Pod) bool { // panic("mock out the IsUploadStarted method") // }, -// IsUploaderReadyFunc: func(pod *corev1.Pod, svc *corev1.Service, ing *netv1.Ingress) bool { +// IsUploaderReadyFunc: func(svc *corev1.Service, ing *netv1.Ingress) (bool, error) { // panic("mock out the IsUploaderReady method") // }, // } @@ -1176,7 +1176,7 @@ type StatMock struct { IsUploadStartedFunc func(ownerUID types.UID, pod *corev1.Pod) bool // IsUploaderReadyFunc mocks the IsUploaderReady method. - IsUploaderReadyFunc func(pod *corev1.Pod, svc *corev1.Service, ing *netv1.Ingress) bool + IsUploaderReadyFunc func(svc *corev1.Service, ing *netv1.Ingress) (bool, error) // calls tracks calls to the methods. calls struct { @@ -1232,8 +1232,6 @@ type StatMock struct { } // IsUploaderReady holds details about calls to the IsUploaderReady method. IsUploaderReady []struct { - // Pod is the pod argument value. - Pod *corev1.Pod // Svc is the svc argument value. Svc *corev1.Service // Ing is the ing argument value. @@ -1528,23 +1526,21 @@ func (mock *StatMock) IsUploadStartedCalls() []struct { } // IsUploaderReady calls IsUploaderReadyFunc. -func (mock *StatMock) IsUploaderReady(pod *corev1.Pod, svc *corev1.Service, ing *netv1.Ingress) bool { +func (mock *StatMock) IsUploaderReady(svc *corev1.Service, ing *netv1.Ingress) (bool, error) { if mock.IsUploaderReadyFunc == nil { panic("StatMock.IsUploaderReadyFunc: method is nil but Stat.IsUploaderReady was just called") } callInfo := struct { - Pod *corev1.Pod Svc *corev1.Service Ing *netv1.Ingress }{ - Pod: pod, Svc: svc, Ing: ing, } mock.lockIsUploaderReady.Lock() mock.calls.IsUploaderReady = append(mock.calls.IsUploaderReady, callInfo) mock.lockIsUploaderReady.Unlock() - return mock.IsUploaderReadyFunc(pod, svc, ing) + return mock.IsUploaderReadyFunc(svc, ing) } // IsUploaderReadyCalls gets all the calls that were made to IsUploaderReady. @@ -1552,12 +1548,10 @@ func (mock *StatMock) IsUploaderReady(pod *corev1.Pod, svc *corev1.Service, ing // // len(mockedStat.IsUploaderReadyCalls()) func (mock *StatMock) IsUploaderReadyCalls() []struct { - Pod *corev1.Pod Svc *corev1.Service Ing *netv1.Ingress } { var calls []struct { - Pod *corev1.Pod Svc *corev1.Service Ing *netv1.Ingress } diff --git a/images/virtualization-artifact/pkg/controller/cvi/internal/source/upload.go b/images/virtualization-artifact/pkg/controller/cvi/internal/source/upload.go index dbbb968549..58e5c9c3bb 100644 --- a/images/virtualization-artifact/pkg/controller/cvi/internal/source/upload.go +++ b/images/virtualization-artifact/pkg/controller/cvi/internal/source/upload.go @@ -221,20 +221,27 @@ func (ds UploadDataSource) Sync(ctx context.Context, cvi *v1alpha2.ClusterVirtua } log.Info("Provisioning...", "progress", cvi.Status.Progress, "pod.phase", pod.Status.Phase) - case ds.statService.IsUploaderReady(pod, svc, ing): - cb. - Status(metav1.ConditionFalse). - Reason(cvicondition.WaitForUserUpload). - Message("Waiting for the user upload.") - - cvi.Status.Phase = v1alpha2.ImageWaitForUserUpload - cvi.Status.Target.RegistryURL = ds.statService.GetDVCRImageName(pod) - cvi.Status.ImageUploadURLs = &v1alpha2.ImageUploadURLs{ - External: ds.uploaderService.GetExternalURL(ctx, ing), - InCluster: ds.uploaderService.GetInClusterURL(ctx, svc), + case podutil.IsPodReady(pod): + isUploderReady, err := ds.statService.IsUploaderReady(svc, ing) + if err != nil { + return reconcile.Result{}, err } - log.Info("Waiting for the user upload", "pod.phase", pod.Status.Phase) + if isUploderReady { + cb. + Status(metav1.ConditionFalse). + Reason(cvicondition.WaitForUserUpload). + Message("Waiting for the user upload.") + + cvi.Status.Phase = v1alpha2.ImageWaitForUserUpload + cvi.Status.Target.RegistryURL = ds.statService.GetDVCRImageName(pod) + cvi.Status.ImageUploadURLs = &v1alpha2.ImageUploadURLs{ + External: ds.uploaderService.GetExternalURL(ctx, ing), + InCluster: ds.uploaderService.GetInClusterURL(ctx, svc), + } + + log.Info("Waiting for the user upload", "pod.phase", pod.Status.Phase) + } default: cb. Status(metav1.ConditionFalse). diff --git a/images/virtualization-artifact/pkg/controller/service/stat_service.go b/images/virtualization-artifact/pkg/controller/service/stat_service.go index 7974be0ab2..f194d49144 100644 --- a/images/virtualization-artifact/pkg/controller/service/stat_service.go +++ b/images/virtualization-artifact/pkg/controller/service/stat_service.go @@ -37,7 +37,6 @@ import ( "github.com/deckhouse/virtualization-controller/pkg/common/humanize_bytes" "github.com/deckhouse/virtualization-controller/pkg/common/imageformat" "github.com/deckhouse/virtualization-controller/pkg/common/percent" - podutil "github.com/deckhouse/virtualization-controller/pkg/common/pod" "github.com/deckhouse/virtualization-controller/pkg/controller/conditions" "github.com/deckhouse/virtualization-controller/pkg/controller/monitoring" "github.com/deckhouse/virtualization/api/core/v1alpha2" @@ -272,14 +271,23 @@ func (s StatService) IsImportStarted(ownerUID types.UID, pod *corev1.Pod) bool { return progress.ProgressRaw() > 0 } -func (s StatService) IsUploaderReady(pod *corev1.Pod, svc *corev1.Service, ing *netv1.Ingress) bool { - if pod == nil || svc == nil || ing == nil { - return false +func (s StatService) IsUploaderReady(svc *corev1.Service, ing *netv1.Ingress) (bool, error) { + if svc == nil || ing == nil { + return false, nil } - ingressIsOK := ing.Annotations[annotations.AnnUploadPath] != "" || ing.Annotations[annotations.AnnUploadURLDeprecated] != "" + uploadURL, ok := ing.Annotations[annotations.AnnUploadURL] + if ok { + response, err := http.Get(uploadURL) + if err != nil { + return false, fmt.Errorf("failed to get upload service status: %w", err) + } + if response.StatusCode == http.StatusOK { + return true, nil + } + } - return podutil.IsPodRunning(pod) && podutil.IsPodStarted(pod) && ingressIsOK + return false, nil } func (s StatService) IsUploadStarted(ownerUID types.UID, pod *corev1.Pod) bool { diff --git a/images/virtualization-artifact/pkg/controller/uploader/uploader_pod.go b/images/virtualization-artifact/pkg/controller/uploader/uploader_pod.go index 5c50ad2b21..570566ca57 100644 --- a/images/virtualization-artifact/pkg/controller/uploader/uploader_pod.go +++ b/images/virtualization-artifact/pkg/controller/uploader/uploader_pod.go @@ -22,6 +22,7 @@ import ( corev1 "k8s.io/api/core/v1" k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client" @@ -36,6 +37,12 @@ const ( destinationAuthVol = "dvcr-secret-vol" ) +// These constants can't be imported from "images/dvcr-artifact/pkg/uploader/uploader.go" due to conflicts with the CDI version. +const ( + healthzPort = 8080 + healthzPath = "/healthz" +) + type Pod struct { PodSettings *PodSettings Settings *Settings @@ -151,6 +158,15 @@ func (p *Pod) makeUploaderContainerSpec() *corev1.Container { SecurityContext: &corev1.SecurityContext{ ReadOnlyRootFilesystem: ptr.To(true), }, + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: healthzPath, + Port: intstr.FromInt(healthzPort), + }, + }, + InitialDelaySeconds: 5, + }, } if p.PodSettings.ResourceRequirements != nil { diff --git a/images/virtualization-artifact/pkg/controller/vd/internal/source/upload.go b/images/virtualization-artifact/pkg/controller/vd/internal/source/upload.go index 1cedbb19ad..cbb8c28f5b 100644 --- a/images/virtualization-artifact/pkg/controller/vd/internal/source/upload.go +++ b/images/virtualization-artifact/pkg/controller/vd/internal/source/upload.go @@ -190,29 +190,35 @@ func (ds UploadDataSource) Sync(ctx context.Context, vd *v1alpha2.VirtualDisk) ( } if !ds.statService.IsUploadStarted(vd.GetUID(), pod) { - if ds.statService.IsUploaderReady(pod, svc, ing) { - log.Info("Waiting for the user upload", "pod.phase", pod.Status.Phase) - - vd.Status.Phase = v1alpha2.DiskWaitForUserUpload - cb. - Status(metav1.ConditionFalse). - Reason(vdcondition.WaitForUserUpload). - Message("Waiting for the user upload.") - vd.Status.ImageUploadURLs = &v1alpha2.ImageUploadURLs{ - External: ds.uploaderService.GetExternalURL(ctx, ing), - InCluster: ds.uploaderService.GetInClusterURL(ctx, svc), + if podutil.IsPodReady(pod) { + isUploaderReady, err := ds.statService.IsUploaderReady(svc, ing) + if err != nil { + return reconcile.Result{}, err + } + if isUploaderReady { + log.Info("Waiting for the user upload", "pod.phase", pod.Status.Phase) + + vd.Status.Phase = v1alpha2.DiskWaitForUserUpload + cb. + Status(metav1.ConditionFalse). + Reason(vdcondition.WaitForUserUpload). + Message("Waiting for the user upload.") + vd.Status.ImageUploadURLs = &v1alpha2.ImageUploadURLs{ + External: ds.uploaderService.GetExternalURL(ctx, ing), + InCluster: ds.uploaderService.GetInClusterURL(ctx, svc), + } + } else { + log.Info("Waiting for the uploader to be ready to process the user's upload", "pod.phase", pod.Status.Phase) + + vd.Status.Phase = v1alpha2.DiskPending + cb. + Status(metav1.ConditionFalse). + Reason(vdcondition.ProvisioningNotStarted). + Message(fmt.Sprintf("Waiting for the uploader %q to be ready to process the user's upload.", pod.Name)) } - } else { - log.Info("Waiting for the uploader to be ready to process the user's upload", "pod.phase", pod.Status.Phase) - vd.Status.Phase = v1alpha2.DiskPending - cb. - Status(metav1.ConditionFalse). - Reason(vdcondition.ProvisioningNotStarted). - Message(fmt.Sprintf("Waiting for the uploader %q to be ready to process the user's upload.", pod.Name)) + return reconcile.Result{RequeueAfter: time.Second}, nil } - - return reconcile.Result{RequeueAfter: time.Second}, nil } log.Info("Provisioning to DVCR is in progress", "podPhase", pod.Status.Phase) diff --git a/images/virtualization-artifact/pkg/controller/vi/internal/source/interfaces.go b/images/virtualization-artifact/pkg/controller/vi/internal/source/interfaces.go index fca75b057a..56ed066f36 100644 --- a/images/virtualization-artifact/pkg/controller/vi/internal/source/interfaces.go +++ b/images/virtualization-artifact/pkg/controller/vi/internal/source/interfaces.go @@ -64,7 +64,7 @@ type Stat interface { step.WaitForPodStepStat step.ReadyContainerRegistryStepStat IsUploadStarted(ownerUID types.UID, pod *corev1.Pod) bool - IsUploaderReady(pod *corev1.Pod, svc *corev1.Service, ing *netv1.Ingress) bool + IsUploaderReady(svc *corev1.Service, ing *netv1.Ingress) (bool, error) GetDownloadSpeed(ownerUID types.UID, pod *corev1.Pod) *v1alpha2.StatusSpeed } diff --git a/images/virtualization-artifact/pkg/controller/vi/internal/source/mock.go b/images/virtualization-artifact/pkg/controller/vi/internal/source/mock.go index ca40fa45e2..69e2ca678c 100644 --- a/images/virtualization-artifact/pkg/controller/vi/internal/source/mock.go +++ b/images/virtualization-artifact/pkg/controller/vi/internal/source/mock.go @@ -1130,7 +1130,7 @@ var _ Stat = &StatMock{} // IsUploadStartedFunc: func(ownerUID types.UID, pod *corev1.Pod) bool { // panic("mock out the IsUploadStarted method") // }, -// IsUploaderReadyFunc: func(pod *corev1.Pod, svc *corev1.Service, ing *netv1.Ingress) bool { +// IsUploaderReadyFunc: func(svc *corev1.Service, ing *netv1.Ingress) (bool, error) { // panic("mock out the IsUploaderReady method") // }, // } @@ -1165,7 +1165,7 @@ type StatMock struct { IsUploadStartedFunc func(ownerUID types.UID, pod *corev1.Pod) bool // IsUploaderReadyFunc mocks the IsUploaderReady method. - IsUploaderReadyFunc func(pod *corev1.Pod, svc *corev1.Service, ing *netv1.Ingress) bool + IsUploaderReadyFunc func(svc *corev1.Service, ing *netv1.Ingress) (bool, error) // calls tracks calls to the methods. calls struct { @@ -1221,8 +1221,6 @@ type StatMock struct { } // IsUploaderReady holds details about calls to the IsUploaderReady method. IsUploaderReady []struct { - // Pod is the pod argument value. - Pod *corev1.Pod // Svc is the svc argument value. Svc *corev1.Service // Ing is the ing argument value. @@ -1517,23 +1515,21 @@ func (mock *StatMock) IsUploadStartedCalls() []struct { } // IsUploaderReady calls IsUploaderReadyFunc. -func (mock *StatMock) IsUploaderReady(pod *corev1.Pod, svc *corev1.Service, ing *netv1.Ingress) bool { +func (mock *StatMock) IsUploaderReady(svc *corev1.Service, ing *netv1.Ingress) (bool, error) { if mock.IsUploaderReadyFunc == nil { panic("StatMock.IsUploaderReadyFunc: method is nil but Stat.IsUploaderReady was just called") } callInfo := struct { - Pod *corev1.Pod Svc *corev1.Service Ing *netv1.Ingress }{ - Pod: pod, Svc: svc, Ing: ing, } mock.lockIsUploaderReady.Lock() mock.calls.IsUploaderReady = append(mock.calls.IsUploaderReady, callInfo) mock.lockIsUploaderReady.Unlock() - return mock.IsUploaderReadyFunc(pod, svc, ing) + return mock.IsUploaderReadyFunc(svc, ing) } // IsUploaderReadyCalls gets all the calls that were made to IsUploaderReady. @@ -1541,12 +1537,10 @@ func (mock *StatMock) IsUploaderReady(pod *corev1.Pod, svc *corev1.Service, ing // // len(mockedStat.IsUploaderReadyCalls()) func (mock *StatMock) IsUploaderReadyCalls() []struct { - Pod *corev1.Pod Svc *corev1.Service Ing *netv1.Ingress } { var calls []struct { - Pod *corev1.Pod Svc *corev1.Service Ing *netv1.Ingress } diff --git a/images/virtualization-artifact/pkg/controller/vi/internal/source/upload.go b/images/virtualization-artifact/pkg/controller/vi/internal/source/upload.go index 9fec96370e..b88edf7443 100644 --- a/images/virtualization-artifact/pkg/controller/vi/internal/source/upload.go +++ b/images/virtualization-artifact/pkg/controller/vi/internal/source/upload.go @@ -172,30 +172,37 @@ func (ds UploadDataSource) StoreToPVC(ctx context.Context, vi *v1alpha2.VirtualI } if !ds.statService.IsUploadStarted(vi.GetUID(), pod) { - if ds.statService.IsUploaderReady(pod, svc, ing) { - log.Info("Waiting for the user upload", "pod.phase", pod.Status.Phase) - - vi.Status.Phase = v1alpha2.ImageWaitForUserUpload - cb. - Status(metav1.ConditionFalse). - Reason(vicondition.WaitForUserUpload). - Message("Waiting for the user upload.") + if podutil.IsPodReady(pod) { + isUploaderReady, err := ds.statService.IsUploaderReady(svc, ing) + if err != nil { + return reconcile.Result{}, err + } - vi.Status.ImageUploadURLs = &v1alpha2.ImageUploadURLs{ - External: ds.uploaderService.GetExternalURL(ctx, ing), - InCluster: ds.uploaderService.GetInClusterURL(ctx, svc), + if isUploaderReady { + log.Info("Waiting for the user upload", "pod.phase", pod.Status.Phase) + + vi.Status.Phase = v1alpha2.ImageWaitForUserUpload + cb. + Status(metav1.ConditionFalse). + Reason(vicondition.WaitForUserUpload). + Message("Waiting for the user upload.") + + vi.Status.ImageUploadURLs = &v1alpha2.ImageUploadURLs{ + External: ds.uploaderService.GetExternalURL(ctx, ing), + InCluster: ds.uploaderService.GetInClusterURL(ctx, svc), + } + } else { + log.Info("Waiting for the uploader to be ready to process the user's upload", "pod.phase", pod.Status.Phase) + + vi.Status.Phase = v1alpha2.ImagePending + cb. + Status(metav1.ConditionFalse). + Reason(vicondition.ProvisioningNotStarted). + Message(fmt.Sprintf("Waiting for the uploader %q to be ready to process the user's upload.", pod.Name)) } - } else { - log.Info("Waiting for the uploader to be ready to process the user's upload", "pod.phase", pod.Status.Phase) - vi.Status.Phase = v1alpha2.ImagePending - cb. - Status(metav1.ConditionFalse). - Reason(vicondition.ProvisioningNotStarted). - Message(fmt.Sprintf("Waiting for the uploader %q to be ready to process the user's upload.", pod.Name)) + return reconcile.Result{RequeueAfter: time.Second}, nil } - - return reconcile.Result{RequeueAfter: time.Second}, nil } vi.Status.Phase = v1alpha2.ImageProvisioning @@ -454,20 +461,27 @@ func (ds UploadDataSource) StoreToDVCR(ctx context.Context, vi *v1alpha2.Virtual } log.Info("Provisioning...", "pod.phase", pod.Status.Phase) - case ds.statService.IsUploaderReady(pod, svc, ing): - cb. - Status(metav1.ConditionFalse). - Reason(vicondition.WaitForUserUpload). - Message("Waiting for the user upload.") - - vi.Status.Phase = v1alpha2.ImageWaitForUserUpload - vi.Status.Target.RegistryURL = ds.statService.GetDVCRImageName(pod) - vi.Status.ImageUploadURLs = &v1alpha2.ImageUploadURLs{ - External: ds.uploaderService.GetExternalURL(ctx, ing), - InCluster: ds.uploaderService.GetInClusterURL(ctx, svc), + case podutil.IsPodReady(pod): + isUploaderReady, err := ds.statService.IsUploaderReady(svc, ing) + if err != nil { + return reconcile.Result{}, err } - log.Info("Waiting for the user upload", "pod.phase", pod.Status.Phase) + if isUploaderReady { + cb. + Status(metav1.ConditionFalse). + Reason(vicondition.WaitForUserUpload). + Message("Waiting for the user upload.") + + vi.Status.Phase = v1alpha2.ImageWaitForUserUpload + vi.Status.Target.RegistryURL = ds.statService.GetDVCRImageName(pod) + vi.Status.ImageUploadURLs = &v1alpha2.ImageUploadURLs{ + External: ds.uploaderService.GetExternalURL(ctx, ing), + InCluster: ds.uploaderService.GetInClusterURL(ctx, svc), + } + + log.Info("Waiting for the user upload", "pod.phase", pod.Status.Phase) + } default: cb. Status(metav1.ConditionFalse). diff --git a/test/e2e/blockdevice/data_exports.go b/test/e2e/blockdevice/data_exports.go index bc3223d755..8bc5897ff4 100644 --- a/test/e2e/blockdevice/data_exports.go +++ b/test/e2e/blockdevice/data_exports.go @@ -285,20 +285,6 @@ func createUploadDisk(f *framework.Framework, name string) *v1alpha2.VirtualDisk return vd } -func retry(maxRetries int, fn func() error) error { - var lastErr error - for attempt := 1; attempt <= maxRetries; attempt++ { - if err := fn(); err != nil { - lastErr = err - GinkgoWriter.Printf("Attempt %d/%d failed: %v\n", attempt, maxRetries, err) - time.Sleep(time.Duration(attempt) * time.Second) - continue - } - return nil - } - return fmt.Errorf("failed after %d attempts: %w", maxRetries, lastErr) -} - func uploadFile(f *framework.Framework, vd *v1alpha2.VirtualDisk, filePath string) { err := f.Clients.GenericClient().Get(context.Background(), crclient.ObjectKeyFromObject(vd), vd) Expect(err).NotTo(HaveOccurred()) @@ -314,14 +300,17 @@ func uploadFile(f *framework.Framework, vd *v1alpha2.VirtualDisk, filePath strin } uploadURL := vd.Status.ImageUploadURLs.External - // During the upload of a VirtualDisk of type 'Upload', there is a bug: - // when the VirtualDisk is in the 'DiskWaitForUserUpload' phase, - // nginx may not be ready yet and can return 413 or 503 errors. - // Once this bug is fixed, the retry mechanism must be removed. - const maxRetries = 5 - err = retry(maxRetries, func() error { - return doUploadAttempt(httpClient, uploadURL, filePath) - }) + // uploaderPod, err := getUploaderPod(vd.Name, vd.Namespace, f) + // Expect(err).NotTo(HaveOccurred()) + // untilUploaderPodReady(uploaderPod.Name, uploaderPod.Namespace, f) + + // uploaderIngress, err := getUploaderIngress(vd.Name, vd.Namespace, f) + // Expect(err).NotTo(HaveOccurred()) + // untilUploaderIngressReady(uploaderIngress.Name, uploaderIngress.Namespace, f) + + // time.Sleep(1 * time.Second) + + err = doUploadAttempt(httpClient, uploadURL, filePath) Expect(err).NotTo(HaveOccurred(), "Upload failed") } @@ -385,3 +374,77 @@ func checkStorageVolumeDataManagerEnabled() (bool, error) { return enabled != nil && *enabled, nil } + +// func getUploaderPod(vd *v1alpha2.VirtualDisk, f *framework.Framework) (*corev1.Pod, error) { +// pods, err := f.Clients.KubeClient().CoreV1().Pods(vd.Namespace).List(context.Background(), metav1.ListOptions{}) +// if err != nil { +// return nil, err +// } + +// for _, pod := range pods.Items { +// for _, ref := range pod.OwnerReferences { +// if ref.Kind == v1alpha2.VirtualDiskKind && ref.Name == vd.Name && strings.HasSuffix(pod.Name, string(vd.UID)) { +// for _, c := range pod.Spec.Containers { +// if c.Name == common.UploaderContainerName { +// return &pod, nil +// } +// } +// } +// } +// } + +// return nil, fmt.Errorf("could not find uploader pod for virtual disk %s", vd.Name) +// } + +// func untilUploaderPodReady(podName, podNamespace string, f *framework.Framework) { +// GinkgoHelper() + +// Eventually(func() error { +// pod, err := f.Clients.KubeClient().CoreV1().Pods(podNamespace).Get(context.Background(), podName, metav1.GetOptions{}) +// if err != nil { +// return err +// } + +// for _, c := range pod.Status.Conditions { +// if c.Type == corev1.PodReady && c.Status == corev1.ConditionTrue { +// return nil +// } +// } + +// return fmt.Errorf("pod %s is not ready", podName) +// }).WithTimeout(framework.ShortTimeout).WithPolling(framework.PollingInterval).Should(Succeed()) +// } + +// func getUploaderIngress(vd *v1alpha2.VirtualDisk, f *framework.Framework) (*netv1.Ingress, error) { +// ings, err := f.Clients.KubeClient().NetworkingV1().Ingresses(vd.Namespace).List(context.Background(), metav1.ListOptions{}) +// if err != nil { +// return nil, err +// } + +// for _, ing := range ings.Items { +// for _, ref := range ing.OwnerReferences { +// if ref.Kind == v1alpha2.VirtualDiskKind && ref.Name == vd.Name && strings.HasSuffix(ing.Name, string(vd.UID)) { +// return &ing, nil +// } +// } +// } + +// return nil, fmt.Errorf("could not find uploader ingress for virtual disk %s", vd.Name) +// } + +// func untilUploaderIngressReady(ingName, ingNamespace string, f *framework.Framework) { +// GinkgoHelper() + +// Eventually(func() error { +// ing, err := f.Clients.KubeClient().NetworkingV1().Ingresses(ingNamespace).Get(context.Background(), ingName, metav1.GetOptions{}) +// if err != nil { +// return err +// } + +// if len(ing.Status.LoadBalancer.Ingress) != 0 { +// return nil +// } + +// return fmt.Errorf("ingress %s is not ready", ingName) +// }).WithTimeout(framework.MiddleTimeout).WithPolling(framework.PollingInterval).Should(Succeed()) +// }