Repository: fedora-infra/bodhi

Issue:
Bodhi sometimes puts too much text in the Bugzilla Fixed in Version field
bodhi-backend01 emitted an error e-mail yesterday due to putting too much text into the Fixed in Version field in Bugzilla:

```
Message
-------
[2017-04-10 21:20:21][     bodhi   ERROR]
python
Unable to close bug #1246113
```


Process Details
---------------
- host:     bodhi-backend01.phx2.fedoraproject.org
- PID:      64964
- name:     fedmsg-hub
- command:  /usr/bin/python /usr/bin/fedmsg-hub
- msg_id:   

Callstack that lead to the logging statement
--------------------------------------------
```
python
  File "/usr/lib64/python2.7/threading.py", line 777 in __bootstrap
    self.__bootstrap_inner()
  File "/usr/lib64/python2.7/threading.py", line 804 in __bootstrap_inner
    self.run()
  File "/usr/lib/python2.7/site-packages/bodhi/server/consumers/masher.py", line 260 in run
    self.work()
  File "/usr/lib/python2.7/site-packages/bodhi/server/consumers/masher.py", line 351 in work
    self.modify_bugs()
  File "/usr/lib/python2.7/site-packages/bodhi/server/consumers/masher.py", line 57 in wrapper
    retval = method(self, *args, **kwargs)
  File "/usr/lib/python2.7/site-packages/bodhi/server/consumers/masher.py", line 811 in modify_bugs
    update.modify_bugs()
  File "/usr/lib/python2.7/site-packages/bodhi/server/models.py", line 1373 in modify_bugs
    bug.close_bug(self)
  File "/usr/lib/python2.7/site-packages/bodhi/server/models.py", line 2232 in close_bug
    bugs.bugtracker.close(self.bug_id, versions=versions, comment=self.default_message(update))
  File "/usr/lib/python2.7/site-packages/bodhi/server/bugs.py", line 145 in close
    log.exception("Unable to close bug #%d" % bug_id)

Traceback (most recent call last):
  File "/usr/lib/python2.7/site-packages/bodhi/server/bugs.py", line 143, in close
    bug.close('ERRATA', **args)
  File "/usr/lib/python2.7/site-packages/bugzilla/bug.py", line 219, in close
    return self.bugzilla.update_bugs(self.bug_id, vals)
  File "/usr/lib/python2.7/site-packages/bugzilla/base.py", line 1264, in update_bugs
    return self._proxy.Bug.update(tmp)
  File "/usr/lib64/python2.7/xmlrpclib.py", line 1243, in __call__
    return self.__send(self.__name, args)
  File "/usr/lib/python2.7/site-packages/bugzilla/base.py", line 168, in _ServerProxy__request
    ret = ServerProxy._ServerProxy__request(self, methodname, params)
  File "/usr/lib64/python2.7/xmlrpclib.py", line 1602, in __request
    verbose=self.__verbose
  File "/usr/lib/python2.7/site-packages/bugzilla/base.py", line 259, in request
    return self._request_helper(url, request_body)
  File "/usr/lib/python2.7/site-packages/bugzilla/base.py", line 245, in _request_helper
    raise sys.exc_info()[1]
Fault: <Fault 104: 'The text you entered in the Fixed In Version field is too long (259 characters, above the maximum length allowed of 255 characters).'>
```

We should check for this max length, and probably should just leave off some NVRs if we are exceeding the length.


Discussion:
Hey @bowlofeggs ! Should this verification be made in the Masher or in Models? (:
Hi @jwnx,

I think this is probably the right place to fix it, so it will be fixed no matter what part of Bodhi uses it:

https://github.com/fedora-infra/bodhi/blob/2.8.1/bodhi/server/bugs.py#L133-L147
I see. Thanks, @bowlofeggs !

Since you suggested to leave some NVRs, my first thought was to check if the adittion of a new version would break the 255 character mark and leave it off if it would. But there is a statement in the code for how this field should be formatted, so maybe there is a preference for inclusion of some NVRs.

Do you think we need a more speciffic rule or the 'over 255' is sufficient? (:
@jwnx I think just putting as many complete NVRs as we can fit while staying under 256 chars will do.

Resolving patch:
diff --git a/bodhi/server/bugs.py b/bodhi/server/bugs.py
--- a/bodhi/server/bugs.py
+++ b/bodhi/server/bugs.py
@@ -201,15 +201,18 @@ def close(self, bug_id, versions, comment):
                 fixedin = [v.strip() for v in bug.fixed_in.split()]
                 # Strip out any empty strings (already stripped)
                 fixedin = [v for v in fixedin if v]
-                # And add our build if its not already there
-                if version not in fixedin:
-                    fixedin.append(version)
 
                 # There are Red Hat preferences to how this field should be
                 # structured.  We should use:
                 # - the full NVR as it appears in koji
                 # - space-separated if there's more than one.
-                args['fixedin'] = " ".join(fixedin)
+                fixedin_str = " ".join(fixedin)
+
+                # Add our build if its not already there
+                # but only if resultant string length is lower than 256 chars
+                # See https://github.com/fedora-infra/bodhi/issues/1430
+                if (version not in fixedin) and ((len(fixedin_str) + len(version)) < 255):
+                    args['fixedin'] = " ".join([fixedin_str, version]).strip()
 
             bug.close('ERRATA', **args)
         except xmlrpc_client.Fault:


Test patch:
diff --git a/bodhi/tests/server/test_bugs.py b/bodhi/tests/server/test_bugs.py
--- a/bodhi/tests/server/test_bugs.py
+++ b/bodhi/tests/server/test_bugs.py
@@ -97,10 +97,45 @@ def test_close_fault(self, exception):
             410, 'You must log in before using this part of Red Hat Bugzilla.')
 
         # This should not raise an Exception.
-        bz.close(12345, {'bodhi': ['bodhi-3.1.0-1.fc27']}, 'whabam!')
+        bz.close(12345, {'bodhi': 'bodhi-3.1.0-1.fc27'}, 'whabam!')
 
         exception.assert_called_once_with('Unable to close bug #12345')
 
+    @mock.patch('bodhi.server.bugs.log.exception')
+    def test_close_successful(self, exception):
+        """Test the close() method with a success case."""
+        bz = bugs.Bugzilla()
+        bz._bz = mock.MagicMock()
+        bz._bz.getbug.return_value.component = 'bodhi'
+
+        bz.close(12345, {'bodhi': 'bodhi-3.1.0-1.fc27'},
+                 'Fixed. Closing bug and adding version to fixed_in field.')
+
+        bz._bz.getbug.assert_called_once_with(12345)
+        bz._bz.getbug.return_value.close.assert_called_once_with(
+            'ERRATA',
+            comment='Fixed. Closing bug and adding version to fixed_in field.',
+            fixedin='bodhi-3.1.0-1.fc27')
+        self.assertEqual(exception.call_count, 0)
+
+    @mock.patch('bodhi.server.bugs.log.exception')
+    def test_close_fixedin_maxlength(self, exception):
+        """Test the close() method when fixed_in field may go over 255 chars."""
+        bz = bugs.Bugzilla()
+        bz._bz = mock.MagicMock()
+        bz._bz.getbug.return_value.component = 'bodhi'
+        fill_text = ' '.join([u'exactly-10', ] * 23)
+        bz._bz.getbug.return_value.fixed_in = fill_text
+
+        bz.close(12345, {'bodhi': 'bodhi-3.1.0-1.fc27'},
+                 'Closing, but don\'t modify fixed_in field to not cross 255 chars limit.')
+
+        bz._bz.getbug.assert_called_once_with(12345)
+        bz._bz.getbug.return_value.close.assert_called_once_with(
+            'ERRATA',
+            comment='Closing, but don\'t modify fixed_in field to not cross 255 chars limit.')
+        self.assertEqual(exception.call_count, 0)
+
     @mock.patch('bodhi.server.bugs.log.exception')
     def test_comment_successful(self, exception):
         """Test the comment() method with a success case."""