Skip to content

[Feature Request]: Support "Warning" for Validation Webhook #1896

Closed

Description

@STRRL

refer: #1788

After Kubernetes 1.19, the webhook could respond with "warning"s, refer: https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#response

Warnings for validation webhook would bring much help for users with interacting with API.

Currently, users of controller-runtime could implement their "customized" admission.Handler to respond with "warnings". But it's might be not easy to use. Also as mentioned in #1788, using util to wrap warning messages might be helpful. IMHO, controller-runtime should support warning "natively" with Validator and CustomValidator.

I am interested in this feature/enhancement, so maybe there are possible designs and steps to build this feature:

  • extend Allowed(), Denied(), ValidationResponse(), make them support warning []string
  • bring an interface to extend error, called ErrorWithWarnings like:
type ErrorWithWarnings interface {
	error
	Warnings() []string
}
  • it means it user want to respond with warning, it should return this interface with ValidateXXX()
  • make type assertion when resolving the error from Validator/CustomValidator, fill Warnings in v1.AdmissionResponse

Some changes might break the existing API, I think it's OK to controller-runtime. If not, I could still try to only introduce new API without breaking the exist one.

Activity

  1. STRRL commented on May 10, 2022

    @STRRL
    ContributorAuthor

    what do you think? If you are not satisfied with this design, I am very happy to make it better. :)

    /cc @FillZpp @vincepri @alvaroaleman

  2. FillZpp commented on May 10, 2022

    @FillZpp
    Member

    @STRRL Thanks. Does the existing WithWarnings function not satisfy the requirement?

    // WithWarnings adds the given warnings to the Response.
    // If any warnings were already given, they will not be overwritten.
    func (r Response) WithWarnings(warnings ...string) Response {
    r.AdmissionResponse.Warnings = append(r.AdmissionResponse.Warnings, warnings...)
    return r
    }

    We can use it with any response like Allowed(), Denied() or ValidationResponse(). For example

        // ...
        return admission.Allowed("").WithWarnings("warning message ...")
  3. STRRL commented on May 10, 2022

    @STRRL
    ContributorAuthor

    Ohh, I missed that method. That would help a lot. We do not require the first step in my original design. ❤️

    But the user still needs to implement Handler instead of Validator and CustomValidator, I think maybe introducing ErrorWithWarnings still makes sense.

  4. FillZpp commented on May 10, 2022

    @FillZpp
    Member

    Emm... I don't really understand. Handler implementation just has to define a func Handle(context.Context, Request) Response and its response could also use the WithWarnings.

  5. STRRL commented on May 10, 2022

    @STRRL
    ContributorAuthor

    I think it's better also support waning on high-level API Validator/CustomValidator based on the provided low-level API Response.WithWarnings. :)

  6. FillZpp commented on May 10, 2022

    @FillZpp
    Member

    Oh, you mean expose the Warnings in the Validator/CustomValidator interfaces? In this way, we should not only expose the Warnings, but the Response with its fields like Warnings, ErrorCode, Reason.

    type Validator interface {
    runtime.Object
    ValidateCreate() error
    ValidateUpdate(old runtime.Object) error
    ValidateDelete() error
    }

    type CustomValidator interface {
    ValidateCreate(ctx context.Context, obj runtime.Object) error
    ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) error
    ValidateDelete(ctx context.Context, obj runtime.Object) error
    }

    That's ok to me. But I'm not sure if the API change is necessary. WDYT @alvaroaleman @vincepri

  7. STRRL commented on May 10, 2022

    @STRRL
    ContributorAuthor

    ErrorCode, Reason could be carried with apierrors.APIStatus:

    err = obj.ValidateCreate()
    if err != nil {
    var apiStatus apierrors.APIStatus
    if goerrors.As(err, &apiStatus) {
    return validationResponseFromStatus(false, apiStatus.Status())
    }
    return Denied(err.Error())
    }

    Warning is not considered right now, so I think maybe we could use a similar way. :)

    One different thing is a user might want only to respond "warnings" without "error". I realized that my original design might not work well with this situation, because it could not resolve the error is nil.

    Maybe make changes on Validator interface (or another interface ValidatorWarning) are required:

     type ValidatorWarning interface { 
     	runtime.Object 
     	ValidateCreate() (err error, warnings []string) 
     	ValidateUpdate(old runtime.Object) (err error, warnings []string)  
     	ValidateDelete() (err error, warnings []string)  
     }
  8. k8s-triage-robot commented on Aug 8, 2022

    @k8s-triage-robot

    The Kubernetes project currently lacks enough contributors to adequately respond to all issues and PRs.

    This bot triages issues and PRs according to the following rules:

    • After 90d of inactivity, lifecycle/stale is applied
    • After 30d of inactivity since lifecycle/stale was applied, lifecycle/rotten is applied
    • After 30d of inactivity since lifecycle/rotten was applied, the issue is closed

    You can:

    • Mark this issue or PR as fresh with /remove-lifecycle stale
    • Mark this issue or PR as rotten with /lifecycle rotten
    • Close this issue or PR with /close
    • Offer to help out with Issue Triage

    Please send feedback to sig-contributor-experience at kubernetes/community.

    /lifecycle stale

  9. added
    lifecycle/staleDenotes an issue or PR has remained open with no activity and has become stale.
    on Aug 8, 2022
  10. invidian commented on Aug 8, 2022

    @invidian

    /remove-lifecycle stale

  11. removed
    lifecycle/staleDenotes an issue or PR has remained open with no activity and has become stale.
    on Aug 8, 2022
  12. STRRL commented on Oct 4, 2022

    @STRRL
    ContributorAuthor

    Hi @invidian , I create a PR for resolve this issue, PTAL ❤️

  13. STRRL commented on Oct 5, 2022

    @STRRL
    ContributorAuthor

    also cc @FillZpp

  14. joelanford commented on Nov 8, 2022

    @joelanford
    Member

    I realized that my original design might not work well with this situation, because it could not resolve the error is nil.

    We could resolve that right? If the error is an ErrorWithWarnings we can use the embedded error as the "real" error, and that one can be nil while warnings is not.

    If the returned error is not an ErrorWithWarnings, then we have the "real" error directly with no warnings.

  15. sbueringer commented on Nov 25, 2022

    @sbueringer
    Member

    @joelanford's suggestion sounds good to me

    I think we have one fundamental choice to make and then everything follows from there:

    Is it oky to change the existing Validator/Defaulter and CustomValidator/CustomDefaulter interfaces or not?

    1. If yes => we can introduce an additional return parameter for warnings
    2. If no => let's go with Joel's suggestion of wrapping errors and warnings in an error

    @alvaroaleman @vincepri Sorry for the ping, I know you're pretty busy. What is your opinion on that?

    P.S. Does 2. come down to the same as #1788?

  16. alvaroaleman commented on Nov 25, 2022

    @alvaroaleman
    Member

    Hm, after skimming the conversation I am actually in favor of breaking the interface over making it hard to use in the context of warnings by having to return a specific error type. It will be a visible break that is easy to fix.

    The only comment I have on the suggestion in #1896 (comment) regarding how that would look like is that errors per golang convention should always be the last return parameter.

  17. sbueringer commented on Nov 26, 2022

    @sbueringer
    Member

    Sounds good to me!

  18. fabriziopandini commented on Nov 28, 2022

    @fabriziopandini
    Member

    I am actually in favor of breaking the interface over making it hard to use in the context of warnings by having to return a specific error type. It will be a visible break that is easy to fix.

    +1

  19. STRRL commented on Dec 5, 2022

    @STRRL
    ContributorAuthor

    I realized that my original design might not work well with this situation, because it could not resolve the error is nil.

    We could resolve that right? If the error is an ErrorWithWarnings we can use the embedded error as the "real" error, and that one can be nil while warnings is not.

    If the returned error is not an ErrorWithWarnings, then we have the "real" error directly with no warnings.

    No. I do not think we could resolve that, because ErrorWithWarnings is an interface(which composes an error interface), not a struct(which embeds an error instance). We could not return a result that carries the "warnings" and also introduces "error is nil" at the same time

  20. STRRL commented on Dec 5, 2022

    @STRRL
    ContributorAuthor

    Hm, after skimming the conversation I am actually in favor of breaking the interface over making it hard to use in the context of warnings by having to return a specific error type. It will be a visible break that is easy to fix.

    The only comment I have on the suggestion in #1896 (comment) regarding how that would look like is that errors per golang convention should always be the last return parameter.

    I would update the PR soon!

  21. k8s-triage-robot commented on Mar 5, 2023

    @k8s-triage-robot

    The Kubernetes project currently lacks enough contributors to adequately respond to all issues.

    This bot triages un-triaged issues according to the following rules:

    • After 90d of inactivity, lifecycle/stale is applied
    • After 30d of inactivity since lifecycle/stale was applied, lifecycle/rotten is applied
    • After 30d of inactivity since lifecycle/rotten was applied, the issue is closed

    You can:

    • Mark this issue as fresh with /remove-lifecycle stale
    • Close this issue with /close
    • Offer to help out with Issue Triage

    Please send feedback to sig-contributor-experience at kubernetes/community.

    /lifecycle stale

  22. added
    lifecycle/staleDenotes an issue or PR has remained open with no activity and has become stale.
    on Mar 5, 2023
  23. invidian commented on Mar 5, 2023

    @invidian

    /remove-lifecycle stale

  24. removed
    lifecycle/staleDenotes an issue or PR has remained open with no activity and has become stale.
    on Mar 5, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions