Skip to content

Commit

Permalink
Merge pull request sylabs#2279 from dtrudg/wrap-squashfs-errors-4.0
Browse files Browse the repository at this point in the history
chore: replace UnavailableError with wrapping for i/p/client/ocisif (release4.0)
  • Loading branch information
dtrudg authored Oct 20, 2023
2 parents 86e5e86 + de01bc0 commit 1d9efb7
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 16 deletions.
40 changes: 27 additions & 13 deletions cmd/internal/cli/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,14 @@ import (
"github.com/sylabs/singularity/v4/internal/pkg/client/library"
"github.com/sylabs/singularity/v4/internal/pkg/client/net"
"github.com/sylabs/singularity/v4/internal/pkg/client/oci"
ocisifclient "github.com/sylabs/singularity/v4/internal/pkg/client/ocisif"
"github.com/sylabs/singularity/v4/internal/pkg/client/oras"
"github.com/sylabs/singularity/v4/internal/pkg/client/shub"
"github.com/sylabs/singularity/v4/internal/pkg/runtime/launcher"
"github.com/sylabs/singularity/v4/internal/pkg/runtime/launcher/native"
ocilauncher "github.com/sylabs/singularity/v4/internal/pkg/runtime/launcher/oci"
"github.com/sylabs/singularity/v4/internal/pkg/util/uri"
"github.com/sylabs/singularity/v4/pkg/ocibundle/ocisif"
bndocisif "github.com/sylabs/singularity/v4/pkg/ocibundle/ocisif"
"github.com/sylabs/singularity/v4/pkg/syfs"
"github.com/sylabs/singularity/v4/pkg/sylog"
useragent "github.com/sylabs/singularity/v4/pkg/util/user-agent"
Expand Down Expand Up @@ -190,13 +191,18 @@ func replaceURIWithImage(ctx context.Context, cmd *cobra.Command, args []string)
sylog.Fatalf("Unsupported transport type: %s", t)
}

var mountErr *ocisif.UnavailableError
if errors.As(err, &mountErr) {
// If we are in OCI mode, then we can still attempt to run from a directory
// bundle if tar->squashfs conversion in OCI-SIF creation fails. This
// fallback is important while sqfstar/tar2sqfs are not bundled, and not
// available in common distros.
if errors.Is(err, ocisifclient.ErrFailedSquashfsConversion) {
if !canUseTmpSandbox {
sylog.Fatalf("OCI-SIF functionality could not be used, and fallback to unpacking OCI bundle in temporary sandbox dir disallowed (original error msg: %s)", err)
sylog.Errorf("%v", err)
sylog.Fatalf("OCI-SIF could not be created, and fallback to temporary sandbox dir disallowed")
}

sylog.Warningf("OCI-SIF functionality could not be used, falling back to unpacking OCI bundle in temporary sandbox dir (original error msg: %s)", err)
sylog.Warningf("%v", err)
sylog.Warningf("OCI-SIF could not be created, falling back to unpacking OCI bundle in temporary sandbox dir")
return origImageURI
}

Expand Down Expand Up @@ -416,30 +422,38 @@ func launchContainer(cmd *cobra.Command, ep launcher.ExecParams) error {
}
}

err = l.Exec(cmd.Context(), ep)
var mountErr *ocisif.UnavailableError
if !(errors.As(err, &mountErr) && strings.HasPrefix(ep.Image, "oci-sif:")) {
return err
execErr := l.Exec(cmd.Context(), ep)

// Check if we are using an OCI-SIF *and* the exec error indicates that a
// direct mount bundle is unavailable (squashfs mount failure etc). If both
// are true, we will try a fallback path extracting to a sandbox dir
// bundle.
var mountErr bndocisif.UnavailableError
if !(errors.As(execErr, &mountErr) && strings.HasPrefix(ep.Image, "oci-sif:")) {
// Any other situation is a failure
return execErr
}

if !canUseTmpSandbox {
return fmt.Errorf("OCI-SIF functionality could not be used, and fallback to unpacking OCI bundle in temporary sandbox dir disallowed (original error msg: %w)", err)
sylog.Errorf("%v", execErr)
return fmt.Errorf("OCI-SIF could not be used, and fallback to temporary sandbox dir disallowed")
}
sylog.Warningf("%v", execErr)
sylog.Warningf("OCI-SIF could not be used, falling back to unpacking OCI bundle in temporary sandbox dir")

sylog.Warningf("OCI-SIF functionality could not be used, falling back to unpacking OCI bundle in temporary sandbox dir (original error msg: %s)", err)
// Create a cache handle only when we know we are using a URI
imgCache := getCacheHandle(cache.Config{Disable: disableCache})
if imgCache == nil {
sylog.Fatalf("failed to create a new image cache handle")
}
origImageURIPtr := cmd.Context().Value(keyOrigImageURI)
if origImageURIPtr == nil {
return fmt.Errorf("unable to recover original image URI from context while attempting temp-dir OCI fallback (original OCI-SIF err: %w)", mountErr)
return fmt.Errorf("unable to recover original image URI from context")
}

origImageURI, ok := origImageURIPtr.(*string)
if !ok {
return fmt.Errorf("unable to recover original image URI (expected string, found: %T) from context while attempting temp-dir OCI fallback (original OCI-SIF err: %w)", origImageURIPtr, mountErr)
return fmt.Errorf("unable to recover original image URI (expected string, found: %T) from context", origImageURIPtr)
}
ep.Image = *origImageURI

Expand Down
6 changes: 4 additions & 2 deletions internal/pkg/client/ocisif/ocisif.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package ocisif
import (
"bytes"
"context"
"errors"
"fmt"
"os"
"path/filepath"
Expand All @@ -30,7 +31,6 @@ import (
"github.com/sylabs/singularity/v4/internal/pkg/ociimage"
"github.com/sylabs/singularity/v4/internal/pkg/ociplatform"
"github.com/sylabs/singularity/v4/internal/pkg/util/fs"
obocisif "github.com/sylabs/singularity/v4/pkg/ocibundle/ocisif"
"github.com/sylabs/singularity/v4/pkg/syfs"
"github.com/sylabs/singularity/v4/pkg/sylog"
useragent "github.com/sylabs/singularity/v4/pkg/util/user-agent"
Expand All @@ -40,6 +40,8 @@ import (
// TODO - Replace when exported from SIF / oci-tools
const SquashfsLayerMediaType types.MediaType = "application/vnd.sylabs.image.layer.v1.squashfs"

var ErrFailedSquashfsConversion = errors.New("could not convert layer to squashfs")

type PullOptions struct {
TmpDir string
OciAuth *ocitypes.DockerAuthConfig
Expand Down Expand Up @@ -231,7 +233,7 @@ func convertLayoutToOciSif(layoutDir string, digest ggcrv1.Hash, imageDest, work
workDir,
mutate.OptSquashfsSkipWhiteoutConversion(true))
if err != nil {
return &obocisif.UnavailableError{Underlying: fmt.Errorf("while converting to squashfs format: %w", err)}
return fmt.Errorf("%w: %v", ErrFailedSquashfsConversion, err)
}
img, err = mutate.Apply(img,
mutate.ReplaceLayers(squashfsLayer),
Expand Down
8 changes: 7 additions & 1 deletion pkg/ocibundle/ocisif/bundle_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ import (
"github.com/sylabs/singularity/v4/pkg/sylog"
)

// UnavailableError is used to wrap an Underlying error, while indicating that
// it is not currently possible to setup an OCI bundle using direct mount(s)
// from an OCI-SIF. This is intended to permit fall-back paths in the caller
// when squashfuse is unavailable / failing.
//
// TODO - replace with native Go error wrapping at major version increment.
type UnavailableError struct {
Underlying error
}
Expand Down Expand Up @@ -172,7 +178,7 @@ func (b *Bundle) Create(ctx context.Context, ociConfig *specs.Spec) error {
sylog.Debugf("Mounting squashfs rootfs from %q to %q", imgFile, tools.RootFs(b.bundlePath).Path())
if err := mount(ctx, imgFile, tools.RootFs(b.bundlePath).Path(), rootfsLayer.Digest); err != nil {
b.Delete(ctx)
return &UnavailableError{Underlying: fmt.Errorf("while mounting squashfs layer: %w", err)}
return UnavailableError{Underlying: fmt.Errorf("while mounting squashfs layer: %w", err)}
}
b.imageMounted = true

Expand Down

0 comments on commit 1d9efb7

Please sign in to comment.