-
Notifications
You must be signed in to change notification settings - Fork 16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support 'metadata update' in 'sign_metadata' task #355
Support 'metadata update' in 'sign_metadata' task #355
Conversation
msg = f"Root v{root.signed.version} is pending signatures" | ||
return self._sign_result(msg, True) | ||
|
||
# TODO: Refactor `_root_metadata_update` to de-duplicate validation |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me know, if I should refactor _root_metadata_update
in this PR or in a follow-up. I think it's correct as is, but performs some unnecessary cycles.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You made a minimalist change in this PR, great work.
So, we can accommodate the refactoring here, but it is up to you.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest to do it in a follow-up. :) I can ticketize.
3a9c034
to
84fd2a8
Compare
Need to fix tests (some expected messages changed) and add new ones for the new feature. Otherwise this should be good for review. |
84fd2a8
to
b697505
Compare
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #355 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 14 14
Lines 960 999 +39
=========================================
+ Hits 960 999 +39
☔ View full report in Codecov by Sentry. |
b96010e
to
5b93aa6
Compare
This is ready for another round of review. I adopted updates in existing tests and added new tests for:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I saw that ROLE_SIGNING
is used in many places where we have documented SIGNING_ROLE
: https://repository-service-tuf.readthedocs.io/en/latest/devel/design.html#rstuf-repository-settings-configuration
Do we want to update the docs or update our code?
@lukpueh @kairoaraujo
Also, what should we do with the payload['role']
argument?
If we decide to keep it we should make the code less opinionated about the role and not mention root
everywhere (with the exception of the bootstrap case of course).
I think there is a possibility we would introduce offline key(s) support for the Targets
role in the future considering we want to give more flexibility on how users want to delegate trust, so I am in favor of keeping this argument for now.
@@ -3311,3 +3267,124 @@ def fake_get_fresh(key): | |||
assert test_repo.write_repository_settings.calls == [ | |||
pretend.call("ROOT_SIGNING", "fake_metadata") | |||
] | |||
|
|||
@pytest.mark.parametrize( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We had a discussion with @kairoaraujo and @KAUTH regarding table testing done in this way and we are all with mixed feelings.
The biggest issues I see with them:
- it's harder to debug which test case fails when you see only
test_sign_metadata__update
with a specific assert that not always point you to the correct use case. - it could be a little complicated for new users.
What do you think @kairoaraujo and @KAUTH ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We had a discussion with @kairoaraujo and @KAUTH regarding table testing done in this way and we are all with mixed feelings. The biggest issues I see with them:
Yeah, I figured. That's why I used the copy/paste approach for the first two main test cases, and only refactored on top of that. (Note though that the table does have a few extra test cases).
- it's harder to debug which test case fails when you see only
test_sign_metadata__update
with a specific assert that not always point you to the correct use case.
That's not necessarily true, if you look at the log you'll see that each call of the method is listed as separat test.
- it could be a little complicated for new users.
🤷 I guess, that's debatable. If we expect devs to get familiar with pytest, monkey patching, pretend, mock, etc., we might as well ask them to read pytest docs for parametrize
. I personally find thousands of lines of testing code more intimidating. But yeah I agree with the intention of making testing code as little engineered as possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to decide about this test and methodology @kairoaraujo and @lukpueh.
Do we want to meet or discuss it here?
PS: Now when I read it it's not immediately clear to me which test case is testing what.
Maybe if there was some kind of a title of a test case would be better.
Something familiar to what we did in python-tuf here: https://github.com/theupdateframework/python-tuf/blob/038ecd65dc6b74e9d0306d3c57c3e7d621155e47/tests/test_updater_key_rotations.py#L92
Thanks for the review, @MVrachev! I'll push a commit for some of your inline suggestions. Regarding the things below:
Let's fix this in the docs, as agreed on slack.
See #355 (comment) for my feelings about Regarding making the function more generic, as you suggest inline, e.g. using |
Fixed by repository-service-tuf/repository-service-tuf#433.
I don't insist on making this change now, but given we already accept The final question is about the testing. I think that this is a question that's debatable and we have different opinions. |
You're right. Let's not decide, if the argument makes sense or not, but just accept its existence. Let me add a check and a test.
Sounds good. I think we can merge the tests in their table form and open an issue where we discuss this. If the team is opposed, it will be easy to just copy and paste this function |
297267f
to
693b4a7
Compare
693b4a7
to
0c8c26f
Compare
I rebased and force pushed this branch to resolve some conflicts, and also added another commit to re-use parts of the "metadata update" task (see last commit). For this feature to be useful / usable, we still need to change the "metadata update" task, to accept partially signed metadata. I have another commit for that which comes at ~ Should I push it here, or open a separate PR? |
@lukpueh |
@kairoaraujo, @MVrachev: This is ready for another round of review. The most recent commit patches The big caveat is that |
I will review the code today, but for testing, it's good if we have repository-service-tuf/repository-service-tuf#515. |
I'm running the tests to check if it doesn't affect the fully signed metadata update. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The main code is clean, elegant and easy to read.
Left a couple of small nits about it.
We still need to discuss:
- do we want to merge this without FT tests?
- what to do with the
parametrize
test?
@@ -3311,3 +3267,124 @@ def fake_get_fresh(key): | |||
assert test_repo.write_repository_settings.calls == [ | |||
pretend.call("ROOT_SIGNING", "fake_metadata") | |||
] | |||
|
|||
@pytest.mark.parametrize( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to decide about this test and methodology @kairoaraujo and @lukpueh.
Do we want to meet or discuss it here?
PS: Now when I read it it's not immediately clear to me which test case is testing what.
Maybe if there was some kind of a title of a test case would be better.
Something familiar to what we did in python-tuf here: https://github.com/theupdateframework/python-tuf/blob/038ecd65dc6b74e9d0306d3c57c3e7d621155e47/tests/test_updater_key_rotations.py#L92
Let me know what it should be. If you are fine with my approach, I can add titles as Martin suggests. Otherwise, I can copy paste. Note that the difference is ~140 vs. ~450 LOC |
I'm fine with using I had an experience (with I noticed that the difficulties with tests, in general, can scare contributors with short experience in the language. That is why I choose to implement tests in very descriptive steps/repetitions, where whoever touches the code can read and follow the steps side-by-side. |
In a meeting with @kairoaraujo and our new contributors @enyinna1234 and @ivanayov they read the Maybe it's because we have to use a lot of mocking when we test Either way, for this particular case it seems it's better to use the more descriptive approach with code duplications. |
Okay, that's a good argument. One final note, though ... and then I'll gladly change it. Is it really the usage of As I said before, I think that nested functions, "fake" variables, Would code documentation like this clarify the use of
I am very much open for renames and restructures of these variables, e.g. |
Implement support for distributed asynchronous root metadata signing in the course of a "metadata update" event. Other than the already supported "bootstrap" signing event, signatures added to root during "metadata update" must validate with keys from trusted OR new root, and meet the signature threshold of trusted AND new root. *Related changes:* - Ignore obsolete "rolename" in sign_metadata payload. We only support root, and check the type when loading "ROOT_SIGNING". - Refactor `_validate_{signature, threshold}` helpers to accept an optional delegator (e.g. trusted root). - Add local `_result` helper to return a "sign metadata"-specific task result. Signed-off-by: Lukas Puehringer <[email protected]>
- remove obsolete "test_sign_metadata_root_signing_no_bootstrap" Now, if there is no ongoing "bootstrap", we just assume there is an ongoing "metadata update". - adopt changes in "test_sign_metadata_invalid_role_type" - new expected error message - fail earlier, before consulting with "BOOTSTRAP" state variable Signed-off-by: Lukas Puehringer <[email protected]>
Signed-off-by: Lukas Puehringer <[email protected]>
Signed-off-by: Lukas Puehringer <[email protected]>
Signed-off-by: Lukas Puehringer <[email protected]>
Combine existing sign_metadata/update metadata tests in a single parametrized test method and add additional test cases for different return values from internal `_validate_{signature, threshold}` calls, in order to test correct sage of OR/AND operators: - signature must be valid according to trusted OR new root - threshold must be met according to trusted AND new root Note: The test removes asserts for internal method calls, which don't seem so interesting, as long as we get the expected result. Signed-off-by: Lukas Puehringer <[email protected]>
- Use elif instead of elsewhere appropriate - Remove blank line in docstrings - Clarify comment about signature/threshold validation Co-authored-by: Martin Vrachev <[email protected]> Signed-off-by: Lukas Puehringer <[email protected]>
This is a temporary measure until after _root_metadata_update has been refactored (see code comment) to not fail silently e.g in in tests that mock the argument passed to _root_metadata_update. This commit also updates the related tests to now mock the `_root_metadata_update` result too. As the wrong result would no longer fail silently. Signed-off-by: Lukas Puehringer <[email protected]>
'sign_metadata' only supports root, thus the role in the payload is not relevant, and was ignored previously. For consistency, this commit adds a check that the role is indeed root and fails otherwise. This is also tested by adding another column to the test table of test_sign_metadata__update, used to patch the default payload in test runs. Signed-off-by: Lukas Puehringer <[email protected]>
Factor out "finalize" part of _root_metadata_update to re-use in sign_metadata. Prior to this commit, sign_metadata would call _root_metadata_update duplicating much of the verification behavior, although it only cared for the finalization part. Now, it can call into the desired subroutine only. Signed-off-by: Lukas Puehringer <[email protected]>
Update `_root_metadata_update` subroutine of `metadata_update` task interface to accept partially signed metadata. If the required threshold is not met, the passed metadata is written to the "ROOT_SIGNING" repository setting and the task returns with a "pending signatures" message Missing signatures can then be added using the `sign_metadata` task interface, which also finalizes the metadata update, as soon as the threshold is reached. NOTE: Currently, there is no sanity check of signatures below the threshold. A useful check might be, that passed metadata has at least 1 initial and only valid signatures, akin to bootstrap. repository-service-tuf#367 will make this a lot easier. This change also includes a reordering of the validation routine to check the version increment prior to signature threshold. Otherwise, a bad version would only be detected after all signatures have been added. Signed-off-by: Lukas Puehringer <[email protected]>
Signed-off-by: Lukas Puehringer <[email protected]>
Use test copy pasta instead of @parametrize. Signed-off-by: Lukas Puehringer <[email protected]>
28113ee
to
f855560
Compare
Force pushed w/o change to rebase on
|
fake_signature_result = iter((False, True)) | ||
fake_threshold_result = iter((False, True)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting way of mocking return type.
I like it better than compared to using MagicMock
as we did until now when we have to mock a different behavior of two calls.
Signed-off-by: Lukas Puehringer <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reapprove to run functional tests with pr repository-service-tuf/repository-service-tuf#515
related to #336
Implement support for distributed asynchronous root metadata signging in the course of a "metadata update" event.
Other than the already supported "bootstrap" signing event, signatures added to root during "metadata update" must validate with keys from trusted OR new root, and meet the signature threshold of trusted AND new root.
Related changes:
_validate_{signature, threshold}
helpers to accept an optional delegator (e.g. trusted root)._sign_result
helper to return a "sign metadata"-specific task result.