k8s.io/apiserver@v0.31.1/pkg/endpoints/handlers/rest.go (about)

     1  /*
     2  Copyright 2014 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 handlers
    18  
    19  import (
    20  	"context"
    21  	"encoding/hex"
    22  	"fmt"
    23  	"io"
    24  	"io/ioutil"
    25  	"net/http"
    26  	"net/url"
    27  	"strings"
    28  	"time"
    29  
    30  	grpccodes "google.golang.org/grpc/codes"
    31  	grpcstatus "google.golang.org/grpc/status"
    32  
    33  	apiequality "k8s.io/apimachinery/pkg/api/equality"
    34  	"k8s.io/apimachinery/pkg/api/errors"
    35  	"k8s.io/apimachinery/pkg/api/meta"
    36  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    37  	metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
    38  	"k8s.io/apimachinery/pkg/runtime"
    39  	"k8s.io/apimachinery/pkg/runtime/schema"
    40  	"k8s.io/apimachinery/pkg/types"
    41  	"k8s.io/apimachinery/pkg/util/managedfields"
    42  	"k8s.io/apiserver/pkg/admission"
    43  	"k8s.io/apiserver/pkg/authorization/authorizer"
    44  	requestmetrics "k8s.io/apiserver/pkg/endpoints/handlers/metrics"
    45  	"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
    46  	"k8s.io/apiserver/pkg/endpoints/metrics"
    47  	"k8s.io/apiserver/pkg/endpoints/request"
    48  	"k8s.io/apiserver/pkg/registry/rest"
    49  	"k8s.io/apiserver/pkg/warning"
    50  )
    51  
    52  const (
    53  	// 34 chose as a number close to 30 that is likely to be unique enough to jump out at me the next time I see a timeout.
    54  	// Everyone chooses 30.
    55  	requestTimeoutUpperBound = 34 * time.Second
    56  	// DuplicateOwnerReferencesWarningFormat is the warning that a client receives when a create/update request contains
    57  	// duplicate owner reference entries.
    58  	DuplicateOwnerReferencesWarningFormat = ".metadata.ownerReferences contains duplicate entries; API server dedups owner references in 1.20+, and may reject such requests as early as 1.24; please fix your requests; duplicate UID(s) observed: %v"
    59  	// DuplicateOwnerReferencesAfterMutatingAdmissionWarningFormat indicates the duplication was observed
    60  	// after mutating admission.
    61  	// NOTE: For CREATE and UPDATE requests the API server dedups both before and after mutating admission.
    62  	// For PATCH request the API server only dedups after mutating admission.
    63  	DuplicateOwnerReferencesAfterMutatingAdmissionWarningFormat = ".metadata.ownerReferences contains duplicate entries after mutating admission happens; API server dedups owner references in 1.20+, and may reject such requests as early as 1.24; please fix your requests; duplicate UID(s) observed: %v"
    64  	// shortPrefix is one possible beginning of yaml unmarshal strict errors.
    65  	shortPrefix = "yaml: unmarshal errors:\n"
    66  	// longPrefix is the other possible beginning of yaml unmarshal strict errors.
    67  	longPrefix = "error converting YAML to JSON: yaml: unmarshal errors:\n"
    68  )
    69  
    70  // RequestScope encapsulates common fields across all RESTful handler methods.
    71  type RequestScope struct {
    72  	Namer ScopeNamer
    73  
    74  	Serializer runtime.NegotiatedSerializer
    75  	runtime.ParameterCodec
    76  
    77  	// StandardSerializers, if set, restricts which serializers can be used when
    78  	// we aren't transforming the output (into Table or PartialObjectMetadata).
    79  	// Used only by CRDs which do not yet support Protobuf.
    80  	StandardSerializers []runtime.SerializerInfo
    81  
    82  	Creater         runtime.ObjectCreater
    83  	Convertor       runtime.ObjectConvertor
    84  	Defaulter       runtime.ObjectDefaulter
    85  	Typer           runtime.ObjectTyper
    86  	UnsafeConvertor runtime.ObjectConvertor
    87  	Authorizer      authorizer.Authorizer
    88  
    89  	EquivalentResourceMapper runtime.EquivalentResourceMapper
    90  
    91  	TableConvertor rest.TableConvertor
    92  	FieldManager   *managedfields.FieldManager
    93  
    94  	Resource schema.GroupVersionResource
    95  	Kind     schema.GroupVersionKind
    96  
    97  	// AcceptsGroupVersionDelegate is an optional delegate that can be queried about whether a given GVK
    98  	// can be accepted in create or update requests. If nil, only scope.Kind is accepted.
    99  	// Note that this does not enable multi-version support for reads from a single endpoint.
   100  	AcceptsGroupVersionDelegate rest.GroupVersionAcceptor
   101  
   102  	Subresource string
   103  
   104  	MetaGroupVersion schema.GroupVersion
   105  
   106  	// HubGroupVersion indicates what version objects read from etcd or incoming requests should be converted to for in-memory handling.
   107  	HubGroupVersion schema.GroupVersion
   108  
   109  	MaxRequestBodyBytes int64
   110  }
   111  
   112  func (scope *RequestScope) err(err error, w http.ResponseWriter, req *http.Request) {
   113  	responsewriters.ErrorNegotiated(err, scope.Serializer, scope.Kind.GroupVersion(), w, req)
   114  }
   115  
   116  // AcceptsGroupVersion returns true if the specified GroupVersion is allowed
   117  // in create and update requests.
   118  func (scope *RequestScope) AcceptsGroupVersion(gv schema.GroupVersion) bool {
   119  	// If there's a custom acceptor, delegate to it. This is extremely rare.
   120  	if scope.AcceptsGroupVersionDelegate != nil {
   121  		return scope.AcceptsGroupVersionDelegate.AcceptsGroupVersion(gv)
   122  	}
   123  	// Fall back to only allowing the singular Kind. This is the typical behavior.
   124  	return gv == scope.Kind.GroupVersion()
   125  }
   126  
   127  func (scope *RequestScope) AllowsMediaTypeTransform(mimeType, mimeSubType string, gvk *schema.GroupVersionKind) bool {
   128  	// some handlers like CRDs can't serve all the mime types that PartialObjectMetadata or Table can - if
   129  	// gvk is nil (no conversion) allow StandardSerializers to further restrict the set of mime types.
   130  	if gvk == nil {
   131  		if len(scope.StandardSerializers) == 0 {
   132  			return true
   133  		}
   134  		for _, info := range scope.StandardSerializers {
   135  			if info.MediaTypeType == mimeType && info.MediaTypeSubType == mimeSubType {
   136  				return true
   137  			}
   138  		}
   139  		return false
   140  	}
   141  
   142  	// TODO: this is temporary, replace with an abstraction calculated at endpoint installation time
   143  	if gvk.GroupVersion() == metav1beta1.SchemeGroupVersion || gvk.GroupVersion() == metav1.SchemeGroupVersion {
   144  		switch gvk.Kind {
   145  		case "Table":
   146  			return scope.TableConvertor != nil &&
   147  				mimeType == "application" &&
   148  				(mimeSubType == "json" || mimeSubType == "yaml")
   149  		case "PartialObjectMetadata", "PartialObjectMetadataList":
   150  			// TODO: should delineate between lists and non-list endpoints
   151  			return true
   152  		default:
   153  			return false
   154  		}
   155  	}
   156  	return false
   157  }
   158  
   159  func (scope *RequestScope) AllowsServerVersion(version string) bool {
   160  	return version == scope.MetaGroupVersion.Version
   161  }
   162  
   163  func (scope *RequestScope) AllowsStreamSchema(s string) bool {
   164  	return s == "watch"
   165  }
   166  
   167  var _ admission.ObjectInterfaces = &RequestScope{}
   168  
   169  func (r *RequestScope) GetObjectCreater() runtime.ObjectCreater     { return r.Creater }
   170  func (r *RequestScope) GetObjectTyper() runtime.ObjectTyper         { return r.Typer }
   171  func (r *RequestScope) GetObjectDefaulter() runtime.ObjectDefaulter { return r.Defaulter }
   172  func (r *RequestScope) GetObjectConvertor() runtime.ObjectConvertor { return r.Convertor }
   173  func (r *RequestScope) GetEquivalentResourceMapper() runtime.EquivalentResourceMapper {
   174  	return r.EquivalentResourceMapper
   175  }
   176  
   177  // ConnectResource returns a function that handles a connect request on a rest.Storage object.
   178  func ConnectResource(connecter rest.Connecter, scope *RequestScope, admit admission.Interface, restPath string, isSubresource bool) http.HandlerFunc {
   179  	return func(w http.ResponseWriter, req *http.Request) {
   180  		if isDryRun(req.URL) {
   181  			scope.err(errors.NewBadRequest("dryRun is not supported"), w, req)
   182  			return
   183  		}
   184  
   185  		namespace, name, err := scope.Namer.Name(req)
   186  		if err != nil {
   187  			scope.err(err, w, req)
   188  			return
   189  		}
   190  		ctx := req.Context()
   191  		ctx = request.WithNamespace(ctx, namespace)
   192  		admit = admission.WithAudit(admit)
   193  
   194  		opts, subpath, subpathKey := connecter.NewConnectOptions()
   195  		if err := getRequestOptions(req, scope, opts, subpath, subpathKey, isSubresource); err != nil {
   196  			err = errors.NewBadRequest(err.Error())
   197  			scope.err(err, w, req)
   198  			return
   199  		}
   200  		if admit != nil && admit.Handles(admission.Connect) {
   201  			userInfo, _ := request.UserFrom(ctx)
   202  			// TODO: remove the mutating admission here as soon as we have ported all plugin that handle CONNECT
   203  			if mutatingAdmission, ok := admit.(admission.MutationInterface); ok {
   204  				err = mutatingAdmission.Admit(ctx, admission.NewAttributesRecord(opts, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Connect, nil, false, userInfo), scope)
   205  				if err != nil {
   206  					scope.err(err, w, req)
   207  					return
   208  				}
   209  			}
   210  			if validatingAdmission, ok := admit.(admission.ValidationInterface); ok {
   211  				err = validatingAdmission.Validate(ctx, admission.NewAttributesRecord(opts, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Connect, nil, false, userInfo), scope)
   212  				if err != nil {
   213  					scope.err(err, w, req)
   214  					return
   215  				}
   216  			}
   217  		}
   218  		requestInfo, _ := request.RequestInfoFrom(ctx)
   219  		metrics.RecordLongRunning(req, requestInfo, metrics.APIServerComponent, func() {
   220  			handler, err := connecter.Connect(ctx, name, opts, &responder{scope: scope, req: req, w: w})
   221  			if err != nil {
   222  				scope.err(err, w, req)
   223  				return
   224  			}
   225  			handler.ServeHTTP(w, req)
   226  		})
   227  	}
   228  }
   229  
   230  // responder implements rest.Responder for assisting a connector in writing objects or errors.
   231  type responder struct {
   232  	scope *RequestScope
   233  	req   *http.Request
   234  	w     http.ResponseWriter
   235  }
   236  
   237  func (r *responder) Object(statusCode int, obj runtime.Object) {
   238  	responsewriters.WriteObjectNegotiated(r.scope.Serializer, r.scope, r.scope.Kind.GroupVersion(), r.w, r.req, statusCode, obj, false)
   239  }
   240  
   241  func (r *responder) Error(err error) {
   242  	r.scope.err(err, r.w, r.req)
   243  }
   244  
   245  // transformDecodeError adds additional information into a bad-request api error when a decode fails.
   246  func transformDecodeError(typer runtime.ObjectTyper, baseErr error, into runtime.Object, gvk *schema.GroupVersionKind, body []byte) error {
   247  	objGVKs, _, err := typer.ObjectKinds(into)
   248  	if err != nil {
   249  		return errors.NewBadRequest(err.Error())
   250  	}
   251  	objGVK := objGVKs[0]
   252  	if gvk != nil && len(gvk.Kind) > 0 {
   253  		return errors.NewBadRequest(fmt.Sprintf("%s in version %q cannot be handled as a %s: %v", gvk.Kind, gvk.Version, objGVK.Kind, baseErr))
   254  	}
   255  	summary := summarizeData(body, 30)
   256  	return errors.NewBadRequest(fmt.Sprintf("the object provided is unrecognized (must be of type %s): %v (%s)", objGVK.Kind, baseErr, summary))
   257  }
   258  
   259  func hasUID(obj runtime.Object) (bool, error) {
   260  	if obj == nil {
   261  		return false, nil
   262  	}
   263  	accessor, err := meta.Accessor(obj)
   264  	if err != nil {
   265  		return false, errors.NewInternalError(err)
   266  	}
   267  	if len(accessor.GetUID()) == 0 {
   268  		return false, nil
   269  	}
   270  	return true, nil
   271  }
   272  
   273  // checkName checks the provided name against the request
   274  func checkName(obj runtime.Object, name, namespace string, namer ScopeNamer) error {
   275  	objNamespace, objName, err := namer.ObjectName(obj)
   276  	if err != nil {
   277  		return errors.NewBadRequest(fmt.Sprintf(
   278  			"the name of the object (%s based on URL) was undeterminable: %v", name, err))
   279  	}
   280  	if objName != name {
   281  		return errors.NewBadRequest(fmt.Sprintf(
   282  			"the name of the object (%s) does not match the name on the URL (%s)", objName, name))
   283  	}
   284  	if len(namespace) > 0 {
   285  		if len(objNamespace) > 0 && objNamespace != namespace {
   286  			return errors.NewBadRequest(fmt.Sprintf(
   287  				"the namespace of the object (%s) does not match the namespace on the request (%s)", objNamespace, namespace))
   288  		}
   289  	}
   290  
   291  	return nil
   292  }
   293  
   294  // dedupOwnerReferences dedups owner references over the entire entry.
   295  // NOTE: We don't know enough about the existing cases of owner references
   296  // sharing the same UID but different fields. Nor do we know what might break.
   297  // In the future we may just dedup/reject owner references with the same UID.
   298  func dedupOwnerReferences(refs []metav1.OwnerReference) ([]metav1.OwnerReference, []string) {
   299  	var result []metav1.OwnerReference
   300  	var duplicates []string
   301  	seen := make(map[types.UID]struct{})
   302  	for _, ref := range refs {
   303  		_, ok := seen[ref.UID]
   304  		// Short-circuit if we haven't seen the UID before. Otherwise
   305  		// check the entire list we have so far.
   306  		if !ok || !hasOwnerReference(result, ref) {
   307  			seen[ref.UID] = struct{}{}
   308  			result = append(result, ref)
   309  		} else {
   310  			duplicates = append(duplicates, string(ref.UID))
   311  		}
   312  	}
   313  	return result, duplicates
   314  }
   315  
   316  // hasOwnerReference returns true if refs has an item equal to ref. The function
   317  // focuses on semantic equality instead of memory equality, to catch duplicates
   318  // with different pointer addresses. The function uses apiequality.Semantic
   319  // instead of implementing its own comparison, to tolerate API changes to
   320  // metav1.OwnerReference.
   321  // NOTE: This is expensive, but we accept it because we've made sure it only
   322  // happens to owner references containing duplicate UIDs, plus typically the
   323  // number of items in the list should be small.
   324  func hasOwnerReference(refs []metav1.OwnerReference, ref metav1.OwnerReference) bool {
   325  	for _, r := range refs {
   326  		if apiequality.Semantic.DeepEqual(r, ref) {
   327  			return true
   328  		}
   329  	}
   330  	return false
   331  }
   332  
   333  // dedupOwnerReferencesAndAddWarning dedups owner references in the object metadata.
   334  // If duplicates are found, the function records a warning to the provided context.
   335  func dedupOwnerReferencesAndAddWarning(obj runtime.Object, requestContext context.Context, afterMutatingAdmission bool) {
   336  	accessor, err := meta.Accessor(obj)
   337  	if err != nil {
   338  		// The object doesn't have metadata. Nothing we need to do here.
   339  		return
   340  	}
   341  	refs := accessor.GetOwnerReferences()
   342  	deduped, duplicates := dedupOwnerReferences(refs)
   343  	if len(duplicates) > 0 {
   344  		// NOTE: For CREATE and UPDATE requests the API server dedups both before and after mutating admission.
   345  		// For PATCH request the API server only dedups after mutating admission.
   346  		format := DuplicateOwnerReferencesWarningFormat
   347  		if afterMutatingAdmission {
   348  			format = DuplicateOwnerReferencesAfterMutatingAdmissionWarningFormat
   349  		}
   350  		warning.AddWarning(requestContext, "", fmt.Sprintf(format,
   351  			strings.Join(duplicates, ", ")))
   352  		accessor.SetOwnerReferences(deduped)
   353  	}
   354  }
   355  
   356  func summarizeData(data []byte, maxLength int) string {
   357  	switch {
   358  	case len(data) == 0:
   359  		return "<empty>"
   360  	case data[0] == '{':
   361  		if len(data) > maxLength {
   362  			return string(data[:maxLength]) + " ..."
   363  		}
   364  		return string(data)
   365  	default:
   366  		if len(data) > maxLength {
   367  			return hex.EncodeToString(data[:maxLength]) + " ..."
   368  		}
   369  		return hex.EncodeToString(data)
   370  	}
   371  }
   372  
   373  func limitedReadBody(req *http.Request, limit int64) ([]byte, error) {
   374  	defer req.Body.Close()
   375  	if limit <= 0 {
   376  		return ioutil.ReadAll(req.Body)
   377  	}
   378  	lr := &io.LimitedReader{
   379  		R: req.Body,
   380  		N: limit + 1,
   381  	}
   382  	data, err := ioutil.ReadAll(lr)
   383  	if err != nil {
   384  		return nil, err
   385  	}
   386  	if lr.N <= 0 {
   387  		return nil, errors.NewRequestEntityTooLargeError(fmt.Sprintf("limit is %d", limit))
   388  	}
   389  	return data, nil
   390  }
   391  
   392  func limitedReadBodyWithRecordMetric(ctx context.Context, req *http.Request, limit int64, resourceGroup string, verb requestmetrics.RequestBodyVerb) ([]byte, error) {
   393  	readBody, err := limitedReadBody(req, limit)
   394  	if err == nil {
   395  		// only record if we've read successfully
   396  		requestmetrics.RecordRequestBodySize(ctx, resourceGroup, verb, len(readBody))
   397  	}
   398  	return readBody, err
   399  }
   400  
   401  func isDryRun(url *url.URL) bool {
   402  	return len(url.Query()["dryRun"]) != 0
   403  }
   404  
   405  // fieldValidation checks that the field validation feature is enabled
   406  // and returns a valid directive of either
   407  // - Ignore
   408  // - Warn (default)
   409  // - Strict
   410  func fieldValidation(directive string) string {
   411  	if directive == "" {
   412  		return metav1.FieldValidationWarn
   413  	}
   414  	return directive
   415  }
   416  
   417  // parseYAMLWarnings takes the strict decoding errors from the yaml decoder's output
   418  // and parses each individual warnings, or leaves the warning as is if
   419  // it does not look like a yaml strict decoding error.
   420  func parseYAMLWarnings(errString string) []string {
   421  	var trimmedString string
   422  	if trimmedShortString := strings.TrimPrefix(errString, shortPrefix); len(trimmedShortString) < len(errString) {
   423  		trimmedString = trimmedShortString
   424  	} else if trimmedLongString := strings.TrimPrefix(errString, longPrefix); len(trimmedLongString) < len(errString) {
   425  		trimmedString = trimmedLongString
   426  	} else {
   427  		// not a yaml error, return as-is
   428  		return []string{errString}
   429  	}
   430  
   431  	splitStrings := strings.Split(trimmedString, "\n")
   432  	for i, s := range splitStrings {
   433  		splitStrings[i] = strings.TrimSpace(s)
   434  	}
   435  	return splitStrings
   436  }
   437  
   438  // addStrictDecodingWarnings confirms that the error is a strict decoding error
   439  // and if so adds a warning for each strict decoding violation.
   440  func addStrictDecodingWarnings(requestContext context.Context, errs []error) {
   441  	for _, e := range errs {
   442  		yamlWarnings := parseYAMLWarnings(e.Error())
   443  		for _, w := range yamlWarnings {
   444  			warning.AddWarning(requestContext, "", w)
   445  		}
   446  	}
   447  }
   448  
   449  type etcdError interface {
   450  	Code() grpccodes.Code
   451  	Error() string
   452  }
   453  
   454  type grpcError interface {
   455  	GRPCStatus() *grpcstatus.Status
   456  }
   457  
   458  func isTooLargeError(err error) bool {
   459  	if err != nil {
   460  		if etcdErr, ok := err.(etcdError); ok {
   461  			if etcdErr.Code() == grpccodes.InvalidArgument && etcdErr.Error() == "etcdserver: request is too large" {
   462  				return true
   463  			}
   464  		}
   465  		if grpcErr, ok := err.(grpcError); ok {
   466  			if grpcErr.GRPCStatus().Code() == grpccodes.ResourceExhausted && strings.Contains(grpcErr.GRPCStatus().Message(), "trying to send message larger than max") {
   467  				return true
   468  			}
   469  		}
   470  	}
   471  	return false
   472  }