k8s.io/apiserver@v0.31.1/pkg/endpoints/handlers/create.go (about) 1 /* 2 Copyright 2017 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 "bytes" 21 "context" 22 "fmt" 23 "net/http" 24 "strings" 25 "time" 26 "unicode" 27 "unicode/utf8" 28 29 "go.opentelemetry.io/otel/attribute" 30 31 "k8s.io/apimachinery/pkg/api/errors" 32 "k8s.io/apimachinery/pkg/api/meta" 33 metainternalversionscheme "k8s.io/apimachinery/pkg/apis/meta/internalversion/scheme" 34 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 35 "k8s.io/apimachinery/pkg/apis/meta/v1/validation" 36 "k8s.io/apimachinery/pkg/runtime" 37 "k8s.io/apimachinery/pkg/runtime/schema" 38 "k8s.io/apiserver/pkg/admission" 39 "k8s.io/apiserver/pkg/audit" 40 "k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager" 41 "k8s.io/apiserver/pkg/endpoints/handlers/finisher" 42 requestmetrics "k8s.io/apiserver/pkg/endpoints/handlers/metrics" 43 "k8s.io/apiserver/pkg/endpoints/handlers/negotiation" 44 "k8s.io/apiserver/pkg/endpoints/request" 45 "k8s.io/apiserver/pkg/registry/rest" 46 "k8s.io/apiserver/pkg/util/dryrun" 47 "k8s.io/component-base/tracing" 48 "k8s.io/klog/v2" 49 ) 50 51 var namespaceGVR = schema.GroupVersionResource{Group: "", Version: "v1", Resource: "namespaces"} 52 53 func createHandler(r rest.NamedCreater, scope *RequestScope, admit admission.Interface, includeName bool) http.HandlerFunc { 54 return func(w http.ResponseWriter, req *http.Request) { 55 ctx := req.Context() 56 // For performance tracking purposes. 57 ctx, span := tracing.Start(ctx, "Create", traceFields(req)...) 58 defer span.End(500 * time.Millisecond) 59 60 namespace, name, err := scope.Namer.Name(req) 61 if err != nil { 62 if includeName { 63 // name was required, return 64 scope.err(err, w, req) 65 return 66 } 67 68 // otherwise attempt to look up the namespace 69 namespace, err = scope.Namer.Namespace(req) 70 if err != nil { 71 scope.err(err, w, req) 72 return 73 } 74 } 75 76 // enforce a timeout of at most requestTimeoutUpperBound (34s) or less if the user-provided 77 // timeout inside the parent context is lower than requestTimeoutUpperBound. 78 ctx, cancel := context.WithTimeout(ctx, requestTimeoutUpperBound) 79 defer cancel() 80 outputMediaType, _, err := negotiation.NegotiateOutputMediaType(req, scope.Serializer, scope) 81 if err != nil { 82 scope.err(err, w, req) 83 return 84 } 85 86 gv := scope.Kind.GroupVersion() 87 s, err := negotiation.NegotiateInputSerializer(req, false, scope.Serializer) 88 if err != nil { 89 scope.err(err, w, req) 90 return 91 } 92 93 body, err := limitedReadBodyWithRecordMetric(ctx, req, scope.MaxRequestBodyBytes, scope.Resource.GroupResource().String(), requestmetrics.Create) 94 if err != nil { 95 span.AddEvent("limitedReadBody failed", attribute.Int("len", len(body)), attribute.String("err", err.Error())) 96 scope.err(err, w, req) 97 return 98 } 99 span.AddEvent("limitedReadBody succeeded", attribute.Int("len", len(body))) 100 101 options := &metav1.CreateOptions{} 102 values := req.URL.Query() 103 if err := metainternalversionscheme.ParameterCodec.DecodeParameters(values, scope.MetaGroupVersion, options); err != nil { 104 err = errors.NewBadRequest(err.Error()) 105 scope.err(err, w, req) 106 return 107 } 108 if errs := validation.ValidateCreateOptions(options); len(errs) > 0 { 109 err := errors.NewInvalid(schema.GroupKind{Group: metav1.GroupName, Kind: "CreateOptions"}, "", errs) 110 scope.err(err, w, req) 111 return 112 } 113 options.TypeMeta.SetGroupVersionKind(metav1.SchemeGroupVersion.WithKind("CreateOptions")) 114 115 defaultGVK := scope.Kind 116 original := r.New() 117 118 validationDirective := fieldValidation(options.FieldValidation) 119 decodeSerializer := s.Serializer 120 if validationDirective == metav1.FieldValidationWarn || validationDirective == metav1.FieldValidationStrict { 121 decodeSerializer = s.StrictSerializer 122 } 123 124 decoder := scope.Serializer.DecoderToVersion(decodeSerializer, scope.HubGroupVersion) 125 span.AddEvent("About to convert to expected version") 126 obj, gvk, err := decoder.Decode(body, &defaultGVK, original) 127 if err != nil { 128 strictError, isStrictError := runtime.AsStrictDecodingError(err) 129 switch { 130 case isStrictError && obj != nil && validationDirective == metav1.FieldValidationWarn: 131 addStrictDecodingWarnings(req.Context(), strictError.Errors()) 132 case isStrictError && validationDirective == metav1.FieldValidationIgnore: 133 klog.Warningf("unexpected strict error when field validation is set to ignore") 134 fallthrough 135 default: 136 err = transformDecodeError(scope.Typer, err, original, gvk, body) 137 scope.err(err, w, req) 138 return 139 } 140 } 141 142 objGV := gvk.GroupVersion() 143 if !scope.AcceptsGroupVersion(objGV) { 144 err = errors.NewBadRequest(fmt.Sprintf("the API version in the data (%s) does not match the expected API version (%v)", objGV.String(), gv.String())) 145 scope.err(err, w, req) 146 return 147 } 148 span.AddEvent("Conversion done") 149 150 // On create, get name from new object if unset 151 if len(name) == 0 { 152 _, name, _ = scope.Namer.ObjectName(obj) 153 } 154 if len(namespace) == 0 && scope.Resource == namespaceGVR { 155 namespace = name 156 } 157 ctx = request.WithNamespace(ctx, namespace) 158 159 admit = admission.WithAudit(admit) 160 audit.LogRequestObject(req.Context(), obj, objGV, scope.Resource, scope.Subresource, scope.Serializer) 161 162 userInfo, _ := request.UserFrom(ctx) 163 164 if objectMeta, err := meta.Accessor(obj); err == nil { 165 preserveObjectMetaSystemFields := false 166 if c, ok := r.(rest.SubresourceObjectMetaPreserver); ok && len(scope.Subresource) > 0 { 167 preserveObjectMetaSystemFields = c.PreserveRequestObjectMetaSystemFieldsOnSubresourceCreate() 168 } 169 if !preserveObjectMetaSystemFields { 170 rest.WipeObjectMetaSystemFields(objectMeta) 171 } 172 173 // ensure namespace on the object is correct, or error if a conflicting namespace was set in the object 174 if err := rest.EnsureObjectNamespaceMatchesRequestNamespace(rest.ExpectedNamespaceForResource(namespace, scope.Resource), objectMeta); err != nil { 175 scope.err(err, w, req) 176 return 177 } 178 } 179 180 span.AddEvent("About to store object in database") 181 admissionAttributes := admission.NewAttributesRecord(obj, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Create, options, dryrun.IsDryRun(options.DryRun), userInfo) 182 requestFunc := func() (runtime.Object, error) { 183 return r.Create( 184 ctx, 185 name, 186 obj, 187 rest.AdmissionToValidateObjectFunc(admit, admissionAttributes, scope), 188 options, 189 ) 190 } 191 // Dedup owner references before updating managed fields 192 dedupOwnerReferencesAndAddWarning(obj, req.Context(), false) 193 result, err := finisher.FinishRequest(ctx, func() (runtime.Object, error) { 194 liveObj, err := scope.Creater.New(scope.Kind) 195 if err != nil { 196 return nil, fmt.Errorf("failed to create new object (Create for %v): %v", scope.Kind, err) 197 } 198 obj = scope.FieldManager.UpdateNoErrors(liveObj, obj, managerOrUserAgent(options.FieldManager, req.UserAgent())) 199 admit = fieldmanager.NewManagedFieldsValidatingAdmissionController(admit) 200 201 if mutatingAdmission, ok := admit.(admission.MutationInterface); ok && mutatingAdmission.Handles(admission.Create) { 202 if err := mutatingAdmission.Admit(ctx, admissionAttributes, scope); err != nil { 203 return nil, err 204 } 205 } 206 // Dedup owner references again after mutating admission happens 207 dedupOwnerReferencesAndAddWarning(obj, req.Context(), true) 208 result, err := requestFunc() 209 // If the object wasn't committed to storage because it's serialized size was too large, 210 // it is safe to remove managedFields (which can be large) and try again. 211 if isTooLargeError(err) { 212 if accessor, accessorErr := meta.Accessor(obj); accessorErr == nil { 213 accessor.SetManagedFields(nil) 214 result, err = requestFunc() 215 } 216 } 217 return result, err 218 }) 219 if err != nil { 220 span.AddEvent("Write to database call failed", attribute.Int("len", len(body)), attribute.String("err", err.Error())) 221 scope.err(err, w, req) 222 return 223 } 224 span.AddEvent("Write to database call succeeded", attribute.Int("len", len(body))) 225 226 code := http.StatusCreated 227 status, ok := result.(*metav1.Status) 228 if ok && status.Code == 0 { 229 status.Code = int32(code) 230 } 231 232 span.AddEvent("About to write a response") 233 defer span.AddEvent("Writing http response done") 234 transformResponseObject(ctx, scope, req, w, code, outputMediaType, result) 235 } 236 } 237 238 // CreateNamedResource returns a function that will handle a resource creation with name. 239 func CreateNamedResource(r rest.NamedCreater, scope *RequestScope, admission admission.Interface) http.HandlerFunc { 240 return createHandler(r, scope, admission, true) 241 } 242 243 // CreateResource returns a function that will handle a resource creation. 244 func CreateResource(r rest.Creater, scope *RequestScope, admission admission.Interface) http.HandlerFunc { 245 return createHandler(&namedCreaterAdapter{r}, scope, admission, false) 246 } 247 248 type namedCreaterAdapter struct { 249 rest.Creater 250 } 251 252 func (c *namedCreaterAdapter) Create(ctx context.Context, name string, obj runtime.Object, createValidatingAdmission rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) { 253 return c.Creater.Create(ctx, obj, createValidatingAdmission, options) 254 } 255 256 // manager is assumed to be already a valid value, we need to make 257 // userAgent into a valid value too. 258 func managerOrUserAgent(manager, userAgent string) string { 259 if manager != "" { 260 return manager 261 } 262 return prefixFromUserAgent(userAgent) 263 } 264 265 // prefixFromUserAgent takes the characters preceding the first /, quote 266 // unprintable character and then trim what's beyond the 267 // FieldManagerMaxLength limit. 268 func prefixFromUserAgent(u string) string { 269 m := strings.Split(u, "/")[0] 270 buf := bytes.NewBuffer(nil) 271 for _, r := range m { 272 // Ignore non-printable characters 273 if !unicode.IsPrint(r) { 274 continue 275 } 276 // Only append if we have room for it 277 if buf.Len()+utf8.RuneLen(r) > validation.FieldManagerMaxLength { 278 break 279 } 280 buf.WriteRune(r) 281 } 282 return buf.String() 283 }