1 package test
2
3 import (
4 "context"
5 "errors"
6
7 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
8 "k8s.io/apimachinery/pkg/runtime"
9 "k8s.io/apimachinery/pkg/runtime/schema"
10 "k8s.io/apimachinery/pkg/util/sets"
11 "sigs.k8s.io/controller-runtime/pkg/client"
12 "sigs.k8s.io/controller-runtime/pkg/client/apiutil"
13 "sigs.k8s.io/controller-runtime/pkg/client/fake"
14 )
15
16
17 type ErrorInjectingFakeClient struct {
18 client.WithWatch
19 withSubResourceSimulation bool
20 CustomSubResourceWriter *CustomSubResourceWriter
21 withObjects []client.Object
22 withStatusSubresource []client.Object
23 failOnCreate bool
24 failOnUpdate bool
25 failOnList bool
26 failOnDelete bool
27 failOnGet bool
28 failOnStatus bool
29 }
30
31 func (c *ErrorInjectingFakeClient) Status() client.SubResourceWriter {
32 if c.failOnStatus {
33
34 if c.CustomSubResourceWriter == nil {
35 return c.NewCustomSubResourceWriter()
36 }
37 return c.CustomSubResourceWriter
38 }
39 return c.WithWatch.Status()
40 }
41
42 func NewErrorInjectingFakeClient(scheme *runtime.Scheme, withSubResourceSimulation bool, initObjects ...client.Object) *ErrorInjectingFakeClient {
43 builder := fake.NewClientBuilder().
44 WithScheme(scheme).
45 WithObjects(initObjects...).
46 WithStatusSubresource(initObjects...)
47 return &ErrorInjectingFakeClient{
48 withSubResourceSimulation: withSubResourceSimulation,
49 WithWatch: builder.Build(),
50 withObjects: initObjects,
51 withStatusSubresource: initObjects,
52 }
53 }
54
55
56 var inTreeResourcesWithStatus = []schema.GroupVersionKind{
57 {Version: "v1", Kind: "Namespace"},
58 {Version: "v1", Kind: "Node"},
59 {Version: "v1", Kind: "PersistentVolumeClaim"},
60 {Version: "v1", Kind: "PersistentVolume"},
61 {Version: "v1", Kind: "Pod"},
62 {Version: "v1", Kind: "ReplicationController"},
63 {Version: "v1", Kind: "Service"},
64
65 {Group: "apps", Version: "v1", Kind: "Deployment"},
66 {Group: "apps", Version: "v1", Kind: "DaemonSet"},
67 {Group: "apps", Version: "v1", Kind: "ReplicaSet"},
68 {Group: "apps", Version: "v1", Kind: "StatefulSet"},
69
70 {Group: "autoscaling", Version: "v1", Kind: "HorizontalPodAutoscaler"},
71
72 {Group: "batch", Version: "v1", Kind: "CronJob"},
73 {Group: "batch", Version: "v1", Kind: "Job"},
74
75 {Group: "certificates.k8s.io", Version: "v1", Kind: "CertificateSigningRequest"},
76
77 {Group: "networking.k8s.io", Version: "v1", Kind: "Ingress"},
78 {Group: "networking.k8s.io", Version: "v1", Kind: "NetworkPolicy"},
79
80 {Group: "policy", Version: "v1", Kind: "PodDisruptionBudget"},
81
82 {Group: "storage.k8s.io", Version: "v1", Kind: "VolumeAttachment"},
83
84 {Group: "apiextensions.k8s.io", Version: "v1", Kind: "CustomResourceDefinition"},
85
86 {Group: "flowcontrol.apiserver.k8s.io", Version: "v1beta2", Kind: "FlowSchema"},
87 {Group: "flowcontrol.apiserver.k8s.io", Version: "v1beta2", Kind: "PriorityLevelConfiguration"},
88 }
89
90 func (c *ErrorInjectingFakeClient) Create(ctx context.Context, obj client.Object, opts ...client.CreateOption) error {
91 if c.failOnCreate {
92 return errors.New("create error")
93 }
94
95 if err := c.WithWatch.Create(ctx, obj, opts...); err != nil {
96 return err
97 }
98 if !c.withSubResourceSimulation {
99 return nil
100 }
101
102 c.withObjects = append(c.withObjects, obj)
103 err := c.recreateFake(ctx)
104 if err != nil {
105 return err
106 }
107
108 return nil
109 }
110
111 func (c *ErrorInjectingFakeClient) Delete(ctx context.Context, obj client.Object, opts ...client.DeleteOption) error {
112 if c.failOnDelete {
113 return errors.New("delete error")
114 }
115
116 if err := c.WithWatch.Delete(ctx, obj, opts...); err != nil {
117 return err
118 }
119
120 return nil
121 }
122
123 func (c *ErrorInjectingFakeClient) Update(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error {
124 if c.failOnUpdate {
125 return errors.New("update error")
126 }
127 return c.WithWatch.Update(ctx, obj, opts...)
128 }
129
130
131 func (c *ErrorInjectingFakeClient) List(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error {
132 if c.failOnList {
133 return errors.New("unsupported list type")
134 }
135 return c.WithWatch.List(ctx, list, opts...)
136 }
137
138
139 func (c *ErrorInjectingFakeClient) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error {
140 if c.failOnGet {
141 return errors.New("unsupported get type")
142 }
143 return c.WithWatch.Get(ctx, key, obj, opts...)
144 }
145
146 func (c *ErrorInjectingFakeClient) recreateFake(ctx context.Context) error {
147
148
149
150
151
152
153
154 gvks := sets.New(inTreeResourcesWithStatus...)
155 for _, o := range c.withObjects {
156 gvk, err := apiutil.GVKForObject(o, c.Scheme())
157 if err != nil {
158 return err
159 }
160 gvks.Insert(gvk)
161 }
162
163 var objs []client.Object
164 for _, gvk := range gvks.UnsortedList() {
165 objList := &unstructured.UnstructuredList{}
166 objList.SetGroupVersionKind(schema.GroupVersionKind{Group: gvk.Group, Version: gvk.Version, Kind: gvk.Kind})
167
168 err := c.List(ctx, objList)
169 if err != nil {
170 return err
171 }
172
173 for _, o := range objList.Items {
174 objs = append(objs, o.DeepCopy())
175 }
176 }
177
178 initObjs := sets.New(objs...).UnsortedList()
179 c.withObjects = initObjs
180 c.withStatusSubresource = initObjs
181
182 c.WithWatch = fake.NewClientBuilder().
183 WithScheme(c.Scheme()).
184 WithObjects(c.withObjects...).
185 WithStatusSubresource(c.withStatusSubresource...).
186 Build()
187 return nil
188 }
189
190
191 func (c *ErrorInjectingFakeClient) SetFailOnCreate() {
192 c.failOnCreate = true
193 }
194
195
196 func (c *ErrorInjectingFakeClient) SetFailOnUpdate() {
197 c.failOnUpdate = true
198 }
199
200
201 func (c *ErrorInjectingFakeClient) SetFailOnList() {
202 c.failOnList = true
203 }
204
205
206 func (c *ErrorInjectingFakeClient) SetFailOnDelete() {
207 c.failOnDelete = true
208 }
209
210
211 func (c *ErrorInjectingFakeClient) SetFailOnGet() {
212 c.failOnGet = true
213 }
214
215
216 func (c *ErrorInjectingFakeClient) SetFailOnStatus() {
217 c.failOnStatus = true
218 }
219
220
221 type CustomSubResourceWriter struct {
222 client client.Client
223 failOnUpdate bool
224 failOnCreate bool
225 failOnPatch bool
226 }
227
228
229 func (w *CustomSubResourceWriter) SetFailOnUpdate() {
230 w.failOnUpdate = true
231 }
232
233
234 func (w *CustomSubResourceWriter) SetFailOnCreate() {
235 w.failOnCreate = true
236 }
237
238
239 func (w *CustomSubResourceWriter) SetFailOnPatch() {
240 w.failOnPatch = true
241 }
242
243 func (w *CustomSubResourceWriter) Create(ctx context.Context, obj client.Object, subResource client.Object, opts ...client.SubResourceCreateOption) error {
244 if w.failOnCreate {
245 return errors.New("create error")
246 }
247 return w.client.SubResource(obj.GetResourceVersion()).Create(ctx, obj, subResource, opts...)
248 }
249
250 func (w *CustomSubResourceWriter) Update(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error {
251 if w.failOnUpdate {
252 return errors.New("update error")
253 }
254 return w.client.SubResource(obj.GetResourceVersion()).Update(ctx, obj, opts...)
255 }
256
257 func (w *CustomSubResourceWriter) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.SubResourcePatchOption) error {
258 if w.failOnPatch {
259 return errors.New("patch error")
260 }
261 return w.client.SubResource(obj.GetResourceVersion()).Patch(ctx, obj, patch, opts...)
262 }
263
264 func (c *ErrorInjectingFakeClient) NewCustomSubResourceWriter() *CustomSubResourceWriter {
265 c.CustomSubResourceWriter = &CustomSubResourceWriter{
266 client: c.WithWatch,
267 }
268 return c.CustomSubResourceWriter
269 }
270
View as plain text