1/*
2Copyright 2015 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package testing
18
19import (
20	"fmt"
21	"path"
22	"strings"
23
24	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
25	"k8s.io/apimachinery/pkg/fields"
26	"k8s.io/apimachinery/pkg/labels"
27	"k8s.io/apimachinery/pkg/runtime"
28	"k8s.io/apimachinery/pkg/runtime/schema"
29	"k8s.io/apimachinery/pkg/types"
30)
31
32func NewRootGetAction(resource schema.GroupVersionResource, name string) GetActionImpl {
33	action := GetActionImpl{}
34	action.Verb = "get"
35	action.Resource = resource
36	action.Name = name
37
38	return action
39}
40
41func NewGetAction(resource schema.GroupVersionResource, namespace, name string) GetActionImpl {
42	action := GetActionImpl{}
43	action.Verb = "get"
44	action.Resource = resource
45	action.Namespace = namespace
46	action.Name = name
47
48	return action
49}
50
51func NewGetSubresourceAction(resource schema.GroupVersionResource, namespace, subresource, name string) GetActionImpl {
52	action := GetActionImpl{}
53	action.Verb = "get"
54	action.Resource = resource
55	action.Subresource = subresource
56	action.Namespace = namespace
57	action.Name = name
58
59	return action
60}
61
62func NewRootGetSubresourceAction(resource schema.GroupVersionResource, subresource, name string) GetActionImpl {
63	action := GetActionImpl{}
64	action.Verb = "get"
65	action.Resource = resource
66	action.Subresource = subresource
67	action.Name = name
68
69	return action
70}
71
72func NewRootListAction(resource schema.GroupVersionResource, kind schema.GroupVersionKind, opts interface{}) ListActionImpl {
73	action := ListActionImpl{}
74	action.Verb = "list"
75	action.Resource = resource
76	action.Kind = kind
77	labelSelector, fieldSelector, _ := ExtractFromListOptions(opts)
78	action.ListRestrictions = ListRestrictions{labelSelector, fieldSelector}
79
80	return action
81}
82
83func NewListAction(resource schema.GroupVersionResource, kind schema.GroupVersionKind, namespace string, opts interface{}) ListActionImpl {
84	action := ListActionImpl{}
85	action.Verb = "list"
86	action.Resource = resource
87	action.Kind = kind
88	action.Namespace = namespace
89	labelSelector, fieldSelector, _ := ExtractFromListOptions(opts)
90	action.ListRestrictions = ListRestrictions{labelSelector, fieldSelector}
91
92	return action
93}
94
95func NewRootCreateAction(resource schema.GroupVersionResource, object runtime.Object) CreateActionImpl {
96	action := CreateActionImpl{}
97	action.Verb = "create"
98	action.Resource = resource
99	action.Object = object
100
101	return action
102}
103
104func NewCreateAction(resource schema.GroupVersionResource, namespace string, object runtime.Object) CreateActionImpl {
105	action := CreateActionImpl{}
106	action.Verb = "create"
107	action.Resource = resource
108	action.Namespace = namespace
109	action.Object = object
110
111	return action
112}
113
114func NewRootCreateSubresourceAction(resource schema.GroupVersionResource, name, subresource string, object runtime.Object) CreateActionImpl {
115	action := CreateActionImpl{}
116	action.Verb = "create"
117	action.Resource = resource
118	action.Subresource = subresource
119	action.Name = name
120	action.Object = object
121
122	return action
123}
124
125func NewCreateSubresourceAction(resource schema.GroupVersionResource, name, subresource, namespace string, object runtime.Object) CreateActionImpl {
126	action := CreateActionImpl{}
127	action.Verb = "create"
128	action.Resource = resource
129	action.Namespace = namespace
130	action.Subresource = subresource
131	action.Name = name
132	action.Object = object
133
134	return action
135}
136
137func NewRootUpdateAction(resource schema.GroupVersionResource, object runtime.Object) UpdateActionImpl {
138	action := UpdateActionImpl{}
139	action.Verb = "update"
140	action.Resource = resource
141	action.Object = object
142
143	return action
144}
145
146func NewUpdateAction(resource schema.GroupVersionResource, namespace string, object runtime.Object) UpdateActionImpl {
147	action := UpdateActionImpl{}
148	action.Verb = "update"
149	action.Resource = resource
150	action.Namespace = namespace
151	action.Object = object
152
153	return action
154}
155
156func NewRootPatchAction(resource schema.GroupVersionResource, name string, pt types.PatchType, patch []byte) PatchActionImpl {
157	action := PatchActionImpl{}
158	action.Verb = "patch"
159	action.Resource = resource
160	action.Name = name
161	action.PatchType = pt
162	action.Patch = patch
163
164	return action
165}
166
167func NewPatchAction(resource schema.GroupVersionResource, namespace string, name string, pt types.PatchType, patch []byte) PatchActionImpl {
168	action := PatchActionImpl{}
169	action.Verb = "patch"
170	action.Resource = resource
171	action.Namespace = namespace
172	action.Name = name
173	action.PatchType = pt
174	action.Patch = patch
175
176	return action
177}
178
179func NewRootPatchSubresourceAction(resource schema.GroupVersionResource, name string, pt types.PatchType, patch []byte, subresources ...string) PatchActionImpl {
180	action := PatchActionImpl{}
181	action.Verb = "patch"
182	action.Resource = resource
183	action.Subresource = path.Join(subresources...)
184	action.Name = name
185	action.PatchType = pt
186	action.Patch = patch
187
188	return action
189}
190
191func NewPatchSubresourceAction(resource schema.GroupVersionResource, namespace, name string, pt types.PatchType, patch []byte, subresources ...string) PatchActionImpl {
192	action := PatchActionImpl{}
193	action.Verb = "patch"
194	action.Resource = resource
195	action.Subresource = path.Join(subresources...)
196	action.Namespace = namespace
197	action.Name = name
198	action.PatchType = pt
199	action.Patch = patch
200
201	return action
202}
203
204func NewRootUpdateSubresourceAction(resource schema.GroupVersionResource, subresource string, object runtime.Object) UpdateActionImpl {
205	action := UpdateActionImpl{}
206	action.Verb = "update"
207	action.Resource = resource
208	action.Subresource = subresource
209	action.Object = object
210
211	return action
212}
213func NewUpdateSubresourceAction(resource schema.GroupVersionResource, subresource string, namespace string, object runtime.Object) UpdateActionImpl {
214	action := UpdateActionImpl{}
215	action.Verb = "update"
216	action.Resource = resource
217	action.Subresource = subresource
218	action.Namespace = namespace
219	action.Object = object
220
221	return action
222}
223
224func NewRootDeleteAction(resource schema.GroupVersionResource, name string) DeleteActionImpl {
225	action := DeleteActionImpl{}
226	action.Verb = "delete"
227	action.Resource = resource
228	action.Name = name
229
230	return action
231}
232
233func NewRootDeleteSubresourceAction(resource schema.GroupVersionResource, subresource string, name string) DeleteActionImpl {
234	action := DeleteActionImpl{}
235	action.Verb = "delete"
236	action.Resource = resource
237	action.Subresource = subresource
238	action.Name = name
239
240	return action
241}
242
243func NewDeleteAction(resource schema.GroupVersionResource, namespace, name string) DeleteActionImpl {
244	action := DeleteActionImpl{}
245	action.Verb = "delete"
246	action.Resource = resource
247	action.Namespace = namespace
248	action.Name = name
249
250	return action
251}
252
253func NewDeleteSubresourceAction(resource schema.GroupVersionResource, subresource, namespace, name string) DeleteActionImpl {
254	action := DeleteActionImpl{}
255	action.Verb = "delete"
256	action.Resource = resource
257	action.Subresource = subresource
258	action.Namespace = namespace
259	action.Name = name
260
261	return action
262}
263
264func NewRootDeleteCollectionAction(resource schema.GroupVersionResource, opts interface{}) DeleteCollectionActionImpl {
265	action := DeleteCollectionActionImpl{}
266	action.Verb = "delete-collection"
267	action.Resource = resource
268	labelSelector, fieldSelector, _ := ExtractFromListOptions(opts)
269	action.ListRestrictions = ListRestrictions{labelSelector, fieldSelector}
270
271	return action
272}
273
274func NewDeleteCollectionAction(resource schema.GroupVersionResource, namespace string, opts interface{}) DeleteCollectionActionImpl {
275	action := DeleteCollectionActionImpl{}
276	action.Verb = "delete-collection"
277	action.Resource = resource
278	action.Namespace = namespace
279	labelSelector, fieldSelector, _ := ExtractFromListOptions(opts)
280	action.ListRestrictions = ListRestrictions{labelSelector, fieldSelector}
281
282	return action
283}
284
285func NewRootWatchAction(resource schema.GroupVersionResource, opts interface{}) WatchActionImpl {
286	action := WatchActionImpl{}
287	action.Verb = "watch"
288	action.Resource = resource
289	labelSelector, fieldSelector, resourceVersion := ExtractFromListOptions(opts)
290	action.WatchRestrictions = WatchRestrictions{labelSelector, fieldSelector, resourceVersion}
291
292	return action
293}
294
295func ExtractFromListOptions(opts interface{}) (labelSelector labels.Selector, fieldSelector fields.Selector, resourceVersion string) {
296	var err error
297	switch t := opts.(type) {
298	case metav1.ListOptions:
299		labelSelector, err = labels.Parse(t.LabelSelector)
300		if err != nil {
301			panic(fmt.Errorf("invalid selector %q: %v", t.LabelSelector, err))
302		}
303		fieldSelector, err = fields.ParseSelector(t.FieldSelector)
304		if err != nil {
305			panic(fmt.Errorf("invalid selector %q: %v", t.FieldSelector, err))
306		}
307		resourceVersion = t.ResourceVersion
308	default:
309		panic(fmt.Errorf("expect a ListOptions %T", opts))
310	}
311	if labelSelector == nil {
312		labelSelector = labels.Everything()
313	}
314	if fieldSelector == nil {
315		fieldSelector = fields.Everything()
316	}
317	return labelSelector, fieldSelector, resourceVersion
318}
319
320func NewWatchAction(resource schema.GroupVersionResource, namespace string, opts interface{}) WatchActionImpl {
321	action := WatchActionImpl{}
322	action.Verb = "watch"
323	action.Resource = resource
324	action.Namespace = namespace
325	labelSelector, fieldSelector, resourceVersion := ExtractFromListOptions(opts)
326	action.WatchRestrictions = WatchRestrictions{labelSelector, fieldSelector, resourceVersion}
327
328	return action
329}
330
331func NewProxyGetAction(resource schema.GroupVersionResource, namespace, scheme, name, port, path string, params map[string]string) ProxyGetActionImpl {
332	action := ProxyGetActionImpl{}
333	action.Verb = "get"
334	action.Resource = resource
335	action.Namespace = namespace
336	action.Scheme = scheme
337	action.Name = name
338	action.Port = port
339	action.Path = path
340	action.Params = params
341	return action
342}
343
344type ListRestrictions struct {
345	Labels labels.Selector
346	Fields fields.Selector
347}
348type WatchRestrictions struct {
349	Labels          labels.Selector
350	Fields          fields.Selector
351	ResourceVersion string
352}
353
354type Action interface {
355	GetNamespace() string
356	GetVerb() string
357	GetResource() schema.GroupVersionResource
358	GetSubresource() string
359	Matches(verb, resource string) bool
360
361	// DeepCopy is used to copy an action to avoid any risk of accidental mutation.  Most people never need to call this
362	// because the invocation logic deep copies before calls to storage and reactors.
363	DeepCopy() Action
364}
365
366type GenericAction interface {
367	Action
368	GetValue() interface{}
369}
370
371type GetAction interface {
372	Action
373	GetName() string
374}
375
376type ListAction interface {
377	Action
378	GetListRestrictions() ListRestrictions
379}
380
381type CreateAction interface {
382	Action
383	GetObject() runtime.Object
384}
385
386type UpdateAction interface {
387	Action
388	GetObject() runtime.Object
389}
390
391type DeleteAction interface {
392	Action
393	GetName() string
394}
395
396type DeleteCollectionAction interface {
397	Action
398	GetListRestrictions() ListRestrictions
399}
400
401type PatchAction interface {
402	Action
403	GetName() string
404	GetPatchType() types.PatchType
405	GetPatch() []byte
406}
407
408type WatchAction interface {
409	Action
410	GetWatchRestrictions() WatchRestrictions
411}
412
413type ProxyGetAction interface {
414	Action
415	GetScheme() string
416	GetName() string
417	GetPort() string
418	GetPath() string
419	GetParams() map[string]string
420}
421
422type ActionImpl struct {
423	Namespace   string
424	Verb        string
425	Resource    schema.GroupVersionResource
426	Subresource string
427}
428
429func (a ActionImpl) GetNamespace() string {
430	return a.Namespace
431}
432func (a ActionImpl) GetVerb() string {
433	return a.Verb
434}
435func (a ActionImpl) GetResource() schema.GroupVersionResource {
436	return a.Resource
437}
438func (a ActionImpl) GetSubresource() string {
439	return a.Subresource
440}
441func (a ActionImpl) Matches(verb, resource string) bool {
442	// Stay backwards compatible.
443	if !strings.Contains(resource, "/") {
444		return strings.EqualFold(verb, a.Verb) &&
445			strings.EqualFold(resource, a.Resource.Resource)
446	}
447
448	parts := strings.SplitN(resource, "/", 2)
449	topresource, subresource := parts[0], parts[1]
450
451	return strings.EqualFold(verb, a.Verb) &&
452		strings.EqualFold(topresource, a.Resource.Resource) &&
453		strings.EqualFold(subresource, a.Subresource)
454}
455func (a ActionImpl) DeepCopy() Action {
456	ret := a
457	return ret
458}
459
460type GenericActionImpl struct {
461	ActionImpl
462	Value interface{}
463}
464
465func (a GenericActionImpl) GetValue() interface{} {
466	return a.Value
467}
468
469func (a GenericActionImpl) DeepCopy() Action {
470	return GenericActionImpl{
471		ActionImpl: a.ActionImpl.DeepCopy().(ActionImpl),
472		// TODO this is wrong, but no worse than before
473		Value: a.Value,
474	}
475}
476
477type GetActionImpl struct {
478	ActionImpl
479	Name string
480}
481
482func (a GetActionImpl) GetName() string {
483	return a.Name
484}
485
486func (a GetActionImpl) DeepCopy() Action {
487	return GetActionImpl{
488		ActionImpl: a.ActionImpl.DeepCopy().(ActionImpl),
489		Name:       a.Name,
490	}
491}
492
493type ListActionImpl struct {
494	ActionImpl
495	Kind             schema.GroupVersionKind
496	Name             string
497	ListRestrictions ListRestrictions
498}
499
500func (a ListActionImpl) GetKind() schema.GroupVersionKind {
501	return a.Kind
502}
503
504func (a ListActionImpl) GetListRestrictions() ListRestrictions {
505	return a.ListRestrictions
506}
507
508func (a ListActionImpl) DeepCopy() Action {
509	return ListActionImpl{
510		ActionImpl: a.ActionImpl.DeepCopy().(ActionImpl),
511		Kind:       a.Kind,
512		Name:       a.Name,
513		ListRestrictions: ListRestrictions{
514			Labels: a.ListRestrictions.Labels.DeepCopySelector(),
515			Fields: a.ListRestrictions.Fields.DeepCopySelector(),
516		},
517	}
518}
519
520type CreateActionImpl struct {
521	ActionImpl
522	Name   string
523	Object runtime.Object
524}
525
526func (a CreateActionImpl) GetObject() runtime.Object {
527	return a.Object
528}
529
530func (a CreateActionImpl) DeepCopy() Action {
531	return CreateActionImpl{
532		ActionImpl: a.ActionImpl.DeepCopy().(ActionImpl),
533		Name:       a.Name,
534		Object:     a.Object.DeepCopyObject(),
535	}
536}
537
538type UpdateActionImpl struct {
539	ActionImpl
540	Object runtime.Object
541}
542
543func (a UpdateActionImpl) GetObject() runtime.Object {
544	return a.Object
545}
546
547func (a UpdateActionImpl) DeepCopy() Action {
548	return UpdateActionImpl{
549		ActionImpl: a.ActionImpl.DeepCopy().(ActionImpl),
550		Object:     a.Object.DeepCopyObject(),
551	}
552}
553
554type PatchActionImpl struct {
555	ActionImpl
556	Name      string
557	PatchType types.PatchType
558	Patch     []byte
559}
560
561func (a PatchActionImpl) GetName() string {
562	return a.Name
563}
564
565func (a PatchActionImpl) GetPatch() []byte {
566	return a.Patch
567}
568
569func (a PatchActionImpl) GetPatchType() types.PatchType {
570	return a.PatchType
571}
572
573func (a PatchActionImpl) DeepCopy() Action {
574	patch := make([]byte, len(a.Patch))
575	copy(patch, a.Patch)
576	return PatchActionImpl{
577		ActionImpl: a.ActionImpl.DeepCopy().(ActionImpl),
578		Name:       a.Name,
579		PatchType:  a.PatchType,
580		Patch:      patch,
581	}
582}
583
584type DeleteActionImpl struct {
585	ActionImpl
586	Name string
587}
588
589func (a DeleteActionImpl) GetName() string {
590	return a.Name
591}
592
593func (a DeleteActionImpl) DeepCopy() Action {
594	return DeleteActionImpl{
595		ActionImpl: a.ActionImpl.DeepCopy().(ActionImpl),
596		Name:       a.Name,
597	}
598}
599
600type DeleteCollectionActionImpl struct {
601	ActionImpl
602	ListRestrictions ListRestrictions
603}
604
605func (a DeleteCollectionActionImpl) GetListRestrictions() ListRestrictions {
606	return a.ListRestrictions
607}
608
609func (a DeleteCollectionActionImpl) DeepCopy() Action {
610	return DeleteCollectionActionImpl{
611		ActionImpl: a.ActionImpl.DeepCopy().(ActionImpl),
612		ListRestrictions: ListRestrictions{
613			Labels: a.ListRestrictions.Labels.DeepCopySelector(),
614			Fields: a.ListRestrictions.Fields.DeepCopySelector(),
615		},
616	}
617}
618
619type WatchActionImpl struct {
620	ActionImpl
621	WatchRestrictions WatchRestrictions
622}
623
624func (a WatchActionImpl) GetWatchRestrictions() WatchRestrictions {
625	return a.WatchRestrictions
626}
627
628func (a WatchActionImpl) DeepCopy() Action {
629	return WatchActionImpl{
630		ActionImpl: a.ActionImpl.DeepCopy().(ActionImpl),
631		WatchRestrictions: WatchRestrictions{
632			Labels:          a.WatchRestrictions.Labels.DeepCopySelector(),
633			Fields:          a.WatchRestrictions.Fields.DeepCopySelector(),
634			ResourceVersion: a.WatchRestrictions.ResourceVersion,
635		},
636	}
637}
638
639type ProxyGetActionImpl struct {
640	ActionImpl
641	Scheme string
642	Name   string
643	Port   string
644	Path   string
645	Params map[string]string
646}
647
648func (a ProxyGetActionImpl) GetScheme() string {
649	return a.Scheme
650}
651
652func (a ProxyGetActionImpl) GetName() string {
653	return a.Name
654}
655
656func (a ProxyGetActionImpl) GetPort() string {
657	return a.Port
658}
659
660func (a ProxyGetActionImpl) GetPath() string {
661	return a.Path
662}
663
664func (a ProxyGetActionImpl) GetParams() map[string]string {
665	return a.Params
666}
667
668func (a ProxyGetActionImpl) DeepCopy() Action {
669	params := map[string]string{}
670	for k, v := range a.Params {
671		params[k] = v
672	}
673	return ProxyGetActionImpl{
674		ActionImpl: a.ActionImpl.DeepCopy().(ActionImpl),
675		Scheme:     a.Scheme,
676		Name:       a.Name,
677		Port:       a.Port,
678		Path:       a.Path,
679		Params:     params,
680	}
681}
682