Skip to content

Commit 2337b81

Browse files
committed
make no matching conditions result in unknown, not success
1 parent 23ffddb commit 2337b81

File tree

3 files changed

+80
-38
lines changed

3 files changed

+80
-38
lines changed

pkg/operator/status/condition.go

Lines changed: 61 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -11,48 +11,78 @@ import (
1111

1212
// conditionMergeState indicates whether you want to merge all Falses or merge all Trues. For instance, Failures merge
1313
// on true, but Available merges on false. Thing of it like an anti-default.
14-
func unionCondition(conditionType string, conditionMergeState operatorv1.ConditionStatus, allConditions ...operatorv1.OperatorCondition) configv1.ClusterOperatorStatusCondition {
15-
var interestingConditions []operatorv1.OperatorCondition
14+
func unionCondition(conditionType string, defaultConditionState operatorv1.ConditionStatus, allConditions ...operatorv1.OperatorCondition) configv1.ClusterOperatorStatusCondition {
15+
var oppositeConditionStatus operatorv1.ConditionStatus
16+
if defaultConditionState == operatorv1.ConditionTrue {
17+
oppositeConditionStatus = operatorv1.ConditionFalse
18+
} else {
19+
oppositeConditionStatus = operatorv1.ConditionTrue
20+
}
21+
22+
interestingConditions := []operatorv1.OperatorCondition{}
23+
badConditions := []operatorv1.OperatorCondition{}
1624
for _, condition := range allConditions {
17-
if strings.HasSuffix(condition.Type, conditionType) && condition.Status == conditionMergeState {
25+
if strings.HasSuffix(condition.Type, conditionType) {
1826
interestingConditions = append(interestingConditions, condition)
27+
28+
if condition.Status == oppositeConditionStatus {
29+
badConditions = append(badConditions, condition)
30+
}
1931
}
2032
}
2133

2234
unionedCondition := operatorv1.OperatorCondition{Type: conditionType, Status: operatorv1.ConditionUnknown}
23-
if len(interestingConditions) > 0 {
24-
unionedCondition.Status = conditionMergeState
25-
var messages []string
26-
latestTransitionTime := metav1.Time{}
27-
for _, condition := range interestingConditions {
28-
if latestTransitionTime.Before(&condition.LastTransitionTime) {
29-
latestTransitionTime = condition.LastTransitionTime
30-
}
35+
if len(interestingConditions) == 0 {
36+
unionedCondition.Status = operatorv1.ConditionUnknown
37+
unionedCondition.Reason = "NoData"
38+
return OperatorConditionToClusterOperatorCondition(unionedCondition)
39+
}
3140

32-
if len(condition.Message) == 0 {
33-
continue
34-
}
35-
for _, message := range strings.Split(condition.Message, "\n") {
36-
messages = append(messages, fmt.Sprintf("%s: %s", condition.Type, message))
37-
}
41+
if len(badConditions) == 0 {
42+
unionedCondition.Status = defaultConditionState
43+
unionedCondition.Message = unionMessage(interestingConditions)
44+
unionedCondition.Reason = "AsExpected"
45+
unionedCondition.LastTransitionTime = latestTransitionTime(interestingConditions)
46+
47+
return OperatorConditionToClusterOperatorCondition(unionedCondition)
48+
}
49+
50+
// at this point we have bad conditions
51+
unionedCondition.Status = oppositeConditionStatus
52+
unionedCondition.Message = unionMessage(badConditions)
53+
unionedCondition.Reason = unionReason(badConditions)
54+
unionedCondition.LastTransitionTime = latestTransitionTime(badConditions)
55+
56+
return OperatorConditionToClusterOperatorCondition(unionedCondition)
57+
}
58+
59+
func latestTransitionTime(conditions []operatorv1.OperatorCondition) metav1.Time {
60+
latestTransitionTime := metav1.Time{}
61+
for _, condition := range conditions {
62+
if latestTransitionTime.Before(&condition.LastTransitionTime) {
63+
latestTransitionTime = condition.LastTransitionTime
3864
}
39-
if len(messages) > 0 {
40-
unionedCondition.Message = strings.Join(messages, "\n")
65+
}
66+
return latestTransitionTime
67+
}
68+
69+
func unionMessage(conditions []operatorv1.OperatorCondition) string {
70+
messages := []string{}
71+
for _, condition := range conditions {
72+
if len(condition.Message) == 0 {
73+
continue
4174
}
42-
if len(interestingConditions) == 1 {
43-
unionedCondition.Reason = interestingConditions[0].Type
44-
} else {
45-
unionedCondition.Reason = "MultipleConditionsMatching"
75+
for _, message := range strings.Split(condition.Message, "\n") {
76+
messages = append(messages, fmt.Sprintf("%s: %s", condition.Type, message))
4677
}
47-
unionedCondition.LastTransitionTime = latestTransitionTime
78+
}
79+
return strings.Join(messages, "\n")
80+
}
4881

82+
func unionReason(conditions []operatorv1.OperatorCondition) string {
83+
if len(conditions) == 1 {
84+
return conditions[0].Type
4985
} else {
50-
if conditionMergeState == operatorv1.ConditionTrue {
51-
unionedCondition.Status = operatorv1.ConditionFalse
52-
} else {
53-
unionedCondition.Status = operatorv1.ConditionTrue
54-
}
86+
return "MultipleConditionsMatching"
5587
}
56-
57-
return OperatorConditionToClusterOperatorCondition(unionedCondition)
5888
}

pkg/operator/status/status_controller.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -132,10 +132,10 @@ func (c StatusSyncer) sync() error {
132132
}
133133

134134
clusterOperatorObj.Status.RelatedObjects = c.relatedObjects
135-
configv1helpers.SetStatusCondition(&clusterOperatorObj.Status.Conditions, unionCondition("Failing", operatorv1.ConditionTrue, currentDetailedStatus.Conditions...))
136-
configv1helpers.SetStatusCondition(&clusterOperatorObj.Status.Conditions, unionCondition("Progressing", operatorv1.ConditionTrue, currentDetailedStatus.Conditions...))
137-
configv1helpers.SetStatusCondition(&clusterOperatorObj.Status.Conditions, unionCondition("Available", operatorv1.ConditionFalse, currentDetailedStatus.Conditions...))
138-
configv1helpers.SetStatusCondition(&clusterOperatorObj.Status.Conditions, unionCondition("Upgradeable", operatorv1.ConditionFalse, currentDetailedStatus.Conditions...))
135+
configv1helpers.SetStatusCondition(&clusterOperatorObj.Status.Conditions, unionCondition("Failing", operatorv1.ConditionFalse, currentDetailedStatus.Conditions...))
136+
configv1helpers.SetStatusCondition(&clusterOperatorObj.Status.Conditions, unionCondition("Progressing", operatorv1.ConditionFalse, currentDetailedStatus.Conditions...))
137+
configv1helpers.SetStatusCondition(&clusterOperatorObj.Status.Conditions, unionCondition("Available", operatorv1.ConditionTrue, currentDetailedStatus.Conditions...))
138+
configv1helpers.SetStatusCondition(&clusterOperatorObj.Status.Conditions, unionCondition("Upgradeable", operatorv1.ConditionTrue, currentDetailedStatus.Conditions...))
139139

140140
// TODO work out removal. We don't always know the existing value, so removing early seems like a bad idea. Perhaps a remove flag.
141141
versions := c.versionGetter.GetVersions()

pkg/operator/status/status_controller_test.go

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package status
22

33
import (
4-
"fmt"
54
"reflect"
65
"strings"
76
"testing"
@@ -20,25 +19,36 @@ import (
2019
func TestFailing(t *testing.T) {
2120

2221
testCases := []struct {
22+
name string
2323
conditions []operatorv1.OperatorCondition
2424
expectedFailingStatus configv1.ConditionStatus
2525
expectedMessages []string
2626
expectedReason string
2727
}{
2828
{
29+
name: "no data",
30+
conditions: []operatorv1.OperatorCondition{},
31+
expectedFailingStatus: configv1.ConditionUnknown,
32+
expectedReason: "NoData",
33+
},
34+
{
35+
name: "one failing false",
2936
conditions: []operatorv1.OperatorCondition{
3037
{Type: "TypeAFailing", Status: operatorv1.ConditionFalse},
3138
},
3239
expectedFailingStatus: configv1.ConditionFalse,
40+
expectedReason: "AsExpected",
3341
},
3442
{
43+
name: "one failing true",
3544
conditions: []operatorv1.OperatorCondition{
3645
{Type: "TypeAFailing", Status: operatorv1.ConditionTrue},
3746
},
3847
expectedFailingStatus: configv1.ConditionTrue,
3948
expectedReason: "TypeAFailing",
4049
},
4150
{
51+
name: "two present, one failing",
4252
conditions: []operatorv1.OperatorCondition{
4353
{Type: "TypeAFailing", Status: operatorv1.ConditionTrue, Message: "a message from type a"},
4454
{Type: "TypeBFailing", Status: operatorv1.ConditionFalse},
@@ -50,6 +60,7 @@ func TestFailing(t *testing.T) {
5060
},
5161
},
5262
{
63+
name: "two present, second one failing",
5364
conditions: []operatorv1.OperatorCondition{
5465
{Type: "TypeAFailing", Status: operatorv1.ConditionFalse},
5566
{Type: "TypeBFailing", Status: operatorv1.ConditionTrue, Message: "a message from type b"},
@@ -61,6 +72,7 @@ func TestFailing(t *testing.T) {
6172
},
6273
},
6374
{
75+
name: "many present, some failing",
6476
conditions: []operatorv1.OperatorCondition{
6577
{Type: "TypeAFailing", Status: operatorv1.ConditionFalse},
6678
{Type: "TypeBFailing", Status: operatorv1.ConditionTrue, Message: "a message from type b\nanother message from type b"},
@@ -76,8 +88,8 @@ func TestFailing(t *testing.T) {
7688
},
7789
},
7890
}
79-
for name, tc := range testCases {
80-
t.Run(fmt.Sprintf("%05d", name), func(t *testing.T) {
91+
for _, tc := range testCases {
92+
t.Run(tc.name, func(t *testing.T) {
8193
clusterOperatorClient := fake.NewSimpleClientset(&configv1.ClusterOperator{
8294
ObjectMeta: metav1.ObjectMeta{Name: "OPERATOR_NAME", ResourceVersion: "12"},
8395
})

0 commit comments

Comments
 (0)