...

Source file src/k8s.io/kubernetes/test/integration/apiserver/max_request_body_bytes_test.go

Documentation: k8s.io/kubernetes/test/integration/apiserver

     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  }
   323  

View as plain text