1039 circular dependency in packages is detected at install-time
authorBart Smaalders <Bart.Smaalders@Sun.COM>
Fri, 11 Apr 2008 10:45:04 -0700
changeset 316 d6ba58c63264
parent 315 989c801f050b
child 317 3ad6f6968724
1039 circular dependency in packages is detected at install-time 1071 fmri.is_successor() doesn't handle missing versions correctly 1097 fmri precedence order wrong in image.load_optional_dependencies() 1100 .pyc files are not sufficiently advanced (in time)
src/modules/actions/file.py
src/modules/actions/legacy.py
src/modules/client/image.py
src/modules/client/imageplan.py
src/modules/fmri.py
src/tests/cli-complete.py
src/tests/cli/t_circular_dependencies.py
src/tests/cli/t_upgrade.py
--- a/src/modules/actions/file.py	Thu Apr 10 18:24:14 2008 -0700
+++ b/src/modules/actions/file.py	Fri Apr 11 10:45:04 2008 -0700
@@ -137,6 +137,18 @@
                 # This is safe even if temp == final_path.
                 portable.rename(temp, final_path)
 
+		# XXX .pyc files are causing problems because they're not enough
+		# newer than the .py files.... if we just installed a .pyc
+		# file, move its modification time into the future to prevent
+		# python commands running as root from updating these files
+		# because they look out of date... the right fix is to fix
+		# Solaris python to look at the entire timestamp.... pending.
+		# in the mean time, this "accomodation" has to be made to
+		# prevent pkg verify errors.
+		if final_path.endswith(".pyc"):
+			t = os.stat(final_path)[ST_MTIME] + 5 # magic
+			os.utime(final_path, (t, t))
+
         def verify(self, img, **args):
                 """ verify that file is present and if preserve attribute
                 not present, that hashes match"""
--- a/src/modules/actions/legacy.py	Thu Apr 10 18:24:14 2008 -0700
+++ b/src/modules/actions/legacy.py	Fri Apr 11 10:45:04 2008 -0700
@@ -86,13 +86,13 @@
                                 pfile.write("%s=%s\n" % (k, v))
                         pfile.close()
 
-                # create hardlink to pkginfo file for this pkg; may be
-                # there already on upgrade
+                # create another hardlink to pkginfo file if
+                # this is not just an upgrade; we use this to make
+                # uninstall easier
 
-                linkfile = os.path.join(pkgdir, 
-                    "pkginfo." + pkgplan.destination_fmri.get_url_path())
-
-                if not os.path.isfile(linkfile):
+                if not orig:
+                        linkfile = os.path.join(pkgdir, 
+                            "pkginfo.%d" % (os.stat(pkginfo)[ST_NLINK] + 1))
                         os.link(pkginfo, linkfile)
 
                 # the svr4 pkg commands need contents file to work, but the
@@ -110,7 +110,7 @@
 
                 os.chmod(pkginfo, 0644)
 
-	def verify(self, img, **args):
+        def verify(self, img, **args):
                 pkgdir = os.path.join(img.get_root(), "var/sadm/pkg",
                     self.attrs["pkg"])
 
@@ -133,17 +133,20 @@
                 pkgdir = os.path.join(pkgplan.image.get_root(), "var/sadm/pkg",
                     self.attrs["pkg"])
 
-                linkfile = os.path.join(pkgdir, 
-                    "pkginfo." + pkgplan.origin_fmri.get_url_path())
-                
-                if os.stat(linkfile)[ST_NLINK] == 2:
-                        try:
-                                os.unlink(os.path.join(pkgdir, "pkginfo"))
-                        except OSError, e:
-                                if e.errno not in (errno.EEXIST, errno.ENOENT):   
-                                        #          can happen if all refs deleted
-                                        raise    # in same pkg invocation
-                os.unlink(linkfile)
+                pkginfo = os.path.join(pkgdir, "pkginfo")
+
+                if os.path.isfile(pkginfo):
+                        link_count = os.stat(pkginfo)[ST_NLINK]
+                        linkfile = os.path.join(pkgdir,
+                            "pkginfo.%d" % (link_count))
+                        
+                        if os.path.isfile(linkfile):
+                                os.unlink(linkfile)
+
+				# do this conditionally to be kinder
+				# to installations done w/ older versions
+				if link_count == 2: # last one
+					os.unlink(pkginfo)
 
         def generate_indices(self):
                 return {
--- a/src/modules/client/image.py	Thu Apr 10 18:24:14 2008 -0700
+++ b/src/modules/client/image.py	Fri Apr 11 10:45:04 2008 -0700
@@ -937,7 +937,7 @@
                         self.fmri_set_default_authority(myfmri)
 
                 ofmri = self.optional_dependencies.get(name, None)
-                if not ofmri or self.fmri_is_successor(ofmri, myfmri):
+                if not ofmri or self.fmri_is_successor(myfmri, ofmri):
                                self.optional_dependencies[name] = myfmri
 
         def apply_optional_dependencies(self, myfmri):
@@ -955,7 +955,7 @@
                 if not minfmri:
                         return myfmri
 
-                if not myfmri.has_version() or self.fmri_is_successor(minfmri, myfmri):
+                if self.fmri_is_successor(minfmri, myfmri):
                         return minfmri
                 return myfmri
 
--- a/src/modules/client/imageplan.py	Thu Apr 10 18:24:14 2008 -0700
+++ b/src/modules/client/imageplan.py	Fri Apr 11 10:45:04 2008 -0700
@@ -97,10 +97,7 @@
         def is_proposed_fmri(self, fmri):
                 for pf in self.target_fmris:
                         if self.image.fmri_is_same_pkg(fmri, pf):
-                                if not self.image.fmri_is_successor(fmri, pf):
-                                        return True
-                                else:
-                                        return False
+                                return not self.image.fmri_is_successor(fmri, pf)
                 return False
 
         def is_proposed_rem_fmri(self, fmri):
@@ -123,7 +120,7 @@
                 #
 
                 fmri = self.image.apply_optional_dependencies(fmri)
-                
+
                 # Add fmri to target list only if it (or a successor) isn't
                 # there already.
                 for i, p in enumerate(self.target_fmris):
--- a/src/modules/fmri.py	Thu Apr 10 18:24:14 2008 -0700
+++ b/src/modules/fmri.py	Fri Apr 11 10:45:04 2008 -0700
@@ -287,6 +287,8 @@
                 return self.pkg_name == fmri.pkg_name
 
         def is_successor(self, fmri):
+                """ returns True if self > fmri """
+
                 if not self.pkg_name == fmri.pkg_name:
                         return False
 
@@ -294,10 +296,10 @@
                         return False
 
                 if fmri.version == None:
-                        return False
+                        return True
 
                 if self.version == None:
-                        return True
+                        return False
 
                 if self.version < fmri.version:
                         return False
--- a/src/tests/cli-complete.py	Thu Apr 10 18:24:14 2008 -0700
+++ b/src/tests/cli-complete.py	Fri Apr 11 10:45:04 2008 -0700
@@ -45,6 +45,7 @@
 #
 def maketests():
 	import cli.t_actions
+	import cli.t_circular_dependencies
 	import cli.t_depot
 	import cli.t_depotcontroller
 	import cli.t_image_create
@@ -65,6 +66,7 @@
 	    cli.t_pkg_list.TestPkgList,
 	    cli.t_commandline.TestCommandLine,
 	    cli.t_upgrade.TestUpgrade,
+	    cli.t_circular_dependencies.TestCircularDependencies,
 	    cli.t_rename.TestRename ]
 
 	for t in tests:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/tests/cli/t_circular_dependencies.py	Fri Apr 11 10:45:04 2008 -0700
@@ -0,0 +1,105 @@
+#!/usr/bin/python
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+
+import testutils
+if __name__ == "__main__":
+        testutils.setup_environment("../../../proto")
+
+import os
+import unittest
+
+class TestCircularDependencies(testutils.SingleDepotTestCase):
+
+        pkg10 = """
+            open [email protected],5.11-0
+            add depend type=require fmri=pkg:/pkg2
+            close
+        """
+
+        pkg20 = """
+            open [email protected],5.11-0
+            add depend type=require fmri=pkg:/pkg3
+            close
+        """
+
+        pkg30 = """
+            open [email protected],5.11-0
+            add depend type=require fmri=pkg:/pkg1
+            close
+        """
+
+
+        pkg11 = """
+            open [email protected],5.11-0
+            add depend type=require fmri=pkg:/[email protected]
+            close
+        """
+
+        pkg21 = """
+            open [email protected],5.11-0
+            add depend type=require fmri=pkg:/[email protected]
+            close
+        """
+
+        pkg31 = """
+            open [email protected],5.11-0
+            add depend type=require fmri=pkg:/[email protected]
+            close
+        """
+
+        def test_unanchored_circular_dependencies(self):
+                """ check to make sure we can install
+                circular dependencies w/o versions
+                """
+
+                # Send 1.0 versions of packages.
+                durl = self.dc.get_depot_url()
+                self.pkgsend_bulk(durl, self.pkg10)
+                self.pkgsend_bulk(durl, self.pkg20)
+                self.pkgsend_bulk(durl, self.pkg30)
+
+                self.image_create(durl)
+                self.pkg("install pkg1")
+                self.pkg("list")
+                self.pkg("verify -v")
+
+        def test_anchored_circular_dependencies(self):
+                """ check to make sure we can install
+                circular dependencies w/ versions
+                """
+
+                # Send 1.0 versions of packages.
+                durl = self.dc.get_depot_url()
+                self.pkgsend_bulk(durl, self.pkg11)
+                self.pkgsend_bulk(durl, self.pkg21)
+                self.pkgsend_bulk(durl, self.pkg31)
+
+                self.image_create(durl)
+                self.pkg("install pkg1")
+                self.pkg("list")
+                self.pkg("verify -v")
+
+if __name__ == "__main__":
+        unittest.main()
--- a/src/tests/cli/t_upgrade.py	Thu Apr 10 18:24:14 2008 -0700
+++ b/src/tests/cli/t_upgrade.py	Fri Apr 11 10:45:04 2008 -0700
@@ -46,6 +46,19 @@
             close
         """
 
+	incorpA = """
+            open [email protected],5.11-0
+            add depend type=incorporate fmri=pkg:/[email protected]
+            add depend type=incorporate fmri=pkg:/[email protected]
+            close
+        """
+
+	incorpB =  """
+            open [email protected],5.11-0
+            add depend type=incorporate fmri=pkg:/[email protected]
+            add depend type=incorporate fmri=pkg:/[email protected]
+            close
+        """
 
         amber10 = """
             open [email protected],5.11-0
@@ -102,6 +115,7 @@
             add file /tmp/bronze2 mode=0444 owner=root group=bin path=/etc/amber2
             add license /tmp/copyright3 license=copyright
             add file /tmp/bronzeA2 mode=0444 owner=root group=bin path=/A1/B2/C3/D4/E5/F6/bronzeA2
+	    add depend fmri=pkg:/[email protected] type=require
             close 
         """
 
@@ -182,9 +196,27 @@
                 self.pkg("verify -v")
 
                 # make sure all directories are gone save /var in test image
-                print os.listdir(self.get_img_path())
                 self.assert_(os.listdir(self.get_img_path()) ==  ["var"])
 
+	def test_upgrade2(self):
+		
 
+                # Send all pkgs
+
+                durl = self.dc.get_depot_url()
+		self.pkgsend_bulk(durl, self.incorpA)
+                self.pkgsend_bulk(durl, self.amber10)
+                self.pkgsend_bulk(durl, self.bronze10)
+		self.pkgsend_bulk(durl, self.incorpB)
+                self.pkgsend_bulk(durl, self.amber20)
+                self.pkgsend_bulk(durl, self.bronze20)
+
+                self.image_create(durl)
+                self.pkg("install incorpA")
+                self.pkg("install incorpB")
+                self.pkg("install bronze")
+		self.pkg("list [email protected]")
+                self.pkg("verify -v")
+		
 if __name__ == "__main__":
         unittest.main()