[CalendarServer-changes] [10667] CalendarServer/trunk

source_changes at macosforge.org source_changes at macosforge.org
Fri Feb 8 09:07:40 PST 2013


Revision: 10667
          http://trac.calendarserver.org//changeset/10667
Author:   cdaboo at apple.com
Date:     2013-02-08 09:07:39 -0800 (Fri, 08 Feb 2013)
Log Message:
-----------
Make attachment migration conditional on managed attachments being enabled. Prevent dropbox from being used
once migration has occurred.

Modified Paths:
--------------
    CalendarServer/trunk/calendarserver/tap/util.py
    CalendarServer/trunk/calendarserver/tools/test/test_purge.py
    CalendarServer/trunk/calendarserver/tools/test/test_purge_old_events.py
    CalendarServer/trunk/twistedcaldav/storebridge.py
    CalendarServer/trunk/twistedcaldav/test/test_wrapping.py
    CalendarServer/trunk/txdav/caldav/datastore/sql.py
    CalendarServer/trunk/txdav/caldav/datastore/test/test_attachments.py
    CalendarServer/trunk/txdav/caldav/icalendarstore.py
    CalendarServer/trunk/txdav/common/datastore/sql.py
    CalendarServer/trunk/txdav/common/datastore/upgrade/sql/others/attachment_migration.py

Added Paths:
-----------
    CalendarServer/trunk/txdav/common/datastore/upgrade/sql/others/test/
    CalendarServer/trunk/txdav/common/datastore/upgrade/sql/others/test/__init__.py
    CalendarServer/trunk/txdav/common/datastore/upgrade/sql/others/test/test_attachment_migration.py

Modified: CalendarServer/trunk/calendarserver/tap/util.py
===================================================================
--- CalendarServer/trunk/calendarserver/tap/util.py	2013-02-08 17:05:13 UTC (rev 10666)
+++ CalendarServer/trunk/calendarserver/tap/util.py	2013-02-08 17:07:39 UTC (rev 10667)
@@ -243,6 +243,7 @@
             txnFactory, notifierFactory,
             FilePath(config.AttachmentsRoot), attachments_uri,
             config.EnableCalDAV, config.EnableCardDAV,
+            config.EnableManagedAttachments,
             quota=quota,
             logLabels=config.LogDatabase.LabelsInSQL,
             logStats=config.LogDatabase.Statistics,

Modified: CalendarServer/trunk/calendarserver/tools/test/test_purge.py
===================================================================
--- CalendarServer/trunk/calendarserver/tools/test/test_purge.py	2013-02-08 17:05:13 UTC (rev 10666)
+++ CalendarServer/trunk/calendarserver/tools/test/test_purge.py	2013-02-08 17:07:39 UTC (rev 10667)
@@ -828,6 +828,7 @@
         txn = self._sqlCalendarStore.newTransaction()
 
         # Add attachment to attachment.ics
+        self._sqlCalendarStore._dropbox_ok = True
         home = (yield txn.calendarHomeWithUID(self.uid))
         calendar = (yield home.calendarWithName("calendar1"))
         event = (yield calendar.calendarObjectWithName("attachment.ics"))
@@ -836,6 +837,7 @@
         t.write("attachment")
         t.write(" text")
         (yield t.loseConnection())
+        self._sqlCalendarStore._dropbox_ok = False
 
         # Share calendars each way
         home2 = (yield txn.calendarHomeWithUID(self.uid2))

Modified: CalendarServer/trunk/calendarserver/tools/test/test_purge_old_events.py
===================================================================
--- CalendarServer/trunk/calendarserver/tools/test/test_purge_old_events.py	2013-02-08 17:05:13 UTC (rev 10666)
+++ CalendarServer/trunk/calendarserver/tools/test/test_purge_old_events.py	2013-02-08 17:07:39 UTC (rev 10667)
@@ -512,6 +512,7 @@
     @inlineCallbacks
     def _addAttachment(self, home, calendar, event, name):
 
+        self._sqlCalendarStore._dropbox_ok = True
         txn = self._sqlCalendarStore.newTransaction()
 
         # Create an event with an attachment
@@ -525,6 +526,7 @@
         (yield t.loseConnection())
 
         (yield txn.commit())
+        self._sqlCalendarStore._dropbox_ok = False
 
         returnValue(attachment)
 

Modified: CalendarServer/trunk/twistedcaldav/storebridge.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/storebridge.py	2013-02-08 17:05:13 UTC (rev 10666)
+++ CalendarServer/trunk/twistedcaldav/storebridge.py	2013-02-08 17:07:39 UTC (rev 10667)
@@ -31,7 +31,8 @@
 from txdav.xml.base import dav_namespace, WebDAVUnknownElement, encodeXMLName
 from txdav.base.propertystore.base import PropertyName
 from txdav.caldav.icalendarstore import QuotaExceeded, AttachmentStoreFailed, \
-    AttachmentStoreValidManagedID, AttachmentRemoveFailed
+    AttachmentStoreValidManagedID, AttachmentRemoveFailed, \
+    AttachmentDropboxNotAllowed
 from txdav.common.icommondatastore import NoSuchObjectResourceError
 from txdav.common.datastore.sql_tables import _BIND_MODE_READ, _BIND_MODE_WRITE
 from txdav.idav import PropertyChangeNotAllowedError
@@ -1967,6 +1968,11 @@
                         self.attachmentName))
             t = self._newStoreAttachment.store(content_type)
             yield readStream(request.stream, t.write)
+
+        except AttachmentDropboxNotAllowed:
+            log.error("Dropbox cannot be used after migration to managed attachments")
+            raise HTTPError(FORBIDDEN)
+
         except Exception, e:
             log.error("Unable to store attachment: %s" % (e,))
             raise HTTPError(SERVICE_UNAVAILABLE)

Modified: CalendarServer/trunk/twistedcaldav/test/test_wrapping.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_wrapping.py	2013-02-08 17:05:13 UTC (rev 10666)
+++ CalendarServer/trunk/twistedcaldav/test/test_wrapping.py	2013-02-08 17:07:39 UTC (rev 10667)
@@ -32,7 +32,7 @@
 from twistedcaldav.ical import Component as VComponent
 from twistedcaldav.vcard import Component as VCComponent
 
-from twistedcaldav.storebridge import DropboxCollection,\
+from twistedcaldav.storebridge import DropboxCollection, \
     CalendarCollectionResource, AddressBookCollectionResource
 
 from twistedcaldav.test.util import TestCase
@@ -42,7 +42,7 @@
 
 from txdav.carddav.datastore.test.test_file import vcard4_text
 
-from txdav.common.datastore.test.util import buildStore, assertProvides,\
+from txdav.common.datastore.test.util import buildStore, assertProvides, \
     StubNotifierFactory
 
 
@@ -65,18 +65,32 @@
     def writeHeaders(self, code, headers):
         self.code = code
         self.headers = headers
+
+
     def registerProducer(self, producer, streaming):
         pass
+
+
     def write(self, data):
         pass
+
+
     def unregisterProducer(self):
         pass
+
+
     def abortConnection(self):
         pass
+
+
     def getHostInfo(self):
         return '127.0.0.1', False
+
+
     def getRemoteHost(self):
         return '127.0.0.1'
+
+
     def finish(self):
         pass
 
@@ -160,7 +174,6 @@
         )
         yield txn.commit()
 
-
     requestUnderTest = None
 
     @inlineCallbacks
@@ -232,7 +245,6 @@
         req.credentialFactories = {}
         return req
 
-
     pathTypes = ['calendar', 'addressbook']
 
 
@@ -412,6 +424,9 @@
         Exceeding quota on an attachment returns an HTTP error code.
         """
         self.patch(config, "EnableDropBox", True)
+        if not hasattr(self.calendarCollection._newStore, "_dropbox_ok"):
+            self.calendarCollection._newStore._dropbox_ok = False
+        self.patch(self.calendarCollection._newStore, "_dropbox_ok", True)
         self.patch(Calendar, "asShared", lambda self: [])
 
         yield self.populateOneObject("1.ics", test_event_text)
@@ -571,7 +586,7 @@
         # see twistedcaldav/directory/test/accounts.xml
         wsanchez = '6423F94A-6B76-4A3A-815B-D52CFD77935D'
         cdaboo = '5A985493-EE2C-4665-94CF-4DFEA3A89500'
-        eventTemplate="""\
+        eventTemplate = """\
 BEGIN:VCALENDAR
 CALSCALE:GREGORIAN
 PRODID:-//Example Inc.//Example Calendar//EN
@@ -615,9 +630,7 @@
 END:VEVENT""".format(wsanchez=wsanchez, cdaboo=cdaboo)
         #txn = self.requestUnderTest._newStoreTransaction
         invalidEvent = eventTemplate.format(invalidInstance, wsanchez=wsanchez, cdaboo=cdaboo).replace(CR, CRLF)
-        resp2, rsrc2 = yield putEvt(invalidEvent)
+        yield putEvt(invalidEvent)
         self.requestUnderTest = None
         yield self.assertCalendarEmpty(wsanchez)
         yield self.assertCalendarEmpty(cdaboo)
-
-

Modified: CalendarServer/trunk/txdav/caldav/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/sql.py	2013-02-08 17:05:13 UTC (rev 10666)
+++ CalendarServer/trunk/txdav/caldav/datastore/sql.py	2013-02-08 17:07:39 UTC (rev 10667)
@@ -62,7 +62,7 @@
     dropboxIDFromCalendarObject
 from txdav.caldav.icalendarstore import ICalendarHome, ICalendar, ICalendarObject, \
     IAttachment, AttachmentStoreFailed, AttachmentStoreValidManagedID, \
-    AttachmentMigrationFailed
+    AttachmentMigrationFailed, AttachmentDropboxNotAllowed
 from txdav.caldav.icalendarstore import QuotaExceeded
 from txdav.common.datastore.sql import CommonHome, CommonHomeChild, \
     CommonObjectResource, ECALENDARTYPE
@@ -90,6 +90,7 @@
 import collections
 import os
 import tempfile
+import urllib
 import uuid
 
 log = Logger()
@@ -170,7 +171,8 @@
             total = rows[0][0]
             count = 0
             log.warn("%d dropbox ids to migrate" % (total,))
-        except RuntimeError:
+        except RuntimeError, e:
+            log.error("Dropbox migration failed when cleaning out dropbox ids: %s" % (e,))
             yield txn.abort()
             raise
         else:
@@ -1814,7 +1816,10 @@
             attachments = component.properties("ATTACH")
             removed = False
             for attachment in tuple(attachments):
-                if attachment.value().endswith("/dropbox/%s/%s" % (oldattachment.dropboxID(), oldattachment.name(),)):
+                if attachment.value().endswith("/dropbox/%s/%s" % (
+                    urllib.quote(oldattachment.dropboxID()),
+                    urllib.quote(oldattachment.name()),
+                )):
                     component.removeProperty(attachment)
                     removed = True
             if removed:
@@ -2308,6 +2313,11 @@
         @type ownerHomeID: C{int}
         """
 
+        # If store has already migrated to managed attachments we will prevent creation of dropbox attachments
+        dropbox = (yield txn.store().dropboxAllowed(txn))
+        if not dropbox:
+            raise AttachmentDropboxNotAllowed
+
         # Now create the DB entry
         att = schema.ATTACHMENT
         rows = (yield Insert({
@@ -2726,8 +2736,8 @@
         fname = self.lastSegmentOfUriPath(self._managedID, self._name)
         location = self._txn._store.attachmentsURIPattern % {
             "home": self._ownerName,
-            "dropbox_id": self._objectDropboxID,
-            "name": fname,
+            "dropbox_id": urllib.quote(self._objectDropboxID),
+            "name": urllib.quote(fname),
         }
         returnValue(location)
 

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/test_attachments.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/test_attachments.py	2013-02-08 17:05:13 UTC (rev 10666)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/test_attachments.py	2013-02-08 17:07:39 UTC (rev 10667)
@@ -197,11 +197,11 @@
         self.notifierFactory.reset()
 
         txn = self._sqlCalendarStore.newTransaction()
-        Delete(
+        yield Delete(
             From=schema.ATTACHMENT,
             Where=None
         ).on(txn)
-        Delete(
+        yield Delete(
             From=schema.ATTACHMENT_CALENDAR_OBJECT,
             Where=None
         ).on(txn)
@@ -219,6 +219,7 @@
     @inlineCallbacks
     def _addAttachment(self, home, calendar, event, dropboxid, name):
 
+        self._sqlCalendarStore._dropbox_ok = True
         txn = self._sqlCalendarStore.newTransaction()
 
         # Create an event with an attachment
@@ -239,6 +240,7 @@
         ))
         yield event.setComponent(cal)
         yield txn.commit()
+        self._sqlCalendarStore._dropbox_ok = False
 
         returnValue(attachment)
 

Modified: CalendarServer/trunk/txdav/caldav/icalendarstore.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/icalendarstore.py	2013-02-08 17:05:13 UTC (rev 10666)
+++ CalendarServer/trunk/txdav/caldav/icalendarstore.py	2013-02-08 17:07:39 UTC (rev 10667)
@@ -84,6 +84,13 @@
 
 
 
+class AttachmentDropboxNotAllowed(Exception):
+    """
+    Dropbox attachments no longer allowed.
+    """
+
+
+
 class QuotaExceeded(Exception):
     """
     The quota for a particular user has been exceeded.

Modified: CalendarServer/trunk/txdav/common/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql.py	2013-02-08 17:05:13 UTC (rev 10666)
+++ CalendarServer/trunk/txdav/common/datastore/sql.py	2013-02-08 17:07:39 UTC (rev 10667)
@@ -150,6 +150,7 @@
     def __init__(self, sqlTxnFactory, notifierFactory,
                  attachmentsPath, attachmentsURIPattern,
                  enableCalendars=True, enableAddressBooks=True,
+                 enableManagedAttachments=True,
                  label="unlabeled", quota=(2 ** 20),
                  logLabels=False, logStats=False, logStatsLogFile=None, logSQL=False,
                  logTransactionWaits=0, timeoutTransactions=0,
@@ -163,6 +164,7 @@
         self.attachmentsURIPattern = attachmentsURIPattern
         self.enableCalendars = enableCalendars
         self.enableAddressBooks = enableAddressBooks
+        self.enableManagedAttachments = enableManagedAttachments
         self.label = label
         self.quota = quota
         self.logLabels = logLabels
@@ -264,7 +266,19 @@
         self._enableNotifications = not state
 
 
+    @inlineCallbacks
+    def dropboxAllowed(self, txn):
+        """
+        Determine whether dropbox attachments are allowed. Once we have migrated to managed attachments,
+        we should never allow dropbox-style attachments to be created.
+        """
+        if not hasattr(self, "_dropbox_ok"):
+            already_migrated = (yield txn.calendarserverValue("MANAGED-ATTACHMENTS", raiseIfMissing=False))
+            self._dropbox_ok = already_migrated is None
+        returnValue(self._dropbox_ok)
 
+
+
 class TransactionStatsCollector(object):
     """
     Used to log each SQL query and statistics about that query during the course of a single transaction.
@@ -480,13 +494,33 @@
 
 
     @inlineCallbacks
-    def calendarserverValue(self, key):
+    def calendarserverValue(self, key, raiseIfMissing=True):
         result = yield self._calendarserver.on(self, name=key)
         if result and len(result) == 1:
             returnValue(result[0][0])
-        raise RuntimeError("Database key %s cannot be determined." % (key,))
+        if raiseIfMissing:
+            raise RuntimeError("Database key %s cannot be determined." % (key,))
+        else:
+            returnValue(None)
 
 
+    @inlineCallbacks
+    def setCalendarserverValue(self, key, value):
+        cs = schema.CALENDARSERVER
+        yield Insert(
+            {cs.NAME: key, cs.VALUE: value},
+        ).on(self)
+
+
+    @inlineCallbacks
+    def updateCalendarserverValue(self, key, value):
+        cs = schema.CALENDARSERVER
+        yield Update(
+            {cs.VALUE: value},
+            Where=cs.NAME == key,
+        ).on(self)
+
+
     def calendarHomeWithUID(self, uid, create=False):
         return self.homeWithUID(ECALENDARTYPE, uid, create=create)
 

Modified: CalendarServer/trunk/txdav/common/datastore/upgrade/sql/others/attachment_migration.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/upgrade/sql/others/attachment_migration.py	2013-02-08 17:05:13 UTC (rev 10666)
+++ CalendarServer/trunk/txdav/common/datastore/upgrade/sql/others/attachment_migration.py	2013-02-08 17:07:39 UTC (rev 10667)
@@ -14,29 +14,46 @@
 # limitations under the License.
 ##
 
-from twisted.internet.defer import inlineCallbacks
+from twisted.internet.defer import inlineCallbacks, returnValue
 from txdav.caldav.datastore.sql import CalendarStoreFeatures
 
 """
 Upgrader that checks for any dropbox attachments, and upgrades them all to managed attachments.
+
+This makes use of a MANAGED-ATTACHMENTS flag in the CALENDARSERVER table to determine whether the upgrade has been
+done for this store. If it has been done, the store will advertise that to the app layer and that must prevent the
+use of dropbox in the future.
 """
 
 @inlineCallbacks
 def doUpgrade(upgrader):
     """
-    Do the required upgrade steps.
+    Do the required upgrade steps. Also, make sure we correctly set the store for having attachments enabled.
     """
 
+    # Ignore if the store is not enabled for managed attachments
+    if not upgrader.sqlStore.enableManagedAttachments:
+        upgrader.log_warn("No dropbox migration - managed attachments not enabled")
+        returnValue(None)
+
+    statusKey = "MANAGED-ATTACHMENTS"
     storeWrapper = CalendarStoreFeatures(upgrader.sqlStore)
     txn = upgrader.sqlStore.newTransaction("attachment_migration.doUpgrade")
     try:
-        needUpgrade = (yield storeWrapper.hasDropboxAttachments(txn))
+        managed = (yield txn.calendarserverValue(statusKey, raiseIfMissing=False))
+        if managed is None:
+            upgrader.log_warn("Checking for dropbox migration")
+            needUpgrade = (yield storeWrapper.hasDropboxAttachments(txn))
+        else:
+            needUpgrade = False
         if needUpgrade:
             upgrader.log_warn("Starting dropbox migration")
             yield storeWrapper.upgradeToManagedAttachments(batchSize=10)
             upgrader.log_warn("Finished dropbox migration")
         else:
             upgrader.log_warn("No dropbox migration needed")
+        if managed is None:
+            yield txn.setCalendarserverValue(statusKey, "1")
     except RuntimeError:
         yield txn.abort()
         raise

Added: CalendarServer/trunk/txdav/common/datastore/upgrade/sql/others/test/__init__.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/upgrade/sql/others/test/__init__.py	                        (rev 0)
+++ CalendarServer/trunk/txdav/common/datastore/upgrade/sql/others/test/__init__.py	2013-02-08 17:07:39 UTC (rev 10667)
@@ -0,0 +1,15 @@
+##
+# Copyright (c) 2013 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##

Added: CalendarServer/trunk/txdav/common/datastore/upgrade/sql/others/test/test_attachment_migration.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/upgrade/sql/others/test/test_attachment_migration.py	                        (rev 0)
+++ CalendarServer/trunk/txdav/common/datastore/upgrade/sql/others/test/test_attachment_migration.py	2013-02-08 17:07:39 UTC (rev 10667)
@@ -0,0 +1,177 @@
+##
+# Copyright (c) 2010-2013 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from twext.enterprise.dal.syntax import Delete
+
+from twisted.internet.defer import inlineCallbacks, succeed, returnValue
+from twisted.trial.unittest import TestCase
+
+from twistedcaldav.config import config
+
+from txdav.caldav.datastore.sql import CalendarStoreFeatures
+from txdav.common.datastore.sql_tables import schema
+from txdav.common.datastore.test.util import theStoreBuilder, \
+    StubNotifierFactory
+from txdav.common.datastore.upgrade.sql.others import attachment_migration
+from txdav.common.datastore.upgrade.sql.upgrade import UpgradeDatabaseOtherService
+
+"""
+Tests for L{txdav.common.datastore.upgrade.sql.upgrade}.
+"""
+
+
+class AttachmentMigrationTests(TestCase):
+    """
+    Tests for L{UpgradeDatabaseSchemaService}.
+    """
+
+    @inlineCallbacks
+    def _initStore(self, enableManagedAttachments=True):
+        """
+        Build a store with certain bits cleaned out.
+        """
+
+        self.patch(config, "EnableManagedAttachments", enableManagedAttachments)
+
+        store = yield theStoreBuilder.buildStore(
+            self, StubNotifierFactory()
+        )
+        store.enableManagedAttachments = enableManagedAttachments
+
+        txn = store.newTransaction()
+        cs = schema.CALENDARSERVER
+        yield Delete(
+            From=cs,
+            Where=cs.NAME == "MANAGED-ATTACHMENTS"
+        ).on(txn)
+        yield txn.commit()
+
+        returnValue(store)
+
+
+    @inlineCallbacks
+    def test_upgradeFromEmptyDropbox(self):
+        """
+        Test L{attachment_migration.doUpgrade} when managed attachments is enabled and dropbox items do not exist.
+        """
+
+        didUpgrade = [False, ]
+        def _hasDropboxAttachments(_self, txn):
+            return succeed(False)
+        self.patch(CalendarStoreFeatures, "hasDropboxAttachments", _hasDropboxAttachments)
+
+        def _upgradeToManagedAttachments(_self, batchSize=10):
+            didUpgrade[0] = True
+            return succeed(None)
+        self.patch(CalendarStoreFeatures, "upgradeToManagedAttachments", _upgradeToManagedAttachments)
+
+        store = (yield self._initStore())
+
+        upgrader = UpgradeDatabaseOtherService(store, None)
+        yield attachment_migration.doUpgrade(upgrader)
+        self.assertFalse(didUpgrade[0])
+
+        txn = upgrader.sqlStore.newTransaction()
+        managed = (yield txn.calendarserverValue("MANAGED-ATTACHMENTS", raiseIfMissing=False))
+        yield txn.commit()
+        self.assertNotEqual(managed, None)
+
+
+    @inlineCallbacks
+    def test_upgradeFromDropboxOK(self):
+        """
+        Test L{attachment_migration.doUpgrade} when managed attachments is enabled and dropbox items exist.
+        """
+
+        didUpgrade = [False, ]
+        def _hasDropboxAttachments(_self, txn):
+            return succeed(True)
+        self.patch(CalendarStoreFeatures, "hasDropboxAttachments", _hasDropboxAttachments)
+
+        def _upgradeToManagedAttachments(_self, batchSize=10):
+            didUpgrade[0] = True
+            return succeed(None)
+        self.patch(CalendarStoreFeatures, "upgradeToManagedAttachments", _upgradeToManagedAttachments)
+
+        store = (yield self._initStore())
+
+        upgrader = UpgradeDatabaseOtherService(store, None)
+        yield attachment_migration.doUpgrade(upgrader)
+        self.assertTrue(didUpgrade[0])
+
+        txn = upgrader.sqlStore.newTransaction()
+        managed = (yield txn.calendarserverValue("MANAGED-ATTACHMENTS", raiseIfMissing=False))
+        yield txn.commit()
+        self.assertNotEqual(managed, None)
+
+
+    @inlineCallbacks
+    def test_upgradeAlreadyDone(self):
+        """
+        Test L{attachment_migration.doUpgrade} when managed attachments is enabled and migration already done.
+        """
+
+        didUpgrade = [False, ]
+        def _hasDropboxAttachments(_self, txn):
+            return succeed(True)
+        self.patch(CalendarStoreFeatures, "hasDropboxAttachments", _hasDropboxAttachments)
+
+        def _upgradeToManagedAttachments(_self, batchSize=10):
+            didUpgrade[0] = True
+            return succeed(None)
+        self.patch(CalendarStoreFeatures, "upgradeToManagedAttachments", _upgradeToManagedAttachments)
+
+        store = (yield self._initStore())
+        txn = store.newTransaction()
+        yield txn.setCalendarserverValue("MANAGED-ATTACHMENTS", "1")
+        yield txn.commit()
+
+        upgrader = UpgradeDatabaseOtherService(store, None)
+        yield attachment_migration.doUpgrade(upgrader)
+        self.assertFalse(didUpgrade[0])
+
+        txn = upgrader.sqlStore.newTransaction()
+        managed = (yield txn.calendarserverValue("MANAGED-ATTACHMENTS", raiseIfMissing=False))
+        yield txn.commit()
+        self.assertNotEqual(managed, None)
+
+
+    @inlineCallbacks
+    def test_upgradeNotEnabled(self):
+        """
+        Test L{attachment_migration.doUpgrade} when managed attachments is disabled.
+        """
+
+        didUpgrade = [False, ]
+        def _hasDropboxAttachments(_self, txn):
+            return succeed(True)
+        self.patch(CalendarStoreFeatures, "hasDropboxAttachments", _hasDropboxAttachments)
+
+        def _upgradeToManagedAttachments(_self, batchSize=10):
+            didUpgrade[0] = True
+            return succeed(None)
+        self.patch(CalendarStoreFeatures, "upgradeToManagedAttachments", _upgradeToManagedAttachments)
+
+        store = (yield self._initStore(False))
+
+        upgrader = UpgradeDatabaseOtherService(store, None)
+        yield attachment_migration.doUpgrade(upgrader)
+        self.assertFalse(didUpgrade[0])
+
+        txn = upgrader.sqlStore.newTransaction()
+        managed = (yield txn.calendarserverValue("MANAGED-ATTACHMENTS", raiseIfMissing=False))
+        yield txn.commit()
+        self.assertEqual(managed, None)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20130208/c3cdb1d5/attachment-0001.html>


More information about the calendarserver-changes mailing list