k8s.io/apiserver@v0.31.1/pkg/registry/rest/create.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 rest 18 19 import ( 20 "context" 21 "fmt" 22 23 "k8s.io/apimachinery/pkg/api/errors" 24 "k8s.io/apimachinery/pkg/api/meta" 25 genericvalidation "k8s.io/apimachinery/pkg/api/validation" 26 "k8s.io/apimachinery/pkg/api/validation/path" 27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 "k8s.io/apimachinery/pkg/runtime" 29 "k8s.io/apimachinery/pkg/runtime/schema" 30 "k8s.io/apimachinery/pkg/util/validation/field" 31 "k8s.io/apiserver/pkg/admission" 32 genericapirequest "k8s.io/apiserver/pkg/endpoints/request" 33 "k8s.io/apiserver/pkg/storage/names" 34 "k8s.io/apiserver/pkg/warning" 35 ) 36 37 // RESTCreateStrategy defines the minimum validation, accepted input, and 38 // name generation behavior to create an object that follows Kubernetes 39 // API conventions. 40 type RESTCreateStrategy interface { 41 runtime.ObjectTyper 42 // The name generator is used when the standard GenerateName field is set. 43 // The NameGenerator will be invoked prior to validation. 44 names.NameGenerator 45 46 // NamespaceScoped returns true if the object must be within a namespace. 47 NamespaceScoped() bool 48 // PrepareForCreate is invoked on create before validation to normalize 49 // the object. For example: remove fields that are not to be persisted, 50 // sort order-insensitive list fields, etc. This should not remove fields 51 // whose presence would be considered a validation error. 52 // 53 // Often implemented as a type check and an initailization or clearing of 54 // status. Clear the status because status changes are internal. External 55 // callers of an api (users) should not be setting an initial status on 56 // newly created objects. 57 PrepareForCreate(ctx context.Context, obj runtime.Object) 58 // Validate returns an ErrorList with validation errors or nil. Validate 59 // is invoked after default fields in the object have been filled in 60 // before the object is persisted. This method should not mutate the 61 // object. 62 Validate(ctx context.Context, obj runtime.Object) field.ErrorList 63 // WarningsOnCreate returns warnings to the client performing a create. 64 // WarningsOnCreate is invoked after default fields in the object have been filled in 65 // and after Validate has passed, before Canonicalize is called, and the object is persisted. 66 // This method must not mutate the object. 67 // 68 // Be brief; limit warnings to 120 characters if possible. 69 // Don't include a "Warning:" prefix in the message (that is added by clients on output). 70 // Warnings returned about a specific field should be formatted as "path.to.field: message". 71 // For example: `spec.imagePullSecrets[0].name: invalid empty name ""` 72 // 73 // Use warning messages to describe problems the client making the API request should correct or be aware of. 74 // For example: 75 // - use of deprecated fields/labels/annotations that will stop working in a future release 76 // - use of obsolete fields/labels/annotations that are non-functional 77 // - malformed or invalid specifications that prevent successful handling of the submitted object, 78 // but are not rejected by validation for compatibility reasons 79 // 80 // Warnings should not be returned for fields which cannot be resolved by the caller. 81 // For example, do not warn about spec fields in a subresource creation request. 82 WarningsOnCreate(ctx context.Context, obj runtime.Object) []string 83 // Canonicalize allows an object to be mutated into a canonical form. This 84 // ensures that code that operates on these objects can rely on the common 85 // form for things like comparison. Canonicalize is invoked after 86 // validation has succeeded but before the object has been persisted. 87 // This method may mutate the object. Often implemented as a type check or 88 // empty method. 89 Canonicalize(obj runtime.Object) 90 } 91 92 // BeforeCreate ensures that common operations for all resources are performed on creation. It only returns 93 // errors that can be converted to api.Status. It invokes PrepareForCreate, then Validate. 94 // It returns nil if the object should be created. 95 func BeforeCreate(strategy RESTCreateStrategy, ctx context.Context, obj runtime.Object) error { 96 objectMeta, kind, kerr := objectMetaAndKind(strategy, obj) 97 if kerr != nil { 98 return kerr 99 } 100 101 // ensure that system-critical metadata has been populated 102 if !metav1.HasObjectMetaSystemFieldValues(objectMeta) { 103 return errors.NewInternalError(fmt.Errorf("system metadata was not initialized")) 104 } 105 106 // ensure the name has been generated 107 if len(objectMeta.GetGenerateName()) > 0 && len(objectMeta.GetName()) == 0 { 108 return errors.NewInternalError(fmt.Errorf("metadata.name was not generated")) 109 } 110 111 // ensure namespace on the object is correct, or error if a conflicting namespace was set in the object 112 requestNamespace, ok := genericapirequest.NamespaceFrom(ctx) 113 if !ok { 114 return errors.NewInternalError(fmt.Errorf("no namespace information found in request context")) 115 } 116 if err := EnsureObjectNamespaceMatchesRequestNamespace(ExpectedNamespaceForScope(requestNamespace, strategy.NamespaceScoped()), objectMeta); err != nil { 117 return err 118 } 119 120 strategy.PrepareForCreate(ctx, obj) 121 122 if errs := strategy.Validate(ctx, obj); len(errs) > 0 { 123 return errors.NewInvalid(kind.GroupKind(), objectMeta.GetName(), errs) 124 } 125 126 // Custom validation (including name validation) passed 127 // Now run common validation on object meta 128 // Do this *after* custom validation so that specific error messages are shown whenever possible 129 if errs := genericvalidation.ValidateObjectMetaAccessor(objectMeta, strategy.NamespaceScoped(), path.ValidatePathSegmentName, field.NewPath("metadata")); len(errs) > 0 { 130 return errors.NewInvalid(kind.GroupKind(), objectMeta.GetName(), errs) 131 } 132 133 for _, w := range strategy.WarningsOnCreate(ctx, obj) { 134 warning.AddWarning(ctx, "", w) 135 } 136 137 strategy.Canonicalize(obj) 138 139 return nil 140 } 141 142 // CheckGeneratedNameError checks whether an error that occurred creating a resource is due 143 // to generation being unable to pick a valid name. 144 func CheckGeneratedNameError(ctx context.Context, strategy RESTCreateStrategy, err error, obj runtime.Object) error { 145 if !errors.IsAlreadyExists(err) { 146 return err 147 } 148 149 objectMeta, gvk, kerr := objectMetaAndKind(strategy, obj) 150 if kerr != nil { 151 return kerr 152 } 153 154 if len(objectMeta.GetGenerateName()) == 0 { 155 // If we don't have a generated name, return the original error (AlreadyExists). 156 // When we're here, the user picked a name that is causing a conflict. 157 return err 158 } 159 160 // Get the group resource information from the context, if populated. 161 gr := schema.GroupResource{} 162 if requestInfo, found := genericapirequest.RequestInfoFrom(ctx); found { 163 gr = schema.GroupResource{Group: gvk.Group, Resource: requestInfo.Resource} 164 } 165 166 // If we have a name and generated name, the server picked a name 167 // that already exists. 168 return errors.NewGenerateNameConflict(gr, objectMeta.GetName(), 1) 169 } 170 171 // objectMetaAndKind retrieves kind and ObjectMeta from a runtime object, or returns an error. 172 func objectMetaAndKind(typer runtime.ObjectTyper, obj runtime.Object) (metav1.Object, schema.GroupVersionKind, error) { 173 objectMeta, err := meta.Accessor(obj) 174 if err != nil { 175 return nil, schema.GroupVersionKind{}, errors.NewInternalError(err) 176 } 177 kinds, _, err := typer.ObjectKinds(obj) 178 if err != nil { 179 return nil, schema.GroupVersionKind{}, errors.NewInternalError(err) 180 } 181 return objectMeta, kinds[0], nil 182 } 183 184 // NamespaceScopedStrategy has a method to tell if the object must be in a namespace. 185 type NamespaceScopedStrategy interface { 186 // NamespaceScoped returns if the object must be in a namespace. 187 NamespaceScoped() bool 188 } 189 190 // AdmissionToValidateObjectFunc converts validating admission to a rest validate object func 191 func AdmissionToValidateObjectFunc(admit admission.Interface, staticAttributes admission.Attributes, o admission.ObjectInterfaces) ValidateObjectFunc { 192 validatingAdmission, ok := admit.(admission.ValidationInterface) 193 if !ok { 194 return func(ctx context.Context, obj runtime.Object) error { return nil } 195 } 196 return func(ctx context.Context, obj runtime.Object) error { 197 name := staticAttributes.GetName() 198 // in case the generated name is populated 199 if len(name) == 0 { 200 if metadata, err := meta.Accessor(obj); err == nil { 201 name = metadata.GetName() 202 } 203 } 204 205 finalAttributes := admission.NewAttributesRecord( 206 obj, 207 staticAttributes.GetOldObject(), 208 staticAttributes.GetKind(), 209 staticAttributes.GetNamespace(), 210 name, 211 staticAttributes.GetResource(), 212 staticAttributes.GetSubresource(), 213 staticAttributes.GetOperation(), 214 staticAttributes.GetOperationOptions(), 215 staticAttributes.IsDryRun(), 216 staticAttributes.GetUserInfo(), 217 ) 218 if !validatingAdmission.Handles(finalAttributes.GetOperation()) { 219 return nil 220 } 221 return validatingAdmission.Validate(ctx, finalAttributes, o) 222 } 223 }