mirror of
https://github.com/aptly-dev/aptly.git
synced 2026-06-12 06:30:35 +00:00
80e4e9bdac
* 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
716 lines
24 KiB
Go
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)
|
|
}
|