Skip to content

Commit 82429a9

Browse files
authored
Merge pull request kubernetes#125873 from benluddy/nonjson-rawextension-writes-test
Add integration test for rejecting non-JSON RawExtensions.
2 parents 5837de2 + d4e146c commit 82429a9

File tree

1 file changed

+108
-0
lines changed

1 file changed

+108
-0
lines changed
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*
2+
Copyright 2024 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package apiserver
18+
19+
import (
20+
"context"
21+
"encoding/json"
22+
"errors"
23+
"fmt"
24+
"testing"
25+
26+
"golang.org/x/net/http2"
27+
28+
appsv1 "k8s.io/api/apps/v1"
29+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
30+
"k8s.io/apimachinery/pkg/runtime"
31+
appsv1applyconfigurations "k8s.io/client-go/applyconfigurations/apps/v1"
32+
clientset "k8s.io/client-go/kubernetes"
33+
kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
34+
"k8s.io/kubernetes/test/integration/framework"
35+
)
36+
37+
// TestRequestObjectConvertibleToUnstructured tests that write requests fail if the request object
38+
// is not convertible to unstructured. The ability to convert an object to unstructured ensures that
39+
// it can be encoded as JSON and that field managers can be determined.
40+
func TestRequestObjectConvertibleToUnstructured(t *testing.T) {
41+
server := kubeapiservertesting.StartTestServerOrDie(t, nil, []string{}, framework.SharedEtcd())
42+
defer server.TearDownFn()
43+
44+
for i, raw := range []string{
45+
``,
46+
`"`,
47+
`{`,
48+
`[`,
49+
`1z`,
50+
`z`,
51+
} {
52+
// The Protobuf request encoding is required. Invalid JSON cannot be embedded in a
53+
// JSON object or array without making the containing object or array also invalid.
54+
protoConfig := server.ClientConfig
55+
protoConfig.ContentConfig.ContentType = runtime.ContentTypeProtobuf
56+
protoConfig.ContentConfig.AcceptContentTypes = runtime.ContentTypeProtobuf
57+
protoClient, err := clientset.NewForConfig(protoConfig)
58+
if err != nil {
59+
t.Fatalf("unexpected error creating proto client: %v", err)
60+
}
61+
62+
createError := new(http2.StreamError)
63+
if _, err := protoClient.AppsV1().ControllerRevisions("default").Create(context.TODO(), &appsv1.ControllerRevision{
64+
ObjectMeta: metav1.ObjectMeta{
65+
Name: fmt.Sprintf("test-revision-create-%d", i),
66+
},
67+
Data: runtime.RawExtension{Raw: []byte(raw)},
68+
}, metav1.CreateOptions{}); errors.As(err, createError) && createError.Code == http2.ErrCodeInternal {
69+
t.Logf("create returned internal error as expected with rawextension %#v: %v", raw, err)
70+
} else {
71+
t.Errorf("create returned unexpected error: %#v", err)
72+
}
73+
74+
var marshalerError *json.MarshalerError
75+
if _, err := protoClient.AppsV1().ControllerRevisions("default").Apply(context.TODO(), appsv1applyconfigurations.ControllerRevision("test-revision-apply", "default").
76+
WithData(runtime.RawExtension{Raw: []byte(raw)}),
77+
metav1.ApplyOptions{}); errors.As(err, &marshalerError) {
78+
// In this case the error is currently client-side, since apply request
79+
// bodies must be encoded as JSON. Included here to cover the future
80+
// possibility of Protobuf-encoded apply configurations.
81+
t.Logf("apply returned client-side marshaler error as expected with rawextension %#v: %v", raw, err)
82+
} else {
83+
t.Errorf("apply returned unexpected error: %#v", err)
84+
}
85+
86+
// Create an object to be updated. If the object does not exist, then the update
87+
// will short-circuit on "not found" before it encounters the error that is
88+
// interesting to this test.
89+
existing, err := protoClient.AppsV1().ControllerRevisions("default").Create(context.TODO(), &appsv1.ControllerRevision{
90+
ObjectMeta: metav1.ObjectMeta{
91+
Name: fmt.Sprintf("test-revision-update-%d", i),
92+
},
93+
Data: runtime.RawExtension{Raw: []byte(`{}`)},
94+
}, metav1.CreateOptions{})
95+
if err != nil {
96+
t.Errorf("expected nil create error, got: %v", err)
97+
continue
98+
}
99+
100+
updateError := new(http2.StreamError)
101+
existing.Data = runtime.RawExtension{Raw: []byte(raw)}
102+
if _, err := protoClient.AppsV1().ControllerRevisions(existing.Namespace).Update(context.TODO(), existing, metav1.UpdateOptions{}); errors.As(err, updateError) && updateError.Code == http2.ErrCodeInternal {
103+
t.Logf("update returned internal error as expected with rawextension %#v: %v", raw, err)
104+
} else {
105+
t.Errorf("update returned unexpected error: %#v", err)
106+
}
107+
}
108+
}

0 commit comments

Comments
 (0)