From 5f1f4724d9b664b5dea8ea66adc202a2687ddc11 Mon Sep 17 00:00:00 2001 From: Daniil Antoshin Date: Tue, 18 Nov 2025 14:53:40 +0200 Subject: [PATCH 1/3] try to catch err no left space Signed-off-by: Daniil Antoshin fix Signed-off-by: Daniil Antoshin --- images/dvcr-artifact/pkg/errors/errors.go | 56 ++++++++++++++++++- images/dvcr-artifact/pkg/registry/registry.go | 20 ++++++- 2 files changed, 74 insertions(+), 2 deletions(-) diff --git a/images/dvcr-artifact/pkg/errors/errors.go b/images/dvcr-artifact/pkg/errors/errors.go index cb2ada7699..1668b53167 100644 --- a/images/dvcr-artifact/pkg/errors/errors.go +++ b/images/dvcr-artifact/pkg/errors/errors.go @@ -16,7 +16,12 @@ limitations under the License. package errors -import "fmt" +import ( + "errors" + "fmt" + "os" + "syscall" +) type ReasonError interface { error @@ -44,3 +49,52 @@ func (e BadImageChecksumError) Reason() string { func (e BadImageChecksumError) Error() string { return fmt.Sprintf("%s sum mismatch: %s != %s", e.algorithm, e.expected, e.actual) } + +func NewNoSpaceLeftError(err error) NoSpaceLeftError { + return NoSpaceLeftError{ + err: err, + } +} + +type NoSpaceLeftError struct { + err error +} + +func (e NoSpaceLeftError) Reason() string { + return "NoSpaceLeft" +} + +func (e NoSpaceLeftError) Error() string { + return fmt.Sprintf("no space left on device: %v", e.err) +} + +func (e NoSpaceLeftError) Unwrap() error { + return e.err +} + +func IsNoSpaceLeftError(err error) bool { + if err == nil { + return false + } + + var noSpaceErr NoSpaceLeftError + if errors.As(err, &noSpaceErr) { + return true + } + + var pathErr *os.PathError + if errors.As(err, &pathErr) { + if pathErr.Err == syscall.ENOSPC { + return true + } + } + + var errno syscall.Errno + if errors.As(err, &errno) { + if errno == syscall.ENOSPC { + return true + } + } + + return false +} diff --git a/images/dvcr-artifact/pkg/registry/registry.go b/images/dvcr-artifact/pkg/registry/registry.go index 20e613e3ce..38b77c56da 100644 --- a/images/dvcr-artifact/pkg/registry/registry.go +++ b/images/dvcr-artifact/pkg/registry/registry.go @@ -35,7 +35,7 @@ import ( "github.com/google/go-containerregistry/pkg/authn" "github.com/google/go-containerregistry/pkg/name" - "github.com/google/go-containerregistry/pkg/v1" + v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/empty" "github.com/google/go-containerregistry/pkg/v1/mutate" "github.com/google/go-containerregistry/pkg/v1/remote" @@ -261,6 +261,9 @@ func (p DataProcessor) inspectAndStreamSourceImage( klog.Infoln("Streaming from the source") doneSize, err := io.Copy(streamWriter, io.TeeReader(sourceImageReader, imageInfoWriter)) if err != nil { + if importerrs.IsNoSpaceLeftError(err) { + return importerrs.NewNoSpaceLeftError(err) + } return fmt.Errorf("error copying from the source: %w", err) } @@ -325,6 +328,9 @@ func (p DataProcessor) uploadLayersAndImage( klog.Infoln("Uploading layer to registry") if err := remote.WriteLayer(repo, layer, remoteOpts...); err != nil { + if importerrs.IsNoSpaceLeftError(err) { + return importerrs.NewNoSpaceLeftError(err) + } return fmt.Errorf("error uploading layer: %w", err) } klog.Infoln("Layer uploaded") @@ -356,6 +362,9 @@ func (p DataProcessor) uploadLayersAndImage( klog.Infof("Uploading image %q to registry", p.destImageName) if err = remote.Write(ref, image, remoteOpts...); err != nil { + if importerrs.IsNoSpaceLeftError(err) { + return importerrs.NewNoSpaceLeftError(err) + } return fmt.Errorf("error uploading image: %w", err) } @@ -403,6 +412,9 @@ func getImageInfo(ctx context.Context, sourceReader io.ReadCloser) (ImageInfo, e uncompressedN, err = io.CopyN(tempImageInfoFile, formatSourceReaders.TopReader(), imageInfoSize) if err != nil && !errors.Is(err, io.EOF) { + if importerrs.IsNoSpaceLeftError(err) { + return ImageInfo{}, importerrs.NewNoSpaceLeftError(err) + } return ImageInfo{}, fmt.Errorf("error writing to temp file: %w", err) } @@ -430,6 +442,9 @@ func getImageInfo(ctx context.Context, sourceReader io.ReadCloser) (ImageInfo, e // It's necessary to read everything from the original image to avoid blocking. _, err = io.Copy(&EmptyWriter{}, sourceReader) if err != nil { + if importerrs.IsNoSpaceLeftError(err) { + return ImageInfo{}, importerrs.NewNoSpaceLeftError(err) + } return ImageInfo{}, fmt.Errorf("error copying to nowhere: %w", err) } @@ -458,6 +473,9 @@ func getImageInfo(ctx context.Context, sourceReader io.ReadCloser) (ImageInfo, e // Count uncompressed size of source image. n, err := io.Copy(&EmptyWriter{}, formatSourceReaders.TopReader()) if err != nil { + if importerrs.IsNoSpaceLeftError(err) { + return ImageInfo{}, importerrs.NewNoSpaceLeftError(err) + } return ImageInfo{}, fmt.Errorf("error copying to nowhere: %w", err) } From 34ba6112dc0a3bc8c2825be48d85f8602e4cdc43 Mon Sep 17 00:00:00 2001 From: Daniil Antoshin Date: Tue, 18 Nov 2025 16:46:11 +0200 Subject: [PATCH 2/3] try again Signed-off-by: Daniil Antoshin --- images/dvcr-artifact/pkg/registry/registry.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/images/dvcr-artifact/pkg/registry/registry.go b/images/dvcr-artifact/pkg/registry/registry.go index 38b77c56da..e2e802233c 100644 --- a/images/dvcr-artifact/pkg/registry/registry.go +++ b/images/dvcr-artifact/pkg/registry/registry.go @@ -26,6 +26,7 @@ import ( "encoding/json" "fmt" "io" + "log/slog" "net/http" "os" "os/exec" @@ -328,6 +329,8 @@ func (p DataProcessor) uploadLayersAndImage( klog.Infoln("Uploading layer to registry") if err := remote.WriteLayer(repo, layer, remoteOpts...); err != nil { + slog.Error(fmt.Sprintf("error uploading layer: %w", err)) + if importerrs.IsNoSpaceLeftError(err) { return importerrs.NewNoSpaceLeftError(err) } From 5ace053bf2b626ade1b2470a10aaf5da3f00f83b Mon Sep 17 00:00:00 2001 From: Daniil Antoshin Date: Tue, 18 Nov 2025 17:10:00 +0200 Subject: [PATCH 3/3] test Signed-off-by: Daniil Antoshin --- images/dvcr-artifact/pkg/registry/registry.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/images/dvcr-artifact/pkg/registry/registry.go b/images/dvcr-artifact/pkg/registry/registry.go index e2e802233c..57c8427741 100644 --- a/images/dvcr-artifact/pkg/registry/registry.go +++ b/images/dvcr-artifact/pkg/registry/registry.go @@ -32,6 +32,7 @@ import ( "os/exec" "path" "strings" + "syscall" "time" "github.com/google/go-containerregistry/pkg/authn" @@ -331,6 +332,10 @@ func (p DataProcessor) uploadLayersAndImage( if err := remote.WriteLayer(repo, layer, remoteOpts...); err != nil { slog.Error(fmt.Sprintf("error uploading layer: %w", err)) + if errors.Is(err, syscall.ENOSPC) { + slog.Error(fmt.Sprintf("ENOSPC uploading layer: %w", err)) + } + if importerrs.IsNoSpaceLeftError(err) { return importerrs.NewNoSpaceLeftError(err) }