1 package k8s
2
3 import (
4 "bufio"
5 "errors"
6 "fmt"
7 "io"
8 "strings"
9
10 spclient "github.com/linkerd/linkerd2/controller/gen/client/clientset/versioned"
11 spfake "github.com/linkerd/linkerd2/controller/gen/client/clientset/versioned/fake"
12
13 spscheme "github.com/linkerd/linkerd2/controller/gen/client/clientset/versioned/scheme"
14 corev1 "k8s.io/api/core/v1"
15 discovery "k8s.io/api/discovery/v1"
16 apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
17 apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
18 apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
19 apiextensionsfake "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/fake"
20 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
21 "k8s.io/apimachinery/pkg/runtime"
22 "k8s.io/apimachinery/pkg/runtime/schema"
23 "k8s.io/apimachinery/pkg/util/rand"
24 yamlDecoder "k8s.io/apimachinery/pkg/util/yaml"
25 discoveryfake "k8s.io/client-go/discovery/fake"
26 "k8s.io/client-go/kubernetes"
27 "k8s.io/client-go/kubernetes/fake"
28 "k8s.io/client-go/kubernetes/scheme"
29 "k8s.io/client-go/rest"
30 "k8s.io/client-go/testing"
31 apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1"
32 apiregistrationclient "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset"
33 apiregistrationfake "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/fake"
34
35 "sigs.k8s.io/yaml"
36 )
37
38 func init() {
39 apiextensionsv1beta1.AddToScheme(scheme.Scheme)
40 apiextensionsv1.AddToScheme(scheme.Scheme)
41 apiregistrationv1.AddToScheme(scheme.Scheme)
42 spscheme.AddToScheme(scheme.Scheme)
43 }
44
45
46 func NewFakeAPI(configs ...string) (*KubernetesAPI, error) {
47 client, apiextClient, apiregClient, _, err := NewFakeClientSets(configs...)
48 if err != nil {
49 return nil, err
50 }
51
52 return &KubernetesAPI{
53 Config: &rest.Config{},
54 Interface: client,
55 Apiextensions: apiextClient,
56 Apiregistration: apiregClient,
57 }, nil
58 }
59
60
61
62 func NewFakeAPIFromManifests(readers []io.Reader) (*KubernetesAPI, error) {
63 client, apiextClient, apiregClient, _, err := newFakeClientSetsFromManifests(readers)
64 if err != nil {
65 return nil, err
66 }
67
68 return &KubernetesAPI{
69 Interface: client,
70 Apiextensions: apiextClient,
71 Apiregistration: apiregClient,
72 }, nil
73 }
74
75
76
77 func NewFakeClientSets(configs ...string) (
78 *fake.Clientset,
79 apiextensionsclient.Interface,
80 apiregistrationclient.Interface,
81 spclient.Interface,
82 error,
83 ) {
84 objs := []runtime.Object{}
85 apiextObjs := []runtime.Object{}
86 apiRegObjs := []runtime.Object{}
87 discoveryObjs := []runtime.Object{}
88 spObjs := []runtime.Object{}
89 for _, config := range configs {
90 obj, err := ToRuntimeObject(config)
91 if err != nil {
92 return nil, nil, nil, nil, err
93 }
94 switch strings.ToLower(obj.GetObjectKind().GroupVersionKind().Kind) {
95 case "customresourcedefinition":
96 apiextObjs = append(apiextObjs, obj)
97 case "apiservice":
98 apiRegObjs = append(apiRegObjs, obj)
99 case "apiresourcelist":
100 discoveryObjs = append(discoveryObjs, obj)
101 case ServiceProfile:
102 spObjs = append(spObjs, obj)
103 case Server:
104 spObjs = append(spObjs, obj)
105 case ExtWorkload:
106 spObjs = append(spObjs, obj)
107 default:
108 objs = append(objs, obj)
109 }
110 }
111
112 endpointslice, err := ToRuntimeObject(`apiVersion: discovery.k8s.io/v1
113 kind: EndpointSlice
114 metadata:
115 name: kubernetes
116 namespace: default`)
117 if err != nil {
118 return nil, nil, nil, nil, err
119 }
120 objs = append(objs, endpointslice)
121
122 cs := fake.NewSimpleClientset(objs...)
123 fakeDiscoveryClient := cs.Discovery().(*discoveryfake.FakeDiscovery)
124 for _, obj := range discoveryObjs {
125 apiResList := obj.(*metav1.APIResourceList)
126 fakeDiscoveryClient.Resources = append(fakeDiscoveryClient.Resources, apiResList)
127 }
128 fakeDiscoveryClient.Resources = append(fakeDiscoveryClient.Resources, &metav1.APIResourceList{
129 TypeMeta: metav1.TypeMeta{
130 Kind: "APIResourceList",
131 APIVersion: "v1",
132 },
133 GroupVersion: discovery.SchemeGroupVersion.String(),
134 APIResources: []metav1.APIResource{
135 {
136 Name: "endpointslices",
137 Kind: "EndpointSlice",
138 SingularName: "endpointslice",
139 },
140 },
141 })
142
143
144 cs.PrependReactor("create", "endpointslices", testing.ReactionFunc(func(action testing.Action) (bool, runtime.Object, error) {
145 es := action.(testing.CreateAction).GetObject().(*discovery.EndpointSlice)
146
147
148
149 if es.GenerateName != "" {
150 es.Name = fmt.Sprintf("%s-%s", es.GenerateName, rand.String(8))
151 es.GenerateName = ""
152 }
153 es.Generation = 1
154
155 return false, es, nil
156 }))
157
158 cs.PrependReactor("update", "endpointslices", testing.ReactionFunc(func(action testing.Action) (bool, runtime.Object, error) {
159
160
161 es := action.(testing.CreateAction).GetObject().(*discovery.EndpointSlice)
162 es.Generation++
163 return false, es, nil
164 }))
165
166 return cs,
167 apiextensionsfake.NewSimpleClientset(apiextObjs...),
168 apiregistrationfake.NewSimpleClientset(apiRegObjs...),
169 spfake.NewSimpleClientset(spObjs...),
170 nil
171 }
172
173
174
175
176
177
178 func newFakeClientSetsFromManifests(readers []io.Reader) (
179 kubernetes.Interface,
180 apiextensionsclient.Interface,
181 apiregistrationclient.Interface,
182 spclient.Interface,
183 error,
184 ) {
185 configs := []string{}
186
187 for _, reader := range readers {
188 r := yamlDecoder.NewYAMLReader(bufio.NewReaderSize(reader, 4096))
189
190
191 for {
192
193 bytes, err := r.Read()
194 if err != nil {
195 if errors.Is(err, io.EOF) {
196 break
197 }
198 return nil, nil, nil, nil, err
199 }
200
201
202 var typeMeta metav1.TypeMeta
203 if err := yaml.Unmarshal(bytes, &typeMeta); err != nil {
204 return nil, nil, nil, nil, err
205 }
206
207 switch typeMeta.Kind {
208 case "":
209
210
211 case "List":
212 var sourceList corev1.List
213 if err := yaml.Unmarshal(bytes, &sourceList); err != nil {
214 return nil, nil, nil, nil, err
215 }
216 for _, item := range sourceList.Items {
217 configs = append(configs, string(item.Raw))
218 }
219
220 default:
221 configs = append(configs, string(bytes))
222 }
223 }
224 }
225
226 return NewFakeClientSets(configs...)
227 }
228
229
230 func ToRuntimeObject(config string) (runtime.Object, error) {
231 decode := scheme.Codecs.UniversalDeserializer().Decode
232 obj, _, err := decode([]byte(config), nil, nil)
233 return obj, err
234 }
235
236
237
238
239
240 func ObjectKinds(obj runtime.Object) ([]schema.GroupVersionKind, bool, error) {
241 apiextensionsv1beta1.AddToScheme(scheme.Scheme)
242 apiextensionsv1.AddToScheme(scheme.Scheme)
243 apiregistrationv1.AddToScheme(scheme.Scheme)
244 spscheme.AddToScheme(scheme.Scheme)
245 return scheme.Scheme.ObjectKinds(obj)
246 }
247
View as plain text