def testFindTrove(self): db = database.Database(':memory:', ':memory:') self.assertEqual(db.getTransactionCounter(), 0) flavor = deps.parseFlavor('~readline,!foo') v10 = ThawVersion("/conary.rpath.com@test:trunk/10:1.2-10-1") f1 = files.FileFromFilesystem("/etc/passwd", self.id1) trv = trove.Trove("testcomp", v10, flavor, None) trv.addFile(self.id1, "/bin/1", v10, f1.fileId()) trvInfo = db.addTrove(trv) db.addTroveDone(trvInfo) f2 = files.FileFromFilesystem("/etc/group", self.id2) v20 = ThawVersion("/conary.rpath.com@local:blah/20:1.3-2-1") trv = trove.Trove("testcomp", v20, flavor, None) trv.addFile(self.id2, "/bin/2", v20, f2.fileId()) trvInfo = db.addTrove(trv) db.addTroveDone(trvInfo) tup = [('testcomp', v10, flavor)] tup2 = [('testcomp', v10, flavor), ('testcomp', v20, flavor)] assert (db.findTrove(None, ('testcomp', '1.2-10-1', None)) == tup) assert (db.findTrove(None, ('testcomp', '1.2', None)) == tup) assert (set(db.findTrove( None, ('testcomp', None, parseFlavor('!foo')))) == set(tup2)) assert (db.findTrove(None, ('testcomp', ':trunk', None)) == tup) assert (db.findTrove([Label('conary.rpath.com@test:foo')], ('testcomp', ':trunk', None)) == tup) assert (db.findTrove(None, ('testcomp', ':trunk', None)) == tup) assert (db.findTrove(None, ('testcomp', '@test:trunk', None)) == tup) assert (db.findTrove([Label('conary.rpath.com@blah:foo')], ('testcomp', '@test:trunk', None)) == tup) # Transaction counter changes upon commit self.assertEqual(db.getTransactionCounter(), 0) db.commit() self.assertEqual(db.getTransactionCounter(), 1)
def testFileContentsMissing(self): # currently causes a 500 error #raise testhelp.SkipTestException # create an absolute changeset cs = changeset.ChangeSet() # add a pkg diff flavor = deps.deps.parseFlavor('') v = versions.VersionFromString('/%s/1.0-1-1' %self.cfg.buildLabel.asString()).copy() v.resetTimeStamps() t = trove.Trove('test:test', v, flavor, None) path = self.workDir + '/blah' f = open(path, 'w') f.write('hello, world!\n') f.close() pathId = sha1helper.md5String('/blah') f = files.FileFromFilesystem(path, pathId) # add the file, but munge the fileid fileId = f.fileId() cs.addFile(None, fileId, f.freeze()) t.addFile(pathId, '/blah', v, fileId) # skip adding the file contents t.computeDigests() diff = t.diff(None, absolute = 1)[0] cs.newTrove(diff) repos = self.openRepository() try: repos.commitChangeSet(cs) assert 0, "Did not raise integrity error" except errors.IntegrityError, e: assert(str(e).startswith("Missing file contents for pathId e806729b6a2b568fa7e77c3efa3a9684, fileId"))
def testFileObjMissing(self): # create an absolute changeset cs = changeset.ChangeSet() # add a pkg diff flavor = deps.deps.parseFlavor('') v = versions.VersionFromString('/%s/1.0-1-1' %self.cfg.buildLabel.asString()).copy() v.resetTimeStamps() t = trove.Trove('test:test', v, flavor, None) path = self.workDir + '/blah' f = open(path, 'w') f.write('hello, world!\n') f.close() pathId = sha1helper.md5String('/blah') f = files.FileFromFilesystem(path, pathId) # add the file, and SKIP including # the filestream by using cs.addFile(). This creates an # incomplete changeset t.addFile(pathId, '/blah', v, f.fileId()) cs.addFileContents(pathId, f.fileId(), changeset.ChangedFileTypes.file, filecontents.FromFilesystem(path), f.flags.isConfig()) t.computeDigests() diff = t.diff(None, absolute = 1)[0] cs.newTrove(diff) repos = self.openRepository() try: repos.commitChangeSet(cs) assert 0, "Did not raise IntegrityError" except errors.IntegrityError, e: assert(str(e).startswith("Incomplete changeset specified: missing pathId e806729b6a2b568fa7e77c3efa3a9684 fileId"))
def testCommonFiles(self): # this test simulates a trove having the ame file in different # path locations with only changed mtimes. store = self._connect() flavor = deps.Flavor() version = ThawVersion("/conary.rpath.com@test:trunk/10:1.2-10") baseNames = set(['file']) dirNames = set(['/junk1', '/junk2']) f = files.FileFromFilesystem("/etc/passwd", self.id) trv = trove.Trove("junk:data", version, flavor, None) trv.addFile(self.id1, "/junk1/file", version, f.fileId()) trv.addFile(self.id2, "/junk2/file", version, f.fileId()) trv.computeDigests() store.db.transaction() store.addTroveSetStart([], dirNames, baseNames) ti = store.addTrove(trv, trv.diff(None)[0]) f.inode.mtime.set(1) ti.addFile(self.id1, "/junk1/file", f.fileId(), version, fileStream=f.freeze()) f.inode.mtime.set(2) ti.addFile(self.id2, "/junk2/file", f.fileId(), version, fileStream=f.freeze()) store.addTroveDone(ti) store.commit()
def testSymbolicLink(self): s = files.SymbolicLink(None) s.inode.perms.set(0604) s.inode.mtime.set(0100) s.inode.owner.set('daemon') s.inode.group.set('uucp') # to make sure that referenced names "exist" files.userCache.nameCache['daemon'] = 2 files.groupCache.nameCache['uucp'] = 14 s.flags.set(0) s.target.set("/some/target") assert(s.sizeString() == " 12") d = tempfile.mkdtemp() try: p = d + "/bar/foo" s.restore(None, d, p) what = os.readlink(p) assert(what == "/some/target") s2 = files.SymbolicLink(None) s2.target.set("/some/place") s2.restore(None, d, p) s3 = files.FileFromFilesystem(p, None) assert(s2.target == s3.target) assert(s.target != s3.target) finally: shutil.rmtree(d) s2 = files.ThawFile(s.freeze(), None) assert(s == s2) # make sure that the frozen form matches what we expect self.assertEqual(s.freeze(), 'l\x03\x00\x04\x00\x00\x00\x00\x05\x00\x1c\x01\x00\x02\x01\x84\x02\x00\x04\x00\x00\x00@\x03\x00\x06daemon\x04\x00\x04uucp\t\x00\x0c/some/target')
def testHidden(self): store = self._connect() cu = store.db.cursor() flavor = deps.Flavor() version = ThawVersion("/conary.rpath.com@test:trunk/10:1.2-10") f = files.FileFromFilesystem("/etc/passwd", self.id) trv = trove.Trove("junk:data", version, flavor, None) trv.computeDigests() store.addTroveSetStart([], set(['/etc']), set(['passwd'])) ti = store.addTrove(trv, trv.diff(None)[0], hidden=True) store.addTroveDone(ti) store.addTroveSetDone() assert (cu.execute("select count(*) from latestcache").fetchall()[0][0] == 0) assert (cu.execute("select isPresent from instances").fetchall()[0][0] == instances.INSTANCE_PRESENT_HIDDEN) store.presentHiddenTroves() assert (cu.execute("select count(*) from latestcache").fetchall()[0][0] == 3) assert (cu.execute("select isPresent from instances").fetchall()[0][0] == instances.INSTANCE_PRESENT_NORMAL)
def testSocket(self): s = files.Socket(None) s.inode.perms.set(0604) s.inode.mtime.set(0100) s.inode.owner.set(self.owner) s.inode.group.set(self.group) s.flags.set(0) assert(s.sizeString() == " 0") d = tempfile.mkdtemp() try: p = d + "/bar/foo" s.restore(None, d, p) what = os.stat(p) assert(stat.S_ISSOCK(what.st_mode)) assert(what.st_mtime == 0100) assert(what.st_mode & 07777 == 0604) s2 = files.FileFromFilesystem(p, None) assert(s == s2) # make sure an os.unlink() happens s.restore(None, d, p) finally: shutil.rmtree(d) s2 = files.ThawFile(s.freeze(), None) assert(s == s2)
def testUnknownUser(self): # CNY-1071 # Tests that FileFromFilesystem packs '+UID' as the owner (and '+GID' # as the group) if the owner/group don't exist. fobj = tempfile.NamedTemporaryFile() fobj.write("test\n") fobj.flush() uid, gid = self.findUnknownIds() import posix origLstat = os.lstat def myLstat(path): s = origLstat(path) if path == fobj.name: # Convert the stat info to a tuple s = tuple(s) # Replace st_uid and st_gid s = s[:4] + (uid, gid) + s[6:] # Convert to stat_result s = posix.stat_result(s) self.assertEqual(s.st_uid, uid) self.assertEqual(s.st_gid, gid) return s try: os.lstat = myLstat # No failure here f = files.FileFromFilesystem(fobj.name, None) self.assertEqual(f.inode.owner(), "+" + str(uid)) self.assertEqual(f.inode.group(), "+" + str(gid)) finally: os.lstat = origLstat
def testFileIdWrong(self): # create an absolute changeset cs = changeset.ChangeSet() # add a pkg diff flavor = deps.deps.parseFlavor('') v = versions.VersionFromString('/%s/1.0-1-1' %self.cfg.buildLabel.asString()).copy() v.resetTimeStamps() t = trove.Trove('test:test', v, flavor, None) path = self.workDir + '/blah' f = open(path, 'w') f.write('hello, world!\n') f.close() pathId = sha1helper.md5String('/blah') f = files.FileFromFilesystem(path, pathId) # add the file, but munge the fileid brokenFileId = ''.join(reversed(f.fileId())) cs.addFile(None, brokenFileId, f.freeze()) t.addFile(pathId, '/blah', v, brokenFileId) t.computeDigests() diff = t.diff(None, absolute = 1)[0] cs.newTrove(diff) repos = self.openRepository() try: repos.commitChangeSet(cs) assert 0, "Integrity Error not raised" except errors.TroveIntegrityError, e: assert(str(e) == 'fileObj.fileId() != fileId in changeset for ' 'pathId %s' % sha1helper.md5ToString(pathId))
def addFile(self, path, realPath): """ Add a file to the build component @param path: the destination of the file in the component @param realPath: the location of the actual file on the filesystem, used to obtain the contents of the file when creating a changeset to commit to the repository """ # skip uid/gid lookups because packagepolicy will change the # ownerships according to Ownership settings anyway (f, linkCount, inode) = files.FileFromFilesystem(realPath, None, inodeInfo = True, assumeRoot = True) f.inode.perms.set(f.inode.perms() & 01777) self[path] = (realPath, f) if (f.inode.perms() & 0400) != 0400: # we can safely change the permissions now, the original # permissions have been recorded os.chmod(realPath, f.inode.perms() | 0400) if linkCount > 1: if f.hasContents: l = self.linkGroups.get(inode, []) l.append(path) self.linkGroups[inode] = l # add to list to check for config files later self.hardlinkMap[path] = l else: if not isinstance(f, files.Directory): # no hardlinks allowed for special files other than dirs self.badhardlinks.append(path) return f
def testDatabase2(self): db = sqldb.Database(':memory:') f1 = files.FileFromFilesystem("/etc/passwd", self.id1) f2 = files.FileFromFilesystem("/etc/services", self.id2) f3 = files.FileFromFilesystem("/etc/group", self.id3) trv = trove.Trove("testcomp", self.v10, self.emptyFlavor, None) trv.addFile(self.id1, "/bin/1", self.v10, f1.fileId()) trv.addFile(self.id2, "/bin/2", self.v10, f2.fileId()) trv.addFile(self.id3, "/bin/3", self.v10, f3.fileId()) trvInfo = db.addTrove(trv) db.addFile(trvInfo, f1.pathId(), "/bin/1", f1.fileId(), self.v10, fileStream=f1.freeze()) db.addFile(trvInfo, f2.pathId(), "/bin/2", f2.fileId(), self.v10, fileStream=f2.freeze()) db.addFile(trvInfo, f3.pathId(), "/bin/3", f3.fileId(), self.v10, fileStream=f3.freeze()) db.addTroveDone(trvInfo) assert (db.getTroves([("testcomp", self.v10, self.emptyFlavor), ("testcomp", self.v20, self.emptyFlavor)], True) == [trv, None]) assert (db.hasTroves([("testcomp", self.v10, self.emptyFlavor), ("testcomp", self.v20, self.emptyFlavor) ]) == [True, False]) f2 = files.FileFromFilesystem("/etc/hosts", self.id2) trv2 = trove.Trove("testcomp", self.v20, self.emptyFlavor, None) trv2.addFile(self.id1, "/bin/1", self.v10, self.fid1) trv2.addFile(self.id2, "/bin/2", self.v20, self.fid2)
def _addFile(self, cs, trv, path): pathId = sha1helper.md5String(path) absPath = self.cfg.root + path fileObj = files.FileFromFilesystem(absPath, pathId) fileId = fileObj.fileId() trv.addFile(pathId, path, trv.getVersion(), fileId) cs.addFile(None, fileId, fileObj.freeze()) if fileObj.hasContents: cs.addFileContents(pathId, fileId, changeset.ChangedFileTypes.file, filecontents.FromFilesystem(absPath), False)
def testPrelink(self): user = pwd.getpwuid(os.getuid()).pw_name group = grp.getgrgid(os.getgid()).gr_name archivePath = resources.get_archive() self.addComponent('test:foo=1', fileContents=[ ('/prelinktest', rephelp.RegularFile( contents=open(archivePath + '/prelinktest'), owner=user, group=group, mode=0755)), ('/prelinktest-orig', rephelp.RegularFile( contents=open(archivePath + '/prelinktest'), owner=user, group=group, mode=0755)) ]) self.updatePkg('test:foo=1') db = database.Database(self.rootDir, self.cfg.dbPath) rc, str = self.captureOutput(verify.verify, ['test:foo'], db, self.cfg) self.assertEquals(str, '') binary = self.rootDir + '/prelinktest' # the test suite can't set the mtime on the file; we'll preserve # it ourself sb = os.stat(binary) os.system("cp %s/prelinktest-prelinked %s" % (archivePath, binary)) os.utime(binary, (sb.st_atime, sb.st_mtime)) self.assertEquals(files.PRELINK_CMD, ('/usr/sbin/prelink', )) oldCmd = files.PRELINK_CMD try: files.PRELINK_CMD = (archivePath + '/prelink', ) files._havePrelink = None rc, str = self.captureOutput(verify.verify, ['test:foo'], db, self.cfg, forceHashCheck=True) self.assertEquals(str, '') # Also verify a path used by addCapsule f, nlinks, devino = files.FileFromFilesystem(binary, pathId='\0' * 16, inodeInfo=True) self.assertEquals( digestlib.sha1(open(binary).read()).hexdigest(), '1114f3a978b60d76d7618dc43aaf207bc999f997') self.assertEquals(f.contents.sha1().encode('hex'), '23ad3a2c940a30809b68a5b8a13392196004efab') finally: files.PRELINK_CMD = oldCmd files._havePrelink = None
def testFileUpdateMissingKey(self): fingerprint = '95B457D16843B21EA3FC73BBC7C32FC1F94E405E' # make a changeset with useful stuff that could be installed cs = changeset.ChangeSet() flavor = deps.parseFlavor('') v = versions.VersionFromString('/%s/1.0-1-1' % self.cfg.buildLabel.asString()).copy() v.resetTimeStamps() t = trove.Trove('test:test', v, flavor, None) path = self.workDir + '/blah' f = open(path, 'w') f.write('hello, world!\n') f.close() pathId = sha1helper.md5String('/blah') f = files.FileFromFilesystem(path, pathId) fileId = f.fileId() t.addFile(pathId, '/blah', v, fileId) cs.addFile(None, fileId, f.freeze()) cs.addFileContents(pathId, fileId, changeset.ChangedFileTypes.file, filecontents.FromFilesystem(path), f.flags.isConfig()) diff = t.diff(None, absolute=1)[0] cs.newTrove(diff) cs.addPrimaryTrove('test:test', v, flavor) # sign the changeset csPath = os.path.join(self.workDir, 'test-1.0-1.ccs') cs = cook.signAbsoluteChangeset(cs, fingerprint) cs.writeToFile(csPath) tmpPath = mkdtemp() keyCache = openpgpkey.getKeyCache() newKeyCache = openpgpkey.OpenPGPKeyFileCache() pubRing = self.cfg.pubRing self.cfg.pubRing = [tmpPath + '/pubring.gpg'] keyCacheCallback = openpgpkey.KeyCacheCallback(None, self.cfg) newKeyCache.setCallback(keyCacheCallback) openpgpkey.setKeyCache(newKeyCache) try: self.updatePkg(self.rootDir, csPath) finally: self.cfg.pubRing = pubRing openpgpkey.setKeyCache(keyCache)
def testEmptyPerms(self): # make sure we can read in a file with no perms; we mistook '0' # for None (CNY-1678) fd, fpath = tempfile.mkstemp() os.write(fd, "test\n") os.close(fd) try: os.chmod(fpath, 000) f = files.FileFromFilesystem(fpath, None) assert(f.inode.owner() != None) assert(f.inode.group() != None) assert(f.inode.perms() == 0) finally: os.unlink(fpath)
def testChangeSetFromFile(self): # ensure that absolute changesets that are read from disk # that contain config files write out changesets to a file # that do not change the file type to a diff. # set up a file with some contents cont = self.workDir + '/contents' f = open(cont, 'w') f.write('hello, world!\n') f.close() pathId = sha1helper.md5FromString('0' * 32) f = files.FileFromFilesystem(cont, pathId) f.flags.isConfig(1) # create an absolute changeset cs = changeset.ChangeSet() # add a pkg diff v = versions.VersionFromString('/localhost@rpl:devel/1.0-1-1', timeStamps=[1.000]) flavor = deps.parseFlavor('') t = trove.Trove('test', v, flavor, None) t.addFile(pathId, '/contents', v, f.fileId()) diff = t.diff(None, absolute=1)[0] cs.newTrove(diff) # add the file and file contents cs.addFile(None, f.fileId(), f.freeze()) cs.addFileContents(pathId, f.fileId(), changeset.ChangedFileTypes.file, filecontents.FromFilesystem(cont), f.flags.isConfig()) # write out the changeset cs.writeToFile(self.workDir + '/foo.ccs') # read it back in cs2 = changeset.ChangeSetFromFile(self.workDir + '/foo.ccs') # write it out again (there was a bug where all config files # became diffs) cs2.writeToFile(self.workDir + '/bar.ccs') # read it again cs3 = changeset.ChangeSetFromFile(self.workDir + '/bar.ccs') # verify that the file is a file, not a diff ctype, contents = cs3.getFileContents(pathId, f.fileId()) assert (ctype == changeset.ChangedFileTypes.file)
def testDuplicatePaths(self): store = self._connect() v10 = ThawVersion("/conary.rpath.com@test:trunk/10:1.2-10") flavor1 = deps.Flavor() flavor2 = deps.parseFlavor('is:x86') cl = changelog.ChangeLog("test", "*****@*****.**", "Changes\n") f1 = files.FileFromFilesystem("/etc/passwd", self.id1) trv1 = trove.Trove('testcomp', v10, flavor1, cl) trv1.addFile(f1.pathId(), "/bin/1", v10, f1.fileId()) trv2 = trove.Trove('testcomp', v10, flavor2, cl) trv2.addFile(f1.pathId(), "/bin/1", v10, f1.fileId()) store.db.transaction() store.addTroveSetStart([], set(['/bin']), set(['1'])) troveInfo = store.addTrove(trv1, trv1.diff(None)[0]) troveInfo.addFile(f1.pathId(), "/bin/1", f1.fileId(), v10, fileStream=f1.freeze()) store.addTroveDone(troveInfo) troveInfo = store.addTrove(trv2, trv2.diff(None)[0]) troveInfo.addFile(f1.pathId(), "/bin/1", f1.fileId(), v10, fileStream=f1.freeze()) store.addTroveDone(troveInfo) store.addTroveSetDone() store.db.commit() # make sure the path was inserted into FilePaths and friends once cu = store.db.cursor() for tbl in ['FilePaths', 'Dirnames', 'Basenames']: cu.execute("select count(*) from %s" % tbl) self.assertEquals(cu.next()[0], 1)
def testDuplicateStreams(self): store = self._connect() flavor = deps.Flavor() v10 = ThawVersion("/conary.rpath.com@test:trunk/10:1.2-10") v20 = ThawVersion("/conary.rpath.com@test:trunk/20:1.2-20") dirNames = set(['/bin', '/etc']) baseNames = set(['1', '2']) f1 = files.FileFromFilesystem("/etc/passwd", self.id1) trv = trove.Trove("testpkg:runtime", v10, flavor, None) trv.addFile(f1.pathId(), "/bin/1", v10, f1.fileId()) trv.computeDigests() store.db.transaction() store.addTroveSetStart([], dirNames, baseNames) troveInfo = store.addTrove(trv, trv.diff(None)[0]) troveInfo.addFile(f1.pathId(), "/bin/1", f1.fileId(), v10) store.addTroveDone(troveInfo) store.addTroveSetDone() store.db.commit() trv = trove.Trove("testpkg:runtime", v20, flavor, None) trv.addFile(f1.pathId(), "/bin/1", v20, f1.fileId()) trv.addFile(f1.pathId(), "/bin/2", v20, f1.fileId()) trv.computeDigests() store.db.transaction() store.addTroveSetStart([], dirNames, baseNames) troveInfo = store.addTrove(trv, trv.diff(None)[0]) troveInfo.addFile(f1.pathId(), "/bin/1", f1.fileId(), v10) troveInfo.addFile(f1.pathId(), "/bin/2", f1.fileId(), v10) store.addTroveDone(troveInfo) store.db.commit()
def remove(self, trv): SingleCapsuleOperation.remove(self, trv) # make sure everything was erased which should have been; RPM's # shared file handling means it may not erase things which we think # ought to be for trv in self.removes: dbFileObjs = self.db.getFileVersions([(x[0], x[2], x[3]) for x in trv.iterFileList()]) for (pathId, path, fileId, version), fileObj in \ itertools.izip(trv.iterFileList(), dbFileObjs): hasCapsule = trv.troveInfo.capsule.type() or False fullPath = util.joinPaths(self.root, path) if not os.path.exists(fullPath): continue if (fileObj.flags.isCapsuleAddition()): # this was added to the package outside of the RPM; # we don't have any responsibility for it continue elif (fileObj.hasContents and trove.conaryContents(hasCapsule, pathId, fileObj)): # this content isn't part of the capsule; remember to put # it back when RPM is done self.preservePath(path, unlink=True) continue fsFileObj = files.FileFromFilesystem(fullPath, pathId, possibleMatch=fileObj) self.fsJob._remove(fsFileObj, path, fullPath, 'removing rpm owned file %s', ignoreMissing=True)
def install(self, flags, troveCs): ACTION_RESTORE = 1 ACTION_SKIP = 2 ACTION_CONFLICT = 3 rc = SingleCapsuleOperation.install(self, flags, troveCs) if rc is None: # parent class thinks we should just ignore this troveCs; I'm # not going to argue with it (it's probably because the capsule # hasn't changed return None (oldTrv, trv) = rc trvInfo = troveCs.getNewNameVersionFlavor() oldTrvInfo = troveCs.getOldNameVersionFlavor() hasCapsule = troveCs.hasCapsule() # Updates the fsJob metadata for installing the current trove. # It assumes files are replaced on install, and complains if something # is in the way unless the appropriate flags are set. This is a very # much simplified version of FilesystemJob._singleTrove() which maps # out a complete install strategy for native packages. Note that # we walk all of the files in this trove, not just the new files # or the changed files, because RPM installs all of the files. toRestore = [] changedByPathId = dict((x[0], x) for x in troveCs.getChangedFileList()) # things which aren't change, new, or removed are unchanged unchangedByPathId = (set(x[0] for x in trv.iterFileList()) - set(changedByPathId.iterkeys()) - set(x[0] for x in troveCs.getNewFileList()) - set(troveCs.getOldFileList())) l = [] for oldFileInfo in troveCs.getChangedFileList(): oldFileId, oldVersion = oldTrv.getFile(oldFileInfo[0])[1:3] l.append((oldFileInfo[0], oldFileId, oldVersion)) for unchangedPathId in unchangedByPathId: unchangedFileId, unchangedFileVersion = \ trv.getFile(unchangedPathId)[1:3] l.append((unchangedPathId, unchangedFileId, unchangedFileVersion)) fileObjs = self.db.getFileVersions(l) fileObjsByPathId = dict([(x[0], y) for x, y in itertools.izip(l, fileObjs)]) for fileInfo in trv.iterFileList(): pathId, path, fileId, version = fileInfo if os.path.dirname(path) in self.netSharedPath: # we do nothing. really. nothing. # # we don't back it up. we don't mark it as removed in # our database. we don't look for conflicts. nothing. continue if pathId in changedByPathId: oldFileId = oldTrv.getFile(pathId)[1] fileChange = self.changeSet.getFileChange(oldFileId, fileId) if (oldFileId == fileId): # only the version number changed; we don't need # to merge anything here fileObj = fileObjsByPathId[pathId] elif fileChange[0] == '\x01': fileObj = fileObjsByPathId[pathId] fileObj.twm(fileChange, fileObj) else: fileObj = files.ThawFile(fileChange, pathId) elif pathId in unchangedByPathId: fileObj = fileObjsByPathId[pathId] else: # if it's not changed and it's not unchanged, it must be new fileStream = self.changeSet.getFileChange(None, fileId) fileObj = files.ThawFile(fileStream, pathId) absolutePath = util.joinPaths(self.root, path) if (fileObj.flags.isCapsuleAddition()): # this was added to the package outside of the RPM; we don't # have any responsibility for it continue elif (trove.conaryContents(hasCapsule, pathId, fileObj) and fileObj.lsTag != 'd'): # this content isn't part of the capsule; remember to put # it back when RPM is done self.preservePath(path, unlink=True) continue s = util.lstat(absolutePath) if not s: # there is nothing in the way, so there is nothing which # concerns us here. Track the file for later. toRestore.append((fileInfo, fileObj)) continue action = ACTION_CONFLICT existingOwners = list( self.db.iterFindPathReferences(path, justPresent=True, withStream=True)) if existingOwners: # Don't complain about files owned by the previous version # of this trove. l = [x for x in existingOwners if x[0:3] == oldTrvInfo] if l: existingOwners.remove(l[0]) if not existingOwners: action = ACTION_RESTORE elif stat.S_ISDIR(s.st_mode) and fileObj.lsTag == 'd': # Don't let existing directories stop us from taking over # ownership of the directory action = ACTION_RESTORE elif fileObj.flags.isInitialContents(): # Initial contents files may be restored on top of things # already in the filesystem. They're ghosts or config files # and RPM will get the contents right either way, and we # should remove them either way. action = ACTION_RESTORE if action == ACTION_CONFLICT and not existingOwners: # Check for "conflicts" that might just be a view across a # symlink. if self.fsJob.findAliasedRemovals(absolutePath): action = ACTION_RESTORE if action == ACTION_CONFLICT and existingOwners: if fileId in [x[4] for x in existingOwners]: # The files share metadata same. Whatever it looks like on # disk, RPM is going to blow it away with the new one. for info in existingOwners: self.fsJob.sharedFile(info[0], info[1], info[2], info[3]) action = ACTION_RESTORE elif path.startswith('/usr/share/doc/'): # Mirror badness Red Hat patches into RPM for rhel4 # and rhel5 action = ACTION_RESTORE else: existingFiles = [ files.ThawFile(x[5], pathId) for x in existingOwners ] compatibility = [ 1 for x in existingFiles if fileObj.compatibleWith(x) ] if 1 in compatibility: # files can be shared even though the fileId's # are different for info in existingOwners: self.fsJob.sharedFile(info[0], info[1], info[2], info[3]) action = ACTION_RESTORE elif 1 in [ files.rpmFileColorCmp(x, fileObj) for x in existingFiles ]: # rpm file colors and the default rpm setting for # file color policy make elf64 files silently replace # elf32 files. follow that behavior here. # # no, i'm not making this up # # yes, really action = ACTION_SKIP elif (self._checkReplaceManagedFiles(flags, path) or 1 in [ files.rpmFileColorCmp(fileObj, x) for x in existingFiles ]): # The files are different. Bail unless we're supposed # to replace managed files. existingFile = files.FileFromFilesystem( absolutePath, pathId) for info in existingOwners: self.fsJob.userRemoval( fileObj=existingFile, content=filecontents.FromFilesystem( absolutePath), *info[0:4]) action = ACTION_RESTORE else: # it's not up to us to decide if this is a true # conflict; the database layer will do that for # us (see checkPathConflicts) action = ACTION_RESTORE elif flags.replaceUnmanagedFiles: # we don't own it, but it's on disk. RPM will just write over # it and we have the flag saying we're good with that action = ACTION_RESTORE if action == ACTION_RESTORE: # We may proceed, and RPM will replace this file for us. We # need to track that it's being restored to avoid conflicts # with other restorations though. toRestore.append((fileInfo, fileObj)) elif action == ACTION_CONFLICT: # The file exists already, we can't share it, and we're not # allowed to overwrite it. self._error( errors.FileInWayError(util.normpath(path), troveCs.getName(), troveCs.getNewVersion(), troveCs.getNewFlavor())) else: assert (action == ACTION_SKIP) self.preservePath(path, unlink=False) self.fsJob.userRemoval(trv.getName(), trv.getVersion(), trv.getFlavor(), pathId) # toRestore is the list of what is going to be restored. We need to get # the fileObjects which will be created so we can track them in the # filesystem job. This lets the filesystem job look for resolveable # conflicts within this update. We handle newly created files first # and files which have changed (so we have to look up the diff) # a bit later. for fileInfo, fileObj in toRestore: fullPath = util.joinPaths(self.root, path) self.fsJob._restore(fileObj, fullPath, trvInfo, "restoring %s from RPM", restoreFile=False, fileId=fileId)
def _addPhantomContents(self, changeSet, trv, header): """Fabricate files for the given RPM header""" for (path, owner, group, mode, size, rdev, flags, vflags, linkto, mtime) in itertools.izip( header[rpmhelper.OLDFILENAMES], header[rpmhelper.FILEUSERNAME], header[rpmhelper.FILEGROUPNAME], header[rpmhelper.FILEMODES], header[rpmhelper.FILESIZES], header[rpmhelper.FILERDEVS], header[rpmhelper.FILEFLAGS], header[rpmhelper.FILEVERIFYFLAGS], header[rpmhelper.FILELINKTOS], header[rpmhelper.FILEMTIMES], ): fullPath = util.joinPaths(self.root, path) fakestat = FakeStat(mode, 0, None, 1, owner, group, size, mtime, mtime, mtime, st_rdev=rdev, linkto=linkto) pathId = os.urandom(16) # Adapted from conary.build.source.addCapsule.doRPM kind = 'regular' if flags & rpmhelper.RPMFILE_GHOST: kind = 'initial' elif flags & (rpmhelper.RPMFILE_CONFIG | rpmhelper.RPMFILE_MISSINGOK | rpmhelper.RPMFILE_NOREPLACE): if size: kind = 'config' else: kind = 'initial' elif vflags: if (stat.S_ISREG(mode) and not (vflags & rpmhelper.RPMVERIFY_FILEDIGEST) or (stat.S_ISLNK(mode) and not (vflags & rpmhelper.RPMVERIFY_LINKTO))): kind = 'initial' # Ignore failures trying to sha1 missing/inaccessible files as long # as those files are flagged initial contents (ghost) fileStream = files.FileFromFilesystem(fullPath, pathId, statBuf=fakestat, sha1FailOk=True) if kind == 'config': fileStream.flags.isConfig(set=True) elif kind == 'initial': fileStream.flags.isInitialContents(set=True) else: assert kind == 'regular' # From conary.build.capsulepolicy.Payload if (isinstance(fileStream, files.RegularFile) and not fileStream.flags.isConfig() and not (fileStream.flags.isInitialContents() and not fileStream.contents.size())): fileStream.flags.isEncapsulatedContent(set=True) fileId = fileStream.fileId() trv.addFile(pathId, path, trv.getVersion(), fileId) changeSet.addFile(None, fileId, fileStream.freeze()) # Config file contents have to go into the database, so snag the # contents from the filesystem and put them in the changeset. if (fileStream.hasContents and not fileStream.flags.isEncapsulatedContent()): if fileStream.contents.sha1() == sha1helper.sha1Empty: # Missing/ghost config file. Hopefully it is supposed to be # empty, but even if not then the fake SHA-1 will be the # SHA-1 of the empty string since there's no hint of what # it was supposed to be. contents = filecontents.FromString('') else: contents = filecontents.FromFilesystem(fullPath) changeSet.addFileContents( pathId, fileId, contType=changeset.ChangedFileTypes.file, contents=contents, cfgFile=fileStream.flags.isConfig(), )
def testRemoval(self): threshold = 60 * 5 # 5 minutes def _dbStatus(db): stat = {} cu = db.cursor() for table in db.tables: cu.execute("SELECT * FROM %s" % table) l = cu.fetchall() # throw away anything which looks a timestamp; this will # break if the test case takes more than 5 minutes to run stat[table] = set() for row in l: thisRow = [] for item in row: if not isinstance(item, (float, decimal.Decimal)) or \ abs(item - now) > threshold: thisRow.append(item) stat[table].add(tuple(thisRow)) return stat def _checkStateDiff(one, two): if one != two: assert (one.keys() == two.keys()) for key in one: if one[key] != two[key]: print "table %s has changed" % key raise AssertionError, "\n%s\n!=\n%s" % (one, two) store = self._connect() # get the current timestamp from the database cu = store.db.cursor() cu.execute('''create table timestamp( foo INTEGER, changed NUMERIC(14,0) NOT NULL DEFAULT 0)''') store.db.loadSchema() store.db.createTrigger('timestamp', 'changed', "INSERT") cu.execute('insert into timestamp values(0, 0)') cu.execute('select changed from timestamp') now = cu.fetchall()[0][0] emptyState = _dbStatus(store.db) v10 = ThawVersion("/conary.rpath.com@test:trunk/10:1.2-10") v20 = ThawVersion("/conary.rpath.com@test:trunk/20:1.2-20") vB = ThawVersion("/conary.rpath.com@test:branch/1:0-0") vOtherRepo = ThawVersion("/other.repos.com@some:label/1:0-0") branch = v10.branch() flavor = deps.parseFlavor('is:x86') flavor64 = deps.parseFlavor('is:x86_64') store.createTroveBranch("trv:comp", branch) f1 = files.FileFromFilesystem("/etc/passwd", self.id1) f2 = files.FileFromFilesystem("/etc/services", self.id2) # add a file that has no contents sha1 try: d = tempfile.mkdtemp() os.symlink('foo', d + '/foo') f5 = files.FileFromFilesystem(d + '/foo', self.id5) finally: shutil.rmtree(d) req = deps.parseDep("file: /bin/bash file: /bin/awk") dirNames = set(['/bin', '']) baseNames = set(['1', '2', 'distributed', 'foo', 'group-foo.recipe']) cl = changelog.ChangeLog("test", "*****@*****.**", "changelog\n") trv = trove.Trove('trv:comp', v10, flavor, cl) trv.addFile(f1.pathId(), "/bin/1", v10, f1.fileId()) trv.addFile(f2.pathId(), "/bin/2", v10, f2.fileId()) trv.addFile(self.id4, "/bin/distributed", v10, self.fid4) trv.addFile(f5.pathId(), "/bin/foo", v10, f5.fileId()) trv.troveInfo.size.set(1234) trv.setRequires(req) store.db.transaction() store.addTroveSetStart([], dirNames, baseNames) troveInfo = store.addTrove(trv, trv.diff(None)[0]) troveInfo.addFile(f1.pathId(), "/bin/1", f1.fileId(), v10, fileStream=f1.freeze()) troveInfo.addFile(f2.pathId(), "/bin/2", f2.fileId(), v10, fileStream=f2.freeze()) troveInfo.addFile(f5.pathId(), "/bin/foo", f5.fileId(), v10, fileStream=f5.freeze()) store.addTroveDone(troveInfo) store.addTroveSetDone() store.db.commit() oneTroveState = _dbStatus(store.db) rc = store._removeTrove("trv:comp", v10, flavor) self.assertEqual(set(rc), set([f1.contents.sha1(), f2.contents.sha1()])) state = _dbStatus(store.db) _checkStateDiff(state, emptyState) store.db.rollback() # the redir itself doesn't overlap with trv:comp; this makes sure # that the tables which the redir target needs are preserved redir = trove.Trove('redir:comp', vB, flavor64, cl, type=trove.TROVE_TYPE_REDIRECT) redir.addRedirect('trv:comp', v10.branch(), flavor) store.addTroveSetStart([], dirNames, baseNames) troveInfo = store.addTrove(redir, redir.diff(None)[0]) store.addTroveDone(troveInfo) store.addTroveSetDone() rc = store._removeTrove("trv:comp", v10, flavor) redir2 = store.getTrove('redir:comp', vB, flavor64) assert (redir == redir2) rc = store._removeTrove("redir:comp", vB, flavor64) state = _dbStatus(store.db) _checkStateDiff(state, emptyState) store.db.rollback() trv2 = trv.copy() trv2.changeVersion(v20) store.db.transaction() store.addTroveSetStart([], dirNames, baseNames) troveInfo = store.addTrove(trv2, trv2.diff(None)[0]) troveInfo.addFile(f1.pathId(), "/bin/1", f1.fileId(), v10, fileStream=f1.freeze()) troveInfo.addFile(f2.pathId(), "/bin/2", f2.fileId(), v10, fileStream=f2.freeze()) store.addTroveDone(troveInfo) store.addTroveSetDone() store.db.commit() twoTroveState = _dbStatus(store.db) rc = store._removeTrove("trv:comp", v20, flavor) assert (not rc) state = _dbStatus(store.db) _checkStateDiff(state, oneTroveState) rc = store._removeTrove("trv:comp", v10, flavor) assert (set(rc) == set([f1.contents.sha1(), f2.contents.sha1()])) state = _dbStatus(store.db) _checkStateDiff(state, emptyState) store.db.rollback() # add a trove which shares a file with trv:comp and make sure removing # it doesn't remove the sha1s (make sure the fileIds are different) anotherTrove = trove.Trove('another:comp', v10, flavor, cl) anotherF = f1.copy() anotherF.inode.owner.set('unowned') anotherTrove.addFile(f1.pathId(), "/bin/1", v10, anotherF.fileId()) store.addTroveSetStart([], dirNames, baseNames) troveInfo = store.addTrove(anotherTrove, anotherTrove.diff(None)[0]) troveInfo.addFile(f1.pathId(), "/bin/1", anotherF.fileId(), v10, fileStream=f1.freeze()) store.addTroveDone(troveInfo) rc = store._removeTrove("another:comp", v10, flavor) assert (not rc) state = _dbStatus(store.db) _checkStateDiff(state, twoTroveState) store.db.rollback() # now try just marking something as removed rc = store.markTroveRemoved("trv:comp", v20, flavor) assert (not rc) removedTrove = store.getTrove("trv:comp", v20, flavor) assert (removedTrove.type() == trove.TROVE_TYPE_REMOVED) rc = store.markTroveRemoved("trv:comp", v10, flavor) assert (set(rc) == set([f1.contents.sha1(), f2.contents.sha1()])) removedTrove = store.getTrove("trv:comp", v10, flavor) assert (removedTrove.type() == trove.TROVE_TYPE_REMOVED) store.db.rollback() # test removing group-*:source anotherTrove = trove.Trove('group-foo:source', v10, flavor, cl) anotherF = f1.copy() anotherF.inode.owner.set('unowned') anotherTrove.addFile(f1.pathId(), "group-foo.recipe", v10, anotherF.fileId()) troveInfo = store.addTrove(anotherTrove, anotherTrove.diff(None)[0]) troveInfo.addFile(f1.pathId(), "group-foo.recipe", anotherF.fileId(), v10, fileStream=anotherF.freeze()) store.addTroveDone(troveInfo) rc = store._removeTrove("group-foo:source", v10, flavor) assert (not rc) state = _dbStatus(store.db) _checkStateDiff(state, twoTroveState) store.db.rollback() groupTrove = trove.Trove('group-foo', v10, flavor, cl) groupTrove.addTrove('foo', vOtherRepo, flavor) troveInfo = store.addTrove(groupTrove, groupTrove.diff(None)[0]) store.addTroveDone(troveInfo) rc = store._removeTrove("group-foo", v10, flavor) state = _dbStatus(store.db) _checkStateDiff(state, twoTroveState) store.db.rollback()
def testTroves(self, flavor=None): if flavor is None: flavor = deps.Flavor() store = self._connect() dirSet = set(['/etc', '/bin']) baseSet = set( ['passwd', 'services', 'group', '1', '2', '3', 'distributed']) v10 = ThawVersion("/conary.rpath.com@test:trunk/10:1.2-10") branch = v10.branch() store.createTroveBranch("testtrove", branch) f1 = files.FileFromFilesystem("/etc/passwd", self.id1) f2 = files.FileFromFilesystem("/etc/services", self.id2) f3 = files.FileFromFilesystem("/etc/group", self.id3) # make a really huge dependency, thus a very large file stream req = deps.DependencySet() for x in xrange(10000): req.addDep(deps.SonameDependencies, deps.Dependency("libtest.so.%d" % x)) f3.requires.set(req) # make sure it's way too big for a blob in mysql assert (len(f3.freeze()) >= 50000) cl = changelog.ChangeLog( "test", "*****@*****.**", """\ Some changes are good. Some changes are bad. Some changes just are. """) trv = trove.Trove('testcomp', v10, flavor, cl) trv.addFile(f1.pathId(), "/bin/1", v10, f1.fileId()) trv.addFile(f2.pathId(), "/bin/2", v10, f2.fileId()) trv.addFile(f3.pathId(), "/bin/3", v10, f3.fileId()) trv.addFile(self.id4, "/bin/distributed", v10, self.fid4) trv.troveInfo.size.set(1234) trv.troveInfo.sourceName.set('somesource') req = deps.DependencySet() req.addDep(deps.FileDependencies, deps.Dependency("/bin/bash")) req.addDep(deps.TroveDependencies, deps.Dependency("foo:runtime")) req.addDep(deps.SonameDependencies, deps.Dependency("libtest.so.1")) trv.setRequires(req) # this also lets us peek at the database to make sure libtest.so.1 # is only in the dep table once prv = deps.DependencySet() prv.addDep(deps.SonameDependencies, deps.Dependency("libtest.so.1")) trv.setProvides(prv) trv.computeDigests() store.db.transaction() store.addTroveSetStart([], dirSet, baseSet) troveInfo = store.addTrove(trv, trv.diff(None)[0]) troveInfo.addFile(f1.pathId(), "/bin/1", f1.fileId(), v10, fileStream=f1.freeze()) troveInfo.addFile(f2.pathId(), "/bin/2", f2.fileId(), v10, fileStream=f2.freeze()) troveInfo.addFile(f3.pathId(), "/bin/3", f3.fileId(), v10, fileStream=f3.freeze()) troveInfo.addFile(self.id4, "/bin/distributed", self.fid4, v10) store.addTroveDone(troveInfo) store.addTroveSetDone() store.db.commit() cu = store.db.cursor() cu.execute("SELECT count(*) FROM Dependencies WHERE " "name = 'libtest.so.1'") self.assertEqual(cu.next(), (1, )) # make sure the sha1s were stored cu.execute(""" SELECT dirname, basename, sha1 FROM TroveFiles JOIN FileStreams USING (streamId) JOIN FilePaths ON TroveFiles.filePathId = FilePaths.filePathId JOIN Dirnames ON FilePaths.dirnameId = Dirnames.dirnameId JOIN Basenames ON FilePaths.basenameId = Basenames.basenameId ORDER BY dirname,basename""") items = [(os.path.join(cu.frombinary(x[0]), cu.frombinary(x[1])), cu.frombinary(x[2])) for x in cu.fetchall()] self.assertEqual(items, [("/bin/1", f1.contents.sha1()), ("/bin/2", f2.contents.sha1()), ("/bin/3", f3.contents.sha1()), ("/bin/distributed", None)]) cl = changelog.ChangeLog("test", "*****@*****.**", "another log\n") fromRepos = store.getTrove("testcomp", v10, flavor, cl) self.assertEqual(fromRepos, trv) self.assertEqual(fromRepos.getVersion().timeStamps(), trv.getVersion().timeStamps()) self.assertEqual(fromRepos.getChangeLog(), trv.getChangeLog()) self.assertEqual([ x for x in store.getTrove("testcomp", v10, flavor, withFiles=False).iterFileList() ], []) l = store.iterFilesInTrove("testcomp", v10, flavor, sortByPath=True) l = [x for x in l] self.assertEqual(l, [(f1.pathId(), "/bin/1", f1.fileId(), v10), (f2.pathId(), "/bin/2", f2.fileId(), v10), (f3.pathId(), "/bin/3", f3.fileId(), v10), (self.id4, "/bin/distributed", self.fid4, v10)]) cl = changelog.ChangeLog("test", "*****@*****.**", "log for testpkg\n") trv2 = trove.Trove("testpkg", v10, flavor, cl) trv2.addTrove(trv.getName(), v10, flavor) trv2.addTrove("weakref", v10, flavor, weakRef=True) trv2.computeDigests() store.addTroveSetStart([], dirSet, baseSet) troveInfo = store.addTrove(trv2, trv2.diff(None)[0]) store.addTroveDone(troveInfo) store.addTroveSetDone() self.assertEqual(store.getTrove("testpkg", v10, flavor), trv2) self.assertEqual([ x for x in store.iterTroves([("testcomp", v10, flavor), ("testpkg", v10, flavor)]) ], [trv, trv2]) self.assertEqual([ x for x in store.iterTroves([("testpkg", v10, flavor), ("testcomp", v10, flavor)]) ], [trv2, trv]) self.assertEqual([ x for x in store.iterTroves([("testpkg", v10, flavor), ("testpkg", v10, flavor)]) ], [trv2, trv2]) self.assertEqual([ x for x in store.iterTroves([("testpkg", v10, flavor), ("blah", v10, flavor)]) ], [trv2, None]) self.assertEqual([ x for x in store.iterTroves([("blah", v10, flavor), ("testpkg", v10, flavor)]) ], [None, trv2]) self.assertEqual( [x for x in store.iterTroves([("blah", v10, flavor)])], [None]) self.assertEqual([ x for x in store.iterTroves([( "testcomp", v10, flavor), ("blah", v10, flavor), ("testpkg", v10, flavor)]) ], [trv, None, trv2]) # erasing doesn't work #store.eraseTrove("testcomp", v10, None) #store.commit() self.assertEqual(store.getTrove("testpkg", v10, flavor), trv2) map = {'testpkg': [v10]} flavors = store.getTroveFlavors(map) if flavor is not None: flavorStr = flavor.freeze() else: flavorStr = '' self.assertEqual(flavors, {'testpkg': {v10: [flavorStr]}}) map = {'testpkg3': [v10]} flavors = store.getTroveFlavors(map) self.assertEqual(flavors, {'testpkg3': {v10: []}}) # test getFiles fileObjs = store.getFiles([(f1.pathId(), f1.fileId()), (f2.pathId(), f2.fileId())]) self.assertEqual(fileObjs[(f1.pathId(), f1.fileId())], f1) self.assertEqual(fileObjs[(f2.pathId(), f2.fileId())], f2) # test that asking for an invalid fileid/pathid pair results # in no entry for the (pathid, fileid) in the returned dict invalidPathId = md5FromString('9' * 32) invalidFileId = sha1FromString('9' * 40) fileObjs = store.getFiles([(invalidPathId, invalidFileId)]) # make sure fileObjs is empty assert (not fileObjs) # test that asking for contents that have to come from # a different repository works - we should get None # back fileObjs = store.getFiles([(self.id4, self.fid4)]) self.assertEqual(fileObjs, {(self.id4, self.fid4): None})
def testDatabase1(self): db = sqldb.Database(':memory:') f1 = files.FileFromFilesystem("/etc/passwd", self.id1) f2 = files.FileFromFilesystem("/etc/services", self.id2) f3 = files.FileFromFilesystem("/etc/group", self.id3) trv = trove.Trove("testcomp", self.v10, self.emptyFlavor, None) trv.addFile(self.id1, "/bin/1", self.v10, f1.fileId()) trv.addFile(self.id2, "/bin/2", self.v10, f2.fileId()) trv.addFile(self.id3, "/bin/3", self.v10, f3.fileId()) trv.troveInfo.size.set(1234) trv.troveInfo.sourceName.set('thesource') req = deps.DependencySet() req.addDep(deps.FileDependencies, deps.Dependency("/bin/bash")) req.addDep(deps.TroveDependencies, deps.Dependency("foo:runtime")) req.addDep(deps.SonameDependencies, deps.Dependency("libtest.so.1")) trv.setRequires(req) trvInfo = db.addTrove(trv) db.addFile(trvInfo, f1.pathId(), "/bin/1", f1.fileId(), self.v10, fileStream=f1.freeze()) db.addFile(trvInfo, f2.pathId(), "/bin/2", f2.fileId(), self.v10, fileStream=f2.freeze()) db.addFile(trvInfo, f3.pathId(), "/bin/3", f3.fileId(), self.v10, fileStream=f3.freeze()) db.addTroveDone(trvInfo) dbTrv = db.getTroves([("testcomp", self.v10, self.emptyFlavor)])[0] assert (dbTrv == trv) assert (dbTrv.__class__ == trove.Trove) assert (db.trovesArePinned([("testcomp", self.v10, self.emptyFlavor) ]) == [False]) dbTrv = db.getTroves([("testcomp", self.v10, self.emptyFlavor)], withFileObjects=True)[0] assert (dbTrv == trv) assert (dbTrv.__class__ == trove.TroveWithFileObjects) for f in (f1, f2, f3): assert (dbTrv.getFileObject(f.fileId()) == f) trv2 = trove.Trove("testpkg", self.v10, self.emptyFlavor, None) ti = trv2.addTrove(trv.getName(), self.v10, trv.getFlavor()) trv2.addTrove("weakref", self.v10, trv.getFlavor(), weakRef=True) ti = db.addTrove(trv2, pin=True) db.addTroveDone(ti) assert (db.trovesArePinned([("testpkg", self.v10, self.emptyFlavor) ]) == [True]) assert (db.getTroves([("testpkg", self.v10, self.emptyFlavor) ])[0] == trv2) assert (db.getTroves([ ("testpkg", self.v10, self.emptyFlavor) ])[0].getVersion().timeStamps() == trv2.getVersion().timeStamps()) assert (db.getTroves([("testpkg", self.v10, self.emptyFlavor), ("testcomp", self.v10, self.emptyFlavor), ("testitem", self.v10, self.emptyFlavor)], True) == [trv2, trv, None]) assert (db.getTroves([("testpkg", self.v10, self.emptyFlavor), ("testcomp", self.v10, req)], True) == [trv2, None]) assert (db.findTroveContainers(["testpkg", "testcomp"]) == [[], [("testpkg", self.v10, self.emptyFlavor)]]) assert (db.getTroveContainers([ ("testpkg", self.v10, self.emptyFlavor), ("testcomp", self.v10, self.emptyFlavor) ]) == [[], [("testpkg", self.v10, self.emptyFlavor)]]) res = db.findTroveReferences(["testpkg", "testcomp"]) assert (db.findTroveReferences(["testpkg", "testcomp" ]) == [[], [("testcomp", self.v10, self.emptyFlavor)]]) v10new = VersionFromString("/conary.rpath.com@test:trunk/1.2-10") assert (db.getTroves([("testpkg", v10new, self.emptyFlavor) ])[0] == trv2) assert (db.getTroves([ ("testpkg", v10new, self.emptyFlavor) ])[0].getVersion().timeStamps() == trv2.getVersion().timeStamps()) assert (set(db.findByNames(['testpkg', 'testcomp'])) == set([ ("testpkg", self.v10, self.emptyFlavor), ("testcomp", self.v10, self.emptyFlavor) ])) db.eraseTrove("testcomp", self.v10, None) assert (db.getTroves([("testpkg", self.v10, self.emptyFlavor) ])[0] == trv2) trv.computePathHashes() trvInfo = db.addTrove(trv) db.addFile(trvInfo, f1.pathId(), "/bin/1", f1.fileId(), self.v10, fileStream=f1.freeze()) db.addFile(trvInfo, f2.pathId(), "/bin/2", f2.fileId(), self.v10, fileStream=f1.freeze()) db.addFile(trvInfo, f3.pathId(), "/bin/3", f3.fileId(), self.v10, fileStream=f1.freeze()) db.addTroveDone(trvInfo) assert (db.getTroves([("testcomp", self.v10, self.emptyFlavor) ])[0] == trv) db.removeFileFromTrove(trv, "/bin/1") changedTrv = db.getTroves([trv.getNameVersionFlavor()], pristine=False)[0] otherChangedTrv = db.getTroves([trv.getNameVersionFlavor()], withFiles=False, pristine=False)[0] assert (len(changedTrv.idMap) + 1 == len(trv.idMap)) assert (len(changedTrv.troveInfo.pathHashes) + 1 == len( trv.troveInfo.pathHashes)) assert (changedTrv.troveInfo.pathHashes == otherChangedTrv.troveInfo.pathHashes) assert (len(otherChangedTrv.idMap) == 0) changedTrv.addFile(self.id1, "/bin/1", self.v10, f1.fileId()) assert (changedTrv.idMap == trv.idMap) changedTrv.computePathHashes() assert (changedTrv.troveInfo.pathHashes == trv.troveInfo.pathHashes) assert (db.getTroves([("testcomp", self.v10, self.emptyFlavor)], pristine=True)[0] == trv) db.eraseTrove("testpkg", self.v10, None) assert (db.getTroves([("testpkg", self.v10, self.emptyFlavor) ]) == [None]) self.assertRaises(KeyError, db.instances.getVersion, 100) db.eraseTrove("testcomp", self.v10, None) db.commit() cu = db.db.cursor() # make sure the versions got removed; the None entry is still there cu.execute("SELECT count(*) FROM Versions") assert (cu.next()[0] == 1) # make sure the dependency table got cleaned up cu.execute("SELECT count(*) FROM Dependencies") assert (cu.next()[0] == 0) # make sure the instances table got cleaned up cu.execute("SELECT count(*) FROM Instances") assert (cu.next()[0] == 0) # make sure the troveInfo table got cleaned up cu.execute("SELECT count(*) FROM TroveInfo") assert (cu.next()[0] == 0)
def testDatabaseVersionCleanup(self): flavor1 = deps.parseFlavor('is:x86(cmov)') db = sqldb.Database(':memory:') cu = db.db.cursor() v10 = ThawVersion('/conary.rpath.com@test:trunk/10:1.2-10') v20 = ThawVersion('/conary.rpath.com@test:trunk/10:1.2-20') v30 = ThawVersion('/conary.rpath.com@test:trunk/10:1.2-30') # test file version cleanup trv = trove.Trove('testcomp:runtime', v10, flavor1, None) f1 = files.FileFromFilesystem('/etc/passwd', self.id1) f2 = files.FileFromFilesystem('/etc/services', self.id2) f3 = files.FileFromFilesystem('/etc/group', self.id3) trv.addFile(self.id1, '/bin/1', v10, f1.fileId()) trv.addFile(self.id2, '/bin/2', v20, f2.fileId()) trv.addFile(self.id3, '/bin/3', v30, f3.fileId()) trvInfo = db.addTrove(trv) db.addFile(trvInfo, f1.pathId(), '/bin/1', f1.fileId(), v10, fileStream=f1.freeze()) db.addFile(trvInfo, f2.pathId(), '/bin/2', f2.fileId(), v20, fileStream=f2.freeze()) db.addFile(trvInfo, f3.pathId(), '/bin/3', f3.fileId(), v30, fileStream=f3.freeze()) db.addTroveDone(trvInfo) # check to see how many versions we have cu.execute('select count(*) from Versions') count = cu.fetchall()[0][0] # should have only 0|NULL self.assertEqual(count, 4) # now erase db.eraseTrove('testcomp:runtime', v10, flavor1) db.commit() cu.execute('select count(*) from Versions') count = cu.fetchall()[0][0] # should have only 0|NULL self.assertEqual(count, 1) # test trove version cleanup trv1 = trove.Trove('testcomp:runtime', v10, flavor1, None) trv2 = trove.Trove('group-test', v20, flavor1, None) trv2.addTrove(trv1.getName(), trv1.getVersion(), trv1.getFlavor(), weakRef=True) trvInfo = db.addTrove(trv2) db.addTroveDone(trvInfo) db.commit() cu.execute('select count(*) from Versions') count = cu.fetchall()[0][0] self.assertEqual(count, 3) db.eraseTrove('group-test', v20, flavor1) db.commit() cu.execute('select count(*) from Versions') count = cu.fetchall()[0][0] # should have only 0|NULL self.assertEqual(count, 1)