k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/test/integration/apiserver/max_request_body_bytes_test.go (about) 1 /* 2 Copyright 2018 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 "strings" 21 "testing" 22 23 v1 "k8s.io/api/core/v1" 24 apierrors "k8s.io/apimachinery/pkg/api/errors" 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 "k8s.io/apimachinery/pkg/types" 27 "k8s.io/kubernetes/test/integration/framework" 28 "k8s.io/kubernetes/test/utils/ktesting" 29 ) 30 31 // Tests that the apiserver limits the resource size in write operations. 32 func TestMaxResourceSize(t *testing.T) { 33 tCtx := ktesting.Init(t) 34 clientSet, _, tearDownFn := framework.StartTestServer(tCtx, t, framework.TestServerSetup{}) 35 defer tearDownFn() 36 37 hugeData := []byte(strings.Repeat("x", 3*1024*1024+1)) 38 39 rest := clientSet.Discovery().RESTClient() 40 41 c := clientSet.CoreV1().RESTClient() 42 t.Run("Create should limit the request body size", func(t *testing.T) { 43 err := c.Post().AbsPath("/api/v1/namespaces/default/pods"). 44 Body(hugeData).Do(tCtx).Error() 45 if err == nil { 46 t.Fatalf("unexpected no error") 47 } 48 if !apierrors.IsRequestEntityTooLargeError(err) { 49 t.Errorf("expected requested entity too large err, got %v", err) 50 51 } 52 }) 53 54 // Create a secret so we can update/patch/delete it. 55 secret := &v1.Secret{ 56 ObjectMeta: metav1.ObjectMeta{ 57 Name: "test", 58 }, 59 } 60 _, err := clientSet.CoreV1().Secrets("default").Create(tCtx, secret, metav1.CreateOptions{}) 61 if err != nil { 62 t.Fatal(err) 63 } 64 65 t.Run("Update should limit the request body size", func(t *testing.T) { 66 err = c.Put().AbsPath("/api/v1/namespaces/default/secrets/test"). 67 Body(hugeData).Do(tCtx).Error() 68 if err == nil { 69 t.Fatalf("unexpected no error") 70 } 71 if !apierrors.IsRequestEntityTooLargeError(err) { 72 t.Errorf("expected requested entity too large err, got %v", err) 73 74 } 75 }) 76 t.Run("Patch should limit the request body size", func(t *testing.T) { 77 err = c.Patch(types.JSONPatchType).AbsPath("/api/v1/namespaces/default/secrets/test"). 78 Body(hugeData).Do(tCtx).Error() 79 if err == nil { 80 t.Fatalf("unexpected no error") 81 } 82 if !apierrors.IsRequestEntityTooLargeError(err) { 83 t.Errorf("expected requested entity too large err, got %v", err) 84 85 } 86 }) 87 t.Run("JSONPatchType should handle a patch just under the max limit", func(t *testing.T) { 88 if testing.Short() { 89 t.Skip("skipping expensive test") 90 } 91 patchBody := []byte(`[{"op":"add","path":"/foo","value":` + strings.Repeat("[", 3*1024*1024/2-100) + strings.Repeat("]", 3*1024*1024/2-100) + `}]`) 92 err = rest.Patch(types.JSONPatchType).AbsPath("/api/v1/namespaces/default/secrets/test"). 93 Body(patchBody).Do(tCtx).Error() 94 if err != nil && !apierrors.IsBadRequest(err) { 95 t.Errorf("expected success or bad request err, got %v", err) 96 } 97 }) 98 t.Run("JSONPatchType should handle a valid patch just under the max limit", func(t *testing.T) { 99 if testing.Short() { 100 t.Skip("skipping expensive test") 101 } 102 patchBody := []byte(`[{"op":"add","path":"/foo","value":0` + strings.Repeat(" ", 3*1024*1024-100) + `}]`) 103 err = rest.Patch(types.JSONPatchType).AbsPath("/api/v1/namespaces/default/secrets/test"). 104 Body(patchBody).Do(tCtx).Error() 105 if err != nil { 106 t.Errorf("unexpected error: %v", err) 107 } 108 }) 109 t.Run("MergePatchType should handle a patch just under the max limit", func(t *testing.T) { 110 if testing.Short() { 111 t.Skip("skipping expensive test") 112 } 113 patchBody := []byte(`{"value":` + strings.Repeat("[", 3*1024*1024/2-100) + strings.Repeat("]", 3*1024*1024/2-100) + `}`) 114 err = rest.Patch(types.MergePatchType).AbsPath("/api/v1/namespaces/default/secrets/test"). 115 Body(patchBody).Do(tCtx).Error() 116 if err != nil && !apierrors.IsBadRequest(err) { 117 t.Errorf("expected success or bad request err, got %v", err) 118 } 119 }) 120 t.Run("MergePatchType should handle a valid patch just under the max limit", func(t *testing.T) { 121 if testing.Short() { 122 t.Skip("skipping expensive test") 123 } 124 patchBody := []byte(`{"value":0` + strings.Repeat(" ", 3*1024*1024-100) + `}`) 125 err = rest.Patch(types.MergePatchType).AbsPath("/api/v1/namespaces/default/secrets/test"). 126 Body(patchBody).Do(tCtx).Error() 127 if err != nil { 128 t.Errorf("unexpected error: %v", err) 129 } 130 }) 131 t.Run("StrategicMergePatchType should handle a patch just under the max limit", func(t *testing.T) { 132 if testing.Short() { 133 t.Skip("skipping expensive test") 134 } 135 patchBody := []byte(`{"value":` + strings.Repeat("[", 3*1024*1024/2-100) + strings.Repeat("]", 3*1024*1024/2-100) + `}`) 136 err = rest.Patch(types.StrategicMergePatchType).AbsPath("/api/v1/namespaces/default/secrets/test"). 137 Body(patchBody).Do(tCtx).Error() 138 if err != nil && !apierrors.IsBadRequest(err) { 139 t.Errorf("expected success or bad request err, got %v", err) 140 } 141 }) 142 t.Run("StrategicMergePatchType should handle a valid patch just under the max limit", func(t *testing.T) { 143 if testing.Short() { 144 t.Skip("skipping expensive test") 145 } 146 patchBody := []byte(`{"value":0` + strings.Repeat(" ", 3*1024*1024-100) + `}`) 147 err = rest.Patch(types.StrategicMergePatchType).AbsPath("/api/v1/namespaces/default/secrets/test"). 148 Body(patchBody).Do(tCtx).Error() 149 if err != nil { 150 t.Errorf("unexpected error: %v", err) 151 } 152 }) 153 t.Run("ApplyPatchType should handle a patch just under the max limit", func(t *testing.T) { 154 if testing.Short() { 155 t.Skip("skipping expensive test") 156 } 157 patchBody := []byte(`{"value":` + strings.Repeat("[", 3*1024*1024/2-100) + strings.Repeat("]", 3*1024*1024/2-100) + `}`) 158 err = rest.Patch(types.ApplyPatchType).Param("fieldManager", "test").AbsPath("/api/v1/namespaces/default/secrets/test"). 159 Body(patchBody).Do(tCtx).Error() 160 if err != nil && !apierrors.IsBadRequest(err) { 161 t.Errorf("expected success or bad request err, got %#v", err) 162 } 163 }) 164 t.Run("ApplyPatchType should handle a valid patch just under the max limit", func(t *testing.T) { 165 if testing.Short() { 166 t.Skip("skipping expensive test") 167 } 168 patchBody := []byte(`{"apiVersion":"v1","kind":"Secret"` + strings.Repeat(" ", 3*1024*1024-100) + `}`) 169 err = rest.Patch(types.ApplyPatchType).Param("fieldManager", "test").AbsPath("/api/v1/namespaces/default/secrets/test"). 170 Body(patchBody).Do(tCtx).Error() 171 if err != nil { 172 t.Errorf("unexpected error: %v", err) 173 } 174 }) 175 t.Run("Delete should limit the request body size", func(t *testing.T) { 176 err = c.Delete().AbsPath("/api/v1/namespaces/default/secrets/test"). 177 Body(hugeData).Do(tCtx).Error() 178 if err == nil { 179 t.Fatalf("unexpected no error") 180 } 181 if !apierrors.IsRequestEntityTooLargeError(err) { 182 t.Errorf("expected requested entity too large err, got %v", err) 183 184 } 185 }) 186 187 // Create YAML over 3MB limit 188 t.Run("create should limit yaml parsing", func(t *testing.T) { 189 yamlBody := []byte(` 190 apiVersion: v1 191 kind: ConfigMap 192 metadata: 193 name: mytest 194 values: ` + strings.Repeat("[", 3*1024*1024)) 195 196 _, err := rest.Post(). 197 SetHeader("Accept", "application/yaml"). 198 SetHeader("Content-Type", "application/yaml"). 199 AbsPath("/api/v1/namespaces/default/configmaps"). 200 Body(yamlBody). 201 DoRaw(tCtx) 202 if !apierrors.IsRequestEntityTooLargeError(err) { 203 t.Errorf("expected too large error, got %v", err) 204 } 205 }) 206 207 // Create YAML just under 3MB limit, nested 208 t.Run("create should handle a yaml document just under the maximum size with correct nesting", func(t *testing.T) { 209 if testing.Short() { 210 t.Skip("skipping expensive test") 211 } 212 yamlBody := []byte(` 213 apiVersion: v1 214 kind: ConfigMap 215 metadata: 216 name: mytest 217 values: ` + strings.Repeat("[", 3*1024*1024/2-500) + strings.Repeat("]", 3*1024*1024/2-500)) 218 219 _, err := rest.Post(). 220 SetHeader("Accept", "application/yaml"). 221 SetHeader("Content-Type", "application/yaml"). 222 AbsPath("/api/v1/namespaces/default/configmaps"). 223 Body(yamlBody). 224 DoRaw(tCtx) 225 if !apierrors.IsBadRequest(err) { 226 t.Errorf("expected bad request, got %v", err) 227 } 228 }) 229 230 // Create YAML just under 3MB limit, not nested 231 t.Run("create should handle a yaml document just under the maximum size with unbalanced nesting", func(t *testing.T) { 232 if testing.Short() { 233 t.Skip("skipping expensive test") 234 } 235 yamlBody := []byte(` 236 apiVersion: v1 237 kind: ConfigMap 238 metadata: 239 name: mytest 240 values: ` + strings.Repeat("[", 3*1024*1024-1000)) 241 242 _, err := rest.Post(). 243 SetHeader("Accept", "application/yaml"). 244 SetHeader("Content-Type", "application/yaml"). 245 AbsPath("/api/v1/namespaces/default/configmaps"). 246 Body(yamlBody). 247 DoRaw(tCtx) 248 if !apierrors.IsBadRequest(err) { 249 t.Errorf("expected bad request, got %v", err) 250 } 251 }) 252 253 // Create JSON over 3MB limit 254 t.Run("create should limit json parsing", func(t *testing.T) { 255 jsonBody := []byte(`{ 256 "apiVersion": "v1", 257 "kind": "ConfigMap", 258 "metadata": { 259 "name": "mytest" 260 }, 261 "values": ` + strings.Repeat("[", 3*1024*1024/2) + strings.Repeat("]", 3*1024*1024/2) + "}") 262 263 _, err := rest.Post(). 264 SetHeader("Accept", "application/json"). 265 SetHeader("Content-Type", "application/json"). 266 AbsPath("/api/v1/namespaces/default/configmaps"). 267 Body(jsonBody). 268 DoRaw(tCtx) 269 if !apierrors.IsRequestEntityTooLargeError(err) { 270 t.Errorf("expected too large error, got %v", err) 271 } 272 }) 273 274 // Create JSON just under 3MB limit, nested 275 t.Run("create should handle a json document just under the maximum size with correct nesting", func(t *testing.T) { 276 if testing.Short() { 277 t.Skip("skipping expensive test") 278 } 279 jsonBody := []byte(`{ 280 "apiVersion": "v1", 281 "kind": "ConfigMap", 282 "metadata": { 283 "name": "mytest" 284 }, 285 "values": ` + strings.Repeat("[", 3*1024*1024/2-100) + strings.Repeat("]", 3*1024*1024/2-100) + "}") 286 287 _, err := rest.Post(). 288 SetHeader("Accept", "application/json"). 289 SetHeader("Content-Type", "application/json"). 290 AbsPath("/api/v1/namespaces/default/configmaps"). 291 Body(jsonBody). 292 DoRaw(tCtx) 293 // TODO(liggitt): expect bad request on deep nesting, rather than success on dropped unknown field data 294 if err != nil && !apierrors.IsBadRequest(err) { 295 t.Errorf("expected bad request, got %v", err) 296 } 297 }) 298 299 // Create JSON just under 3MB limit, not nested 300 t.Run("create should handle a json document just under the maximum size with unbalanced nesting", func(t *testing.T) { 301 if testing.Short() { 302 t.Skip("skipping expensive test") 303 } 304 jsonBody := []byte(`{ 305 "apiVersion": "v1", 306 "kind": "ConfigMap", 307 "metadata": { 308 "name": "mytest" 309 }, 310 "values": ` + strings.Repeat("[", 3*1024*1024-1000) + "}") 311 312 _, err := rest.Post(). 313 SetHeader("Accept", "application/json"). 314 SetHeader("Content-Type", "application/json"). 315 AbsPath("/api/v1/namespaces/default/configmaps"). 316 Body(jsonBody). 317 DoRaw(tCtx) 318 if !apierrors.IsBadRequest(err) { 319 t.Errorf("expected bad request, got %v", err) 320 } 321 }) 322 }