Merge ~robru/britney/+git/britney2-ubuntu:source_ppa into ~ubuntu-release/britney/+git/britney2-ubuntu:master
| Status: | Merged |
|---|---|
| Merge reported by: | Martin Pitt |
| Merged at revision: | not available |
| Proposed branch: | ~robru/britney/+git/britney2-ubuntu:source_ppa |
| Merge into: | ~ubuntu-release/britney/+git/britney2-ubuntu:master |
| Diff against target: |
373 lines (+303/-2) 5 files modified
britney.py (+4/-2) policies/sourceppa.py (+115/-0) tests/data/sourceppa.json (+7/-0) tests/test_autopkgtest.py (+43/-0) tests/test_sourceppa.py (+134/-0) |
| Related bugs: |
| Reviewer | Review Type | Date Requested | Status |
|---|---|---|---|
| Martin Pitt (community) | 2016-10-08 | Approve on 2016-11-17 | |
| Robert Bruce Park (community) | Approve on 2016-11-15 | ||
| Steve Langasek | 2016-11-08 | Pending | |
|
Review via email:
|
|||
Description of the Change
Teach Britney to reject packages if other packages copied from same PPA are also rejected.
| Robert Bruce Park (robru) wrote : | # |
| Martin Pitt (pitti) wrote : | # |
The general direction looks good indeed, thanks for working on this!
| Martin Pitt (pitti) wrote : | # |
... oh, and you of course need to actually instantiate the new policy in britney.py.
| Robert Bruce Park (robru) wrote : | # |
(some discussion inline; everything I didn't reply to I agree with & will fix)
| Martin Pitt (pitti) wrote : | # |
Likewise, some remaining remarks; I didn't follow up to parts which I consider resolved/agree with.
I just rebased our britney2 to current Debian master (this also includes your speedup patch); please rebase your branch against current britney2-ubuntu.
| Robert Bruce Park (robru) wrote : | # |
Ok, I beleive I've addressed all the issues you've identified, will begin writing those tests shortly. let me know if I've overlooked anything, thanks.
| Martin Pitt (pitti) wrote : | # |
A few minor niggles left, but this looks great now! (still leaving as needs fixing for the tests).
| Robert Bruce Park (robru) wrote : | # |
Ok I've written some tests, please re-review.
One thought occurred to me: When I had to rebase the work, there was a merge conflict in the imports in policy.py (apparently they changed upstream). That was why I had them grouped and separated from the other imports, to hopefully avoid merge conflicts there, but it happened anyway. I wonder if it might make more sense to move the SourcePPAPolicy definition into a new file? This would avoid potential future merge conflicts if upstream touches the imports in policy.py
| Martin Pitt (pitti) wrote : | # |
I like the new test cases in general, to test the finer details of the new policy. But this mocks Britney, Options, and even Excuse, so they don't prove that the overall plumbing of this is correct. Can you please add an integration test case to test_autopkgtest.py which checks this end-to-end?
Thanks!
> move the SourcePPAPolicy definition into a new file?
If you wish, sure.
| Robert Bruce Park (robru) wrote : | # |
Ok, I'll move sourceppapolicy into a new file. That'll decrease the likelihood of merge conflicts on future upstream rebases, which'll make your life easier.
Can you point me at an example of an existing test that integrates britney as you describe? I'm not sure what that would look like really.
| Martin Pitt (pitti) wrote : | # |
All of tests/test_
For that you need to be able to mock the Launchpad calls: I suggest setting $LAUNCHPAD_URL to file://<temp dir> so that you can provide defined mock replies.
- 618a76b... by Martin Pitt on 2016-10-20
- 1d68151... by Martin Pitt on 2016-10-20
- e1b2027... by Martin Pitt on 2016-10-20
- 212c91e... by Niels Thykier on 2016-10-22
- c341f37... by Martin Pitt on 2016-10-24
- fd3e644... by Martin Pitt on 2016-10-26
| Robert Bruce Park (robru) wrote : | # |
Ok, rebase was easy enough, just working on tests still.
- 8aea1a8... by Martin Pitt on 2016-10-27
| Robert Bruce Park (robru) wrote : | # |
Ok Martin, we are really close here! Please re-review. There's just one small problem and I need your help getting past this. When apply_policy_impl gets called with the excuse object, the excuse object given to it has not actually had its is_valid property set yet, and it defaults to false. This means that, with the existing policy architecture, it is only possible to detect other invalid excuses, it's not possible to detect if my own excuse is already invalid.
So, eg, this case is working:
source ppa has 3 packages, and the second one is rejected, so when the third one is called it notices and rejects all excuses from that ppa
But this case isn't working:
source ppa has 2 packages, and the second one is rejected, so at neither invocation of apply_policy_impl am I able to detect that anything is rejected and thus reject the whole ppa.
To make matters worse, excuses class defaults to is_valid=False, which means when I inspect excuses object it's *always* false, because the logic for deciding what to set it to doesn't happen until after all the policies run. So I need a way to reach back up into britney.py and say "hey what were the results of the previous policy runs" and that just isn't in there. :-/
| Robert Bruce Park (robru) wrote : | # |
(if you run the tests currently, there's one failure, because of this issue)
| Martin Pitt (pitti) wrote : | # |
> When apply_policy_impl gets called with the excuse object, the excuse object given to it has not actually had its is_valid property set yet
Right, that only gets set at the end of should_
> So I need a way to reach back up into britney.py and say "hey what were the results of the previous policy runs" and that just isn't in there.
This wouldn't actually be hard to fix -- the policy verdicts are collected in should_
for policy in self.policies:
if suite in policy.
v = policy.
if v > policy_verdict:
if policy_verdict in [PolicyVerdict.
so in theory we could just pass the current policy_verdict to apply_policy(). However, this isn't sufficient: there are reasons why an excuse is invalid beyond policies, like "not being built everywhere" or "uninstallable". These are not (yet?) implemented as Policies, but directly in britney.py.
So I have three ideas:
(1) Move the "if policy_verdict ..." (last line in the code above) into the loop, and pass "update_candidate" to policy.
(2) Drop the local "update_candidate" variable and use excuse.is_valid for it, which would IMHO be simpler and less intrusive. But likewise, please clear with Niels.
(3) In your apply_policy_
If you do (1) or (2), please do this as a separate commit.
| Robert Bruce Park (robru) wrote : | # |
Yeah i don't like 1 or 2 because they are invasive changes to the policy framework that likely won't accepted upstream because there's no benefit to them. And i don't like 3 because it's incomplete.
What about option 4, where instead of implementing this as a policy, i simply bolt it on at the very end, after all excuses are generated? That would simplify the implementation as well, because currently there's a bit of overhead where i have to simultaneously check other packages to invalidate myself, then also once invalidated i have to go back and make sure everything else is invalidated as well, in a two-step process that's a bit clunky.
If instead i could simply iterate over a fully-formed britney.excuses, i could go back to my initial implementation that worked with a single loop a lot more cleanly.
Thoughts?
| Martin Pitt (pitti) wrote : | # |
I would actually go with (2). It makes the code simpler and more useful. Niels accepted several things which are not immediately useful for Debian, but could become so in the future, including the policy reorg patches recently. I'd start with that, show it to him, and carry the patch in our tree until it lands, it shouldn't be that big.
- 1029a4e... by Martin Pitt on 2016-11-01
| Martin Pitt (pitti) wrote : | # |
I implemented (2) in https:/
| Robert Bruce Park (robru) wrote : | # |
With or without that patch, i still prefer this approach, as it vastly simplifies my algorithm to have access to ALL excuse objects fully formed, just having the excuse object for current source isn't good enough and makes extra work to wait for the other excuse objects in the same ppa. Can you please take a look at this one? It's much cleaner.
https:/
| Martin Pitt (pitti) wrote : | # |
How can it be so massively simpler? You still need to iterate through all excuses, and a policy already implicitly gives you that. You even split discover_ppas() and reject_sources(), so I don't see any net simplification actually. It also immediately follows the Policy loop, so there's nothing in between that does additional computation.
With the is_valid patch the approach in this MP should actually work, doesn't it?
I don't like the "bolt-on" MP because it forces any subsequent addition to also not be a policy; for example, at some point we want to do email notifications. These would all need to be bolted on too. Also, the bolt-on MP is very confusing: It looks like a policy by location and class naming, but isn't. It's just a single magical constructor.
| Robert Bruce Park (robru) wrote : | # |
On Nov 3, 2016 12:44 AM, "Martin Pitt" <email address hidden> wrote:
> How can it be so massively simpler? You still need to iterate through all
excuses, and a policy already implicitly gives you that. You even split
discover_ppas() and reject_sources(), so I don't see any net simplification
actually. It also immediately follows the Policy loop, so there's nothing
in between that does additional computation.
The key difference is that apply_policy_impl doesn't give us access to the
complete picture, so the policy implementation must do an awkward two-step
check for any reason to invalidate the current package and then also reach
back and invalidate other packages as well.
>
> With the is_valid patch the approach in this MP should actually work,
doesn't it?
The main problem with this approach is that the excuses dict is not fully
generated when the policy is applied, which makes it awkward because every
application of the policy needs to check all other packages, and then
disable all other packages, after each package is loaded.
The bolted-on approach lets us just scan the completed excuses once,
whereas this approach means we have to check the previously loaded excuses
for each excuse loaded. Just look at apply_policy_impl, it's huge compared
to the bolt on approach.
Also I'm not convinced that your is_valid patch really helped that much at
all. I need to know if the excuse is invalid for any reason, policies or
otherwise. I'll need to review if that's indeed the case, for some reason i
was thinking it was only capturing some invalidations and not all.
>
> I don't like the "bolt-on" MP because it forces any subsequent addition
to also not be a policy; for example, at some point we want to do email
notifications. These would all need to be bolted on too.
Well the source ppa grouping needs to the last one anyway since any other
reason to reject something needs to come first. If there's a feature to
send emails, is that really a policy anyway?
> Also, the bolt-on MP is very confusing: It looks like a policy by
location and class naming, but isn't. It's just a single magical
constructor.
Yes yes of course, this is just bike shedding, my main concern is that the
implementation is a lot simpler if i can access the complete excuses dict
rather than apply_policy_impl which shows only an awkward slice of the
information we need to reject an entire source ppa.
Think of it this way: apply_policy_impl is called within a loop, once for
each package, but contains two loops: one to check other packages for
validity and one to then reject all other packages from same ppa.
The bolt on approach contains just two loops: one to discover the source
ppa and then one to do the rejections. It looks so much cleaner!
| Martin Pitt (pitti) wrote : | # |
Robert Bruce Park [2016-11-03 8:06 -0000]:
> The key difference is that apply_policy_impl doesn't give us access to the
> complete picture, so the policy implementation must do an awkward two-step
> check for any reason to invalidate the current package and then also reach
> back and invalidate other packages as well.
Right, you can only look backwards, not forwards (in Excuses order).
But which way around you process it doesn't make the process
significantly more complicated -- you collect the "friends" mapping as
you go, and as soon as you find any invalid source from a PPA you
invalidate all friends as well. You have to do all these for a full
excuses map as well.
> awkward because every application of the policy needs to check all
> other packages, and then disable all other packages, after each
> package is loaded.
Not all other packages, just the ones from the same PPA, which you
already cache in the "friends" dict. And you can maintain a single
bool per PPA whether it's (so far) good or already invalid. So for
every individual Excuse check (per policy invocation) there should not
be any looping at all?
> Also I'm not convinced that your is_valid patch really helped that much at
> all. I need to know if the excuse is invalid for any reason, policies or
> otherwise. I'll need to review if that's indeed the case, for some reason i
> was thinking it was only capturing some invalidations and not all.
No, it accurately reflects (and in fact *is*) the "is-candidate".
> Well the source ppa grouping needs to the last one anyway since any other
> reason to reject something needs to come first. If there's a feature to
> send emails, is that really a policy anyway?
We can do it as a policy. But e. g. the upcoming boot speed
testing/gating will also run after the PPA policy, as it's really
expensive.
> Think of it this way: apply_policy_impl is called within a loop, once for
> each package, but contains two loops: one to check other packages for
> validity and one to then reject all other packages from same ppa.
It's not necessary to loop for the former, and you have to do the
latter in either case.
| Robert Bruce Park (robru) wrote : | # |
Ok I've rebased this on latest master and fixed up the tests. This approach is fundamentally broken, however. In britney.py:
policy_info = excuse.policy_info
for policy in self.policies:
if suite in policy.
v = policy.
if v > policy_verdict:
if policy_verdict in [PolicyVerdict.
This means that excuse.is_valid does not get set until *after* all policies have run, which means that at the time my policy runs, inspecting excuse.is_valid can only find the default value, which is false, which means my policy has no way of distinguishing between an excuse that has been rejected by a policy and an excuse that has not been rejected by a policy (both cases are False).
I really would strongly prefer to inspect britney.excuses after it is fully formed than try to write a policy that reaches outside the scope of a policy and inspects other excuses before the excuse-building is even complete. It's thus far proving to be quite error prone.
| Martin Pitt (pitti) wrote : | # |
Oh right, that "if policy_verdict" needs to be indented one level.
| Robert Bruce Park (robru) wrote : | # |
Hmm, i suppose that would work. I overlooked the "excuse.is_valid = True" near the beginning of the method and just saw the excuse class definition where it defaults to false for some reason. Will you submit a patch for that then or should i?
| Martin Pitt (pitti) wrote : | # |
Robert Bruce Park [2016-11-14 21:33 -0000]:
> Hmm, i suppose that would work. I overlooked the "excuse.is_valid =
> True" near the beginning of the method and just saw the excuse class
> definition where it defaults to false for some reason. Will you
> submit a patch for that then or should i?
Please apply it locally in your branch for now, so that when we
forward this upstream we know exactly what we actually need.
- 24622f6... by Robert Bruce Park on 2016-11-15
| Robert Bruce Park (robru) wrote : | # |
Ok this is done, integration test written, tests passing, please accept.
| Martin Pitt (pitti) wrote : | # |
I have some stylistic/pyflakes updates and another test for applying this policy to an unbuilt package (to make sure that this works as well); this also adds a HTML explanation of the blocker: http://
However, there is still some weird race condition here:
$ while tests/test_
[...]
=======
FAIL: test_sourceppa (__main__.T)
Packages from same source PPA should be grouped.
-------
Traceback (most recent call last):
File "tests/
self.
AssertionError: {} != {'red': 'team/ubuntu/ppa', 'green': 'team/ubuntu/ppa'}
- {}
+ {'green': 'team/ubuntu/ppa', 'red': 'team/ubuntu/ppa'}
This apparently depends on some python dict randomness, depending on whether red or green get examined first. The friend invalidation also happens multiple times, causing the reasons to be added multiple times (which looks confusing). This also needs fixing still.
I'm still looking into this, but wanted to provide some feedback of the current state. In general this looks really nice now, thanks!
BTW: Not sure if you are aware, but for debugging such issues, this is useful:
SHOW_OUTPUT=1 SHOW_EXCUSES=1 SHOW_HTML=1 tests/test_
| Martin Pitt (pitti) wrote : | # |
I adjusted the preliminary britney policy support fix for upstream, sent it to Niels, and rebased our Ubuntu changes on top:
https:/
The "main" commit here is
https:/
which differs from your's just in some nitpicks and two pyflakes fixes.
I did a followup commit
https:/
with a few fixes, particularly that unstable policy_info bit from above.
I'll land this now, let's see what happens! Thanks!

Ok, this is conceptually complete, I just need to write the tests for it ;-)