sigs.k8s.io/cluster-api@v1.7.1/util/conditions/setter_test.go (about)

     1  /*
     2  Copyright 2020 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package conditions
    18  
    19  import (
    20  	"testing"
    21  	"time"
    22  
    23  	. "github.com/onsi/gomega"
    24  	"github.com/onsi/gomega/format"
    25  	"github.com/onsi/gomega/types"
    26  	"github.com/pkg/errors"
    27  	corev1 "k8s.io/api/core/v1"
    28  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    29  
    30  	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
    31  )
    32  
    33  func TestHasSameState(t *testing.T) {
    34  	g := NewWithT(t)
    35  
    36  	// same condition
    37  	falseInfo2 := falseInfo1.DeepCopy()
    38  	g.Expect(hasSameState(falseInfo1, falseInfo2)).To(BeTrue())
    39  
    40  	// different LastTransitionTime does not impact state
    41  	falseInfo2 = falseInfo1.DeepCopy()
    42  	falseInfo2.LastTransitionTime = metav1.NewTime(time.Date(1900, time.November, 10, 23, 0, 0, 0, time.UTC))
    43  	g.Expect(hasSameState(falseInfo1, falseInfo2)).To(BeTrue())
    44  
    45  	// different Type, Status, Reason, Severity and Message determine different state
    46  	falseInfo2 = falseInfo1.DeepCopy()
    47  	falseInfo2.Type = "another type"
    48  	g.Expect(hasSameState(falseInfo1, falseInfo2)).To(BeFalse())
    49  
    50  	falseInfo2 = falseInfo1.DeepCopy()
    51  	falseInfo2.Status = corev1.ConditionTrue
    52  	g.Expect(hasSameState(falseInfo1, falseInfo2)).To(BeFalse())
    53  
    54  	falseInfo2 = falseInfo1.DeepCopy()
    55  	falseInfo2.Severity = clusterv1.ConditionSeverityWarning
    56  	g.Expect(hasSameState(falseInfo1, falseInfo2)).To(BeFalse())
    57  
    58  	falseInfo2 = falseInfo1.DeepCopy()
    59  	falseInfo2.Reason = "another severity"
    60  	g.Expect(hasSameState(falseInfo1, falseInfo2)).To(BeFalse())
    61  
    62  	falseInfo2 = falseInfo1.DeepCopy()
    63  	falseInfo2.Message = "another message"
    64  	g.Expect(hasSameState(falseInfo1, falseInfo2)).To(BeFalse())
    65  }
    66  
    67  func TestLexicographicLess(t *testing.T) {
    68  	g := NewWithT(t)
    69  
    70  	// alphabetical order of Type is respected
    71  	a := TrueCondition("A")
    72  	b := TrueCondition("B")
    73  	g.Expect(lexicographicLess(a, b)).To(BeTrue())
    74  
    75  	a = TrueCondition("B")
    76  	b = TrueCondition("A")
    77  	g.Expect(lexicographicLess(a, b)).To(BeFalse())
    78  
    79  	// Ready condition is treated as an exception and always goes first
    80  	a = TrueCondition(clusterv1.ReadyCondition)
    81  	b = TrueCondition("A")
    82  	g.Expect(lexicographicLess(a, b)).To(BeTrue())
    83  
    84  	a = TrueCondition("A")
    85  	b = TrueCondition(clusterv1.ReadyCondition)
    86  	g.Expect(lexicographicLess(a, b)).To(BeFalse())
    87  }
    88  
    89  func TestSet(t *testing.T) {
    90  	a := TrueCondition("a")
    91  	b := TrueCondition("b")
    92  	ready := TrueCondition(clusterv1.ReadyCondition)
    93  
    94  	tests := []struct {
    95  		name      string
    96  		to        Setter
    97  		condition *clusterv1.Condition
    98  		want      clusterv1.Conditions
    99  	}{
   100  		{
   101  			name:      "Set adds a condition",
   102  			to:        setterWithConditions(),
   103  			condition: a,
   104  			want:      conditionList(a),
   105  		},
   106  		{
   107  			name:      "Set adds more conditions",
   108  			to:        setterWithConditions(a),
   109  			condition: b,
   110  			want:      conditionList(a, b),
   111  		},
   112  		{
   113  			name:      "Set does not duplicate existing conditions",
   114  			to:        setterWithConditions(a, b),
   115  			condition: a,
   116  			want:      conditionList(a, b),
   117  		},
   118  		{
   119  			name:      "Set sorts conditions in lexicographic order",
   120  			to:        setterWithConditions(b, a),
   121  			condition: ready,
   122  			want:      conditionList(ready, a, b),
   123  		},
   124  	}
   125  
   126  	for _, tt := range tests {
   127  		t.Run(tt.name, func(t *testing.T) {
   128  			g := NewWithT(t)
   129  
   130  			Set(tt.to, tt.condition)
   131  
   132  			g.Expect(tt.to.GetConditions()).To(haveSameConditionsOf(tt.want))
   133  		})
   134  	}
   135  }
   136  
   137  func TestSetLastTransitionTime(t *testing.T) {
   138  	x := metav1.Date(2012, time.January, 1, 12, 15, 30, 5e8, time.UTC)
   139  
   140  	foo := FalseCondition("foo", "reason foo", clusterv1.ConditionSeverityInfo, "message foo")
   141  	fooWithLastTransitionTime := FalseCondition("foo", "reason foo", clusterv1.ConditionSeverityInfo, "message foo")
   142  	fooWithLastTransitionTime.LastTransitionTime = x
   143  	fooWithAnotherState := TrueCondition("foo")
   144  
   145  	tests := []struct {
   146  		name                    string
   147  		to                      Setter
   148  		new                     *clusterv1.Condition
   149  		LastTransitionTimeCheck func(*WithT, metav1.Time)
   150  	}{
   151  		{
   152  			name: "Set a condition that does not exists should set the last transition time if not defined",
   153  			to:   setterWithConditions(),
   154  			new:  foo,
   155  			LastTransitionTimeCheck: func(g *WithT, lastTransitionTime metav1.Time) {
   156  				g.Expect(lastTransitionTime).ToNot(BeZero())
   157  			},
   158  		},
   159  		{
   160  			name: "Set a condition that does not exists should preserve the last transition time if defined",
   161  			to:   setterWithConditions(),
   162  			new:  fooWithLastTransitionTime,
   163  			LastTransitionTimeCheck: func(g *WithT, lastTransitionTime metav1.Time) {
   164  				g.Expect(lastTransitionTime).To(Equal(x))
   165  			},
   166  		},
   167  		{
   168  			name: "Set a condition that already exists with the same state should preserves the last transition time",
   169  			to:   setterWithConditions(fooWithLastTransitionTime),
   170  			new:  foo,
   171  			LastTransitionTimeCheck: func(g *WithT, lastTransitionTime metav1.Time) {
   172  				g.Expect(lastTransitionTime).To(Equal(x))
   173  			},
   174  		},
   175  		{
   176  			name: "Set a condition that already exists but with different state should changes the last transition time",
   177  			to:   setterWithConditions(fooWithLastTransitionTime),
   178  			new:  fooWithAnotherState,
   179  			LastTransitionTimeCheck: func(g *WithT, lastTransitionTime metav1.Time) {
   180  				g.Expect(lastTransitionTime).ToNot(Equal(x))
   181  			},
   182  		},
   183  	}
   184  
   185  	for _, tt := range tests {
   186  		t.Run(tt.name, func(t *testing.T) {
   187  			g := NewWithT(t)
   188  
   189  			Set(tt.to, tt.new)
   190  
   191  			tt.LastTransitionTimeCheck(g, Get(tt.to, "foo").LastTransitionTime)
   192  		})
   193  	}
   194  }
   195  
   196  func TestMarkMethods(t *testing.T) {
   197  	g := NewWithT(t)
   198  
   199  	cluster := &clusterv1.Cluster{}
   200  
   201  	// test MarkTrue
   202  	MarkTrue(cluster, "conditionFoo")
   203  	g.Expect(Get(cluster, "conditionFoo")).To(HaveSameStateOf(&clusterv1.Condition{
   204  		Type:   "conditionFoo",
   205  		Status: corev1.ConditionTrue,
   206  	}))
   207  
   208  	// test MarkFalse
   209  	MarkFalse(cluster, "conditionBar", "reasonBar", clusterv1.ConditionSeverityError, "messageBar")
   210  	g.Expect(Get(cluster, "conditionBar")).To(HaveSameStateOf(&clusterv1.Condition{
   211  		Type:     "conditionBar",
   212  		Status:   corev1.ConditionFalse,
   213  		Severity: clusterv1.ConditionSeverityError,
   214  		Reason:   "reasonBar",
   215  		Message:  "messageBar",
   216  	}))
   217  
   218  	// test MarkUnknown
   219  	MarkUnknown(cluster, "conditionBaz", "reasonBaz", "messageBaz")
   220  	g.Expect(Get(cluster, "conditionBaz")).To(HaveSameStateOf(&clusterv1.Condition{
   221  		Type:    "conditionBaz",
   222  		Status:  corev1.ConditionUnknown,
   223  		Reason:  "reasonBaz",
   224  		Message: "messageBaz",
   225  	}))
   226  }
   227  
   228  func TestSetSummary(t *testing.T) {
   229  	g := NewWithT(t)
   230  	target := setterWithConditions(TrueCondition("foo"))
   231  
   232  	SetSummary(target)
   233  
   234  	g.Expect(Has(target, clusterv1.ReadyCondition)).To(BeTrue())
   235  }
   236  
   237  func TestSetMirror(t *testing.T) {
   238  	g := NewWithT(t)
   239  	source := getterWithConditions(TrueCondition(clusterv1.ReadyCondition))
   240  	target := setterWithConditions()
   241  
   242  	SetMirror(target, "foo", source)
   243  
   244  	g.Expect(Has(target, "foo")).To(BeTrue())
   245  }
   246  
   247  func TestSetAggregate(t *testing.T) {
   248  	g := NewWithT(t)
   249  	source1 := getterWithConditions(TrueCondition(clusterv1.ReadyCondition))
   250  	source2 := getterWithConditions(TrueCondition(clusterv1.ReadyCondition))
   251  	target := setterWithConditions()
   252  
   253  	SetAggregate(target, "foo", []Getter{source1, source2})
   254  
   255  	g.Expect(Has(target, "foo")).To(BeTrue())
   256  }
   257  
   258  func setterWithConditions(conditions ...*clusterv1.Condition) Setter {
   259  	obj := &clusterv1.Cluster{}
   260  	obj.SetConditions(conditionList(conditions...))
   261  	return obj
   262  }
   263  
   264  func nilSetter() Setter {
   265  	var obj *clusterv1.Cluster
   266  	return obj
   267  }
   268  
   269  func haveSameConditionsOf(expected clusterv1.Conditions) types.GomegaMatcher {
   270  	return &ConditionsMatcher{
   271  		Expected: expected,
   272  	}
   273  }
   274  
   275  type ConditionsMatcher struct {
   276  	Expected clusterv1.Conditions
   277  }
   278  
   279  func (matcher *ConditionsMatcher) Match(actual interface{}) (success bool, err error) {
   280  	actualConditions, ok := actual.(clusterv1.Conditions)
   281  	if !ok {
   282  		return false, errors.New("Value should be a conditions list")
   283  	}
   284  
   285  	if len(actualConditions) != len(matcher.Expected) {
   286  		return false, nil
   287  	}
   288  
   289  	for i := range actualConditions {
   290  		if !hasSameState(&actualConditions[i], &matcher.Expected[i]) {
   291  			return false, nil
   292  		}
   293  	}
   294  	return true, nil
   295  }
   296  
   297  func (matcher *ConditionsMatcher) FailureMessage(actual interface{}) (message string) {
   298  	return format.Message(actual, "to have the same conditions of", matcher.Expected)
   299  }
   300  func (matcher *ConditionsMatcher) NegatedFailureMessage(actual interface{}) (message string) {
   301  	return format.Message(actual, "not to have the same conditions of", matcher.Expected)
   302  }