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
34 changes: 22 additions & 12 deletions internal/pkg/build/sources/conveyorPacker_local.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,14 @@ import (
"github.com/sylabs/singularity/v4/pkg/sylog"
)

// LocalConveyor only needs to hold the conveyor to have the needed data to pack
type LocalConveyor struct {
src string
}

// LocalPacker ...
type LocalPacker interface {
Pack(context.Context) (*types.Bundle, error)
}

// LocalConveyorPacker only needs to hold the conveyor to have the needed data to pack
type LocalConveyorPacker struct {
LocalConveyor
LocalPacker
localPacker LocalPacker
}

// GetLocalPacker ...
Expand Down Expand Up @@ -104,13 +98,29 @@ func GetLocalPacker(ctx context.Context, src string, b *types.Bundle) (LocalPack

// Get just stores the source.
func (cp *LocalConveyorPacker) Get(ctx context.Context, b *types.Bundle) (err error) {
// insert base metadata before unpacking fs
src := filepath.Clean(b.Recipe.Header["from"])

cp.localPacker, err = GetLocalPacker(ctx, src, b)
return err
}

// Delegate to local packer then insert base env
func (cp *LocalConveyorPacker) Pack(ctx context.Context) (*types.Bundle, error) {
sylog.Infof("Extracting local image...")
b, err := cp.localPacker.Pack(ctx)
if err != nil {
return nil, fmt.Errorf("while unpacking local image: %v", err)
}

// insert base metadata AFTER unpacking fs to avoid conflicts with contained files/symlinks
if err = makeBaseEnv(b.RootfsPath); err != nil {
return fmt.Errorf("while inserting base environment: %v", err)
return nil, fmt.Errorf("while inserting base environment: %v", err)
}

cp.src = filepath.Clean(b.Recipe.Header["from"])
sylog.Infof("Inserting Singularity configuration...")
if err = makeBaseEnv(b.RootfsPath); err != nil {
return nil, fmt.Errorf("while inserting base environment: %v", err)
}

cp.LocalPacker, err = GetLocalPacker(ctx, cp.src, b)
return err
return b, nil
}
124 changes: 124 additions & 0 deletions internal/pkg/build/sources/conveyorPacker_local_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// Copyright (c) 2018-2024, Sylabs Inc. All rights reserved.
// This software is licensed under a 3-clause BSD license. Please consult the
// LICENSE.md file distributed with the sources of this project regarding your
// rights to use or distribute this software.

package sources_test

import (
"context"
"os"
"os/exec"
"path/filepath"
"testing"

"github.com/sylabs/singularity/v4/internal/pkg/build/sources"
"github.com/sylabs/singularity/v4/internal/pkg/image/packer"
"github.com/sylabs/singularity/v4/internal/pkg/util/fs"
"github.com/sylabs/singularity/v4/pkg/build/types"
)

func createArchiveFromDir(dir string, t *testing.T) *os.File {
mk, err := exec.LookPath("mksquashfs")
if err != nil {
t.SkipNow()
}
f, err := os.CreateTemp("", "archive-")
if err != nil {
t.Fatal(err)
}
cmd := exec.Command(mk, dir, f.Name(), "-noappend", "-no-progress")
if err := cmd.Run(); err != nil {
t.Fatal(err)
}
return f
}

func makeDir(path string, t *testing.T) {
if err := os.MkdirAll(path, 0o755); err != nil {
t.Fatalf("while creating directory %s: %v", path, err)
}
}

func isExist(path string) bool {
result, _ := fs.PathExists(path)
return result
}

func TestSquashfsInput(t *testing.T) {
if s := packer.NewSquashfs(); !s.HasMksquashfs() {
t.Skip("mksquashfs not found, skipping")
}

dir, err := os.MkdirTemp(os.TempDir(), "test-localpacker-squashfs-")
if err != nil {
t.Fatalf("while creating tmpdir: %v", err)
}
defer os.RemoveAll(dir)

// Check that folder symlinks in the image to common folders (in base env) work
inputDir := filepath.Join(dir, "input")

makeDir(filepath.Join(dir, "var", "tmp"), t)
// Symlink /var/tmp -> /tmp in image
makeDir(filepath.Join(inputDir, "tmp"), t)
makeDir(filepath.Join(inputDir, "var"), t)
if err := os.Symlink("../tmp", filepath.Join(inputDir, "var", "tmp")); err != nil {
t.Fatalf("while creating symlink: %v", err)
}
// And a file we can check for
testfile := "conveyorPacker_local_test.go"
data, err := os.ReadFile(testfile)
if err != nil {
t.Fatalf("while reading test file: %v", err)
}
if err := os.WriteFile(filepath.Join(inputDir, testfile), data, 0o644); err != nil {
t.Fatalf("while writing test file: %v", err)
}
archive := createArchiveFromDir(inputDir, t)
defer os.Remove(archive.Name())

bundleTmp, _ := os.MkdirTemp(os.TempDir(), "bundle-tmp-")
b, err := types.NewBundle(dir, bundleTmp)
if err != nil {
t.Fatalf("while creating bundle: %v", err)
}
b.Recipe, _ = types.NewDefinitionFromURI("localimage://" + archive.Name())

lcp := &sources.LocalConveyorPacker{}
if err := lcp.Get(context.Background(), b); err != nil {
t.Fatalf("while getting local packer: %v", err)
}

_, err = lcp.Pack(context.Background())
if err != nil {
t.Fatalf("failed to Pack from %s: %v\n", archive.Name(), err)
}
rootfs := b.RootfsPath

// check if testfile was extracted
path := filepath.Join(rootfs, testfile)
if !isExist(path) {
t.Errorf("extraction failed, %s is missing", path)
}
// Check folders and symlinks
path = filepath.Join(rootfs, "tmp")
if !fs.IsDir(path) {
t.Errorf("extraction failed, %s is missing", path)
}
path = filepath.Join(rootfs, "var")
if !fs.IsDir(path) {
t.Errorf("extraction failed, %s is missing", path)
}
path = filepath.Join(rootfs, "var", "tmp")
if !isExist(path) {
t.Errorf("extraction failed, %s is missing", path)
} else if !fs.IsLink(path) {
t.Errorf("extraction failed, %s is not a symlink", path)
} else {
tgt, _ := os.Readlink(path)
if tgt != "../tmp" {
t.Errorf("extraction failed, %s wrongly points to %s", path, tgt)
}
}
}