1 package kclient
2
3 import (
4 "fmt"
5 "testing"
6 "time"
7
8 "github.com/golang/mock/gomock"
9 "github.com/google/go-cmp/cmp"
10 "k8s.io/apimachinery/pkg/api/meta"
11 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
12 "k8s.io/apimachinery/pkg/runtime"
13 "k8s.io/apimachinery/pkg/runtime/schema"
14
15 corev1 "k8s.io/api/core/v1"
16 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
17
18 ktesting "k8s.io/client-go/testing"
19 )
20
21 func TestGetOnePodFromSelector(t *testing.T) {
22 fakePod := FakePodStatus(corev1.PodRunning, "nodejs")
23 fakePod.Labels["component"] = "nodejs"
24
25 fakePodWithDeletionTimeStamp := FakePodStatus(corev1.PodRunning, "nodejs")
26 fakePodWithDeletionTimeStamp.Labels["component"] = "nodejs"
27 currentTime := metav1.NewTime(time.Now())
28 fakePodWithDeletionTimeStamp.DeletionTimestamp = ¤tTime
29
30 type args struct {
31 selector string
32 }
33 tests := []struct {
34 name string
35 args args
36 returnedPods *corev1.PodList
37 want *corev1.Pod
38 wantErr bool
39 }{
40 {
41 name: "valid number of pods",
42 args: args{selector: fmt.Sprintf("component=%s", "nodejs")},
43 returnedPods: &corev1.PodList{
44 Items: []corev1.Pod{
45 *fakePod,
46 },
47 },
48 want: fakePod,
49 wantErr: false,
50 },
51 {
52 name: "zero pods",
53 args: args{selector: fmt.Sprintf("component=%s", "nodejs")},
54 returnedPods: &corev1.PodList{
55 Items: []corev1.Pod{},
56 },
57 want: &corev1.Pod{},
58 wantErr: true,
59 },
60 {
61 name: "mutiple pods",
62 args: args{selector: fmt.Sprintf("component=%s", "nodejs")},
63 returnedPods: &corev1.PodList{
64 Items: []corev1.Pod{
65 *fakePod,
66 *fakePod,
67 },
68 },
69 want: &corev1.Pod{},
70 wantErr: true,
71 },
72 {
73 name: "pod is in the deletion state",
74 args: args{selector: fmt.Sprintf("component=%s", "nodejs")},
75 returnedPods: &corev1.PodList{
76 Items: []corev1.Pod{
77 *fakePodWithDeletionTimeStamp,
78 },
79 },
80 want: &corev1.Pod{},
81 wantErr: true,
82 },
83 }
84 for _, tt := range tests {
85 t.Run(tt.name, func(t *testing.T) {
86
87 fkclient, fkclientset := FakeNew()
88
89 fkclientset.Kubernetes.PrependReactor("list", "pods", func(action ktesting.Action) (handled bool, ret runtime.Object, err error) {
90 if action.(ktesting.ListAction).GetListRestrictions().Labels.String() != fmt.Sprintf("component=%s", "nodejs") {
91 t.Errorf("list called with different selector want:%s, got:%s", fmt.Sprintf("component=%s", "nodejs"), action.(ktesting.ListAction).GetListRestrictions().Labels.String())
92 }
93 return true, tt.returnedPods, nil
94 })
95
96 got, err := fkclient.GetRunningPodFromSelector(tt.args.selector)
97 if (err != nil) != tt.wantErr {
98 t.Errorf("GetOnePodFromSelector() error = %v, wantErr %v", err, tt.wantErr)
99 return
100 } else if tt.wantErr && err != nil {
101 return
102 }
103 if diff := cmp.Diff(tt.want, got); diff != "" {
104 t.Errorf("Client.GetRunningPodFromSelector() mismatch (-want +got):\n%s", diff)
105 }
106 })
107 }
108 }
109
110 func TestGetPodUsingComponentName(t *testing.T) {
111 fakePod := FakePodStatus(corev1.PodRunning, "nodejs")
112 fakePod.Labels["component"] = "nodejs"
113
114 type args struct {
115 componentName string
116 }
117 tests := []struct {
118 name string
119 args args
120 want *corev1.Pod
121 wantErr bool
122 }{
123 {
124 name: "list called with same component name",
125 args: args{
126 componentName: "nodejs",
127 },
128 want: fakePod,
129 wantErr: false,
130 },
131 }
132 for _, tt := range tests {
133 t.Run(tt.name, func(t *testing.T) {
134 fkclient, fkclientset := FakeNew()
135
136 fkclientset.Kubernetes.PrependReactor("list", "pods", func(action ktesting.Action) (handled bool, ret runtime.Object, err error) {
137 if action.(ktesting.ListAction).GetListRestrictions().Labels.String() != fmt.Sprintf("component=%s", tt.args.componentName) {
138 t.Errorf("list called with different selector want:%s, got:%s", fmt.Sprintf("component=%s", tt.args.componentName), action.(ktesting.ListAction).GetListRestrictions().Labels.String())
139 }
140 return true, &corev1.PodList{
141 Items: []corev1.Pod{
142 *fakePod,
143 },
144 }, nil
145 })
146
147 got, err := fkclient.GetPodUsingComponentName(tt.args.componentName)
148 if (err != nil) != tt.wantErr {
149 t.Errorf("GetPodUsingComponentName() error = %v, wantErr %v", err, tt.wantErr)
150 return
151 }
152 if diff := cmp.Diff(tt.want, got); diff != "" {
153 t.Errorf("Client.GetPodUsingComponentName() mismatch (-want +got):\n%s", diff)
154 }
155 })
156 }
157 }
158
159 func generateOwnerReference(object unstructured.Unstructured) metav1.OwnerReference {
160 return metav1.OwnerReference{
161 APIVersion: object.GetAPIVersion(),
162 Kind: object.GetKind(),
163 Name: object.GetName(),
164 UID: object.GetUID(),
165 }
166 }
167
168 func Test_matchOwnerReferenceWithResources_PodsWithOwnerInResources(t *testing.T) {
169 type args struct {
170 resources func() []unstructured.Unstructured
171 }
172 tests := []struct {
173 name string
174 args args
175 want bool
176 wantErr bool
177 }{
178 {
179 name: "Case 1: pod owned by a deployment",
180 args: args{
181 resources: func() []unstructured.Unstructured {
182 pod := fakePod("pod")
183 deployment := fakeDeployment("deployment")
184 deployOwnerRef := generateOwnerReference(deployment)
185 pod.SetOwnerReferences([]metav1.OwnerReference{deployOwnerRef})
186 return []unstructured.Unstructured{pod, deployment}
187 },
188 },
189 want: true,
190 wantErr: false,
191 },
192 }
193 for _, tt := range tests {
194 t.Run(tt.name, func(t *testing.T) {
195 ctrl := gomock.NewController(t)
196 kubernetesClient := NewMockClientInterface(ctrl)
197
198 got, err := matchOwnerReferenceWithResources(
199 kubernetesClient,
200 tt.args.resources()[0].GetOwnerReferences()[0],
201 tt.args.resources(),
202 )
203 if (err != nil) != tt.wantErr {
204 t.Errorf("matchOwnerReferenceWithResources() error = %v, wantErr %v", err, tt.wantErr)
205 return
206 }
207 if got != tt.want {
208 t.Errorf("matchOwnerReferenceWithResources() got = %v, want %v", got, tt.want)
209 }
210 })
211 }
212 }
213
214 func fakePod(name string) unstructured.Unstructured {
215 return unstructured.Unstructured{Object: map[string]interface{}{
216 "apiVersion": "v1",
217 "kind": "Pod",
218 "metadata": map[string]interface{}{
219 "name": fmt.Sprintf("pod-%s", name),
220 "uid": fmt.Sprintf("pod-%s", name),
221 },
222 "spec": map[string]interface{}{
223 "containers": map[string]interface{}{
224 "name": fmt.Sprintf("%s-1", name),
225 "image": "image",
226 },
227 },
228 }}
229 }
230
231 func fakeDeployment(name string) unstructured.Unstructured {
232 return unstructured.Unstructured{
233 Object: map[string]interface{}{
234 "apiVersion": "apps/v1",
235 "kind": "Deployment",
236 "metadata": map[string]interface{}{
237 "name": fmt.Sprintf("deployment-%s", name),
238 "uid": fmt.Sprintf("deployment-%s", name),
239 },
240 "spec": map[string]interface{}{
241 "selector": map[string]interface{}{
242 "matchLabels": map[string]interface{}{
243 "app": "test",
244 },
245 },
246 "template": map[string]interface{}{
247 "metadata": map[string]interface{}{
248 "labels": map[string]interface{}{
249 "app": "test",
250 },
251 },
252 "spec": fakePod(name),
253 },
254 },
255 },
256 }
257 }
258
259 func Test_matchOwnerReferenceWithResources_PodsWithNoOwnerInResources(t *testing.T) {
260
261 independentDeploy := fakeDeployment("independent-deploy")
262 independentPod := fakePod("independent-pod")
263 independentPod.SetOwnerReferences([]metav1.OwnerReference{generateOwnerReference(independentDeploy)})
264
265 type args struct {
266 resources func() []unstructured.Unstructured
267 }
268 tests := []struct {
269 name string
270 args args
271 gvk *meta.RESTMapping
272 resource *unstructured.Unstructured
273 want bool
274 wantErr bool
275 }{
276 {
277 name: "Case 1: Pod not owned by anything in `resources` slice",
278 args: args{
279 resources: func() []unstructured.Unstructured {
280 pod := fakePod("pod")
281 deployment := fakeDeployment("deployment")
282 deployOwnerRef := generateOwnerReference(deployment)
283 pod.SetOwnerReferences([]metav1.OwnerReference{deployOwnerRef})
284 return []unstructured.Unstructured{pod, deployment}
285 },
286 },
287 gvk: &meta.RESTMapping{
288 Resource: schema.GroupVersionResource{
289 Group: "apps",
290 Version: "v1",
291 Resource: "deployments",
292 },
293 GroupVersionKind: schema.GroupVersionKind{
294 Group: "apps",
295 Version: "v1",
296 Kind: "Deployment",
297 },
298 },
299 resource: &unstructured.Unstructured{Object: map[string]interface{}{
300 "apiVersion": "apps/v1",
301 "kind": "Deployment",
302 "metadata": map[string]interface{}{
303 "namespace": independentDeploy.GetNamespace(),
304 "name": independentDeploy.GetName(),
305 "uid": independentDeploy.GetUID(),
306 },
307 "spec": map[string]interface{}{
308 "selector": map[string]interface{}{
309 "matchLabels": map[string]interface{}{
310 "app": "test",
311 },
312 },
313 "template": map[string]interface{}{
314 "metadata": map[string]interface{}{
315 "labels": map[string]interface{}{
316 "app": "test",
317 },
318 },
319 "spec": fakePod(independentDeploy.GetName()),
320 },
321 },
322 }},
323 want: false,
324 wantErr: false,
325 },
326 }
327 for _, tt := range tests {
328 t.Run(tt.name, func(t *testing.T) {
329 ctrl := gomock.NewController(t)
330 kubernetesClient := NewMockClientInterface(ctrl)
331 kubernetesClient.EXPECT().GetRestMappingFromGVK(
332 schema.FromAPIVersionAndKind(independentDeploy.GetAPIVersion(), independentDeploy.GetKind())).Return(tt.gvk, nil).AnyTimes()
333 kubernetesClient.EXPECT().GetDynamicResource(tt.gvk.Resource, independentDeploy.GetName()).Return(tt.resource, nil).AnyTimes()
334
335 got, err := matchOwnerReferenceWithResources(kubernetesClient, independentPod.GetOwnerReferences()[0], tt.args.resources())
336 if (err != nil) != tt.wantErr {
337 t.Errorf("matchOwnerReferenceWithResources() error = %v, wantErr %v", err, tt.wantErr)
338 return
339 }
340 if got != tt.want {
341 t.Errorf("matchOwnerReferenceWithResources() got = %v, want %v", got, tt.want)
342 }
343 })
344 }
345 }
346
View as plain text