k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/registry/core/namespace/strategy.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 namespace 18 19 import ( 20 "context" 21 "fmt" 22 23 v1 "k8s.io/api/core/v1" 24 "k8s.io/apimachinery/pkg/fields" 25 "k8s.io/apimachinery/pkg/labels" 26 "k8s.io/apimachinery/pkg/runtime" 27 "k8s.io/apimachinery/pkg/util/validation/field" 28 "k8s.io/apiserver/pkg/registry/generic" 29 apistorage "k8s.io/apiserver/pkg/storage" 30 "k8s.io/apiserver/pkg/storage/names" 31 "k8s.io/kubernetes/pkg/api/legacyscheme" 32 api "k8s.io/kubernetes/pkg/apis/core" 33 "k8s.io/kubernetes/pkg/apis/core/validation" 34 "sigs.k8s.io/structured-merge-diff/v4/fieldpath" 35 ) 36 37 // namespaceStrategy implements behavior for Namespaces 38 type namespaceStrategy struct { 39 runtime.ObjectTyper 40 names.NameGenerator 41 } 42 43 // Strategy is the default logic that applies when creating and updating Namespace 44 // objects via the REST API. 45 var Strategy = namespaceStrategy{legacyscheme.Scheme, names.SimpleNameGenerator} 46 47 // NamespaceScoped is false for namespaces. 48 func (namespaceStrategy) NamespaceScoped() bool { 49 return false 50 } 51 52 // GetResetFields returns the set of fields that get reset by the strategy 53 // and should not be modified by the user. 54 func (namespaceStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set { 55 return map[fieldpath.APIVersion]*fieldpath.Set{ 56 "v1": fieldpath.NewSet( 57 fieldpath.MakePathOrDie("status"), 58 ), 59 } 60 } 61 62 // PrepareForCreate clears fields that are not allowed to be set by end users on creation. 63 func (namespaceStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { 64 // on create, status is active 65 namespace := obj.(*api.Namespace) 66 namespace.Status = api.NamespaceStatus{ 67 Phase: api.NamespaceActive, 68 } 69 // on create, we require the kubernetes value 70 // we cannot use this in defaults conversion because we let it get removed over life of object 71 hasKubeFinalizer := false 72 for i := range namespace.Spec.Finalizers { 73 if namespace.Spec.Finalizers[i] == api.FinalizerKubernetes { 74 hasKubeFinalizer = true 75 break 76 } 77 } 78 if !hasKubeFinalizer { 79 if len(namespace.Spec.Finalizers) == 0 { 80 namespace.Spec.Finalizers = []api.FinalizerName{api.FinalizerKubernetes} 81 } else { 82 namespace.Spec.Finalizers = append(namespace.Spec.Finalizers, api.FinalizerKubernetes) 83 } 84 } 85 } 86 87 // PrepareForUpdate clears fields that are not allowed to be set by end users on update. 88 func (namespaceStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { 89 newNamespace := obj.(*api.Namespace) 90 oldNamespace := old.(*api.Namespace) 91 newNamespace.Spec.Finalizers = oldNamespace.Spec.Finalizers 92 newNamespace.Status = oldNamespace.Status 93 } 94 95 // Validate validates a new namespace. 96 func (namespaceStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList { 97 namespace := obj.(*api.Namespace) 98 return validation.ValidateNamespace(namespace) 99 } 100 101 // WarningsOnCreate returns warnings for the creation of the given object. 102 func (namespaceStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string { 103 return nil 104 } 105 106 // Canonicalize normalizes the object after validation. 107 func (namespaceStrategy) Canonicalize(obj runtime.Object) { 108 // Ensure the label matches the name for namespaces just created using GenerateName, 109 // where the final name wasn't available for defaulting to make this change. 110 // This code needs to be kept in sync with the implementation that exists 111 // in Namespace defaulting (pkg/apis/core/v1) 112 // Why this hook *and* defaults.go? 113 // 114 // CREATE: 115 // Defaulting and PrepareForCreate happen before generateName is completed 116 // (i.e. the name is not yet known). Validation happens after generateName 117 // but should not modify objects. Canonicalize happens later, which makes 118 // it the best hook for setting the label. 119 // 120 // UPDATE: 121 // Defaulting and Canonicalize will both trigger with the full name. 122 // 123 // GET: 124 // Only defaulting will be applied. 125 ns := obj.(*api.Namespace) 126 if ns.Labels == nil { 127 ns.Labels = map[string]string{} 128 } 129 ns.Labels[v1.LabelMetadataName] = ns.Name 130 } 131 132 // AllowCreateOnUpdate is false for namespaces. 133 func (namespaceStrategy) AllowCreateOnUpdate() bool { 134 return false 135 } 136 137 // ValidateUpdate is the default update validation for an end user. 138 func (namespaceStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { 139 errorList := validation.ValidateNamespace(obj.(*api.Namespace)) 140 return append(errorList, validation.ValidateNamespaceUpdate(obj.(*api.Namespace), old.(*api.Namespace))...) 141 } 142 143 // WarningsOnUpdate returns warnings for the given update. 144 func (namespaceStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string { 145 return nil 146 } 147 148 func (namespaceStrategy) AllowUnconditionalUpdate() bool { 149 return true 150 } 151 152 type namespaceStatusStrategy struct { 153 namespaceStrategy 154 } 155 156 var StatusStrategy = namespaceStatusStrategy{Strategy} 157 158 // GetResetFields returns the set of fields that get reset by the strategy 159 // and should not be modified by the user. 160 func (namespaceStatusStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set { 161 return map[fieldpath.APIVersion]*fieldpath.Set{ 162 "v1": fieldpath.NewSet( 163 fieldpath.MakePathOrDie("spec"), 164 ), 165 } 166 } 167 168 func (namespaceStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { 169 newNamespace := obj.(*api.Namespace) 170 oldNamespace := old.(*api.Namespace) 171 newNamespace.Spec = oldNamespace.Spec 172 } 173 174 func (namespaceStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { 175 return validation.ValidateNamespaceStatusUpdate(obj.(*api.Namespace), old.(*api.Namespace)) 176 } 177 178 // WarningsOnUpdate returns warnings for the given update. 179 func (namespaceStatusStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string { 180 return nil 181 } 182 183 type namespaceFinalizeStrategy struct { 184 namespaceStrategy 185 } 186 187 var FinalizeStrategy = namespaceFinalizeStrategy{Strategy} 188 189 func (namespaceFinalizeStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { 190 return validation.ValidateNamespaceFinalizeUpdate(obj.(*api.Namespace), old.(*api.Namespace)) 191 } 192 193 // WarningsOnUpdate returns warnings for the given update. 194 func (namespaceFinalizeStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string { 195 return nil 196 } 197 198 // GetResetFields returns the set of fields that get reset by the strategy 199 // and should not be modified by the user. 200 func (namespaceFinalizeStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set { 201 return map[fieldpath.APIVersion]*fieldpath.Set{ 202 "v1": fieldpath.NewSet( 203 fieldpath.MakePathOrDie("status"), 204 ), 205 } 206 } 207 208 // PrepareForUpdate clears fields that are not allowed to be set by end users on update. 209 func (namespaceFinalizeStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { 210 newNamespace := obj.(*api.Namespace) 211 oldNamespace := old.(*api.Namespace) 212 newNamespace.Status = oldNamespace.Status 213 } 214 215 // GetAttrs returns labels and fields of a given object for filtering purposes. 216 func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) { 217 namespaceObj, ok := obj.(*api.Namespace) 218 if !ok { 219 return nil, nil, fmt.Errorf("not a namespace") 220 } 221 return labels.Set(namespaceObj.Labels), NamespaceToSelectableFields(namespaceObj), nil 222 } 223 224 // MatchNamespace returns a generic matcher for a given label and field selector. 225 func MatchNamespace(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate { 226 return apistorage.SelectionPredicate{ 227 Label: label, 228 Field: field, 229 GetAttrs: GetAttrs, 230 } 231 } 232 233 // NamespaceToSelectableFields returns a field set that represents the object 234 func NamespaceToSelectableFields(namespace *api.Namespace) fields.Set { 235 objectMetaFieldsSet := generic.ObjectMetaFieldsSet(&namespace.ObjectMeta, false) 236 specificFieldsSet := fields.Set{ 237 "status.phase": string(namespace.Status.Phase), 238 // This is a bug, but we need to support it for backward compatibility. 239 "name": namespace.Name, 240 } 241 return generic.MergeFieldsSets(objectMetaFieldsSet, specificFieldsSet) 242 }