* Add support for admission webhook warnings.
This extends `apis.FieldError` to support designating certain FieldErrors as "warnings" (or explicitly as "errors", however, this is the default for back-compat).
You can turn an `apis.FieldError` into a warning using: `fe.At(apis.WarningLevel)` or force it into an error using: `fe.At(apis.ErrorLevel)`.
You can get the errors at a particular diagnostic level using: `fe.Filter(apis.WarningLevel)`.
This change also hooks this into the admission webhook infrastructure to support surfacing the "warning" level `apis.FieldError`s via the `Warnings` section of the `AdmissionResponse`.
Fixes: #2497
* Add a comment about the use of defer.
* Add support for callback defaults
Signed-off-by: Pierangelo Di Pilato <pdipilat@redhat.com>
* Put unstr object in ctx and set user info
Signed-off-by: Pierangelo Di Pilato <pdipilat@redhat.com>
* Move get callback at the top
Signed-off-by: Pierangelo Di Pilato <pdipilat@redhat.com>
* Panic when using delete verb
Signed-off-by: Pierangelo Di Pilato <pdipilat@redhat.com>
* Split tests and add callback ctx tests
Signed-off-by: Pierangelo Di Pilato <pdipilat@redhat.com>
* Set user info annotations
Signed-off-by: Pierangelo Di Pilato <pdipilat@redhat.com>
* Register Webhook Rules from callbacks
Signed-off-by: Pierangelo Di Pilato <pdipilat@redhat.com>
* Adapt unstructured objects to apis.HasSpec
Signed-off-by: Pierangelo Di Pilato <pierdipi@redhat.com>
* Change json tag name to match struct field name
Signed-off-by: Pierangelo Di Pilato <pierdipi@redhat.com>
* Add new callback pattern to pkg
* include the context
* typo
* Remove the empty instance of unstructured
* initialize the unstructured var
* Eliminate the unneeded pointer
* Pass a pointer to unstructured callback
* Create a validation specific context struct
* Move callback tests to own unit test case
* Switch from converting to decoding
* Update webhook/resourcesemantics/validation/validation.go
Co-Authored-By: Victor Agababov <vagababov@gmail.com>
* don't wrap context and include params
* split validation files
* include 2020 copyright
* include unit test for WithKubeClient
* Don't bother updating copyright date
* Inclue a unit test for panic
* Move dryRun to context
* Include context dry run unit test
* put the request operation in the context
* eliminate circular dep
* move kubeclient test out of context_test
* dont bother iterating callback map
* Callback takes a list of supported verbs
* Remove extra type
* Ensure Callback interface is public
* Alias Operation into validation
* alias Operation right in Webhook
* Update webhook/resourcesemantics/validation/validation_admit.go
Co-Authored-By: Victor Agababov <vagababov@gmail.com>
* Update webhook/resourcesemantics/validation/validation_admit_test.go
Co-Authored-By: Victor Agababov <vagababov@gmail.com>
* Update webhook/resourcesemantics/validation/validation_admit_test.go
Co-Authored-By: Victor Agababov <vagababov@gmail.com>
* Update webhook/resourcesemantics/validation/validation_admit.go
Co-Authored-By: Victor Agababov <vagababov@gmail.com>
* Update webhook/resourcesemantics/validation/validation_admit.go
Co-Authored-By: Victor Agababov <vagababov@gmail.com>
* Update webhook/resourcesemantics/validation/validation_admit_test.go
Co-Authored-By: Victor Agababov <vagababov@gmail.com>
* correct parens
* minor style fixes
* Rename Callback to Func
* Fix build error
* Switch callback to take a list with a factory
* keep descriptive names
* update comment
* Drop pointer, correct comments
* Add a unit test to disallow duplicate verbs
* fix comments, struct{} for set
* switch to variadic arg for NewCallback
Co-authored-by: Victor Agababov <vagababov@gmail.com>
This PR adds facilities to make it easier to create both components of a Binding
over "Pod Spec"-able resources. Rather than rehashing it all here, please look
at `./pkg/webhook/psbinding/README.md` for more details.
This adds a ToUnstructured method to complement the FromUnstructured method
we have had for some time. The ToUnstructured method is effectively scoped
to Knative-style types since we expect it to implement both kmeta.Accessor
and kmeta.OwnerRefable in order to ensure that the TypeMeta is always
properly populated.
By combining our validation logic into our mutating webhook we were previously allowing for mutating webhooks evaluated after our own to modify our resources into invalid shapes. There are no guarantees around ordering of mutating webhooks (that I could find), so the only way to remedy this properly is to split apart the two into separate webhook configurations:
- `defaulting`: which runs during the mutating admission webhook phase
- `validation`: which runs during the validating admission webhook phase.
The diagram in [this post](https://kubernetes.io/blog/2019/03/21/a-guide-to-kubernetes-admission-controllers/) is very helpful in illustrating the flow of webhooks.
Fixes: https://github.com/knative/pkg/issues/847
* #457 Duck type user annotation logic
* #457 Duck type user annotation logic - tests
* #457 Revert updater annotation key from lastModifier to updater
* #457 Rename HasSpec#GetSpec() to HasSpec#GetUntypedSpec()
* #457 Fix some indentation
* #457 Get group for user info annotations from the request
* #457 Reduce confusuion in webhook testing by using same group
* have simple tests. working on impl.
* strict setting, reflection based.
* ran codegen.
* adding license.
* update based on feedback and merge better.
* getting closer to something simpler assuming shallow reflect.
* adding validation test.
* use the json tag.
* Golang things nil typed pointers are not nil.
* Use real value of reflect invalid.
* add a missing test.
* two methods, one for update, one for single check.
* checkdep is now in apis.
* fix pkg.
* Update apis/deprecated_test.go
Co-Authored-By: n3wscott <32305648+n3wscott@users.noreply.github.com>
* add code clarity.
* include inlined struct objects recursively.
* Update commnets and add a flatten error test for inlined.
This deprecates the `apis.Immutable` and `apis.Annotatable` interfaces,
which were both awkward niche extensions of `apis.Validatable` and
`apis.SetDefaults` for specific contexts that the former set didn't
cover well.
With this change, the expectation is that types that want to check
for immutability will instead access the "baseline" object via the
context from within updates. For example:
```
func (new *Type) Validate(ctx context.Context) *apis.FieldError {
if apis.IsInUpdate(ctx) {
old := apis.GetBaseline(ctx).(*Type)
// Update specific validation based on new and old.
}
}
```
For applying user annotations, the type writer can write:
```
func (new *Type) SetDefaults(ctx context.Context) {
if apis.IsInCreate(ctx) {
ui := apis.GetUserInfo(ctx)
// Set creator annotation from ui
}
if apis.IsInUpdate(ctx) {
ui := apis.GetUserInfo(ctx)
old := apis.GetBaseline(ctx).(*Type)
// Compare old.Spec vs. new.Spec and on changes
// update the "updater" annotation from ui.
}
}
```
One of the key motivations for this refactoring was to enable us
to do more powerful validation in `apis.Validate` beyond the niche
of immutability checking (and without introducing yet-another
one-off niche interface). In the BYO Revision name PoC I abused
`apis.Immutable` to do more arbitrary before/after validation,
which with this can simply be a part of `apis.Validatable`.
See: https://github.com/knative/serving/pull/3562
The general stance on deprecating interfaces such as these will be
to deprecate them in a non-breaking way (via a comment for now). They
will be hollowed out when the functionality is removed from the webhook,
but left in because of diamond dependency problems. In this change
we remove the `apis.Annotatable` functionality and deprecate the
`apis.Immutable` functionality.
This moves the common Condition stuff to apis, and creates a v1beta1 form of Status that uses the Condition it defines (changing this in v1alpha1 is too breaking).
There aren't really any meaningful changes in this PR, mostly reorganization. Enumerating what I did:
1. Copied `condition_set*.go` to `apis/`,
1. Copied the `Condition` portions of `conditions_types.go` to `apis/`,
1. Copied the balance of `conditions_types.go` to `apis/duck/v1beta1/status_types.go`,
1. Changed the parts of the above to reference things in the appropriate new places,
1. Removed the reflection-based `ConditionsAccessor` stuff, implementing it instead on `duckv1beta1.Status`.
1. Incorporate: https://github.com/knative/pkg/pull/358
Clients of webhook can now decorate the request context with additional metadata
via:
```
ac := &AdmissionController{
... // As before
WithContext: func(ctx context.Context) context.Context {
// logic to attach stuff to ctx
}
}
```
This metadata can then be accessed off of the context in methods like
`SetDefaults` and `Validate` on types registered as webhook handlers.
Fixes: https://github.com/knative/pkg/issues/306
* Drop webhook logic to increment spec.generation
With Kubernetes 1.11+ metadata.generation now increments properly
when the status subresource is enabled on CRDs
For more details see: https://github.com/knative/serving/issues/643
* Drop the generational duck type
* Initial commit for the webhook to set the annotations about mutator.
The user that created or updated the resource will be set in the
annotations.
* update comments
* remove debug logging
* logging :/
* logging :/, returns
* logging :/ III
* error wrap
* simplify test
* rename the test
* add pkg/errors to the deps for better errors
* do not require CRD to implement Annotatable
* review issues
* fix interface as required by review
* Webhook creates a patch for all fields generated by round tripping the JSON through Golang types.
* Add unit tests for InnerDefaultResource.
* Linter errors.
* PR comments - test changes
* t.Helper()
* PR comments.
Immutable fields with default values may now be changed iff they change is to populate their default value. This is to support defaulting in the scenario where an object was created long ago and a new field (with a default!) is added. When controllers attempt to mutate the object status today, this would create a webhook rejection! With this change, we compare against a freshly defaulted "old" object to exclude newly defaulted fields from the immutability check.
We saw this in knative/serving for the newly added TimeoutSeconds field in Revision (otherwise immutable), which I believe it leading to upgrade testing flakes since post-upgrade Revision status updates will fail.
* Change VerifyType to return an error instead of panicking
Signed-off-by: Vincent Demeester <vdemeest@redhat.com>
* Move the use of `VerifyType` in tests
Those calls to `duck.VerifyType` are done at runtime and thus could be
costly at program startup. Putting them under tests ensure we still
assert those types but during unit testing.
Signed-off-by: Vincent Demeester <vdemeest@redhat.com>
This starts to sketch common libraries for instantiating informers/listers for a particular GroupVersionResource as one of our duck types.
You can instantiate a duck.InformerFactory like so:
```go
dynaClient, err := dynamic.NewForConfig(cfg)
if err != nil {
logger.Fatalf("Error building dynamic clientset: %v", err)
}
// Cache as the outermost layer so we only register the EventHandler once.
dif := &duck.CachedInformerFactory{
Delegate: &duck.EnqueueInformerFactory{
Delegate: &duck.TypedInformerFactory{
Client: dynaClient,
Type: &duckv1alpha1.Target{},
ResyncPeriod: 30 * time.Second,
StopChannel: stopCh,
},
EventHandler: cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
// Enqueue, obj is: *duckerv1alpha1.Target
},
UpdateFunc: func(old, new interface{}) {
// Enqueue, old and new are: *duckerv1alpha1.Target
},
},
},
}
```
Then, as you come across new GroupVersionResources that you want to handle:
```go
informer, lister, err := dif.Get(gvr)
if err != nil {
logger.Fatalf("Error starting shared index informer: %v", err)
}
```
With the `duck.TypedInformerFactory` the objects will be returned as the provided `Type:`, so in this example, you could safely write:
```go
elt, err := lister.ByNamespace(ns).Get(name)
if err != nil { ... }
target := elt.(*duckv1alpha1.Target)
// Stuff involving target.
```
* Prune the GenericCRD spec to what is used.
Encapsulate our change detection slightly.
* Support common spec mutations via duck typing.
This adds support for performing common mutations to objects via duck types and JSON patching.
Fixes: https://github.com/knative/pkg/issues/76
* Eliminate getSpecJSON thru schemaless duck typing.
This leverages a one-off trick to get the JSON of the spec field from arbitrary types.