예제 #1
0
    def from_upload(cls, upload, addon, platforms, send_signal=True,
                    source=None, is_beta=False):
        from olympia.addons.models import AddonFeatureCompatibility

        data = utils.parse_addon(upload, addon)
        try:
            license = addon.versions.latest().license_id
        except Version.DoesNotExist:
            license = None
        v = cls.objects.create(
            addon=addon,
            version=data['version'],
            license_id=license,
            source=source
        )
        log.info('New version: %r (%s) from %r' % (v, v.id, upload))

        # Update the add-on e10s compatibility since we're creating a new
        # version that may change that.
        e10s_compatibility = data.get('e10s_compatibility')
        if e10s_compatibility is not None:
            feature_compatibility = (
                AddonFeatureCompatibility.objects.get_or_create(addon=addon)[0]
            )
            feature_compatibility.update(e10s=e10s_compatibility)

        AV = ApplicationsVersions
        for app in data.get('apps', []):
            AV(version=v, min=app.min, max=app.max,
               application=app.id).save()
        if addon.type == amo.ADDON_SEARCH:
            # Search extensions are always for all platforms.
            platforms = [amo.PLATFORM_ALL.id]
        else:
            platforms = cls._make_safe_platform_files(platforms)

        for platform in platforms:
            File.from_upload(upload, v, platform, parse_data=data,
                             is_beta=is_beta)

        v.disable_old_files()
        # After the upload has been copied to all platforms, remove the upload.
        storage.delete(upload.path)
        if send_signal:
            version_uploaded.send(sender=v)

        # Track the time it took from first upload through validation
        # (and whatever else) until a version was created.
        upload_start = utc_millesecs_from_epoch(upload.created)
        now = datetime.datetime.now()
        now_ts = utc_millesecs_from_epoch(now)
        upload_time = now_ts - upload_start

        log.info('Time for version {version} creation from upload: {delta}; '
                 'created={created}; now={now}'
                 .format(delta=upload_time, version=v,
                         created=upload.created, now=now))
        statsd.timing('devhub.version_created_from_upload', upload_time)

        return v
예제 #2
0
    def from_upload(cls, upload, addon, platforms, send_signal=True,
                    source=None, is_beta=False):
        data = utils.parse_addon(upload, addon)
        try:
            license = addon.versions.latest().license_id
        except Version.DoesNotExist:
            license = None
        max_len = cls._meta.get_field_by_name('_developer_name')[0].max_length
        developer = data.get('developer_name', '')[:max_len]
        v = cls.objects.create(
            addon=addon,
            version=data['version'],
            license_id=license,
            _developer_name=developer,
            source=source
        )
        log.info('New version: %r (%s) from %r' % (v, v.id, upload))

        AV = ApplicationsVersions
        for app in data.get('apps', []):
            AV(version=v, min=app.min, max=app.max,
               application=app.id).save()
        if addon.type == amo.ADDON_SEARCH:
            # Search extensions are always for all platforms.
            platforms = [amo.PLATFORM_ALL.id]
        else:
            platforms = cls._make_safe_platform_files(platforms)

        for platform in platforms:
            File.from_upload(upload, v, platform, parse_data=data,
                             is_beta=is_beta)

        v.disable_old_files()
        # After the upload has been copied to all platforms, remove the upload.
        storage.delete(upload.path)
        if send_signal:
            version_uploaded.send(sender=v)

        # Track the time it took from first upload through validation
        # (and whatever else) until a version was created.
        upload_start = utc_millesecs_from_epoch(upload.created)
        now = datetime.datetime.now()
        now_ts = utc_millesecs_from_epoch(now)
        upload_time = now_ts - upload_start

        log.info('Time for version {version} creation from upload: {delta}; '
                 'created={created}; now={now}'
                 .format(delta=upload_time, version=v,
                         created=upload.created, now=now))
        statsd.timing('devhub.version_created_from_upload', upload_time)

        return v
예제 #3
0
 def test_trusted_public_to_public(self):
     upload = self.upload('extension')
     d = parse_addon(upload.path)
     self.addon.update(status=amo.STATUS_PUBLIC, trusted=True)
     eq_(self.addon.status, amo.STATUS_PUBLIC)
     f = File.from_upload(upload, self.version, self.platform, parse_data=d)
     eq_(f.status, amo.STATUS_PUBLIC)
예제 #4
0
 def test_public_to_unreviewed(self):
     upload = self.upload('extension')
     d = parse_addon(upload.path)
     self.addon.update(status=amo.STATUS_PUBLIC)
     eq_(self.addon.status, amo.STATUS_PUBLIC)
     f = File.from_upload(upload, self.version, self.platform, parse_data=d)
     eq_(f.status, amo.STATUS_UNREVIEWED)
예제 #5
0
 def test_public_to_unreviewed(self):
     upload = self.upload('extension')
     d = parse_addon(upload.path)
     self.addon.update(status=amo.STATUS_PUBLIC)
     assert self.addon.status == amo.STATUS_PUBLIC
     f = File.from_upload(upload, self.version, self.platform, parse_data=d)
     assert f.status == amo.STATUS_AWAITING_REVIEW
예제 #6
0
    def test_extract(self):
        upload = self.get_upload('webextension_no_id.xpi')
        parsed_data = parse_addon(upload, user=mock.Mock())
        # Remove the permissions from the parsed data so they aren't added.
        pdata_permissions = parsed_data.pop('permissions')
        pdata_cscript = parsed_data.pop('content_scripts')
        file_ = File.from_upload(upload, self.version, self.platform,
                                 parsed_data=parsed_data)
        assert WebextPermission.objects.count() == 0
        assert file_.webext_permissions_list == []

        call_command('extract_permissions')

        file_ = File.objects.get(id=file_.id)
        assert WebextPermission.objects.get(file=file_)
        permissions_list = file_.webext_permissions_list
        assert len(permissions_list) == 8
        assert permissions_list == [
            # first 5 are 'permissions'
            u'http://*/*', u'https://*/*', 'bookmarks', 'made up permission',
            'https://google.com/',
            # last 3 are 'content_scripts' matches we treat the same
            '*://*.mozilla.org/*', '*://*.mozilla.com/*',
            'https://*.mozillians.org/*']
        assert permissions_list[0:5] == pdata_permissions
        assert permissions_list[5:8] == [x for y in [
            cs['matches'] for cs in pdata_cscript] for x in y]
예제 #7
0
 def test_trusted_lite_to_lite(self):
     upload = self.upload('extension')
     d = parse_addon(upload.path)
     self.addon.update(status=amo.STATUS_LITE, trusted=True)
     assert self.addon.status == amo.STATUS_LITE
     f = File.from_upload(upload, self.version, self.platform, parse_data=d)
     assert f.status == amo.STATUS_LITE
예제 #8
0
 def test_search_extension(self):
     upload = self.upload('search.xml')
     data = parse_addon(upload.path)
     file_ = File.from_upload(upload, self.version, self.platform,
                              parsed_data=data)
     assert file_.filename.endswith('.xml')
     assert file_.no_restart
예제 #9
0
 def test_lite_to_unreviewed(self):
     upload = self.upload('extension')
     d = parse_addon(upload.path)
     self.addon.update(status=amo.STATUS_LITE)
     assert self.addon.status == amo.STATUS_LITE
     f = File.from_upload(upload, self.version, self.platform, parse_data=d)
     assert f.status == amo.STATUS_UNREVIEWED
예제 #10
0
 def test_public_to_beta(self):
     upload = self.upload('beta-extension')
     d = parse_addon(upload.path)
     self.addon.update(status=amo.STATUS_PUBLIC)
     assert self.addon.status == amo.STATUS_PUBLIC
     f = File.from_upload(upload, self.version, self.platform, is_beta=True,
                          parse_data=d)
     assert f.status == amo.STATUS_BETA
예제 #11
0
 def test_beta_version_non_public(self):
     # Only public add-ons can get beta versions.
     upload = self.upload('beta-extension')
     d = parse_addon(upload.path)
     self.addon.update(status=amo.STATUS_LITE)
     assert self.addon.status == amo.STATUS_LITE
     f = File.from_upload(upload, self.version, self.platform, parse_data=d)
     assert f.status == amo.STATUS_UNREVIEWED
예제 #12
0
 def test_file_validation(self):
     upload = self.upload('jetpack')
     file = File.from_upload(upload, self.version, self.platform)
     fv = FileValidation.objects.get(file=file)
     eq_(json.loads(fv.validation), json.loads(upload.validation))
     eq_(fv.valid, True)
     eq_(fv.errors, 0)
     eq_(fv.warnings, 1)
     eq_(fv.notices, 2)
예제 #13
0
 def test_litenominated_to_unreviewed(self):
     upload = self.upload('extension')
     d = parse_addon(upload.path)
     with mock.patch('olympia.addons.models.Addon.update_status'):
         # mock update_status because it doesn't like Addons without files.
         self.addon.update(status=amo.STATUS_LITE_AND_NOMINATED)
     assert self.addon.status == amo.STATUS_LITE_AND_NOMINATED
     f = File.from_upload(upload, self.version, self.platform, parse_data=d)
     assert f.status == amo.STATUS_UNREVIEWED
예제 #14
0
 def test_file_validation(self):
     upload = self.upload('jetpack')
     file = File.from_upload(upload, self.version, self.platform)
     fv = FileValidation.objects.get(file=file)
     assert json.loads(fv.validation) == json.loads(upload.validation)
     assert fv.valid
     assert fv.errors == 0
     assert fv.warnings == 1
     assert fv.notices == 2
예제 #15
0
 def test_webextension_crx(self):
     """Test to ensure we accept CRX uploads, but convert them into XPI
     files ASAP to keep things simple.
     """
     upload = self.upload('webextension.crx')
     file_ = File.from_upload(upload, self.version, self.platform,
                              parse_data={'is_webextension': True})
     assert file_.filename.endswith('.xpi')
     assert file_.is_webextension
     storage.delete(upload.path)
예제 #16
0
 def test_webextension_crx_large(self):
     """Test to ensure we accept large CRX uploads, because of how we
     write them to storage.
     """
     upload = self.upload('https-everywhere.crx')
     file_ = File.from_upload(upload, self.version, self.platform,
                              parse_data={'is_webextension': True})
     assert file_.filename.endswith('.xpi')
     assert file_.is_webextension
     storage.delete(upload.path)
예제 #17
0
 def test_permissions(self):
     upload = self.upload('webextension_no_id.xpi')
     parsed_data = parse_addon(upload)
     # 5 total permissions.
     assert len(parsed_data['permissions']) == 5
     file_ = File.from_upload(upload, self.version, self.platform,
                              parsed_data=parsed_data)
     permissions_list = file_.webext_permissions_list
     assert len(permissions_list) == 5
     assert permissions_list == [u'http://*/*', u'https://*/*', 'bookmarks',
                                 'made up permission', 'https://google.com/'
                                 ]
예제 #18
0
    def test_force_extract(self):
        upload = self.get_upload('webextension_no_id.xpi')
        parsed_data = parse_addon(upload, user=mock.Mock())
        # change the permissions so we can tell they've been re-parsed.
        parsed_data['permissions'].pop()
        file_ = File.from_upload(upload, self.version, self.platform,
                                 parsed_data=parsed_data)
        assert WebextPermission.objects.count() == 1
        assert len(file_.webext_permissions_list) == 7

        call_command('extract_permissions', force=True)

        file_ = File.objects.get(id=file_.id)
        assert WebextPermission.objects.get(file=file_)
        assert len(file_.webext_permissions_list) == 8
예제 #19
0
    def test_extract(self):
        upload = self.get_upload('webextension_no_id.xpi')
        parsed_data = parse_addon(upload)
        # Delete the permissions from the parsed data so they aren't added.
        del parsed_data['permissions']
        file_ = File.from_upload(upload, self.version, self.platform,
                                 parsed_data=parsed_data)
        assert WebextPermission.objects.count() == 0
        assert file_.webext_permissions_list == []

        call_command('extract_permissions')

        file_ = File.objects.no_cache().get(id=file_.id)
        assert WebextPermission.objects.get(file=file_)
        permissions_list = file_.webext_permissions_list
        assert len(permissions_list) == 5
        assert permissions_list == [u'http://*/*', u'https://*/*', 'bookmarks',
                                    'made up permission', 'https://google.com/'
                                    ]
예제 #20
0
 def test_validator_sets_require_chrome(self):
     validation = json.dumps({
         "errors": 0,
         "success": True,
         "warnings": 0,
         "notices": 0,
         "message_tree": {},
         "messages": [],
         "metadata": {
             "version": "1.0",
             "name": "gK0Bes Bot",
             "id": "gkobes@gkobes",
             "requires_chrome": True
         }
     })
     upload = self.get_upload(filename='extension.xpi',
                              validation=validation)
     version = Version.objects.filter(addon__pk=3615)[0]
     file_ = File.from_upload(upload, version, amo.PLATFORM_LINUX.id)
     assert file_.requires_chrome
예제 #21
0
 def test_validator_sets_binary_via_content(self):
     validation = json.dumps({
         "errors": 0,
         "success": True,
         "warnings": 0,
         "notices": 0,
         "message_tree": {},
         "messages": [],
         "metadata": {
             "contains_binary_content": True,
             "version": "1.0",
             "name": "gK0Bes Bot",
             "id": "gkobes@gkobes",
         }
     })
     upload = self.get_upload(filename='extension.xpi',
                              validation=validation)
     version = Version.objects.filter(addon__pk=3615)[0]
     file_ = File.from_upload(upload, version, amo.PLATFORM_LINUX.id)
     eq_(file_.binary, True)
예제 #22
0
 def test_extension_extension(self):
     upload = self.upload('extension.xpi')
     f = File.from_upload(upload, self.version, self.platform)
     eq_(f.filename.endswith('.xpi'), True)
     assert not self.addon.tags.exists()
예제 #23
0
 def test_size(self):
     upload = self.upload('extension')
     f = File.from_upload(upload, self.version, self.platform)
     eq_(f.size, 2264)
예제 #24
0
 def test_extension_extension(self):
     upload = self.upload('extension.xpi')
     f = File.from_upload(upload, self.version, self.platform)
     assert f.filename.endswith('.xpi')
예제 #25
0
 def test_jetpack_version(self):
     upload = self.upload('jetpack')
     f = File.from_upload(upload, self.version, self.platform)
     file_ = File.objects.get(id=f.id)
     assert file_.jetpack_version == '1.0b4'
예제 #26
0
 def test_file_hash_paranoia(self):
     upload = self.upload('extension')
     f = File.from_upload(upload, self.version, self.platform)
     assert f.hash.startswith('sha256:035ae07b4988711')
예제 #27
0
    def from_upload(cls,
                    upload,
                    addon,
                    selected_apps,
                    channel,
                    parsed_data=None):
        """
        Create a Version instance and corresponding File(s) from a
        FileUpload, an Addon, a list of compatible app ids, a channel id and
        the parsed_data generated by parse_addon().

        Note that it's the caller's responsability to ensure the file is valid.
        We can't check for that here because an admin may have overridden the
        validation results.
        """
        from olympia.addons.models import AddonReviewerFlags
        from olympia.git.utils import create_git_extraction_entry

        assert parsed_data is not None

        if addon.status == amo.STATUS_DISABLED:
            raise VersionCreateError(
                'Addon is Mozilla Disabled; no new versions are allowed.')

        license_id = None
        if channel == amo.RELEASE_CHANNEL_LISTED:
            previous_version = addon.find_latest_version(channel=channel,
                                                         exclude=())
            if previous_version and previous_version.license_id:
                license_id = previous_version.license_id
        approval_notes = None
        if parsed_data.get('is_mozilla_signed_extension'):
            approval_notes = (u'This version has been signed with '
                              u'Mozilla internal certificate.')
        version = cls.objects.create(
            addon=addon,
            approval_notes=approval_notes,
            version=parsed_data['version'],
            license_id=license_id,
            channel=channel,
        )
        email = upload.user.email if upload.user and upload.user.email else ''
        with core.override_remote_addr(upload.ip_address):
            log.info('New version: %r (%s) from %r' %
                     (version, version.id, upload),
                     extra={
                         'email': email,
                         'guid': addon.guid,
                         'upload': upload.uuid.hex,
                         'user_id': upload.user_id,
                         'from_api': upload.source == amo.UPLOAD_SOURCE_API,
                     })
            activity.log_create(amo.LOG.ADD_VERSION,
                                version,
                                addon,
                                user=upload.user or get_task_user())

        if addon.type == amo.ADDON_STATICTHEME:
            # We don't let developers select apps for static themes
            selected_apps = [app.id for app in amo.APP_USAGE]

        compatible_apps = {}
        for app in parsed_data.get('apps', []):
            if app.id not in selected_apps:
                # If the user chose to explicitly deselect Firefox for Android
                # we're not creating the respective `ApplicationsVersions`
                # which will have this add-on then be listed only for
                # Firefox specifically.
                continue

            compatible_apps[app.appdata] = ApplicationsVersions(
                version=version, min=app.min, max=app.max, application=app.id)
            compatible_apps[app.appdata].save()

        # See #2828: sometimes when we generate the filename(s) below, in
        # File.from_upload(), cache-machine is confused and has trouble
        # fetching the ApplicationsVersions that were just created. To work
        # around this we pre-generate version.compatible_apps and avoid the
        # queries completely.
        version._compatible_apps = compatible_apps

        # For backwards compatibility. We removed specific platform
        # support during submission but we don't handle it any different
        # beyond that yet. That means, we're going to simply set it
        # to `PLATFORM_ALL` and also have the backend create separate
        # files for each platform. Cleaning that up is another step.
        # Given the timing on this, we don't care about updates to legacy
        # add-ons as well.
        # Create relevant file and update the all_files cached property on the
        # Version, because we might need it afterwards.
        version.all_files = [
            File.from_upload(upload=upload,
                             version=version,
                             platform=amo.PLATFORM_ALL.id,
                             parsed_data=parsed_data)
        ]

        version.inherit_nomination(from_statuses=[amo.STATUS_AWAITING_REVIEW])
        version.disable_old_files()

        # After the upload has been copied to all platforms, remove the upload.
        storage.delete(upload.path)
        upload.path = ''
        upload.save()

        version_uploaded.send(instance=version, sender=Version)

        if version.is_webextension:
            if (waffle.switch_is_active('enable-yara')
                    or waffle.switch_is_active('enable-customs')
                    or waffle.switch_is_active('enable-wat')):
                ScannerResult.objects.filter(upload_id=upload.id).update(
                    version=version)

        if waffle.switch_is_active('enable-uploads-commit-to-git-storage'):
            # Schedule this version for git extraction.
            transaction.on_commit(
                lambda: create_git_extraction_entry(version=version))

        # Generate a preview and icon for listed static themes
        if (addon.type == amo.ADDON_STATICTHEME
                and channel == amo.RELEASE_CHANNEL_LISTED):
            theme_data = parsed_data.get('theme', {})
            generate_static_theme_preview(theme_data, version.pk)

        # Reset add-on reviewer flags to disable auto-approval and require
        # admin code review if the package has already been signed by mozilla.
        reviewer_flags_defaults = {}
        is_mozilla_signed = parsed_data.get('is_mozilla_signed_extension')
        if upload.validation_timeout:
            reviewer_flags_defaults['needs_admin_code_review'] = True
        if is_mozilla_signed and addon.type != amo.ADDON_LPAPP:
            reviewer_flags_defaults['needs_admin_code_review'] = True
            reviewer_flags_defaults['auto_approval_disabled'] = True

        if reviewer_flags_defaults:
            AddonReviewerFlags.objects.update_or_create(
                addon=addon, defaults=reviewer_flags_defaults)

        # Authors need to be notified about auto-approval delay again since
        # they are submitting a new version.
        addon.reset_notified_about_auto_approval_delay()

        # Track the time it took from first upload through validation
        # (and whatever else) until a version was created.
        upload_start = utc_millesecs_from_epoch(upload.created)
        now = datetime.datetime.now()
        now_ts = utc_millesecs_from_epoch(now)
        upload_time = now_ts - upload_start

        log.info('Time for version {version} creation from upload: {delta}; '
                 'created={created}; now={now}'.format(delta=upload_time,
                                                       version=version,
                                                       created=upload.created,
                                                       now=now))
        statsd.timing('devhub.version_created_from_upload', upload_time)

        return version
예제 #28
0
 def test_filename(self):
     upload = self.upload('jetpack')
     f = File.from_upload(upload, self.version, self.platform)
     eq_(f.filename, 'xxx-0.1-mac.xpi')
예제 #29
0
 def test_jetpack_with_invalid_json(self):
     upload = self.upload('jetpack_invalid')
     f = File.from_upload(upload, self.version, self.platform)
     file_ = File.objects.get(id=f.id)
     eq_(file_.jetpack_version, None)
     assert not self.addon.tags.exists()
예제 #30
0
 def test_jetpack_version(self):
     upload = self.upload('jetpack')
     f = File.from_upload(upload, self.version, self.platform)
     file_ = File.objects.get(id=f.id)
     eq_(file_.jetpack_version, '1.0b4')
     eq_(['jetpack'], [t.tag_text for t in self.addon.tags.all()])
예제 #31
0
 def test_not_multi_package(self):
     upload = self.upload('extension')
     file_ = File.from_upload(upload, self.version, self.platform)
     assert not file_.is_multi_package
예제 #32
0
 def test_search_extension(self):
     upload = self.upload('search.xml')
     f = File.from_upload(upload, self.version, self.platform)
     eq_(f.filename.endswith('.xml'), True)
예제 #33
0
 def test_langpack_extension(self):
     upload = self.upload('langpack.xpi')
     f = File.from_upload(upload, self.version, self.platform)
     eq_(f.filename.endswith('.xpi'), True)
예제 #34
0
    def from_upload(cls,
                    upload,
                    addon,
                    selected_apps,
                    channel,
                    parsed_data=None):
        """
        Create a Version instance and corresponding File(s) from a
        FileUpload, an Addon, a list of compatible app ids, a channel id and
        the parsed_data generated by parse_addon().

        Note that it's the caller's responsability to ensure the file is valid.
        We can't check for that here because an admin may have overridden the
        validation results.
        """
        from olympia.addons.models import AddonReviewerFlags
        from olympia.addons.utils import RestrictionChecker
        from olympia.git.utils import create_git_extraction_entry

        assert parsed_data is not None

        if addon.status == amo.STATUS_DISABLED:
            raise VersionCreateError(
                'Addon is Mozilla Disabled; no new versions are allowed.')

        if upload.addon and upload.addon != addon:
            raise VersionCreateError(
                'FileUpload was made for a different Addon')

        if not upload.user or not upload.ip_address or not upload.source:
            raise VersionCreateError(
                'FileUpload does not have some required fields')

        if not upload.user.last_login_ip or not upload.user.email:
            raise VersionCreateError(
                'FileUpload user does not have some required fields')

        license_id = None
        if channel == amo.RELEASE_CHANNEL_LISTED:
            previous_version = addon.find_latest_version(channel=channel,
                                                         exclude=())
            if previous_version and previous_version.license_id:
                license_id = previous_version.license_id
        approval_notes = None
        if parsed_data.get('is_mozilla_signed_extension'):
            approval_notes = (
                'This version has been signed with Mozilla internal certificate.'
            )
        version = cls.objects.create(
            addon=addon,
            approval_notes=approval_notes,
            version=parsed_data['version'],
            license_id=license_id,
            channel=channel,
        )
        email = upload.user.email if upload.user and upload.user.email else ''
        with core.override_remote_addr(upload.ip_address):
            # The following log statement is used by foxsec-pipeline.
            # We override the IP because it might be called from a task and we
            # want the original IP from the submitter.
            log.info(
                'New version: %r (%s) from %r' % (version, version.id, upload),
                extra={
                    'email': email,
                    'guid': addon.guid,
                    'upload': upload.uuid.hex,
                    'user_id': upload.user_id,
                    'from_api': upload.source == amo.UPLOAD_SOURCE_API,
                },
            )
            activity.log_create(amo.LOG.ADD_VERSION,
                                version,
                                addon,
                                user=upload.user or get_task_user())

        if addon.type == amo.ADDON_STATICTHEME:
            # We don't let developers select apps for static themes
            selected_apps = [app.id for app in amo.APP_USAGE]

        compatible_apps = {}
        for app in parsed_data.get('apps', []):
            if app.id not in selected_apps:
                # If the user chose to explicitly deselect Firefox for Android
                # we're not creating the respective `ApplicationsVersions`
                # which will have this add-on then be listed only for
                # Firefox specifically.
                continue

            compatible_apps[app.appdata] = ApplicationsVersions(
                version=version, min=app.min, max=app.max, application=app.id)
            compatible_apps[app.appdata].save()

        # Pre-generate _compatible_apps property to avoid accidentally
        # triggering queries with that instance later.
        version._compatible_apps = compatible_apps

        # Create relevant file and update the all_files cached property on the
        # Version, because we might need it afterwards.
        version.all_files = [
            File.from_upload(
                upload=upload,
                version=version,
                parsed_data=parsed_data,
            )
        ]

        version.inherit_nomination(from_statuses=[amo.STATUS_AWAITING_REVIEW])
        version.disable_old_files()

        # After the upload has been copied to its permanent location, delete it
        # from storage. Keep the FileUpload instance (it gets cleaned up by a
        # cron eventually some time after its creation, in amo.cron.gc()),
        # making sure it's associated with the add-on instance.
        storage.delete(upload.path)
        upload.path = ''
        if upload.addon is None:
            upload.addon = addon
        upload.save()

        version_uploaded.send(instance=version, sender=Version)

        if version.is_webextension:
            if (waffle.switch_is_active('enable-yara')
                    or waffle.switch_is_active('enable-customs')
                    or waffle.switch_is_active('enable-wat')):
                ScannerResult.objects.filter(upload_id=upload.id).update(
                    version=version)

        if waffle.switch_is_active('enable-uploads-commit-to-git-storage'):
            # Schedule this version for git extraction.
            transaction.on_commit(
                lambda: create_git_extraction_entry(version=version))

        # Generate a preview and icon for listed static themes
        if (addon.type == amo.ADDON_STATICTHEME
                and channel == amo.RELEASE_CHANNEL_LISTED):
            theme_data = parsed_data.get('theme', {})
            generate_static_theme_preview(theme_data, version.pk)

        # Reset add-on reviewer flags to disable auto-approval and require
        # admin code review if the package has already been signed by mozilla.
        reviewer_flags_defaults = {}
        is_mozilla_signed = parsed_data.get('is_mozilla_signed_extension')
        if upload.validation_timeout:
            reviewer_flags_defaults['needs_admin_code_review'] = True
        if is_mozilla_signed and addon.type != amo.ADDON_LPAPP:
            reviewer_flags_defaults['needs_admin_code_review'] = True
            reviewer_flags_defaults['auto_approval_disabled'] = True

        # Check if the approval should be restricted
        if not RestrictionChecker(upload=upload).is_auto_approval_allowed():
            flag = ('auto_approval_disabled'
                    if channel == amo.RELEASE_CHANNEL_LISTED else
                    'auto_approval_disabled_unlisted')
            reviewer_flags_defaults[flag] = True

        if reviewer_flags_defaults:
            AddonReviewerFlags.objects.update_or_create(
                addon=addon, defaults=reviewer_flags_defaults)

        # Authors need to be notified about auto-approval delay again since
        # they are submitting a new version.
        addon.reset_notified_about_auto_approval_delay()

        # Track the time it took from first upload through validation
        # (and whatever else) until a version was created.
        upload_start = utc_millesecs_from_epoch(upload.created)
        now = datetime.datetime.now()
        now_ts = utc_millesecs_from_epoch(now)
        upload_time = now_ts - upload_start

        log.info('Time for version {version} creation from upload: {delta}; '
                 'created={created}; now={now}'.format(delta=upload_time,
                                                       version=version,
                                                       created=upload.created,
                                                       now=now))
        statsd.timing('devhub.version_created_from_upload', upload_time)

        return version
예제 #35
0
    def from_upload(cls, upload, addon, selected_apps, channel,
                    parsed_data=None):
        """
        Create a Version instance and corresponding File(s) from a
        FileUpload, an Addon, a list of compatible app ids, a channel id and
        the parsed_data generated by parse_addon().

        Note that it's the caller's responsability to ensure the file is valid.
        We can't check for that here because an admin may have overridden the
        validation results.
        """
        assert parsed_data is not None

        if addon.status == amo.STATUS_DISABLED:
            raise VersionCreateError(
                'Addon is Mozilla Disabled; no new versions are allowed.')

        license_id = None
        if channel == amo.RELEASE_CHANNEL_LISTED:
            previous_version = addon.find_latest_version(
                channel=channel, exclude=())
            if previous_version and previous_version.license_id:
                license_id = previous_version.license_id
        approval_notes = None
        if parsed_data.get('is_mozilla_signed_extension'):
            approval_notes = (u'This version has been signed with '
                              u'Mozilla internal certificate.')
        version = cls.objects.create(
            addon=addon,
            approval_notes=approval_notes,
            version=parsed_data['version'],
            license_id=license_id,
            channel=channel,
        )
        log.info(
            'New version: %r (%s) from %r' % (version, version.id, upload))
        activity.log_create(amo.LOG.ADD_VERSION, version, addon)

        if addon.type == amo.ADDON_STATICTHEME:
            # We don't let developers select apps for static themes
            selected_apps = [app.id for app in amo.APP_USAGE]

        compatible_apps = {}
        for app in parsed_data.get('apps', []):
            if app.id not in selected_apps:
                # If the user chose to explicitly deselect Firefox for Android
                # we're not creating the respective `ApplicationsVersions`
                # which will have this add-on then be listed only for
                # Firefox specifically.
                continue

            compatible_apps[app.appdata] = ApplicationsVersions(
                version=version, min=app.min, max=app.max, application=app.id)
            compatible_apps[app.appdata].save()

        # See #2828: sometimes when we generate the filename(s) below, in
        # File.from_upload(), cache-machine is confused and has trouble
        # fetching the ApplicationsVersions that were just created. To work
        # around this we pre-generate version.compatible_apps and avoid the
        # queries completely.
        version._compatible_apps = compatible_apps

        # For backwards compatibility. We removed specific platform
        # support during submission but we don't handle it any different
        # beyond that yet. That means, we're going to simply set it
        # to `PLATFORM_ALL` and also have the backend create separate
        # files for each platform. Cleaning that up is another step.
        # Given the timing on this, we don't care about updates to legacy
        # add-ons as well.
        # Create relevant file and update the all_files cached property on the
        # Version, because we might need it afterwards.
        version.all_files = [File.from_upload(
            upload=upload, version=version, platform=amo.PLATFORM_ALL.id,
            parsed_data=parsed_data
        )]

        version.inherit_nomination(from_statuses=[amo.STATUS_AWAITING_REVIEW])
        version.disable_old_files()
        # After the upload has been copied to all platforms, remove the upload.
        storage.delete(upload.path)
        version_uploaded.send(sender=version)

        if waffle.switch_is_active('enable-uploads-commit-to-git-storage'):
            # Extract into git repository
            AddonGitRepository.extract_and_commit_from_version(
                version=version, author=upload.user)

        # Generate a preview and icon for listed static themes
        if (addon.type == amo.ADDON_STATICTHEME and
                channel == amo.RELEASE_CHANNEL_LISTED):
            theme_data = parsed_data.get('theme', {})
            generate_static_theme_preview(theme_data, version.pk)

        # Track the time it took from first upload through validation
        # (and whatever else) until a version was created.
        upload_start = utc_millesecs_from_epoch(upload.created)
        now = datetime.datetime.now()
        now_ts = utc_millesecs_from_epoch(now)
        upload_time = now_ts - upload_start

        log.info('Time for version {version} creation from upload: {delta}; '
                 'created={created}; now={now}'
                 .format(delta=upload_time, version=version,
                         created=upload.created, now=now))
        statsd.timing('devhub.version_created_from_upload', upload_time)

        return version
예제 #36
0
 def test_filename_no_extension(self):
     upload = self.upload('jetpack')
     # Remove the exension.
     upload.name = upload.name.rsplit('.', 1)[0]
     f = File.from_upload(upload, self.version, self.platform)
     eq_(f.filename, 'xxx-0.1-mac.xpi')
예제 #37
0
    def from_upload(cls,
                    upload,
                    addon,
                    platforms,
                    send_signal=True,
                    source=None,
                    is_beta=False):
        from olympia.addons.models import AddonFeatureCompatibility

        data = utils.parse_addon(upload, addon)
        try:
            license = addon.versions.latest().license_id
        except Version.DoesNotExist:
            license = None
        v = cls.objects.create(addon=addon,
                               version=data['version'],
                               license_id=license,
                               source=source)
        log.info('New version: %r (%s) from %r' % (v, v.id, upload))

        # Update the add-on e10s compatibility since we're creating a new
        # version that may change that.
        e10s_compatibility = data.get('e10s_compatibility')
        if e10s_compatibility is not None:
            feature_compatibility = (
                AddonFeatureCompatibility.objects.get_or_create(
                    addon=addon)[0])
            feature_compatibility.update(e10s=e10s_compatibility)

        AV = ApplicationsVersions
        for app in data.get('apps', []):
            AV(version=v, min=app.min, max=app.max, application=app.id).save()
        if addon.type == amo.ADDON_SEARCH:
            # Search extensions are always for all platforms.
            platforms = [amo.PLATFORM_ALL.id]
        else:
            platforms = cls._make_safe_platform_files(platforms)

        for platform in platforms:
            File.from_upload(upload,
                             v,
                             platform,
                             parse_data=data,
                             is_beta=is_beta)

        v.disable_old_files()
        # After the upload has been copied to all platforms, remove the upload.
        storage.delete(upload.path)
        if send_signal:
            version_uploaded.send(sender=v)

        # Track the time it took from first upload through validation
        # (and whatever else) until a version was created.
        upload_start = utc_millesecs_from_epoch(upload.created)
        now = datetime.datetime.now()
        now_ts = utc_millesecs_from_epoch(now)
        upload_time = now_ts - upload_start

        log.info('Time for version {version} creation from upload: {delta}; '
                 'created={created}; now={now}'.format(delta=upload_time,
                                                       version=v,
                                                       created=upload.created,
                                                       now=now))
        statsd.timing('devhub.version_created_from_upload', upload_time)

        return v
예제 #38
0
 def test_file_hash(self):
     upload = self.upload('jetpack')
     f = File.from_upload(upload, self.version, self.platform)
     assert f.hash.startswith('sha256:')
     assert len(f.hash) == 64 + 7  # 64 for hash, 7 for 'sha256:'
예제 #39
0
    def from_upload(cls,
                    upload,
                    addon,
                    platforms,
                    channel,
                    send_signal=True,
                    source=None,
                    is_beta=False):
        from olympia.addons.models import AddonFeatureCompatibility

        data = utils.parse_addon(upload, addon)
        try:
            license = addon.versions.latest().license_id
        except Version.DoesNotExist:
            license = None
        version = cls.objects.create(
            addon=addon,
            version=data['version'],
            license_id=license,
            source=source,
            channel=channel,
        )
        log.info('New version: %r (%s) from %r' %
                 (version, version.id, upload))

        # Update the add-on e10s compatibility since we're creating a new
        # version that may change that.
        e10s_compatibility = data.get('e10s_compatibility')
        if e10s_compatibility is not None:
            feature_compatibility = (
                AddonFeatureCompatibility.objects.get_or_create(
                    addon=addon)[0])
            feature_compatibility.update(e10s=e10s_compatibility)

        compatible_apps = {}
        for app in data.get('apps', []):
            compatible_apps[app.appdata] = ApplicationsVersions(
                version=version, min=app.min, max=app.max, application=app.id)
            compatible_apps[app.appdata].save()

        # See #2828: sometimes when we generate the filename(s) below, in
        # File.from_upload(), cache-machine is confused and has trouble
        # fetching the ApplicationsVersions that were just created. To work
        # around this we pre-generate version.compatible_apps and avoid the
        # queries completely.
        version.compatible_apps = compatible_apps

        if addon.type == amo.ADDON_SEARCH:
            # Search extensions are always for all platforms.
            platforms = [amo.PLATFORM_ALL.id]
        else:
            platforms = cls._make_safe_platform_files(platforms)

        for platform in platforms:
            File.from_upload(upload,
                             version,
                             platform,
                             parse_data=data,
                             is_beta=is_beta)

        version.disable_old_files()
        # After the upload has been copied to all platforms, remove the upload.
        storage.delete(upload.path)
        if send_signal:
            version_uploaded.send(sender=version)

        # Track the time it took from first upload through validation
        # (and whatever else) until a version was created.
        upload_start = utc_millesecs_from_epoch(upload.created)
        now = datetime.datetime.now()
        now_ts = utc_millesecs_from_epoch(now)
        upload_time = now_ts - upload_start

        log.info('Time for version {version} creation from upload: {delta}; '
                 'created={created}; now={now}'.format(delta=upload_time,
                                                       version=version,
                                                       created=upload.created,
                                                       now=now))
        statsd.timing('devhub.version_created_from_upload', upload_time)

        return version
예제 #40
0
 def test_strict_compat(self):
     upload = self.upload('strict-compat')
     d = parse_addon(upload.path)
     f = File.from_upload(upload, self.version, self.platform, parse_data=d)
     eq_(f.strict_compatibility, True)
예제 #41
0
 def test_extension_zip(self):
     upload = self.upload('recurse.zip')
     file_ = File.from_upload(upload, self.version, self.platform)
     assert file_.filename.endswith('.xpi')
     assert not file_.is_webextension
     storage.delete(upload.path)
예제 #42
0
 def test_file_hash(self):
     upload = self.upload('jetpack')
     f = File.from_upload(upload, self.version, self.platform)
     assert f.hash.startswith('sha256:')
     assert len(f.hash) == 64 + 7  # 64 for hash, 7 for 'sha256:'
예제 #43
0
 def test_size_small(self):
     upload = self.upload('alt-rdf')
     f = File.from_upload(upload, self.version, self.platform)
     eq_(f.size, 675)
예제 #44
0
 def test_no_restart_dictionary(self):
     upload = self.upload('dictionary-explicit-type-test')
     d = parse_addon(upload.path)
     f = File.from_upload(upload, self.version, self.platform, parse_data=d)
     assert f.no_restart
예제 #45
0
 def test_utf8(self):
     upload = self.upload(u'jétpack')
     self.version.addon.name = u'jéts!'
     f = File.from_upload(upload, self.version, self.platform)
     eq_(f.filename, u'jets-0.1-mac.xpi')
예제 #46
0
 def test_utf8(self):
     upload = self.upload(u'jétpack')
     self.version.addon.name = u'jéts!'
     f = File.from_upload(upload, self.version, self.platform)
     assert f.filename == u'jets-0.1-mac.xpi'
예제 #47
0
 def test_size_small(self):
     upload = self.upload('alt-rdf')
     f = File.from_upload(upload, self.version, self.platform)
     assert f.size == 675
예제 #48
0
 def test_theme_extension(self):
     upload = self.upload('theme.jar')
     f = File.from_upload(upload, self.version, self.platform)
     eq_(f.filename.endswith('.xpi'), True)
예제 #49
0
 def test_no_restart_true(self):
     upload = self.upload('jetpack')
     d = parse_addon(upload.path)
     f = File.from_upload(upload, self.version, self.platform, parse_data=d)
     assert f.no_restart
예제 #50
0
    def from_upload(cls, upload, addon, selected_apps, channel,
                    parsed_data=None):
        """
        Create a Version instance and corresponding File(s) from a
        FileUpload, an Addon, a list of compatible app ids, a channel id and
        the parsed_data generated by parse_addon().

        Note that it's the caller's responsability to ensure the file is valid.
        We can't check for that here because an admin may have overridden the
        validation results.
        """
        assert parsed_data is not None

        from olympia.addons.models import AddonFeatureCompatibility

        if addon.status == amo.STATUS_DISABLED:
            raise VersionCreateError(
                'Addon is Mozilla Disabled; no new versions are allowed.')

        license_id = None
        if channel == amo.RELEASE_CHANNEL_LISTED:
            previous_version = addon.find_latest_version(
                channel=channel, exclude=())
            if previous_version and previous_version.license_id:
                license_id = previous_version.license_id
        approvalnotes = None
        if parsed_data.get('is_mozilla_signed_extension'):
            approvalnotes = (u'This version has been signed with '
                             u'Mozilla internal certificate.')
        version = cls.objects.create(
            addon=addon,
            approvalnotes=approvalnotes,
            version=parsed_data['version'],
            license_id=license_id,
            channel=channel,
        )
        log.info(
            'New version: %r (%s) from %r' % (version, version.id, upload))
        activity.log_create(amo.LOG.ADD_VERSION, version, addon)
        # Update the add-on e10s compatibility since we're creating a new
        # version that may change that.
        e10s_compatibility = parsed_data.get('e10s_compatibility')
        if e10s_compatibility is not None:
            feature_compatibility = (
                AddonFeatureCompatibility.objects.get_or_create(addon=addon)[0]
            )
            feature_compatibility.update(e10s=e10s_compatibility)

        compatible_apps = {}
        for app in parsed_data.get('apps', []):
            if app.id not in selected_apps:
                # If the user chose to explicitly deselect Firefox for Android
                # we're not creating the respective `ApplicationsVersions`
                # which will have this add-on then be listed only for
                # Firefox specifically.
                continue

            compatible_apps[app.appdata] = ApplicationsVersions(
                version=version, min=app.min, max=app.max, application=app.id)
            compatible_apps[app.appdata].save()

        # See #2828: sometimes when we generate the filename(s) below, in
        # File.from_upload(), cache-machine is confused and has trouble
        # fetching the ApplicationsVersions that were just created. To work
        # around this we pre-generate version.compatible_apps and avoid the
        # queries completely.
        version._compatible_apps = compatible_apps

        # For backwards compatibility. We removed specific platform
        # support during submission but we don't handle it any different
        # beyond that yet. That means, we're going to simply set it
        # to `PLATFORM_ALL` and also have the backend create separate
        # files for each platform. Cleaning that up is another step.
        # Given the timing on this, we don't care about updates to legacy
        # add-ons as well.
        # Create relevant file and update the all_files cached property on the
        # Version, because we might need it afterwards.
        version.all_files = [File.from_upload(
            upload=upload, version=version, platform=amo.PLATFORM_ALL.id,
            parsed_data=parsed_data
        )]

        version.inherit_nomination(from_statuses=[amo.STATUS_AWAITING_REVIEW])
        version.disable_old_files()
        # After the upload has been copied to all platforms, remove the upload.
        storage.delete(upload.path)
        version_uploaded.send(sender=version)

        if waffle.switch_is_active('enable-uploads-commit-to-git-storage'):
            # Extract into git repository
            AddonGitRepository.extract_and_commit_from_file_obj(
                file_obj=version.all_files[0],
                channel=channel,
                author=upload.user)

        # Generate a preview and icon for listed static themes
        if (addon.type == amo.ADDON_STATICTHEME and
                channel == amo.RELEASE_CHANNEL_LISTED):
            theme_data = parsed_data.get('theme', {})
            generate_static_theme_preview(theme_data, version.pk)

        # Track the time it took from first upload through validation
        # (and whatever else) until a version was created.
        upload_start = utc_millesecs_from_epoch(upload.created)
        now = datetime.datetime.now()
        now_ts = utc_millesecs_from_epoch(now)
        upload_time = now_ts - upload_start

        log.info('Time for version {version} creation from upload: {delta}; '
                 'created={created}; now={now}'
                 .format(delta=upload_time, version=version,
                         created=upload.created, now=now))
        statsd.timing('devhub.version_created_from_upload', upload_time)

        return version
예제 #51
0
 def test_no_restart_false(self):
     upload = self.upload('extension')
     d = parse_addon(upload.path)
     f = File.from_upload(upload, self.version, self.platform, parse_data=d)
     assert not f.no_restart
예제 #52
0
 def test_size_small(self):
     upload = self.upload('alt-rdf')
     f = File.from_upload(upload, self.version, self.platform)
     assert f.size == 675
예제 #53
0
 def test_size(self):
     upload = self.upload('extension')
     f = File.from_upload(upload, self.version, self.platform)
     assert f.size == 2264
예제 #54
0
    def from_upload(cls, upload, addon, platforms, channel,
                    source=None, parsed_data=None):
        """
        Create a Version instance and corresponding File(s) from a
        FileUpload, an Addon, a list of platform ids, a channel id and the
        parsed_data generated by parse_addon().

        Note that it's the caller's responsability to ensure the file is valid.
        We can't check for that here because an admin may have overridden the
        validation results.
        """
        assert parsed_data is not None

        from olympia.addons.models import AddonFeatureCompatibility

        if addon.status == amo.STATUS_DISABLED:
            raise VersionCreateError(
                'Addon is Mozilla Disabled; no new versions are allowed.')

        license_id = None
        if channel == amo.RELEASE_CHANNEL_LISTED:
            previous_version = addon.find_latest_version(
                channel=channel, exclude=())
            if previous_version and previous_version.license_id:
                license_id = previous_version.license_id
        version = cls.objects.create(
            addon=addon,
            version=parsed_data['version'],
            license_id=license_id,
            source=source,
            channel=channel,
        )
        log.info(
            'New version: %r (%s) from %r' % (version, version.id, upload))
        activity.log_create(amo.LOG.ADD_VERSION, version, addon)
        # Update the add-on e10s compatibility since we're creating a new
        # version that may change that.
        e10s_compatibility = parsed_data.get('e10s_compatibility')
        if e10s_compatibility is not None:
            feature_compatibility = (
                AddonFeatureCompatibility.objects.get_or_create(addon=addon)[0]
            )
            feature_compatibility.update(e10s=e10s_compatibility)

        compatible_apps = {}
        for app in parsed_data.get('apps', []):
            compatible_apps[app.appdata] = ApplicationsVersions(
                version=version, min=app.min, max=app.max, application=app.id)
            compatible_apps[app.appdata].save()

        # See #2828: sometimes when we generate the filename(s) below, in
        # File.from_upload(), cache-machine is confused and has trouble
        # fetching the ApplicationsVersions that were just created. To work
        # around this we pre-generate version.compatible_apps and avoid the
        # queries completely.
        version._compatible_apps = compatible_apps

        if addon.type in [amo.ADDON_SEARCH, amo.ADDON_STATICTHEME]:
            # Search extensions and static themes are always for all platforms.
            platforms = [amo.PLATFORM_ALL.id]
        else:
            platforms = cls._make_safe_platform_files(platforms)

        # Create as many files as we have platforms. Update the all_files
        # cached property on the Version while we're at it, because we might
        # need it afterwards.
        version.all_files = [
            File.from_upload(
                upload, version, platform, parsed_data=parsed_data)
            for platform in platforms]

        version.inherit_nomination(from_statuses=[amo.STATUS_AWAITING_REVIEW])
        version.disable_old_files()
        # After the upload has been copied to all platforms, remove the upload.
        storage.delete(upload.path)
        version_uploaded.send(sender=version)

        # Generate a preview and icon for listed static themes
        if (addon.type == amo.ADDON_STATICTHEME and
                channel == amo.RELEASE_CHANNEL_LISTED):
            dst_root = os.path.join(user_media_path('addons'), str(addon.id))
            theme_data = parsed_data.get('theme', {})
            version_root = os.path.join(dst_root, unicode(version.id))

            utils.extract_header_img(
                version.all_files[0].file_path, theme_data, version_root)
            preview = VersionPreview.objects.create(version=version)
            generate_static_theme_preview(
                theme_data, version_root, preview.pk)

        # Track the time it took from first upload through validation
        # (and whatever else) until a version was created.
        upload_start = utc_millesecs_from_epoch(upload.created)
        now = datetime.datetime.now()
        now_ts = utc_millesecs_from_epoch(now)
        upload_time = now_ts - upload_start

        log.info('Time for version {version} creation from upload: {delta}; '
                 'created={created}; now={now}'
                 .format(delta=upload_time, version=version,
                         created=upload.created, now=now))
        statsd.timing('devhub.version_created_from_upload', upload_time)

        return version
예제 #55
0
 def test_no_restart_false(self):
     upload = self.upload('extension')
     d = parse_addon(upload.path)
     f = File.from_upload(upload, self.version, self.platform, parse_data=d)
     assert not f.no_restart
예제 #56
0
 def test_jetpack_with_invalid_json(self):
     upload = self.upload('jetpack_invalid')
     f = File.from_upload(upload, self.version, self.platform)
     file_ = File.objects.get(id=f.id)
     assert file_.jetpack_version is None
예제 #57
0
 def test_no_restart_true(self):
     upload = self.upload('jetpack')
     d = parse_addon(upload.path)
     f = File.from_upload(upload, self.version, self.platform, parse_data=d)
     assert f.no_restart
예제 #58
0
    def from_upload(cls, upload, addon, platforms, channel, send_signal=True,
                    source=None, is_beta=False, parsed_data=None):
        from olympia.addons.models import AddonFeatureCompatibility

        if addon.status == amo.STATUS_DISABLED:
            raise VersionCreateError(
                'Addon is Mozilla Disabled; no new versions are allowed.')

        if parsed_data is None:
            parsed_data = utils.parse_addon(upload, addon)
        license_id = None
        if channel == amo.RELEASE_CHANNEL_LISTED:
            previous_version = addon.find_latest_version(
                channel=channel, exclude=())
            if previous_version and previous_version.license_id:
                license_id = previous_version.license_id
        version = cls.objects.create(
            addon=addon,
            version=parsed_data['version'],
            license_id=license_id,
            source=source,
            channel=channel,
        )
        log.info(
            'New version: %r (%s) from %r' % (version, version.id, upload))
        activity.log_create(amo.LOG.ADD_VERSION, version, addon)
        # Update the add-on e10s compatibility since we're creating a new
        # version that may change that.
        e10s_compatibility = parsed_data.get('e10s_compatibility')
        if e10s_compatibility is not None:
            feature_compatibility = (
                AddonFeatureCompatibility.objects.get_or_create(addon=addon)[0]
            )
            feature_compatibility.update(e10s=e10s_compatibility)

        compatible_apps = {}
        for app in parsed_data.get('apps', []):
            compatible_apps[app.appdata] = ApplicationsVersions(
                version=version, min=app.min, max=app.max, application=app.id)
            compatible_apps[app.appdata].save()

        # See #2828: sometimes when we generate the filename(s) below, in
        # File.from_upload(), cache-machine is confused and has trouble
        # fetching the ApplicationsVersions that were just created. To work
        # around this we pre-generate version.compatible_apps and avoid the
        # queries completely.
        version._compatible_apps = compatible_apps

        if addon.type in [amo.ADDON_SEARCH, amo.ADDON_STATICTHEME]:
            # Search extensions and static themes are always for all platforms.
            platforms = [amo.PLATFORM_ALL.id]
        else:
            platforms = cls._make_safe_platform_files(platforms)

        for platform in platforms:
            File.from_upload(upload, version, platform,
                             parsed_data=parsed_data, is_beta=is_beta)

        version.inherit_nomination(from_statuses=[amo.STATUS_AWAITING_REVIEW])
        version.disable_old_files()
        # After the upload has been copied to all platforms, remove the upload.
        storage.delete(upload.path)
        if send_signal:
            version_uploaded.send(sender=version)

        # Track the time it took from first upload through validation
        # (and whatever else) until a version was created.
        upload_start = utc_millesecs_from_epoch(upload.created)
        now = datetime.datetime.now()
        now_ts = utc_millesecs_from_epoch(now)
        upload_time = now_ts - upload_start

        log.info('Time for version {version} creation from upload: {delta}; '
                 'created={created}; now={now}'
                 .format(delta=upload_time, version=version,
                         created=upload.created, now=now))
        statsd.timing('devhub.version_created_from_upload', upload_time)

        return version
예제 #59
0
    def from_upload(cls, upload, addon, platforms, channel, send_signal=True,
                    source=None, is_beta=False):
        from olympia.addons.models import AddonFeatureCompatibility

        data = utils.parse_addon(upload, addon)
        try:
            license = addon.versions.latest().license_id
        except Version.DoesNotExist:
            license = None
        version = cls.objects.create(
            addon=addon,
            version=data['version'],
            license_id=license,
            source=source,
            channel=channel,
        )
        log.info(
            'New version: %r (%s) from %r' % (version, version.id, upload))

        # Update the add-on e10s compatibility since we're creating a new
        # version that may change that.
        e10s_compatibility = data.get('e10s_compatibility')
        if e10s_compatibility is not None:
            feature_compatibility = (
                AddonFeatureCompatibility.objects.get_or_create(addon=addon)[0]
            )
            feature_compatibility.update(e10s=e10s_compatibility)

        compatible_apps = {}
        for app in data.get('apps', []):
            compatible_apps[app.appdata] = ApplicationsVersions(
                version=version, min=app.min, max=app.max, application=app.id)
            compatible_apps[app.appdata].save()

        # See #2828: sometimes when we generate the filename(s) below, in
        # File.from_upload(), cache-machine is confused and has trouble
        # fetching the ApplicationsVersions that were just created. To work
        # around this we pre-generate version.compatible_apps and avoid the
        # queries completely.
        version.compatible_apps = compatible_apps

        if addon.type == amo.ADDON_SEARCH:
            # Search extensions are always for all platforms.
            platforms = [amo.PLATFORM_ALL.id]
        else:
            platforms = cls._make_safe_platform_files(platforms)

        for platform in platforms:
            File.from_upload(upload, version, platform, parse_data=data,
                             is_beta=is_beta)

        version.disable_old_files()
        # After the upload has been copied to all platforms, remove the upload.
        storage.delete(upload.path)
        if send_signal:
            version_uploaded.send(sender=version)

        # Track the time it took from first upload through validation
        # (and whatever else) until a version was created.
        upload_start = utc_millesecs_from_epoch(upload.created)
        now = datetime.datetime.now()
        now_ts = utc_millesecs_from_epoch(now)
        upload_time = now_ts - upload_start

        log.info('Time for version {version} creation from upload: {delta}; '
                 'created={created}; now={now}'
                 .format(delta=upload_time, version=version,
                         created=upload.created, now=now))
        statsd.timing('devhub.version_created_from_upload', upload_time)

        return version
예제 #60
0
 def test_no_restart_dictionary(self):
     upload = self.upload('dictionary-explicit-type-test')
     d = parse_addon(upload.path)
     f = File.from_upload(upload, self.version, self.platform, parse_data=d)
     assert f.no_restart