mirror of https://github.com/knative/pkg.git
Fix subresource update logic. (#2546)
* Fix subresource update logic. Signed-off-by: Ville Aikas <vaikas@chainguard.dev> * Rename IsInSubResourceUpdate => GetUpdatedSubresource Signed-off-by: Ville Aikas <vaikas@chainguard.dev>
This commit is contained in:
parent
418e13833b
commit
f1f36a2c97
|
@ -85,14 +85,21 @@ func IsInUpdate(ctx context.Context) bool {
|
||||||
return ctx.Value(inUpdateKey{}) != nil
|
return ctx.Value(inUpdateKey{}) != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsInStatusUpdate checks whether the context is an Update.
|
// GetUpdatedSubresource returns the subresource being updated or "" if there
|
||||||
func IsInStatusUpdate(ctx context.Context) bool {
|
// is no subresource that's being updated. Examples are "status" for Status
|
||||||
|
// updates, or "scale" for scaling Deployment.
|
||||||
|
func GetUpdatedSubresource(ctx context.Context) string {
|
||||||
value := ctx.Value(inUpdateKey{})
|
value := ctx.Value(inUpdateKey{})
|
||||||
if value == nil {
|
if value == nil {
|
||||||
return false
|
return ""
|
||||||
}
|
}
|
||||||
up := value.(*updatePayload)
|
up := value.(*updatePayload)
|
||||||
return up.subresource == "status"
|
return up.subresource
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsInStatusUpdate checks whether the context is an Update.
|
||||||
|
func IsInStatusUpdate(ctx context.Context) bool {
|
||||||
|
return GetUpdatedSubresource(ctx) == "status"
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBaseline returns the baseline of the update, or nil when we
|
// GetBaseline returns the baseline of the update, or nil when we
|
||||||
|
|
|
@ -55,6 +55,9 @@ type ResourceSpec struct {
|
||||||
FieldForCallbackDefaultingUsername string `json:"fieldForCallbackDefaultingUsername,omitempty"`
|
FieldForCallbackDefaultingUsername string `json:"fieldForCallbackDefaultingUsername,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reject any udpates to this subresource.
|
||||||
|
const disallowedSubresource = "badbadsubresource"
|
||||||
|
|
||||||
// GetGroupVersionKind returns the GroupVersionKind.
|
// GetGroupVersionKind returns the GroupVersionKind.
|
||||||
func (r *Resource) GetGroupVersionKind() schema.GroupVersionKind {
|
func (r *Resource) GetGroupVersionKind() schema.GroupVersionKind {
|
||||||
return SchemeGroupVersion.WithKind("Resource")
|
return SchemeGroupVersion.WithKind("Resource")
|
||||||
|
@ -86,6 +89,7 @@ func (r *Resource) Validate(ctx context.Context) *apis.FieldError {
|
||||||
if apis.IsInUpdate(ctx) {
|
if apis.IsInUpdate(ctx) {
|
||||||
original := apis.GetBaseline(ctx).(*Resource)
|
original := apis.GetBaseline(ctx).(*Resource)
|
||||||
err = err.Also(r.CheckImmutableFields(ctx, original))
|
err = err.Also(r.CheckImmutableFields(ctx, original))
|
||||||
|
err = err.Also(r.CheckAllowedSubresourceUpdate(ctx, original))
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -146,6 +150,16 @@ func (r *Resource) CheckImmutableFields(ctx context.Context, original *Resource)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Resource) CheckAllowedSubresourceUpdate(ctx context.Context, original *Resource) *apis.FieldError {
|
||||||
|
if apis.GetUpdatedSubresource(ctx) == disallowedSubresource {
|
||||||
|
return &apis.FieldError{
|
||||||
|
Message: "Disallowed subresource update",
|
||||||
|
Details: fmt.Sprintf("Disallowed subresource update: %s", disallowedSubresource),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetListType implements apis.Listable
|
// GetListType implements apis.Listable
|
||||||
func (r *Resource) GetListType() runtime.Object {
|
func (r *Resource) GetListType() runtime.Object {
|
||||||
return &ResourceList{}
|
return &ResourceList{}
|
||||||
|
|
|
@ -141,13 +141,13 @@ func (ac *reconciler) decodeRequestAndPrepareContext(
|
||||||
ctx = apis.WithDryRun(ctx)
|
ctx = apis.WithDryRun(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
if newObj != nil && oldObj != nil && req.SubResource == "" {
|
|
||||||
ctx = apis.WithinSubResourceUpdate(ctx, oldObj, req.SubResource)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch req.Operation {
|
switch req.Operation {
|
||||||
case admissionv1.Update:
|
case admissionv1.Update:
|
||||||
ctx = apis.WithinUpdate(ctx, oldObj)
|
if req.SubResource != "" {
|
||||||
|
ctx = apis.WithinSubResourceUpdate(ctx, oldObj, req.SubResource)
|
||||||
|
} else {
|
||||||
|
ctx = apis.WithinUpdate(ctx, oldObj)
|
||||||
|
}
|
||||||
case admissionv1.Create:
|
case admissionv1.Create:
|
||||||
ctx = apis.WithinCreate(ctx)
|
ctx = apis.WithinCreate(ctx)
|
||||||
case admissionv1.Delete:
|
case admissionv1.Delete:
|
||||||
|
|
|
@ -468,10 +468,11 @@ func createCreateResource(ctx context.Context, t *testing.T, r *Resource) *admis
|
||||||
|
|
||||||
func TestAdmitUpdates(t *testing.T) {
|
func TestAdmitUpdates(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
setup func(context.Context, *Resource)
|
setup func(context.Context, *Resource)
|
||||||
mutate func(context.Context, *Resource)
|
mutate func(context.Context, *Resource)
|
||||||
rejection string
|
subresource string
|
||||||
|
rejection string
|
||||||
}{{
|
}{{
|
||||||
name: "test simple update (no diff)",
|
name: "test simple update (no diff)",
|
||||||
setup: func(ctx context.Context, r *Resource) {
|
setup: func(ctx context.Context, r *Resource) {
|
||||||
|
@ -499,6 +500,23 @@ func TestAdmitUpdates(t *testing.T) {
|
||||||
r.Spec.FieldWithValidation = "not what's expected"
|
r.Spec.FieldWithValidation = "not what's expected"
|
||||||
},
|
},
|
||||||
rejection: "invalid value",
|
rejection: "invalid value",
|
||||||
|
}, {
|
||||||
|
name: "bad mutation (invalid subresource)",
|
||||||
|
setup: func(ctx context.Context, r *Resource) {
|
||||||
|
r.SetDefaults(ctx)
|
||||||
|
},
|
||||||
|
mutate: func(ctx context.Context, r *Resource) {
|
||||||
|
},
|
||||||
|
subresource: "badbadsubresource",
|
||||||
|
rejection: "validation failed: Disallowed subresource update: \nDisallowed subresource update: badbadsubresource",
|
||||||
|
}, {
|
||||||
|
name: "good mutation with valid subresource",
|
||||||
|
setup: func(ctx context.Context, r *Resource) {
|
||||||
|
r.SetDefaults(ctx)
|
||||||
|
},
|
||||||
|
mutate: func(ctx context.Context, r *Resource) {
|
||||||
|
},
|
||||||
|
subresource: "goodgoodsubresource",
|
||||||
}}
|
}}
|
||||||
|
|
||||||
for _, tc := range tests {
|
for _, tc := range tests {
|
||||||
|
@ -521,7 +539,7 @@ func TestAdmitUpdates(t *testing.T) {
|
||||||
tc.mutate(ctx, new)
|
tc.mutate(ctx, new)
|
||||||
|
|
||||||
_, ac := newNonRunningTestResourceAdmissionController(t)
|
_, ac := newNonRunningTestResourceAdmissionController(t)
|
||||||
resp := ac.Admit(ctx, createUpdateResource(ctx, t, old, new))
|
resp := ac.Admit(ctx, createUpdateResource(ctx, t, old, new, tc.subresource))
|
||||||
|
|
||||||
if tc.rejection == "" {
|
if tc.rejection == "" {
|
||||||
ExpectAllowed(t, resp)
|
ExpectAllowed(t, resp)
|
||||||
|
@ -532,7 +550,7 @@ func TestAdmitUpdates(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func createUpdateResource(ctx context.Context, t *testing.T, old, new *Resource) *admissionv1.AdmissionRequest {
|
func createUpdateResource(ctx context.Context, t *testing.T, old, new *Resource, subresource string) *admissionv1.AdmissionRequest {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
req := &admissionv1.AdmissionRequest{
|
req := &admissionv1.AdmissionRequest{
|
||||||
Operation: admissionv1.Update,
|
Operation: admissionv1.Update,
|
||||||
|
@ -541,7 +559,8 @@ func createUpdateResource(ctx context.Context, t *testing.T, old, new *Resource)
|
||||||
Version: "v1alpha1",
|
Version: "v1alpha1",
|
||||||
Kind: "Resource",
|
Kind: "Resource",
|
||||||
},
|
},
|
||||||
UserInfo: *apis.GetUserInfo(ctx),
|
UserInfo: *apis.GetUserInfo(ctx),
|
||||||
|
SubResource: subresource,
|
||||||
}
|
}
|
||||||
marshaled, err := json.Marshal(new)
|
marshaled, err := json.Marshal(new)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -47,7 +47,7 @@ func TestStrictValidation(t *testing.T) {
|
||||||
|
|
||||||
newUpdateReq := func(old, new []byte) *admissionv1.AdmissionRequest {
|
newUpdateReq := func(old, new []byte) *admissionv1.AdmissionRequest {
|
||||||
req := &admissionv1.AdmissionRequest{
|
req := &admissionv1.AdmissionRequest{
|
||||||
Operation: admissionv1.Create,
|
Operation: admissionv1.Update,
|
||||||
Kind: metav1.GroupVersionKind{
|
Kind: metav1.GroupVersionKind{
|
||||||
Group: "pkg.knative.dev",
|
Group: "pkg.knative.dev",
|
||||||
Version: "v1alpha1",
|
Version: "v1alpha1",
|
||||||
|
|
Loading…
Reference in New Issue