Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion docs/gateway-api-compatibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,9 @@ Fields:
* `parents`
* `parentRef` - supported.
* `controllerName` - supported.
* `conditions` - partially supported.
* `conditions` - partially supported. Supported (Condition/Status/Reason):
* `Accepted/True/Accepted`
* `Accepted/False/NoMatchingListenerHostname`

### TLSRoute

Expand Down
128 changes: 98 additions & 30 deletions internal/state/change_processor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package state_test

import (
"context"
"sort"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
Expand All @@ -17,6 +18,7 @@ import (
"github.com/nginxinc/nginx-kubernetes-gateway/internal/helpers"
"github.com/nginxinc/nginx-kubernetes-gateway/internal/manager/index"
"github.com/nginxinc/nginx-kubernetes-gateway/internal/state"
"github.com/nginxinc/nginx-kubernetes-gateway/internal/state/conditions"
"github.com/nginxinc/nginx-kubernetes-gateway/internal/state/relationship"
"github.com/nginxinc/nginx-kubernetes-gateway/internal/state/relationship/relationshipfakes"
"github.com/nginxinc/nginx-kubernetes-gateway/internal/state/statefakes"
Expand Down Expand Up @@ -228,6 +230,24 @@ var _ = Describe("ChangeProcessor", func() {

gw2 = createGatewayWithTLSListener("gateway-2")
})

assertStatuses := func(expected, result state.Statuses) {
sortConditions := func(statuses state.HTTPRouteStatuses) {
for _, status := range statuses {
for _, ps := range status.ParentStatuses {
sort.Slice(ps.Conditions, func(i, j int) bool {
return ps.Conditions[i].Type < ps.Conditions[j].Type
})
}
}
}

sortConditions(expected.HTTPRouteStatuses)
sortConditions(result.HTTPRouteStatuses)

ExpectWithOffset(1, helpers.Diff(expected, result)).To(BeEmpty())
}

When("no upsert has occurred", func() {
It("returns empty configuration and statuses", func() {
changed, conf, statuses := processor.Process(context.TODO())
Expand Down Expand Up @@ -279,8 +299,18 @@ var _ = Describe("ChangeProcessor", func() {
{Namespace: "test", Name: "hr-1"}: {
ObservedGeneration: hr1.Generation,
ParentStatuses: map[string]state.ParentStatus{
"listener-80-1": {Attached: false},
"listener-443-1": {Attached: false},
"listener-80-1": {
Conditions: append(
conditions.NewDefaultRouteConditions(),
conditions.NewRouteTODO("GatewayClass is invalid or doesn't exist"),
),
},
"listener-443-1": {
Conditions: append(
conditions.NewDefaultRouteConditions(),
conditions.NewRouteTODO("GatewayClass is invalid or doesn't exist"),
),
},
},
},
},
Expand All @@ -289,7 +319,7 @@ var _ = Describe("ChangeProcessor", func() {
changed, conf, statuses := processor.Process(context.TODO())
Expect(changed).To(BeTrue())
Expect(helpers.Diff(expectedConf, conf)).To(BeEmpty())
Expect(helpers.Diff(expectedStatuses, statuses)).To(BeEmpty())
assertStatuses(expectedStatuses, statuses)
})
})
})
Expand Down Expand Up @@ -367,8 +397,12 @@ var _ = Describe("ChangeProcessor", func() {
{Namespace: "test", Name: "hr-1"}: {
ObservedGeneration: hr1.Generation,
ParentStatuses: map[string]state.ParentStatus{
"listener-80-1": {Attached: true},
"listener-443-1": {Attached: true},
"listener-80-1": {
Conditions: conditions.NewDefaultRouteConditions(),
},
"listener-443-1": {
Conditions: conditions.NewDefaultRouteConditions(),
},
},
},
},
Expand All @@ -377,7 +411,7 @@ var _ = Describe("ChangeProcessor", func() {
changed, conf, statuses := processor.Process(context.TODO())
Expect(changed).To(BeTrue())
Expect(helpers.Diff(expectedConf, conf)).To(BeEmpty())
Expect(helpers.Diff(expectedStatuses, statuses)).To(BeEmpty())
assertStatuses(expectedStatuses, statuses)
})
})
When("the first HTTPRoute without a generation changed is processed", func() {
Expand Down Expand Up @@ -465,8 +499,12 @@ var _ = Describe("ChangeProcessor", func() {
{Namespace: "test", Name: "hr-1"}: {
ObservedGeneration: hr1Updated.Generation,
ParentStatuses: map[string]state.ParentStatus{
"listener-80-1": {Attached: true},
"listener-443-1": {Attached: true},
"listener-80-1": {
Conditions: conditions.NewDefaultRouteConditions(),
},
"listener-443-1": {
Conditions: conditions.NewDefaultRouteConditions(),
},
},
},
},
Expand All @@ -475,7 +513,7 @@ var _ = Describe("ChangeProcessor", func() {
changed, conf, statuses := processor.Process(context.TODO())
Expect(changed).To(BeTrue())
Expect(helpers.Diff(expectedConf, conf)).To(BeEmpty())
Expect(helpers.Diff(expectedStatuses, statuses)).To(BeEmpty())
assertStatuses(expectedStatuses, statuses)
},
)
})
Expand Down Expand Up @@ -564,8 +602,12 @@ var _ = Describe("ChangeProcessor", func() {
{Namespace: "test", Name: "hr-1"}: {
ObservedGeneration: hr1Updated.Generation,
ParentStatuses: map[string]state.ParentStatus{
"listener-80-1": {Attached: true},
"listener-443-1": {Attached: true},
"listener-80-1": {
Conditions: conditions.NewDefaultRouteConditions(),
},
"listener-443-1": {
Conditions: conditions.NewDefaultRouteConditions(),
},
},
},
},
Expand All @@ -574,7 +616,7 @@ var _ = Describe("ChangeProcessor", func() {
changed, conf, statuses := processor.Process(context.TODO())
Expect(changed).To(BeTrue())
Expect(helpers.Diff(expectedConf, conf)).To(BeEmpty())
Expect(helpers.Diff(expectedStatuses, statuses)).To(BeEmpty())
assertStatuses(expectedStatuses, statuses)
})
})
When("the GatewayClass update without generation change is processed", func() {
Expand Down Expand Up @@ -662,8 +704,12 @@ var _ = Describe("ChangeProcessor", func() {
{Namespace: "test", Name: "hr-1"}: {
ObservedGeneration: hr1Updated.Generation,
ParentStatuses: map[string]state.ParentStatus{
"listener-80-1": {Attached: true},
"listener-443-1": {Attached: true},
"listener-80-1": {
Conditions: conditions.NewDefaultRouteConditions(),
},
"listener-443-1": {
Conditions: conditions.NewDefaultRouteConditions(),
},
},
},
},
Expand All @@ -672,7 +718,7 @@ var _ = Describe("ChangeProcessor", func() {
changed, conf, statuses := processor.Process(context.TODO())
Expect(changed).To(BeTrue())
Expect(helpers.Diff(expectedConf, conf)).To(BeEmpty())
Expect(helpers.Diff(expectedStatuses, statuses)).To(BeEmpty())
assertStatuses(expectedStatuses, statuses)
})
})
When("no changes are captured", func() {
Expand Down Expand Up @@ -763,8 +809,12 @@ var _ = Describe("ChangeProcessor", func() {
{Namespace: "test", Name: "hr-1"}: {
ObservedGeneration: hr1Updated.Generation,
ParentStatuses: map[string]state.ParentStatus{
"listener-80-1": {Attached: true},
"listener-443-1": {Attached: true},
"listener-80-1": {
Conditions: conditions.NewDefaultRouteConditions(),
},
"listener-443-1": {
Conditions: conditions.NewDefaultRouteConditions(),
},
},
},
},
Expand All @@ -773,7 +823,7 @@ var _ = Describe("ChangeProcessor", func() {
changed, conf, statuses := processor.Process(context.TODO())
Expect(changed).To(BeTrue())
Expect(helpers.Diff(expectedConf, conf)).To(BeEmpty())
Expect(helpers.Diff(expectedStatuses, statuses)).To(BeEmpty())
assertStatuses(expectedStatuses, statuses)
})
})
When("the second HTTPRoute is upserted", func() {
Expand Down Expand Up @@ -853,15 +903,29 @@ var _ = Describe("ChangeProcessor", func() {
{Namespace: "test", Name: "hr-1"}: {
ObservedGeneration: hr1Updated.Generation,
ParentStatuses: map[string]state.ParentStatus{
"listener-80-1": {Attached: true},
"listener-443-1": {Attached: true},
"listener-80-1": {
Conditions: conditions.NewDefaultRouteConditions(),
},
"listener-443-1": {
Conditions: conditions.NewDefaultRouteConditions(),
},
},
},
{Namespace: "test", Name: "hr-2"}: {
ObservedGeneration: hr2.Generation,
ParentStatuses: map[string]state.ParentStatus{
"listener-80-1": {Attached: false},
"listener-443-1": {Attached: false},
"listener-80-1": {
Conditions: append(
conditions.NewDefaultRouteConditions(),
conditions.NewRouteTODO("Gateway is ignored"),
),
},
"listener-443-1": {
Conditions: append(
conditions.NewDefaultRouteConditions(),
conditions.NewRouteTODO("Gateway is ignored"),
),
},
},
},
},
Expand All @@ -870,7 +934,7 @@ var _ = Describe("ChangeProcessor", func() {
changed, conf, statuses := processor.Process(context.TODO())
Expect(changed).To(BeTrue())
Expect(helpers.Diff(expectedConf, conf)).To(BeEmpty())
Expect(helpers.Diff(expectedStatuses, statuses)).To(BeEmpty())
assertStatuses(expectedStatuses, statuses)
})
})
When("the first Gateway is deleted", func() {
Expand Down Expand Up @@ -949,8 +1013,12 @@ var _ = Describe("ChangeProcessor", func() {
{Namespace: "test", Name: "hr-2"}: {
ObservedGeneration: hr2.Generation,
ParentStatuses: map[string]state.ParentStatus{
"listener-80-1": {Attached: true},
"listener-443-1": {Attached: true},
"listener-80-1": {
Conditions: conditions.NewDefaultRouteConditions(),
},
"listener-443-1": {
Conditions: conditions.NewDefaultRouteConditions(),
},
},
},
},
Expand All @@ -959,7 +1027,7 @@ var _ = Describe("ChangeProcessor", func() {
changed, conf, statuses := processor.Process(context.TODO())
Expect(changed).To(BeTrue())
Expect(helpers.Diff(expectedConf, conf)).To(BeEmpty())
Expect(helpers.Diff(expectedStatuses, statuses)).To(BeEmpty())
assertStatuses(expectedStatuses, statuses)
})
})
When("the second HTTPRoute is deleted", func() {
Expand Down Expand Up @@ -1003,7 +1071,7 @@ var _ = Describe("ChangeProcessor", func() {
changed, conf, statuses := processor.Process(context.TODO())
Expect(changed).To(BeTrue())
Expect(helpers.Diff(expectedConf, conf)).To(BeEmpty())
Expect(helpers.Diff(expectedStatuses, statuses)).To(BeEmpty())
assertStatuses(expectedStatuses, statuses)
})
})
When("the GatewayClass is deleted", func() {
Expand Down Expand Up @@ -1035,7 +1103,7 @@ var _ = Describe("ChangeProcessor", func() {
changed, conf, statuses := processor.Process(context.TODO())
Expect(changed).To(BeTrue())
Expect(helpers.Diff(expectedConf, conf)).To(BeEmpty())
Expect(helpers.Diff(expectedStatuses, statuses)).To(BeEmpty())
assertStatuses(expectedStatuses, statuses)
})
})
When("the second Gateway is deleted", func() {
Expand All @@ -1054,7 +1122,7 @@ var _ = Describe("ChangeProcessor", func() {
changed, conf, statuses := processor.Process(context.TODO())
Expect(changed).To(BeTrue())
Expect(helpers.Diff(expectedConf, conf)).To(BeEmpty())
Expect(helpers.Diff(expectedStatuses, statuses)).To(BeEmpty())
assertStatuses(expectedStatuses, statuses)
})
})
When("the first HTTPRoute is deleted", func() {
Expand All @@ -1073,7 +1141,7 @@ var _ = Describe("ChangeProcessor", func() {
changed, conf, statuses := processor.Process(context.TODO())
Expect(changed).To(BeTrue())
Expect(helpers.Diff(expectedConf, conf)).To(BeEmpty())
Expect(helpers.Diff(expectedStatuses, statuses)).To(BeEmpty())
assertStatuses(expectedStatuses, statuses)
})
})
})
Expand Down
72 changes: 72 additions & 0 deletions internal/state/conditions/httproute.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package conditions

import (
"fmt"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/gateway-api/apis/v1beta1"
)

// RouteCondition defines a condition to be reported in the status of an HTTPRoute.
type RouteCondition struct {
Type v1beta1.RouteConditionType
Status metav1.ConditionStatus
Reason v1beta1.RouteConditionReason
Message string
}

// DeduplicateRouteConditions removes duplicate conditions based on the condition type.
// The last condition wins.
func DeduplicateRouteConditions(conds []RouteCondition) []RouteCondition {
uniqueConds := make(map[v1beta1.RouteConditionType]RouteCondition)

for _, cond := range conds {
uniqueConds[cond.Type] = cond
}

result := make([]RouteCondition, 0, len(uniqueConds))

for _, cond := range uniqueConds {
result = append(result, cond)
}

return result
}

// NewDefaultRouteConditions returns the default conditions that must be present in the status of an HTTPRoute.
func NewDefaultRouteConditions() []RouteCondition {
return []RouteCondition{
NewRouteAccepted(),
}
}

// NewRouteNoMatchingListenerHostname returns a RouteCondition that indicates that the hostname of the listener
// does not match the hostnames of the HTTPRoute.
func NewRouteNoMatchingListenerHostname() RouteCondition {
return RouteCondition{
Type: v1beta1.RouteConditionAccepted,
Status: metav1.ConditionFalse,
Reason: v1beta1.RouteReasonNoMatchingListenerHostname,
Message: "Listener hostname does not match the HTTPRoute hostnames",
}
}

// NewRouteAccepted returns a RouteCondition that indicates that the HTTPRoute is accepted.
func NewRouteAccepted() RouteCondition {
return RouteCondition{
Type: v1beta1.RouteConditionAccepted,
Status: metav1.ConditionTrue,
Reason: v1beta1.RouteReasonAccepted,
Message: "The route is accepted",
}
}

// NewRouteTODO returns a RouteCondition that can be used as a placeholder for a condition that is not yet implemented.
func NewRouteTODO(msg string) RouteCondition {
return RouteCondition{
Type: "TODO",
Status: metav1.ConditionTrue,
Reason: "TODO",
Message: fmt.Sprintf("The condition for this has not been implemented yet: %s", msg),
}
}
Loading