[CalendarServer-changes] [3731] CalendarServer/branches/users/sagen/migration-3709/twistedcaldav

source_changes at macosforge.org source_changes at macosforge.org
Tue Feb 24 15:43:45 PST 2009


Revision: 3731
          http://trac.macosforge.org/projects/calendarserver/changeset/3731
Author:   sagen at apple.com
Date:     2009-02-24 15:43:44 -0800 (Tue, 24 Feb 2009)
Log Message:
-----------
Added new methods for creating a file/folder hierarchy in a temporary directory, including file contents and xattrs, and also verifying a hierarchy.  Added test to use these.

Modified Paths:
--------------
    CalendarServer/branches/users/sagen/migration-3709/twistedcaldav/test/test_upgrade.py
    CalendarServer/branches/users/sagen/migration-3709/twistedcaldav/test/util.py
    CalendarServer/branches/users/sagen/migration-3709/twistedcaldav/upgrade.py

Modified: CalendarServer/branches/users/sagen/migration-3709/twistedcaldav/test/test_upgrade.py
===================================================================
--- CalendarServer/branches/users/sagen/migration-3709/twistedcaldav/test/test_upgrade.py	2009-02-24 22:52:50 UTC (rev 3730)
+++ CalendarServer/branches/users/sagen/migration-3709/twistedcaldav/test/test_upgrade.py	2009-02-24 23:43:44 UTC (rev 3731)
@@ -28,12 +28,15 @@
 
 class ProxyDBUpgradeTests(TestCase):
     
-    def setUpInitialStates(self):
-
+    def setUpXMLDirectory(self):
         xmlFile = os.path.join(os.path.dirname(os.path.dirname(__file__)),
             "directory", "test", "accounts.xml")
         config.DirectoryService.params.xmlFile = xmlFile
-        
+
+
+    def setUpInitialStates(self):
+        self.setUpXMLDirectory()
+
         self.setUpOldDocRoot()
         self.setUpOldDocRootWithoutDB()
         self.setUpNewDocRoot()
@@ -55,7 +58,6 @@
         os.mkdir(os.path.join(principals, "locations"))
         os.mkdir(os.path.join(principals, "resources"))
         os.mkdir(os.path.join(principals, "sudoers"))
-        os.mkdir(os.path.join(self.olddocroot, "calendars"))
 
         proxyDB = CalendarUserProxyDatabase(principals)
         proxyDB._db()
@@ -64,6 +66,7 @@
             os.path.join(principals, CalendarUserProxyDatabase.dbOldFilename),
         )
 
+
     def setUpOldDocRootWithoutDB(self):
         
         # Set up doc root
@@ -248,3 +251,172 @@
         newValue = zlib.decompress(newValue)
         self.assertEquals(newValue, expected)
 
+
+        #
+        # Shortname not in directory, raise an UpgradeError
+        #
+
+        value = "<?xml version='1.0' encoding='UTF-8'?>\r\n<calendar-free-busy-set xmlns='urn:ietf:params:xml:ns:caldav'>\r\n  <href xmlns='DAV:'>/calendars/users/nonexistent/calendar</href>\r\n</calendar-free-busy-set>\r\n"
+        self.assertRaises(UpgradeError, updateFreeBusySet, value, directory)
+
+    def test_calendarsUpgrade(self):
+
+        self.setUpXMLDirectory()
+        directory = getDirectory()
+
+        fbAttr = "WebDAV:{urn:ietf:params:xml:ns:caldav}calendar-free-busy-set"
+
+        before = {
+            "calendars" :
+            {
+                "users" :
+                {
+                    "wsanchez" :
+                    {
+                        "calendar" :
+                        {
+                            "1E238CA1-3C95-4468-B8CD-C8A399F78C72.ics" :
+                            {
+                                "@contents" : event01_before,
+                            },
+                        },
+                        "inbox" :
+                        {
+                            "@xattrs" :
+                            {
+                                fbAttr : cPickle.dumps(davxml.WebDAVDocument.fromString("<?xml version='1.0' encoding='UTF-8'?>\r\n<calendar-free-busy-set xmlns='urn:ietf:params:xml:ns:caldav'>\r\n  <href xmlns='DAV:'>/calendars/users/wsanchez/calendar</href>\r\n</calendar-free-busy-set>\r\n").root_element),
+                            },
+                        },
+                    },
+                },
+            },
+        }
+
+        after = {
+            ".calendarserver_version" :
+            {
+                "@contents" : "1",
+            },
+            "calendars" :
+            {
+                "__uids__" :
+                {
+                    "64" :
+                    {
+                        "23" :
+                        {
+                            "6423F94A-6B76-4A3A-815B-D52CFD77935D" :
+                            {
+                                "calendar" :
+                                {
+                                    "1E238CA1-3C95-4468-B8CD-C8A399F78C72.ics" :
+                                    {
+                                        "@contents" : event01_after,
+                                    },
+                                },
+                                "inbox" :
+                                {
+                                    "@xattrs" :
+                                    {
+                                        fbAttr : zlib.compress("<?xml version='1.0' encoding='UTF-8'?>\r\n<calendar-free-busy-set xmlns='urn:ietf:params:xml:ns:caldav'>\r\n  <href xmlns='DAV:'>/calendars/__uids__/6423F94A-6B76-4A3A-815B-D52CFD77935D/calendar/</href>\r\n</calendar-free-busy-set>\r\n"),
+                                    },
+                                },
+                            },
+                        },
+                    },
+                },
+            },
+        }
+
+        root = self.createHierarchy(before)
+
+        config.DocumentRoot = root
+        config.DataRoot = root
+
+        upgradeData(config)
+        self.assertTrue(self.compareHierarchy(root, after))
+
+
+event01_before = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Apple Inc.//iCal 3.0//EN
+CALSCALE:GREGORIAN
+BEGIN:VTIMEZONE
+TZID:US/Pacific
+BEGIN:DAYLIGHT
+TZOFFSETFROM:-0800
+TZOFFSETTO:-0700
+DTSTART:20070311T020000
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
+TZNAME:PDT
+END:DAYLIGHT
+BEGIN:STANDARD
+TZOFFSETFROM:-0700
+TZOFFSETTO:-0800
+DTSTART:20071104T020000
+RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
+TZNAME:PST
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+SEQUENCE:2
+TRANSP:OPAQUE
+UID:1E238CA1-3C95-4468-B8CD-C8A399F78C71
+DTSTART;TZID=US/Pacific:20090203T120000
+ORGANIZER;CN="Cyrus":mailto:cdaboo at example.com
+DTSTAMP:20090203T181924Z
+SUMMARY:New Event
+DESCRIPTION:This has \\" Bad Quotes \\" in it
+ATTENDEE;CN="Wilfredo";CUTYPE=INDIVIDUAL;PARTSTAT=ACCEPTED:mailto:wsanchez
+ @example.com
+ATTENDEE;CN="Cyrus";CUTYPE=INDIVIDUAL;PARTSTAT=ACCEPTED;ROLE=REQ-PARTICI
+ PANT:mailto:cdaboo at example.com
+CREATED:20090203T181910Z
+DTEND;TZID=US/Pacific:20090203T130000
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n")
+
+event01_after = """BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//Apple Inc.//iCal 3.0//EN
+BEGIN:VTIMEZONE
+TZID:US/Pacific
+BEGIN:STANDARD
+DTSTART:20071104T020000
+RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
+TZNAME:PST
+TZOFFSETFROM:-0700
+TZOFFSETTO:-0800
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:20070311T020000
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
+TZNAME:PDT
+TZOFFSETFROM:-0800
+TZOFFSETTO:-0700
+END:DAYLIGHT
+END:VTIMEZONE
+BEGIN:VEVENT
+UID:1E238CA1-3C95-4468-B8CD-C8A399F78C71
+DTSTART;TZID=US/Pacific:20090203T120000
+DTEND;TZID=US/Pacific:20090203T130000
+ATTENDEE;CN=Wilfredo Sanchez;CUTYPE=INDIVIDUAL;PARTSTAT=ACCEPTED;X-CALENDA
+ RSERVER-EMAIL=wsanchez at example.com:urn:uuid:6423F94A-6B76-4A3A-815B-D52CFD
+ 77935D
+ATTENDEE;CN=Cyrus Daboo;CUTYPE=INDIVIDUAL;PARTSTAT=ACCEPTED;ROLE=REQ-PARTI
+ CIPANT;X-CALENDARSERVER-EMAIL=cdaboo at example.com:urn:uuid:5A985493-EE2C-46
+ 65-94CF-4DFEA3A89500
+CREATED:20090203T181910Z
+DESCRIPTION:This has " Bad Quotes " in it
+DTSTAMP:20090203T181924Z
+ORGANIZER;CN=Cyrus Daboo;X-CALENDARSERVER-EMAIL=cdaboo at example.com:urn:uui
+ d:5A985493-EE2C-4665-94CF-4DFEA3A89500
+SEQUENCE:2
+SUMMARY:New Event
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n")
+

Modified: CalendarServer/branches/users/sagen/migration-3709/twistedcaldav/test/util.py
===================================================================
--- CalendarServer/branches/users/sagen/migration-3709/twistedcaldav/test/util.py	2009-02-24 22:52:50 UTC (rev 3730)
+++ CalendarServer/branches/users/sagen/migration-3709/twistedcaldav/test/util.py	2009-02-24 23:43:44 UTC (rev 3731)
@@ -14,7 +14,10 @@
 # limitations under the License.
 ##
 
+from __future__ import with_statement
+
 import os
+import xattr
 
 from twisted.python.failure import Failure
 from twisted.internet.defer import succeed, fail
@@ -38,6 +41,75 @@
         config.Memcached.ClientEnabled = False
         config.Memcached.ServerEnabled = False
 
+    def createHierarchy(self, structure):
+        root = self.mktemp()
+        os.mkdir(root)
+
+        def createChildren(parent, subStructure):
+            for childName, childStructure in subStructure.iteritems():
+
+                if childName.startswith("@"):
+                    continue
+
+                childPath = os.path.join(parent, childName)
+                if childStructure.has_key("@contents"):
+                    # This is a file
+                    with open(childPath, "w") as child:
+                        child.write(childStructure["@contents"])
+
+                else:
+                    # This is a directory
+                    os.mkdir(childPath)
+                    createChildren(childPath, childStructure)
+
+                if childStructure.has_key("@xattrs"):
+                    xattrs = childStructure["@xattrs"]
+                    for attr, value in xattrs.iteritems():
+                        xattr.setxattr(childPath, attr, value)
+
+        createChildren(root, structure)
+        return root
+
+    def compareHierarchy(self, root, structure):
+
+        def compareChildren(parent, subStructure):
+
+            for childName, childStructure in subStructure.iteritems():
+
+                if childName.startswith("@"):
+                    continue
+
+                childPath = os.path.join(parent, childName)
+
+                if not os.path.exists(childPath):
+                    return False
+
+                if childStructure.has_key("@contents"):
+                    # This is a file
+                    with open(childPath) as child:
+                        contents = child.read()
+                        if contents != childStructure["@contents"]:
+                            return False
+
+                else:
+                    # This is a directory
+                    if not compareChildren(childPath, childStructure):
+                        return False
+
+                if childStructure.has_key("@xattrs"):
+                    xattrs = childStructure["@xattrs"]
+                    for attr, value in xattrs.iteritems():
+                        try:
+                            if xattr.getxattr(childPath, attr) != value:
+                                return False
+                        except:
+                            return False
+
+            return True
+
+        return compareChildren(root, structure)
+
+
 class InMemoryPropertyStore(object):
     def __init__(self):
         class _FauxPath(object):

Modified: CalendarServer/branches/users/sagen/migration-3709/twistedcaldav/upgrade.py
===================================================================
--- CalendarServer/branches/users/sagen/migration-3709/twistedcaldav/upgrade.py	2009-02-24 22:52:50 UTC (rev 3730)
+++ CalendarServer/branches/users/sagen/migration-3709/twistedcaldav/upgrade.py	2009-02-24 23:43:44 UTC (rev 3731)
@@ -103,19 +103,19 @@
                 try:
                     data, fixed = fixBadQuotes(data)
                     if fixed:
-                        log.info("Fixing bad quotes in %s" % (oldRes,))
+                        log.info("Fixing bad quotes in %s" % (resPath,))
                 except Exception, e:
                     log.error("Error while fixing bad quotes in %s: %s" %
-                        (oldRes, e))
+                        (resPath, e))
                     raise
 
                 try:
                     data, fixed = normalizeCUAddrs(data, directory)
                     if fixed:
-                        log.info("Normalized CUAddrs in %s" % (oldRes,))
+                        log.info("Normalized CUAddrs in %s" % (resPath,))
                 except Exception, e:
                     log.error("Error while normalizing %s: %s" %
-                        (oldRes, e))
+                        (resPath, e))
                     raise
 
             # Write to a new file, then rename it over the old one
@@ -198,8 +198,11 @@
                 % (oldHome, newHome)
             )
 
+        os.makedirs(os.path.dirname(newHome.rstrip("/")))
+        os.rename(oldHome, newHome)
 
 
+
     directory = getDirectory()
     docRoot = config.DocumentRoot
 
@@ -270,16 +273,13 @@
 
 
 
-# Each method in this array will upgrade from one version to the next;
-# the index of each method within the array corresponds to the on-disk
-# version number that it upgrades from.  For example, if the on-disk
-# .version file contains a "3", but there are 6 methods in this array,
-# methods 3 through 5 (using 0-based array indexing) will be executed in
-# order.
+# The on-disk version number (which defaults to zero if .calendarserver_version
+# doesn't exist), is compared with each of the numbers in the upgradeMethods
+# array.  If it is less than the number, the associated method is called.
+
 upgradeMethods = [
-    upgrade_to_1,
+    (1, upgrade_to_1),
 ]
-# MOR: Change the above to an array of tuples
 
 def upgradeData(config):
 
@@ -290,8 +290,6 @@
 
     versionFilePath = os.path.join(docRoot, ".calendarserver_version")
 
-    newestVersion = len(upgradeMethods)
-
     onDiskVersion = 0
     if os.path.exists(versionFilePath):
         try:
@@ -304,11 +302,12 @@
             log.error("Invalid version number in %s; skipping migration" %
                 (versionFilePath,))
 
-    for upgradeVersion in range(onDiskVersion, newestVersion):
-        log.info("Upgrading to version %d" % (upgradeVersion+1,))
-        upgradeMethods[upgradeVersion](config)
-        with open(versionFilePath, "w") as verFile:
-            verFile.write(str(upgradeVersion+1))
+    for version, method in upgradeMethods:
+        if onDiskVersion < version:
+            log.info("Upgrading to version %d" % (version,))
+            method(config)
+            with open(versionFilePath, "w") as verFile:
+                verFile.write(str(version))
 
 
 class UpgradeError(RuntimeError):
@@ -357,7 +356,7 @@
         except UnpicklingError:
             log.err("Invalid xattr property value for: %s" % attr)
             # MOR: continue on?
-            return
+            return None
 
     fbset = set()
     didUpdate = False
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20090224/df4ac80c/attachment-0001.html>


More information about the calendarserver-changes mailing list