Skip to content

Commit

Permalink
feat: expose MountFUSE / UnmountFUSE API
Browse files Browse the repository at this point in the history
Move `Mount` and `Unmount` to `pkg` from `internal`.

Rename to `xxxxFUSE` to make it clear these are FUSE mounts, rather
than anything else.

Fixes sylabs/sif#206

Signed-off-by: Edita Kizinevic <edita.kizinevic@cern.ch>

Conflicts:
	internal/app/siftool/mount.go
	internal/app/siftool/unmount.go
	pkg/sif/mount.go
	pkg/siftool/unmount_test.go
  • Loading branch information
dtrudg authored and edytuk committed May 10, 2022
1 parent 377a570 commit e621bf4
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 65 deletions.
8 changes: 4 additions & 4 deletions internal/app/siftool/mount.go
Expand Up @@ -12,13 +12,13 @@ package siftool
import (
"context"

"github.com/apptainer/sif/v2/internal/pkg/exp"
"github.com/apptainer/sif/v2/pkg/sif"
)

// Mount mounts the primary system partition of the SIF file at path into mountPath.
func (a *App) Mount(ctx context.Context, path, mountPath string) error {
return exp.Mount(ctx, path, mountPath,
exp.OptMountStdout(a.opts.out),
exp.OptMountStderr(a.opts.err),
return sif.MountFUSE(ctx, path, mountPath,
sif.OptMountFUSEStdout(a.opts.out),
sif.OptMountFUSEStderr(a.opts.err),
)
}
8 changes: 4 additions & 4 deletions internal/app/siftool/unmount.go
Expand Up @@ -12,13 +12,13 @@ package siftool
import (
"context"

"github.com/apptainer/sif/v2/internal/pkg/exp"
"github.com/apptainer/sif/v2/pkg/sif"
)

// Unmounts the FUSE mounted filesystem at mountPath.
func (a *App) Unmount(ctx context.Context, mountPath string) error {
return exp.Unmount(ctx, mountPath,
exp.OptUnmountStdout(a.opts.out),
exp.OptUnmountStderr(a.opts.err),
return sif.UnmountFUSE(ctx, mountPath,
sif.OptUnmountFUSEStdout(a.opts.out),
sif.OptUnmountFUSEStderr(a.opts.err),
)
}
48 changes: 22 additions & 26 deletions internal/pkg/exp/mount.go → pkg/sif/mount.go
Expand Up @@ -7,9 +7,7 @@
// LICENSE file distributed with the sources of this project regarding your
// rights to use or distribute this software.

// Package exp contains experimental functionality that is not sufficiently mature to be exported
// as part of the module API.
package exp
package sif

import (
"context"
Expand All @@ -19,12 +17,10 @@ import (
"os"
"os/exec"
"path/filepath"

"github.com/apptainer/sif/v2/pkg/sif"
)

// mountSquashFS mounts the SquashFS filesystem from path at offset into mountPath.
func mountSquashFS(ctx context.Context, offset int64, path, mountPath string, mo mountOpts) error {
func mountSquashFS(ctx context.Context, offset int64, path, mountPath string, mo mountFUSEOpts) error {
args := []string{
"-o", fmt.Sprintf("ro,offset=%d", offset),
filepath.Clean(path),
Expand All @@ -42,38 +38,38 @@ func mountSquashFS(ctx context.Context, offset int64, path, mountPath string, mo
return nil
}

// mountOpts accumulates mount options.
type mountOpts struct {
// mountFUSEOpts accumulates mount options.
type mountFUSEOpts struct {
stdout io.Writer
stderr io.Writer
squashfusePath string
}

// MountOpt are used to specify mount options.
type MountOpt func(*mountOpts) error
// MountFUSEOpt are used to specify mount options.
type MountFUSEOpt func(*mountFUSEOpts) error

// OptMountStdout writes standard output to w.
func OptMountStdout(w io.Writer) MountOpt {
return func(mo *mountOpts) error {
func OptMountFUSEStdout(w io.Writer) MountFUSEOpt {
return func(mo *mountFUSEOpts) error {
mo.stdout = w
return nil
}
}

// OptMountStderr writes standard error to w.
func OptMountStderr(w io.Writer) MountOpt {
return func(mo *mountOpts) error {
// OptMountFUSEStderr writes standard error to w.
func OptMountFUSEStderr(w io.Writer) MountFUSEOpt {
return func(mo *mountFUSEOpts) error {
mo.stderr = w
return nil
}
}

var errSquashfusePathInvalid = errors.New("squashfuse path must be relative or absolute")

// OptMountSquashfusePath sets an explicit path to the squashfuse binary. The path must be an
// OptMountFUSESquashfusePath sets an explicit path to the squashfuse binary. The path must be an
// absolute or relative path.
func OptMountSquashfusePath(path string) MountOpt {
return func(mo *mountOpts) error {
func OptMountFUSESquashfusePath(path string) MountFUSEOpt {
return func(mo *mountFUSEOpts) error {
if filepath.Base(path) == path {
return errSquashfusePathInvalid
}
Expand All @@ -84,16 +80,16 @@ func OptMountSquashfusePath(path string) MountOpt {

var errUnsupportedFSType = errors.New("unrecognized filesystem type")

// Mount mounts the primary system partition of the SIF file at path into mountPath.
// MountFUSE mounts the primary system partition of the SIF file at path into mountPath.
//
// Mount may start one or more underlying processes. By default, stdout and stderr of these
// MountFUSE may start one or more underlying processes. By default, stdout and stderr of these
// processes is discarded. To modify this behavior, consider using OptMountStdout and/or
// OptMountStderr.
//
// By default, Mount searches for a squashfuse binary in the directories named by the PATH
// By default, MountFUSE searches for a squashfuse binary in the directories named by the PATH
// environment variable. To override this behavior, consider using OptMountSquashfusePath().
func Mount(ctx context.Context, path, mountPath string, opts ...MountOpt) error {
mo := mountOpts{
func MountFUSE(ctx context.Context, path, mountPath string, opts ...MountFUSEOpt) error {
mo := mountFUSEOpts{
squashfusePath: "squashfuse",
}

Expand All @@ -103,13 +99,13 @@ func Mount(ctx context.Context, path, mountPath string, opts ...MountOpt) error
}
}

f, err := sif.LoadContainerFromPath(path, sif.OptLoadWithFlag(os.O_RDONLY))
f, err := LoadContainerFromPath(path, OptLoadWithFlag(os.O_RDONLY))
if err != nil {
return fmt.Errorf("failed to load image: %w", err)
}
defer func() { _ = f.UnloadContainer() }()

d, err := f.GetDescriptor(sif.WithPartitionType(sif.PartPrimSys))
d, err := f.GetDescriptor(WithPartitionType(PartPrimSys))
if err != nil {
return fmt.Errorf("failed to get partition descriptor: %w", err)
}
Expand All @@ -120,7 +116,7 @@ func Mount(ctx context.Context, path, mountPath string, opts ...MountOpt) error
}

switch fs {
case sif.FsSquash:
case FsSquash:
return mountSquashFS(ctx, d.Offset(), path, mountPath, mo)
default:
return errUnsupportedFSType
Expand Down
40 changes: 20 additions & 20 deletions internal/pkg/exp/unmount.go → pkg/sif/unmount.go
Expand Up @@ -7,7 +7,7 @@
// LICENSE file distributed with the sources of this project regarding your
// rights to use or distribute this software.

package exp
package sif

import (
"context"
Expand All @@ -19,7 +19,7 @@ import (
)

// unmountSquashFS unmounts the filesystem at mountPath.
func unmountSquashFS(ctx context.Context, mountPath string, uo unmountOpts) error {
func unmountSquashFS(ctx context.Context, mountPath string, uo unmountFUSEOpts) error {
args := []string{
"-u",
filepath.Clean(mountPath),
Expand All @@ -35,37 +35,37 @@ func unmountSquashFS(ctx context.Context, mountPath string, uo unmountOpts) erro
return nil
}

// unmountOpts accumulates unmount options.
type unmountOpts struct {
// unmountFUSEOpts accumulates unmount options.
type unmountFUSEOpts struct {
stdout io.Writer
stderr io.Writer
fusermountPath string
}

// UnmountOpt are used to specify unmount options.
type UnmountOpt func(*unmountOpts) error
// UnmountFUSEOpt are used to specify unmount options.
type UnmountFUSEOpt func(*unmountFUSEOpts) error

// OptUnmountStdout writes standard output to w.
func OptUnmountStdout(w io.Writer) UnmountOpt {
return func(mo *unmountOpts) error {
// OptUnmountFUSEStdout writes standard output to w.
func OptUnmountFUSEStdout(w io.Writer) UnmountFUSEOpt {
return func(mo *unmountFUSEOpts) error {
mo.stdout = w
return nil
}
}

// OptUnmountStderr writes standard error to w.
func OptUnmountStderr(w io.Writer) UnmountOpt {
return func(mo *unmountOpts) error {
// OptUnmountFUSEStderr writes standard error to w.
func OptUnmountFUSEStderr(w io.Writer) UnmountFUSEOpt {
return func(mo *unmountFUSEOpts) error {
mo.stderr = w
return nil
}
}

var errFusermountPathInvalid = errors.New("fusermount path must be relative or absolute")

// OptUnmountFusermountPath sets the path to the fusermount binary.
func OptUnmountFusermountPath(path string) UnmountOpt {
return func(mo *unmountOpts) error {
// OptUnmountFUSEFusermountPath sets the path to the fusermount binary.
func OptUnmountFUSEFusermountPath(path string) UnmountFUSEOpt {
return func(mo *unmountFUSEOpts) error {
if filepath.Base(path) == path {
return errFusermountPathInvalid
}
Expand All @@ -74,16 +74,16 @@ func OptUnmountFusermountPath(path string) UnmountOpt {
}
}

// Unmount the FUSE mounted filesystem at mountPath.
// UnmountFUSE unmounts the FUSE mounted filesystem at mountPath.
//
// Unmount may start one or more underlying processes. By default, stdout and stderr of these
// UnmountFUSE may start one or more underlying processes. By default, stdout and stderr of these
// processes is discarded. To modify this behavior, consider using OptUnmountStdout and/or
// OptUnmountStderr.
//
// By default, Unmount searches for a fusermount binary in the directories named by the PATH
// By default, UnmountFUSE searches for a fusermount binary in the directories named by the PATH
// environment variable. To override this behavior, consider using OptUnmountFusermountPath().
func Unmount(ctx context.Context, mountPath string, opts ...UnmountOpt) error {
uo := unmountOpts{
func UnmountFUSE(ctx context.Context, mountPath string, opts ...UnmountFUSEOpt) error {
uo := unmountFUSEOpts{
fusermountPath: "fusermount",
}

Expand Down
16 changes: 7 additions & 9 deletions internal/pkg/exp/unmount_test.go → pkg/sif/unmount_test.go
Expand Up @@ -7,7 +7,7 @@
// LICENSE file distributed with the sources of this project regarding your
// rights to use or distribute this software.

package exp
package sif

import (
"bufio"
Expand All @@ -21,9 +21,7 @@ import (
"testing"
)

var corpus = filepath.Join("..", "..", "..", "test", "images")

func Test_Unmount(t *testing.T) {
func Test_UnmountFUSE(t *testing.T) {
if _, err := exec.LookPath("squashfuse"); err != nil {
t.Skip(" not found, skipping mount tests")
}
Expand All @@ -44,7 +42,7 @@ func Test_Unmount(t *testing.T) {
name string
mountSIF string
mountPath string
opts []UnmountOpt
opts []UnmountFUSEOpt
wantErr bool
wantUnmounted bool
}{
Expand All @@ -71,28 +69,28 @@ func Test_Unmount(t *testing.T) {
name: "FusermountBare",
mountSIF: "",
mountPath: path,
opts: []UnmountOpt{OptUnmountFusermountPath("fusermount")},
opts: []UnmountFUSEOpt{OptUnmountFUSEFusermountPath("fusermount")},
wantErr: true,
},
{
name: "FusermountValid",
mountSIF: filepath.Join(corpus, "one-group.sif"),
mountPath: path,
opts: []UnmountOpt{OptUnmountFusermountPath(fusermountPath)},
opts: []UnmountFUSEOpt{OptUnmountFUSEFusermountPath(fusermountPath)},
wantErr: false,
wantUnmounted: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.mountSIF != "" {
err := Mount(context.Background(), tt.mountSIF, path)
err := MountFUSE(context.Background(), tt.mountSIF, path)
if err != nil {
t.Fatal(err)
}
}

err := Unmount(context.Background(), tt.mountPath, tt.opts...)
err := UnmountFUSE(context.Background(), tt.mountPath, tt.opts...)

if err != nil && !tt.wantErr {
t.Errorf("Unexpected error: %s", err)
Expand Down
4 changes: 2 additions & 2 deletions pkg/siftool/unmount_test.go
Expand Up @@ -16,7 +16,7 @@ import (
"path/filepath"
"testing"

"github.com/apptainer/sif/v2/internal/pkg/exp"
"github.com/apptainer/sif/v2/pkg/sif"
)

func Test_command_getUnmount(t *testing.T) {
Expand All @@ -36,7 +36,7 @@ func Test_command_getUnmount(t *testing.T) {
})

testSIF := filepath.Join(corpus, "one-group.sif")
if err := exp.Mount(context.Background(), testSIF, path); err != nil {
if err := sif.MountFUSE(context.Background(), testSIF, path); err != nil {
t.Fatal(err)
}

Expand Down

0 comments on commit e621bf4

Please sign in to comment.