예제 #1
0
 def testSafeEntityDel(self):
     """Test SafeEntityDel()."""
     key_name = 'fookeyname'
     self.mox.StubOutWithMock(gae_util.logging, 'info')
     entity = self.mox.CreateMockAnything()
     entity.delete_async().AndRaise(gae_util.db.Error)
     key = self.mox.CreateMockAnything()
     entity.key().AndReturn(key)
     key.name().AndReturn(key_name)
     exc = ''
     gae_util.logging.warning(('Model.delete(%s) failed: %s. '
                               'this entity is now probably empty.'),
                              key_name, str(exc))
     self.mox.ReplayAll()
     gae_util.SafeEntityDel(entity)
     self.mox.VerifyAll()
예제 #2
0
    def post(self):
        """POST method.

    This method behaves a little strangely.  BlobstoreUploadHandler
    only allows returns statuses of 301, 302, 303 (not even 200), so
    one must redirect away to return more information to the caller.

    Parameters:
      file: package file contents
      pkginfo: packageinfo file contents
      name: filename of package e.g. 'Firefox-1.0.dmg'
    """
        # Only blobstore/upload service/scotty requests should be
        # invoking this handler.
        if not handlers.IsBlobstore():
            logging.critical('POST to /uploadpkg not from Blobstore: %s',
                             self.request.headers)
            self.redirect('/')

        gaeserver.DoMunkiAuth(require_level=gaeserver.LEVEL_UPLOADPKG)

        user = self.request.get('user')
        filename = self.request.get('name')
        install_types = self.request.get('install_types')
        catalogs = self.request.get('catalogs', None)
        manifests = self.request.get('manifests', None)
        if catalogs is None or not install_types or not user or not filename:
            msg = 'uploadpkg POST required parameters missing'
            logging.error(msg)
            self.redirect('/uploadpkg?mode=error&msg=%s' % msg)
            return
        if catalogs == '':
            catalogs = []
        else:
            catalogs = catalogs.split(',')
        if manifests in ['', None]:
            manifests = []
        else:
            manifests = manifests.split(',')
        install_types = install_types.split(',')

        upload_files = self.get_uploads('file')
        upload_pkginfo_files = self.get_uploads('pkginfo')
        if not len(upload_pkginfo_files) and not self.request.get('pkginfo'):
            self.redirect('/uploadpkg?mode=error&msg=No%20file%20received')
            return

        if len(upload_pkginfo_files):
            # obtain the pkginfo from a blob, and then throw it away.  this is
            # a necessary hack because the upload handler grabbed it, but we don't
            # intend to keep it in blobstore.
            pkginfo_str = gae_util.GetBlobAndDel(upload_pkginfo_files[0].key())
        else:
            # otherwise, grab the form parameter.
            pkginfo_str = self.request.get('pkginfo')

        blob_info = upload_files[0]
        blobstore_key = str(blob_info.key())

        # Parse, validate, and encode the pkginfo plist.
        plist = plist_lib.MunkiPackageInfoPlist(pkginfo_str)
        try:
            plist.Parse()
        except plist_lib.PlistError:
            logging.exception('Invalid pkginfo plist uploaded:\n%s\n',
                              pkginfo_str)
            gae_util.SafeBlobDel(blobstore_key)
            self.redirect(
                '/uploadpkg?mode=error&msg=No%20valid%20pkginfo%20received')
            return

        filename = plist['installer_item_location']
        pkgdata_sha256 = plist['installer_item_hash']

        # verify the blob was actually written; in case Blobstore failed to write
        # the blob but still POSTed to this handler (very, very rare).
        blob_info = blobstore.BlobInfo.get(blobstore_key)
        if not blob_info:
            logging.critical(
                'Blobstore returned a key for %s that does not exist: %s',
                filename, blobstore_key)
            self.redirect('/uploadpkg?mode=error&msg=Blobstore%20failure')
            return

        # Obtain a lock on the PackageInfo entity for this package.
        lock = 'pkgsinfo_%s' % filename
        if not gae_util.ObtainLock(lock, timeout=5.0):
            gae_util.SafeBlobDel(blobstore_key)
            self.redirect(
                '/uploadpkg?mode=error&msg=Could%20not%20lock%20pkgsinfo')
            return

        old_blobstore_key = None
        pkg = models.PackageInfo.get_or_insert(filename)
        if not pkg.IsSafeToModify():
            gae_util.ReleaseLock(lock)
            gae_util.SafeBlobDel(blobstore_key)
            self.redirect(
                '/uploadpkg?mode=error&msg=Package%20is%20not%20modifiable')
            return

        if pkg.blobstore_key:
            # a previous blob exists.  delete it when the update has succeeded.
            old_blobstore_key = pkg.blobstore_key

        pkg.blobstore_key = blobstore_key
        pkg.name = plist.GetPackageName()
        pkg.filename = filename
        pkg.user = user
        pkg.catalogs = catalogs
        pkg.manifests = manifests
        pkg.install_types = install_types
        pkg.plist = plist
        pkg.pkgdata_sha256 = pkgdata_sha256

        # update the PackageInfo model with the new plist string and blobstore key.
        try:
            pkg.put()
            success = True
        except db.Error:
            logging.exception('error on PackageInfo.put()')
            success = False

        # if it failed, delete the blob that was just uploaded -- it's
        # an orphan.
        if not success:
            gae_util.SafeBlobDel(blobstore_key)
            # if this is a new entity (get_or_insert puts), attempt to delete it.
            if not old_blobstore_key:
                gae_util.SafeEntityDel(pkg)
            gae_util.ReleaseLock(lock)
            self.redirect('/uploadpkg?mode=error')
            return

        # if an old blob was associated with this Package, delete it.
        # the new blob that was just uploaded has replaced it.
        if old_blobstore_key:
            gae_util.SafeBlobDel(old_blobstore_key)

        gae_util.ReleaseLock(lock)

        # Generate catalogs for newly uploaded pkginfo plist.
        for catalog in pkg.catalogs:
            models.Catalog.Generate(catalog, delay=1)

        # Log admin upload to Datastore.
        admin_log = models.AdminPackageLog(user=user,
                                           action='uploadpkg',
                                           filename=filename,
                                           catalogs=catalogs,
                                           manifests=manifests,
                                           install_types=install_types,
                                           plist=pkg.plist.GetXml())
        admin_log.put()

        self.redirect('/uploadpkg?mode=success&key=%s' % blobstore_key)