Skip to content

Commit 15bec1d

Browse files
This commit adds a namespace enforcing wrapper for client.Client.
This helps while dealing with namespace-scoped objects, where the namespace value need not be specified in every operation.
1 parent 5c2b42d commit 15bec1d

File tree

2 files changed

+678
-0
lines changed

2 files changed

+678
-0
lines changed

pkg/client/namespaced_client.go

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
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+
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
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, rmp meta.RESTMapper, sch *runtime.Scheme) Client {
31+
return &namespacedClient{
32+
client: c,
33+
namespace: ns,
34+
restmapper: rmp,
35+
scheme: *sch,
36+
}
37+
}
38+
39+
var _ Client = &namespacedClient{}
40+
41+
// namespacedClient is a Client that wraps another Client in order to enforce the specified namespace value.
42+
type namespacedClient struct {
43+
namespace string
44+
client Client
45+
restmapper meta.RESTMapper
46+
scheme runtime.Scheme
47+
}
48+
49+
func getNamespace(restmapper meta.RESTMapper, obj runtime.Object, sch *runtime.Scheme) (bool, error) {
50+
// var sch = runtime.NewScheme()
51+
// // appsv1.AddToScheme(sch)
52+
// rbacv1.AddToScheme(sch)
53+
gvk, err := apiutil.GVKForObject(obj, sch)
54+
if err != nil {
55+
return false, err
56+
}
57+
if restmapper == nil {
58+
return false, err
59+
}
60+
61+
// gvk := schema.GroupKind{
62+
// Group: obj.GetObjectKind().GroupVersionKind().Group,
63+
// Kind: obj.GetObjectKind().GroupVersionKind().Kind,
64+
// }
65+
restmapping, err := restmapper.RESTMapping(gvk.GroupKind())
66+
if err != nil {
67+
return false, fmt.Errorf("error here restmapping %v", obj)
68+
}
69+
scope := restmapping.Scope.Name()
70+
71+
if scope == "" {
72+
return false, nil
73+
}
74+
75+
if scope != meta.RESTScopeNameNamespace {
76+
return true, nil
77+
}
78+
return false, nil
79+
}
80+
81+
// Create implements clinet.Client
82+
func (n *namespacedClient) Create(ctx context.Context, obj runtime.Object, opts ...CreateOption) error {
83+
metaObj, err := meta.Accessor(obj)
84+
if err != nil {
85+
return err
86+
}
87+
88+
isNamespaceScoped, err := getNamespace(n.restmapper, obj, &n.scheme)
89+
if err != nil {
90+
return fmt.Errorf("erroring Here, %v", err)
91+
}
92+
if isNamespaceScoped {
93+
metaObj.SetNamespace(n.namespace)
94+
}
95+
return n.client.Create(ctx, obj, opts...)
96+
}
97+
98+
// Update implements client.Client
99+
func (n *namespacedClient) Update(ctx context.Context, obj runtime.Object, opts ...UpdateOption) error {
100+
metaObj, err := meta.Accessor(obj)
101+
if err != nil {
102+
return err
103+
}
104+
105+
if n.namespace != "" && metaObj.GetNamespace() != n.namespace {
106+
metaObj.SetNamespace(n.namespace)
107+
}
108+
return n.client.Update(ctx, obj, opts...)
109+
}
110+
111+
// Delete implements client.Client
112+
func (n *namespacedClient) Delete(ctx context.Context, obj runtime.Object, opts ...DeleteOption) error {
113+
metaObj, err := meta.Accessor(obj)
114+
if err != nil {
115+
return err
116+
}
117+
118+
if n.namespace != "" && metaObj.GetNamespace() != n.namespace {
119+
metaObj.SetNamespace(n.namespace)
120+
}
121+
return n.client.Delete(ctx, obj, opts...)
122+
}
123+
124+
// DeleteAllOf implements client.Client
125+
func (n *namespacedClient) DeleteAllOf(ctx context.Context, obj runtime.Object, opts ...DeleteAllOfOption) error {
126+
if n.namespace != "" {
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 runtime.Object, patch Patch, opts ...PatchOption) error {
134+
metaObj, err := meta.Accessor(obj)
135+
if err != nil {
136+
return err
137+
}
138+
139+
if n.namespace != "" && metaObj.GetNamespace() != n.namespace {
140+
metaObj.SetNamespace(n.namespace)
141+
}
142+
return n.client.Patch(ctx, obj, patch, opts...)
143+
}
144+
145+
// Get implements client.Client
146+
func (n *namespacedClient) Get(ctx context.Context, key ObjectKey, obj runtime.Object) error {
147+
isNamespaceScoped, err := getNamespace(n.restmapper, obj, &n.scheme)
148+
if err != nil {
149+
return fmt.Errorf("erroring Here, %v %v", err, obj.GetObjectKind())
150+
}
151+
if isNamespaceScoped {
152+
key.Namespace = n.namespace
153+
}
154+
return n.client.Get(ctx, key, obj)
155+
}
156+
157+
// List implements client.Client
158+
func (n *namespacedClient) List(ctx context.Context, obj runtime.Object, opts ...ListOption) error {
159+
if n.namespace != "" {
160+
opts = append(opts, InNamespace(n.namespace))
161+
}
162+
return n.client.List(ctx, obj, opts...)
163+
}
164+
165+
// Status implements client.StatusClient
166+
func (n *namespacedClient) Status() StatusWriter {
167+
return &namespacedClientStatusWriter{client: n.client.Status(), namespace: n.namespace}
168+
}
169+
170+
// ensure namespacedClientStatusWriter implements client.StatusWriter
171+
var _ StatusWriter = &namespacedClientStatusWriter{}
172+
173+
type namespacedClientStatusWriter struct {
174+
client StatusWriter
175+
namespace string
176+
}
177+
178+
// Update implements client.StatusWriter
179+
func (nsw *namespacedClientStatusWriter) Update(ctx context.Context, obj runtime.Object, opts ...UpdateOption) error {
180+
metaObj, err := meta.Accessor(obj)
181+
if err != nil {
182+
return err
183+
}
184+
185+
if nsw.namespace != "" && metaObj.GetNamespace() != nsw.namespace {
186+
metaObj.SetNamespace(nsw.namespace)
187+
}
188+
return nsw.client.Update(ctx, obj, opts...)
189+
}
190+
191+
// Patch implements client.StatusWriter
192+
func (nsw *namespacedClientStatusWriter) Patch(ctx context.Context, obj runtime.Object, patch Patch, opts ...PatchOption) error {
193+
metaObj, err := meta.Accessor(obj)
194+
if err != nil {
195+
return err
196+
}
197+
198+
if nsw.namespace != "" && metaObj.GetNamespace() != nsw.namespace {
199+
metaObj.SetNamespace(nsw.namespace)
200+
}
201+
return nsw.client.Patch(ctx, obj, patch, opts...)
202+
}

0 commit comments

Comments
 (0)