Merge pull request #125873 from benluddy/nonjson-rawextension-writes-test

Add integration test for rejecting non-JSON RawExtensions.
This commit is contained in:
Kubernetes Prow Robot 2024-07-05 09:56:49 -07:00 committed by GitHub
commit 82429a9dda
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -0,0 +1,108 @@
/*
Copyright 2024 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package apiserver
import (
"context"
"encoding/json"
"errors"
"fmt"
"testing"
"golang.org/x/net/http2"
appsv1 "k8s.io/api/apps/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
appsv1applyconfigurations "k8s.io/client-go/applyconfigurations/apps/v1"
clientset "k8s.io/client-go/kubernetes"
kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
"k8s.io/kubernetes/test/integration/framework"
)
// TestRequestObjectConvertibleToUnstructured tests that write requests fail if the request object
// is not convertible to unstructured. The ability to convert an object to unstructured ensures that
// it can be encoded as JSON and that field managers can be determined.
func TestRequestObjectConvertibleToUnstructured(t *testing.T) {
server := kubeapiservertesting.StartTestServerOrDie(t, nil, []string{}, framework.SharedEtcd())
defer server.TearDownFn()
for i, raw := range []string{
``,
`"`,
`{`,
`[`,
`1z`,
`z`,
} {
// The Protobuf request encoding is required. Invalid JSON cannot be embedded in a
// JSON object or array without making the containing object or array also invalid.
protoConfig := server.ClientConfig
protoConfig.ContentConfig.ContentType = runtime.ContentTypeProtobuf
protoConfig.ContentConfig.AcceptContentTypes = runtime.ContentTypeProtobuf
protoClient, err := clientset.NewForConfig(protoConfig)
if err != nil {
t.Fatalf("unexpected error creating proto client: %v", err)
}
createError := new(http2.StreamError)
if _, err := protoClient.AppsV1().ControllerRevisions("default").Create(context.TODO(), &appsv1.ControllerRevision{
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf("test-revision-create-%d", i),
},
Data: runtime.RawExtension{Raw: []byte(raw)},
}, metav1.CreateOptions{}); errors.As(err, createError) && createError.Code == http2.ErrCodeInternal {
t.Logf("create returned internal error as expected with rawextension %#v: %v", raw, err)
} else {
t.Errorf("create returned unexpected error: %#v", err)
}
var marshalerError *json.MarshalerError
if _, err := protoClient.AppsV1().ControllerRevisions("default").Apply(context.TODO(), appsv1applyconfigurations.ControllerRevision("test-revision-apply", "default").
WithData(runtime.RawExtension{Raw: []byte(raw)}),
metav1.ApplyOptions{}); errors.As(err, &marshalerError) {
// In this case the error is currently client-side, since apply request
// bodies must be encoded as JSON. Included here to cover the future
// possibility of Protobuf-encoded apply configurations.
t.Logf("apply returned client-side marshaler error as expected with rawextension %#v: %v", raw, err)
} else {
t.Errorf("apply returned unexpected error: %#v", err)
}
// Create an object to be updated. If the object does not exist, then the update
// will short-circuit on "not found" before it encounters the error that is
// interesting to this test.
existing, err := protoClient.AppsV1().ControllerRevisions("default").Create(context.TODO(), &appsv1.ControllerRevision{
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf("test-revision-update-%d", i),
},
Data: runtime.RawExtension{Raw: []byte(`{}`)},
}, metav1.CreateOptions{})
if err != nil {
t.Errorf("expected nil create error, got: %v", err)
continue
}
updateError := new(http2.StreamError)
existing.Data = runtime.RawExtension{Raw: []byte(raw)}
if _, err := protoClient.AppsV1().ControllerRevisions(existing.Namespace).Update(context.TODO(), existing, metav1.UpdateOptions{}); errors.As(err, updateError) && updateError.Code == http2.ErrCodeInternal {
t.Logf("update returned internal error as expected with rawextension %#v: %v", raw, err)
} else {
t.Errorf("update returned unexpected error: %#v", err)
}
}
}