1/*
2Copyright 2017 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 apiextensions
18
19import (
20	"reflect"
21	"testing"
22	"time"
23
24	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
25)
26
27func TestCRDHasFinalizer(t *testing.T) {
28	tests := []struct {
29		name             string
30		crd              *CustomResourceDefinition
31		finalizerToCheck string
32
33		expected bool
34	}{
35		{
36			name: "missing",
37			crd: &CustomResourceDefinition{
38				ObjectMeta: metav1.ObjectMeta{Finalizers: []string{"not-it"}},
39			},
40			finalizerToCheck: "it",
41			expected:         false,
42		},
43		{
44			name: "present",
45			crd: &CustomResourceDefinition{
46				ObjectMeta: metav1.ObjectMeta{Finalizers: []string{"not-it", "it"}},
47			},
48			finalizerToCheck: "it",
49			expected:         true,
50		},
51	}
52	for _, tc := range tests {
53		actual := CRDHasFinalizer(tc.crd, tc.finalizerToCheck)
54		if tc.expected != actual {
55			t.Errorf("%v expected %v, got %v", tc.name, tc.expected, actual)
56		}
57	}
58}
59
60func TestCRDRemoveFinalizer(t *testing.T) {
61	tests := []struct {
62		name             string
63		crd              *CustomResourceDefinition
64		finalizerToCheck string
65
66		expected []string
67	}{
68		{
69			name: "missing",
70			crd: &CustomResourceDefinition{
71				ObjectMeta: metav1.ObjectMeta{Finalizers: []string{"not-it"}},
72			},
73			finalizerToCheck: "it",
74			expected:         []string{"not-it"},
75		},
76		{
77			name: "present",
78			crd: &CustomResourceDefinition{
79				ObjectMeta: metav1.ObjectMeta{Finalizers: []string{"not-it", "it"}},
80			},
81			finalizerToCheck: "it",
82			expected:         []string{"not-it"},
83		},
84	}
85	for _, tc := range tests {
86		CRDRemoveFinalizer(tc.crd, tc.finalizerToCheck)
87		if !reflect.DeepEqual(tc.expected, tc.crd.Finalizers) {
88			t.Errorf("%v expected %v, got %v", tc.name, tc.expected, tc.crd.Finalizers)
89		}
90	}
91}
92
93func TestSetCRDCondition(t *testing.T) {
94	tests := []struct {
95		name                 string
96		crdCondition         []CustomResourceDefinitionCondition
97		newCondition         CustomResourceDefinitionCondition
98		expectedcrdCondition []CustomResourceDefinitionCondition
99	}{
100		{
101			name: "test setCRDcondition when one condition",
102			crdCondition: []CustomResourceDefinitionCondition{
103				{
104					Type:               Established,
105					Status:             ConditionTrue,
106					Reason:             "Accepted",
107					Message:            "the initial names have been accepted",
108					LastTransitionTime: metav1.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC),
109				},
110			},
111			newCondition: CustomResourceDefinitionCondition{
112				Type:               Established,
113				Status:             ConditionFalse,
114				Reason:             "NotAccepted",
115				Message:            "Not accepted",
116				LastTransitionTime: metav1.Date(2018, 1, 2, 0, 0, 0, 0, time.UTC),
117			},
118			expectedcrdCondition: []CustomResourceDefinitionCondition{
119				{
120					Type:               Established,
121					Status:             ConditionFalse,
122					Reason:             "NotAccepted",
123					Message:            "Not accepted",
124					LastTransitionTime: metav1.Date(2018, 1, 2, 0, 0, 0, 0, time.UTC),
125				},
126			},
127		},
128		{
129			name: "test setCRDcondition when two condition",
130			crdCondition: []CustomResourceDefinitionCondition{
131				{
132					Type:               Established,
133					Status:             ConditionTrue,
134					Reason:             "Accepted",
135					Message:            "the initial names have been accepted",
136					LastTransitionTime: metav1.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC),
137				},
138				{
139					Type:               NamesAccepted,
140					Status:             ConditionTrue,
141					Reason:             "NoConflicts",
142					Message:            "no conflicts found",
143					LastTransitionTime: metav1.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC),
144				},
145			},
146			newCondition: CustomResourceDefinitionCondition{
147				Type:               NamesAccepted,
148				Status:             ConditionFalse,
149				Reason:             "Conflicts",
150				Message:            "conflicts found",
151				LastTransitionTime: metav1.Date(2018, 1, 2, 0, 0, 0, 0, time.UTC),
152			},
153			expectedcrdCondition: []CustomResourceDefinitionCondition{
154				{
155					Type:               Established,
156					Status:             ConditionTrue,
157					Reason:             "Accepted",
158					Message:            "the initial names have been accepted",
159					LastTransitionTime: metav1.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC),
160				},
161				{
162					Type:               NamesAccepted,
163					Status:             ConditionFalse,
164					Reason:             "Conflicts",
165					Message:            "conflicts found",
166					LastTransitionTime: metav1.Date(2018, 1, 2, 0, 0, 0, 0, time.UTC),
167				},
168			},
169		},
170		{
171			name: "test setCRDcondition when condition needs to be appended",
172			crdCondition: []CustomResourceDefinitionCondition{
173				{
174					Type:               Established,
175					Status:             ConditionTrue,
176					Reason:             "Accepted",
177					Message:            "the initial names have been accepted",
178					LastTransitionTime: metav1.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC),
179				},
180			},
181			newCondition: CustomResourceDefinitionCondition{
182				Type:               Terminating,
183				Status:             ConditionFalse,
184				Reason:             "NeverEstablished",
185				Message:            "resource was never established",
186				LastTransitionTime: metav1.Date(2018, 2, 1, 0, 0, 0, 0, time.UTC),
187			},
188			expectedcrdCondition: []CustomResourceDefinitionCondition{
189				{
190					Type:               Established,
191					Status:             ConditionTrue,
192					Reason:             "Accepted",
193					Message:            "the initial names have been accepted",
194					LastTransitionTime: metav1.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC),
195				},
196				{
197					Type:               Terminating,
198					Status:             ConditionFalse,
199					Reason:             "NeverEstablished",
200					Message:            "resource was never established",
201					LastTransitionTime: metav1.Date(2018, 2, 1, 0, 0, 0, 0, time.UTC),
202				},
203			},
204		},
205		{
206			name: "set new condition which doesn't have lastTransitionTime set",
207			crdCondition: []CustomResourceDefinitionCondition{
208				{
209					Type:               Established,
210					Status:             ConditionTrue,
211					Reason:             "Accepted",
212					Message:            "the initial names have been accepted",
213					LastTransitionTime: metav1.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC),
214				},
215			},
216			newCondition: CustomResourceDefinitionCondition{
217				Type:    Established,
218				Status:  ConditionFalse,
219				Reason:  "NotAccepted",
220				Message: "Not accepted",
221			},
222			expectedcrdCondition: []CustomResourceDefinitionCondition{
223				{
224					Type:               Established,
225					Status:             ConditionFalse,
226					Reason:             "NotAccepted",
227					Message:            "Not accepted",
228					LastTransitionTime: metav1.Date(2018, 1, 2, 0, 0, 0, 0, time.UTC),
229				},
230			},
231		},
232		{
233			name: "append new condition which doesn't have lastTransitionTime set",
234			crdCondition: []CustomResourceDefinitionCondition{
235				{
236					Type:               Established,
237					Status:             ConditionTrue,
238					Reason:             "Accepted",
239					Message:            "the initial names have been accepted",
240					LastTransitionTime: metav1.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC),
241				},
242			},
243			newCondition: CustomResourceDefinitionCondition{
244				Type:    Terminating,
245				Status:  ConditionFalse,
246				Reason:  "NeverEstablished",
247				Message: "resource was never established",
248			},
249			expectedcrdCondition: []CustomResourceDefinitionCondition{
250				{
251					Type:               Established,
252					Status:             ConditionTrue,
253					Reason:             "Accepted",
254					Message:            "the initial names have been accepted",
255					LastTransitionTime: metav1.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC),
256				},
257				{
258					Type:               Terminating,
259					Status:             ConditionFalse,
260					Reason:             "NeverEstablished",
261					Message:            "resource was never established",
262					LastTransitionTime: metav1.Date(2018, 2, 1, 0, 0, 0, 0, time.UTC),
263				},
264			},
265		},
266	}
267	for _, tc := range tests {
268		crd := generateCRDwithCondition(tc.crdCondition)
269		SetCRDCondition(crd, tc.newCondition)
270		if len(tc.expectedcrdCondition) != len(crd.Status.Conditions) {
271			t.Errorf("%v expected %v, got %v", tc.name, tc.expectedcrdCondition, crd.Status.Conditions)
272		}
273		for i := range tc.expectedcrdCondition {
274			if !IsCRDConditionEquivalent(&tc.expectedcrdCondition[i], &crd.Status.Conditions[i]) {
275				t.Errorf("%v expected %v, got %v", tc.name, tc.expectedcrdCondition[i], crd.Status.Conditions[i])
276			}
277			if crd.Status.Conditions[i].LastTransitionTime.IsZero() {
278				t.Errorf("%q/%d lastTransitionTime should not be null: %v", tc.name, i, crd.Status.Conditions[i])
279			}
280		}
281	}
282}
283
284func TestRemoveCRDCondition(t *testing.T) {
285	tests := []struct {
286		name                 string
287		crdCondition         []CustomResourceDefinitionCondition
288		conditionType        CustomResourceDefinitionConditionType
289		expectedcrdCondition []CustomResourceDefinitionCondition
290	}{
291		{
292			name: "test remove CRDCondition when the conditionType meets",
293			crdCondition: []CustomResourceDefinitionCondition{
294				{
295					Type:               Established,
296					Status:             ConditionTrue,
297					Reason:             "Accepted",
298					Message:            "the initial names have been accepted",
299					LastTransitionTime: metav1.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC),
300				},
301				{
302					Type:               NamesAccepted,
303					Status:             ConditionTrue,
304					Reason:             "NoConflicts",
305					Message:            "no conflicts found",
306					LastTransitionTime: metav1.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC),
307				},
308			},
309			conditionType: NamesAccepted,
310			expectedcrdCondition: []CustomResourceDefinitionCondition{
311				{
312					Type:               Established,
313					Status:             ConditionTrue,
314					Reason:             "Accepted",
315					Message:            "the initial names have been accepted",
316					LastTransitionTime: metav1.Date(2011, 1, 2, 0, 0, 0, 0, time.UTC),
317				},
318			},
319		},
320		{
321			name: "test remove CRDCondition when the conditionType not meets",
322			crdCondition: []CustomResourceDefinitionCondition{
323				{
324					Type:               Established,
325					Status:             ConditionTrue,
326					Reason:             "Accepted",
327					Message:            "the initial names have been accepted",
328					LastTransitionTime: metav1.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC),
329				},
330				{
331					Type:               NamesAccepted,
332					Status:             ConditionTrue,
333					Reason:             "NoConflicts",
334					Message:            "no conflicts found",
335					LastTransitionTime: metav1.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC),
336				},
337			},
338			conditionType: Terminating,
339			expectedcrdCondition: []CustomResourceDefinitionCondition{
340				{
341					Type:               Established,
342					Status:             ConditionTrue,
343					Reason:             "Accepted",
344					Message:            "the initial names have been accepted",
345					LastTransitionTime: metav1.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC),
346				},
347				{
348					Type:               NamesAccepted,
349					Status:             ConditionTrue,
350					Reason:             "NoConflicts",
351					Message:            "no conflicts found",
352					LastTransitionTime: metav1.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC),
353				},
354			},
355		},
356	}
357	for _, tc := range tests {
358		crd := generateCRDwithCondition(tc.crdCondition)
359		RemoveCRDCondition(crd, tc.conditionType)
360		if len(tc.expectedcrdCondition) != len(crd.Status.Conditions) {
361			t.Errorf("%v expected %v, got %v", tc.name, tc.expectedcrdCondition, crd.Status.Conditions)
362		}
363		for i := range tc.expectedcrdCondition {
364			if !IsCRDConditionEquivalent(&tc.expectedcrdCondition[i], &crd.Status.Conditions[i]) {
365				t.Errorf("%v expected %v, got %v", tc.name, tc.expectedcrdCondition, crd.Status.Conditions)
366			}
367		}
368	}
369}
370
371func TestIsCRDConditionPresentAndEqual(t *testing.T) {
372	tests := []struct {
373		name          string
374		crdCondition  []CustomResourceDefinitionCondition
375		conditionType CustomResourceDefinitionConditionType
376		status        ConditionStatus
377		expectresult  bool
378	}{
379		{
380			name: "test CRDCondition is not Present",
381			crdCondition: []CustomResourceDefinitionCondition{
382				{
383					Type:               Established,
384					Status:             ConditionTrue,
385					Reason:             "Accepted",
386					Message:            "the initial names have been accepted",
387					LastTransitionTime: metav1.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC),
388				},
389				{
390					Type:               NamesAccepted,
391					Status:             ConditionTrue,
392					Reason:             "NoConflicts",
393					Message:            "no conflicts found",
394					LastTransitionTime: metav1.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC),
395				},
396			},
397			conditionType: Terminating,
398			status:        ConditionTrue,
399			expectresult:  false,
400		},
401		{
402			name: "test CRDCondition is Present but not Equal",
403			crdCondition: []CustomResourceDefinitionCondition{
404				{
405					Type:               Established,
406					Status:             ConditionTrue,
407					Reason:             "Accepted",
408					Message:            "the initial names have been accepted",
409					LastTransitionTime: metav1.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC),
410				},
411				{
412					Type:               NamesAccepted,
413					Status:             ConditionTrue,
414					Reason:             "NoConflicts",
415					Message:            "no conflicts found",
416					LastTransitionTime: metav1.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC),
417				},
418			},
419			conditionType: Established,
420			status:        ConditionFalse,
421			expectresult:  false,
422		},
423		{
424			name: "test CRDCondition is Present and Equal",
425			crdCondition: []CustomResourceDefinitionCondition{
426				{
427					Type:               Established,
428					Status:             ConditionTrue,
429					Reason:             "Accepted",
430					Message:            "the initial names have been accepted",
431					LastTransitionTime: metav1.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC),
432				},
433				{
434					Type:               NamesAccepted,
435					Status:             ConditionTrue,
436					Reason:             "NoConflicts",
437					Message:            "no conflicts found",
438					LastTransitionTime: metav1.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC),
439				},
440			},
441			conditionType: NamesAccepted,
442			status:        ConditionTrue,
443			expectresult:  true,
444		},
445	}
446	for _, tc := range tests {
447		crd := generateCRDwithCondition(tc.crdCondition)
448		res := IsCRDConditionPresentAndEqual(crd, tc.conditionType, tc.status)
449		if res != tc.expectresult {
450			t.Errorf("%v expected %t, got %t", tc.name, tc.expectresult, res)
451		}
452	}
453}
454
455func generateCRDwithCondition(conditions []CustomResourceDefinitionCondition) *CustomResourceDefinition {
456	testCRDObjectMeta := metav1.ObjectMeta{
457		Name:            "plural.group.com",
458		ResourceVersion: "12",
459	}
460	testCRDSpec := CustomResourceDefinitionSpec{
461		Group:   "group.com",
462		Version: "version",
463		Scope:   ResourceScope("Cluster"),
464		Names: CustomResourceDefinitionNames{
465			Plural:   "plural",
466			Singular: "singular",
467			Kind:     "kind",
468			ListKind: "listkind",
469		},
470	}
471	testCRDAcceptedNames := CustomResourceDefinitionNames{
472		Plural:   "plural",
473		Singular: "singular",
474		Kind:     "kind",
475		ListKind: "listkind",
476	}
477	return &CustomResourceDefinition{
478		ObjectMeta: testCRDObjectMeta,
479		Spec:       testCRDSpec,
480		Status: CustomResourceDefinitionStatus{
481			AcceptedNames: testCRDAcceptedNames,
482			Conditions:    conditions,
483		},
484	}
485}
486