Files
aptly/files/public_test.go
T
André Roth 80e4e9bdac publish: fix race conditions
* remove useless resource lock
  Resource locks need to be before the background task. creating same publish endpoint at the same time is unlikely...
* load data inside background tasks
  This fixes a flaw in async apis, which loaded the published repo from the DB and mutated it outside the task closure, before the task lock was acquired.
  Perform collection.LoadComplete inside maybeRunTaskInBackground and have tasks use a fresh copy of taskCollectionFactory, taskCollection
* lock source repos/snapshots for publish operations
  Concurrent tasks were not properly locking their resources, leading to inconsistent published indexes:
  SourceLocalRepo: iterate published.Sources (component -> source UUID), look up each local repo via localRepoCollection.ByUUID and append string(repo.Key()) to resources
  SourceSnapshot: iterate b.Snapshots,look up each snapshot via snapshotCollection.ByName and append string(snapshot.ResourceKey()) to resources.
* lock pool on non MultiDist publish
* revert mutex on LinkFromPool
* use uuids, since names can be renamed
2026-06-11 12:25:49 +02:00

716 lines
24 KiB
Go

package files
import (
"bytes"
"errors"
"io"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"syscall"
"github.com/aptly-dev/aptly/aptly"
"github.com/aptly-dev/aptly/utils"
. "gopkg.in/check.v1"
)
type fakeProgress struct{ bytes.Buffer }
func (p *fakeProgress) Start() {}
func (p *fakeProgress) Shutdown() {}
func (p *fakeProgress) Flush() {}
func (p *fakeProgress) InitBar(count int64, isBytes bool, barType aptly.BarType) {
}
func (p *fakeProgress) ShutdownBar() {}
func (p *fakeProgress) AddBar(count int) {}
func (p *fakeProgress) SetBar(count int) {}
func (p *fakeProgress) Printf(msg string, a ...interface{}) {
}
func (p *fakeProgress) ColoredPrintf(msg string, a ...interface{}) {
}
func (p *fakeProgress) PrintfStdErr(msg string, a ...interface{}) {
}
type fakeRSC struct {
*bytes.Reader
closeErr error
}
func (r *fakeRSC) Close() error { return r.closeErr }
type fakePool struct {
sizeErr error
openFn func(string) (aptly.ReadSeekerCloser, error)
}
type fakeLocalPool struct {
fakePool
statErr error
}
func (p *fakeLocalPool) Stat(path string) (os.FileInfo, error) { return nil, p.statErr }
func (p *fakeLocalPool) GenerateTempPath(filename string) (string, error) {
return "", nil
}
func (p *fakeLocalPool) Link(path, dstPath string) error { return nil }
func (p *fakeLocalPool) Symlink(path, dstPath string) error { return nil }
func (p *fakeLocalPool) FullPath(path string) string { return path }
func (p *fakePool) Verify(poolPath, basename string, checksums *utils.ChecksumInfo, checksumStorage aptly.ChecksumStorage) (string, bool, error) {
return "", false, nil
}
func (p *fakePool) Import(srcPath, basename string, checksums *utils.ChecksumInfo, move bool, storage aptly.ChecksumStorage) (string, error) {
return "", nil
}
func (p *fakePool) LegacyPath(filename string, checksums *utils.ChecksumInfo) (string, error) {
return "", nil
}
func (p *fakePool) Size(path string) (int64, error) {
if p.sizeErr != nil {
return 0, p.sizeErr
}
return int64(len(path)), nil
}
func (p *fakePool) Open(path string) (aptly.ReadSeekerCloser, error) {
if p.openFn != nil {
return p.openFn(path)
}
return nil, io.EOF
}
func (p *fakePool) FilepathList(progress aptly.Progress) ([]string, error) { return nil, nil }
func (p *fakePool) Remove(path string) (int64, error) { return 0, nil }
type PublishedStorageSuite struct {
root string
storage *PublishedStorage
storageSymlink *PublishedStorage
storageCopy *PublishedStorage
storageCopySize *PublishedStorage
cs aptly.ChecksumStorage
}
var _ = Suite(&PublishedStorageSuite{})
func (s *PublishedStorageSuite) SetUpTest(c *C) {
s.root = c.MkDir()
s.storage = NewPublishedStorage(filepath.Join(s.root, "public"), "", "")
s.storageSymlink = NewPublishedStorage(filepath.Join(s.root, "public_symlink"), "symlink", "")
s.storageCopy = NewPublishedStorage(filepath.Join(s.root, "public_copy"), "copy", "")
s.storageCopySize = NewPublishedStorage(filepath.Join(s.root, "public_copysize"), "copy", "size")
s.cs = NewMockChecksumStorage()
}
func (s *PublishedStorageSuite) TestLinkMethodField(c *C) {
c.Assert(s.storage.linkMethod, Equals, LinkMethodHardLink)
c.Assert(s.storageSymlink.linkMethod, Equals, LinkMethodSymLink)
c.Assert(s.storageCopy.linkMethod, Equals, LinkMethodCopy)
c.Assert(s.storageCopySize.linkMethod, Equals, LinkMethodCopy)
}
func (s *PublishedStorageSuite) TestVerifyMethodField(c *C) {
c.Assert(s.storageCopy.verifyMethod, Equals, VerificationMethodChecksum)
c.Assert(s.storageCopySize.verifyMethod, Equals, VerificationMethodFileSize)
}
func (s *PublishedStorageSuite) TestPublicPath(c *C) {
c.Assert(s.storage.PublicPath(), Equals, filepath.Join(s.root, "public"))
c.Assert(s.storageSymlink.PublicPath(), Equals, filepath.Join(s.root, "public_symlink"))
c.Assert(s.storageCopy.PublicPath(), Equals, filepath.Join(s.root, "public_copy"))
c.Assert(s.storageCopySize.PublicPath(), Equals, filepath.Join(s.root, "public_copysize"))
}
func (s *PublishedStorageSuite) TestMkDir(c *C) {
err := s.storage.MkDir("ppa/dists/squeeze/")
c.Assert(err, IsNil)
_, err = os.Stat(filepath.Join(s.storage.rootPath, "ppa/dists/squeeze/"))
c.Assert(err, IsNil)
}
func (s *PublishedStorageSuite) TestPutFile(c *C) {
err := s.storage.MkDir("ppa/dists/squeeze/")
c.Assert(err, IsNil)
err = s.storage.PutFile("ppa/dists/squeeze/Release", "/dev/null")
c.Assert(err, IsNil)
_, err = os.Stat(filepath.Join(s.storage.rootPath, "ppa/dists/squeeze/Release"))
c.Assert(err, IsNil)
}
func (s *PublishedStorageSuite) TestPutFileReturnsErrorIfSourceMissing(c *C) {
err := s.storage.MkDir("ppa/dists/squeeze/")
c.Assert(err, IsNil)
err = s.storage.PutFile("ppa/dists/squeeze/Release", filepath.Join(s.root, "no-such-file"))
c.Assert(err, NotNil)
}
func (s *PublishedStorageSuite) TestFilelist(c *C) {
err := s.storage.MkDir("ppa/pool/main/a/ab/")
c.Assert(err, IsNil)
err = s.storage.PutFile("ppa/pool/main/a/ab/a.deb", "/dev/null")
c.Assert(err, IsNil)
err = s.storage.PutFile("ppa/pool/main/a/ab/b.deb", "/dev/null")
c.Assert(err, IsNil)
list, err := s.storage.Filelist("ppa/pool/main/")
c.Check(err, IsNil)
c.Check(list, DeepEquals, []string{"a/ab/a.deb", "a/ab/b.deb"})
list, err = s.storage.Filelist("ppa/pool/doenstexist/")
c.Check(err, IsNil)
c.Check(list, DeepEquals, []string{})
}
func (s *PublishedStorageSuite) TestRenameFile(c *C) {
err := s.storage.MkDir("ppa/dists/squeeze/")
c.Assert(err, IsNil)
err = s.storage.PutFile("ppa/dists/squeeze/Release", "/dev/null")
c.Assert(err, IsNil)
err = s.storage.RenameFile("ppa/dists/squeeze/Release", "ppa/dists/squeeze/InRelease")
c.Check(err, IsNil)
_, err = os.Stat(filepath.Join(s.storage.rootPath, "ppa/dists/squeeze/InRelease"))
c.Assert(err, IsNil)
}
func (s *PublishedStorageSuite) TestFileExists(c *C) {
err := s.storage.MkDir("ppa/dists/squeeze/")
c.Assert(err, IsNil)
exists, _ := s.storage.FileExists("ppa/dists/squeeze/Release")
c.Check(exists, Equals, false)
err = s.storage.PutFile("ppa/dists/squeeze/Release", "/dev/null")
c.Assert(err, IsNil)
exists, _ = s.storage.FileExists("ppa/dists/squeeze/Release")
c.Check(exists, Equals, true)
}
func (s *PublishedStorageSuite) TestSymLink(c *C) {
err := s.storage.MkDir("ppa/dists/squeeze/")
c.Assert(err, IsNil)
err = s.storage.PutFile("ppa/dists/squeeze/Release", "/dev/null")
c.Assert(err, IsNil)
err = s.storage.SymLink("ppa/dists/squeeze/Release", "ppa/dists/squeeze/InRelease")
c.Assert(err, IsNil)
exists, _ := s.storage.FileExists("ppa/dists/squeeze/InRelease")
c.Check(exists, Equals, true)
linkTarget, err := s.storage.ReadLink("ppa/dists/squeeze/InRelease")
c.Assert(err, IsNil)
c.Assert(linkTarget, Equals, "ppa/dists/squeeze/Release")
}
func (s *PublishedStorageSuite) TestReadLinkReturnsErrorOnMissingPath(c *C) {
_, err := s.storage.ReadLink("does/not/exist")
c.Assert(err, NotNil)
}
func (s *PublishedStorageSuite) TestHardLink(c *C) {
err := s.storage.MkDir("ppa/dists/squeeze/")
c.Assert(err, IsNil)
err = s.storage.PutFile("ppa/dists/squeeze/Release", "/dev/null")
c.Assert(err, IsNil)
err = s.storage.HardLink("ppa/dists/squeeze/Release", "ppa/dists/squeeze/InRelease")
c.Assert(err, IsNil)
exists, _ := s.storage.FileExists("ppa/dists/squeeze/InRelease")
c.Check(exists, Equals, true)
}
func (s *PublishedStorageSuite) TestRemoveDirs(c *C) {
err := s.storage.MkDir("ppa/dists/squeeze/")
c.Assert(err, IsNil)
err = s.storage.PutFile("ppa/dists/squeeze/Release", "/dev/null")
c.Assert(err, IsNil)
err = s.storage.RemoveDirs("ppa/dists/", nil)
c.Assert(err, IsNil)
_, err = os.Stat(filepath.Join(s.storage.rootPath, "ppa/dists/squeeze/Release"))
c.Assert(err, NotNil)
c.Assert(os.IsNotExist(err), Equals, true)
}
func (s *PublishedStorageSuite) TestRemoveDirsWithProgress(c *C) {
err := s.storage.MkDir("ppa/dists/squeeze/")
c.Assert(err, IsNil)
err = s.storage.PutFile("ppa/dists/squeeze/Release", "/dev/null")
c.Assert(err, IsNil)
p := &fakeProgress{}
err = s.storage.RemoveDirs("ppa/dists/", p)
c.Assert(err, IsNil)
}
func (s *PublishedStorageSuite) TestRemove(c *C) {
err := s.storage.MkDir("ppa/dists/squeeze/")
c.Assert(err, IsNil)
err = s.storage.PutFile("ppa/dists/squeeze/Release", "/dev/null")
c.Assert(err, IsNil)
err = s.storage.Remove("ppa/dists/squeeze/Release")
c.Assert(err, IsNil)
_, err = os.Stat(filepath.Join(s.storage.rootPath, "ppa/dists/squeeze/Release"))
c.Assert(err, NotNil)
c.Assert(os.IsNotExist(err), Equals, true)
}
func (s *PublishedStorageSuite) TestLinkFromPool(c *C) {
tests := []struct {
prefix string
sourcePath string
publishedDirectory string
expectedFilename string
}{
{ // package name regular
prefix: "",
sourcePath: "mars-invaders_1.03.deb",
publishedDirectory: "pool/main/m/mars-invaders",
expectedFilename: "pool/main/m/mars-invaders/mars-invaders_1.03.deb",
},
{ // lib-like filename
prefix: "",
sourcePath: "libmars-invaders_1.03.deb",
publishedDirectory: "pool/main/libm/libmars-invaders",
expectedFilename: "pool/main/libm/libmars-invaders/libmars-invaders_1.03.deb",
},
{ // duplicate link, shouldn't panic
prefix: "",
sourcePath: "mars-invaders_1.03.deb",
publishedDirectory: "pool/main/m/mars-invaders",
expectedFilename: "pool/main/m/mars-invaders/mars-invaders_1.03.deb",
},
{ // prefix & component
prefix: "ppa",
sourcePath: "libmars-invaders_1.04.deb",
publishedDirectory: "pool/contrib/libm/libmars-invaders",
expectedFilename: "pool/contrib/libm/libmars-invaders/libmars-invaders_1.04.deb",
},
{ // installer file
prefix: "",
sourcePath: "netboot/boot.img.gz",
publishedDirectory: "dists/jessie/non-free/installer-i386/current/images",
expectedFilename: "dists/jessie/non-free/installer-i386/current/images/netboot/boot.img.gz",
},
}
pool := NewPackagePool(s.root, false)
for _, t := range tests {
tmpPath := filepath.Join(c.MkDir(), t.sourcePath)
_ = os.MkdirAll(filepath.Dir(tmpPath), 0777)
err := os.WriteFile(tmpPath, []byte("Contents"), 0644)
c.Assert(err, IsNil)
sourceChecksum, err := utils.ChecksumsForFile(tmpPath)
c.Assert(err, IsNil)
srcPoolPath, err := pool.Import(tmpPath, t.sourcePath, &utils.ChecksumInfo{MD5: "c1df1da7a1ce305a3b60af9d5733ac1d"}, false, s.cs)
c.Assert(err, IsNil)
// Test using hardlinks
err = s.storage.LinkFromPool(t.prefix, t.publishedDirectory, t.sourcePath, pool, srcPoolPath, sourceChecksum, false)
c.Assert(err, IsNil)
st, err := os.Stat(filepath.Join(s.storage.rootPath, t.prefix, t.expectedFilename))
c.Assert(err, IsNil)
info := st.Sys().(*syscall.Stat_t)
c.Check(int(info.Nlink), Equals, 3)
// Test using symlinks
err = s.storageSymlink.LinkFromPool(t.prefix, t.publishedDirectory, t.sourcePath, pool, srcPoolPath, sourceChecksum, false)
c.Assert(err, IsNil)
st, err = os.Lstat(filepath.Join(s.storageSymlink.rootPath, t.prefix, t.expectedFilename))
c.Assert(err, IsNil)
info = st.Sys().(*syscall.Stat_t)
c.Check(int(info.Nlink), Equals, 1)
c.Check(int(info.Mode&syscall.S_IFMT), Equals, int(syscall.S_IFLNK))
// Test using copy with checksum verification
err = s.storageCopy.LinkFromPool(t.prefix, t.publishedDirectory, t.sourcePath, pool, srcPoolPath, sourceChecksum, false)
c.Assert(err, IsNil)
st, err = os.Stat(filepath.Join(s.storageCopy.rootPath, t.prefix, t.expectedFilename))
c.Assert(err, IsNil)
info = st.Sys().(*syscall.Stat_t)
c.Check(int(info.Nlink), Equals, 1)
// Test using copy with size verification
err = s.storageCopySize.LinkFromPool(t.prefix, t.publishedDirectory, t.sourcePath, pool, srcPoolPath, sourceChecksum, false)
c.Assert(err, IsNil)
st, err = os.Stat(filepath.Join(s.storageCopySize.rootPath, t.prefix, t.expectedFilename))
c.Assert(err, IsNil)
info = st.Sys().(*syscall.Stat_t)
c.Check(int(info.Nlink), Equals, 1)
}
// test linking files to duplicate final name
tmpPath := filepath.Join(c.MkDir(), "mars-invaders_1.03.deb")
err := os.WriteFile(tmpPath, []byte("cONTENTS"), 0644)
c.Assert(err, IsNil)
sourceChecksum, err := utils.ChecksumsForFile(tmpPath)
c.Assert(err, IsNil)
srcPoolPath, err := pool.Import(tmpPath, "mars-invaders_1.03.deb", &utils.ChecksumInfo{MD5: "02bcda7a1ce305a3b60af9d5733ac1d"}, true, s.cs)
c.Assert(err, IsNil)
st, err := pool.Stat(srcPoolPath)
c.Assert(err, IsNil)
nlinks := int(st.Sys().(*syscall.Stat_t).Nlink)
err = s.storage.LinkFromPool("", filepath.Join("pool", "main", "m/mars-invaders"), "mars-invaders_1.03.deb", pool, srcPoolPath, sourceChecksum, false)
c.Check(err, ErrorMatches, ".*file already exists and is different")
st, err = pool.Stat(srcPoolPath)
c.Assert(err, IsNil)
c.Check(int(st.Sys().(*syscall.Stat_t).Nlink), Equals, nlinks)
// linking with force
err = s.storage.LinkFromPool("", filepath.Join("pool", "main", "m/mars-invaders"), "mars-invaders_1.03.deb", pool, srcPoolPath, sourceChecksum, true)
c.Check(err, IsNil)
st, err = pool.Stat(srcPoolPath)
c.Assert(err, IsNil)
c.Check(int(st.Sys().(*syscall.Stat_t).Nlink), Equals, nlinks+1)
// Test using symlinks
err = s.storageSymlink.LinkFromPool("", filepath.Join("pool", "main", "m/mars-invaders"), "mars-invaders_1.03.deb", pool, srcPoolPath, sourceChecksum, false)
c.Check(err, ErrorMatches, ".*file already exists and is different")
err = s.storageSymlink.LinkFromPool("", filepath.Join("pool", "main", "m/mars-invaders"), "mars-invaders_1.03.deb", pool, srcPoolPath, sourceChecksum, true)
c.Check(err, IsNil)
// Test using copy with checksum verification
err = s.storageCopy.LinkFromPool("", filepath.Join("pool", "main", "m/mars-invaders"), "mars-invaders_1.03.deb", pool, srcPoolPath, sourceChecksum, false)
c.Check(err, ErrorMatches, ".*file already exists and is different")
err = s.storageCopy.LinkFromPool("", filepath.Join("pool", "main", "m/mars-invaders"), "mars-invaders_1.03.deb", pool, srcPoolPath, sourceChecksum, true)
c.Check(err, IsNil)
// Test using copy with size verification (this will NOT detect the difference)
err = s.storageCopySize.LinkFromPool("", filepath.Join("pool", "main", "m/mars-invaders"), "mars-invaders_1.03.deb", pool, srcPoolPath, sourceChecksum, false)
c.Check(err, IsNil)
}
func (s *PublishedStorageSuite) TestRootRemove(c *C) {
// Prevent deletion of the root directory by passing empty subpaths.
pwd := c.MkDir()
// Symlink
linkedDir := filepath.Join(pwd, "linkedDir")
_ = os.Symlink(s.root, linkedDir)
linkStorage := NewPublishedStorage(linkedDir, "", "")
c.Assert(func() { _ = linkStorage.Remove("") }, PanicMatches, "trying to remove empty path")
// Actual dir
dirStorage := NewPublishedStorage(pwd, "", "")
c.Assert(func() { _ = dirStorage.RemoveDirs("", nil) }, PanicMatches, "trying to remove the root directory")
}
// DiskFullSuite uses a loopback mount; requires Linux + root.
type DiskFullSuite struct {
root string
}
var _ = Suite(&DiskFullSuite{})
func (s *DiskFullSuite) SetUpTest(c *C) {
if runtime.GOOS != "linux" {
c.Skip("disk full tests only run on Linux")
}
s.root = c.MkDir()
}
func (s *DiskFullSuite) TestPutFileOutOfSpace(c *C) {
mountPoint := "/smallfs"
if os.Geteuid() == 0 {
mountPoint = filepath.Join(s.root, "smallfs")
err := os.MkdirAll(mountPoint, 0777)
c.Assert(err, IsNil)
fsImage := filepath.Join(s.root, "small.img")
cmd := exec.Command("dd", "if=/dev/zero", "of="+fsImage, "bs=1M", "count=1")
err = cmd.Run()
c.Assert(err, IsNil)
cmd = exec.Command("mkfs.ext4", "-F", fsImage)
err = cmd.Run()
c.Assert(err, IsNil)
cmd = exec.Command("mount", "-o", "loop", fsImage, mountPoint)
err = cmd.Run()
c.Assert(err, IsNil)
defer func() {
_ = exec.Command("umount", mountPoint).Run()
}()
}
storage := NewPublishedStorage(mountPoint, "", "")
largeFile := filepath.Join(s.root, "largefile")
cmd := exec.Command("dd", "if=/dev/zero", "of="+largeFile, "bs=1M", "count=2")
err := cmd.Run()
c.Assert(err, IsNil)
err = storage.PutFile("testfile", largeFile)
c.Assert(err, NotNil)
c.Check(strings.Contains(err.Error(), "no space left on device") ||
strings.Contains(err.Error(), "sync"), Equals, true,
Commentf("Expected disk full error, got: %v", err))
}
func (s *DiskFullSuite) TestLinkFromPoolCopyOutOfSpace(c *C) {
mountPoint := "/smallfs"
if os.Geteuid() == 0 {
mountPoint = filepath.Join(s.root, "smallfs")
err := os.MkdirAll(mountPoint, 0777)
c.Assert(err, IsNil)
fsImage := filepath.Join(s.root, "small.img")
cmd := exec.Command("dd", "if=/dev/zero", "of="+fsImage, "bs=1M", "count=1")
err = cmd.Run()
c.Assert(err, IsNil)
cmd = exec.Command("mkfs.ext4", "-F", fsImage)
err = cmd.Run()
c.Assert(err, IsNil)
cmd = exec.Command("mount", "-o", "loop", fsImage, mountPoint)
err = cmd.Run()
c.Assert(err, IsNil)
defer func() {
_ = exec.Command("umount", mountPoint).Run()
}()
}
storage := NewPublishedStorage(mountPoint, "copy", "")
poolPath := filepath.Join(s.root, "pool")
pool := NewPackagePool(poolPath, false)
cs := NewMockChecksumStorage()
largeFile := filepath.Join(s.root, "package.deb")
cmd := exec.Command("dd", "if=/dev/zero", "of="+largeFile, "bs=1M", "count=2")
err := cmd.Run()
c.Assert(err, IsNil)
sourceChecksum, err := utils.ChecksumsForFile(largeFile)
c.Assert(err, IsNil)
srcPoolPath, err := pool.Import(largeFile, "package.deb",
&utils.ChecksumInfo{MD5: "d41d8cd98f00b204e9800998ecf8427e"}, false, cs)
c.Assert(err, IsNil)
err = storage.LinkFromPool("", "pool/main/p/package", "package.deb",
pool, srcPoolPath, sourceChecksum, false)
c.Assert(err, NotNil)
c.Check(strings.Contains(err.Error(), "no space left on device") ||
strings.Contains(err.Error(), "sync"), Equals, true,
Commentf("Expected disk full error, got: %v", err))
}
type DiskFullNoRootSuite struct {
root string
}
var _ = Suite(&DiskFullNoRootSuite{})
func (s *DiskFullNoRootSuite) SetUpTest(c *C) {
s.root = c.MkDir()
}
func (s *DiskFullNoRootSuite) TestSyncIsCalled(c *C) {
storage := NewPublishedStorage(s.root, "", "")
sourceFile := filepath.Join(s.root, "source.txt")
err := os.WriteFile(sourceFile, []byte("test content"), 0644)
c.Assert(err, IsNil)
err = storage.PutFile("dest.txt", sourceFile)
c.Assert(err, IsNil)
content, err := os.ReadFile(filepath.Join(s.root, "dest.txt"))
c.Assert(err, IsNil)
c.Check(string(content), Equals, "test content")
}
func (s *DiskFullNoRootSuite) TestLinkFromPoolCopySyncIsCalled(c *C) {
storage := NewPublishedStorage(s.root, "copy", "")
poolPath := filepath.Join(s.root, "pool")
pool := NewPackagePool(poolPath, false)
cs := NewMockChecksumStorage()
pkgFile := filepath.Join(s.root, "package.deb")
err := os.WriteFile(pkgFile, []byte("package content"), 0644)
c.Assert(err, IsNil)
sourceChecksum, err := utils.ChecksumsForFile(pkgFile)
c.Assert(err, IsNil)
srcPoolPath, err := pool.Import(pkgFile, "package.deb",
&utils.ChecksumInfo{MD5: "d41d8cd98f00b204e9800998ecf8427e"}, false, cs)
c.Assert(err, IsNil)
err = storage.LinkFromPool("", "pool/main/p/package", "package.deb",
pool, srcPoolPath, sourceChecksum, false)
c.Assert(err, IsNil)
destPath := filepath.Join(s.root, "pool/main/p/package/package.deb")
content, err := os.ReadFile(destPath)
c.Assert(err, IsNil)
c.Check(string(content), Equals, "package content")
}
func (s *DiskFullNoRootSuite) TestPutFileSyncErrorIsReturned(c *C) {
storage := NewPublishedStorage(s.root, "", "")
sourceFile := filepath.Join(s.root, "source-syncfail.txt")
err := os.WriteFile(sourceFile, []byte("test content"), 0644)
c.Assert(err, IsNil)
oldSyncFile := syncFile
syncFile = func(_ *os.File) error { return syscall.ENOSPC }
defer func() { syncFile = oldSyncFile }()
err = storage.PutFile("dest-syncfail.txt", sourceFile)
c.Assert(err, NotNil)
c.Check(strings.Contains(err.Error(), "error syncing file"), Equals, true)
}
func (s *DiskFullNoRootSuite) TestLinkFromPoolCopySyncErrorIsReturned(c *C) {
storage := NewPublishedStorage(s.root, "copy", "")
poolPath := filepath.Join(s.root, "pool")
pool := NewPackagePool(poolPath, false)
cs := NewMockChecksumStorage()
pkgFile := filepath.Join(s.root, "package-syncfail.deb")
err := os.WriteFile(pkgFile, []byte("package content"), 0644)
c.Assert(err, IsNil)
sourceChecksum, err := utils.ChecksumsForFile(pkgFile)
c.Assert(err, IsNil)
srcPoolPath, err := pool.Import(pkgFile, "package-syncfail.deb",
&utils.ChecksumInfo{MD5: "d41d8cd98f00b204e9800998ecf8427e"}, false, cs)
c.Assert(err, IsNil)
oldSyncFile := syncFile
syncFile = func(_ *os.File) error { return syscall.ENOSPC }
defer func() { syncFile = oldSyncFile }()
err = storage.LinkFromPool("", "pool/main/p/package", "package-syncfail.deb",
pool, srcPoolPath, sourceChecksum, false)
c.Assert(err, NotNil)
c.Check(strings.Contains(err.Error(), "error syncing file"), Equals, true)
}
func (s *DiskFullNoRootSuite) TestPutFileFailsIfDestinationDirMissing(c *C) {
storage := NewPublishedStorage(s.root, "", "")
sourceFile := filepath.Join(s.root, "src.txt")
err := os.WriteFile(sourceFile, []byte("x"), 0644)
c.Assert(err, IsNil)
err = storage.PutFile("missingdir/dest.txt", sourceFile)
c.Assert(err, NotNil)
}
func (s *DiskFullNoRootSuite) TestLinkFromPoolRejectsNonLocalPoolForHardlink(c *C) {
storage := NewPublishedStorage(s.root, "", "")
pool := &fakePool{}
err := storage.LinkFromPool("", "pool/main/p/pkg", "x.deb", pool, "x", utils.ChecksumInfo{MD5: "x"}, false)
c.Assert(err, NotNil)
c.Check(strings.Contains(err.Error(), "cannot link"), Equals, true)
}
func (s *DiskFullNoRootSuite) TestLinkFromPoolCopyReturnsErrorIfOpenFails(c *C) {
storage := NewPublishedStorage(s.root, "copy", "")
pool := &fakePool{openFn: func(string) (aptly.ReadSeekerCloser, error) { return nil, io.ErrUnexpectedEOF }}
err := storage.LinkFromPool("", "pool/main/p/pkg", "x.deb", pool, "x", utils.ChecksumInfo{MD5: "x"}, false)
c.Assert(err, NotNil)
}
func (s *DiskFullNoRootSuite) TestLinkFromPoolCopyReturnsErrorIfReaderCloseFails(c *C) {
storage := NewPublishedStorage(s.root, "copy", "")
pool := &fakePool{openFn: func(string) (aptly.ReadSeekerCloser, error) {
return &fakeRSC{Reader: bytes.NewReader([]byte("data")), closeErr: io.ErrClosedPipe}, nil
}}
err := storage.LinkFromPool("", "pool/main/p/pkg", "x.deb", pool, "x", utils.ChecksumInfo{MD5: "x"}, false)
c.Assert(err, NotNil)
c.Check(err, Equals, io.ErrClosedPipe)
}
func (s *DiskFullNoRootSuite) TestLinkFromPoolCopyReturnsErrorIfSizeFailsWhenDestExists(c *C) {
storage := NewPublishedStorage(s.root, "copy", "size")
pool := &fakePool{sizeErr: io.ErrUnexpectedEOF, openFn: func(string) (aptly.ReadSeekerCloser, error) {
return &fakeRSC{Reader: bytes.NewReader([]byte("data")), closeErr: nil}, nil
}}
destDir := filepath.Join(s.root, "pool/main/p/pkg")
c.Assert(os.MkdirAll(destDir, 0777), IsNil)
c.Assert(os.WriteFile(filepath.Join(destDir, "x.deb"), []byte("old"), 0644), IsNil)
err := storage.LinkFromPool("", "pool/main/p/pkg", "x.deb", pool, "x", utils.ChecksumInfo{MD5: "x"}, false)
c.Assert(err, NotNil)
c.Check(err, Equals, io.ErrUnexpectedEOF)
}
func (s *DiskFullNoRootSuite) TestLinkFromPoolCopyChecksumReturnsErrorIfDstMD5Fails(c *C) {
storage := NewPublishedStorage(s.root, "copy", "")
pool := &fakePool{openFn: func(string) (aptly.ReadSeekerCloser, error) {
return &fakeRSC{Reader: bytes.NewReader([]byte("data")), closeErr: nil}, nil
}}
// Make destinationPath a directory so MD5ChecksumForFile fails.
destDir := filepath.Join(s.root, "pool/main/p/pkg")
c.Assert(os.MkdirAll(destDir, 0777), IsNil)
c.Assert(os.MkdirAll(filepath.Join(destDir, "x.deb"), 0777), IsNil)
err := storage.LinkFromPool("", "pool/main/p/pkg", "x.deb", pool, "x", utils.ChecksumInfo{MD5: "x"}, false)
c.Assert(err, NotNil)
}
func (s *DiskFullNoRootSuite) TestLinkFromPoolHardlinkReturnsErrorIfStatFailsWhenDestExists(c *C) {
storage := NewPublishedStorage(c.MkDir(), "hardlink", "")
pool := &fakeLocalPool{statErr: errors.New("stat failed")}
destDir := filepath.Join(storage.rootPath, "pool", "main", "p", "pkg")
c.Assert(os.MkdirAll(destDir, 0777), IsNil)
c.Assert(os.WriteFile(filepath.Join(destDir, "x.deb"), []byte("x"), 0644), IsNil)
err := storage.LinkFromPool("", "pool/main/p/pkg", "x.deb", pool, "x", utils.ChecksumInfo{MD5: "x"}, false)
c.Assert(err, NotNil)
}