Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions pkg/domain/infra/abi/images.go
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,11 @@ func (ir *ImageEngine) Load(ctx context.Context, options entities.ImageLoadOptio

loadedImages, err := ir.Libpod.LibimageRuntime().Load(ctx, options.Input, loadOptions)
if err != nil {
// Surface ENOSPC immediately — no point wrapping in a generic error.
// The error string arrives from a subprocess, so we check the message.
if strings.Contains(err.Error(), "no space left on device") {
return nil, fmt.Errorf("loading image: %w", syscall.ENOSPC)
}
return nil, err
}
return &entities.ImageLoadReport{Names: loadedImages}, nil
Expand Down
35 changes: 35 additions & 0 deletions pkg/domain/infra/abi/images_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
package abi

import (
"errors"
"fmt"
"strings"
"syscall"
"testing"

"github.com/stretchr/testify/assert"
Expand All @@ -18,3 +22,34 @@ func TestToDomainHistoryLayer(t *testing.T) {
newLayer := toDomainHistoryLayer(&layer)
assert.Equal(t, layer.Size, newLayer.Size)
}

// TestLoadImageENOSPCWrapping verifies that the ENOSPC-detection logic used
// in (*ImageEngine).Load wraps disk-full errors so that callers can detect
// them with errors.Is(err, syscall.ENOSPC).
func TestLoadImageENOSPCWrapping(t *testing.T) {
// applyENOSPCCheck reproduces the inline check added to the Load function.
applyENOSPCCheck := func(loadErr error) error {
if loadErr == nil {
return nil
}
if strings.Contains(loadErr.Error(), "no space left on device") {
return fmt.Errorf("loading image: %w", syscall.ENOSPC)
}
return loadErr
}

// A subprocess error that contains the canonical ENOSPC string.
enospcErr := errors.New("writing blob: no space left on device")
result := applyENOSPCCheck(enospcErr)
assert.True(t, errors.Is(result, syscall.ENOSPC),
"expected syscall.ENOSPC in error chain, got: %v", result)

// A generic error must not be mistaken for ENOSPC.
otherErr := errors.New("permission denied")
result = applyENOSPCCheck(otherErr)
assert.False(t, errors.Is(result, syscall.ENOSPC),
"non-ENOSPC error must not wrap syscall.ENOSPC")

// No error must pass through as nil.
assert.NoError(t, applyENOSPCCheck(nil))
}