sigs.k8s.io/controller-runtime@v0.18.2/pkg/client/namespaced_client.go (about) 1 /* 2 Copyright 2020 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 client 18 19 import ( 20 "context" 21 "fmt" 22 23 "k8s.io/apimachinery/pkg/api/meta" 24 "k8s.io/apimachinery/pkg/runtime" 25 "k8s.io/apimachinery/pkg/runtime/schema" 26 ) 27 28 // NewNamespacedClient wraps an existing client enforcing the namespace value. 29 // All functions using this client will have the same namespace declared here. 30 func NewNamespacedClient(c Client, ns string) Client { 31 return &namespacedClient{ 32 client: c, 33 namespace: ns, 34 } 35 } 36 37 var _ Client = &namespacedClient{} 38 39 // namespacedClient is a Client that wraps another Client in order to enforce the specified namespace value. 40 type namespacedClient struct { 41 namespace string 42 client Client 43 } 44 45 // Scheme returns the scheme this client is using. 46 func (n *namespacedClient) Scheme() *runtime.Scheme { 47 return n.client.Scheme() 48 } 49 50 // RESTMapper returns the scheme this client is using. 51 func (n *namespacedClient) RESTMapper() meta.RESTMapper { 52 return n.client.RESTMapper() 53 } 54 55 // GroupVersionKindFor returns the GroupVersionKind for the given object. 56 func (n *namespacedClient) GroupVersionKindFor(obj runtime.Object) (schema.GroupVersionKind, error) { 57 return n.client.GroupVersionKindFor(obj) 58 } 59 60 // IsObjectNamespaced returns true if the GroupVersionKind of the object is namespaced. 61 func (n *namespacedClient) IsObjectNamespaced(obj runtime.Object) (bool, error) { 62 return n.client.IsObjectNamespaced(obj) 63 } 64 65 // Create implements client.Client. 66 func (n *namespacedClient) Create(ctx context.Context, obj Object, opts ...CreateOption) error { 67 isNamespaceScoped, err := n.IsObjectNamespaced(obj) 68 if err != nil { 69 return fmt.Errorf("error finding the scope of the object: %w", err) 70 } 71 72 objectNamespace := obj.GetNamespace() 73 if objectNamespace != n.namespace && objectNamespace != "" { 74 return fmt.Errorf("namespace %s of the object %s does not match the namespace %s on the client", objectNamespace, obj.GetName(), n.namespace) 75 } 76 77 if isNamespaceScoped && objectNamespace == "" { 78 obj.SetNamespace(n.namespace) 79 } 80 return n.client.Create(ctx, obj, opts...) 81 } 82 83 // Update implements client.Client. 84 func (n *namespacedClient) Update(ctx context.Context, obj Object, opts ...UpdateOption) error { 85 isNamespaceScoped, err := n.IsObjectNamespaced(obj) 86 if err != nil { 87 return fmt.Errorf("error finding the scope of the object: %w", err) 88 } 89 90 objectNamespace := obj.GetNamespace() 91 if objectNamespace != n.namespace && objectNamespace != "" { 92 return fmt.Errorf("namespace %s of the object %s does not match the namespace %s on the client", objectNamespace, obj.GetName(), n.namespace) 93 } 94 95 if isNamespaceScoped && objectNamespace == "" { 96 obj.SetNamespace(n.namespace) 97 } 98 return n.client.Update(ctx, obj, opts...) 99 } 100 101 // Delete implements client.Client. 102 func (n *namespacedClient) Delete(ctx context.Context, obj Object, opts ...DeleteOption) error { 103 isNamespaceScoped, err := n.IsObjectNamespaced(obj) 104 if err != nil { 105 return fmt.Errorf("error finding the scope of the object: %w", err) 106 } 107 108 objectNamespace := obj.GetNamespace() 109 if objectNamespace != n.namespace && objectNamespace != "" { 110 return fmt.Errorf("namespace %s of the object %s does not match the namespace %s on the client", objectNamespace, obj.GetName(), n.namespace) 111 } 112 113 if isNamespaceScoped && objectNamespace == "" { 114 obj.SetNamespace(n.namespace) 115 } 116 return n.client.Delete(ctx, obj, opts...) 117 } 118 119 // DeleteAllOf implements client.Client. 120 func (n *namespacedClient) DeleteAllOf(ctx context.Context, obj Object, opts ...DeleteAllOfOption) error { 121 isNamespaceScoped, err := n.IsObjectNamespaced(obj) 122 if err != nil { 123 return fmt.Errorf("error finding the scope of the object: %w", err) 124 } 125 126 if isNamespaceScoped { 127 opts = append(opts, InNamespace(n.namespace)) 128 } 129 return n.client.DeleteAllOf(ctx, obj, opts...) 130 } 131 132 // Patch implements client.Client. 133 func (n *namespacedClient) Patch(ctx context.Context, obj Object, patch Patch, opts ...PatchOption) error { 134 isNamespaceScoped, err := n.IsObjectNamespaced(obj) 135 if err != nil { 136 return fmt.Errorf("error finding the scope of the object: %w", err) 137 } 138 139 objectNamespace := obj.GetNamespace() 140 if objectNamespace != n.namespace && objectNamespace != "" { 141 return fmt.Errorf("namespace %s of the object %s does not match the namespace %s on the client", objectNamespace, obj.GetName(), n.namespace) 142 } 143 144 if isNamespaceScoped && objectNamespace == "" { 145 obj.SetNamespace(n.namespace) 146 } 147 return n.client.Patch(ctx, obj, patch, opts...) 148 } 149 150 // Get implements client.Client. 151 func (n *namespacedClient) Get(ctx context.Context, key ObjectKey, obj Object, opts ...GetOption) error { 152 isNamespaceScoped, err := n.IsObjectNamespaced(obj) 153 if err != nil { 154 return fmt.Errorf("error finding the scope of the object: %w", err) 155 } 156 if isNamespaceScoped { 157 if key.Namespace != "" && key.Namespace != n.namespace { 158 return fmt.Errorf("namespace %s provided for the object %s does not match the namespace %s on the client", key.Namespace, obj.GetName(), n.namespace) 159 } 160 key.Namespace = n.namespace 161 } 162 return n.client.Get(ctx, key, obj, opts...) 163 } 164 165 // List implements client.Client. 166 func (n *namespacedClient) List(ctx context.Context, obj ObjectList, opts ...ListOption) error { 167 if n.namespace != "" { 168 opts = append(opts, InNamespace(n.namespace)) 169 } 170 return n.client.List(ctx, obj, opts...) 171 } 172 173 // Status implements client.StatusClient. 174 func (n *namespacedClient) Status() SubResourceWriter { 175 return n.SubResource("status") 176 } 177 178 // SubResource implements client.SubResourceClient. 179 func (n *namespacedClient) SubResource(subResource string) SubResourceClient { 180 return &namespacedClientSubResourceClient{client: n.client.SubResource(subResource), namespace: n.namespace, namespacedclient: n} 181 } 182 183 // ensure namespacedClientSubResourceClient implements client.SubResourceClient. 184 var _ SubResourceClient = &namespacedClientSubResourceClient{} 185 186 type namespacedClientSubResourceClient struct { 187 client SubResourceClient 188 namespace string 189 namespacedclient Client 190 } 191 192 func (nsw *namespacedClientSubResourceClient) Get(ctx context.Context, obj, subResource Object, opts ...SubResourceGetOption) error { 193 isNamespaceScoped, err := nsw.namespacedclient.IsObjectNamespaced(obj) 194 if err != nil { 195 return fmt.Errorf("error finding the scope of the object: %w", err) 196 } 197 198 objectNamespace := obj.GetNamespace() 199 if objectNamespace != nsw.namespace && objectNamespace != "" { 200 return fmt.Errorf("namespace %s of the object %s does not match the namespace %s on the client", objectNamespace, obj.GetName(), nsw.namespace) 201 } 202 203 if isNamespaceScoped && objectNamespace == "" { 204 obj.SetNamespace(nsw.namespace) 205 } 206 207 return nsw.client.Get(ctx, obj, subResource, opts...) 208 } 209 210 func (nsw *namespacedClientSubResourceClient) Create(ctx context.Context, obj, subResource Object, opts ...SubResourceCreateOption) error { 211 isNamespaceScoped, err := nsw.namespacedclient.IsObjectNamespaced(obj) 212 if err != nil { 213 return fmt.Errorf("error finding the scope of the object: %w", err) 214 } 215 216 objectNamespace := obj.GetNamespace() 217 if objectNamespace != nsw.namespace && objectNamespace != "" { 218 return fmt.Errorf("namespace %s of the object %s does not match the namespace %s on the client", objectNamespace, obj.GetName(), nsw.namespace) 219 } 220 221 if isNamespaceScoped && objectNamespace == "" { 222 obj.SetNamespace(nsw.namespace) 223 } 224 225 return nsw.client.Create(ctx, obj, subResource, opts...) 226 } 227 228 // Update implements client.SubResourceWriter. 229 func (nsw *namespacedClientSubResourceClient) Update(ctx context.Context, obj Object, opts ...SubResourceUpdateOption) error { 230 isNamespaceScoped, err := nsw.namespacedclient.IsObjectNamespaced(obj) 231 if err != nil { 232 return fmt.Errorf("error finding the scope of the object: %w", err) 233 } 234 235 objectNamespace := obj.GetNamespace() 236 if objectNamespace != nsw.namespace && objectNamespace != "" { 237 return fmt.Errorf("namespace %s of the object %s does not match the namespace %s on the client", objectNamespace, obj.GetName(), nsw.namespace) 238 } 239 240 if isNamespaceScoped && objectNamespace == "" { 241 obj.SetNamespace(nsw.namespace) 242 } 243 return nsw.client.Update(ctx, obj, opts...) 244 } 245 246 // Patch implements client.SubResourceWriter. 247 func (nsw *namespacedClientSubResourceClient) Patch(ctx context.Context, obj Object, patch Patch, opts ...SubResourcePatchOption) error { 248 isNamespaceScoped, err := nsw.namespacedclient.IsObjectNamespaced(obj) 249 if err != nil { 250 return fmt.Errorf("error finding the scope of the object: %w", err) 251 } 252 253 objectNamespace := obj.GetNamespace() 254 if objectNamespace != nsw.namespace && objectNamespace != "" { 255 return fmt.Errorf("namespace %s of the object %s does not match the namespace %s on the client", objectNamespace, obj.GetName(), nsw.namespace) 256 } 257 258 if isNamespaceScoped && objectNamespace == "" { 259 obj.SetNamespace(nsw.namespace) 260 } 261 return nsw.client.Patch(ctx, obj, patch, opts...) 262 }