Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[bug] mutating webhook admission failed because of wrong json patch #510

Closed
FillZpp opened this issue Dec 5, 2018 · 1 comment · Fixed by kubernetes-sigs/controller-runtime#256
Assignees

Comments

@FillZpp
Copy link

FillZpp commented Dec 5, 2018

Take an example

  1. Define a CRD, spec like this:
type MyResourceSpec struct {
        Foo string `json:"foo,omitempty"`
        Bar MyResourceBar `json:"myResourceBar,omitempty"`
}

type MyResourceBar struct {
        Baz string `json:"baz,omitempty"`
}
  1. Generate a mutating webhook, the handler like this:
func (h *MyResourceCreateUpdateHandler) Handle(ctx context.Context, req types.Request) types.Response {
	obj := &appsv1beta1.MyResource{}

	err := h.Decoder.Decode(req, obj)
	if err != nil {
		return admission.ErrorResponse(http.StatusBadRequest, err)
	}
	copy := obj.DeepCopy()

	err = h.mutatingMyResourceFn(ctx, copy)
	if err != nil {
		return admission.ErrorResponse(http.StatusInternalServerError, err)
	}
	return admission.PatchResponse(obj, copy)
}
  1. In mutatingMyResourceFn, i try to set value for baz:
func (h *MyResourceCreateUpdateHandler) mutatingMyResourceFn(ctx context.Context, obj *appsv1beta1.MyResource) error {
	obj.Spec.Bar.Baz = "test"

	return nil
}
  1. After installing and deploying, i try to create a CR:
apiVersion: apps.fillzpp.org/v1beta1
kind: MyResource
metadata:
  labels:
    controller-tools.k8s.io: "1.0"
  name: test-myresource
spec:
  foo: foo1
  1. Create failed, error like this:
Error from server (InternalError): error when creating "test.yaml":
 Internal error occurred: Internal error occurred: 
jsonpatch add operation does not apply: doc is missing path: /spec/bar/baz

This is because my test.yaml has no bar in spec, but admission ask to add /spec/bar/baz.

My opinion

admission.PatchResponse should use req.AdmissionRequest.Object.Raw as original object to prepare with current object.

The problem is, the obj in handler contains some fields/objects no existing in original object raw. So when we compare obj with current object, we might get the wrong json patch, and apiserver will report such error message.

The correct code like this

controller-runtime

// PatchResponse returns a new response with json patch.
func PatchResponseWithRaw(originalRaw []byte, original, current runtime.Object) types.Response {
	patches, err := patch.newJSONPatchWithRaw(originalRaw, original, current)
	if err != nil {
		return admission.ErrorResponse(http.StatusInternalServerError, err)
	}
	return types.Response{
		Patches: patches,
		Response: &admissionv1beta1.AdmissionResponse{
			Allowed:   true,
			PatchType: func() *admissionv1beta1.PatchType { pt := admissionv1beta1.PatchTypeJSONPatch; return &pt }(),
		},
	}
}

// NewJSONPatch calculates the JSON patch between original and current objects.
func NewJSONPatchWithRaw(originalRaw []byte, original, current runtime.Object) ([]jsonpatch.JsonPatchOperation, error) {
	originalGVK := original.GetObjectKind().GroupVersionKind()
	currentGVK := current.GetObjectKind().GroupVersionKind()
	if !reflect.DeepEqual(originalGVK, currentGVK) {
		return nil, fmt.Errorf("GroupVersionKind %#v is expected to match %#v", originalGVK, currentGVK)
	}
	cur, err := json.Marshal(current)
	if err != nil {
		return nil, err
	}
	return jsonpatch.CreatePatch(originalRaw, cur)
}

mutating admission handler use new admission.PatchResponseWithRaw

func (h *MyResourceCreateUpdateHandler) Handle(ctx context.Context, req types.Request) types.Response {
	obj := &appsv1beta1.MyResource{}

	err := h.Decoder.Decode(req, obj)
	if err != nil {
		return admission.ErrorResponse(http.StatusBadRequest, err)
	}
	copy := obj.DeepCopy()

	err = h.mutatingMyResourceFn(ctx, copy)
	if err != nil {
		return admission.ErrorResponse(http.StatusInternalServerError, err)
	}
	return admission.PatchResponseWithRaw(req.AdmissionRequest.Object.Raw, obj, copy)
}
@FillZpp FillZpp changed the title 🐛 mutating webhook admission failed because of wrong json patch [bug] mutating webhook admission failed because of wrong json patch Dec 5, 2018
@mengqiy mengqiy self-assigned this Dec 5, 2018
@mengqiy
Copy link
Member

mengqiy commented Dec 7, 2018

@FillZpp Thanks for your suggestion.
It looks very likely your suggested approach will work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants