Skip to content

Conversation

@klihub
Copy link
Member

@klihub klihub commented May 14, 2025

This PR is stacked on #169.

This PR is the second in a stack which splits up #163 into multiple PRs, which collectively implement configurable restrictions using the proposed pluggable validation API and a builtin default validator plugin.

This PR defines the validation API and implements the core validation logic for container adjustments.

/cc @chrishenzie

}

func (p *plugin) ValidateContainerAdjustment(ctx context.Context, req *ValidateContainerAdjustmentRequest) error {
if !p.events.IsSet(Event_VALIDATE_CONTAINER_ADJUSTMENT) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Could we make use of isContainerAdjustmentValidator() above since we're doing the same thing?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here, I'd prefer to keep, in that regard, the boilerplate to forward the request to the plugin identical to the other boilerplates. The is*-checker is used in a different context, where it is more readable to hide the test behind a function with a self-explanatory name.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there is a strong feeling against having both, then I think I'd rather get rid of isContainerAdjustmentValidator() and write it out in the other context.

@klihub klihub force-pushed the devel/pluggable-validation/implement-validation branch 4 times, most recently from 8dbb423 to 66e2aa6 Compare May 15, 2025 15:17
Copy link
Member

@samuelkarp samuelkarp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This mostly looks good, just one comment about validator plugin invocation order and also not going to approve this until after #169 is merged.

@klihub klihub force-pushed the devel/pluggable-validation/implement-validation branch from 66e2aa6 to dc7034b Compare May 16, 2025 11:32
@samuelkarp
Copy link
Member

@klihub Can you rebase this branch on main so the two already-merged commits disappear from this PR?


for _, p := range r.validators {
wg.Add(1)
go func(p *plugin) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a note for the future that we might want to revisit this slightly and think about whether we want to limit the concurrency a bit; might become an issue if there are many validator plugins. No need to change as part of this PR.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there any places where we might need to update comments to explain that validation happens in parallel? (Just wondering if customers might try creating plugins with low plugin_idx and wonder why they don't receive priority)

@chrishenzie Yes, I think it will be a good idea to emphasize in the documentation that during the validation phase there is no 'plugin ordering by index' as validating plugins are run in parallel.

But regarding users wondering "why they don't get priority", I think they should not. Any presumed ordering/priority would be relevant only if it was possible to force validation both to fail and to succeed. But our semantics is deliberately such that the latter is not possible.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

true and users of the api may want ordering regardless of our scheme... E.g. I will only approve after ___ approval, if my check is also valid. Allocate claim foo before claim foo'...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typically one would have a set operating in parallel at a level where the higher levels must finish first? built-in, set-a.. set b with a dependency in set-a..

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

E.g. I will only approve after ___ approval, if my check is also valid

The validations are a logical AND, so if any fails the container is rejected. I don't think you can model "I will only approve after $foo approval" since if $foo rejects the container is rejected anyway, and if $foo succeeds then you'd also succeed your validation?

Implement pluggable container adjustment validation. When validator
plugins are present, use them to validate the collected adjustments,
failing container creation if any validation fails.

For adjustment validation plugins receive the pod, the pristing un-
adjusted container, the collected container adjustments, information
about which plugins adjusted what container parameters, and the list
of plugins consulted for the adjustments. The plugin can then choose
to accept or reject the adjustments.

Accepting or rejecting adjustments are transactional. Either all or
none of the adjustments are accepted, together with the container
creation request. IOW, rejecting an adjustment results in a failed
container creation request.

Signed-off-by: Krisztian Litkey <[email protected]>
@klihub klihub force-pushed the devel/pluggable-validation/implement-validation branch from dc7034b to e9f95d0 Compare May 17, 2025 03:56
@klihub
Copy link
Member Author

klihub commented May 18, 2025

True, we did. I updated the PR to run validation in parallel and to not log/imply any particular validation invocation order.

One more related thing I need to check though: is it goroutine-safe to access the same request locally in the builtin plugin and at the same time pass it over ttrpc to an external plugin from another goroutine. My assumption is that protoimpl.{SizeCache,MessageState} are set/used on the receiver side only, but this needs to be verified.

@samuelkarp @chrishenzie Okay, so while adding test cases for the default validator in #171 it became obvious that running validation with multiple plugins and at least one of them being a builtin one (the default validator) is not parallel/goroutine safe. But the reason is not protobuf itself. Rather it is simply that in the current implementation a query/lookup of a field owner of a particular container is (counterintuitively) not a read-only operation if the container has not been adjusted at all. In that case it has no FieldOwners entry at all and that then gets implicitly created during the lookup (with a write to a map). I fixed this in a separate commit in #171 prior to adding the test cases.

Copy link
Member

@mikebrow mikebrow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@samuelkarp samuelkarp merged commit 51adc44 into containerd:main May 19, 2025
9 checks passed
@klihub klihub deleted the devel/pluggable-validation/implement-validation branch May 20, 2025 06:35
@samuelkarp samuelkarp added this to the 1.0 milestone May 23, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants