mirror of https://github.com/knative/pkg.git
This threads a context.Context through the webhook interfaces we expose. (#332)
Related to: https://github.com/knative/pkg/issues/306
This commit is contained in:
parent
3b8bc39ced
commit
60fdcbcabd
|
@ -17,6 +17,8 @@ limitations under the License.
|
|||
package apis
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
authenticationv1 "k8s.io/api/authentication/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
@ -24,13 +26,13 @@ import (
|
|||
// Defaultable defines an interface for setting the defaults for the
|
||||
// uninitialized fields of this instance.
|
||||
type Defaultable interface {
|
||||
SetDefaults()
|
||||
SetDefaults(context.Context)
|
||||
}
|
||||
|
||||
// Validatable indicates that a particular type may have its fields validated.
|
||||
type Validatable interface {
|
||||
// Validate checks the validity of this types fields.
|
||||
Validate() *FieldError
|
||||
Validate(context.Context) *FieldError
|
||||
}
|
||||
|
||||
// Immutable indicates that a particular type has fields that should
|
||||
|
@ -38,7 +40,7 @@ type Validatable interface {
|
|||
type Immutable interface {
|
||||
// CheckImmutableFields checks that the current instance's immutable
|
||||
// fields haven't changed from the provided original.
|
||||
CheckImmutableFields(original Immutable) *FieldError
|
||||
CheckImmutableFields(ctx context.Context, original Immutable) *FieldError
|
||||
}
|
||||
|
||||
// Listable indicates that a particular type can be returned via the returned
|
||||
|
@ -51,5 +53,5 @@ type Listable interface {
|
|||
|
||||
// Annotatable indicates that a particular type applies various annotations.
|
||||
type Annotatable interface {
|
||||
AnnotateUserInfo(previous Annotatable, ui *authenticationv1.UserInfo)
|
||||
AnnotateUserInfo(ctx context.Context, previous Annotatable, ui *authenticationv1.UserInfo)
|
||||
}
|
||||
|
|
|
@ -17,6 +17,8 @@ limitations under the License.
|
|||
package testing
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/knative/pkg/apis"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
@ -47,22 +49,23 @@ var _ apis.Validatable = (*InnerDefaultResource)(nil)
|
|||
var _ apis.Defaultable = (*InnerDefaultResource)(nil)
|
||||
|
||||
// SetDefaults sets default values.
|
||||
func (i *InnerDefaultResource) SetDefaults() {
|
||||
i.Spec.SetDefaults()
|
||||
func (i *InnerDefaultResource) SetDefaults(ctx context.Context) {
|
||||
i.Spec.SetDefaults(ctx)
|
||||
}
|
||||
|
||||
// SetDefaults sets default values.
|
||||
func (cs *InnerDefaultSpec) SetDefaults() {
|
||||
func (cs *InnerDefaultSpec) SetDefaults(ctx context.Context) {
|
||||
if cs.FieldWithDefault == "" {
|
||||
cs.FieldWithDefault = "I'm a default."
|
||||
}
|
||||
}
|
||||
|
||||
// Validate validates the resource.
|
||||
func (*InnerDefaultResource) Validate() *apis.FieldError {
|
||||
func (*InnerDefaultResource) Validate(ctx context.Context) *apis.FieldError {
|
||||
return nil
|
||||
}
|
||||
|
||||
// AnnotateUserInfo satisfies the Annotatable interface.
|
||||
// For this type it is nop.
|
||||
func (*InnerDefaultResource) AnnotateUserInfo(p apis.Annotatable, userName string) {}
|
||||
func (*InnerDefaultResource) AnnotateUserInfo(ctx context.Context, p apis.Annotatable, userName string) {
|
||||
}
|
||||
|
|
|
@ -16,18 +16,21 @@ limitations under the License.
|
|||
|
||||
package testing
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestInnerDefaultResource_Validate(t *testing.T) {
|
||||
r := InnerDefaultResource{}
|
||||
if err := r.Validate(); err != nil {
|
||||
if err := r.Validate(context.TODO()); err != nil {
|
||||
t.Fatalf("Expected no validation errors. Actual %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInnerDefaultResource_SetDefaults(t *testing.T) {
|
||||
r := InnerDefaultResource{}
|
||||
r.SetDefaults()
|
||||
r.SetDefaults(context.TODO())
|
||||
if r.Spec.FieldWithDefault != "I'm a default." {
|
||||
t.Fatalf("Unexpected defaulted value: %v", r.Spec.FieldWithDefault)
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
package testing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/knative/pkg/apis"
|
||||
|
@ -60,12 +61,12 @@ type ResourceSpec struct {
|
|||
}
|
||||
|
||||
// SetDefaults sets the defaults on the object.
|
||||
func (c *Resource) SetDefaults() {
|
||||
c.Spec.SetDefaults()
|
||||
func (c *Resource) SetDefaults(ctx context.Context) {
|
||||
c.Spec.SetDefaults(ctx)
|
||||
}
|
||||
|
||||
// SetDefaults sets the defaults on the spec.
|
||||
func (cs *ResourceSpec) SetDefaults() {
|
||||
func (cs *ResourceSpec) SetDefaults(ctx context.Context) {
|
||||
if cs.FieldWithDefault == "" {
|
||||
cs.FieldWithDefault = "I'm a default."
|
||||
}
|
||||
|
@ -75,7 +76,7 @@ func (cs *ResourceSpec) SetDefaults() {
|
|||
}
|
||||
|
||||
// AnnotateUserInfo satisfies the Annotatable interface.
|
||||
func (c *Resource) AnnotateUserInfo(prev apis.Annotatable, ui *authenticationv1.UserInfo) {
|
||||
func (c *Resource) AnnotateUserInfo(ctx context.Context, prev apis.Annotatable, ui *authenticationv1.UserInfo) {
|
||||
a := c.ObjectMeta.GetAnnotations()
|
||||
if a == nil {
|
||||
a = map[string]string{}
|
||||
|
@ -102,18 +103,18 @@ func (c *Resource) AnnotateUserInfo(prev apis.Annotatable, ui *authenticationv1.
|
|||
c.ObjectMeta.SetAnnotations(a)
|
||||
}
|
||||
|
||||
func (c *Resource) Validate() *apis.FieldError {
|
||||
return c.Spec.Validate().ViaField("spec")
|
||||
func (c *Resource) Validate(ctx context.Context) *apis.FieldError {
|
||||
return c.Spec.Validate(ctx).ViaField("spec")
|
||||
}
|
||||
|
||||
func (cs *ResourceSpec) Validate() *apis.FieldError {
|
||||
func (cs *ResourceSpec) Validate(ctx context.Context) *apis.FieldError {
|
||||
if cs.FieldWithValidation != "magic value" {
|
||||
return apis.ErrInvalidValue(cs.FieldWithValidation, "fieldWithValidation")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (current *Resource) CheckImmutableFields(og apis.Immutable) *apis.FieldError {
|
||||
func (current *Resource) CheckImmutableFields(ctx context.Context, og apis.Immutable) *apis.FieldError {
|
||||
original, ok := og.(*Resource)
|
||||
if !ok {
|
||||
return &apis.FieldError{Message: "The provided original was not a Resource"}
|
||||
|
|
|
@ -208,18 +208,21 @@ func validate(old GenericCRD, new GenericCRD) error {
|
|||
// Copy the old object and set defaults so that we don't reject our own
|
||||
// defaulting done earlier in the webhook.
|
||||
old = old.DeepCopyObject().(GenericCRD)
|
||||
old.SetDefaults()
|
||||
// TODO(mattmoor): Plumb through a real context
|
||||
old.SetDefaults(context.TODO())
|
||||
|
||||
immutableOld, ok := old.(apis.Immutable)
|
||||
if !ok {
|
||||
return fmt.Errorf("unexpected type mismatch %T vs. %T", old, new)
|
||||
}
|
||||
if err := immutableNew.CheckImmutableFields(immutableOld); err != nil {
|
||||
// TODO(mattmoor): Plumb through a real context
|
||||
if err := immutableNew.CheckImmutableFields(context.TODO(), immutableOld); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Can't just `return new.Validate()` because it doesn't properly nil-check.
|
||||
if err := new.Validate(); err != nil {
|
||||
// TODO(mattmoor): Plumb through a real context
|
||||
if err := new.Validate(context.TODO()); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
@ -240,7 +243,8 @@ func setAnnotations(patches duck.JSONPatch, new, old GenericCRD, ui *authenticat
|
|||
}
|
||||
b, a := new.DeepCopyObject().(apis.Annotatable), na
|
||||
|
||||
a.AnnotateUserInfo(oa, ui)
|
||||
// TODO(mattmoor): Plumb through a real context
|
||||
a.AnnotateUserInfo(context.TODO(), oa, ui)
|
||||
patch, err := duck.CreatePatch(b, a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -251,7 +255,8 @@ func setAnnotations(patches duck.JSONPatch, new, old GenericCRD, ui *authenticat
|
|||
// setDefaults simply leverages apis.Defaultable to set defaults.
|
||||
func setDefaults(patches duck.JSONPatch, crd GenericCRD) (duck.JSONPatch, error) {
|
||||
before, after := crd.DeepCopyObject(), crd
|
||||
after.SetDefaults()
|
||||
// TODO(mattmoor): Plumb through a real context
|
||||
after.SetDefaults(context.TODO())
|
||||
|
||||
patch, err := duck.CreatePatch(before, after)
|
||||
if err != nil {
|
||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
package webhook
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
|
@ -138,7 +139,7 @@ func TestValidCreateResourceSucceeds(t *testing.T) {
|
|||
r := createResource("a name")
|
||||
for _, v := range []string{"v1alpha1", "v1beta1"} {
|
||||
r.TypeMeta.APIVersion = v
|
||||
r.SetDefaults() // Fill in defaults to check that there are no patches.
|
||||
r.SetDefaults(context.TODO()) // Fill in defaults to check that there are no patches.
|
||||
_, ac := newNonRunningTestAdmissionController(t, newDefaultOptions())
|
||||
resp := ac.admit(TestContextWithLogger(t), createCreateResource(r))
|
||||
expectAllowed(t, resp)
|
||||
|
@ -234,9 +235,9 @@ func TestInvalidCreateResourceFails(t *testing.T) {
|
|||
|
||||
func TestNopUpdateResourceSucceeds(t *testing.T) {
|
||||
r := createResource("a name")
|
||||
r.SetDefaults() // Fill in defaults to check that there are no patches.
|
||||
r.SetDefaults(context.TODO()) // Fill in defaults to check that there are no patches.
|
||||
nr := r.DeepCopyObject().(*Resource)
|
||||
r.AnnotateUserInfo(nil, &authenticationv1.UserInfo{Username: user1})
|
||||
r.AnnotateUserInfo(context.TODO(), nil, &authenticationv1.UserInfo{Username: user1})
|
||||
_, ac := newNonRunningTestAdmissionController(t, newDefaultOptions())
|
||||
resp := ac.admit(TestContextWithLogger(t), createUpdateResource(r, nr))
|
||||
expectAllowed(t, resp)
|
||||
|
@ -245,10 +246,10 @@ func TestNopUpdateResourceSucceeds(t *testing.T) {
|
|||
|
||||
func TestValidUpdateResourcePreserveAnnotations(t *testing.T) {
|
||||
old := createResource("a name")
|
||||
old.SetDefaults() // Fill in defaults to check that there are no patches.
|
||||
old.AnnotateUserInfo(nil, &authenticationv1.UserInfo{Username: user1})
|
||||
old.SetDefaults(context.TODO()) // Fill in defaults to check that there are no patches.
|
||||
old.AnnotateUserInfo(context.TODO(), nil, &authenticationv1.UserInfo{Username: user1})
|
||||
new := createResource("a name")
|
||||
new.SetDefaults()
|
||||
new.SetDefaults(context.TODO())
|
||||
// User set annotations on the resource.
|
||||
new.ObjectMeta.SetAnnotations(map[string]string{
|
||||
"key": "to-my-heart",
|
||||
|
@ -263,8 +264,8 @@ func TestValidUpdateResourcePreserveAnnotations(t *testing.T) {
|
|||
|
||||
func TestValidBigChangeResourceSucceeds(t *testing.T) {
|
||||
old := createResource("a name")
|
||||
old.SetDefaults() // Fill in defaults to check that there are no patches.
|
||||
old.AnnotateUserInfo(nil, &authenticationv1.UserInfo{Username: user1})
|
||||
old.SetDefaults(context.TODO()) // Fill in defaults to check that there are no patches.
|
||||
old.AnnotateUserInfo(context.TODO(), nil, &authenticationv1.UserInfo{Username: user1})
|
||||
new := createResource("a name")
|
||||
new.Spec.FieldWithDefault = "melon collie and the infinite sadness"
|
||||
|
||||
|
@ -281,8 +282,8 @@ func TestValidBigChangeResourceSucceeds(t *testing.T) {
|
|||
|
||||
func TestValidUpdateResourceSucceeds(t *testing.T) {
|
||||
old := createResource("a name")
|
||||
old.SetDefaults() // Fill in defaults to check that there are no patches.
|
||||
old.AnnotateUserInfo(nil, &authenticationv1.UserInfo{Username: user1})
|
||||
old.SetDefaults(context.TODO()) // Fill in defaults to check that there are no patches.
|
||||
old.AnnotateUserInfo(context.TODO(), nil, &authenticationv1.UserInfo{Username: user1})
|
||||
new := createResource("a name")
|
||||
|
||||
_, ac := newNonRunningTestAdmissionController(t, newDefaultOptions())
|
||||
|
@ -326,7 +327,7 @@ func TestInvalidUpdateResourceFailsImmutability(t *testing.T) {
|
|||
|
||||
func TestDefaultingImmutableFields(t *testing.T) {
|
||||
old := createResource("a name")
|
||||
old.AnnotateUserInfo(nil, &authenticationv1.UserInfo{Username: user1})
|
||||
old.AnnotateUserInfo(context.TODO(), nil, &authenticationv1.UserInfo{Username: user1})
|
||||
new := createResource("a name")
|
||||
|
||||
// If we don't specify the new, but immutable field, we default it,
|
||||
|
|
Loading…
Reference in New Issue