...

Source file src/edge-infra.dev/pkg/k8s/runtime/conditions/setter_test.go

Documentation: edge-infra.dev/pkg/k8s/runtime/conditions

     1  package conditions
     2  
     3  import (
     4  	"testing"
     5  	"time"
     6  
     7  	. "github.com/onsi/gomega" //nolint:revive // TODO(aw185176): remove
     8  	"github.com/onsi/gomega/format"
     9  	"github.com/onsi/gomega/types"
    10  	"github.com/pkg/errors"
    11  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    12  
    13  	"edge-infra.dev/pkg/k8s/meta/status"
    14  	"edge-infra.dev/pkg/k8s/object/fobject"
    15  )
    16  
    17  func TestHasSameState(t *testing.T) {
    18  	g := NewWithT(t)
    19  
    20  	// same condition
    21  	true2 := true1.DeepCopy()
    22  	g.Expect(hasSameState(true1, true2)).To(BeTrue())
    23  
    24  	// different LastTransitionTime does not impact state
    25  	true2 = true1.DeepCopy()
    26  	true2.LastTransitionTime = metav1.NewTime(time.Date(1900, time.November, 10, 23, 0, 0, 0, time.UTC))
    27  	g.Expect(hasSameState(true1, true2)).To(BeTrue())
    28  
    29  	// different ObservedGeneration does not impact state
    30  	true2 = true1.DeepCopy()
    31  	true2.ObservedGeneration = 1
    32  	g.Expect(hasSameState(true1, true2)).To(BeTrue())
    33  
    34  	// different Type, Status, Reason, and Message determine
    35  	// different state
    36  	true2 = true1.DeepCopy()
    37  	true2.Type = "another type"
    38  	g.Expect(hasSameState(true1, true2)).To(BeFalse())
    39  
    40  	true2 = true1.DeepCopy()
    41  	true2.Status = metav1.ConditionFalse
    42  	g.Expect(hasSameState(true1, true2)).To(BeFalse())
    43  
    44  	true2 = true1.DeepCopy()
    45  	true2.Message = "another message"
    46  	g.Expect(hasSameState(true1, true2)).To(BeFalse())
    47  }
    48  
    49  func TestLexicographicLess(t *testing.T) {
    50  	g := NewWithT(t)
    51  
    52  	// alphabetical order of Type is respected
    53  	a := TrueCondition("A", "", "")
    54  	b := TrueCondition("B", "", "")
    55  	g.Expect(lexicographicLess(a, b)).To(BeTrue())
    56  
    57  	a = TrueCondition("B", "", "")
    58  	b = TrueCondition("A", "", "")
    59  	g.Expect(lexicographicLess(a, b)).To(BeFalse())
    60  
    61  	// observed generation is respected
    62  	a = TrueCondition("A", "", "")
    63  	a.ObservedGeneration = 2
    64  	b = TrueCondition("B", "", "")
    65  	b.ObservedGeneration = 2
    66  	g.Expect(lexicographicLess(a, b)).To(BeTrue())
    67  
    68  	a = TrueCondition("A", "", "")
    69  	a.ObservedGeneration = 1
    70  	b = TrueCondition("B", "", "")
    71  	b.ObservedGeneration = 2
    72  	g.Expect(lexicographicLess(a, b)).To(BeFalse())
    73  
    74  	a = TrueCondition("A", "", "")
    75  	a.ObservedGeneration = 1
    76  	b = TrueCondition("B", "", "")
    77  	b.ObservedGeneration = 0
    78  	g.Expect(lexicographicLess(a, b)).To(BeTrue())
    79  
    80  	// Disregard Type when observed generations aren't equal.
    81  	c := TrueCondition("C", "", "")
    82  	c.ObservedGeneration = 1
    83  	b = TrueCondition("B", "", "")
    84  	b.ObservedGeneration = 0
    85  	g.Expect(lexicographicLess(c, b)).To(BeTrue())
    86  
    87  	// Stalled, Ready, and Reconciling conditions are threaded as an
    88  	// exception and always go first.
    89  	stalled := TrueCondition(status.StalledCondition, "", "")
    90  	ready := FalseCondition(status.ReadyCondition, "", "")
    91  	reconciling := TrueCondition(status.ReconcilingCondition, "", "")
    92  
    93  	g.Expect(lexicographicLess(stalled, ready)).To(BeTrue())
    94  	g.Expect(lexicographicLess(ready, stalled)).To(BeFalse())
    95  
    96  	g.Expect(lexicographicLess(reconciling, ready)).To(BeTrue())
    97  	g.Expect(lexicographicLess(ready, reconciling)).To(BeFalse())
    98  
    99  	g.Expect(lexicographicLess(stalled, reconciling)).To(BeTrue())
   100  	g.Expect(lexicographicLess(reconciling, stalled)).To(BeFalse())
   101  
   102  	g.Expect(lexicographicLess(ready, b)).To(BeTrue())
   103  	g.Expect(lexicographicLess(b, ready)).To(BeFalse())
   104  
   105  	ready.ObservedGeneration = 1
   106  	b.ObservedGeneration = 2
   107  	g.Expect(lexicographicLess(ready, b)).To(BeTrue())
   108  }
   109  
   110  func TestSet(t *testing.T) {
   111  	a := TrueCondition("a", "", "")
   112  	b := TrueCondition("b", "", "")
   113  	ready := TrueCondition(status.ReadyCondition, "", "")
   114  
   115  	tests := []struct {
   116  		name      string
   117  		to        Setter
   118  		condition *metav1.Condition
   119  		want      []metav1.Condition
   120  	}{
   121  		{
   122  			name:      "Set adds a condition",
   123  			to:        setterWithConditions(),
   124  			condition: a,
   125  			want:      conditionList(a),
   126  		},
   127  		{
   128  			name:      "Set adds more conditions",
   129  			to:        setterWithConditions(a),
   130  			condition: b,
   131  			want:      conditionList(a, b),
   132  		},
   133  		{
   134  			name:      "Set does not duplicate existing conditions",
   135  			to:        setterWithConditions(a, b),
   136  			condition: a,
   137  			want:      conditionList(a, b),
   138  		},
   139  		{
   140  			name:      "Set sorts conditions in lexicographic order",
   141  			to:        setterWithConditions(b, a),
   142  			condition: ready,
   143  			want:      conditionList(ready, a, b),
   144  		},
   145  	}
   146  
   147  	for _, tt := range tests {
   148  		t.Run(tt.name, func(t *testing.T) {
   149  			g := NewWithT(t)
   150  
   151  			Set(tt.to, tt.condition)
   152  
   153  			g.Expect(tt.to.GetConditions()).To(haveSameConditionsOf(tt.want))
   154  		})
   155  	}
   156  }
   157  
   158  func TestSetLastTransitionTime(t *testing.T) {
   159  	x := metav1.Date(2012, time.January, 1, 12, 15, 30, 5e8, time.UTC)
   160  
   161  	foo := FalseCondition("foo", "reason foo", "message foo")
   162  	fooWithLastTransitionTime := FalseCondition("foo", "reason foo", "message foo")
   163  	fooWithLastTransitionTime.LastTransitionTime = x
   164  	fooWithAnotherState := TrueCondition("foo", "", "")
   165  
   166  	tests := []struct {
   167  		name                    string
   168  		to                      Setter
   169  		new                     *metav1.Condition
   170  		LastTransitionTimeCheck func(*WithT, metav1.Time)
   171  	}{
   172  		{
   173  			name: "Set a condition that does not exists should set the last transition time if not defined",
   174  			to:   setterWithConditions(),
   175  			new:  foo,
   176  			LastTransitionTimeCheck: func(g *WithT, lastTransitionTime metav1.Time) {
   177  				g.Expect(lastTransitionTime).ToNot(BeZero())
   178  			},
   179  		},
   180  		{
   181  			name: "Set a condition that does not exists should preserve the last transition time if defined",
   182  			to:   setterWithConditions(),
   183  			new:  fooWithLastTransitionTime,
   184  			LastTransitionTimeCheck: func(g *WithT, lastTransitionTime metav1.Time) {
   185  				g.Expect(lastTransitionTime).To(Equal(x))
   186  			},
   187  		},
   188  		{
   189  			name: "Set a condition that already exists with the same state should preserves the last transition time",
   190  			to:   setterWithConditions(fooWithLastTransitionTime),
   191  			new:  foo,
   192  			LastTransitionTimeCheck: func(g *WithT, lastTransitionTime metav1.Time) {
   193  				g.Expect(lastTransitionTime).To(Equal(x))
   194  			},
   195  		},
   196  		{
   197  			name: "Set a condition that already exists but with different state should changes the last transition time",
   198  			to:   setterWithConditions(fooWithLastTransitionTime),
   199  			new:  fooWithAnotherState,
   200  			LastTransitionTimeCheck: func(g *WithT, lastTransitionTime metav1.Time) {
   201  				g.Expect(lastTransitionTime).ToNot(Equal(x))
   202  			},
   203  		},
   204  	}
   205  
   206  	for _, tt := range tests {
   207  		t.Run(tt.name, func(t *testing.T) {
   208  			g := NewWithT(t)
   209  
   210  			Set(tt.to, tt.new)
   211  
   212  			tt.LastTransitionTimeCheck(g, Get(tt.to, "foo").LastTransitionTime)
   213  		})
   214  	}
   215  }
   216  
   217  func TestSetObservedGeneration(t *testing.T) {
   218  	g := NewWithT(t)
   219  
   220  	obj := &fobject.Fake{}
   221  	x := metav1.Date(2012, time.January, 1, 12, 15, 30, 5e8, time.UTC)
   222  
   223  	// Conditions with stale observed generation.
   224  	foo1 := FalseCondition("foo1", "reasonFoo1", "messageFoo1")
   225  	foo1.ObservedGeneration = 2
   226  	foo1.LastTransitionTime = x
   227  	foo2 := TrueCondition("foo2", "reasonFoo2", "messageFoo2")
   228  	foo2.ObservedGeneration = 3
   229  	foo2.LastTransitionTime = x
   230  
   231  	// Object with higher generation and stale conditions.
   232  	obj.Generation = 4
   233  	obj.SetConditions([]metav1.Condition{*foo1, *foo2})
   234  
   235  	// Ensure the conditions haven't updated.
   236  	g.Expect(Get(obj, "foo1").ObservedGeneration).To(BeEquivalentTo(2))
   237  	g.Expect(Get(obj, "foo2").ObservedGeneration).To(BeEquivalentTo(3))
   238  
   239  	// Update the object's generation and Set the conditions without any state
   240  	// change.
   241  	obj.Generation = 5
   242  	Set(obj, foo1)
   243  	Set(obj, foo2)
   244  
   245  	// ObservedGeneration is updated but not the LastTransitionTime.
   246  	g.Expect(Get(obj, "foo1").ObservedGeneration).To(BeEquivalentTo(5))
   247  	g.Expect(Get(obj, "foo1").LastTransitionTime).To(Equal(x))
   248  	g.Expect(Get(obj, "foo2").ObservedGeneration).To(BeEquivalentTo(5))
   249  	g.Expect(Get(obj, "foo2").LastTransitionTime).To(Equal(x))
   250  }
   251  
   252  func TestMarkMethods(t *testing.T) {
   253  	g := NewWithT(t)
   254  
   255  	obj := &fobject.Fake{}
   256  
   257  	// test MarkTrue
   258  	MarkTrue(obj, "conditionFoo", "reasonFoo", "messageFoo")
   259  	g.Expect(Get(obj, "conditionFoo")).To(HaveSameStateOf(&metav1.Condition{
   260  		Type:    "conditionFoo",
   261  		Status:  metav1.ConditionTrue,
   262  		Reason:  "reasonFoo",
   263  		Message: "messageFoo",
   264  	}))
   265  
   266  	// test MarkFalse
   267  	MarkFalse(obj, "conditionBar", "reasonBar", "messageBar")
   268  	g.Expect(Get(obj, "conditionBar")).To(HaveSameStateOf(&metav1.Condition{
   269  		Type:    "conditionBar",
   270  		Status:  metav1.ConditionFalse,
   271  		Reason:  "reasonBar",
   272  		Message: "messageBar",
   273  	}))
   274  
   275  	// test MarkUnknown
   276  	MarkUnknown(obj, "conditionBaz", "reasonBaz", "messageBaz")
   277  	g.Expect(Get(obj, "conditionBaz")).To(HaveSameStateOf(&metav1.Condition{
   278  		Type:    "conditionBaz",
   279  		Status:  metav1.ConditionUnknown,
   280  		Reason:  "reasonBaz",
   281  		Message: "messageBaz",
   282  	}))
   283  
   284  	// test MarkReconciling
   285  	MarkTrue(obj, status.StalledCondition, "reasonStalled", "messageStalled")
   286  	MarkReconciling(obj, "reasonReconciling", "messageReconciling")
   287  	g.Expect(Get(obj, status.ReconcilingCondition)).To(HaveSameStateOf(&metav1.Condition{
   288  		Type:    status.ReconcilingCondition,
   289  		Status:  metav1.ConditionTrue,
   290  		Reason:  "reasonReconciling",
   291  		Message: "messageReconciling",
   292  	}))
   293  	g.Expect(IsUnknown(obj, status.StalledCondition)).To(BeTrue())
   294  
   295  	// test MarkStalled
   296  	MarkTrue(obj, status.ReconcilingCondition, "reasonReconciling", "messageReconciling")
   297  	MarkStalled(obj, "reasonStalled", "messageStalled")
   298  	g.Expect(Get(obj, status.StalledCondition)).To(HaveSameStateOf(&metav1.Condition{
   299  		Type:    status.StalledCondition,
   300  		Status:  metav1.ConditionTrue,
   301  		Reason:  "reasonStalled",
   302  		Message: "messageStalled",
   303  	}))
   304  	g.Expect(IsUnknown(obj, status.ReconcilingCondition)).To(BeTrue())
   305  }
   306  
   307  func TestSetSummary(t *testing.T) {
   308  	g := NewWithT(t)
   309  	target := setterWithConditions(TrueCondition("foo", "", ""))
   310  
   311  	SetSummary(target, "test")
   312  
   313  	g.Expect(Has(target, "test")).To(BeTrue())
   314  }
   315  
   316  func TestSetMirror(t *testing.T) {
   317  	g := NewWithT(t)
   318  	source := getterWithConditions(TrueCondition(status.ReadyCondition, "", ""))
   319  	target := setterWithConditions()
   320  
   321  	SetMirror(target, "foo", source)
   322  
   323  	g.Expect(Has(target, "foo")).To(BeTrue())
   324  }
   325  
   326  func TestSetAggregate(t *testing.T) {
   327  	g := NewWithT(t)
   328  	source1 := getterWithConditions(TrueCondition(status.ReadyCondition, "", ""))
   329  	source2 := getterWithConditions(TrueCondition(status.ReadyCondition, "", ""))
   330  	target := setterWithConditions()
   331  
   332  	SetAggregate(target, "foo", []Getter{source1, source2})
   333  
   334  	g.Expect(Has(target, "foo")).To(BeTrue())
   335  }
   336  
   337  func setterWithConditions(conditions ...*metav1.Condition) Setter {
   338  	obj := &fobject.Fake{}
   339  	obj.SetConditions(conditionList(conditions...))
   340  	return obj
   341  }
   342  
   343  func haveSameConditionsOf(expected []metav1.Condition) types.GomegaMatcher {
   344  	return &ConditionsMatcher{
   345  		Expected: expected,
   346  	}
   347  }
   348  
   349  type ConditionsMatcher struct {
   350  	Expected []metav1.Condition
   351  }
   352  
   353  func (matcher *ConditionsMatcher) Match(actual interface{}) (success bool, err error) {
   354  	actualConditions, ok := actual.([]metav1.Condition)
   355  	if !ok {
   356  		return false, errors.New("Value should be a conditions list")
   357  	}
   358  
   359  	if len(actualConditions) != len(matcher.Expected) {
   360  		return false, nil
   361  	}
   362  
   363  	for i := range actualConditions {
   364  		if !hasSameState(&actualConditions[i], &matcher.Expected[i]) {
   365  			return false, nil
   366  		}
   367  	}
   368  	return true, nil
   369  }
   370  
   371  func (matcher *ConditionsMatcher) FailureMessage(actual interface{}) (message string) {
   372  	return format.Message(actual, "to have the same conditions of", matcher.Expected)
   373  }
   374  func (matcher *ConditionsMatcher) NegatedFailureMessage(actual interface{}) (message string) {
   375  	return format.Message(actual, "not to have the same conditions of", matcher.Expected)
   376  }
   377  

View as plain text