Skip to content

Commit 2277050

Browse files
authored
Refactor Status Updater to be resource agnostic (#1814)
Problem: Currently, the status updater is aware of what resource it is updating the status of. This makes it difficult to extend because for each new resource we add support for, we have to modify the status updater. Solution: Replace old status updater with two new ones (1) Regular Updater, to update statuses of multiple resources via UpdateRequest type. (2) LeaderAwareGroupUpdater to update statuses of groups of resources and save any pending UpdateRequests until the updater is enabled (when pod becomes leader). It uses Regular Updater. Using groups allow replacing UpdateRequests of subset of resources, without the need to recompute UpdateRequests for all resources. The new status package is resource agnostic. This is accomplished by representing status update as a function (Setter). Other updates: - provisioner manager uses regular Updater. - static manager uses LeaderAwareGroupUpdater (because it needs to support leader election) - framework Status setter were simplified and moved to static/status package - status related functions in static package were moved to static/status package. - Previous Build* functions were replaced with PrepareRequests functions. Such functions don't create any intermediate status representation (like it was before), but create status UpdateRequests which directly set the resource statuses. - static manager handler conditionally updates statuses GatewayClass based on manager configuration. Previously, the conditional logic was implemented in the Status Updater. CLOSES -- #1071
1 parent 3d180b2 commit 2277050

36 files changed

+3035
-3268
lines changed

internal/framework/conditions/conditions.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,3 +129,25 @@ func NewGatewayClassConflict() Condition {
129129
Message: GatewayClassMessageGatewayClassConflict,
130130
}
131131
}
132+
133+
// ConvertConditions converts conditions to Kubernetes API conditions.
134+
func ConvertConditions(
135+
conds []Condition,
136+
observedGeneration int64,
137+
transitionTime metav1.Time,
138+
) []metav1.Condition {
139+
apiConds := make([]metav1.Condition, len(conds))
140+
141+
for i := range conds {
142+
apiConds[i] = metav1.Condition{
143+
Type: conds[i].Type,
144+
Status: conds[i].Status,
145+
ObservedGeneration: observedGeneration,
146+
LastTransitionTime: transitionTime,
147+
Reason: conds[i].Reason,
148+
Message: conds[i].Message,
149+
}
150+
}
151+
152+
return apiConds
153+
}

internal/framework/conditions/conditions_test.go

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ import (
88
)
99

1010
func TestDeduplicateConditions(t *testing.T) {
11-
g := NewWithT(t)
12-
1311
conds := []Condition{
1412
{
1513
Type: "Type1",
@@ -56,6 +54,52 @@ func TestDeduplicateConditions(t *testing.T) {
5654
},
5755
}
5856

57+
g := NewWithT(t)
58+
5959
result := DeduplicateConditions(conds)
6060
g.Expect(result).Should(Equal(expected))
6161
}
62+
63+
func TestConvertConditions(t *testing.T) {
64+
conds := []Condition{
65+
{
66+
Type: "Type1",
67+
Status: metav1.ConditionTrue,
68+
Reason: "Reason1",
69+
Message: "Message1",
70+
},
71+
{
72+
Type: "Type2",
73+
Status: metav1.ConditionFalse,
74+
Reason: "Reason2",
75+
Message: "Message2",
76+
},
77+
}
78+
79+
const generation = 3
80+
time := metav1.Now()
81+
82+
expected := []metav1.Condition{
83+
{
84+
Type: "Type1",
85+
Status: metav1.ConditionTrue,
86+
Reason: "Reason1",
87+
Message: "Message1",
88+
LastTransitionTime: time,
89+
ObservedGeneration: generation,
90+
},
91+
{
92+
Type: "Type2",
93+
Status: metav1.ConditionFalse,
94+
Reason: "Reason2",
95+
Message: "Message2",
96+
LastTransitionTime: time,
97+
ObservedGeneration: generation,
98+
},
99+
}
100+
101+
g := NewWithT(t)
102+
103+
result := ConvertConditions(conds, generation, time)
104+
g.Expect(result).Should(Equal(expected))
105+
}

internal/framework/helpers/helpers.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
// Package helpers contains helper functions for unit tests.
1+
// Package helpers contains helper functions
22
package helpers
33

44
import (
55
"fmt"
66

77
"github.com/google/go-cmp/cmp"
88
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
9+
"sigs.k8s.io/controller-runtime/pkg/client"
910
)
1011

1112
// Diff prints the diff between two structs.
@@ -41,3 +42,12 @@ func PrepareTimeForFakeClient(t metav1.Time) metav1.Time {
4142

4243
return t
4344
}
45+
46+
// MustCastObject casts the client.Object to the specified type that implements it.
47+
func MustCastObject[T client.Object](object client.Object) T {
48+
if obj, ok := object.(T); ok {
49+
return obj
50+
}
51+
52+
panic(fmt.Errorf("unexpected object type %T", object))
53+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package helpers
2+
3+
import (
4+
"testing"
5+
6+
. "github.com/onsi/gomega"
7+
8+
"sigs.k8s.io/controller-runtime/pkg/client"
9+
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
10+
gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
11+
)
12+
13+
func TestMustCastObject(t *testing.T) {
14+
g := NewWithT(t)
15+
16+
var obj client.Object = &gatewayv1.Gateway{}
17+
18+
g.Expect(func() {
19+
_ = MustCastObject[*gatewayv1.Gateway](obj)
20+
}).ToNot(Panic())
21+
22+
g.Expect(func() {
23+
_ = MustCastObject[*gatewayv1alpha2.BackendTLSPolicy](obj)
24+
}).To(Panic())
25+
}

internal/framework/status/backend_tls.go

Lines changed: 0 additions & 45 deletions
This file was deleted.

internal/framework/status/backend_tls_test.go

Lines changed: 0 additions & 51 deletions
This file was deleted.

internal/framework/status/clock.go

Lines changed: 0 additions & 25 deletions
This file was deleted.
Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,31 @@
11
package status
22

33
import (
4-
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
4+
"slices"
55

6-
"github.com/nginxinc/nginx-gateway-fabric/internal/framework/conditions"
6+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
77
)
88

9-
func convertConditions(
10-
conds []conditions.Condition,
11-
observedGeneration int64,
12-
transitionTime metav1.Time,
13-
) []metav1.Condition {
14-
apiConds := make([]metav1.Condition, len(conds))
9+
// ConditionsEqual compares conditions.
10+
// It doesn't check the last transition time of conditions.
11+
func ConditionsEqual(prev, cur []metav1.Condition) bool {
12+
return slices.EqualFunc(prev, cur, func(c1, c2 metav1.Condition) bool {
13+
if c1.ObservedGeneration != c2.ObservedGeneration {
14+
return false
15+
}
16+
17+
if c1.Type != c2.Type {
18+
return false
19+
}
20+
21+
if c1.Status != c2.Status {
22+
return false
23+
}
1524

16-
for i := range conds {
17-
apiConds[i] = metav1.Condition{
18-
Type: conds[i].Type,
19-
Status: conds[i].Status,
20-
ObservedGeneration: observedGeneration,
21-
LastTransitionTime: transitionTime,
22-
Reason: conds[i].Reason,
23-
Message: conds[i].Message,
25+
if c1.Message != c2.Message {
26+
return false
2427
}
25-
}
2628

27-
return apiConds
29+
return c1.Reason == c2.Reason
30+
})
2831
}

0 commit comments

Comments
 (0)