From 730a655602b7bb236e989ab7000872e9f73c3f05 Mon Sep 17 00:00:00 2001 From: Tina Usova Date: Tue, 7 Oct 2025 14:09:19 +0100 Subject: [PATCH 1/7] Warnings for unsupported Gateway and Routes fields --- .../policies/clientsettings/validator.go | 8 +- .../policies/clientsettings/validator_test.go | 12 +- .../policies/observability/validator.go | 12 +- .../policies/observability/validator_test.go | 22 +-- .../policies/policiesfakes/fake_validator.go | 40 ++-- .../policies/upstreamsettings/validator.go | 8 +- .../upstreamsettings/validator_test.go | 10 +- .../nginx/config/policies/validator.go | 8 +- .../nginx/config/policies/validator_test.go | 16 +- .../controller/state/change_processor_test.go | 46 ++--- .../controller/state/conditions/conditions.go | 118 +++++++---- .../state/conditions/conditions_test.go | 12 +- .../controller/state/graph/backend_refs.go | 8 +- .../state/graph/backend_refs_test.go | 32 +-- .../state/graph/backend_tls_policy.go | 20 +- .../state/graph/backend_tls_policy_test.go | 4 +- internal/controller/state/graph/gateway.go | 29 ++- .../state/graph/gateway_listener.go | 24 +-- .../state/graph/gateway_listener_test.go | 6 +- .../controller/state/graph/gateway_test.go | 185 ++++++++++++++++-- .../controller/state/graph/gatewayclass.go | 12 +- .../state/graph/gatewayclass_test.go | 12 +- internal/controller/state/graph/graph_test.go | 22 +-- internal/controller/state/graph/grpcroute.go | 42 +++- .../controller/state/graph/grpcroute_test.go | 154 +++++++++++++-- internal/controller/state/graph/httproute.go | 56 +++++- .../controller/state/graph/httproute_test.go | 163 +++++++++++++-- .../state/graph/multiple_gateways_test.go | 18 +- internal/controller/state/graph/policies.go | 20 +- .../controller/state/graph/policies_test.go | 60 +++--- .../controller/state/graph/route_common.go | 27 +-- .../state/graph/route_common_test.go | 44 ++--- .../controller/state/graph/snippets_filter.go | 4 +- .../state/graph/snippets_filter_test.go | 2 +- internal/controller/state/graph/tlsroute.go | 10 +- .../controller/state/graph/tlsroute_test.go | 18 +- internal/controller/state/graph/validation.go | 10 + .../validationfakes/fake_policy_validator.go | 40 ++-- .../controller/state/validation/validator.go | 4 +- .../controller/status/prepare_requests.go | 18 +- .../status/prepare_requests_test.go | 36 ++-- 41 files changed, 992 insertions(+), 400 deletions(-) diff --git a/internal/controller/nginx/config/policies/clientsettings/validator.go b/internal/controller/nginx/config/policies/clientsettings/validator.go index c4d83864e7..bbbb321546 100644 --- a/internal/controller/nginx/config/policies/clientsettings/validator.go +++ b/internal/controller/nginx/config/policies/clientsettings/validator.go @@ -24,7 +24,7 @@ func NewValidator(genericValidator validation.GenericValidator) *Validator { } // Validate validates the spec of a ClientSettingsPolicy. -func (v *Validator) Validate(policy policies.Policy) []conditions.Condition { +func (v *Validator) Validate(policy policies.Policy) conditions.Conditions { csp := helpers.MustCastObject[*ngfAPI.ClientSettingsPolicy](policy) targetRefPath := field.NewPath("spec").Child("targetRef") @@ -32,11 +32,11 @@ func (v *Validator) Validate(policy policies.Policy) []conditions.Condition { supportedGroups := []gatewayv1.Group{gatewayv1.GroupName} if err := policies.ValidateTargetRef(csp.Spec.TargetRef, targetRefPath, supportedGroups, supportedKinds); err != nil { - return []conditions.Condition{conditions.NewPolicyInvalid(err.Error())} + return conditions.Conditions{conditions.NewPolicyInvalid(err.Error())} } if err := v.validateSettings(csp.Spec); err != nil { - return []conditions.Condition{conditions.NewPolicyInvalid(err.Error())} + return conditions.Conditions{conditions.NewPolicyInvalid(err.Error())} } return nil @@ -46,7 +46,7 @@ func (v *Validator) Validate(policy policies.Policy) []conditions.Condition { func (v *Validator) ValidateGlobalSettings( _ policies.Policy, _ *policies.GlobalSettings, -) []conditions.Condition { +) conditions.Conditions { return nil } diff --git a/internal/controller/nginx/config/policies/clientsettings/validator_test.go b/internal/controller/nginx/config/policies/clientsettings/validator_test.go index 948e79901b..315c729593 100644 --- a/internal/controller/nginx/config/policies/clientsettings/validator_test.go +++ b/internal/controller/nginx/config/policies/clientsettings/validator_test.go @@ -56,7 +56,7 @@ func TestValidator_Validate(t *testing.T) { tests := []struct { name string policy *ngfAPI.ClientSettingsPolicy - expConditions []conditions.Condition + expConditions conditions.Conditions }{ { name: "invalid target ref; unsupported group", @@ -64,7 +64,7 @@ func TestValidator_Validate(t *testing.T) { p.Spec.TargetRef.Group = "Unsupported" return p }), - expConditions: []conditions.Condition{ + expConditions: conditions.Conditions{ conditions.NewPolicyInvalid("spec.targetRef.group: Unsupported value: \"Unsupported\": " + "supported values: \"gateway.networking.k8s.io\""), }, @@ -75,7 +75,7 @@ func TestValidator_Validate(t *testing.T) { p.Spec.TargetRef.Kind = "Unsupported" return p }), - expConditions: []conditions.Condition{ + expConditions: conditions.Conditions{ conditions.NewPolicyInvalid("spec.targetRef.kind: Unsupported value: \"Unsupported\": " + "supported values: \"Gateway\", \"HTTPRoute\", \"GRPCRoute\""), }, @@ -86,7 +86,7 @@ func TestValidator_Validate(t *testing.T) { p.Spec.Body.MaxSize = helpers.GetPointer[ngfAPI.Size]("invalid") return p }), - expConditions: []conditions.Condition{ + expConditions: conditions.Conditions{ conditions.NewPolicyInvalid("spec.body.maxSize: Invalid value: \"invalid\": ^\\d{1,4}(k|m|g)?$ " + "(e.g. '1024', or '8k', or '20m', or '1g', regex used for validation is 'must contain a number. " + "May be followed by 'k', 'm', or 'g', otherwise bytes are assumed')"), @@ -101,7 +101,7 @@ func TestValidator_Validate(t *testing.T) { p.Spec.KeepAlive.Timeout.Header = helpers.GetPointer[ngfAPI.Duration]("invalid") return p }), - expConditions: []conditions.Condition{ + expConditions: conditions.Conditions{ conditions.NewPolicyInvalid( "[spec.body.timeout: Invalid value: \"invalid\": ^[0-9]{1,4}(ms|s|m|h)? " + "(e.g. '5ms', or '10s', or '500m', or '1000h', regex used for validation is " + @@ -123,7 +123,7 @@ func TestValidator_Validate(t *testing.T) { p.Spec.KeepAlive.Timeout.Server = nil return p }), - expConditions: []conditions.Condition{ + expConditions: conditions.Conditions{ conditions.NewPolicyInvalid("spec.keepAlive.timeout: Invalid value: null: " + "server timeout must be set if header timeout is set"), }, diff --git a/internal/controller/nginx/config/policies/observability/validator.go b/internal/controller/nginx/config/policies/observability/validator.go index 43d47dce3e..85ded08802 100644 --- a/internal/controller/nginx/config/policies/observability/validator.go +++ b/internal/controller/nginx/config/policies/observability/validator.go @@ -24,7 +24,7 @@ func NewValidator(genericValidator validation.GenericValidator) *Validator { } // Validate validates the spec of an ObservabilityPolicy. -func (v *Validator) Validate(policy policies.Policy) []conditions.Condition { +func (v *Validator) Validate(policy policies.Policy) conditions.Conditions { obs := helpers.MustCastObject[*ngfAPIv1alpha2.ObservabilityPolicy](policy) targetRefPath := field.NewPath("spec").Child("targetRefs") @@ -33,12 +33,12 @@ func (v *Validator) Validate(policy policies.Policy) []conditions.Condition { for _, ref := range obs.Spec.TargetRefs { if err := policies.ValidateTargetRef(ref, targetRefPath, supportedGroups, supportedKinds); err != nil { - return []conditions.Condition{conditions.NewPolicyInvalid(err.Error())} + return conditions.Conditions{conditions.NewPolicyInvalid(err.Error())} } } if err := v.validateSettings(obs.Spec); err != nil { - return []conditions.Condition{conditions.NewPolicyInvalid(err.Error())} + return conditions.Conditions{conditions.NewPolicyInvalid(err.Error())} } return nil @@ -48,15 +48,15 @@ func (v *Validator) Validate(policy policies.Policy) []conditions.Condition { func (v *Validator) ValidateGlobalSettings( _ policies.Policy, globalSettings *policies.GlobalSettings, -) []conditions.Condition { +) conditions.Conditions { if globalSettings == nil { - return []conditions.Condition{ + return conditions.Conditions{ conditions.NewPolicyNotAcceptedNginxProxyNotSet(conditions.PolicyMessageNginxProxyInvalid), } } if !globalSettings.TelemetryEnabled { - return []conditions.Condition{ + return conditions.Conditions{ conditions.NewPolicyNotAcceptedNginxProxyNotSet(conditions.PolicyMessageTelemetryNotEnabled), } } diff --git a/internal/controller/nginx/config/policies/observability/validator_test.go b/internal/controller/nginx/config/policies/observability/validator_test.go index 7cc283df3c..9681e8be52 100644 --- a/internal/controller/nginx/config/policies/observability/validator_test.go +++ b/internal/controller/nginx/config/policies/observability/validator_test.go @@ -57,7 +57,7 @@ func TestValidator_Validate(t *testing.T) { tests := []struct { name string policy *ngfAPIv1alpha2.ObservabilityPolicy - expConditions []conditions.Condition + expConditions conditions.Conditions }{ { name: "invalid target ref; unsupported group", @@ -65,7 +65,7 @@ func TestValidator_Validate(t *testing.T) { p.Spec.TargetRefs[0].Group = "Unsupported" return p }), - expConditions: []conditions.Condition{ + expConditions: conditions.Conditions{ conditions.NewPolicyInvalid("spec.targetRefs.group: Unsupported value: \"Unsupported\": " + "supported values: \"gateway.networking.k8s.io\""), }, @@ -76,7 +76,7 @@ func TestValidator_Validate(t *testing.T) { p.Spec.TargetRefs[0].Kind = "Unsupported" return p }), - expConditions: []conditions.Condition{ + expConditions: conditions.Conditions{ conditions.NewPolicyInvalid("spec.targetRefs.kind: Unsupported value: \"Unsupported\": " + "supported values: \"HTTPRoute\", \"GRPCRoute\""), }, @@ -87,7 +87,7 @@ func TestValidator_Validate(t *testing.T) { p.Spec.Tracing.Strategy = "invalid" return p }), - expConditions: []conditions.Condition{ + expConditions: conditions.Conditions{ conditions.NewPolicyInvalid("spec.tracing.strategy: Unsupported value: \"invalid\": " + "supported values: \"ratio\", \"parent\""), }, @@ -98,7 +98,7 @@ func TestValidator_Validate(t *testing.T) { p.Spec.Tracing.Context = helpers.GetPointer[ngfAPIv1alpha2.TraceContext]("invalid") return p }), - expConditions: []conditions.Condition{ + expConditions: conditions.Conditions{ conditions.NewPolicyInvalid("spec.tracing.context: Unsupported value: \"invalid\": " + "supported values: \"extract\", \"inject\", \"propagate\", \"ignore\""), }, @@ -109,7 +109,7 @@ func TestValidator_Validate(t *testing.T) { p.Spec.Tracing.SpanName = helpers.GetPointer("invalid$$$") return p }), - expConditions: []conditions.Condition{ + expConditions: conditions.Conditions{ conditions.NewPolicyInvalid("spec.tracing.spanName: Invalid value: \"invalid$$$\": " + "a valid value must have all '\"' escaped and must not contain any '$' or end with an " + "unescaped '\\' (regex used for validation is '([^\"$\\\\]|\\\\[^$])*')"), @@ -121,7 +121,7 @@ func TestValidator_Validate(t *testing.T) { p.Spec.Tracing.SpanAttributes[0].Key = "invalid$$$" return p }), - expConditions: []conditions.Condition{ + expConditions: conditions.Conditions{ conditions.NewPolicyInvalid("spec.tracing.spanAttributes.key: Invalid value: \"invalid$$$\": " + "a valid value must have all '\"' escaped and must not contain any '$' or end with an " + "unescaped '\\' (regex used for validation is '([^\"$\\\\]|\\\\[^$])*')"), @@ -133,7 +133,7 @@ func TestValidator_Validate(t *testing.T) { p.Spec.Tracing.SpanAttributes[0].Value = "invalid$$$" return p }), - expConditions: []conditions.Condition{ + expConditions: conditions.Conditions{ conditions.NewPolicyInvalid("spec.tracing.spanAttributes.value: Invalid value: \"invalid$$$\": " + "a valid value must have all '\"' escaped and must not contain any '$' or end with an " + "unescaped '\\' (regex used for validation is '([^\"$\\\\]|\\\\[^$])*')"), @@ -178,18 +178,18 @@ func TestValidator_ValidateGlobalSettings(t *testing.T) { tests := []struct { name string globalSettings *policies.GlobalSettings - expConditions []conditions.Condition + expConditions conditions.Conditions }{ { name: "global settings are nil", - expConditions: []conditions.Condition{ + expConditions: conditions.Conditions{ conditions.NewPolicyNotAcceptedNginxProxyNotSet(conditions.PolicyMessageNginxProxyInvalid), }, }, { name: "telemetry is not enabled", globalSettings: &policies.GlobalSettings{TelemetryEnabled: false}, - expConditions: []conditions.Condition{ + expConditions: conditions.Conditions{ conditions.NewPolicyNotAcceptedNginxProxyNotSet(conditions.PolicyMessageTelemetryNotEnabled), }, }, diff --git a/internal/controller/nginx/config/policies/policiesfakes/fake_validator.go b/internal/controller/nginx/config/policies/policiesfakes/fake_validator.go index 9389e6ccff..53b716de5e 100644 --- a/internal/controller/nginx/config/policies/policiesfakes/fake_validator.go +++ b/internal/controller/nginx/config/policies/policiesfakes/fake_validator.go @@ -21,28 +21,28 @@ type FakeValidator struct { conflictsReturnsOnCall map[int]struct { result1 bool } - ValidateStub func(policies.Policy) []conditions.Condition + ValidateStub func(policies.Policy) conditions.Conditions validateMutex sync.RWMutex validateArgsForCall []struct { arg1 policies.Policy } validateReturns struct { - result1 []conditions.Condition + result1 conditions.Conditions } validateReturnsOnCall map[int]struct { - result1 []conditions.Condition + result1 conditions.Conditions } - ValidateGlobalSettingsStub func(policies.Policy, *policies.GlobalSettings) []conditions.Condition + ValidateGlobalSettingsStub func(policies.Policy, *policies.GlobalSettings) conditions.Conditions validateGlobalSettingsMutex sync.RWMutex validateGlobalSettingsArgsForCall []struct { arg1 policies.Policy arg2 *policies.GlobalSettings } validateGlobalSettingsReturns struct { - result1 []conditions.Condition + result1 conditions.Conditions } validateGlobalSettingsReturnsOnCall map[int]struct { - result1 []conditions.Condition + result1 conditions.Conditions } invocations map[string][][]interface{} invocationsMutex sync.RWMutex @@ -110,7 +110,7 @@ func (fake *FakeValidator) ConflictsReturnsOnCall(i int, result1 bool) { }{result1} } -func (fake *FakeValidator) Validate(arg1 policies.Policy) []conditions.Condition { +func (fake *FakeValidator) Validate(arg1 policies.Policy) conditions.Conditions { fake.validateMutex.Lock() ret, specificReturn := fake.validateReturnsOnCall[len(fake.validateArgsForCall)] fake.validateArgsForCall = append(fake.validateArgsForCall, struct { @@ -135,7 +135,7 @@ func (fake *FakeValidator) ValidateCallCount() int { return len(fake.validateArgsForCall) } -func (fake *FakeValidator) ValidateCalls(stub func(policies.Policy) []conditions.Condition) { +func (fake *FakeValidator) ValidateCalls(stub func(policies.Policy) conditions.Conditions) { fake.validateMutex.Lock() defer fake.validateMutex.Unlock() fake.ValidateStub = stub @@ -148,30 +148,30 @@ func (fake *FakeValidator) ValidateArgsForCall(i int) policies.Policy { return argsForCall.arg1 } -func (fake *FakeValidator) ValidateReturns(result1 []conditions.Condition) { +func (fake *FakeValidator) ValidateReturns(result1 conditions.Conditions) { fake.validateMutex.Lock() defer fake.validateMutex.Unlock() fake.ValidateStub = nil fake.validateReturns = struct { - result1 []conditions.Condition + result1 conditions.Conditions }{result1} } -func (fake *FakeValidator) ValidateReturnsOnCall(i int, result1 []conditions.Condition) { +func (fake *FakeValidator) ValidateReturnsOnCall(i int, result1 conditions.Conditions) { fake.validateMutex.Lock() defer fake.validateMutex.Unlock() fake.ValidateStub = nil if fake.validateReturnsOnCall == nil { fake.validateReturnsOnCall = make(map[int]struct { - result1 []conditions.Condition + result1 conditions.Conditions }) } fake.validateReturnsOnCall[i] = struct { - result1 []conditions.Condition + result1 conditions.Conditions }{result1} } -func (fake *FakeValidator) ValidateGlobalSettings(arg1 policies.Policy, arg2 *policies.GlobalSettings) []conditions.Condition { +func (fake *FakeValidator) ValidateGlobalSettings(arg1 policies.Policy, arg2 *policies.GlobalSettings) conditions.Conditions { fake.validateGlobalSettingsMutex.Lock() ret, specificReturn := fake.validateGlobalSettingsReturnsOnCall[len(fake.validateGlobalSettingsArgsForCall)] fake.validateGlobalSettingsArgsForCall = append(fake.validateGlobalSettingsArgsForCall, struct { @@ -197,7 +197,7 @@ func (fake *FakeValidator) ValidateGlobalSettingsCallCount() int { return len(fake.validateGlobalSettingsArgsForCall) } -func (fake *FakeValidator) ValidateGlobalSettingsCalls(stub func(policies.Policy, *policies.GlobalSettings) []conditions.Condition) { +func (fake *FakeValidator) ValidateGlobalSettingsCalls(stub func(policies.Policy, *policies.GlobalSettings) conditions.Conditions) { fake.validateGlobalSettingsMutex.Lock() defer fake.validateGlobalSettingsMutex.Unlock() fake.ValidateGlobalSettingsStub = stub @@ -210,26 +210,26 @@ func (fake *FakeValidator) ValidateGlobalSettingsArgsForCall(i int) (policies.Po return argsForCall.arg1, argsForCall.arg2 } -func (fake *FakeValidator) ValidateGlobalSettingsReturns(result1 []conditions.Condition) { +func (fake *FakeValidator) ValidateGlobalSettingsReturns(result1 conditions.Conditions) { fake.validateGlobalSettingsMutex.Lock() defer fake.validateGlobalSettingsMutex.Unlock() fake.ValidateGlobalSettingsStub = nil fake.validateGlobalSettingsReturns = struct { - result1 []conditions.Condition + result1 conditions.Conditions }{result1} } -func (fake *FakeValidator) ValidateGlobalSettingsReturnsOnCall(i int, result1 []conditions.Condition) { +func (fake *FakeValidator) ValidateGlobalSettingsReturnsOnCall(i int, result1 conditions.Conditions) { fake.validateGlobalSettingsMutex.Lock() defer fake.validateGlobalSettingsMutex.Unlock() fake.ValidateGlobalSettingsStub = nil if fake.validateGlobalSettingsReturnsOnCall == nil { fake.validateGlobalSettingsReturnsOnCall = make(map[int]struct { - result1 []conditions.Condition + result1 conditions.Conditions }) } fake.validateGlobalSettingsReturnsOnCall[i] = struct { - result1 []conditions.Condition + result1 conditions.Conditions }{result1} } diff --git a/internal/controller/nginx/config/policies/upstreamsettings/validator.go b/internal/controller/nginx/config/policies/upstreamsettings/validator.go index 9b54fb48e2..c578869df4 100644 --- a/internal/controller/nginx/config/policies/upstreamsettings/validator.go +++ b/internal/controller/nginx/config/policies/upstreamsettings/validator.go @@ -24,7 +24,7 @@ func NewValidator(genericValidator validation.GenericValidator) Validator { } // Validate validates the spec of an UpstreamsSettingsPolicy. -func (v Validator) Validate(policy policies.Policy) []conditions.Condition { +func (v Validator) Validate(policy policies.Policy) conditions.Conditions { usp := helpers.MustCastObject[*ngfAPI.UpstreamSettingsPolicy](policy) targetRefsPath := field.NewPath("spec").Child("targetRefs") @@ -34,12 +34,12 @@ func (v Validator) Validate(policy policies.Policy) []conditions.Condition { for i, ref := range usp.Spec.TargetRefs { indexedPath := targetRefsPath.Index(i) if err := policies.ValidateTargetRef(ref, indexedPath, supportedGroups, supportedKinds); err != nil { - return []conditions.Condition{conditions.NewPolicyInvalid(err.Error())} + return conditions.Conditions{conditions.NewPolicyInvalid(err.Error())} } } if err := v.validateSettings(usp.Spec); err != nil { - return []conditions.Condition{conditions.NewPolicyInvalid(err.Error())} + return conditions.Conditions{conditions.NewPolicyInvalid(err.Error())} } return nil @@ -49,7 +49,7 @@ func (v Validator) Validate(policy policies.Policy) []conditions.Condition { func (v Validator) ValidateGlobalSettings( _ policies.Policy, _ *policies.GlobalSettings, -) []conditions.Condition { +) conditions.Conditions { return nil } diff --git a/internal/controller/nginx/config/policies/upstreamsettings/validator_test.go b/internal/controller/nginx/config/policies/upstreamsettings/validator_test.go index 6ad3d72ca7..3f58be66e3 100644 --- a/internal/controller/nginx/config/policies/upstreamsettings/validator_test.go +++ b/internal/controller/nginx/config/policies/upstreamsettings/validator_test.go @@ -52,7 +52,7 @@ func TestValidator_Validate(t *testing.T) { tests := []struct { name string policy *ngfAPI.UpstreamSettingsPolicy - expConditions []conditions.Condition + expConditions conditions.Conditions }{ { name: "invalid target ref; unsupported group", @@ -66,7 +66,7 @@ func TestValidator_Validate(t *testing.T) { }) return p }), - expConditions: []conditions.Condition{ + expConditions: conditions.Conditions{ conditions.NewPolicyInvalid("spec.targetRefs[1].group: Unsupported value: \"Unsupported\": " + "supported values: \"\", \"core\""), }, @@ -83,7 +83,7 @@ func TestValidator_Validate(t *testing.T) { }) return p }), - expConditions: []conditions.Condition{ + expConditions: conditions.Conditions{ conditions.NewPolicyInvalid("spec.targetRefs[1].kind: Unsupported value: \"Unsupported\": " + "supported values: \"Service\""), }, @@ -94,7 +94,7 @@ func TestValidator_Validate(t *testing.T) { p.Spec.ZoneSize = helpers.GetPointer[ngfAPI.Size]("invalid") return p }), - expConditions: []conditions.Condition{ + expConditions: conditions.Conditions{ conditions.NewPolicyInvalid("spec.zoneSize: Invalid value: \"invalid\": ^\\d{1,4}(k|m|g)?$ " + "(e.g. '1024', or '8k', or '20m', or '1g', regex used for validation is 'must contain a number. " + "May be followed by 'k', 'm', or 'g', otherwise bytes are assumed')"), @@ -107,7 +107,7 @@ func TestValidator_Validate(t *testing.T) { p.Spec.KeepAlive.Timeout = helpers.GetPointer[ngfAPI.Duration]("invalid") return p }), - expConditions: []conditions.Condition{ + expConditions: conditions.Conditions{ conditions.NewPolicyInvalid( "[spec.keepAlive.time: Invalid value: \"invalid\": ^[0-9]{1,4}(ms|s|m|h)? " + "(e.g. '5ms', or '10s', or '500m', or '1000h', regex used for validation is " + diff --git a/internal/controller/nginx/config/policies/validator.go b/internal/controller/nginx/config/policies/validator.go index 5182c9781a..a19e8ea037 100644 --- a/internal/controller/nginx/config/policies/validator.go +++ b/internal/controller/nginx/config/policies/validator.go @@ -16,9 +16,9 @@ import ( //counterfeiter:generate . Validator type Validator interface { // Validate validates an NGF Policy. - Validate(policy Policy) []conditions.Condition + Validate(policy Policy) conditions.Conditions // ValidateGlobalSettings validates an NGF Policy with the NginxProxy settings. - ValidateGlobalSettings(policy Policy, globalSettings *GlobalSettings) []conditions.Condition + ValidateGlobalSettings(policy Policy, globalSettings *GlobalSettings) conditions.Conditions // Conflicts returns true if the two Policies conflict. Conflicts(a, b Policy) bool } @@ -56,7 +56,7 @@ func NewManager( } // Validate validates the policy. -func (m *CompositeValidator) Validate(policy Policy) []conditions.Condition { +func (m *CompositeValidator) Validate(policy Policy) conditions.Conditions { gvk := m.mustExtractGVK(policy) validator, ok := m.validators[gvk] @@ -71,7 +71,7 @@ func (m *CompositeValidator) Validate(policy Policy) []conditions.Condition { func (m *CompositeValidator) ValidateGlobalSettings( policy Policy, globalSettings *GlobalSettings, -) []conditions.Condition { +) conditions.Conditions { gvk := m.mustExtractGVK(policy) validator, ok := m.validators[gvk] diff --git a/internal/controller/nginx/config/policies/validator_test.go b/internal/controller/nginx/config/policies/validator_test.go index f30b7e7790..7116391497 100644 --- a/internal/controller/nginx/config/policies/validator_test.go +++ b/internal/controller/nginx/config/policies/validator_test.go @@ -48,11 +48,11 @@ var _ = Describe("Policy CompositeValidator", func() { mustExtractGVK, policies.ManagerConfig{ Validator: &policiesfakes.FakeValidator{ - ValidateStub: func(_ policies.Policy) []conditions.Condition { - return []conditions.Condition{conditions.NewPolicyInvalid("apple error")} + ValidateStub: func(_ policies.Policy) conditions.Conditions { + return conditions.Conditions{conditions.NewPolicyInvalid("apple error")} }, - ValidateGlobalSettingsStub: func(_ policies.Policy, _ *policies.GlobalSettings) []conditions.Condition { - return []conditions.Condition{conditions.NewPolicyInvalid("apple global settings error")} + ValidateGlobalSettingsStub: func(_ policies.Policy, _ *policies.GlobalSettings) conditions.Conditions { + return conditions.Conditions{conditions.NewPolicyInvalid("apple global settings error")} }, ConflictsStub: func(_ policies.Policy, _ policies.Policy) bool { return true }, }, @@ -60,11 +60,11 @@ var _ = Describe("Policy CompositeValidator", func() { }, policies.ManagerConfig{ Validator: &policiesfakes.FakeValidator{ - ValidateStub: func(_ policies.Policy) []conditions.Condition { - return []conditions.Condition{conditions.NewPolicyInvalid("orange error")} + ValidateStub: func(_ policies.Policy) conditions.Conditions { + return conditions.Conditions{conditions.NewPolicyInvalid("orange error")} }, - ValidateGlobalSettingsStub: func(_ policies.Policy, _ *policies.GlobalSettings) []conditions.Condition { - return []conditions.Condition{conditions.NewPolicyInvalid("orange global settings error")} + ValidateGlobalSettingsStub: func(_ policies.Policy, _ *policies.GlobalSettings) conditions.Conditions { + return conditions.Conditions{conditions.NewPolicyInvalid("orange global settings error")} }, ConflictsStub: func(_ policies.Policy, _ policies.Policy) bool { return false }, }, diff --git a/internal/controller/state/change_processor_test.go b/internal/controller/state/change_processor_test.go index 2d17e6f6e9..d20cce6eee 100644 --- a/internal/controller/state/change_processor_test.go +++ b/internal/controller/state/change_processor_test.go @@ -736,7 +736,7 @@ var _ = Describe("ChangeProcessor", func() { }, Valid: true, Attachable: true, - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ conditions.NewRouteBackendRefRefBackendNotFound( "spec.rules[0].backendRefs[0].name: Not found: \"service\"", ), @@ -804,7 +804,7 @@ var _ = Describe("ChangeProcessor", func() { }, Valid: true, Attachable: true, - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ conditions.NewRouteBackendRefRefBackendNotFound( "spec.rules[0].backendRefs[0].name: Not found: \"service\"", ), @@ -872,7 +872,7 @@ var _ = Describe("ChangeProcessor", func() { }, Valid: true, Attachable: true, - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ conditions.NewRouteBackendRefRefBackendNotFound( "spec.rules[0].backendRefs[0].name: Not found: \"grpc-service\"", ), @@ -940,7 +940,7 @@ var _ = Describe("ChangeProcessor", func() { }, Valid: true, Attachable: true, - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ conditions.NewRouteBackendRefRefBackendNotFound( "spec.rules[0].backendRefs[0].name: Not found: \"grpc-service\"", ), @@ -976,7 +976,7 @@ var _ = Describe("ChangeProcessor", func() { }, Valid: true, Attachable: true, - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ conditions.NewRouteBackendRefRefBackendNotFound( "spec.rules[0].backendRefs[0].name: Not found: \"tls-service\"", ), @@ -1012,7 +1012,7 @@ var _ = Describe("ChangeProcessor", func() { }, Valid: true, Attachable: true, - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ conditions.NewRouteBackendRefRefBackendNotFound( "spec.rules[0].backendRefs[0].name: Not found: \"tls-service\"", ), @@ -1281,21 +1281,21 @@ var _ = Describe("ChangeProcessor", func() { gw.Listeners = nil // no ref grant exists yet for the routes - expGraph.Routes[httpRouteKey1].Conditions = []conditions.Condition{ + expGraph.Routes[httpRouteKey1].Conditions = conditions.Conditions{ conditions.NewRouteBackendRefRefNotPermitted( "spec.rules[0].backendRefs[0].namespace: Forbidden: " + "Backend ref to Service service-ns/service not permitted by any ReferenceGrant", ), } - expGraph.Routes[grpcRouteKey1].Conditions = []conditions.Condition{ + expGraph.Routes[grpcRouteKey1].Conditions = conditions.Conditions{ conditions.NewRouteBackendRefRefNotPermitted( "spec.rules[0].backendRefs[0].namespace: Forbidden: Backend ref to Service " + "grpc-service-ns/grpc-service not permitted by any ReferenceGrant", ), } - expGraph.L4Routes[trKey1].Conditions = []conditions.Condition{ + expGraph.L4Routes[trKey1].Conditions = conditions.Conditions{ conditions.NewRouteBackendRefRefNotPermitted( "spec.rules[0].backendRefs[0].namespace: Forbidden: Backend ref to Service " + "tls-service-ns/tls-service not permitted by any ReferenceGrant", @@ -1305,23 +1305,23 @@ var _ = Describe("ChangeProcessor", func() { // gateway class does not exist so routes cannot attach expGraph.Routes[httpRouteKey1].ParentRefs[0].Attachment = &graph.ParentRefAttachmentStatus{ AcceptedHostnames: map[string][]string{}, - FailedConditions: []conditions.Condition{conditions.NewRouteNoMatchingParent()}, + FailedConditions: conditions.Conditions{conditions.NewRouteNoMatchingParent()}, } expGraph.Routes[httpRouteKey1].ParentRefs[1].Attachment = &graph.ParentRefAttachmentStatus{ AcceptedHostnames: map[string][]string{}, - FailedConditions: []conditions.Condition{conditions.NewRouteNoMatchingParent()}, + FailedConditions: conditions.Conditions{conditions.NewRouteNoMatchingParent()}, } expGraph.Routes[grpcRouteKey1].ParentRefs[0].Attachment = &graph.ParentRefAttachmentStatus{ AcceptedHostnames: map[string][]string{}, - FailedConditions: []conditions.Condition{conditions.NewRouteNoMatchingParent()}, + FailedConditions: conditions.Conditions{conditions.NewRouteNoMatchingParent()}, } expGraph.Routes[grpcRouteKey1].ParentRefs[1].Attachment = &graph.ParentRefAttachmentStatus{ AcceptedHostnames: map[string][]string{}, - FailedConditions: []conditions.Condition{conditions.NewRouteNoMatchingParent()}, + FailedConditions: conditions.Conditions{conditions.NewRouteNoMatchingParent()}, } expGraph.L4Routes[trKey1].ParentRefs[0].Attachment = &graph.ParentRefAttachmentStatus{ AcceptedHostnames: map[string][]string{}, - FailedConditions: []conditions.Condition{conditions.NewRouteNoMatchingParent()}, + FailedConditions: conditions.Conditions{conditions.NewRouteNoMatchingParent()}, } expGraph.ReferencedSecrets = nil @@ -1379,7 +1379,7 @@ var _ = Describe("ChangeProcessor", func() { listener443.Routes[grpcRouteKey1].ParentRefs[1].Attachment = expAttachment443 // no ref grant exists yet for hr1 - expGraph.Routes[httpRouteKey1].Conditions = []conditions.Condition{ + expGraph.Routes[httpRouteKey1].Conditions = conditions.Conditions{ conditions.NewRouteBackendRefRefNotPermitted( "spec.rules[0].backendRefs[0].namespace: Forbidden: Backend ref to Service " + "service-ns/service not permitted by any ReferenceGrant", @@ -1390,7 +1390,7 @@ var _ = Describe("ChangeProcessor", func() { expGraph.Routes[httpRouteKey1].ParentRefs[1].Attachment = expAttachment443 // no ref grant exists yet for gr1 - expGraph.Routes[grpcRouteKey1].Conditions = []conditions.Condition{ + expGraph.Routes[grpcRouteKey1].Conditions = conditions.Conditions{ conditions.NewRouteBackendRefRefNotPermitted( "spec.rules[0].backendRefs[0].namespace: Forbidden: Backend ref to Service " + "grpc-service-ns/grpc-service not permitted by any ReferenceGrant", @@ -1401,7 +1401,7 @@ var _ = Describe("ChangeProcessor", func() { expGraph.Routes[grpcRouteKey1].ParentRefs[1].Attachment = expAttachment443 // no ref grant exists yet for tr1 - expGraph.L4Routes[trKey1].Conditions = []conditions.Condition{ + expGraph.L4Routes[trKey1].Conditions = conditions.Conditions{ conditions.NewRouteBackendRefRefNotPermitted( "spec.rules[0].backendRefs[0].namespace: Forbidden: Backend ref to Service " + "tls-service-ns/tls-service not permitted by any ReferenceGrant", @@ -1423,7 +1423,7 @@ var _ = Describe("ChangeProcessor", func() { processor.CaptureUpsertChange(secretRefGrant) // no ref grant exists yet for hr1 - expGraph.Routes[httpRouteKey1].Conditions = []conditions.Condition{ + expGraph.Routes[httpRouteKey1].Conditions = conditions.Conditions{ conditions.NewRouteBackendRefRefNotPermitted( "spec.rules[0].backendRefs[0].namespace: Forbidden: Backend ref to Service " + "service-ns/service not permitted by any ReferenceGrant", @@ -1431,7 +1431,7 @@ var _ = Describe("ChangeProcessor", func() { } // no ref grant exists yet for gr1 - expGraph.Routes[grpcRouteKey1].Conditions = []conditions.Condition{ + expGraph.Routes[grpcRouteKey1].Conditions = conditions.Conditions{ conditions.NewRouteBackendRefRefNotPermitted( "spec.rules[0].backendRefs[0].namespace: Forbidden: Backend ref to Service " + "grpc-service-ns/grpc-service not permitted by any ReferenceGrant", @@ -1439,7 +1439,7 @@ var _ = Describe("ChangeProcessor", func() { } // no ref grant exists yet for tr1 - expGraph.L4Routes[trKey1].Conditions = []conditions.Condition{ + expGraph.L4Routes[trKey1].Conditions = conditions.Conditions{ conditions.NewRouteBackendRefRefNotPermitted( "spec.rules[0].backendRefs[0].namespace: Forbidden: Backend ref to Service " + "tls-service-ns/tls-service not permitted by any ReferenceGrant", @@ -1471,7 +1471,7 @@ var _ = Describe("ChangeProcessor", func() { processor.CaptureUpsertChange(hrServiceRefGrant) // no ref grant exists yet for gr1 - expGraph.Routes[grpcRouteKey1].Conditions = []conditions.Condition{ + expGraph.Routes[grpcRouteKey1].Conditions = conditions.Conditions{ conditions.NewRouteBackendRefRefNotPermitted( "spec.rules[0].backendRefs[0].namespace: Forbidden: Backend ref to Service " + "grpc-service-ns/grpc-service not permitted by any ReferenceGrant", @@ -1481,7 +1481,7 @@ var _ = Describe("ChangeProcessor", func() { expRouteGR1.Spec.Rules[0].BackendRefs[0].SvcNsName = types.NamespacedName{} // no ref grant exists yet for tr1 - expGraph.L4Routes[trKey1].Conditions = []conditions.Condition{ + expGraph.L4Routes[trKey1].Conditions = conditions.Conditions{ conditions.NewRouteBackendRefRefNotPermitted( "spec.rules[0].backendRefs[0].namespace: Forbidden: Backend ref to Service " + "tls-service-ns/tls-service not permitted by any ReferenceGrant", @@ -1510,7 +1510,7 @@ var _ = Describe("ChangeProcessor", func() { processor.CaptureUpsertChange(grServiceRefGrant) // no ref grant exists yet for tr1 - expGraph.L4Routes[trKey1].Conditions = []conditions.Condition{ + expGraph.L4Routes[trKey1].Conditions = conditions.Conditions{ conditions.NewRouteBackendRefRefNotPermitted( "spec.rules[0].backendRefs[0].namespace: Forbidden: Backend ref to Service " + "tls-service-ns/tls-service not permitted by any ReferenceGrant", diff --git a/internal/controller/state/conditions/conditions.go b/internal/controller/state/conditions/conditions.go index ad5d00a0dc..b42948c483 100644 --- a/internal/controller/state/conditions/conditions.go +++ b/internal/controller/state/conditions/conditions.go @@ -38,6 +38,13 @@ const ( // Route rules has a backendRef with an unsupported value. RouteReasonBackendRefUnsupportedValue v1.RouteConditionReason = "UnsupportedValue" + // RouteReasonUnsupportedField is used when a Route contains fields that are not yet supported. + RouteReasonUnsupportedField v1.RouteConditionReason = "UnsupportedField" + + // RouteConditionUnsupportedField indicates that the Route contains fields that are not yet supported. + // The route is still valid and processed, but these fields are ignored. + RouteConditionUnsupportedField v1.RouteConditionType = "UnsupportedField" + // RouteReasonInvalidGateway is used with the "Accepted" (false) condition when the Gateway the Route // references is invalid. RouteReasonInvalidGateway v1.RouteConditionReason = "InvalidGateway" @@ -62,6 +69,13 @@ const ( // invalid. Used with ResolvedRefs (false). RouteReasonInvalidFilter v1.RouteConditionReason = "InvalidFilter" + // GatewayReasonUnsupportedField is used when a Route contains fields that are not yet supported. + GatewayReasonUnsupportedField v1.GatewayConditionReason = "UnsupportedField" + + // GatewayConditionUnsupportedField indicates that the Gateway contains fields that are not yet supported. + // The gateway is still valid and processed, but these fields are ignored. + GatewayConditionUnsupportedField v1.GatewayConditionType = "UnsupportedField" + // GatewayReasonUnsupportedValue is used with GatewayConditionAccepted (false) when a value of a field in a Gateway // is invalid or not supported. GatewayReasonUnsupportedValue v1.GatewayConditionReason = "UnsupportedValue" @@ -165,9 +179,22 @@ type Condition struct { Message string } +type Conditions []Condition + +func (c *Conditions) CountInvalid() int { + var count int + for _, cond := range *c { + if cond.Status == metav1.ConditionFalse { + count++ + } + } + + return count +} + // DeduplicateConditions removes duplicate conditions based on the condition type. // The last condition wins. The order of conditions is preserved. -func DeduplicateConditions(conds []Condition) []Condition { +func DeduplicateConditions(conds Conditions) Conditions { type elem struct { cond Condition reverseIdx int @@ -188,7 +215,7 @@ func DeduplicateConditions(conds []Condition) []Condition { idx++ } - result := make([]Condition, len(uniqueElems)) + result := make(Conditions, len(uniqueElems)) for _, el := range uniqueElems { result[len(result)-el.reverseIdx-1] = el.cond @@ -199,7 +226,7 @@ func DeduplicateConditions(conds []Condition) []Condition { // ConvertConditions converts conditions to Kubernetes API conditions. func ConvertConditions( - conds []Condition, + conds Conditions, observedGeneration int64, transitionTime metav1.Time, ) []metav1.Condition { @@ -220,7 +247,7 @@ func ConvertConditions( } // HasMatchingCondition checks if the given condition matches any of the existing conditions. -func HasMatchingCondition(existingConditions []Condition, cond Condition) bool { +func HasMatchingCondition(existingConditions Conditions, cond Condition) bool { for _, existing := range existingConditions { if existing.Type == cond.Type && existing.Status == cond.Status && @@ -234,8 +261,8 @@ func HasMatchingCondition(existingConditions []Condition, cond Condition) bool { // NewDefaultGatewayClassConditions returns Conditions that indicate that the GatewayClass is accepted and that the // Gateway API CRD versions are supported. -func NewDefaultGatewayClassConditions() []Condition { - return []Condition{ +func NewDefaultGatewayClassConditions() Conditions { + return Conditions{ { Type: string(v1.GatewayClassConditionStatusAccepted), Status: metav1.ConditionTrue, @@ -254,8 +281,8 @@ func NewDefaultGatewayClassConditions() []Condition { // NewGatewayClassSupportedVersionBestEffort returns a Condition that indicates that the GatewayClass is accepted, // but the Gateway API CRD versions are not supported. This means NGF will attempt to generate configuration, // but it does not guarantee support. -func NewGatewayClassSupportedVersionBestEffort(recommendedVersion string) []Condition { - return []Condition{ +func NewGatewayClassSupportedVersionBestEffort(recommendedVersion string) Conditions { + return Conditions{ { Type: string(v1.GatewayClassConditionStatusSupportedVersion), Status: metav1.ConditionFalse, @@ -270,8 +297,8 @@ func NewGatewayClassSupportedVersionBestEffort(recommendedVersion string) []Cond // NewGatewayClassUnsupportedVersion returns Conditions that indicate that the GatewayClass is not accepted because // the Gateway API CRD versions are not supported. NGF will not generate configuration in this case. -func NewGatewayClassUnsupportedVersion(recommendedVersion string) []Condition { - return []Condition{ +func NewGatewayClassUnsupportedVersion(recommendedVersion string) Conditions { + return Conditions{ { Type: string(v1.GatewayClassConditionStatusAccepted), Status: metav1.ConditionFalse, @@ -305,8 +332,8 @@ func NewGatewayClassConflict() Condition { } // NewDefaultRouteConditions returns the default conditions that must be present in the status of a Route. -func NewDefaultRouteConditions() []Condition { - return []Condition{ +func NewDefaultRouteConditions() Conditions { + return Conditions{ NewRouteAccepted(), NewRouteResolvedRefs(), } @@ -354,6 +381,16 @@ func NewRouteUnsupportedValue(msg string) Condition { } } +// NewRouteUnsupportedField returns a Condition that indicates that the Route includes an unsupported field. +func NewRouteUnsupportedField(msg string) Condition { + return Condition{ + Type: string(RouteConditionUnsupportedField), + Status: metav1.ConditionTrue, + Reason: string(RouteReasonUnsupportedField), + Message: fmt.Sprintf("Some parameters are ignored due to an error: %s", msg), + } +} + // NewRoutePartiallyInvalid returns a Condition that indicates that the Route contains a combination // of both valid and invalid rules. // @@ -514,8 +551,8 @@ func NewRouteResolvedRefsInvalidFilter(msg string) Condition { // NewDefaultListenerConditions returns the default Conditions that must be present in the status of a Listener. // If existingConditions contains conflict-related conditions (like OverlappingTLSConfig or Conflicted), // the NoConflicts condition is excluded to avoid conflicting condition states. -func NewDefaultListenerConditions(existingConditions []Condition) []Condition { - defaultConds := []Condition{ +func NewDefaultListenerConditions(existingConditions Conditions) Conditions { + defaultConds := Conditions{ NewListenerAccepted(), NewListenerProgrammed(), NewListenerResolvedRefs(), @@ -530,7 +567,7 @@ func NewDefaultListenerConditions(existingConditions []Condition) []Condition { } // hasConflictConditions checks if the listener has any conflict-related conditions. -func hasConflictConditions(conditions []Condition) bool { +func hasConflictConditions(conditions Conditions) bool { for _, cond := range conditions { if cond.Type == string(v1.ListenerConditionConflicted) || cond.Type == string(v1.ListenerConditionOverlappingTLSConfig) { @@ -593,8 +630,8 @@ func NewListenerNotProgrammedInvalid(msg string) Condition { // NewListenerUnsupportedValue returns Conditions that indicate that a field of a Listener has an unsupported value. // Unsupported means that the value is not supported by the implementation or invalid. -func NewListenerUnsupportedValue(msg string) []Condition { - return []Condition{ +func NewListenerUnsupportedValue(msg string) Conditions { + return Conditions{ { Type: string(v1.ListenerConditionAccepted), Status: metav1.ConditionFalse, @@ -606,8 +643,8 @@ func NewListenerUnsupportedValue(msg string) []Condition { } // NewListenerInvalidCertificateRef returns Conditions that indicate that a CertificateRef of a Listener is invalid. -func NewListenerInvalidCertificateRef(msg string) []Condition { - return []Condition{ +func NewListenerInvalidCertificateRef(msg string) Conditions { + return Conditions{ { Type: string(v1.ListenerConditionAccepted), Status: metav1.ConditionFalse, @@ -626,8 +663,8 @@ func NewListenerInvalidCertificateRef(msg string) []Condition { // NewListenerInvalidRouteKinds returns Conditions that indicate that an invalid or unsupported Route kind is // specified by the Listener. -func NewListenerInvalidRouteKinds(msg string) []Condition { - return []Condition{ +func NewListenerInvalidRouteKinds(msg string) Conditions { + return Conditions{ { Type: string(v1.ListenerReasonResolvedRefs), Status: metav1.ConditionFalse, @@ -640,8 +677,8 @@ func NewListenerInvalidRouteKinds(msg string) []Condition { // NewListenerProtocolConflict returns Conditions that indicate multiple Listeners are specified with the same // Listener port number, but have conflicting protocol specifications. -func NewListenerProtocolConflict(msg string) []Condition { - return []Condition{ +func NewListenerProtocolConflict(msg string) Conditions { + return Conditions{ { Type: string(v1.ListenerConditionAccepted), Status: metav1.ConditionFalse, @@ -660,8 +697,8 @@ func NewListenerProtocolConflict(msg string) []Condition { // NewListenerHostnameConflict returns Conditions that indicate multiple Listeners are specified with the same // Listener port, but are HTTPS and TLS and have overlapping hostnames. -func NewListenerHostnameConflict(msg string) []Condition { - return []Condition{ +func NewListenerHostnameConflict(msg string) Conditions { + return Conditions{ { Type: string(v1.ListenerConditionAccepted), Status: metav1.ConditionFalse, @@ -679,8 +716,8 @@ func NewListenerHostnameConflict(msg string) []Condition { } // NewListenerUnsupportedProtocol returns Conditions that indicate that the protocol of a Listener is unsupported. -func NewListenerUnsupportedProtocol(msg string) []Condition { - return []Condition{ +func NewListenerUnsupportedProtocol(msg string) Conditions { + return Conditions{ { Type: string(v1.ListenerConditionAccepted), Status: metav1.ConditionFalse, @@ -693,8 +730,8 @@ func NewListenerUnsupportedProtocol(msg string) []Condition { // NewListenerRefNotPermitted returns Conditions that indicates that the Listener references a TLS secret that is not // permitted by a ReferenceGrant. -func NewListenerRefNotPermitted(msg string) []Condition { - return []Condition{ +func NewListenerRefNotPermitted(msg string) Conditions { + return Conditions{ { Type: string(v1.ListenerConditionAccepted), Status: metav1.ConditionFalse, @@ -768,8 +805,8 @@ func NewGatewayClassInvalidParameters(msg string) Condition { } // NewDefaultGatewayConditions returns the default Conditions that must be present in the status of a Gateway. -func NewDefaultGatewayConditions() []Condition { - return []Condition{ +func NewDefaultGatewayConditions() Conditions { + return Conditions{ NewGatewayAccepted(), NewGatewayProgrammed(), } @@ -798,9 +835,9 @@ func NewGatewayAcceptedListenersNotValid() Condition { // NewGatewayNotAcceptedListenersNotValid returns Conditions that indicate the Gateway is not accepted, // because all listeners are invalid. -func NewGatewayNotAcceptedListenersNotValid() []Condition { +func NewGatewayNotAcceptedListenersNotValid() Conditions { msg := "Gateway has no valid listeners" - return []Condition{ + return Conditions{ { Type: string(v1.GatewayConditionAccepted), Status: metav1.ConditionFalse, @@ -813,8 +850,8 @@ func NewGatewayNotAcceptedListenersNotValid() []Condition { // NewGatewayInvalid returns Conditions that indicate the Gateway is not accepted and programmed because it is // semantically or syntactically invalid. The provided message contains the details of why the Gateway is invalid. -func NewGatewayInvalid(msg string) []Condition { - return []Condition{ +func NewGatewayInvalid(msg string) Conditions { + return Conditions{ { Type: string(v1.GatewayConditionAccepted), Status: metav1.ConditionFalse, @@ -945,6 +982,17 @@ func NewGatewayInvalidParameters(msg string) Condition { } } +// NewGatewayUnsupportedField returns a Condition that indicates the Gateway is accepted but +// contains a field that is not supported. +func NewGatewayUnsupportedField(msg string) Condition { + return Condition{ + Type: string(GatewayConditionUnsupportedField), + Status: metav1.ConditionTrue, + Reason: string(GatewayReasonUnsupportedField), + Message: fmt.Sprintf("Gateway is accepted, but some parameters are ignored due to an error: %s", msg), + } +} + // NewPolicyAccepted returns a Condition that indicates that the Policy is accepted. func NewPolicyAccepted() Condition { return Condition{ diff --git a/internal/controller/state/conditions/conditions_test.go b/internal/controller/state/conditions/conditions_test.go index 45ebcc5808..8883ad3516 100644 --- a/internal/controller/state/conditions/conditions_test.go +++ b/internal/controller/state/conditions/conditions_test.go @@ -9,7 +9,7 @@ import ( func TestDeduplicateConditions(t *testing.T) { t.Parallel() - conds := []Condition{ + conds := Conditions{ { Type: "Type1", Status: metav1.ConditionTrue, @@ -37,7 +37,7 @@ func TestDeduplicateConditions(t *testing.T) { }, } - expected := []Condition{ + expected := Conditions{ { Type: "Type1", Status: metav1.ConditionFalse, @@ -63,7 +63,7 @@ func TestDeduplicateConditions(t *testing.T) { func TestConvertConditions(t *testing.T) { t.Parallel() - conds := []Condition{ + conds := Conditions{ { Type: "Type1", Status: metav1.ConditionTrue, @@ -112,7 +112,7 @@ func TestHasMatchingCondition(t *testing.T) { tests := []struct { condition Condition name string - conds []Condition + conds Conditions expected bool }{ { @@ -123,13 +123,13 @@ func TestHasMatchingCondition(t *testing.T) { }, { name: "condition matches existing condition", - conds: []Condition{NewClientSettingsPolicyAffected()}, + conds: Conditions{NewClientSettingsPolicyAffected()}, condition: NewClientSettingsPolicyAffected(), expected: true, }, { name: "condition does not match existing condition", - conds: []Condition{NewClientSettingsPolicyAffected()}, + conds: Conditions{NewClientSettingsPolicyAffected()}, condition: NewObservabilityPolicyAffected(), expected: false, }, diff --git a/internal/controller/state/graph/backend_refs.go b/internal/controller/state/graph/backend_refs.go index d18a81cc43..dd3fb98c1e 100644 --- a/internal/controller/state/graph/backend_refs.go +++ b/internal/controller/state/graph/backend_refs.go @@ -135,7 +135,7 @@ func createBackendRef( services map[types.NamespacedName]*v1.Service, refPath *field.Path, backendTLSPolicies map[types.NamespacedName]*BackendTLSPolicy, -) (BackendRef, []conditions.Condition) { +) (BackendRef, conditions.Conditions) { // Data plane will handle invalid ref by responding with 500. // Because of that, we always need to add a BackendRef to group.Backends, even if the ref is invalid. // Additionally, we always calculate the weight, even if it is invalid. @@ -158,7 +158,7 @@ func createBackendRef( InvalidForGateways: make(map[types.NamespacedName]conditions.Condition), } - return backendRef, []conditions.Condition{cond} + return backendRef, conditions.Conditions{cond} } ns := route.Source.GetNamespace() @@ -177,10 +177,10 @@ func createBackendRef( InvalidForGateways: make(map[types.NamespacedName]conditions.Condition), } - return backendRef, []conditions.Condition{conditions.NewRouteBackendRefRefBackendNotFound(err.Error())} + return backendRef, conditions.Conditions{conditions.NewRouteBackendRefRefBackendNotFound(err.Error())} } - var conds []conditions.Condition + var conds conditions.Conditions invalidForGateways := make(map[types.NamespacedName]conditions.Condition) // Check if this is an ExternalName service and validate DNS resolver configuration diff --git a/internal/controller/state/graph/backend_refs_test.go b/internal/controller/state/graph/backend_refs_test.go index 6e07bad538..c75584de77 100644 --- a/internal/controller/state/graph/backend_refs_test.go +++ b/internal/controller/state/graph/backend_refs_test.go @@ -602,7 +602,7 @@ func TestAddBackendRefsToRules(t *testing.T) { }, }, }, - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ { Type: "Accepted", Status: "True", @@ -632,7 +632,7 @@ func TestAddBackendRefsToRules(t *testing.T) { policies map[types.NamespacedName]*BackendTLSPolicy name string expectedBackendRefs []BackendRef - expectedConditions []conditions.Condition + expectedConditions conditions.Conditions }{ { route: createRoute("hr1", RouteTypeHTTP, "Service", 1, "svc1"), @@ -706,7 +706,7 @@ func TestAddBackendRefsToRules(t *testing.T) { InvalidForGateways: map[types.NamespacedName]conditions.Condition{}, }, }, - expectedConditions: []conditions.Condition{ + expectedConditions: conditions.Conditions{ conditions.NewRouteBackendRefUnsupportedProtocol( "route type http does not support service port appProtocol kubernetes.io/h2c;" + " nginx does not support proxying to upstreams with http2 or h2c", @@ -758,7 +758,7 @@ func TestAddBackendRefsToRules(t *testing.T) { InvalidForGateways: map[types.NamespacedName]conditions.Condition{}, }, }, - expectedConditions: []conditions.Condition{ + expectedConditions: conditions.Conditions{ conditions.NewRouteBackendRefUnsupportedProtocol( "route type http does not support service port appProtocol kubernetes.io/wss;" + " missing corresponding BackendTLSPolicy", @@ -793,7 +793,7 @@ func TestAddBackendRefsToRules(t *testing.T) { InvalidForGateways: map[types.NamespacedName]conditions.Condition{}, }, }, - expectedConditions: []conditions.Condition{ + expectedConditions: conditions.Conditions{ conditions.NewRouteBackendRefUnsupportedProtocol( "route type grpc does not support service port appProtocol kubernetes.io/ws", ), @@ -812,7 +812,7 @@ func TestAddBackendRefsToRules(t *testing.T) { InvalidForGateways: map[types.NamespacedName]conditions.Condition{}, }, }, - expectedConditions: []conditions.Condition{ + expectedConditions: conditions.Conditions{ conditions.NewRouteBackendRefUnsupportedProtocol( "route type grpc does not support service port appProtocol kubernetes.io/wss", ), @@ -890,7 +890,7 @@ func TestAddBackendRefsToRules(t *testing.T) { InvalidForGateways: map[types.NamespacedName]conditions.Condition{}, }, }, - expectedConditions: []conditions.Condition{ + expectedConditions: conditions.Conditions{ conditions.NewRouteBackendRefInvalidKind( `spec.rules[0].backendRefs[0].kind: Unsupported value: "NotService": supported values: "Service"`, ), @@ -921,7 +921,7 @@ func TestAddBackendRefsToRules(t *testing.T) { InvalidForGateways: map[types.NamespacedName]conditions.Condition{}, }, }, - expectedConditions: []conditions.Condition{ + expectedConditions: conditions.Conditions{ conditions.NewRouteBackendRefUnsupportedValue( `Backend TLS policies do not match for all backends`, ), @@ -1084,7 +1084,7 @@ func TestCreateBackend(t *testing.T) { }, }, Valid: false, - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ conditions.NewPolicyInvalid("unsupported value"), }, } @@ -1094,7 +1094,7 @@ func TestCreateBackend(t *testing.T) { name string expectedServicePortReference string ref gatewayv1.HTTPBackendRef - expectedConditions []conditions.Condition + expectedConditions conditions.Conditions expectedBackend BackendRef }{ { @@ -1145,7 +1145,7 @@ func TestCreateBackend(t *testing.T) { Valid: false, }, expectedServicePortReference: "", - expectedConditions: []conditions.Condition{ + expectedConditions: conditions.Conditions{ conditions.NewRouteBackendRefUnsupportedValue( "test.weight: Invalid value: -1: must be in the range [0, 1000000]", ), @@ -1167,7 +1167,7 @@ func TestCreateBackend(t *testing.T) { Valid: false, }, expectedServicePortReference: "", - expectedConditions: []conditions.Condition{ + expectedConditions: conditions.Conditions{ conditions.NewRouteBackendRefInvalidKind( `test.kind: Unsupported value: "NotService": supported values: "Service"`, ), @@ -1191,7 +1191,7 @@ func TestCreateBackend(t *testing.T) { InvalidForGateways: map[types.NamespacedName]conditions.Condition{}, }, expectedServicePortReference: "", - expectedConditions: []conditions.Condition{ + expectedConditions: conditions.Conditions{ conditions.NewRouteBackendRefRefBackendNotFound(`test.name: Not found: "not-exist"`), }, name: "service doesn't exist", @@ -1250,7 +1250,7 @@ func TestCreateBackend(t *testing.T) { InvalidForGateways: map[types.NamespacedName]conditions.Condition{}, }, expectedServicePortReference: "", - expectedConditions: []conditions.Condition{ + expectedConditions: conditions.Conditions{ conditions.NewRouteBackendRefUnsupportedValue( "the backend TLS policy is invalid: unsupported value", ), @@ -1345,7 +1345,7 @@ func TestCreateBackend(t *testing.T) { }, }, expectedServicePortReference: "", - expectedConditions: []conditions.Condition{ + expectedConditions: conditions.Conditions{ conditions.NewRouteBackendRefUnsupportedValue( "ExternalName service has empty or invalid externalName field", ), @@ -1371,7 +1371,7 @@ func TestCreateBackend(t *testing.T) { }, }, expectedServicePortReference: "", - expectedConditions: []conditions.Condition{ + expectedConditions: conditions.Conditions{ conditions.NewRouteBackendRefUnsupportedValue( "ExternalName service has empty or invalid externalName field", ), diff --git a/internal/controller/state/graph/backend_tls_policy.go b/internal/controller/state/graph/backend_tls_policy.go index 591f83d6d3..ec8567b619 100644 --- a/internal/controller/state/graph/backend_tls_policy.go +++ b/internal/controller/state/graph/backend_tls_policy.go @@ -24,7 +24,7 @@ type BackendTLSPolicy struct { // Only contains gateways where the policy can be applied (not limited by ancestor status). Gateways []types.NamespacedName // Conditions include Conditions for the BackendTLSPolicy. - Conditions []conditions.Condition + Conditions conditions.Conditions // Valid shows whether the BackendTLSPolicy is valid. Valid bool // IsReferenced shows whether the BackendTLSPolicy is referenced by a BackendRef. @@ -70,7 +70,7 @@ func validateBackendTLSPolicy( backendTLSPolicy *v1alpha3.BackendTLSPolicy, configMapResolver *configMapResolver, secretResolver *secretResolver, -) (valid, ignored bool, conds []conditions.Condition) { +) (valid, ignored bool, conds conditions.Conditions) { valid = true ignored = false @@ -132,11 +132,11 @@ func validateBackendTLSCACertRef( btp *v1alpha3.BackendTLSPolicy, configMapResolver *configMapResolver, secretResolver *secretResolver, -) []conditions.Condition { +) conditions.Conditions { if len(btp.Spec.Validation.CACertificateRefs) != 1 { path := field.NewPath("validation.caCertificateRefs") valErr := field.TooMany(path, len(btp.Spec.Validation.CACertificateRefs), 1) - return []conditions.Condition{conditions.NewPolicyInvalid(valErr.Error())} + return conditions.Conditions{conditions.NewPolicyInvalid(valErr.Error())} } selectedCertRef := btp.Spec.Validation.CACertificateRefs[0] @@ -145,7 +145,7 @@ func validateBackendTLSCACertRef( if !slices.Contains(allowedCaCertKinds, selectedCertRef.Kind) { path := field.NewPath("validation.caCertificateRefs[0].kind") valErr := field.NotSupported(path, btp.Spec.Validation.CACertificateRefs[0].Kind, allowedCaCertKinds) - return []conditions.Condition{ + return conditions.Conditions{ conditions.NewBackendTLSPolicyInvalidKind(valErr.Error()), conditions.NewBackendTLSPolicyNoValidCACertificate("No valid CACertificateRef found"), } @@ -154,7 +154,7 @@ func validateBackendTLSCACertRef( selectedCertRef.Group != "core" { path := field.NewPath("validation.caCertificateRefs[0].group") valErr := field.NotSupported(path, selectedCertRef.Group, []string{"", "core"}) - return []conditions.Condition{ + return conditions.Conditions{ conditions.NewBackendTLSPolicyInvalidKind(valErr.Error()), conditions.NewBackendTLSPolicyNoValidCACertificate("No valid CACertificateRef found"), } @@ -169,7 +169,7 @@ func validateBackendTLSCACertRef( if err := configMapResolver.resolve(nsName); err != nil { path := field.NewPath("validation.caCertificateRefs[0]") valErr := field.Invalid(path, selectedCertRef, err.Error()) - return []conditions.Condition{ + return conditions.Conditions{ conditions.NewBackendTLSPolicyInvalidCACertificateRef(valErr.Error()), conditions.NewBackendTLSPolicyNoValidCACertificate("No valid CACertificateRef found"), } @@ -178,7 +178,7 @@ func validateBackendTLSCACertRef( if err := secretResolver.resolve(nsName); err != nil { path := field.NewPath("validation.caCertificateRefs[0]") valErr := field.Invalid(path, selectedCertRef, err.Error()) - return []conditions.Condition{ + return conditions.Conditions{ conditions.NewBackendTLSPolicyInvalidCACertificateRef(valErr.Error()), conditions.NewBackendTLSPolicyNoValidCACertificate("No valid CACertificateRef found"), } @@ -212,10 +212,10 @@ func countNonNGFAncestors(policy *v1alpha3.BackendTLSPolicy, ctlrName string) in // addPolicyAncestorLimitCondition adds or updates a PolicyAncestorLimitReached condition. func addPolicyAncestorLimitCondition( - conds []conditions.Condition, + conds conditions.Conditions, policyName string, policyType string, -) []conditions.Condition { +) conditions.Conditions { for i, condition := range conds { if condition.Reason == string(conditions.PolicyReasonAncestorLimitReached) { if !strings.Contains(condition.Message, policyName) { diff --git a/internal/controller/state/graph/backend_tls_policy_test.go b/internal/controller/state/graph/backend_tls_policy_test.go index 53e9011edb..b38cefe469 100644 --- a/internal/controller/state/graph/backend_tls_policy_test.go +++ b/internal/controller/state/graph/backend_tls_policy_test.go @@ -804,13 +804,13 @@ func TestAddGatewaysForBackendTLSPoliciesAncestorLimit(t *testing.T) { Source: &gatewayv1.Gateway{ ObjectMeta: metav1.ObjectMeta{Name: "gateway1", Namespace: "test"}, }, - Conditions: []conditions.Condition{}, // Start with empty conditions + Conditions: conditions.Conditions{}, // Start with empty conditions }, {Namespace: "test", Name: "gateway2"}: { Source: &gatewayv1.Gateway{ ObjectMeta: metav1.ObjectMeta{Name: "gateway2", Namespace: "test"}, }, - Conditions: []conditions.Condition{}, // Start with empty conditions + Conditions: conditions.Conditions{}, // Start with empty conditions }, } diff --git a/internal/controller/state/graph/gateway.go b/internal/controller/state/graph/gateway.go index 103634871e..1a4839e8fe 100644 --- a/internal/controller/state/graph/gateway.go +++ b/internal/controller/state/graph/gateway.go @@ -29,7 +29,7 @@ type Gateway struct { // Listeners include the listeners of the Gateway. Listeners []*Listener // Conditions holds the conditions for the Gateway. - Conditions []conditions.Condition + Conditions conditions.Conditions // Policies holds the policies attached to the Gateway. Policies []*Policy // Valid indicates whether the Gateway Spec is valid. @@ -127,8 +127,8 @@ func buildGateways( return builtGateways } -func validateGatewayParametersRef(npCfg *NginxProxy, ref v1.LocalParametersReference) []conditions.Condition { - var conds []conditions.Condition +func validateGatewayParametersRef(npCfg *NginxProxy, ref v1.LocalParametersReference) conditions.Conditions { + var conds conditions.Conditions path := field.NewPath("spec.infrastructure.parametersRef") @@ -170,8 +170,8 @@ func validateGatewayParametersRef(npCfg *NginxProxy, ref v1.LocalParametersRefer return conds } -func validateGateway(gw *v1.Gateway, gc *GatewayClass, npCfg *NginxProxy) ([]conditions.Condition, bool) { - var conds []conditions.Condition +func validateGateway(gw *v1.Gateway, gc *GatewayClass, npCfg *NginxProxy) (conditions.Conditions, bool) { + var conds conditions.Conditions if gc == nil { conds = append(conds, conditions.NewGatewayInvalid("GatewayClass doesn't exist")...) @@ -179,6 +179,9 @@ func validateGateway(gw *v1.Gateway, gc *GatewayClass, npCfg *NginxProxy) ([]con conds = append(conds, conditions.NewGatewayInvalid("GatewayClass is invalid")...) } + // Validate unsupported fields Gateway fields + conds = append(conds, validateUnsupportedGatewayFields(gw)...) + // Set the unaccepted conditions here, because those make the gateway invalid. We set the unprogrammed conditions // elsewhere, because those do not make the gateway invalid. for _, address := range gw.Spec.Addresses { @@ -191,7 +194,7 @@ func validateGateway(gw *v1.Gateway, gc *GatewayClass, npCfg *NginxProxy) ([]con // we evaluate validity before validating parametersRef because an invalid parametersRef/NginxProxy does not // invalidate the entire Gateway. - valid := len(conds) == 0 + valid := conds.CountInvalid() == 0 if gw.Spec.Infrastructure != nil && gw.Spec.Infrastructure.ParametersRef != nil { paramConds := validateGatewayParametersRef(npCfg, *gw.Spec.Infrastructure.ParametersRef) @@ -267,3 +270,17 @@ func (g *Gateway) collectSnippetsFiltersFromRoute( } } } + +func validateUnsupportedGatewayFields(gw *v1.Gateway) conditions.Conditions { + var conds conditions.Conditions + + if gw.Spec.AllowedListeners != nil { + conds = append(conds, conditions.NewGatewayUnsupportedField("AllowedListeners are not supported")) + } + + if gw.Spec.BackendTLS != nil { + conds = append(conds, conditions.NewGatewayUnsupportedField("BackendTLS is not supported")) + } + + return conds +} diff --git a/internal/controller/state/graph/gateway_listener.go b/internal/controller/state/graph/gateway_listener.go index 3f6e69587e..78d77f3831 100644 --- a/internal/controller/state/graph/gateway_listener.go +++ b/internal/controller/state/graph/gateway_listener.go @@ -36,7 +36,7 @@ type Listener struct { // Only applicable for HTTPS listeners. ResolvedSecret *types.NamespacedName // Conditions holds the conditions of the Listener. - Conditions []conditions.Condition + Conditions conditions.Conditions // SupportedKinds is the list of RouteGroupKinds allowed by the listener. SupportedKinds []v1.RouteGroupKind // Valid shows whether the Listener is valid. @@ -94,7 +94,7 @@ func newListenerConfiguratorFactory( return &listenerConfiguratorFactory{ unsupportedProtocol: &listenerConfigurator{ validators: []listenerValidator{ - func(listener v1.Listener) ([]conditions.Condition, bool) { + func(listener v1.Listener) (conditions.Conditions, bool) { valErr := field.NotSupported( field.NewPath("protocol"), listener.Protocol, @@ -148,7 +148,7 @@ func newListenerConfiguratorFactory( // listenerValidator validates a listener. If the listener is invalid, the validator will return appropriate conditions. // It also returns whether the listener is attachable, which is independent of whether the listener is valid. -type listenerValidator func(v1.Listener) (conds []conditions.Condition, attachable bool) +type listenerValidator func(v1.Listener) (conds conditions.Conditions, attachable bool) // listenerConflictResolver resolves conflicts between listeners. In case of a conflict, the resolver will make // the conflicting listeners invalid - i.e. it will modify the passed listener and the previously processed conflicting @@ -173,7 +173,7 @@ type listenerConfigurator struct { } func (c *listenerConfigurator) configure(listener v1.Listener, gwNSName types.NamespacedName) *Listener { - var conds []conditions.Condition + var conds conditions.Conditions attachable := true @@ -230,7 +230,7 @@ func (c *listenerConfigurator) configure(listener v1.Listener, gwNSName types.Na return l } -func validateListenerHostname(listener v1.Listener) (conds []conditions.Condition, attachable bool) { +func validateListenerHostname(listener v1.Listener) (conds conditions.Conditions, attachable bool) { if listener.Hostname == nil { return nil, true } @@ -253,10 +253,10 @@ func validateListenerHostname(listener v1.Listener) (conds []conditions.Conditio // The supported kinds are determined based on the listener's allowedRoutes field. // If the listener does not specify allowedRoutes, listener determines allowed routes based on its protocol. func getAndValidateListenerSupportedKinds(listener v1.Listener) ( - []conditions.Condition, + conditions.Conditions, []v1.RouteGroupKind, ) { - var conds []conditions.Condition + var conds conditions.Conditions var supportedKinds []v1.RouteGroupKind var validKinds []v1.RouteGroupKind @@ -315,7 +315,7 @@ func getAndValidateListenerSupportedKinds(listener v1.Listener) ( return conds, validKinds } -func validateListenerAllowedRouteKind(listener v1.Listener) (conds []conditions.Condition, attachable bool) { +func validateListenerAllowedRouteKind(listener v1.Listener) (conds conditions.Conditions, attachable bool) { conds, _ = getAndValidateListenerSupportedKinds(listener) return conds, len(conds) == 0 } @@ -325,7 +325,7 @@ func getListenerSupportedKinds(listener v1.Listener) []v1.RouteGroupKind { return sk } -func validateListenerLabelSelector(listener v1.Listener) (conds []conditions.Condition, attachable bool) { +func validateListenerLabelSelector(listener v1.Listener) (conds conditions.Conditions, attachable bool) { if listener.AllowedRoutes != nil && listener.AllowedRoutes.Namespaces != nil && listener.AllowedRoutes.Namespaces.From != nil && @@ -339,7 +339,7 @@ func validateListenerLabelSelector(listener v1.Listener) (conds []conditions.Con } func createHTTPListenerValidator(protectedPorts ProtectedPorts) listenerValidator { - return func(listener v1.Listener) (conds []conditions.Condition, attachable bool) { + return func(listener v1.Listener) (conds conditions.Conditions, attachable bool) { if err := validateListenerPort(listener.Port, protectedPorts); err != nil { path := field.NewPath("port") valErr := field.Invalid(path, listener.Port, err.Error()) @@ -368,7 +368,7 @@ func validateListenerPort(port v1.PortNumber, protectedPorts ProtectedPorts) err return nil } -func validateTLSFieldOnTLSListener(listener v1.Listener) (conds []conditions.Condition, attachable bool) { +func validateTLSFieldOnTLSListener(listener v1.Listener) (conds conditions.Conditions, attachable bool) { tlspath := field.NewPath("TLS") if listener.TLS == nil { valErr := field.Required(tlspath, "tls must be defined for TLS listener") @@ -382,7 +382,7 @@ func validateTLSFieldOnTLSListener(listener v1.Listener) (conds []conditions.Con } func createHTTPSListenerValidator(protectedPorts ProtectedPorts) listenerValidator { - return func(listener v1.Listener) (conds []conditions.Condition, attachable bool) { + return func(listener v1.Listener) (conds conditions.Conditions, attachable bool) { if err := validateListenerPort(listener.Port, protectedPorts); err != nil { path := field.NewPath("port") valErr := field.Invalid(path, listener.Port, err.Error()) diff --git a/internal/controller/state/graph/gateway_listener_test.go b/internal/controller/state/graph/gateway_listener_test.go index 804d4638b3..f3c7937e58 100644 --- a/internal/controller/state/graph/gateway_listener_test.go +++ b/internal/controller/state/graph/gateway_listener_test.go @@ -20,7 +20,7 @@ func TestValidateHTTPListener(t *testing.T) { tests := []struct { l v1.Listener name string - expected []conditions.Condition + expected conditions.Conditions }{ { l: v1.Listener{ @@ -101,7 +101,7 @@ func TestValidateHTTPSListener(t *testing.T) { tests := []struct { l v1.Listener name string - expected []conditions.Condition + expected conditions.Conditions }{ { l: v1.Listener{ @@ -607,7 +607,7 @@ func TestValidateTLSFieldOnTLSListener(t *testing.T) { tests := []struct { listener v1.Listener msg string - expectedCond []conditions.Condition + expectedCond conditions.Conditions expectValid bool }{ { diff --git a/internal/controller/state/graph/gateway_test.go b/internal/controller/state/graph/gateway_test.go index f31fa1dd3c..a8628c2054 100644 --- a/internal/controller/state/graph/gateway_test.go +++ b/internal/controller/state/graph/gateway_test.go @@ -286,10 +286,12 @@ func TestBuildGateway(t *testing.T) { ) type gatewayCfg struct { - name string - ref *v1.LocalParametersReference - listeners []v1.Listener - addresses []v1.GatewaySpecAddress + ref *v1.LocalParametersReference + allowedListeners *v1.AllowedListeners + backendTLS *v1.GatewayBackendTLS + name string + listeners []v1.Listener + addresses []v1.GatewaySpecAddress } var lastCreatedGateway *v1.Gateway @@ -304,6 +306,8 @@ func TestBuildGateway(t *testing.T) { GatewayClassName: gcName, Listeners: cfg.listeners, Addresses: cfg.addresses, + AllowedListeners: cfg.allowedListeners, + BackendTLS: cfg.backendTLS, }, } @@ -601,7 +605,7 @@ func TestBuildGateway(t *testing.T) { Port: helpers.GetPointer(int32(90)), }, }, - Conditions: []conditions.Condition{conditions.NewGatewayResolvedRefs()}, + Conditions: conditions.Conditions{conditions.NewGatewayResolvedRefs()}, }, }, name: "valid http listener with valid NginxProxy; GatewayClass has no NginxProxy", @@ -647,7 +651,7 @@ func TestBuildGateway(t *testing.T) { Port: helpers.GetPointer(int32(90)), }, }, - Conditions: []conditions.Condition{conditions.NewGatewayResolvedRefs()}, + Conditions: conditions.Conditions{conditions.NewGatewayResolvedRefs()}, }, }, name: "valid http listener with valid NginxProxy; GatewayClass has valid NginxProxy too", @@ -1331,7 +1335,7 @@ func TestBuildGateway(t *testing.T) { Name: controller.CreateNginxResourceName("gateway1", gcName), }, Valid: true, // invalid parametersRef does not invalidate Gateway. - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ conditions.NewGatewayRefInvalid( "spec.infrastructure.parametersRef.kind: Unsupported value: \"Invalid\": " + "supported values: \"NginxProxy\"", @@ -1374,7 +1378,7 @@ func TestBuildGateway(t *testing.T) { Name: controller.CreateNginxResourceName("gateway1", gcName), }, Valid: true, // invalid parametersRef does not invalidate Gateway. - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ conditions.NewGatewayRefNotFound(), conditions.NewGatewayInvalidParameters( "spec.infrastructure.parametersRef.name: Not found: \"does-not-exist\"", @@ -1420,7 +1424,7 @@ func TestBuildGateway(t *testing.T) { }, Valid: false, }, - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ conditions.NewGatewayRefInvalid("somePath: Required value: someField"), conditions.NewGatewayInvalidParameters("somePath: Required value: someField"), }, @@ -1480,7 +1484,7 @@ func TestBuildGateway(t *testing.T) { Name: controller.CreateNginxResourceName("gateway-addr-unspecified", gcName), }, Valid: false, - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ conditions.NewGatewayUnsupportedAddress("AddressType must be specified"), }, }, @@ -1507,12 +1511,109 @@ func TestBuildGateway(t *testing.T) { Name: controller.CreateNginxResourceName("gateway-addr-unsupported", gcName), }, Valid: false, - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ conditions.NewGatewayUnsupportedAddress("Only AddressType IPAddress is supported"), }, }, }, }, + { + name: "One unsupported field + supported fields (valid)", + gateway: createGateway(gatewayCfg{ + name: "gateway-valid-np", + listeners: []v1.Listener{foo80Listener1}, + ref: validGwNpRef, + allowedListeners: &v1.AllowedListeners{}, + }), + gatewayClass: validGCWithNp, + expected: map[types.NamespacedName]*Gateway{ + {Namespace: validGwNp.Namespace, Name: "gateway-valid-np"}: { + Source: getLastCreatedGateway(), + Listeners: []*Listener{ + { + Name: "foo-80-1", + GatewayName: client.ObjectKeyFromObject(getLastCreatedGateway()), + Source: foo80Listener1, + Valid: true, + Attachable: true, + Routes: map[RouteKey]*L7Route{}, + L4Routes: map[L4RouteKey]*L4Route{}, + SupportedKinds: supportedKindsForListeners, + }, + }, + DeploymentName: types.NamespacedName{ + Namespace: "test", + Name: controller.CreateNginxResourceName("gateway-valid-np", gcName), + }, + Valid: true, + NginxProxy: &NginxProxy{ + Source: validGwNp, + Valid: true, + }, + EffectiveNginxProxy: &EffectiveNginxProxy{ + Logging: &ngfAPIv1alpha2.NginxLogging{ + ErrorLevel: helpers.GetPointer(ngfAPIv1alpha2.NginxLogLevelError), + }, + IPFamily: helpers.GetPointer(ngfAPIv1alpha2.Dual), + Metrics: &ngfAPIv1alpha2.Metrics{ + Disable: helpers.GetPointer(false), + Port: helpers.GetPointer(int32(90)), + }, + }, + Conditions: conditions.Conditions{ + conditions.NewGatewayUnsupportedField("AllowedListeners are not supported"), + conditions.NewGatewayResolvedRefs(), + }, + }, + }, + }, + { + name: "One unsupported field + NewGatewayRefInvalid (invalid)", + gateway: createGateway(gatewayCfg{ + name: "gateway-valid-np", + listeners: []v1.Listener{foo80Listener1}, + ref: &v1.LocalParametersReference{ + Kind: "wrong-kind", // Invalid reference + Name: "invalid-ref", + }, + backendTLS: &v1.GatewayBackendTLS{}, + }), + gatewayClass: validGCWithNp, + expected: map[types.NamespacedName]*Gateway{ + {Namespace: validGwNp.Namespace, Name: "gateway-valid-np"}: { + Source: getLastCreatedGateway(), + Listeners: []*Listener{ + { + Name: "foo-80-1", + GatewayName: client.ObjectKeyFromObject(getLastCreatedGateway()), + Source: foo80Listener1, + Valid: true, + Attachable: true, + Routes: map[RouteKey]*L7Route{}, + L4Routes: map[L4RouteKey]*L4Route{}, + SupportedKinds: supportedKindsForListeners, + }, + }, + DeploymentName: types.NamespacedName{ + Namespace: "test", + Name: controller.CreateNginxResourceName("gateway-valid-np", gcName), + }, + Valid: true, + EffectiveNginxProxy: &EffectiveNginxProxy{ + IPFamily: helpers.GetPointer(ngfAPIv1alpha2.Dual), + }, + Conditions: conditions.Conditions{ + conditions.NewGatewayUnsupportedField("BackendTLS is not supported"), + conditions.NewGatewayRefInvalid( + "spec.infrastructure.parametersRef.kind: Unsupported value: \"wrong-kind\": supported values: \"NginxProxy\"", + ), + conditions.NewGatewayInvalidParameters( + "spec.infrastructure.parametersRef.kind: Unsupported value: \"wrong-kind\": supported values: \"NginxProxy\"", + ), + }, + }, + }, + }, } secretResolver := newSecretResolver( @@ -1548,14 +1649,14 @@ func TestValidateGatewayParametersRef(t *testing.T) { name string np *NginxProxy ref v1.LocalParametersReference - expConds []conditions.Condition + expConds conditions.Conditions }{ { name: "unsupported parameter ref kind", ref: v1.LocalParametersReference{ Kind: "wrong-kind", }, - expConds: []conditions.Condition{ + expConds: conditions.Conditions{ conditions.NewGatewayRefInvalid( "spec.infrastructure.parametersRef.kind: Unsupported value: \"wrong-kind\": " + "supported values: \"NginxProxy\"", @@ -1573,7 +1674,7 @@ func TestValidateGatewayParametersRef(t *testing.T) { Kind: kinds.NginxProxy, Name: "np", }, - expConds: []conditions.Condition{ + expConds: conditions.Conditions{ conditions.NewGatewayRefNotFound(), conditions.NewGatewayInvalidParameters("spec.infrastructure.parametersRef.name: Not found: \"np\""), }, @@ -1592,7 +1693,7 @@ func TestValidateGatewayParametersRef(t *testing.T) { Kind: kinds.NginxProxy, Name: "np", }, - expConds: []conditions.Condition{ + expConds: conditions.Conditions{ conditions.NewGatewayRefInvalid("somePath: Required value: someField"), conditions.NewGatewayInvalidParameters("somePath: Required value: someField"), }, @@ -1608,7 +1709,7 @@ func TestValidateGatewayParametersRef(t *testing.T) { Kind: kinds.NginxProxy, Name: "np", }, - expConds: []conditions.Condition{ + expConds: conditions.Conditions{ conditions.NewGatewayResolvedRefs(), }, }, @@ -1821,3 +1922,55 @@ func TestGetReferencedSnippetsFilters(t *testing.T) { emptyFilterResult := gw.GetReferencedSnippetsFilters(routes, map[types.NamespacedName]*SnippetsFilter{}) g.Expect(emptyFilterResult).To(BeEmpty()) } + +func TestValidateUnsupportedGatewayFields(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + gateway *v1.Gateway + expectedConds conditions.Conditions + }{ + { + name: "No unsupported fields", + gateway: &v1.Gateway{ + Spec: v1.GatewaySpec{}, + }, + expectedConds: nil, + }, + { + name: "One unsupported field: AllowedListeners", + gateway: &v1.Gateway{ + Spec: v1.GatewaySpec{ + AllowedListeners: &v1.AllowedListeners{}, + }, + }, + expectedConds: conditions.Conditions{ + conditions.NewGatewayUnsupportedField("AllowedListeners are not supported"), + }, + }, + { + name: "Multiple unsupported fields: AllowedListeners and BackendTLS", + gateway: &v1.Gateway{ + Spec: v1.GatewaySpec{ + AllowedListeners: &v1.AllowedListeners{}, + BackendTLS: &v1.GatewayBackendTLS{}, + }, + }, + expectedConds: conditions.Conditions{ + conditions.NewGatewayUnsupportedField("AllowedListeners are not supported"), + conditions.NewGatewayUnsupportedField("BackendTLS is not supported"), + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + t.Parallel() + g := NewWithT(t) + + conds := validateUnsupportedGatewayFields(test.gateway) + g.Expect(conds).To(Equal(test.expectedConds)) + }) + } +} diff --git a/internal/controller/state/graph/gatewayclass.go b/internal/controller/state/graph/gatewayclass.go index e396395441..0152f08004 100644 --- a/internal/controller/state/graph/gatewayclass.go +++ b/internal/controller/state/graph/gatewayclass.go @@ -37,7 +37,7 @@ type GatewayClass struct { // NginxProxy is the NginxProxy resource referenced by this GatewayClass. NginxProxy *NginxProxy // Conditions include Conditions for the GatewayClass. - Conditions []conditions.Condition + Conditions conditions.Conditions // Valid shows whether the GatewayClass is valid. Valid bool } @@ -115,7 +115,7 @@ func getNginxProxyForGatewayClass( return nps[npName] } -func validateGatewayClassParametersRef(path *field.Path, ref v1.ParametersReference) []conditions.Condition { +func validateGatewayClassParametersRef(path *field.Path, ref v1.ParametersReference) conditions.Conditions { var errs field.ErrorList if _, ok := supportedParamKinds[string(ref.Kind)]; !ok { @@ -131,7 +131,7 @@ func validateGatewayClassParametersRef(path *field.Path, ref v1.ParametersRefere if len(errs) > 0 { msg := errs.ToAggregate().Error() - return []conditions.Condition{ + return conditions.Conditions{ conditions.NewGatewayClassRefInvalid(msg), conditions.NewGatewayClassInvalidParameters(msg), } @@ -144,8 +144,8 @@ func validateGatewayClass( gc *v1.GatewayClass, npCfg *NginxProxy, crdVersions map[types.NamespacedName]*metav1.PartialObjectMetadata, -) ([]conditions.Condition, bool) { - var conds []conditions.Condition +) (conditions.Conditions, bool) { + var conds conditions.Conditions supportedVersionConds, versionsValid := validateCRDVersions(crdVersions) conds = append(conds, supportedVersionConds...) @@ -198,7 +198,7 @@ type apiVersion struct { func validateCRDVersions( crdMetadata map[types.NamespacedName]*metav1.PartialObjectMetadata, -) (conds []conditions.Condition, valid bool) { +) (conds conditions.Conditions, valid bool) { installedAPIVersions := getBundleVersions(crdMetadata) supportedAPIVersion := parseVersionString(SupportedVersion) diff --git a/internal/controller/state/graph/gatewayclass_test.go b/internal/controller/state/graph/gatewayclass_test.go index b7b01f9d15..aeadd996e1 100644 --- a/internal/controller/state/graph/gatewayclass_test.go +++ b/internal/controller/state/graph/gatewayclass_test.go @@ -213,7 +213,7 @@ func TestBuildGatewayClass(t *testing.T) { expected: &GatewayClass{ Source: gcWithParams, Valid: true, - Conditions: []conditions.Condition{conditions.NewGatewayClassResolvedRefs()}, + Conditions: conditions.Conditions{conditions.NewGatewayClassResolvedRefs()}, NginxProxy: &NginxProxy{ Valid: true, Source: np, @@ -226,7 +226,7 @@ func TestBuildGatewayClass(t *testing.T) { expected: &GatewayClass{ Source: gcWithParamsNoNamespace, Valid: true, - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ conditions.NewGatewayClassRefInvalid( "spec.parametersRef.namespace: Required value: ParametersRef must specify Namespace", ), @@ -242,7 +242,7 @@ func TestBuildGatewayClass(t *testing.T) { expected: &GatewayClass{ Source: gcWithInvalidKind, Valid: true, - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ conditions.NewGatewayClassRefInvalid( "spec.parametersRef.kind: Unsupported value: \"Invalid\": supported values: \"NginxProxy\"", ), @@ -258,7 +258,7 @@ func TestBuildGatewayClass(t *testing.T) { expected: &GatewayClass{ Source: gcWithParams, Valid: true, - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ conditions.NewGatewayClassRefNotFound(), conditions.NewGatewayClassInvalidParameters( "spec.parametersRef.name: Not found: \"nginx-proxy\"", @@ -289,7 +289,7 @@ func TestBuildGatewayClass(t *testing.T) { expected: &GatewayClass{ Source: gcWithParams, Valid: true, - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ conditions.NewGatewayClassRefInvalid( "[spec.telemetry.serviceName: Invalid value: \"my-svc\": error" + ", spec.telemetry.exporter.endpoint: Invalid value: \"my-endpoint\": error]", @@ -363,7 +363,7 @@ func TestValidateCRDVersions(t *testing.T) { tests := []struct { crds map[types.NamespacedName]*metav1.PartialObjectMetadata name string - expConds []conditions.Condition + expConds conditions.Conditions valid bool }{ { diff --git a/internal/controller/state/graph/graph_test.go b/internal/controller/state/graph/graph_test.go index ac5cfff3a2..8cfce2fa9f 100644 --- a/internal/controller/state/graph/graph_test.go +++ b/internal/controller/state/graph/graph_test.go @@ -49,7 +49,7 @@ func TestBuildGraph(t *testing.T) { }, } - btpAcceptedConds := []conditions.Condition{ + btpAcceptedConds := conditions.Conditions{ conditions.NewBackendTLSPolicyResolvedRefs(), conditions.NewPolicyAccepted(), conditions.NewPolicyAccepted(), @@ -767,7 +767,7 @@ func TestBuildGraph(t *testing.T) { Rules: []RouteRule{createValidRuleWithBackendRefsAndFilters(routeMatches, RouteTypeHTTP)}, }, Policies: []*Policy{processedRoutePolicy}, - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ conditions.NewClientSettingsPolicyAffected(), }, } @@ -786,7 +786,7 @@ func TestBuildGraph(t *testing.T) { Attachment: &ParentRefAttachmentStatus{ AcceptedHostnames: map[string][]string{}, Attached: false, - FailedConditions: []conditions.Condition{conditions.NewRouteNotAllowedByListeners()}, + FailedConditions: conditions.Conditions{conditions.NewRouteNotAllowedByListeners()}, }, SectionName: &gw1.Source.Spec.Listeners[0].Name, }, @@ -799,7 +799,7 @@ func TestBuildGraph(t *testing.T) { Attachment: &ParentRefAttachmentStatus{ AcceptedHostnames: map[string][]string{}, Attached: false, - FailedConditions: []conditions.Condition{conditions.NewRouteNotAllowedByListeners()}, + FailedConditions: conditions.Conditions{conditions.NewRouteNotAllowedByListeners()}, }, SectionName: &gw1.Source.Spec.Listeners[1].Name, }, @@ -868,7 +868,7 @@ func TestBuildGraph(t *testing.T) { Attachment: &ParentRefAttachmentStatus{ Attached: false, AcceptedHostnames: map[string][]string{}, - FailedConditions: []conditions.Condition{conditions.NewRouteNotAllowedByListeners()}, + FailedConditions: conditions.Conditions{conditions.NewRouteNotAllowedByListeners()}, }, SectionName: &gw1.Source.Spec.Listeners[0].Name, }, @@ -881,7 +881,7 @@ func TestBuildGraph(t *testing.T) { Attachment: &ParentRefAttachmentStatus{ AcceptedHostnames: map[string][]string{}, Attached: false, - FailedConditions: []conditions.Condition{conditions.NewRouteNotAllowedByListeners()}, + FailedConditions: conditions.Conditions{conditions.NewRouteNotAllowedByListeners()}, }, SectionName: &gw1.Source.Spec.Listeners[1].Name, }, @@ -894,7 +894,7 @@ func TestBuildGraph(t *testing.T) { Attachment: &ParentRefAttachmentStatus{ Attached: false, AcceptedHostnames: map[string][]string{}, - FailedConditions: []conditions.Condition{conditions.NewRouteHostnameConflict()}, + FailedConditions: conditions.Conditions{conditions.NewRouteHostnameConflict()}, }, SectionName: &gw1.Source.Spec.Listeners[2].Name, }, @@ -907,7 +907,7 @@ func TestBuildGraph(t *testing.T) { Attachment: &ParentRefAttachmentStatus{ Attached: false, AcceptedHostnames: map[string][]string{}, - FailedConditions: []conditions.Condition{conditions.NewRouteHostnameConflict()}, + FailedConditions: conditions.Conditions{conditions.NewRouteHostnameConflict()}, }, SectionName: &gw1.Source.Spec.Listeners[3].Name, }, @@ -1002,7 +1002,7 @@ func TestBuildGraph(t *testing.T) { GatewayClass: &GatewayClass{ Source: gc, Valid: true, - Conditions: []conditions.Condition{conditions.NewGatewayClassResolvedRefs()}, + Conditions: conditions.Conditions{conditions.NewGatewayClassResolvedRefs()}, NginxProxy: &NginxProxy{ Source: npGlobal, Valid: true, @@ -1085,7 +1085,7 @@ func TestBuildGraph(t *testing.T) { ErrorLevel: helpers.GetPointer(ngfAPIv1alpha2.NginxLogLevelError), }, }, - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ conditions.NewGatewayResolvedRefs(), conditions.NewClientSettingsPolicyAffected(), }, @@ -1164,7 +1164,7 @@ func TestBuildGraph(t *testing.T) { }, IPFamily: helpers.GetPointer(ngfAPIv1alpha2.IPv6), }, - Conditions: []conditions.Condition{conditions.NewGatewayResolvedRefs()}, + Conditions: conditions.Conditions{conditions.NewGatewayResolvedRefs()}, DeploymentName: types.NamespacedName{ Namespace: "test", Name: "gateway-2-my-class", diff --git a/internal/controller/state/graph/grpcroute.go b/internal/controller/state/graph/grpcroute.go index 68b3a6ed06..25ee1b5b94 100644 --- a/internal/controller/state/graph/grpcroute.go +++ b/internal/controller/state/graph/grpcroute.go @@ -165,6 +165,11 @@ func processGRPCRouteRule( validMatches := true + unsupportedFieldsErrors := checkForUnsupportedGRPCFields(specRule, rulePath) + if len(unsupportedFieldsErrors) > 0 { + errors.warn = append(errors.warn, unsupportedFieldsErrors...) + } + for j, match := range specRule.Matches { matchPath := rulePath.Child("matches").Index(j) @@ -230,7 +235,7 @@ func processGRPCRouteRules( specRules []v1.GRPCRouteRule, validator validation.HTTPFieldsValidator, resolveExtRefFunc resolveExtRefFilter, -) (rules []RouteRule, valid bool, conds []conditions.Condition) { +) (rules []RouteRule, valid bool, conds conditions.Conditions) { rules = make([]RouteRule, len(specRules)) var ( @@ -257,9 +262,16 @@ func processGRPCRouteRules( rules[i] = rr } - conds = make([]conditions.Condition, 0, 2) + conds = make(conditions.Conditions, 0, 2) valid = true + // add warning condition for unsupported fields if any + if len(allRulesErrors.warn) > 0 { + msg := "There are rules with unsupported fields configurations: " + allRulesErrors.warn.ToAggregate().Error() + conds = append(conds, conditions.NewRouteUnsupportedField(msg)) + valid = true + } + if len(allRulesErrors.invalid) > 0 { msg := allRulesErrors.invalid.ToAggregate().Error() @@ -444,3 +456,29 @@ func validateGRPCHeaderMatch( return allErrs } + +func checkForUnsupportedGRPCFields(rule v1.GRPCRouteRule, rulePath *field.Path) field.ErrorList { + supportedFields := []string{"GRPCBackendRef", "GRPCRouteMatch", "GRPCRouteFilter"} + var ruleErrors field.ErrorList + + if rule.Name != nil { + ruleErrors = append(ruleErrors, field.Forbidden( + rulePath.Child("name"), + "NGINX Gateway Fabric does not support \"SectionName\" field at the moment, supported fields are: "+ + strings.Join(supportedFields, ", "), + )) + } + if rule.SessionPersistence != nil { + ruleErrors = append(ruleErrors, field.Forbidden( + rulePath.Child("sessionPersistence"), + "NGINX Gateway Fabric does not support \"SessionPersistence\" field at the moment, supported fields are: "+ + strings.Join(supportedFields, ", "), + )) + } + + if len(ruleErrors) == 0 { + return nil + } + + return ruleErrors +} diff --git a/internal/controller/state/graph/grpcroute_test.go b/internal/controller/state/graph/grpcroute_test.go index 8579c54627..c7717af1ee 100644 --- a/internal/controller/state/graph/grpcroute_test.go +++ b/internal/controller/state/graph/grpcroute_test.go @@ -7,12 +7,14 @@ import ( . "github.com/onsi/gomega" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/validation/field" "sigs.k8s.io/controller-runtime/pkg/client" v1 "sigs.k8s.io/gateway-api/apis/v1" ngfAPIv1alpha1 "github.com/nginx/nginx-gateway-fabric/v2/apis/v1alpha1" "github.com/nginx/nginx-gateway-fabric/v2/internal/controller/state/conditions" "github.com/nginx/nginx-gateway-fabric/v2/internal/controller/state/mirror" + "github.com/nginx/nginx-gateway-fabric/v2/internal/controller/state/validation" "github.com/nginx/nginx-gateway-fabric/v2/internal/controller/state/validation/validationfakes" "github.com/nginx/nginx-gateway-fabric/v2/internal/framework/helpers" "github.com/nginx/nginx-gateway-fabric/v2/internal/framework/kinds" @@ -644,7 +646,7 @@ func TestBuildGRPCRoute(t *testing.T) { SectionName: grInvalidMatchesEmptyMethodFields.Spec.ParentRefs[0].SectionName, }, }, - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ conditions.NewRouteUnsupportedValue( `All rules are invalid: ` + `[spec.rules[0].matches[0].method.type: Unsupported value: "": supported values: "Exact",` + @@ -688,7 +690,7 @@ func TestBuildGRPCRoute(t *testing.T) { SectionName: grInvalidMatchesInvalidMethodFields.Spec.ParentRefs[0].SectionName, }, }, - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ conditions.NewRouteUnsupportedValue( `All rules are invalid: ` + `[spec.rules[0].matches[0].method.service: Invalid value: "service{}": invalid path value,` + @@ -736,7 +738,7 @@ func TestBuildGRPCRoute(t *testing.T) { SectionName: grOneInvalid.Spec.ParentRefs[0].SectionName, }, }, - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ conditions.NewRoutePartiallyInvalid( `spec.rules[1].matches[0].headers[0].type: Unsupported value: "": supported values: "Exact", "RegularExpression"`, ), @@ -782,7 +784,7 @@ func TestBuildGRPCRoute(t *testing.T) { SectionName: grInvalidHeadersInvalidType.Spec.ParentRefs[0].SectionName, }, }, - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ conditions.NewRouteUnsupportedValue( `All rules are invalid: spec.rules[0].matches[0].headers[0].type: ` + `Unsupported value: "": supported values: "Exact", "RegularExpression"`, @@ -820,7 +822,7 @@ func TestBuildGRPCRoute(t *testing.T) { SectionName: grInvalidHeadersEmptyType.Spec.ParentRefs[0].SectionName, }, }, - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ conditions.NewRouteUnsupportedValue( `All rules are invalid: spec.rules[0].matches[0].headers[0].type: ` + `Required value: cannot be empty`, @@ -858,7 +860,7 @@ func TestBuildGRPCRoute(t *testing.T) { SectionName: grInvalidMatchesNilMethodType.Spec.ParentRefs[0].SectionName, }, }, - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ conditions.NewRouteUnsupportedValue( `All rules are invalid: spec.rules[0].matches[0].method.type: Required value: cannot be empty`, ), @@ -895,7 +897,7 @@ func TestBuildGRPCRoute(t *testing.T) { SectionName: grInvalidFilter.Spec.ParentRefs[0].SectionName, }, }, - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ conditions.NewRouteUnsupportedValue( `All rules are invalid: spec.rules[0].filters[0].type: Unsupported value: ` + `"InvalidFilter": supported values: "ResponseHeaderModifier", ` + @@ -940,7 +942,7 @@ func TestBuildGRPCRoute(t *testing.T) { SectionName: grInvalidHostname.Spec.ParentRefs[0].SectionName, }, }, - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ conditions.NewRouteUnsupportedValue( `spec.hostnames[0]: Invalid value: "": cannot be empty string`, ), @@ -963,7 +965,7 @@ func TestBuildGRPCRoute(t *testing.T) { SectionName: grInvalidSnippetsFilter.Spec.ParentRefs[0].SectionName, }, }, - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ conditions.NewRouteUnsupportedValue( "All rules are invalid: spec.rules[0].filters[0].extensionRef: " + "Unsupported value: \"wrong\": supported values: \"gateway.nginx.org\"", @@ -1001,7 +1003,7 @@ func TestBuildGRPCRoute(t *testing.T) { SectionName: grUnresolvableSnippetsFilter.Spec.ParentRefs[0].SectionName, }, }, - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ conditions.NewRouteResolvedRefsInvalidFilter( "spec.rules[0].filters[0].extensionRef: Not found: " + `{"group":"gateway.nginx.org","kind":"SnippetsFilter",` + @@ -1040,7 +1042,7 @@ func TestBuildGRPCRoute(t *testing.T) { SectionName: grInvalidAndUnresolvableSnippetsFilter.Spec.ParentRefs[0].SectionName, }, }, - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ conditions.NewRouteUnsupportedValue( "All rules are invalid: spec.rules[0].filters[1].extensionRef: " + "Unsupported value: \"wrong\": supported values: \"gateway.nginx.org\"", @@ -1376,3 +1378,133 @@ func TestConvertGRPCHeaderMatchType(t *testing.T) { }) } } + +func TestProcessGRPCRouteRule_UnsupportedFields(t *testing.T) { + t.Parallel() + + tests := []struct { + specRule v1.GRPCRouteRule + name string + expectedErrors int + }{ + { + name: "No unsupported fields", + specRule: v1.GRPCRouteRule{}, // Empty rule, no unsupported fields + expectedErrors: 0, + }, + { + name: "One unsupported field", + specRule: v1.GRPCRouteRule{ + Name: helpers.GetPointer[v1.SectionName]("unsupported-name"), + }, + expectedErrors: 1, + }, + { + name: "Multiple unsupported fields", + specRule: v1.GRPCRouteRule{ + Name: helpers.GetPointer[v1.SectionName]("unsupported-name"), + SessionPersistence: helpers.GetPointer( + v1.SessionPersistence{ + Type: helpers.GetPointer(v1.SessionPersistenceType("unsupported-session-persistence")), + }), + }, + expectedErrors: 2, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + t.Parallel() + g := NewWithT(t) + + rulePath := field.NewPath("spec").Child("rules") + var errors routeRuleErrors + + // Wrap the rule in GRPCRouteRuleWrapper + unsupportedFieldsErrors := checkForUnsupportedGRPCFields(test.specRule, rulePath) + if len(unsupportedFieldsErrors) > 0 { + errors.warn = append(errors.warn, unsupportedFieldsErrors...) + } + + g.Expect(errors.warn).To(HaveLen(test.expectedErrors)) + }) + } +} + +func TestProcessGRPCRouteRules_UnsupportedFields(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + specRules []v1.GRPCRouteRule + expectedConds conditions.Conditions + expectedWarns int + expectedValid bool + }{ + { + name: "No unsupported fields", + specRules: []v1.GRPCRouteRule{{}}, + expectedValid: true, + expectedConds: nil, + expectedWarns: 0, + }, + { + name: "One unsupported field", + specRules: []v1.GRPCRouteRule{ + { + Name: helpers.GetPointer[v1.SectionName]("unsupported-name"), + }, + }, + expectedValid: true, + expectedConds: conditions.Conditions{ + conditions.NewRouteUnsupportedField("There are rules with unsupported fields configurations: spec.rules[0].name: " + + "Forbidden: NGINX Gateway Fabric does not support \"SectionName\" field at the moment, supported fields are: " + + "GRPCBackendRef, GRPCRouteMatch, GRPCRouteFilter"), + }, + expectedWarns: 1, + }, + { + name: "Multiple unsupported fields", + specRules: []v1.GRPCRouteRule{ + { + Name: helpers.GetPointer[v1.SectionName]("unsupported-name"), + SessionPersistence: helpers.GetPointer(v1.SessionPersistence{ + Type: helpers.GetPointer(v1.SessionPersistenceType("unsupported-session-persistence")), + }), + }, + }, + expectedValid: true, + expectedConds: conditions.Conditions{ + conditions.NewRouteUnsupportedField("There are rules with unsupported fields configurations: " + + "[spec.rules[0].name: Forbidden: NGINX Gateway Fabric does not support \"SectionName\" field at the moment, " + + "supported fields are: GRPCBackendRef, GRPCRouteMatch, GRPCRouteFilter, spec.rules[0].sessionPersistence: " + + "Forbidden: NGINX Gateway Fabric does not support \"SessionPersistence\" field at the moment, " + + "supported fields are: GRPCBackendRef, GRPCRouteMatch, GRPCRouteFilter]"), + }, + expectedWarns: 2, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + t.Parallel() + g := NewWithT(t) + + _, valid, conds := processGRPCRouteRules( + test.specRules, + validation.SkipValidator{}, + nil, + ) + + g.Expect(valid).To(Equal(test.expectedValid)) + if test.expectedConds == nil { + g.Expect(conds).To(BeEmpty()) + } else { + g.Expect(conds).To(HaveLen(len(test.expectedConds))) + for i, expectedCond := range test.expectedConds { + g.Expect(conds[i].Message).To(Equal(expectedCond.Message)) + } + } + }) + } +} diff --git a/internal/controller/state/graph/httproute.go b/internal/controller/state/graph/httproute.go index 48415d0573..ba532b5977 100644 --- a/internal/controller/state/graph/httproute.go +++ b/internal/controller/state/graph/httproute.go @@ -171,6 +171,11 @@ func processHTTPRouteRule( validMatches := true + unsupportedFieldsErrors := checkForUnsupportedHTTPFields(specRule, rulePath) + if len(unsupportedFieldsErrors) > 0 { + errors.warn = append(errors.warn, unsupportedFieldsErrors...) + } + for j, match := range specRule.Matches { matchPath := rulePath.Child("matches").Index(j) @@ -236,7 +241,7 @@ func processHTTPRouteRules( specRules []v1.HTTPRouteRule, validator validation.HTTPFieldsValidator, resolveExtRefFunc resolveExtRefFilter, -) (rules []RouteRule, valid bool, conds []conditions.Condition) { +) (rules []RouteRule, valid bool, conds conditions.Conditions) { rules = make([]RouteRule, len(specRules)) var ( @@ -263,10 +268,17 @@ func processHTTPRouteRules( rules[i] = rr } - conds = make([]conditions.Condition, 0, 2) + conds = make(conditions.Conditions, 0, 2) valid = true + // add warning condition for unsupported fields if any + if len(allRulesErrors.warn) > 0 { + msg := "There are rules with unsupported fields configurations: " + allRulesErrors.warn.ToAggregate().Error() + conds = append(conds, conditions.NewRouteUnsupportedField(msg)) + valid = true + } + if len(allRulesErrors.invalid) > 0 { msg := allRulesErrors.invalid.ToAggregate().Error() @@ -518,3 +530,43 @@ func validateFilterRewrite( return allErrs } + +func checkForUnsupportedHTTPFields(rule v1.HTTPRouteRule, rulePath *field.Path) field.ErrorList { + supportedFields := []string{"HTTPBackendRef", "HTTPRouteMatch", "HTTPRouteFilter"} + var ruleErrors field.ErrorList + + if rule.Name != nil { + ruleErrors = append(ruleErrors, field.Forbidden( + rulePath.Child("name"), + "NGINX Gateway Fabric does not support SectionName field at the moment, supported fields are: "+ + strings.Join(supportedFields, ", "), + )) + } + if rule.Timeouts != nil { + ruleErrors = append(ruleErrors, field.Forbidden( + rulePath.Child("timeouts"), + "NGINX Gateway Fabric does not support \"HTTPRouteTimeouts\" field at the moment, supported fields are: "+ + strings.Join(supportedFields, ", "), + )) + } + if rule.Retry != nil { + ruleErrors = append(ruleErrors, field.Forbidden( + rulePath.Child("retry"), + "NGINX Gateway Fabric does not support \"HTTPRouteRetry\" field at the moment, supported fields are: "+ + strings.Join(supportedFields, ", "), + )) + } + if rule.SessionPersistence != nil { + ruleErrors = append(ruleErrors, field.Forbidden( + rulePath.Child("sessionPersistence"), + "NGINX Gateway Fabric does not support \"SessionPersistence\" field at the moment, supported fields are: "+ + strings.Join(supportedFields, ", "), + )) + } + + if len(ruleErrors) == 0 { + return nil + } + + return ruleErrors +} diff --git a/internal/controller/state/graph/httproute_test.go b/internal/controller/state/graph/httproute_test.go index 3b90b0970f..80124a2b47 100644 --- a/internal/controller/state/graph/httproute_test.go +++ b/internal/controller/state/graph/httproute_test.go @@ -14,6 +14,7 @@ import ( ngfAPI "github.com/nginx/nginx-gateway-fabric/v2/apis/v1alpha1" "github.com/nginx/nginx-gateway-fabric/v2/internal/controller/state/conditions" "github.com/nginx/nginx-gateway-fabric/v2/internal/controller/state/mirror" + "github.com/nginx/nginx-gateway-fabric/v2/internal/controller/state/validation" "github.com/nginx/nginx-gateway-fabric/v2/internal/controller/state/validation/validationfakes" "github.com/nginx/nginx-gateway-fabric/v2/internal/framework/helpers" "github.com/nginx/nginx-gateway-fabric/v2/internal/framework/kinds" @@ -454,7 +455,7 @@ func TestBuildHTTPRoute(t *testing.T) { SectionName: hrInvalidMatchesEmptyPathType.Spec.ParentRefs[0].SectionName, }, }, - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ conditions.NewRouteUnsupportedValue( `All rules are invalid: spec.rules[0].matches[0].path.type: Required value: path type cannot be nil`, ), @@ -500,7 +501,7 @@ func TestBuildHTTPRoute(t *testing.T) { SectionName: hrInvalidMatchesEmptyPathValue.Spec.ParentRefs[0].SectionName, }, }, - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ conditions.NewRouteUnsupportedValue( `All rules are invalid: spec.rules[0].matches[0].path.value: Required value: path value cannot be nil`, ), @@ -543,7 +544,7 @@ func TestBuildHTTPRoute(t *testing.T) { SectionName: hrInvalidHostname.Spec.ParentRefs[0].SectionName, }, }, - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ conditions.NewRouteUnsupportedValue( `spec.hostnames[0]: Invalid value: "": cannot be empty string`, ), @@ -566,7 +567,7 @@ func TestBuildHTTPRoute(t *testing.T) { SectionName: hrInvalidMatches.Spec.ParentRefs[0].SectionName, }, }, - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ conditions.NewRouteUnsupportedValue( `All rules are invalid: spec.rules[0].matches[0].path.value: Invalid value: "/invalid": invalid path`, ), @@ -603,7 +604,7 @@ func TestBuildHTTPRoute(t *testing.T) { SectionName: hrInvalidFilters.Spec.ParentRefs[0].SectionName, }, }, - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ conditions.NewRouteUnsupportedValue( `All rules are invalid: spec.rules[0].filters[0].requestRedirect.hostname: ` + `Invalid value: "invalid.example.com": invalid hostname`, @@ -641,7 +642,7 @@ func TestBuildHTTPRoute(t *testing.T) { SectionName: hrDroppedInvalidMatches.Spec.ParentRefs[0].SectionName, }, }, - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ conditions.NewRoutePartiallyInvalid( `spec.rules[0].matches[0].path.value: Invalid value: "/invalid": invalid path`, ), @@ -688,7 +689,7 @@ func TestBuildHTTPRoute(t *testing.T) { SectionName: hrDroppedInvalidMatchesAndInvalidFilters.Spec.ParentRefs[0].SectionName, }, }, - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ conditions.NewRoutePartiallyInvalid( `[spec.rules[0].matches[0].path.value: Invalid value: "/invalid": invalid path, ` + `spec.rules[1].filters[0].requestRedirect.hostname: Invalid value: ` + @@ -747,7 +748,7 @@ func TestBuildHTTPRoute(t *testing.T) { SectionName: hrDroppedInvalidFilters.Spec.ParentRefs[0].SectionName, }, }, - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ conditions.NewRoutePartiallyInvalid( `spec.rules[1].filters[0].requestRedirect.hostname: Invalid value: ` + `"invalid.example.com": invalid hostname`, @@ -836,7 +837,7 @@ func TestBuildHTTPRoute(t *testing.T) { SectionName: hrInvalidSnippetsFilter.Spec.ParentRefs[0].SectionName, }, }, - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ conditions.NewRouteUnsupportedValue( "All rules are invalid: spec.rules[0].filters[0].extensionRef: " + "Unsupported value: \"wrong\": supported values: \"gateway.nginx.org\"", @@ -874,7 +875,7 @@ func TestBuildHTTPRoute(t *testing.T) { SectionName: hrUnresolvableSnippetsFilter.Spec.ParentRefs[0].SectionName, }, }, - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ conditions.NewRouteResolvedRefsInvalidFilter( "spec.rules[0].filters[0].extensionRef: Not found: " + `{"group":"gateway.nginx.org","kind":"SnippetsFilter",` + @@ -913,7 +914,7 @@ func TestBuildHTTPRoute(t *testing.T) { SectionName: hrInvalidAndUnresolvableSnippetsFilter.Spec.ParentRefs[0].SectionName, }, }, - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ conditions.NewRouteUnsupportedValue( "All rules are invalid: spec.rules[0].filters[0].extensionRef: " + "Unsupported value: \"wrong\": supported values: \"gateway.nginx.org\"", @@ -1660,3 +1661,143 @@ func TestValidateFilterRewrite(t *testing.T) { }) } } + +func TestUnsupportedFieldsErrors(t *testing.T) { + t.Parallel() + + tests := []struct { + specRule gatewayv1.HTTPRouteRule + name string + expectedErrors int + }{ + { + name: "No unsupported fields", + specRule: gatewayv1.HTTPRouteRule{}, // Empty rule, no unsupported fields + expectedErrors: 0, + }, + { + name: "One unsupported field", + specRule: gatewayv1.HTTPRouteRule{ + Name: helpers.GetPointer[gatewayv1.SectionName]("unsupported-name"), + }, + expectedErrors: 1, + }, + { + name: "Multiple unsupported fields", + specRule: gatewayv1.HTTPRouteRule{ + Name: helpers.GetPointer[gatewayv1.SectionName]("unsupported-name"), + Timeouts: helpers.GetPointer(gatewayv1.HTTPRouteTimeouts{ + Request: (*gatewayv1.Duration)(helpers.GetPointer("unsupported-timeouts")), + }), + Retry: helpers.GetPointer(gatewayv1.HTTPRouteRetry{Attempts: helpers.GetPointer(3)}), + SessionPersistence: helpers.GetPointer(gatewayv1.SessionPersistence{ + Type: helpers.GetPointer(gatewayv1.SessionPersistenceType("unsupported-session-persistence")), + }), + }, + expectedErrors: 4, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + t.Parallel() + g := NewWithT(t) + + rulePath := field.NewPath("spec").Child("rules") + var errors routeRuleErrors + + unsupportedFieldsErrors := checkForUnsupportedHTTPFields(test.specRule, rulePath) + if len(unsupportedFieldsErrors) > 0 { + errors.warn = append(errors.warn, unsupportedFieldsErrors...) + } + + g.Expect(errors.warn).To(HaveLen(test.expectedErrors)) + }) + } +} + +func TestProcessHTTPRouteRules_UnsupportedFields(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + specRules []gatewayv1.HTTPRouteRule + expectedConds conditions.Conditions + expectedWarns int + expectedValid bool + }{ + { + name: "No unsupported fields", + specRules: []gatewayv1.HTTPRouteRule{{}}, + expectedValid: true, + expectedConds: nil, + expectedWarns: 0, + }, + { + name: "One unsupported field", + specRules: []gatewayv1.HTTPRouteRule{ + { + Name: helpers.GetPointer[gatewayv1.SectionName]("unsupported-name"), + }, + }, + expectedValid: true, + expectedConds: conditions.Conditions{ + conditions.NewRouteUnsupportedField("There are rules with unsupported fields configurations: spec.rules[0].name: " + + "Forbidden: NGINX Gateway Fabric does not support SectionName field at the moment, supported fields are: " + + "HTTPBackendRef, HTTPRouteMatch, HTTPRouteFilter"), + }, + expectedWarns: 1, + }, + { + name: "Multiple unsupported fields", + specRules: []gatewayv1.HTTPRouteRule{ + { + Name: helpers.GetPointer[gatewayv1.SectionName]("unsupported-name"), + Timeouts: helpers.GetPointer(gatewayv1.HTTPRouteTimeouts{ + Request: (*gatewayv1.Duration)(helpers.GetPointer("unsupported-timeouts")), + }), + Retry: helpers.GetPointer(gatewayv1.HTTPRouteRetry{Attempts: helpers.GetPointer(3)}), + SessionPersistence: helpers.GetPointer(gatewayv1.SessionPersistence{ + Type: helpers.GetPointer(gatewayv1.SessionPersistenceType("unsupported-session-persistence")), + }), + }, + }, + expectedValid: true, + expectedConds: conditions.Conditions{ + conditions.NewRouteUnsupportedField("There are rules with unsupported fields configurations: " + + "[spec.rules[0].name: Forbidden: NGINX Gateway Fabric does not support SectionName field at the moment, " + + "supported fields are: HTTPBackendRef, HTTPRouteMatch, HTTPRouteFilter, spec.rules[0].timeouts: Forbidden: " + + "NGINX Gateway Fabric does not support \"HTTPRouteTimeouts\" field at the moment, supported fields are: " + + "HTTPBackendRef, HTTPRouteMatch, HTTPRouteFilter, spec.rules[0].retry: Forbidden: NGINX Gateway Fabric " + + "does not support \"HTTPRouteRetry\" field at the moment, supported fields are: HTTPBackendRef, " + + "HTTPRouteMatch, HTTPRouteFilter, spec.rules[0].sessionPersistence: Forbidden: NGINX Gateway Fabric " + + "does not support \"SessionPersistence\" field at the moment, supported fields are: HTTPBackendRef, " + + "HTTPRouteMatch, HTTPRouteFilter]"), + }, + expectedWarns: 4, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + t.Parallel() + g := NewWithT(t) + + _, valid, conds := processHTTPRouteRules( + test.specRules, + validation.SkipValidator{}, + nil, + ) + + g.Expect(valid).To(Equal(test.expectedValid)) + if test.expectedConds == nil { + g.Expect(conds).To(BeEmpty()) + } else { + g.Expect(conds).To(HaveLen(len(test.expectedConds))) + for i, expectedCond := range test.expectedConds { + g.Expect(conds[i].Message).To(Equal(expectedCond.Message)) + } + } + }) + } +} diff --git a/internal/controller/state/graph/multiple_gateways_test.go b/internal/controller/state/graph/multiple_gateways_test.go index 80f4a10b04..cde7f47926 100644 --- a/internal/controller/state/graph/multiple_gateways_test.go +++ b/internal/controller/state/graph/multiple_gateways_test.go @@ -150,7 +150,7 @@ func convertedGateway( nginxProxy *NginxProxy, effectiveNp *EffectiveNginxProxy, listeners []*Listener, - conds []conditions.Condition, + conds conditions.Conditions, ) *Gateway { return &Gateway{ Source: gw, @@ -244,7 +244,7 @@ func Test_MultipleGateways_WithNginxProxy(t *testing.T) { gateway1withNP := createGateway("gateway-1", testNs, "nginx-proxy-gateway-1", []gatewayv1.Listener{}) gateway3withNP := createGateway("gateway-3", "test2", "nginx-proxy-gateway-3", []gatewayv1.Listener{}) - gcConditions := []conditions.Condition{conditions.NewGatewayClassResolvedRefs()} + gcConditions := conditions.Conditions{conditions.NewGatewayClassResolvedRefs()} tests := []struct { clusterState ClusterState @@ -618,7 +618,7 @@ func Test_MultipleGateways_WithListeners(t *testing.T) { map[L4RouteKey]*L4Route{}, ), }, - []conditions.Condition{conditions.NewGatewayClassResolvedRefs()}, + conditions.Conditions{conditions.NewGatewayClassResolvedRefs()}, ), client.ObjectKeyFromObject(gateway2): convertedGateway( gateway2, @@ -634,7 +634,7 @@ func Test_MultipleGateways_WithListeners(t *testing.T) { map[L4RouteKey]*L4Route{}, ), }, - []conditions.Condition{conditions.NewGatewayClassResolvedRefs()}, + conditions.Conditions{conditions.NewGatewayClassResolvedRefs()}, ), }, Routes: map[RouteKey]*L7Route{}, @@ -707,7 +707,7 @@ func Test_MultipleGateways_WithListeners(t *testing.T) { map[L4RouteKey]*L4Route{}, ), }, - []conditions.Condition{conditions.NewGatewayClassResolvedRefs()}, + conditions.Conditions{conditions.NewGatewayClassResolvedRefs()}, ), client.ObjectKeyFromObject(gatewayMultipleListeners2): convertedGateway( gatewayMultipleListeners2, @@ -739,7 +739,7 @@ func Test_MultipleGateways_WithListeners(t *testing.T) { map[L4RouteKey]*L4Route{}, ), }, - []conditions.Condition{conditions.NewGatewayClassResolvedRefs()}, + conditions.Conditions{conditions.NewGatewayClassResolvedRefs()}, ), client.ObjectKeyFromObject(gatewayMultipleListeners3): convertedGateway( gatewayMultipleListeners3, @@ -771,7 +771,7 @@ func Test_MultipleGateways_WithListeners(t *testing.T) { map[L4RouteKey]*L4Route{}, ), }, - []conditions.Condition{conditions.NewGatewayClassResolvedRefs()}, + conditions.Conditions{conditions.NewGatewayClassResolvedRefs()}, ), }, Routes: map[RouteKey]*L7Route{}, @@ -826,7 +826,7 @@ func Test_MultipleGateways_WithListeners(t *testing.T) { map[L4RouteKey]*L4Route{}, ), }, - []conditions.Condition{conditions.NewGatewayClassResolvedRefs()}, + conditions.Conditions{conditions.NewGatewayClassResolvedRefs()}, ), client.ObjectKeyFromObject(gatewayHTTPSSamePortHostname): convertedGateway( gatewayHTTPSSamePortHostname, @@ -842,7 +842,7 @@ func Test_MultipleGateways_WithListeners(t *testing.T) { map[L4RouteKey]*L4Route{}, ), }, - []conditions.Condition{conditions.NewGatewayClassResolvedRefs()}, + conditions.Conditions{conditions.NewGatewayClassResolvedRefs()}, ), }, Routes: map[RouteKey]*L7Route{}, diff --git a/internal/controller/state/graph/policies.go b/internal/controller/state/graph/policies.go index f790f478e0..55236af6a0 100644 --- a/internal/controller/state/graph/policies.go +++ b/internal/controller/state/graph/policies.go @@ -32,7 +32,7 @@ type Policy struct { // Conditions holds the conditions for the Policy. // These conditions apply to the entire Policy. // The conditions in the Ancestor apply only to the Policy in regard to the Ancestor. - Conditions []conditions.Condition + Conditions conditions.Conditions // Valid indicates whether the Policy is valid. Valid bool } @@ -42,7 +42,7 @@ type PolicyAncestor struct { // Ancestor is the ancestor object. Ancestor v1.ParentReference // Conditions contains the list of conditions of the Policy in relation to the ancestor. - Conditions []conditions.Condition + Conditions conditions.Conditions } // PolicyTargetRef represents the object that the Policy is targeting. @@ -205,7 +205,7 @@ func attachPolicyToService( if !gw.Valid { policy.InvalidForGateways[gwNsName] = struct{}{} - ancestor.Conditions = []conditions.Condition{conditions.NewPolicyTargetNotFound("Parent Gateway is invalid")} + ancestor.Conditions = conditions.Conditions{conditions.NewPolicyTargetNotFound("Parent Gateway is invalid")} policy.Ancestors = append(policy.Ancestors, ancestor) continue } @@ -254,7 +254,7 @@ func attachPolicyToRoute( } if !route.Valid || !route.Attachable || len(route.ParentRefs) == 0 { - ancestor.Conditions = []conditions.Condition{conditions.NewPolicyTargetNotFound("TargetRef is invalid")} + ancestor.Conditions = conditions.Conditions{conditions.NewPolicyTargetNotFound("TargetRef is invalid")} policy.Ancestors = append(policy.Ancestors, ancestor) return } @@ -334,14 +334,14 @@ func attachPolicyToGateway( if !exists || (gw != nil && gw.Source == nil) { policy.InvalidForGateways[ref.Nsname] = struct{}{} - ancestor.Conditions = []conditions.Condition{conditions.NewPolicyTargetNotFound("TargetRef is not found")} + ancestor.Conditions = conditions.Conditions{conditions.NewPolicyTargetNotFound("TargetRef is not found")} policy.Ancestors = append(policy.Ancestors, ancestor) return } if !gw.Valid { policy.InvalidForGateways[ref.Nsname] = struct{}{} - ancestor.Conditions = []conditions.Condition{conditions.NewPolicyTargetNotFound("TargetRef is invalid")} + ancestor.Conditions = conditions.Conditions{conditions.NewPolicyTargetNotFound("TargetRef is invalid")} policy.Ancestors = append(policy.Ancestors, ancestor) return } @@ -366,7 +366,7 @@ func processPolicies( processedPolicies := make(map[PolicyKey]*Policy) for key, policy := range pols { - var conds []conditions.Condition + var conds conditions.Conditions targetRefs := make([]PolicyTargetRef, 0, len(policy.GetTargetRefs())) targetedRoutes := make(map[types.NamespacedName]*L7Route) @@ -428,8 +428,8 @@ func processPolicies( func checkTargetRoutesForOverlap( targetedRoutes map[types.NamespacedName]*L7Route, graphRoutes map[RouteKey]*L7Route, -) []conditions.Condition { - var conds []conditions.Condition +) conditions.Conditions { + var conds conditions.Conditions for _, targetedRoute := range targetedRoutes { // We need to check if this route referenced in the policy has an overlapping @@ -622,7 +622,7 @@ func addPolicyAffectedStatusToTargetRefs( } } -func addStatusToTargetRefs(policyKind string, conditionsList *[]conditions.Condition) { +func addStatusToTargetRefs(policyKind string, conditionsList *conditions.Conditions) { if conditionsList == nil { return } diff --git a/internal/controller/state/graph/policies_test.go b/internal/controller/state/graph/policies_test.go index e84bbd6b56..346646f266 100644 --- a/internal/controller/state/graph/policies_test.go +++ b/internal/controller/state/graph/policies_test.go @@ -299,9 +299,9 @@ func TestAttachPolicyToRoute(t *testing.T) { } validatorError := &policiesfakes.FakeValidator{ - ValidateGlobalSettingsStub: func(_ policies.Policy, gs *policies.GlobalSettings) []conditions.Condition { + ValidateGlobalSettingsStub: func(_ policies.Policy, gs *policies.GlobalSettings) conditions.Conditions { if !gs.TelemetryEnabled { - return []conditions.Condition{ + return conditions.Conditions{ conditions.NewPolicyNotAcceptedNginxProxyNotSet(conditions.PolicyMessageTelemetryNotEnabled), } } @@ -361,7 +361,7 @@ func TestAttachPolicyToRoute(t *testing.T) { expAncestors: []PolicyAncestor{ { Ancestor: createExpAncestor(kinds.HTTPRoute), - Conditions: []conditions.Condition{conditions.NewPolicyTargetNotFound("TargetRef is invalid")}, + Conditions: conditions.Conditions{conditions.NewPolicyTargetNotFound("TargetRef is invalid")}, }, }, expAttached: false, @@ -374,7 +374,7 @@ func TestAttachPolicyToRoute(t *testing.T) { expAncestors: []PolicyAncestor{ { Ancestor: createExpAncestor(kinds.HTTPRoute), - Conditions: []conditions.Condition{conditions.NewPolicyTargetNotFound("TargetRef is invalid")}, + Conditions: conditions.Conditions{conditions.NewPolicyTargetNotFound("TargetRef is invalid")}, }, }, expAttached: false, @@ -387,7 +387,7 @@ func TestAttachPolicyToRoute(t *testing.T) { expAncestors: []PolicyAncestor{ { Ancestor: createExpAncestor(kinds.HTTPRoute), - Conditions: []conditions.Condition{conditions.NewPolicyTargetNotFound("TargetRef is invalid")}, + Conditions: conditions.Conditions{conditions.NewPolicyTargetNotFound("TargetRef is invalid")}, }, }, expAttached: false, @@ -447,7 +447,7 @@ func TestAttachPolicyToRoute(t *testing.T) { expAncestors: []PolicyAncestor{ { Ancestor: createExpAncestor(kinds.HTTPRoute), - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ conditions.NewPolicyNotAcceptedNginxProxyNotSet(conditions.PolicyMessageTelemetryNotEnabled), }, }, @@ -486,7 +486,7 @@ func TestAttachPolicyToRoute(t *testing.T) { expAncestors: []PolicyAncestor{ { Ancestor: createExpAncestor(kinds.HTTPRoute), - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ conditions.NewPolicyNotAcceptedNginxProxyNotSet(conditions.PolicyMessageTelemetryNotEnabled), }, }, @@ -596,7 +596,7 @@ func TestAttachPolicyToGateway(t *testing.T) { expAncestors: []PolicyAncestor{ { Ancestor: getGatewayParentRef(gateway2NsName), - Conditions: []conditions.Condition{conditions.NewPolicyTargetNotFound("TargetRef is not found")}, + Conditions: conditions.Conditions{conditions.NewPolicyTargetNotFound("TargetRef is not found")}, }, }, expAttached: false, @@ -617,7 +617,7 @@ func TestAttachPolicyToGateway(t *testing.T) { expAncestors: []PolicyAncestor{ { Ancestor: getGatewayParentRef(gatewayNsName), - Conditions: []conditions.Condition{conditions.NewPolicyTargetNotFound("TargetRef is invalid")}, + Conditions: conditions.Conditions{conditions.NewPolicyTargetNotFound("TargetRef is invalid")}, }, }, expAttached: false, @@ -811,7 +811,7 @@ func TestAttachPolicyToService(t *testing.T) { expAncestors: []PolicyAncestor{ { Ancestor: getGatewayParentRef(gwNsname), - Conditions: []conditions.Condition{conditions.NewPolicyTargetNotFound("Parent Gateway is invalid")}, + Conditions: conditions.Conditions{conditions.NewPolicyTargetNotFound("Parent Gateway is invalid")}, }, }, }, @@ -1015,9 +1015,9 @@ func TestProcessPolicies(t *testing.T) { { name: "invalid and valid policies", validator: &policiesfakes.FakeValidator{ - ValidateStub: func(policy policies.Policy) []conditions.Condition { + ValidateStub: func(policy policies.Policy) conditions.Conditions { if policy.GetName() == "pol1" { - return []conditions.Condition{conditions.NewPolicyInvalid("invalid error")} + return conditions.Conditions{conditions.NewPolicyInvalid("invalid error")} } return nil @@ -1037,7 +1037,7 @@ func TestProcessPolicies(t *testing.T) { Group: v1.GroupName, }, }, - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ conditions.NewPolicyInvalid("invalid error"), }, Ancestors: []PolicyAncestor{}, @@ -1093,7 +1093,7 @@ func TestProcessPolicies(t *testing.T) { Group: v1.GroupName, }, }, - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ conditions.NewPolicyConflicted("Conflicts with another MyPolicy"), }, Ancestors: []PolicyAncestor{}, @@ -1174,7 +1174,7 @@ func TestProcessPolicies_RouteOverlap(t *testing.T) { policies map[PolicyKey]policies.Policy routes map[RouteKey]*L7Route name string - expConditions []conditions.Condition + expConditions conditions.Conditions valid bool }{ { @@ -1231,7 +1231,7 @@ func TestProcessPolicies_RouteOverlap(t *testing.T) { }: createTestRouteWithPaths("hr2", "/coffee"), }, valid: false, - expConditions: []conditions.Condition{ + expConditions: conditions.Conditions{ { Type: "Accepted", Status: "False", @@ -1280,7 +1280,7 @@ func TestProcessPolicies_RouteOverlap(t *testing.T) { }: createTestRouteWithPaths("hr-coffee-latte", "/coffee", "/latte"), }, valid: false, - expConditions: []conditions.Condition{ + expConditions: conditions.Conditions{ { Type: "Accepted", Status: "False", @@ -1715,7 +1715,7 @@ func TestAddPolicyAffectedStatusOnTargetRefs(t *testing.T) { policies map[PolicyKey]*Policy gws map[types.NamespacedName]*Gateway routes map[RouteKey]*L7Route - expectedConditions map[types.NamespacedName][]conditions.Condition + expectedConditions map[types.NamespacedName]conditions.Conditions name string missingKeys bool }{ @@ -1735,7 +1735,7 @@ func TestAddPolicyAffectedStatusOnTargetRefs(t *testing.T) { }, gws: createGatewayMap(types.NamespacedName{Namespace: testNs, Name: "gw1"}), routes: nil, - expectedConditions: map[types.NamespacedName][]conditions.Condition{ + expectedConditions: map[types.NamespacedName]conditions.Conditions{ {Namespace: testNs, Name: "gw1"}: { conditions.NewClientSettingsPolicyAffected(), }, @@ -1755,7 +1755,7 @@ func TestAddPolicyAffectedStatusOnTargetRefs(t *testing.T) { }, gws: createGatewayMap(types.NamespacedName{Namespace: testNs, Name: "gw2"}), routes: nil, - expectedConditions: map[types.NamespacedName][]conditions.Condition{ + expectedConditions: map[types.NamespacedName]conditions.Conditions{ {Namespace: testNs, Name: "gw2"}: { conditions.NewClientSettingsPolicyAffected(), conditions.NewObservabilityPolicyAffected(), @@ -1792,7 +1792,7 @@ func TestAddPolicyAffectedStatusOnTargetRefs(t *testing.T) { }, }, }, - expectedConditions: map[types.NamespacedName][]conditions.Condition{ + expectedConditions: map[types.NamespacedName]conditions.Conditions{ {Namespace: testNs, Name: "hr1"}: { conditions.NewObservabilityPolicyAffected(), }, @@ -1838,7 +1838,7 @@ func TestAddPolicyAffectedStatusOnTargetRefs(t *testing.T) { }, }, }, - expectedConditions: map[types.NamespacedName][]conditions.Condition{ + expectedConditions: map[types.NamespacedName]conditions.Conditions{ {Namespace: testNs, Name: "gw3"}: { conditions.NewClientSettingsPolicyAffected(), conditions.NewObservabilityPolicyAffected(), @@ -1874,7 +1874,7 @@ func TestAddPolicyAffectedStatusOnTargetRefs(t *testing.T) { }, }, }, - expectedConditions: map[types.NamespacedName][]conditions.Condition{ + expectedConditions: map[types.NamespacedName]conditions.Conditions{ {Namespace: testNs, Name: "hr3"}: { conditions.NewClientSettingsPolicyAffected(), }, @@ -1898,7 +1898,7 @@ func TestAddPolicyAffectedStatusOnTargetRefs(t *testing.T) { }, }, }, - expectedConditions: map[types.NamespacedName][]conditions.Condition{ + expectedConditions: map[types.NamespacedName]conditions.Conditions{ {Namespace: testNs, Name: "invalid"}: {}, }, }, @@ -1913,7 +1913,7 @@ func TestAddPolicyAffectedStatusOnTargetRefs(t *testing.T) { gws: createGatewayMap( types.NamespacedName{Namespace: testNs, Name: "gw2"}, ), - expectedConditions: map[types.NamespacedName][]conditions.Condition{ + expectedConditions: map[types.NamespacedName]conditions.Conditions{ {Namespace: testNs, Name: "gw1"}: {}, }, missingKeys: true, @@ -1929,7 +1929,7 @@ func TestAddPolicyAffectedStatusOnTargetRefs(t *testing.T) { gws: map[types.NamespacedName]*Gateway{ {Namespace: testNs, Name: "gw1"}: nil, }, - expectedConditions: map[types.NamespacedName][]conditions.Condition{ + expectedConditions: map[types.NamespacedName]conditions.Conditions{ {Namespace: testNs, Name: "gw1"}: {}, }, missingKeys: true, @@ -1952,7 +1952,7 @@ func TestAddPolicyAffectedStatusOnTargetRefs(t *testing.T) { }, }, }, - expectedConditions: map[types.NamespacedName][]conditions.Condition{ + expectedConditions: map[types.NamespacedName]conditions.Conditions{ {Namespace: testNs, Name: "hr1"}: {}, }, missingKeys: true, @@ -2225,13 +2225,13 @@ func TestNGFPolicyAncestorLimitHandling(t *testing.T) { Source: &v1.Gateway{ ObjectMeta: metav1.ObjectMeta{Name: "gateway1", Namespace: "test"}, }, - Conditions: []conditions.Condition{}, // Start with empty conditions + Conditions: conditions.Conditions{}, // Start with empty conditions }, {Namespace: "test", Name: "gateway2"}: { Source: &v1.Gateway{ ObjectMeta: metav1.ObjectMeta{Name: "gateway2", Namespace: "test"}, }, - Conditions: []conditions.Condition{}, // Start with empty conditions + Conditions: conditions.Conditions{}, // Start with empty conditions }, } @@ -2249,7 +2249,7 @@ func TestNGFPolicyAncestorLimitHandling(t *testing.T) { // Create fake validator validator := &policiesfakes.FakeValidator{ - ValidateStub: func(_ policies.Policy) []conditions.Condition { + ValidateStub: func(_ policies.Policy) conditions.Conditions { return nil }, ConflictsStub: func(_, _ policies.Policy) bool { diff --git a/internal/controller/state/graph/route_common.go b/internal/controller/state/graph/route_common.go index c156ca738a..77320a78da 100644 --- a/internal/controller/state/graph/route_common.go +++ b/internal/controller/state/graph/route_common.go @@ -44,7 +44,7 @@ type ParentRefAttachmentStatus struct { // FailedConditions are the conditions that describe why the ParentRef is not attached to the Gateway, or other // failures that may lead to partial attachments. For example, a backendRef could be invalid, but the route can // still attach. The backendRef condition would be displayed here. - FailedConditions []conditions.Condition + FailedConditions conditions.Conditions // ListenerPort is the port on the Listener that the Route is attached to. // FIXME(sarthyparty): https://github.com/nginx/nginx-gateway-fabric/issues/3811 // Needs to be a map of to port number @@ -98,7 +98,7 @@ type L4Route struct { // ParentRefs describe the references to the parents in a Route. ParentRefs []ParentRef // Conditions define the conditions to be reported in the status of the Route. - Conditions []conditions.Condition + Conditions conditions.Conditions // Spec is the L4RouteSpec of the Route Spec L4RouteSpec // Valid indicates if the Route is valid. @@ -126,7 +126,7 @@ type L7Route struct { // ParentRefs describe the references to the parents in a Route. ParentRefs []ParentRef // Conditions define the conditions to be reported in the status of the Route. - Conditions []conditions.Condition + Conditions conditions.Conditions // Policies holds the policies that are attached to the Route. Policies []*Policy // Valid indicates if the Route is valid. @@ -143,16 +143,15 @@ type L7RouteSpec struct { } type RouteRule struct { - // Matches define the predicate used to match requests to a given action. - Matches []v1.HTTPRouteMatch - // RouteBackendRefs are a wrapper for v1.BackendRef and any BackendRef filters from the HTTPRoute or GRPCRoute. - RouteBackendRefs []RouteBackendRef - // BackendRefs is an internal representation of a backendRef in a Route. - BackendRefs []BackendRef - // Filters define processing steps that must be completed during the request or response lifecycle. - Filters RouteRuleFilters - // ValidMatches indicates if the matches are valid and accepted by the Route. - ValidMatches bool + Name *v1.SectionName + Timeouts *v1.HTTPRouteTimeouts + Retry *v1.HTTPRouteRetry + SessionPersistence *v1.SessionPersistence + Matches []v1.HTTPRouteMatch + RouteBackendRefs []RouteBackendRef + BackendRefs []BackendRef + Filters RouteRuleFilters + ValidMatches bool } // RouteBackendRef is a wrapper for v1.BackendRef and any BackendRef filters from the HTTPRoute or GRPCRoute. @@ -200,12 +199,14 @@ func CreateGatewayListenerKey(gwNSName types.NamespacedName, listenerName string type routeRuleErrors struct { invalid field.ErrorList resolve field.ErrorList + warn field.ErrorList } func (e routeRuleErrors) append(newErrors routeRuleErrors) routeRuleErrors { return routeRuleErrors{ invalid: append(e.invalid, newErrors.invalid...), resolve: append(e.resolve, newErrors.resolve...), + warn: append(e.warn, newErrors.warn...), } } diff --git a/internal/controller/state/graph/route_common_test.go b/internal/controller/state/graph/route_common_test.go index 6ded299d73..93f1bcd299 100644 --- a/internal/controller/state/graph/route_common_test.go +++ b/internal/controller/state/graph/route_common_test.go @@ -574,7 +574,7 @@ func TestBindRouteToListeners(t *testing.T) { name string expectedGatewayListeners []*Listener expectedSectionNameRefs []ParentRef - expectedConditions []conditions.Condition + expectedConditions conditions.Conditions }{ { route: createNormalHTTPRoute(gw), @@ -704,7 +704,7 @@ func TestBindRouteToListeners(t *testing.T) { SectionName: hrWithEmptySectionName.Spec.ParentRefs[0].SectionName, Attachment: &ParentRefAttachmentStatus{ Attached: false, - FailedConditions: []conditions.Condition{conditions.NewRouteInvalidListener()}, + FailedConditions: conditions.Conditions{conditions.NewRouteInvalidListener()}, AcceptedHostnames: map[string][]string{}, }, }, @@ -768,7 +768,7 @@ func TestBindRouteToListeners(t *testing.T) { SectionName: hrWithNonExistingListener.Spec.ParentRefs[0].SectionName, Attachment: &ParentRefAttachmentStatus{ Attached: false, - FailedConditions: []conditions.Condition{conditions.NewRouteNoMatchingParent()}, + FailedConditions: conditions.Conditions{conditions.NewRouteNoMatchingParent()}, AcceptedHostnames: map[string][]string{}, }, }, @@ -794,7 +794,7 @@ func TestBindRouteToListeners(t *testing.T) { SectionName: hr.Spec.ParentRefs[0].SectionName, Attachment: &ParentRefAttachmentStatus{ Attached: false, - FailedConditions: []conditions.Condition{conditions.NewRouteInvalidListener()}, + FailedConditions: conditions.Conditions{conditions.NewRouteInvalidListener()}, AcceptedHostnames: map[string][]string{}, }, }, @@ -820,7 +820,7 @@ func TestBindRouteToListeners(t *testing.T) { SectionName: hr.Spec.ParentRefs[0].SectionName, Attachment: &ParentRefAttachmentStatus{ Attached: false, - FailedConditions: []conditions.Condition{conditions.NewRouteNoMatchingListenerHostname()}, + FailedConditions: conditions.Conditions{conditions.NewRouteNoMatchingListenerHostname()}, AcceptedHostnames: map[string][]string{}, }, }, @@ -868,7 +868,7 @@ func TestBindRouteToListeners(t *testing.T) { SectionName: hr.Spec.ParentRefs[0].SectionName, Attachment: &ParentRefAttachmentStatus{ Attached: false, - FailedConditions: []conditions.Condition{conditions.NewRouteInvalidGateway()}, + FailedConditions: conditions.Conditions{conditions.NewRouteInvalidGateway()}, AcceptedHostnames: map[string][]string{}, }, }, @@ -913,7 +913,7 @@ func TestBindRouteToListeners(t *testing.T) { } }), }, - expectedConditions: []conditions.Condition{conditions.NewRouteInvalidListener()}, + expectedConditions: conditions.Conditions{conditions.NewRouteInvalidListener()}, name: "invalid attachable listener", }, { @@ -985,7 +985,7 @@ func TestBindRouteToListeners(t *testing.T) { } }), }, - expectedConditions: []conditions.Condition{conditions.NewRouteInvalidListener()}, + expectedConditions: conditions.Conditions{conditions.NewRouteInvalidListener()}, name: "invalid attachable listener with invalid attachable route", }, { @@ -1012,7 +1012,7 @@ func TestBindRouteToListeners(t *testing.T) { SectionName: hr.Spec.ParentRefs[0].SectionName, Attachment: &ParentRefAttachmentStatus{ Attached: false, - FailedConditions: []conditions.Condition{conditions.NewRouteNotAllowedByListeners()}, + FailedConditions: conditions.Conditions{conditions.NewRouteNotAllowedByListeners()}, AcceptedHostnames: map[string][]string{}, }, }, @@ -1101,7 +1101,7 @@ func TestBindRouteToListeners(t *testing.T) { SectionName: hr.Spec.ParentRefs[0].SectionName, Attachment: &ParentRefAttachmentStatus{ Attached: false, - FailedConditions: []conditions.Condition{conditions.NewRouteNotAllowedByListeners()}, + FailedConditions: conditions.Conditions{conditions.NewRouteNotAllowedByListeners()}, AcceptedHostnames: map[string][]string{}, }, }, @@ -1230,7 +1230,7 @@ func TestBindRouteToListeners(t *testing.T) { SectionName: gr.Spec.ParentRefs[0].SectionName, Attachment: &ParentRefAttachmentStatus{ Attached: false, - FailedConditions: []conditions.Condition{conditions.NewRouteNotAllowedByListeners()}, + FailedConditions: conditions.Conditions{conditions.NewRouteNotAllowedByListeners()}, AcceptedHostnames: map[string][]string{}, }, }, @@ -1275,7 +1275,7 @@ func TestBindRouteToListeners(t *testing.T) { SectionName: gr.Spec.ParentRefs[0].SectionName, Attachment: &ParentRefAttachmentStatus{ Attached: false, - FailedConditions: []conditions.Condition{ + FailedConditions: conditions.Conditions{ conditions.NewRouteUnsupportedConfiguration( `HTTP2 is disabled - cannot configure GRPCRoutes`, ), @@ -1360,7 +1360,7 @@ func TestBindRouteToListeners(t *testing.T) { SectionName: hr.Spec.ParentRefs[0].SectionName, Attachment: &ParentRefAttachmentStatus{ Attached: true, - FailedConditions: []conditions.Condition{ + FailedConditions: conditions.Conditions{ {Message: "invalid backend"}, }, AcceptedHostnames: map[string][]string{ @@ -1763,7 +1763,7 @@ func TestBindL4RouteToListeners(t *testing.T) { noMatchingParentAttachment := ParentRefAttachmentStatus{ AcceptedHostnames: map[string][]string{}, - FailedConditions: []conditions.Condition{conditions.NewRouteNoMatchingParent()}, + FailedConditions: conditions.Conditions{conditions.NewRouteNoMatchingParent()}, } notAttachableRoute := &L4Route{ @@ -1810,7 +1810,7 @@ func TestBindL4RouteToListeners(t *testing.T) { expectedGatewayListeners []*Listener name string expectedSectionNameRefs []ParentRef - expectedConditions []conditions.Condition + expectedConditions conditions.Conditions }{ { route: createNormalRoute(gw), @@ -1920,7 +1920,7 @@ func TestBindL4RouteToListeners(t *testing.T) { { Attachment: &ParentRefAttachmentStatus{ AcceptedHostnames: map[string][]string{}, - FailedConditions: []conditions.Condition{ + FailedConditions: conditions.Conditions{ conditions.NewRouteNoMatchingParent(), }, Attached: false, @@ -1955,7 +1955,7 @@ func TestBindL4RouteToListeners(t *testing.T) { { Attachment: &ParentRefAttachmentStatus{ AcceptedHostnames: map[string][]string{}, - FailedConditions: []conditions.Condition{conditions.NewRouteInvalidGateway()}, + FailedConditions: conditions.Conditions{conditions.NewRouteInvalidGateway()}, Attached: false, }, SectionName: tr.Spec.ParentRefs[0].SectionName, @@ -1995,7 +1995,7 @@ func TestBindL4RouteToListeners(t *testing.T) { SectionName: tr.Spec.ParentRefs[0].SectionName, Attachment: &ParentRefAttachmentStatus{ AcceptedHostnames: map[string][]string{}, - FailedConditions: []conditions.Condition{conditions.NewRouteNotAllowedByListeners()}, + FailedConditions: conditions.Conditions{conditions.NewRouteNotAllowedByListeners()}, }, }, }, @@ -2068,7 +2068,7 @@ func TestBindL4RouteToListeners(t *testing.T) { } }), }, - expectedConditions: []conditions.Condition{conditions.NewRouteInvalidListener()}, + expectedConditions: conditions.Conditions{conditions.NewRouteInvalidListener()}, name: "invalid attachable listener", }, { @@ -2093,7 +2093,7 @@ func TestBindL4RouteToListeners(t *testing.T) { SectionName: tr.Spec.ParentRefs[0].SectionName, Attachment: &ParentRefAttachmentStatus{ AcceptedHostnames: map[string][]string{}, - FailedConditions: []conditions.Condition{conditions.NewRouteNoMatchingListenerHostname()}, + FailedConditions: conditions.Conditions{conditions.NewRouteNoMatchingListenerHostname()}, }, }, }, @@ -2266,7 +2266,7 @@ func TestBindL4RouteToListeners(t *testing.T) { Gateway: &ParentRefGateway{NamespacedName: client.ObjectKeyFromObject(gw)}, Attachment: &ParentRefAttachmentStatus{ AcceptedHostnames: map[string][]string{}, - FailedConditions: []conditions.Condition{conditions.NewRouteNotAllowedByListeners()}, + FailedConditions: conditions.Conditions{conditions.NewRouteNotAllowedByListeners()}, }, SectionName: helpers.GetPointer[gatewayv1.SectionName]("listener-443"), }, @@ -2298,7 +2298,7 @@ func TestBindL4RouteToListeners(t *testing.T) { SectionName: tr.Spec.ParentRefs[0].SectionName, Attachment: &ParentRefAttachmentStatus{ Attached: true, - FailedConditions: []conditions.Condition{ + FailedConditions: conditions.Conditions{ {Message: "invalid backend"}, }, AcceptedHostnames: map[string][]string{ diff --git a/internal/controller/state/graph/snippets_filter.go b/internal/controller/state/graph/snippets_filter.go index dd5727a77e..4e0f2e5fd8 100644 --- a/internal/controller/state/graph/snippets_filter.go +++ b/internal/controller/state/graph/snippets_filter.go @@ -17,7 +17,7 @@ type SnippetsFilter struct { // Snippets stored as a map of nginx context to snippet value. Snippets map[ngfAPI.NginxContext]string // Conditions define the conditions to be reported in the status of the SnippetsFilter. - Conditions []conditions.Condition + Conditions conditions.Conditions // Valid indicates whether the SnippetsFilter is semantically and syntactically valid. Valid bool // Referenced indicates whether the SnippetsFilter is referenced by a Route. @@ -64,7 +64,7 @@ func processSnippetsFilters( if cond := validateSnippetsFilter(sf); cond != nil { processed[nsname] = &SnippetsFilter{ Source: sf, - Conditions: []conditions.Condition{*cond}, + Conditions: conditions.Conditions{*cond}, Valid: false, } diff --git a/internal/controller/state/graph/snippets_filter_test.go b/internal/controller/state/graph/snippets_filter_test.go index dcfdd62159..0abe174e30 100644 --- a/internal/controller/state/graph/snippets_filter_test.go +++ b/internal/controller/state/graph/snippets_filter_test.go @@ -100,7 +100,7 @@ func TestProcessSnippetsFilters(t *testing.T) { }, invalidFilterNsName: { Source: invalidFilter, - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ conditions.NewSnippetsFilterInvalid( "spec.snippets[1].context: Unsupported value: \"invalid context\": " + "supported values: \"main\", \"http\", \"http.server\", \"http.server.location\"", diff --git a/internal/controller/state/graph/tlsroute.go b/internal/controller/state/graph/tlsroute.go index db0b87e11e..9ebe4fb8b3 100644 --- a/internal/controller/state/graph/tlsroute.go +++ b/internal/controller/state/graph/tlsroute.go @@ -69,7 +69,7 @@ func validateBackendRefTLSRoute( services map[types.NamespacedName]*apiv1.Service, parentRefs []ParentRef, refGrantResolver func(resource toResource) bool, -) (BackendRef, []conditions.Condition) { +) (BackendRef, conditions.Conditions) { // Length of BackendRefs and Rules is guaranteed to be one due to earlier check in buildTLSRoute refPath := field.NewPath("spec").Child("rules").Index(0).Child("backendRefs").Index(0) @@ -86,7 +86,7 @@ func validateBackendRefTLSRoute( InvalidForGateways: make(map[types.NamespacedName]conditions.Condition), } - return backendRef, []conditions.Condition{cond} + return backendRef, conditions.Conditions{cond} } ns := gtr.Namespace @@ -116,7 +116,7 @@ func validateBackendRefTLSRoute( if err != nil { backendRef.Valid = false - return backendRef, []conditions.Condition{conditions.NewRouteBackendRefRefBackendNotFound(err.Error())} + return backendRef, conditions.Conditions{conditions.NewRouteBackendRefRefBackendNotFound(err.Error())} } if svcPort.AppProtocol != nil { @@ -124,11 +124,11 @@ func validateBackendRefTLSRoute( if err != nil { backendRef.Valid = false - return backendRef, []conditions.Condition{conditions.NewRouteBackendRefUnsupportedProtocol(err.Error())} + return backendRef, conditions.Conditions{conditions.NewRouteBackendRefUnsupportedProtocol(err.Error())} } } - var conds []conditions.Condition + var conds conditions.Conditions for _, parentRef := range parentRefs { if err := verifyIPFamily(parentRef.Gateway.EffectiveNginxProxy, svcIPFamily); err != nil { backendRef.Valid = backendRef.Valid || false diff --git a/internal/controller/state/graph/tlsroute_test.go b/internal/controller/state/graph/tlsroute_test.go index 29fac01b1e..f0b4c117f3 100644 --- a/internal/controller/state/graph/tlsroute_test.go +++ b/internal/controller/state/graph/tlsroute_test.go @@ -330,7 +330,7 @@ func TestBuildTLSRoute(t *testing.T) { expected: &L4Route{ Source: invalidHostnameGtr, ParentRefs: []ParentRef{parentRefGraph}, - Conditions: []conditions.Condition{conditions.NewRouteUnsupportedValue( + Conditions: conditions.Conditions{conditions.NewRouteUnsupportedValue( "spec.hostnames[0]: Invalid value: \"hi....com\": a lowercase RFC 1" + "123 subdomain must consist of lower case alphanumeric characters" + ", '-' or '.', and must start and end with an alphanumeric charac" + @@ -354,7 +354,7 @@ func TestBuildTLSRoute(t *testing.T) { "app.example.com", }, }, - Conditions: []conditions.Condition{conditions.NewRouteBackendRefUnsupportedValue( + Conditions: conditions.Conditions{conditions.NewRouteBackendRefUnsupportedValue( "Must have exactly one Rule and BackendRef", )}, Valid: false, @@ -382,7 +382,7 @@ func TestBuildTLSRoute(t *testing.T) { }, Attachable: true, Valid: true, - Conditions: []conditions.Condition{conditions.NewRouteBackendRefUnsupportedProtocol( + Conditions: conditions.Conditions{conditions.NewRouteBackendRefUnsupportedProtocol( "route type tls does not support service port appProtocol kubernetes.io/h2c", )}, }, @@ -411,7 +411,7 @@ func TestBuildTLSRoute(t *testing.T) { }, Attachable: true, Valid: true, - Conditions: []conditions.Condition{conditions.NewRouteBackendRefUnsupportedProtocol( + Conditions: conditions.Conditions{conditions.NewRouteBackendRefUnsupportedProtocol( "route type tls does not support service port appProtocol kubernetes.io/ws", )}, }, @@ -440,7 +440,7 @@ func TestBuildTLSRoute(t *testing.T) { InvalidForGateways: map[types.NamespacedName]conditions.Condition{}, }, }, - Conditions: []conditions.Condition{conditions.NewRouteBackendRefRefBackendNotFound( + Conditions: conditions.Conditions{conditions.NewRouteBackendRefRefBackendNotFound( "spec.rules[0].backendRefs[0].name: Not found: \"hi\"", )}, Attachable: true, @@ -465,7 +465,7 @@ func TestBuildTLSRoute(t *testing.T) { InvalidForGateways: map[types.NamespacedName]conditions.Condition{}, }, }, - Conditions: []conditions.Condition{conditions.NewRouteBackendRefInvalidKind( + Conditions: conditions.Conditions{conditions.NewRouteBackendRefInvalidKind( "spec.rules[0].backendRefs[0].group:" + " Unsupported value: \"wrong\": supported values: \"core\", \"\"", )}, @@ -493,7 +493,7 @@ func TestBuildTLSRoute(t *testing.T) { InvalidForGateways: map[types.NamespacedName]conditions.Condition{}, }, }, - Conditions: []conditions.Condition{conditions.NewRouteBackendRefInvalidKind( + Conditions: conditions.Conditions{conditions.NewRouteBackendRefInvalidKind( "spec.rules[0].backendRefs[0].kind:" + " Unsupported value: \"not service\": supported values: \"Service\"", )}, @@ -521,7 +521,7 @@ func TestBuildTLSRoute(t *testing.T) { InvalidForGateways: map[types.NamespacedName]conditions.Condition{}, }, }, - Conditions: []conditions.Condition{conditions.NewRouteBackendRefRefNotPermitted( + Conditions: conditions.Conditions{conditions.NewRouteBackendRefRefNotPermitted( "spec.rules[0].backendRefs[0].namespace: Forbidden: Backend ref to Service " + "diff/hi not permitted by any ReferenceGrant", )}, @@ -549,7 +549,7 @@ func TestBuildTLSRoute(t *testing.T) { InvalidForGateways: map[types.NamespacedName]conditions.Condition{}, }, }, - Conditions: []conditions.Condition{conditions.NewRouteBackendRefUnsupportedValue( + Conditions: conditions.Conditions{conditions.NewRouteBackendRefUnsupportedValue( "spec.rules[0].backendRefs[0].port: Required value: port cannot be nil", )}, Attachable: true, diff --git a/internal/controller/state/graph/validation.go b/internal/controller/state/graph/validation.go index 27d23bf135..fab5d88c31 100644 --- a/internal/controller/state/graph/validation.go +++ b/internal/controller/state/graph/validation.go @@ -5,6 +5,7 @@ import ( "strings" "k8s.io/apimachinery/pkg/util/validation" + v1 "sigs.k8s.io/gateway-api/apis/v1" ) func validateHostname(hostname string) error { @@ -29,3 +30,12 @@ func validateHostname(hostname string) error { return nil } + +// RuleWithUnsupportedFields defines an interface for rules with unsupported fields. +type RuleWithUnsupportedFields interface { + GetName() *v1.SectionName + GetTimeouts() *v1.HTTPRouteTimeouts + GetRetry() *v1.HTTPRouteRetry + GetSessionPersistence() *v1.SessionPersistence + GetSupportedFields() []string +} diff --git a/internal/controller/state/validation/validationfakes/fake_policy_validator.go b/internal/controller/state/validation/validationfakes/fake_policy_validator.go index 0cb8b7f232..88b014e147 100644 --- a/internal/controller/state/validation/validationfakes/fake_policy_validator.go +++ b/internal/controller/state/validation/validationfakes/fake_policy_validator.go @@ -22,28 +22,28 @@ type FakePolicyValidator struct { conflictsReturnsOnCall map[int]struct { result1 bool } - ValidateStub func(policies.Policy) []conditions.Condition + ValidateStub func(policies.Policy) conditions.Conditions validateMutex sync.RWMutex validateArgsForCall []struct { arg1 policies.Policy } validateReturns struct { - result1 []conditions.Condition + result1 conditions.Conditions } validateReturnsOnCall map[int]struct { - result1 []conditions.Condition + result1 conditions.Conditions } - ValidateGlobalSettingsStub func(policies.Policy, *policies.GlobalSettings) []conditions.Condition + ValidateGlobalSettingsStub func(policies.Policy, *policies.GlobalSettings) conditions.Conditions validateGlobalSettingsMutex sync.RWMutex validateGlobalSettingsArgsForCall []struct { arg1 policies.Policy arg2 *policies.GlobalSettings } validateGlobalSettingsReturns struct { - result1 []conditions.Condition + result1 conditions.Conditions } validateGlobalSettingsReturnsOnCall map[int]struct { - result1 []conditions.Condition + result1 conditions.Conditions } invocations map[string][][]interface{} invocationsMutex sync.RWMutex @@ -111,7 +111,7 @@ func (fake *FakePolicyValidator) ConflictsReturnsOnCall(i int, result1 bool) { }{result1} } -func (fake *FakePolicyValidator) Validate(arg1 policies.Policy) []conditions.Condition { +func (fake *FakePolicyValidator) Validate(arg1 policies.Policy) conditions.Conditions { fake.validateMutex.Lock() ret, specificReturn := fake.validateReturnsOnCall[len(fake.validateArgsForCall)] fake.validateArgsForCall = append(fake.validateArgsForCall, struct { @@ -136,7 +136,7 @@ func (fake *FakePolicyValidator) ValidateCallCount() int { return len(fake.validateArgsForCall) } -func (fake *FakePolicyValidator) ValidateCalls(stub func(policies.Policy) []conditions.Condition) { +func (fake *FakePolicyValidator) ValidateCalls(stub func(policies.Policy) conditions.Conditions) { fake.validateMutex.Lock() defer fake.validateMutex.Unlock() fake.ValidateStub = stub @@ -149,30 +149,30 @@ func (fake *FakePolicyValidator) ValidateArgsForCall(i int) policies.Policy { return argsForCall.arg1 } -func (fake *FakePolicyValidator) ValidateReturns(result1 []conditions.Condition) { +func (fake *FakePolicyValidator) ValidateReturns(result1 conditions.Conditions) { fake.validateMutex.Lock() defer fake.validateMutex.Unlock() fake.ValidateStub = nil fake.validateReturns = struct { - result1 []conditions.Condition + result1 conditions.Conditions }{result1} } -func (fake *FakePolicyValidator) ValidateReturnsOnCall(i int, result1 []conditions.Condition) { +func (fake *FakePolicyValidator) ValidateReturnsOnCall(i int, result1 conditions.Conditions) { fake.validateMutex.Lock() defer fake.validateMutex.Unlock() fake.ValidateStub = nil if fake.validateReturnsOnCall == nil { fake.validateReturnsOnCall = make(map[int]struct { - result1 []conditions.Condition + result1 conditions.Conditions }) } fake.validateReturnsOnCall[i] = struct { - result1 []conditions.Condition + result1 conditions.Conditions }{result1} } -func (fake *FakePolicyValidator) ValidateGlobalSettings(arg1 policies.Policy, arg2 *policies.GlobalSettings) []conditions.Condition { +func (fake *FakePolicyValidator) ValidateGlobalSettings(arg1 policies.Policy, arg2 *policies.GlobalSettings) conditions.Conditions { fake.validateGlobalSettingsMutex.Lock() ret, specificReturn := fake.validateGlobalSettingsReturnsOnCall[len(fake.validateGlobalSettingsArgsForCall)] fake.validateGlobalSettingsArgsForCall = append(fake.validateGlobalSettingsArgsForCall, struct { @@ -198,7 +198,7 @@ func (fake *FakePolicyValidator) ValidateGlobalSettingsCallCount() int { return len(fake.validateGlobalSettingsArgsForCall) } -func (fake *FakePolicyValidator) ValidateGlobalSettingsCalls(stub func(policies.Policy, *policies.GlobalSettings) []conditions.Condition) { +func (fake *FakePolicyValidator) ValidateGlobalSettingsCalls(stub func(policies.Policy, *policies.GlobalSettings) conditions.Conditions) { fake.validateGlobalSettingsMutex.Lock() defer fake.validateGlobalSettingsMutex.Unlock() fake.ValidateGlobalSettingsStub = stub @@ -211,26 +211,26 @@ func (fake *FakePolicyValidator) ValidateGlobalSettingsArgsForCall(i int) (polic return argsForCall.arg1, argsForCall.arg2 } -func (fake *FakePolicyValidator) ValidateGlobalSettingsReturns(result1 []conditions.Condition) { +func (fake *FakePolicyValidator) ValidateGlobalSettingsReturns(result1 conditions.Conditions) { fake.validateGlobalSettingsMutex.Lock() defer fake.validateGlobalSettingsMutex.Unlock() fake.ValidateGlobalSettingsStub = nil fake.validateGlobalSettingsReturns = struct { - result1 []conditions.Condition + result1 conditions.Conditions }{result1} } -func (fake *FakePolicyValidator) ValidateGlobalSettingsReturnsOnCall(i int, result1 []conditions.Condition) { +func (fake *FakePolicyValidator) ValidateGlobalSettingsReturnsOnCall(i int, result1 conditions.Conditions) { fake.validateGlobalSettingsMutex.Lock() defer fake.validateGlobalSettingsMutex.Unlock() fake.ValidateGlobalSettingsStub = nil if fake.validateGlobalSettingsReturnsOnCall == nil { fake.validateGlobalSettingsReturnsOnCall = make(map[int]struct { - result1 []conditions.Condition + result1 conditions.Conditions }) } fake.validateGlobalSettingsReturnsOnCall[i] = struct { - result1 []conditions.Condition + result1 conditions.Conditions }{result1} } diff --git a/internal/controller/state/validation/validator.go b/internal/controller/state/validation/validator.go index d01a907e7f..d03d42c3ca 100644 --- a/internal/controller/state/validation/validator.go +++ b/internal/controller/state/validation/validator.go @@ -55,9 +55,9 @@ type GenericValidator interface { //counterfeiter:generate . PolicyValidator type PolicyValidator interface { // Validate validates an NGF Policy. - Validate(policy policies.Policy) []conditions.Condition + Validate(policy policies.Policy) conditions.Conditions // ValidateGlobalSettings validates an NGF Policy with the NginxProxy settings. - ValidateGlobalSettings(policy policies.Policy, globalSettings *policies.GlobalSettings) []conditions.Condition + ValidateGlobalSettings(policy policies.Policy, globalSettings *policies.GlobalSettings) conditions.Conditions // Conflicts returns true if the two Policies conflict. Conflicts(a, b policies.Policy) bool } diff --git a/internal/controller/status/prepare_requests.go b/internal/controller/status/prepare_requests.go index 87e3b441cc..91b71912e2 100644 --- a/internal/controller/status/prepare_requests.go +++ b/internal/controller/status/prepare_requests.go @@ -136,7 +136,7 @@ func removeDuplicateIndexParentRefs(parentRefs []graph.ParentRef) []graph.Parent func prepareRouteStatus( gatewayCtlrName string, parentRefs []graph.ParentRef, - conds []conditions.Condition, + conds conditions.Conditions, transitionTime metav1.Time, srcGeneration int64, ) v1.RouteStatus { @@ -155,7 +155,7 @@ func prepareRouteStatus( if ref.Attachment != nil { failedAttachmentCondCount = len(ref.Attachment.FailedConditions) } - allConds := make([]conditions.Condition, 0, len(conds)+len(defaultConds)+failedAttachmentCondCount) + allConds := make(conditions.Conditions, 0, len(conds)+len(defaultConds)+failedAttachmentCondCount) // We add defaultConds first, so that any additional conditions will override them, which is // ensured by DeduplicateConditions. @@ -195,7 +195,7 @@ func PrepareGatewayClassRequests( if gc != nil { defaultConds := conditions.NewDefaultGatewayClassConditions() - conds := make([]conditions.Condition, 0, len(gc.Conditions)+len(defaultConds)) + conds := make(conditions.Conditions, 0, len(gc.Conditions)+len(defaultConds)) // We add default conds first, so that any additional conditions will override them, which is // ensured by DeduplicateConditions. @@ -223,7 +223,7 @@ func PrepareGatewayClassRequests( ResourceType: &v1.GatewayClass{}, Setter: newGatewayClassStatusSetter(v1.GatewayClassStatus{ Conditions: conditions.ConvertConditions( - []conditions.Condition{conditions.NewGatewayClassConflict()}, + conditions.Conditions{conditions.NewGatewayClassConflict()}, gwClass.Generation, transitionTime, ), @@ -370,7 +370,7 @@ func PrepareNGFPolicyRequests( } for _, ancestor := range pol.Ancestors { - allConds := make([]conditions.Condition, 0, len(pol.Conditions)+len(ancestor.Conditions)+1) + allConds := make(conditions.Conditions, 0, len(pol.Conditions)+len(ancestor.Conditions)+1) // The order of conditions matters here. // We add the default condition first, followed by the ancestor conditions, and finally the policy conditions. @@ -455,7 +455,7 @@ func PrepareSnippetsFilterRequests( reqs := make([]UpdateRequest, 0, len(snippetsFilters)) for nsname, snippetsFilter := range snippetsFilters { - allConds := make([]conditions.Condition, 0, len(snippetsFilter.Conditions)+1) + allConds := make(conditions.Conditions, 0, len(snippetsFilter.Conditions)+1) // The order of conditions matters here. // We add the default condition first, followed by the snippetsFilter conditions. @@ -501,14 +501,14 @@ func PrepareNginxGatewayStatus( return nil } - var conds []conditions.Condition + var conds conditions.Conditions if cpUpdateRes.Error != nil { msg := "Failed to update control plane configuration" - conds = []conditions.Condition{ + conds = conditions.Conditions{ conditions.NewNginxGatewayInvalid(fmt.Sprintf("%s: %v", msg, cpUpdateRes.Error)), } } else { - conds = []conditions.Condition{conditions.NewNginxGatewayValid()} + conds = conditions.Conditions{conditions.NewNginxGatewayValid()} } return &UpdateRequest{ diff --git a/internal/controller/status/prepare_requests_test.go b/internal/controller/status/prepare_requests_test.go index 3cb629d3c2..7ebb39c560 100644 --- a/internal/controller/status/prepare_requests_test.go +++ b/internal/controller/status/prepare_requests_test.go @@ -113,7 +113,7 @@ var ( SectionName: commonRouteSpecValid.ParentRefs[1].SectionName, Attachment: &graph.ParentRefAttachmentStatus{ Attached: false, - FailedConditions: []conditions.Condition{invalidAttachmentCondition}, + FailedConditions: conditions.Conditions{invalidAttachmentCondition}, }, }, { @@ -122,7 +122,7 @@ var ( SectionName: commonRouteSpecValid.ParentRefs[2].SectionName, Attachment: &graph.ParentRefAttachmentStatus{ Attached: true, - FailedConditions: []conditions.Condition{invalidAttachmentCondition}, + FailedConditions: conditions.Conditions{invalidAttachmentCondition}, }, }, { @@ -131,7 +131,7 @@ var ( SectionName: commonRouteSpecValid.ParentRefs[3].SectionName, Attachment: &graph.ParentRefAttachmentStatus{ Attached: false, - FailedConditions: []conditions.Condition{invalidAttachmentCondition}, + FailedConditions: conditions.Conditions{invalidAttachmentCondition}, }, }, { @@ -148,7 +148,7 @@ var ( SectionName: commonRouteSpecValid.ParentRefs[5].SectionName, Attachment: &graph.ParentRefAttachmentStatus{ Attached: true, - FailedConditions: []conditions.Condition{invalidAttachmentCondition}, + FailedConditions: conditions.Conditions{invalidAttachmentCondition}, }, }, { @@ -157,7 +157,7 @@ var ( SectionName: commonRouteSpecValid.ParentRefs[6].SectionName, Attachment: &graph.ParentRefAttachmentStatus{ Attached: false, - FailedConditions: []conditions.Condition{invalidAttachmentCondition}, + FailedConditions: conditions.Conditions{invalidAttachmentCondition}, }, }, { @@ -166,7 +166,7 @@ var ( SectionName: commonRouteSpecValid.ParentRefs[7].SectionName, Attachment: &graph.ParentRefAttachmentStatus{ Attached: false, - FailedConditions: []conditions.Condition{invalidAttachmentCondition}, + FailedConditions: conditions.Conditions{invalidAttachmentCondition}, }, }, } @@ -401,7 +401,7 @@ func TestBuildHTTPRouteStatuses(t *testing.T) { }, graph.CreateRouteKey(hrInvalid): { Valid: false, - Conditions: []conditions.Condition{invalidRouteCondition}, + Conditions: conditions.Conditions{invalidRouteCondition}, Source: hrInvalid, ParentRefs: parentRefsInvalid, RouteType: graph.RouteTypeHTTP, @@ -479,7 +479,7 @@ func TestBuildGRPCRouteStatuses(t *testing.T) { }, graph.CreateRouteKey(grInvalid): { Valid: false, - Conditions: []conditions.Condition{invalidRouteCondition}, + Conditions: conditions.Conditions{invalidRouteCondition}, Source: grInvalid, ParentRefs: parentRefsInvalid, RouteType: graph.RouteTypeGRPC, @@ -556,7 +556,7 @@ func TestBuildTLSRouteStatuses(t *testing.T) { }, graph.CreateRouteKeyL4(trInvalid): { Valid: false, - Conditions: []conditions.Condition{invalidRouteCondition}, + Conditions: conditions.Conditions{invalidRouteCondition}, Source: trInvalid, ParentRefs: parentRefsInvalid, }, @@ -1142,7 +1142,7 @@ func TestBuildGatewayStatuses(t *testing.T) { }, }, Valid: true, - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ conditions.NewGatewayResolvedRefs(), }, }, @@ -1197,7 +1197,7 @@ func TestBuildGatewayStatuses(t *testing.T) { }, }, Valid: true, - Conditions: []conditions.Condition{ + Conditions: conditions.Conditions{ conditions.NewGatewayRefNotFound(), conditions.NewGatewayInvalidParameters("ParametersRef not found"), }, @@ -1390,7 +1390,7 @@ func TestBuildBackendTLSPolicyStatuses(t *testing.T) { type policyCfg struct { Name string - Conditions []conditions.Condition + Conditions conditions.Conditions Gateways []types.NamespacedName Valid bool Ignored bool @@ -1414,8 +1414,8 @@ func TestBuildBackendTLSPolicyStatuses(t *testing.T) { } } - attachedConds := []conditions.Condition{conditions.NewPolicyAccepted()} - invalidConds := []conditions.Condition{conditions.NewPolicyInvalid("invalid backendTLSPolicy")} + attachedConds := conditions.Conditions{conditions.NewPolicyAccepted()} + invalidConds := conditions.Conditions{conditions.NewPolicyInvalid("invalid backendTLSPolicy")} validPolicyCfg := policyCfg{ Name: "valid-bt", @@ -1744,7 +1744,7 @@ func TestBuildNGFPolicyStatuses(t *testing.T) { type policyCfg struct { Ancestors []graph.PolicyAncestor Name string - Conditions []conditions.Condition + Conditions conditions.Conditions } // We have to use a real policy here because the test makes the status update using the k8sClient. @@ -1763,8 +1763,8 @@ func TestBuildNGFPolicyStatuses(t *testing.T) { } } - invalidConds := []conditions.Condition{conditions.NewPolicyInvalid("invalid")} - targetRefNotFoundConds := []conditions.Condition{conditions.NewPolicyTargetNotFound("target not found")} + invalidConds := conditions.Conditions{conditions.NewPolicyInvalid("invalid")} + targetRefNotFoundConds := conditions.Conditions{conditions.NewPolicyTargetNotFound("target not found")} validPolicyKey := graph.PolicyKey{ NsName: types.NamespacedName{Namespace: "test", Name: "valid-pol"}, @@ -2059,7 +2059,7 @@ func TestBuildSnippetsFilterStatuses(t *testing.T) { Generation: 1, }, }, - Conditions: []conditions.Condition{conditions.NewSnippetsFilterInvalid("invalid snippetsFilter")}, + Conditions: conditions.Conditions{conditions.NewSnippetsFilterInvalid("invalid snippetsFilter")}, Valid: false, } From 46b9faa6a8597215894c3fc8c21bb57679b3f68a Mon Sep 17 00:00:00 2001 From: Tina Usova Date: Wed, 8 Oct 2025 10:25:32 +0100 Subject: [PATCH 2/7] remove []Condition->Conditions change and add Route processing tests for unsupported fields --- .../policies/clientsettings/validator.go | 8 +- .../policies/clientsettings/validator_test.go | 12 +- .../policies/observability/validator.go | 12 +- .../policies/observability/validator_test.go | 22 +-- .../policies/policiesfakes/fake_validator.go | 40 ++--- .../policies/upstreamsettings/validator.go | 8 +- .../upstreamsettings/validator_test.go | 10 +- .../nginx/config/policies/validator.go | 8 +- .../nginx/config/policies/validator_test.go | 16 +- .../controller/state/change_processor_test.go | 46 +++--- .../controller/state/conditions/conditions.go | 89 +++++----- .../state/conditions/conditions_test.go | 12 +- .../controller/state/graph/backend_refs.go | 8 +- .../state/graph/backend_refs_test.go | 32 ++-- .../state/graph/backend_tls_policy.go | 20 +-- .../state/graph/backend_tls_policy_test.go | 4 +- internal/controller/state/graph/gateway.go | 22 +-- .../state/graph/gateway_listener.go | 24 +-- .../state/graph/gateway_listener_test.go | 6 +- .../controller/state/graph/gateway_test.go | 34 ++-- .../controller/state/graph/gatewayclass.go | 12 +- .../state/graph/gatewayclass_test.go | 12 +- internal/controller/state/graph/graph_test.go | 22 +-- internal/controller/state/graph/grpcroute.go | 11 +- .../controller/state/graph/grpcroute_test.go | 152 +++++++++++++++--- internal/controller/state/graph/httproute.go | 21 +-- .../controller/state/graph/httproute_test.go | 82 +++++++--- .../state/graph/multiple_gateways_test.go | 18 +-- internal/controller/state/graph/policies.go | 20 +-- .../controller/state/graph/policies_test.go | 60 +++---- .../controller/state/graph/route_common.go | 31 ++-- .../state/graph/route_common_test.go | 44 ++--- .../controller/state/graph/snippets_filter.go | 4 +- .../state/graph/snippets_filter_test.go | 2 +- internal/controller/state/graph/tlsroute.go | 10 +- .../controller/state/graph/tlsroute_test.go | 18 +-- internal/controller/state/graph/validation.go | 10 -- .../validationfakes/fake_policy_validator.go | 40 ++--- .../controller/state/validation/validator.go | 4 +- .../controller/status/prepare_requests.go | 18 +-- .../status/prepare_requests_test.go | 36 ++--- 41 files changed, 595 insertions(+), 465 deletions(-) diff --git a/internal/controller/nginx/config/policies/clientsettings/validator.go b/internal/controller/nginx/config/policies/clientsettings/validator.go index bbbb321546..c4d83864e7 100644 --- a/internal/controller/nginx/config/policies/clientsettings/validator.go +++ b/internal/controller/nginx/config/policies/clientsettings/validator.go @@ -24,7 +24,7 @@ func NewValidator(genericValidator validation.GenericValidator) *Validator { } // Validate validates the spec of a ClientSettingsPolicy. -func (v *Validator) Validate(policy policies.Policy) conditions.Conditions { +func (v *Validator) Validate(policy policies.Policy) []conditions.Condition { csp := helpers.MustCastObject[*ngfAPI.ClientSettingsPolicy](policy) targetRefPath := field.NewPath("spec").Child("targetRef") @@ -32,11 +32,11 @@ func (v *Validator) Validate(policy policies.Policy) conditions.Conditions { supportedGroups := []gatewayv1.Group{gatewayv1.GroupName} if err := policies.ValidateTargetRef(csp.Spec.TargetRef, targetRefPath, supportedGroups, supportedKinds); err != nil { - return conditions.Conditions{conditions.NewPolicyInvalid(err.Error())} + return []conditions.Condition{conditions.NewPolicyInvalid(err.Error())} } if err := v.validateSettings(csp.Spec); err != nil { - return conditions.Conditions{conditions.NewPolicyInvalid(err.Error())} + return []conditions.Condition{conditions.NewPolicyInvalid(err.Error())} } return nil @@ -46,7 +46,7 @@ func (v *Validator) Validate(policy policies.Policy) conditions.Conditions { func (v *Validator) ValidateGlobalSettings( _ policies.Policy, _ *policies.GlobalSettings, -) conditions.Conditions { +) []conditions.Condition { return nil } diff --git a/internal/controller/nginx/config/policies/clientsettings/validator_test.go b/internal/controller/nginx/config/policies/clientsettings/validator_test.go index 315c729593..948e79901b 100644 --- a/internal/controller/nginx/config/policies/clientsettings/validator_test.go +++ b/internal/controller/nginx/config/policies/clientsettings/validator_test.go @@ -56,7 +56,7 @@ func TestValidator_Validate(t *testing.T) { tests := []struct { name string policy *ngfAPI.ClientSettingsPolicy - expConditions conditions.Conditions + expConditions []conditions.Condition }{ { name: "invalid target ref; unsupported group", @@ -64,7 +64,7 @@ func TestValidator_Validate(t *testing.T) { p.Spec.TargetRef.Group = "Unsupported" return p }), - expConditions: conditions.Conditions{ + expConditions: []conditions.Condition{ conditions.NewPolicyInvalid("spec.targetRef.group: Unsupported value: \"Unsupported\": " + "supported values: \"gateway.networking.k8s.io\""), }, @@ -75,7 +75,7 @@ func TestValidator_Validate(t *testing.T) { p.Spec.TargetRef.Kind = "Unsupported" return p }), - expConditions: conditions.Conditions{ + expConditions: []conditions.Condition{ conditions.NewPolicyInvalid("spec.targetRef.kind: Unsupported value: \"Unsupported\": " + "supported values: \"Gateway\", \"HTTPRoute\", \"GRPCRoute\""), }, @@ -86,7 +86,7 @@ func TestValidator_Validate(t *testing.T) { p.Spec.Body.MaxSize = helpers.GetPointer[ngfAPI.Size]("invalid") return p }), - expConditions: conditions.Conditions{ + expConditions: []conditions.Condition{ conditions.NewPolicyInvalid("spec.body.maxSize: Invalid value: \"invalid\": ^\\d{1,4}(k|m|g)?$ " + "(e.g. '1024', or '8k', or '20m', or '1g', regex used for validation is 'must contain a number. " + "May be followed by 'k', 'm', or 'g', otherwise bytes are assumed')"), @@ -101,7 +101,7 @@ func TestValidator_Validate(t *testing.T) { p.Spec.KeepAlive.Timeout.Header = helpers.GetPointer[ngfAPI.Duration]("invalid") return p }), - expConditions: conditions.Conditions{ + expConditions: []conditions.Condition{ conditions.NewPolicyInvalid( "[spec.body.timeout: Invalid value: \"invalid\": ^[0-9]{1,4}(ms|s|m|h)? " + "(e.g. '5ms', or '10s', or '500m', or '1000h', regex used for validation is " + @@ -123,7 +123,7 @@ func TestValidator_Validate(t *testing.T) { p.Spec.KeepAlive.Timeout.Server = nil return p }), - expConditions: conditions.Conditions{ + expConditions: []conditions.Condition{ conditions.NewPolicyInvalid("spec.keepAlive.timeout: Invalid value: null: " + "server timeout must be set if header timeout is set"), }, diff --git a/internal/controller/nginx/config/policies/observability/validator.go b/internal/controller/nginx/config/policies/observability/validator.go index 85ded08802..43d47dce3e 100644 --- a/internal/controller/nginx/config/policies/observability/validator.go +++ b/internal/controller/nginx/config/policies/observability/validator.go @@ -24,7 +24,7 @@ func NewValidator(genericValidator validation.GenericValidator) *Validator { } // Validate validates the spec of an ObservabilityPolicy. -func (v *Validator) Validate(policy policies.Policy) conditions.Conditions { +func (v *Validator) Validate(policy policies.Policy) []conditions.Condition { obs := helpers.MustCastObject[*ngfAPIv1alpha2.ObservabilityPolicy](policy) targetRefPath := field.NewPath("spec").Child("targetRefs") @@ -33,12 +33,12 @@ func (v *Validator) Validate(policy policies.Policy) conditions.Conditions { for _, ref := range obs.Spec.TargetRefs { if err := policies.ValidateTargetRef(ref, targetRefPath, supportedGroups, supportedKinds); err != nil { - return conditions.Conditions{conditions.NewPolicyInvalid(err.Error())} + return []conditions.Condition{conditions.NewPolicyInvalid(err.Error())} } } if err := v.validateSettings(obs.Spec); err != nil { - return conditions.Conditions{conditions.NewPolicyInvalid(err.Error())} + return []conditions.Condition{conditions.NewPolicyInvalid(err.Error())} } return nil @@ -48,15 +48,15 @@ func (v *Validator) Validate(policy policies.Policy) conditions.Conditions { func (v *Validator) ValidateGlobalSettings( _ policies.Policy, globalSettings *policies.GlobalSettings, -) conditions.Conditions { +) []conditions.Condition { if globalSettings == nil { - return conditions.Conditions{ + return []conditions.Condition{ conditions.NewPolicyNotAcceptedNginxProxyNotSet(conditions.PolicyMessageNginxProxyInvalid), } } if !globalSettings.TelemetryEnabled { - return conditions.Conditions{ + return []conditions.Condition{ conditions.NewPolicyNotAcceptedNginxProxyNotSet(conditions.PolicyMessageTelemetryNotEnabled), } } diff --git a/internal/controller/nginx/config/policies/observability/validator_test.go b/internal/controller/nginx/config/policies/observability/validator_test.go index 9681e8be52..7cc283df3c 100644 --- a/internal/controller/nginx/config/policies/observability/validator_test.go +++ b/internal/controller/nginx/config/policies/observability/validator_test.go @@ -57,7 +57,7 @@ func TestValidator_Validate(t *testing.T) { tests := []struct { name string policy *ngfAPIv1alpha2.ObservabilityPolicy - expConditions conditions.Conditions + expConditions []conditions.Condition }{ { name: "invalid target ref; unsupported group", @@ -65,7 +65,7 @@ func TestValidator_Validate(t *testing.T) { p.Spec.TargetRefs[0].Group = "Unsupported" return p }), - expConditions: conditions.Conditions{ + expConditions: []conditions.Condition{ conditions.NewPolicyInvalid("spec.targetRefs.group: Unsupported value: \"Unsupported\": " + "supported values: \"gateway.networking.k8s.io\""), }, @@ -76,7 +76,7 @@ func TestValidator_Validate(t *testing.T) { p.Spec.TargetRefs[0].Kind = "Unsupported" return p }), - expConditions: conditions.Conditions{ + expConditions: []conditions.Condition{ conditions.NewPolicyInvalid("spec.targetRefs.kind: Unsupported value: \"Unsupported\": " + "supported values: \"HTTPRoute\", \"GRPCRoute\""), }, @@ -87,7 +87,7 @@ func TestValidator_Validate(t *testing.T) { p.Spec.Tracing.Strategy = "invalid" return p }), - expConditions: conditions.Conditions{ + expConditions: []conditions.Condition{ conditions.NewPolicyInvalid("spec.tracing.strategy: Unsupported value: \"invalid\": " + "supported values: \"ratio\", \"parent\""), }, @@ -98,7 +98,7 @@ func TestValidator_Validate(t *testing.T) { p.Spec.Tracing.Context = helpers.GetPointer[ngfAPIv1alpha2.TraceContext]("invalid") return p }), - expConditions: conditions.Conditions{ + expConditions: []conditions.Condition{ conditions.NewPolicyInvalid("spec.tracing.context: Unsupported value: \"invalid\": " + "supported values: \"extract\", \"inject\", \"propagate\", \"ignore\""), }, @@ -109,7 +109,7 @@ func TestValidator_Validate(t *testing.T) { p.Spec.Tracing.SpanName = helpers.GetPointer("invalid$$$") return p }), - expConditions: conditions.Conditions{ + expConditions: []conditions.Condition{ conditions.NewPolicyInvalid("spec.tracing.spanName: Invalid value: \"invalid$$$\": " + "a valid value must have all '\"' escaped and must not contain any '$' or end with an " + "unescaped '\\' (regex used for validation is '([^\"$\\\\]|\\\\[^$])*')"), @@ -121,7 +121,7 @@ func TestValidator_Validate(t *testing.T) { p.Spec.Tracing.SpanAttributes[0].Key = "invalid$$$" return p }), - expConditions: conditions.Conditions{ + expConditions: []conditions.Condition{ conditions.NewPolicyInvalid("spec.tracing.spanAttributes.key: Invalid value: \"invalid$$$\": " + "a valid value must have all '\"' escaped and must not contain any '$' or end with an " + "unescaped '\\' (regex used for validation is '([^\"$\\\\]|\\\\[^$])*')"), @@ -133,7 +133,7 @@ func TestValidator_Validate(t *testing.T) { p.Spec.Tracing.SpanAttributes[0].Value = "invalid$$$" return p }), - expConditions: conditions.Conditions{ + expConditions: []conditions.Condition{ conditions.NewPolicyInvalid("spec.tracing.spanAttributes.value: Invalid value: \"invalid$$$\": " + "a valid value must have all '\"' escaped and must not contain any '$' or end with an " + "unescaped '\\' (regex used for validation is '([^\"$\\\\]|\\\\[^$])*')"), @@ -178,18 +178,18 @@ func TestValidator_ValidateGlobalSettings(t *testing.T) { tests := []struct { name string globalSettings *policies.GlobalSettings - expConditions conditions.Conditions + expConditions []conditions.Condition }{ { name: "global settings are nil", - expConditions: conditions.Conditions{ + expConditions: []conditions.Condition{ conditions.NewPolicyNotAcceptedNginxProxyNotSet(conditions.PolicyMessageNginxProxyInvalid), }, }, { name: "telemetry is not enabled", globalSettings: &policies.GlobalSettings{TelemetryEnabled: false}, - expConditions: conditions.Conditions{ + expConditions: []conditions.Condition{ conditions.NewPolicyNotAcceptedNginxProxyNotSet(conditions.PolicyMessageTelemetryNotEnabled), }, }, diff --git a/internal/controller/nginx/config/policies/policiesfakes/fake_validator.go b/internal/controller/nginx/config/policies/policiesfakes/fake_validator.go index 53b716de5e..9389e6ccff 100644 --- a/internal/controller/nginx/config/policies/policiesfakes/fake_validator.go +++ b/internal/controller/nginx/config/policies/policiesfakes/fake_validator.go @@ -21,28 +21,28 @@ type FakeValidator struct { conflictsReturnsOnCall map[int]struct { result1 bool } - ValidateStub func(policies.Policy) conditions.Conditions + ValidateStub func(policies.Policy) []conditions.Condition validateMutex sync.RWMutex validateArgsForCall []struct { arg1 policies.Policy } validateReturns struct { - result1 conditions.Conditions + result1 []conditions.Condition } validateReturnsOnCall map[int]struct { - result1 conditions.Conditions + result1 []conditions.Condition } - ValidateGlobalSettingsStub func(policies.Policy, *policies.GlobalSettings) conditions.Conditions + ValidateGlobalSettingsStub func(policies.Policy, *policies.GlobalSettings) []conditions.Condition validateGlobalSettingsMutex sync.RWMutex validateGlobalSettingsArgsForCall []struct { arg1 policies.Policy arg2 *policies.GlobalSettings } validateGlobalSettingsReturns struct { - result1 conditions.Conditions + result1 []conditions.Condition } validateGlobalSettingsReturnsOnCall map[int]struct { - result1 conditions.Conditions + result1 []conditions.Condition } invocations map[string][][]interface{} invocationsMutex sync.RWMutex @@ -110,7 +110,7 @@ func (fake *FakeValidator) ConflictsReturnsOnCall(i int, result1 bool) { }{result1} } -func (fake *FakeValidator) Validate(arg1 policies.Policy) conditions.Conditions { +func (fake *FakeValidator) Validate(arg1 policies.Policy) []conditions.Condition { fake.validateMutex.Lock() ret, specificReturn := fake.validateReturnsOnCall[len(fake.validateArgsForCall)] fake.validateArgsForCall = append(fake.validateArgsForCall, struct { @@ -135,7 +135,7 @@ func (fake *FakeValidator) ValidateCallCount() int { return len(fake.validateArgsForCall) } -func (fake *FakeValidator) ValidateCalls(stub func(policies.Policy) conditions.Conditions) { +func (fake *FakeValidator) ValidateCalls(stub func(policies.Policy) []conditions.Condition) { fake.validateMutex.Lock() defer fake.validateMutex.Unlock() fake.ValidateStub = stub @@ -148,30 +148,30 @@ func (fake *FakeValidator) ValidateArgsForCall(i int) policies.Policy { return argsForCall.arg1 } -func (fake *FakeValidator) ValidateReturns(result1 conditions.Conditions) { +func (fake *FakeValidator) ValidateReturns(result1 []conditions.Condition) { fake.validateMutex.Lock() defer fake.validateMutex.Unlock() fake.ValidateStub = nil fake.validateReturns = struct { - result1 conditions.Conditions + result1 []conditions.Condition }{result1} } -func (fake *FakeValidator) ValidateReturnsOnCall(i int, result1 conditions.Conditions) { +func (fake *FakeValidator) ValidateReturnsOnCall(i int, result1 []conditions.Condition) { fake.validateMutex.Lock() defer fake.validateMutex.Unlock() fake.ValidateStub = nil if fake.validateReturnsOnCall == nil { fake.validateReturnsOnCall = make(map[int]struct { - result1 conditions.Conditions + result1 []conditions.Condition }) } fake.validateReturnsOnCall[i] = struct { - result1 conditions.Conditions + result1 []conditions.Condition }{result1} } -func (fake *FakeValidator) ValidateGlobalSettings(arg1 policies.Policy, arg2 *policies.GlobalSettings) conditions.Conditions { +func (fake *FakeValidator) ValidateGlobalSettings(arg1 policies.Policy, arg2 *policies.GlobalSettings) []conditions.Condition { fake.validateGlobalSettingsMutex.Lock() ret, specificReturn := fake.validateGlobalSettingsReturnsOnCall[len(fake.validateGlobalSettingsArgsForCall)] fake.validateGlobalSettingsArgsForCall = append(fake.validateGlobalSettingsArgsForCall, struct { @@ -197,7 +197,7 @@ func (fake *FakeValidator) ValidateGlobalSettingsCallCount() int { return len(fake.validateGlobalSettingsArgsForCall) } -func (fake *FakeValidator) ValidateGlobalSettingsCalls(stub func(policies.Policy, *policies.GlobalSettings) conditions.Conditions) { +func (fake *FakeValidator) ValidateGlobalSettingsCalls(stub func(policies.Policy, *policies.GlobalSettings) []conditions.Condition) { fake.validateGlobalSettingsMutex.Lock() defer fake.validateGlobalSettingsMutex.Unlock() fake.ValidateGlobalSettingsStub = stub @@ -210,26 +210,26 @@ func (fake *FakeValidator) ValidateGlobalSettingsArgsForCall(i int) (policies.Po return argsForCall.arg1, argsForCall.arg2 } -func (fake *FakeValidator) ValidateGlobalSettingsReturns(result1 conditions.Conditions) { +func (fake *FakeValidator) ValidateGlobalSettingsReturns(result1 []conditions.Condition) { fake.validateGlobalSettingsMutex.Lock() defer fake.validateGlobalSettingsMutex.Unlock() fake.ValidateGlobalSettingsStub = nil fake.validateGlobalSettingsReturns = struct { - result1 conditions.Conditions + result1 []conditions.Condition }{result1} } -func (fake *FakeValidator) ValidateGlobalSettingsReturnsOnCall(i int, result1 conditions.Conditions) { +func (fake *FakeValidator) ValidateGlobalSettingsReturnsOnCall(i int, result1 []conditions.Condition) { fake.validateGlobalSettingsMutex.Lock() defer fake.validateGlobalSettingsMutex.Unlock() fake.ValidateGlobalSettingsStub = nil if fake.validateGlobalSettingsReturnsOnCall == nil { fake.validateGlobalSettingsReturnsOnCall = make(map[int]struct { - result1 conditions.Conditions + result1 []conditions.Condition }) } fake.validateGlobalSettingsReturnsOnCall[i] = struct { - result1 conditions.Conditions + result1 []conditions.Condition }{result1} } diff --git a/internal/controller/nginx/config/policies/upstreamsettings/validator.go b/internal/controller/nginx/config/policies/upstreamsettings/validator.go index c578869df4..9b54fb48e2 100644 --- a/internal/controller/nginx/config/policies/upstreamsettings/validator.go +++ b/internal/controller/nginx/config/policies/upstreamsettings/validator.go @@ -24,7 +24,7 @@ func NewValidator(genericValidator validation.GenericValidator) Validator { } // Validate validates the spec of an UpstreamsSettingsPolicy. -func (v Validator) Validate(policy policies.Policy) conditions.Conditions { +func (v Validator) Validate(policy policies.Policy) []conditions.Condition { usp := helpers.MustCastObject[*ngfAPI.UpstreamSettingsPolicy](policy) targetRefsPath := field.NewPath("spec").Child("targetRefs") @@ -34,12 +34,12 @@ func (v Validator) Validate(policy policies.Policy) conditions.Conditions { for i, ref := range usp.Spec.TargetRefs { indexedPath := targetRefsPath.Index(i) if err := policies.ValidateTargetRef(ref, indexedPath, supportedGroups, supportedKinds); err != nil { - return conditions.Conditions{conditions.NewPolicyInvalid(err.Error())} + return []conditions.Condition{conditions.NewPolicyInvalid(err.Error())} } } if err := v.validateSettings(usp.Spec); err != nil { - return conditions.Conditions{conditions.NewPolicyInvalid(err.Error())} + return []conditions.Condition{conditions.NewPolicyInvalid(err.Error())} } return nil @@ -49,7 +49,7 @@ func (v Validator) Validate(policy policies.Policy) conditions.Conditions { func (v Validator) ValidateGlobalSettings( _ policies.Policy, _ *policies.GlobalSettings, -) conditions.Conditions { +) []conditions.Condition { return nil } diff --git a/internal/controller/nginx/config/policies/upstreamsettings/validator_test.go b/internal/controller/nginx/config/policies/upstreamsettings/validator_test.go index 3f58be66e3..6ad3d72ca7 100644 --- a/internal/controller/nginx/config/policies/upstreamsettings/validator_test.go +++ b/internal/controller/nginx/config/policies/upstreamsettings/validator_test.go @@ -52,7 +52,7 @@ func TestValidator_Validate(t *testing.T) { tests := []struct { name string policy *ngfAPI.UpstreamSettingsPolicy - expConditions conditions.Conditions + expConditions []conditions.Condition }{ { name: "invalid target ref; unsupported group", @@ -66,7 +66,7 @@ func TestValidator_Validate(t *testing.T) { }) return p }), - expConditions: conditions.Conditions{ + expConditions: []conditions.Condition{ conditions.NewPolicyInvalid("spec.targetRefs[1].group: Unsupported value: \"Unsupported\": " + "supported values: \"\", \"core\""), }, @@ -83,7 +83,7 @@ func TestValidator_Validate(t *testing.T) { }) return p }), - expConditions: conditions.Conditions{ + expConditions: []conditions.Condition{ conditions.NewPolicyInvalid("spec.targetRefs[1].kind: Unsupported value: \"Unsupported\": " + "supported values: \"Service\""), }, @@ -94,7 +94,7 @@ func TestValidator_Validate(t *testing.T) { p.Spec.ZoneSize = helpers.GetPointer[ngfAPI.Size]("invalid") return p }), - expConditions: conditions.Conditions{ + expConditions: []conditions.Condition{ conditions.NewPolicyInvalid("spec.zoneSize: Invalid value: \"invalid\": ^\\d{1,4}(k|m|g)?$ " + "(e.g. '1024', or '8k', or '20m', or '1g', regex used for validation is 'must contain a number. " + "May be followed by 'k', 'm', or 'g', otherwise bytes are assumed')"), @@ -107,7 +107,7 @@ func TestValidator_Validate(t *testing.T) { p.Spec.KeepAlive.Timeout = helpers.GetPointer[ngfAPI.Duration]("invalid") return p }), - expConditions: conditions.Conditions{ + expConditions: []conditions.Condition{ conditions.NewPolicyInvalid( "[spec.keepAlive.time: Invalid value: \"invalid\": ^[0-9]{1,4}(ms|s|m|h)? " + "(e.g. '5ms', or '10s', or '500m', or '1000h', regex used for validation is " + diff --git a/internal/controller/nginx/config/policies/validator.go b/internal/controller/nginx/config/policies/validator.go index a19e8ea037..5182c9781a 100644 --- a/internal/controller/nginx/config/policies/validator.go +++ b/internal/controller/nginx/config/policies/validator.go @@ -16,9 +16,9 @@ import ( //counterfeiter:generate . Validator type Validator interface { // Validate validates an NGF Policy. - Validate(policy Policy) conditions.Conditions + Validate(policy Policy) []conditions.Condition // ValidateGlobalSettings validates an NGF Policy with the NginxProxy settings. - ValidateGlobalSettings(policy Policy, globalSettings *GlobalSettings) conditions.Conditions + ValidateGlobalSettings(policy Policy, globalSettings *GlobalSettings) []conditions.Condition // Conflicts returns true if the two Policies conflict. Conflicts(a, b Policy) bool } @@ -56,7 +56,7 @@ func NewManager( } // Validate validates the policy. -func (m *CompositeValidator) Validate(policy Policy) conditions.Conditions { +func (m *CompositeValidator) Validate(policy Policy) []conditions.Condition { gvk := m.mustExtractGVK(policy) validator, ok := m.validators[gvk] @@ -71,7 +71,7 @@ func (m *CompositeValidator) Validate(policy Policy) conditions.Conditions { func (m *CompositeValidator) ValidateGlobalSettings( policy Policy, globalSettings *GlobalSettings, -) conditions.Conditions { +) []conditions.Condition { gvk := m.mustExtractGVK(policy) validator, ok := m.validators[gvk] diff --git a/internal/controller/nginx/config/policies/validator_test.go b/internal/controller/nginx/config/policies/validator_test.go index 7116391497..f30b7e7790 100644 --- a/internal/controller/nginx/config/policies/validator_test.go +++ b/internal/controller/nginx/config/policies/validator_test.go @@ -48,11 +48,11 @@ var _ = Describe("Policy CompositeValidator", func() { mustExtractGVK, policies.ManagerConfig{ Validator: &policiesfakes.FakeValidator{ - ValidateStub: func(_ policies.Policy) conditions.Conditions { - return conditions.Conditions{conditions.NewPolicyInvalid("apple error")} + ValidateStub: func(_ policies.Policy) []conditions.Condition { + return []conditions.Condition{conditions.NewPolicyInvalid("apple error")} }, - ValidateGlobalSettingsStub: func(_ policies.Policy, _ *policies.GlobalSettings) conditions.Conditions { - return conditions.Conditions{conditions.NewPolicyInvalid("apple global settings error")} + ValidateGlobalSettingsStub: func(_ policies.Policy, _ *policies.GlobalSettings) []conditions.Condition { + return []conditions.Condition{conditions.NewPolicyInvalid("apple global settings error")} }, ConflictsStub: func(_ policies.Policy, _ policies.Policy) bool { return true }, }, @@ -60,11 +60,11 @@ var _ = Describe("Policy CompositeValidator", func() { }, policies.ManagerConfig{ Validator: &policiesfakes.FakeValidator{ - ValidateStub: func(_ policies.Policy) conditions.Conditions { - return conditions.Conditions{conditions.NewPolicyInvalid("orange error")} + ValidateStub: func(_ policies.Policy) []conditions.Condition { + return []conditions.Condition{conditions.NewPolicyInvalid("orange error")} }, - ValidateGlobalSettingsStub: func(_ policies.Policy, _ *policies.GlobalSettings) conditions.Conditions { - return conditions.Conditions{conditions.NewPolicyInvalid("orange global settings error")} + ValidateGlobalSettingsStub: func(_ policies.Policy, _ *policies.GlobalSettings) []conditions.Condition { + return []conditions.Condition{conditions.NewPolicyInvalid("orange global settings error")} }, ConflictsStub: func(_ policies.Policy, _ policies.Policy) bool { return false }, }, diff --git a/internal/controller/state/change_processor_test.go b/internal/controller/state/change_processor_test.go index d20cce6eee..2d17e6f6e9 100644 --- a/internal/controller/state/change_processor_test.go +++ b/internal/controller/state/change_processor_test.go @@ -736,7 +736,7 @@ var _ = Describe("ChangeProcessor", func() { }, Valid: true, Attachable: true, - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewRouteBackendRefRefBackendNotFound( "spec.rules[0].backendRefs[0].name: Not found: \"service\"", ), @@ -804,7 +804,7 @@ var _ = Describe("ChangeProcessor", func() { }, Valid: true, Attachable: true, - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewRouteBackendRefRefBackendNotFound( "spec.rules[0].backendRefs[0].name: Not found: \"service\"", ), @@ -872,7 +872,7 @@ var _ = Describe("ChangeProcessor", func() { }, Valid: true, Attachable: true, - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewRouteBackendRefRefBackendNotFound( "spec.rules[0].backendRefs[0].name: Not found: \"grpc-service\"", ), @@ -940,7 +940,7 @@ var _ = Describe("ChangeProcessor", func() { }, Valid: true, Attachable: true, - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewRouteBackendRefRefBackendNotFound( "spec.rules[0].backendRefs[0].name: Not found: \"grpc-service\"", ), @@ -976,7 +976,7 @@ var _ = Describe("ChangeProcessor", func() { }, Valid: true, Attachable: true, - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewRouteBackendRefRefBackendNotFound( "spec.rules[0].backendRefs[0].name: Not found: \"tls-service\"", ), @@ -1012,7 +1012,7 @@ var _ = Describe("ChangeProcessor", func() { }, Valid: true, Attachable: true, - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewRouteBackendRefRefBackendNotFound( "spec.rules[0].backendRefs[0].name: Not found: \"tls-service\"", ), @@ -1281,21 +1281,21 @@ var _ = Describe("ChangeProcessor", func() { gw.Listeners = nil // no ref grant exists yet for the routes - expGraph.Routes[httpRouteKey1].Conditions = conditions.Conditions{ + expGraph.Routes[httpRouteKey1].Conditions = []conditions.Condition{ conditions.NewRouteBackendRefRefNotPermitted( "spec.rules[0].backendRefs[0].namespace: Forbidden: " + "Backend ref to Service service-ns/service not permitted by any ReferenceGrant", ), } - expGraph.Routes[grpcRouteKey1].Conditions = conditions.Conditions{ + expGraph.Routes[grpcRouteKey1].Conditions = []conditions.Condition{ conditions.NewRouteBackendRefRefNotPermitted( "spec.rules[0].backendRefs[0].namespace: Forbidden: Backend ref to Service " + "grpc-service-ns/grpc-service not permitted by any ReferenceGrant", ), } - expGraph.L4Routes[trKey1].Conditions = conditions.Conditions{ + expGraph.L4Routes[trKey1].Conditions = []conditions.Condition{ conditions.NewRouteBackendRefRefNotPermitted( "spec.rules[0].backendRefs[0].namespace: Forbidden: Backend ref to Service " + "tls-service-ns/tls-service not permitted by any ReferenceGrant", @@ -1305,23 +1305,23 @@ var _ = Describe("ChangeProcessor", func() { // gateway class does not exist so routes cannot attach expGraph.Routes[httpRouteKey1].ParentRefs[0].Attachment = &graph.ParentRefAttachmentStatus{ AcceptedHostnames: map[string][]string{}, - FailedConditions: conditions.Conditions{conditions.NewRouteNoMatchingParent()}, + FailedConditions: []conditions.Condition{conditions.NewRouteNoMatchingParent()}, } expGraph.Routes[httpRouteKey1].ParentRefs[1].Attachment = &graph.ParentRefAttachmentStatus{ AcceptedHostnames: map[string][]string{}, - FailedConditions: conditions.Conditions{conditions.NewRouteNoMatchingParent()}, + FailedConditions: []conditions.Condition{conditions.NewRouteNoMatchingParent()}, } expGraph.Routes[grpcRouteKey1].ParentRefs[0].Attachment = &graph.ParentRefAttachmentStatus{ AcceptedHostnames: map[string][]string{}, - FailedConditions: conditions.Conditions{conditions.NewRouteNoMatchingParent()}, + FailedConditions: []conditions.Condition{conditions.NewRouteNoMatchingParent()}, } expGraph.Routes[grpcRouteKey1].ParentRefs[1].Attachment = &graph.ParentRefAttachmentStatus{ AcceptedHostnames: map[string][]string{}, - FailedConditions: conditions.Conditions{conditions.NewRouteNoMatchingParent()}, + FailedConditions: []conditions.Condition{conditions.NewRouteNoMatchingParent()}, } expGraph.L4Routes[trKey1].ParentRefs[0].Attachment = &graph.ParentRefAttachmentStatus{ AcceptedHostnames: map[string][]string{}, - FailedConditions: conditions.Conditions{conditions.NewRouteNoMatchingParent()}, + FailedConditions: []conditions.Condition{conditions.NewRouteNoMatchingParent()}, } expGraph.ReferencedSecrets = nil @@ -1379,7 +1379,7 @@ var _ = Describe("ChangeProcessor", func() { listener443.Routes[grpcRouteKey1].ParentRefs[1].Attachment = expAttachment443 // no ref grant exists yet for hr1 - expGraph.Routes[httpRouteKey1].Conditions = conditions.Conditions{ + expGraph.Routes[httpRouteKey1].Conditions = []conditions.Condition{ conditions.NewRouteBackendRefRefNotPermitted( "spec.rules[0].backendRefs[0].namespace: Forbidden: Backend ref to Service " + "service-ns/service not permitted by any ReferenceGrant", @@ -1390,7 +1390,7 @@ var _ = Describe("ChangeProcessor", func() { expGraph.Routes[httpRouteKey1].ParentRefs[1].Attachment = expAttachment443 // no ref grant exists yet for gr1 - expGraph.Routes[grpcRouteKey1].Conditions = conditions.Conditions{ + expGraph.Routes[grpcRouteKey1].Conditions = []conditions.Condition{ conditions.NewRouteBackendRefRefNotPermitted( "spec.rules[0].backendRefs[0].namespace: Forbidden: Backend ref to Service " + "grpc-service-ns/grpc-service not permitted by any ReferenceGrant", @@ -1401,7 +1401,7 @@ var _ = Describe("ChangeProcessor", func() { expGraph.Routes[grpcRouteKey1].ParentRefs[1].Attachment = expAttachment443 // no ref grant exists yet for tr1 - expGraph.L4Routes[trKey1].Conditions = conditions.Conditions{ + expGraph.L4Routes[trKey1].Conditions = []conditions.Condition{ conditions.NewRouteBackendRefRefNotPermitted( "spec.rules[0].backendRefs[0].namespace: Forbidden: Backend ref to Service " + "tls-service-ns/tls-service not permitted by any ReferenceGrant", @@ -1423,7 +1423,7 @@ var _ = Describe("ChangeProcessor", func() { processor.CaptureUpsertChange(secretRefGrant) // no ref grant exists yet for hr1 - expGraph.Routes[httpRouteKey1].Conditions = conditions.Conditions{ + expGraph.Routes[httpRouteKey1].Conditions = []conditions.Condition{ conditions.NewRouteBackendRefRefNotPermitted( "spec.rules[0].backendRefs[0].namespace: Forbidden: Backend ref to Service " + "service-ns/service not permitted by any ReferenceGrant", @@ -1431,7 +1431,7 @@ var _ = Describe("ChangeProcessor", func() { } // no ref grant exists yet for gr1 - expGraph.Routes[grpcRouteKey1].Conditions = conditions.Conditions{ + expGraph.Routes[grpcRouteKey1].Conditions = []conditions.Condition{ conditions.NewRouteBackendRefRefNotPermitted( "spec.rules[0].backendRefs[0].namespace: Forbidden: Backend ref to Service " + "grpc-service-ns/grpc-service not permitted by any ReferenceGrant", @@ -1439,7 +1439,7 @@ var _ = Describe("ChangeProcessor", func() { } // no ref grant exists yet for tr1 - expGraph.L4Routes[trKey1].Conditions = conditions.Conditions{ + expGraph.L4Routes[trKey1].Conditions = []conditions.Condition{ conditions.NewRouteBackendRefRefNotPermitted( "spec.rules[0].backendRefs[0].namespace: Forbidden: Backend ref to Service " + "tls-service-ns/tls-service not permitted by any ReferenceGrant", @@ -1471,7 +1471,7 @@ var _ = Describe("ChangeProcessor", func() { processor.CaptureUpsertChange(hrServiceRefGrant) // no ref grant exists yet for gr1 - expGraph.Routes[grpcRouteKey1].Conditions = conditions.Conditions{ + expGraph.Routes[grpcRouteKey1].Conditions = []conditions.Condition{ conditions.NewRouteBackendRefRefNotPermitted( "spec.rules[0].backendRefs[0].namespace: Forbidden: Backend ref to Service " + "grpc-service-ns/grpc-service not permitted by any ReferenceGrant", @@ -1481,7 +1481,7 @@ var _ = Describe("ChangeProcessor", func() { expRouteGR1.Spec.Rules[0].BackendRefs[0].SvcNsName = types.NamespacedName{} // no ref grant exists yet for tr1 - expGraph.L4Routes[trKey1].Conditions = conditions.Conditions{ + expGraph.L4Routes[trKey1].Conditions = []conditions.Condition{ conditions.NewRouteBackendRefRefNotPermitted( "spec.rules[0].backendRefs[0].namespace: Forbidden: Backend ref to Service " + "tls-service-ns/tls-service not permitted by any ReferenceGrant", @@ -1510,7 +1510,7 @@ var _ = Describe("ChangeProcessor", func() { processor.CaptureUpsertChange(grServiceRefGrant) // no ref grant exists yet for tr1 - expGraph.L4Routes[trKey1].Conditions = conditions.Conditions{ + expGraph.L4Routes[trKey1].Conditions = []conditions.Condition{ conditions.NewRouteBackendRefRefNotPermitted( "spec.rules[0].backendRefs[0].namespace: Forbidden: Backend ref to Service " + "tls-service-ns/tls-service not permitted by any ReferenceGrant", diff --git a/internal/controller/state/conditions/conditions.go b/internal/controller/state/conditions/conditions.go index b42948c483..0e40194fd9 100644 --- a/internal/controller/state/conditions/conditions.go +++ b/internal/controller/state/conditions/conditions.go @@ -38,7 +38,8 @@ const ( // Route rules has a backendRef with an unsupported value. RouteReasonBackendRefUnsupportedValue v1.RouteConditionReason = "UnsupportedValue" - // RouteReasonUnsupportedField is used when a Route contains fields that are not yet supported. + // RouteReasonUnsupportedField is used with the "Accepted" condition when a Route contains fields that are + // not yet supported. RouteReasonUnsupportedField v1.RouteConditionReason = "UnsupportedField" // RouteConditionUnsupportedField indicates that the Route contains fields that are not yet supported. @@ -69,7 +70,8 @@ const ( // invalid. Used with ResolvedRefs (false). RouteReasonInvalidFilter v1.RouteConditionReason = "InvalidFilter" - // GatewayReasonUnsupportedField is used when a Route contains fields that are not yet supported. + // GatewayReasonUnsupportedField is used with the "Accepted" condition when a Gateway contains fields + // that are not yet supported. GatewayReasonUnsupportedField v1.GatewayConditionReason = "UnsupportedField" // GatewayConditionUnsupportedField indicates that the Gateway contains fields that are not yet supported. @@ -179,22 +181,9 @@ type Condition struct { Message string } -type Conditions []Condition - -func (c *Conditions) CountInvalid() int { - var count int - for _, cond := range *c { - if cond.Status == metav1.ConditionFalse { - count++ - } - } - - return count -} - // DeduplicateConditions removes duplicate conditions based on the condition type. // The last condition wins. The order of conditions is preserved. -func DeduplicateConditions(conds Conditions) Conditions { +func DeduplicateConditions(conds []Condition) []Condition { type elem struct { cond Condition reverseIdx int @@ -215,7 +204,7 @@ func DeduplicateConditions(conds Conditions) Conditions { idx++ } - result := make(Conditions, len(uniqueElems)) + result := make([]Condition, len(uniqueElems)) for _, el := range uniqueElems { result[len(result)-el.reverseIdx-1] = el.cond @@ -226,7 +215,7 @@ func DeduplicateConditions(conds Conditions) Conditions { // ConvertConditions converts conditions to Kubernetes API conditions. func ConvertConditions( - conds Conditions, + conds []Condition, observedGeneration int64, transitionTime metav1.Time, ) []metav1.Condition { @@ -247,7 +236,7 @@ func ConvertConditions( } // HasMatchingCondition checks if the given condition matches any of the existing conditions. -func HasMatchingCondition(existingConditions Conditions, cond Condition) bool { +func HasMatchingCondition(existingConditions []Condition, cond Condition) bool { for _, existing := range existingConditions { if existing.Type == cond.Type && existing.Status == cond.Status && @@ -261,8 +250,8 @@ func HasMatchingCondition(existingConditions Conditions, cond Condition) bool { // NewDefaultGatewayClassConditions returns Conditions that indicate that the GatewayClass is accepted and that the // Gateway API CRD versions are supported. -func NewDefaultGatewayClassConditions() Conditions { - return Conditions{ +func NewDefaultGatewayClassConditions() []Condition { + return []Condition{ { Type: string(v1.GatewayClassConditionStatusAccepted), Status: metav1.ConditionTrue, @@ -281,8 +270,8 @@ func NewDefaultGatewayClassConditions() Conditions { // NewGatewayClassSupportedVersionBestEffort returns a Condition that indicates that the GatewayClass is accepted, // but the Gateway API CRD versions are not supported. This means NGF will attempt to generate configuration, // but it does not guarantee support. -func NewGatewayClassSupportedVersionBestEffort(recommendedVersion string) Conditions { - return Conditions{ +func NewGatewayClassSupportedVersionBestEffort(recommendedVersion string) []Condition { + return []Condition{ { Type: string(v1.GatewayClassConditionStatusSupportedVersion), Status: metav1.ConditionFalse, @@ -297,8 +286,8 @@ func NewGatewayClassSupportedVersionBestEffort(recommendedVersion string) Condit // NewGatewayClassUnsupportedVersion returns Conditions that indicate that the GatewayClass is not accepted because // the Gateway API CRD versions are not supported. NGF will not generate configuration in this case. -func NewGatewayClassUnsupportedVersion(recommendedVersion string) Conditions { - return Conditions{ +func NewGatewayClassUnsupportedVersion(recommendedVersion string) []Condition { + return []Condition{ { Type: string(v1.GatewayClassConditionStatusAccepted), Status: metav1.ConditionFalse, @@ -332,8 +321,8 @@ func NewGatewayClassConflict() Condition { } // NewDefaultRouteConditions returns the default conditions that must be present in the status of a Route. -func NewDefaultRouteConditions() Conditions { - return Conditions{ +func NewDefaultRouteConditions() []Condition { + return []Condition{ NewRouteAccepted(), NewRouteResolvedRefs(), } @@ -551,8 +540,8 @@ func NewRouteResolvedRefsInvalidFilter(msg string) Condition { // NewDefaultListenerConditions returns the default Conditions that must be present in the status of a Listener. // If existingConditions contains conflict-related conditions (like OverlappingTLSConfig or Conflicted), // the NoConflicts condition is excluded to avoid conflicting condition states. -func NewDefaultListenerConditions(existingConditions Conditions) Conditions { - defaultConds := Conditions{ +func NewDefaultListenerConditions(existingConditions []Condition) []Condition { + defaultConds := []Condition{ NewListenerAccepted(), NewListenerProgrammed(), NewListenerResolvedRefs(), @@ -567,7 +556,7 @@ func NewDefaultListenerConditions(existingConditions Conditions) Conditions { } // hasConflictConditions checks if the listener has any conflict-related conditions. -func hasConflictConditions(conditions Conditions) bool { +func hasConflictConditions(conditions []Condition) bool { for _, cond := range conditions { if cond.Type == string(v1.ListenerConditionConflicted) || cond.Type == string(v1.ListenerConditionOverlappingTLSConfig) { @@ -630,8 +619,8 @@ func NewListenerNotProgrammedInvalid(msg string) Condition { // NewListenerUnsupportedValue returns Conditions that indicate that a field of a Listener has an unsupported value. // Unsupported means that the value is not supported by the implementation or invalid. -func NewListenerUnsupportedValue(msg string) Conditions { - return Conditions{ +func NewListenerUnsupportedValue(msg string) []Condition { + return []Condition{ { Type: string(v1.ListenerConditionAccepted), Status: metav1.ConditionFalse, @@ -643,8 +632,8 @@ func NewListenerUnsupportedValue(msg string) Conditions { } // NewListenerInvalidCertificateRef returns Conditions that indicate that a CertificateRef of a Listener is invalid. -func NewListenerInvalidCertificateRef(msg string) Conditions { - return Conditions{ +func NewListenerInvalidCertificateRef(msg string) []Condition { + return []Condition{ { Type: string(v1.ListenerConditionAccepted), Status: metav1.ConditionFalse, @@ -663,8 +652,8 @@ func NewListenerInvalidCertificateRef(msg string) Conditions { // NewListenerInvalidRouteKinds returns Conditions that indicate that an invalid or unsupported Route kind is // specified by the Listener. -func NewListenerInvalidRouteKinds(msg string) Conditions { - return Conditions{ +func NewListenerInvalidRouteKinds(msg string) []Condition { + return []Condition{ { Type: string(v1.ListenerReasonResolvedRefs), Status: metav1.ConditionFalse, @@ -677,8 +666,8 @@ func NewListenerInvalidRouteKinds(msg string) Conditions { // NewListenerProtocolConflict returns Conditions that indicate multiple Listeners are specified with the same // Listener port number, but have conflicting protocol specifications. -func NewListenerProtocolConflict(msg string) Conditions { - return Conditions{ +func NewListenerProtocolConflict(msg string) []Condition { + return []Condition{ { Type: string(v1.ListenerConditionAccepted), Status: metav1.ConditionFalse, @@ -697,8 +686,8 @@ func NewListenerProtocolConflict(msg string) Conditions { // NewListenerHostnameConflict returns Conditions that indicate multiple Listeners are specified with the same // Listener port, but are HTTPS and TLS and have overlapping hostnames. -func NewListenerHostnameConflict(msg string) Conditions { - return Conditions{ +func NewListenerHostnameConflict(msg string) []Condition { + return []Condition{ { Type: string(v1.ListenerConditionAccepted), Status: metav1.ConditionFalse, @@ -716,8 +705,8 @@ func NewListenerHostnameConflict(msg string) Conditions { } // NewListenerUnsupportedProtocol returns Conditions that indicate that the protocol of a Listener is unsupported. -func NewListenerUnsupportedProtocol(msg string) Conditions { - return Conditions{ +func NewListenerUnsupportedProtocol(msg string) []Condition { + return []Condition{ { Type: string(v1.ListenerConditionAccepted), Status: metav1.ConditionFalse, @@ -730,8 +719,8 @@ func NewListenerUnsupportedProtocol(msg string) Conditions { // NewListenerRefNotPermitted returns Conditions that indicates that the Listener references a TLS secret that is not // permitted by a ReferenceGrant. -func NewListenerRefNotPermitted(msg string) Conditions { - return Conditions{ +func NewListenerRefNotPermitted(msg string) []Condition { + return []Condition{ { Type: string(v1.ListenerConditionAccepted), Status: metav1.ConditionFalse, @@ -805,8 +794,8 @@ func NewGatewayClassInvalidParameters(msg string) Condition { } // NewDefaultGatewayConditions returns the default Conditions that must be present in the status of a Gateway. -func NewDefaultGatewayConditions() Conditions { - return Conditions{ +func NewDefaultGatewayConditions() []Condition { + return []Condition{ NewGatewayAccepted(), NewGatewayProgrammed(), } @@ -835,9 +824,9 @@ func NewGatewayAcceptedListenersNotValid() Condition { // NewGatewayNotAcceptedListenersNotValid returns Conditions that indicate the Gateway is not accepted, // because all listeners are invalid. -func NewGatewayNotAcceptedListenersNotValid() Conditions { +func NewGatewayNotAcceptedListenersNotValid() []Condition { msg := "Gateway has no valid listeners" - return Conditions{ + return []Condition{ { Type: string(v1.GatewayConditionAccepted), Status: metav1.ConditionFalse, @@ -850,8 +839,8 @@ func NewGatewayNotAcceptedListenersNotValid() Conditions { // NewGatewayInvalid returns Conditions that indicate the Gateway is not accepted and programmed because it is // semantically or syntactically invalid. The provided message contains the details of why the Gateway is invalid. -func NewGatewayInvalid(msg string) Conditions { - return Conditions{ +func NewGatewayInvalid(msg string) []Condition { + return []Condition{ { Type: string(v1.GatewayConditionAccepted), Status: metav1.ConditionFalse, diff --git a/internal/controller/state/conditions/conditions_test.go b/internal/controller/state/conditions/conditions_test.go index 8883ad3516..45ebcc5808 100644 --- a/internal/controller/state/conditions/conditions_test.go +++ b/internal/controller/state/conditions/conditions_test.go @@ -9,7 +9,7 @@ import ( func TestDeduplicateConditions(t *testing.T) { t.Parallel() - conds := Conditions{ + conds := []Condition{ { Type: "Type1", Status: metav1.ConditionTrue, @@ -37,7 +37,7 @@ func TestDeduplicateConditions(t *testing.T) { }, } - expected := Conditions{ + expected := []Condition{ { Type: "Type1", Status: metav1.ConditionFalse, @@ -63,7 +63,7 @@ func TestDeduplicateConditions(t *testing.T) { func TestConvertConditions(t *testing.T) { t.Parallel() - conds := Conditions{ + conds := []Condition{ { Type: "Type1", Status: metav1.ConditionTrue, @@ -112,7 +112,7 @@ func TestHasMatchingCondition(t *testing.T) { tests := []struct { condition Condition name string - conds Conditions + conds []Condition expected bool }{ { @@ -123,13 +123,13 @@ func TestHasMatchingCondition(t *testing.T) { }, { name: "condition matches existing condition", - conds: Conditions{NewClientSettingsPolicyAffected()}, + conds: []Condition{NewClientSettingsPolicyAffected()}, condition: NewClientSettingsPolicyAffected(), expected: true, }, { name: "condition does not match existing condition", - conds: Conditions{NewClientSettingsPolicyAffected()}, + conds: []Condition{NewClientSettingsPolicyAffected()}, condition: NewObservabilityPolicyAffected(), expected: false, }, diff --git a/internal/controller/state/graph/backend_refs.go b/internal/controller/state/graph/backend_refs.go index dd3fb98c1e..d18a81cc43 100644 --- a/internal/controller/state/graph/backend_refs.go +++ b/internal/controller/state/graph/backend_refs.go @@ -135,7 +135,7 @@ func createBackendRef( services map[types.NamespacedName]*v1.Service, refPath *field.Path, backendTLSPolicies map[types.NamespacedName]*BackendTLSPolicy, -) (BackendRef, conditions.Conditions) { +) (BackendRef, []conditions.Condition) { // Data plane will handle invalid ref by responding with 500. // Because of that, we always need to add a BackendRef to group.Backends, even if the ref is invalid. // Additionally, we always calculate the weight, even if it is invalid. @@ -158,7 +158,7 @@ func createBackendRef( InvalidForGateways: make(map[types.NamespacedName]conditions.Condition), } - return backendRef, conditions.Conditions{cond} + return backendRef, []conditions.Condition{cond} } ns := route.Source.GetNamespace() @@ -177,10 +177,10 @@ func createBackendRef( InvalidForGateways: make(map[types.NamespacedName]conditions.Condition), } - return backendRef, conditions.Conditions{conditions.NewRouteBackendRefRefBackendNotFound(err.Error())} + return backendRef, []conditions.Condition{conditions.NewRouteBackendRefRefBackendNotFound(err.Error())} } - var conds conditions.Conditions + var conds []conditions.Condition invalidForGateways := make(map[types.NamespacedName]conditions.Condition) // Check if this is an ExternalName service and validate DNS resolver configuration diff --git a/internal/controller/state/graph/backend_refs_test.go b/internal/controller/state/graph/backend_refs_test.go index c75584de77..6e07bad538 100644 --- a/internal/controller/state/graph/backend_refs_test.go +++ b/internal/controller/state/graph/backend_refs_test.go @@ -602,7 +602,7 @@ func TestAddBackendRefsToRules(t *testing.T) { }, }, }, - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ { Type: "Accepted", Status: "True", @@ -632,7 +632,7 @@ func TestAddBackendRefsToRules(t *testing.T) { policies map[types.NamespacedName]*BackendTLSPolicy name string expectedBackendRefs []BackendRef - expectedConditions conditions.Conditions + expectedConditions []conditions.Condition }{ { route: createRoute("hr1", RouteTypeHTTP, "Service", 1, "svc1"), @@ -706,7 +706,7 @@ func TestAddBackendRefsToRules(t *testing.T) { InvalidForGateways: map[types.NamespacedName]conditions.Condition{}, }, }, - expectedConditions: conditions.Conditions{ + expectedConditions: []conditions.Condition{ conditions.NewRouteBackendRefUnsupportedProtocol( "route type http does not support service port appProtocol kubernetes.io/h2c;" + " nginx does not support proxying to upstreams with http2 or h2c", @@ -758,7 +758,7 @@ func TestAddBackendRefsToRules(t *testing.T) { InvalidForGateways: map[types.NamespacedName]conditions.Condition{}, }, }, - expectedConditions: conditions.Conditions{ + expectedConditions: []conditions.Condition{ conditions.NewRouteBackendRefUnsupportedProtocol( "route type http does not support service port appProtocol kubernetes.io/wss;" + " missing corresponding BackendTLSPolicy", @@ -793,7 +793,7 @@ func TestAddBackendRefsToRules(t *testing.T) { InvalidForGateways: map[types.NamespacedName]conditions.Condition{}, }, }, - expectedConditions: conditions.Conditions{ + expectedConditions: []conditions.Condition{ conditions.NewRouteBackendRefUnsupportedProtocol( "route type grpc does not support service port appProtocol kubernetes.io/ws", ), @@ -812,7 +812,7 @@ func TestAddBackendRefsToRules(t *testing.T) { InvalidForGateways: map[types.NamespacedName]conditions.Condition{}, }, }, - expectedConditions: conditions.Conditions{ + expectedConditions: []conditions.Condition{ conditions.NewRouteBackendRefUnsupportedProtocol( "route type grpc does not support service port appProtocol kubernetes.io/wss", ), @@ -890,7 +890,7 @@ func TestAddBackendRefsToRules(t *testing.T) { InvalidForGateways: map[types.NamespacedName]conditions.Condition{}, }, }, - expectedConditions: conditions.Conditions{ + expectedConditions: []conditions.Condition{ conditions.NewRouteBackendRefInvalidKind( `spec.rules[0].backendRefs[0].kind: Unsupported value: "NotService": supported values: "Service"`, ), @@ -921,7 +921,7 @@ func TestAddBackendRefsToRules(t *testing.T) { InvalidForGateways: map[types.NamespacedName]conditions.Condition{}, }, }, - expectedConditions: conditions.Conditions{ + expectedConditions: []conditions.Condition{ conditions.NewRouteBackendRefUnsupportedValue( `Backend TLS policies do not match for all backends`, ), @@ -1084,7 +1084,7 @@ func TestCreateBackend(t *testing.T) { }, }, Valid: false, - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewPolicyInvalid("unsupported value"), }, } @@ -1094,7 +1094,7 @@ func TestCreateBackend(t *testing.T) { name string expectedServicePortReference string ref gatewayv1.HTTPBackendRef - expectedConditions conditions.Conditions + expectedConditions []conditions.Condition expectedBackend BackendRef }{ { @@ -1145,7 +1145,7 @@ func TestCreateBackend(t *testing.T) { Valid: false, }, expectedServicePortReference: "", - expectedConditions: conditions.Conditions{ + expectedConditions: []conditions.Condition{ conditions.NewRouteBackendRefUnsupportedValue( "test.weight: Invalid value: -1: must be in the range [0, 1000000]", ), @@ -1167,7 +1167,7 @@ func TestCreateBackend(t *testing.T) { Valid: false, }, expectedServicePortReference: "", - expectedConditions: conditions.Conditions{ + expectedConditions: []conditions.Condition{ conditions.NewRouteBackendRefInvalidKind( `test.kind: Unsupported value: "NotService": supported values: "Service"`, ), @@ -1191,7 +1191,7 @@ func TestCreateBackend(t *testing.T) { InvalidForGateways: map[types.NamespacedName]conditions.Condition{}, }, expectedServicePortReference: "", - expectedConditions: conditions.Conditions{ + expectedConditions: []conditions.Condition{ conditions.NewRouteBackendRefRefBackendNotFound(`test.name: Not found: "not-exist"`), }, name: "service doesn't exist", @@ -1250,7 +1250,7 @@ func TestCreateBackend(t *testing.T) { InvalidForGateways: map[types.NamespacedName]conditions.Condition{}, }, expectedServicePortReference: "", - expectedConditions: conditions.Conditions{ + expectedConditions: []conditions.Condition{ conditions.NewRouteBackendRefUnsupportedValue( "the backend TLS policy is invalid: unsupported value", ), @@ -1345,7 +1345,7 @@ func TestCreateBackend(t *testing.T) { }, }, expectedServicePortReference: "", - expectedConditions: conditions.Conditions{ + expectedConditions: []conditions.Condition{ conditions.NewRouteBackendRefUnsupportedValue( "ExternalName service has empty or invalid externalName field", ), @@ -1371,7 +1371,7 @@ func TestCreateBackend(t *testing.T) { }, }, expectedServicePortReference: "", - expectedConditions: conditions.Conditions{ + expectedConditions: []conditions.Condition{ conditions.NewRouteBackendRefUnsupportedValue( "ExternalName service has empty or invalid externalName field", ), diff --git a/internal/controller/state/graph/backend_tls_policy.go b/internal/controller/state/graph/backend_tls_policy.go index ec8567b619..591f83d6d3 100644 --- a/internal/controller/state/graph/backend_tls_policy.go +++ b/internal/controller/state/graph/backend_tls_policy.go @@ -24,7 +24,7 @@ type BackendTLSPolicy struct { // Only contains gateways where the policy can be applied (not limited by ancestor status). Gateways []types.NamespacedName // Conditions include Conditions for the BackendTLSPolicy. - Conditions conditions.Conditions + Conditions []conditions.Condition // Valid shows whether the BackendTLSPolicy is valid. Valid bool // IsReferenced shows whether the BackendTLSPolicy is referenced by a BackendRef. @@ -70,7 +70,7 @@ func validateBackendTLSPolicy( backendTLSPolicy *v1alpha3.BackendTLSPolicy, configMapResolver *configMapResolver, secretResolver *secretResolver, -) (valid, ignored bool, conds conditions.Conditions) { +) (valid, ignored bool, conds []conditions.Condition) { valid = true ignored = false @@ -132,11 +132,11 @@ func validateBackendTLSCACertRef( btp *v1alpha3.BackendTLSPolicy, configMapResolver *configMapResolver, secretResolver *secretResolver, -) conditions.Conditions { +) []conditions.Condition { if len(btp.Spec.Validation.CACertificateRefs) != 1 { path := field.NewPath("validation.caCertificateRefs") valErr := field.TooMany(path, len(btp.Spec.Validation.CACertificateRefs), 1) - return conditions.Conditions{conditions.NewPolicyInvalid(valErr.Error())} + return []conditions.Condition{conditions.NewPolicyInvalid(valErr.Error())} } selectedCertRef := btp.Spec.Validation.CACertificateRefs[0] @@ -145,7 +145,7 @@ func validateBackendTLSCACertRef( if !slices.Contains(allowedCaCertKinds, selectedCertRef.Kind) { path := field.NewPath("validation.caCertificateRefs[0].kind") valErr := field.NotSupported(path, btp.Spec.Validation.CACertificateRefs[0].Kind, allowedCaCertKinds) - return conditions.Conditions{ + return []conditions.Condition{ conditions.NewBackendTLSPolicyInvalidKind(valErr.Error()), conditions.NewBackendTLSPolicyNoValidCACertificate("No valid CACertificateRef found"), } @@ -154,7 +154,7 @@ func validateBackendTLSCACertRef( selectedCertRef.Group != "core" { path := field.NewPath("validation.caCertificateRefs[0].group") valErr := field.NotSupported(path, selectedCertRef.Group, []string{"", "core"}) - return conditions.Conditions{ + return []conditions.Condition{ conditions.NewBackendTLSPolicyInvalidKind(valErr.Error()), conditions.NewBackendTLSPolicyNoValidCACertificate("No valid CACertificateRef found"), } @@ -169,7 +169,7 @@ func validateBackendTLSCACertRef( if err := configMapResolver.resolve(nsName); err != nil { path := field.NewPath("validation.caCertificateRefs[0]") valErr := field.Invalid(path, selectedCertRef, err.Error()) - return conditions.Conditions{ + return []conditions.Condition{ conditions.NewBackendTLSPolicyInvalidCACertificateRef(valErr.Error()), conditions.NewBackendTLSPolicyNoValidCACertificate("No valid CACertificateRef found"), } @@ -178,7 +178,7 @@ func validateBackendTLSCACertRef( if err := secretResolver.resolve(nsName); err != nil { path := field.NewPath("validation.caCertificateRefs[0]") valErr := field.Invalid(path, selectedCertRef, err.Error()) - return conditions.Conditions{ + return []conditions.Condition{ conditions.NewBackendTLSPolicyInvalidCACertificateRef(valErr.Error()), conditions.NewBackendTLSPolicyNoValidCACertificate("No valid CACertificateRef found"), } @@ -212,10 +212,10 @@ func countNonNGFAncestors(policy *v1alpha3.BackendTLSPolicy, ctlrName string) in // addPolicyAncestorLimitCondition adds or updates a PolicyAncestorLimitReached condition. func addPolicyAncestorLimitCondition( - conds conditions.Conditions, + conds []conditions.Condition, policyName string, policyType string, -) conditions.Conditions { +) []conditions.Condition { for i, condition := range conds { if condition.Reason == string(conditions.PolicyReasonAncestorLimitReached) { if !strings.Contains(condition.Message, policyName) { diff --git a/internal/controller/state/graph/backend_tls_policy_test.go b/internal/controller/state/graph/backend_tls_policy_test.go index b38cefe469..53e9011edb 100644 --- a/internal/controller/state/graph/backend_tls_policy_test.go +++ b/internal/controller/state/graph/backend_tls_policy_test.go @@ -804,13 +804,13 @@ func TestAddGatewaysForBackendTLSPoliciesAncestorLimit(t *testing.T) { Source: &gatewayv1.Gateway{ ObjectMeta: metav1.ObjectMeta{Name: "gateway1", Namespace: "test"}, }, - Conditions: conditions.Conditions{}, // Start with empty conditions + Conditions: []conditions.Condition{}, // Start with empty conditions }, {Namespace: "test", Name: "gateway2"}: { Source: &gatewayv1.Gateway{ ObjectMeta: metav1.ObjectMeta{Name: "gateway2", Namespace: "test"}, }, - Conditions: conditions.Conditions{}, // Start with empty conditions + Conditions: []conditions.Condition{}, // Start with empty conditions }, } diff --git a/internal/controller/state/graph/gateway.go b/internal/controller/state/graph/gateway.go index 1a4839e8fe..82d4a983e2 100644 --- a/internal/controller/state/graph/gateway.go +++ b/internal/controller/state/graph/gateway.go @@ -29,7 +29,7 @@ type Gateway struct { // Listeners include the listeners of the Gateway. Listeners []*Listener // Conditions holds the conditions for the Gateway. - Conditions conditions.Conditions + Conditions []conditions.Condition // Policies holds the policies attached to the Gateway. Policies []*Policy // Valid indicates whether the Gateway Spec is valid. @@ -127,8 +127,8 @@ func buildGateways( return builtGateways } -func validateGatewayParametersRef(npCfg *NginxProxy, ref v1.LocalParametersReference) conditions.Conditions { - var conds conditions.Conditions +func validateGatewayParametersRef(npCfg *NginxProxy, ref v1.LocalParametersReference) []conditions.Condition { + var conds []conditions.Condition path := field.NewPath("spec.infrastructure.parametersRef") @@ -170,8 +170,8 @@ func validateGatewayParametersRef(npCfg *NginxProxy, ref v1.LocalParametersRefer return conds } -func validateGateway(gw *v1.Gateway, gc *GatewayClass, npCfg *NginxProxy) (conditions.Conditions, bool) { - var conds conditions.Conditions +func validateGateway(gw *v1.Gateway, gc *GatewayClass, npCfg *NginxProxy) ([]conditions.Condition, bool) { + var conds []conditions.Condition if gc == nil { conds = append(conds, conditions.NewGatewayInvalid("GatewayClass doesn't exist")...) @@ -179,9 +179,6 @@ func validateGateway(gw *v1.Gateway, gc *GatewayClass, npCfg *NginxProxy) (condi conds = append(conds, conditions.NewGatewayInvalid("GatewayClass is invalid")...) } - // Validate unsupported fields Gateway fields - conds = append(conds, validateUnsupportedGatewayFields(gw)...) - // Set the unaccepted conditions here, because those make the gateway invalid. We set the unprogrammed conditions // elsewhere, because those do not make the gateway invalid. for _, address := range gw.Spec.Addresses { @@ -194,7 +191,10 @@ func validateGateway(gw *v1.Gateway, gc *GatewayClass, npCfg *NginxProxy) (condi // we evaluate validity before validating parametersRef because an invalid parametersRef/NginxProxy does not // invalidate the entire Gateway. - valid := conds.CountInvalid() == 0 + valid := len(conds) == 0 + + // Validate unsupported fields - these are warnings, don't affect validity + conds = append(conds, validateUnsupportedGatewayFields(gw)...) if gw.Spec.Infrastructure != nil && gw.Spec.Infrastructure.ParametersRef != nil { paramConds := validateGatewayParametersRef(npCfg, *gw.Spec.Infrastructure.ParametersRef) @@ -271,8 +271,8 @@ func (g *Gateway) collectSnippetsFiltersFromRoute( } } -func validateUnsupportedGatewayFields(gw *v1.Gateway) conditions.Conditions { - var conds conditions.Conditions +func validateUnsupportedGatewayFields(gw *v1.Gateway) []conditions.Condition { + var conds []conditions.Condition if gw.Spec.AllowedListeners != nil { conds = append(conds, conditions.NewGatewayUnsupportedField("AllowedListeners are not supported")) diff --git a/internal/controller/state/graph/gateway_listener.go b/internal/controller/state/graph/gateway_listener.go index 78d77f3831..3f6e69587e 100644 --- a/internal/controller/state/graph/gateway_listener.go +++ b/internal/controller/state/graph/gateway_listener.go @@ -36,7 +36,7 @@ type Listener struct { // Only applicable for HTTPS listeners. ResolvedSecret *types.NamespacedName // Conditions holds the conditions of the Listener. - Conditions conditions.Conditions + Conditions []conditions.Condition // SupportedKinds is the list of RouteGroupKinds allowed by the listener. SupportedKinds []v1.RouteGroupKind // Valid shows whether the Listener is valid. @@ -94,7 +94,7 @@ func newListenerConfiguratorFactory( return &listenerConfiguratorFactory{ unsupportedProtocol: &listenerConfigurator{ validators: []listenerValidator{ - func(listener v1.Listener) (conditions.Conditions, bool) { + func(listener v1.Listener) ([]conditions.Condition, bool) { valErr := field.NotSupported( field.NewPath("protocol"), listener.Protocol, @@ -148,7 +148,7 @@ func newListenerConfiguratorFactory( // listenerValidator validates a listener. If the listener is invalid, the validator will return appropriate conditions. // It also returns whether the listener is attachable, which is independent of whether the listener is valid. -type listenerValidator func(v1.Listener) (conds conditions.Conditions, attachable bool) +type listenerValidator func(v1.Listener) (conds []conditions.Condition, attachable bool) // listenerConflictResolver resolves conflicts between listeners. In case of a conflict, the resolver will make // the conflicting listeners invalid - i.e. it will modify the passed listener and the previously processed conflicting @@ -173,7 +173,7 @@ type listenerConfigurator struct { } func (c *listenerConfigurator) configure(listener v1.Listener, gwNSName types.NamespacedName) *Listener { - var conds conditions.Conditions + var conds []conditions.Condition attachable := true @@ -230,7 +230,7 @@ func (c *listenerConfigurator) configure(listener v1.Listener, gwNSName types.Na return l } -func validateListenerHostname(listener v1.Listener) (conds conditions.Conditions, attachable bool) { +func validateListenerHostname(listener v1.Listener) (conds []conditions.Condition, attachable bool) { if listener.Hostname == nil { return nil, true } @@ -253,10 +253,10 @@ func validateListenerHostname(listener v1.Listener) (conds conditions.Conditions // The supported kinds are determined based on the listener's allowedRoutes field. // If the listener does not specify allowedRoutes, listener determines allowed routes based on its protocol. func getAndValidateListenerSupportedKinds(listener v1.Listener) ( - conditions.Conditions, + []conditions.Condition, []v1.RouteGroupKind, ) { - var conds conditions.Conditions + var conds []conditions.Condition var supportedKinds []v1.RouteGroupKind var validKinds []v1.RouteGroupKind @@ -315,7 +315,7 @@ func getAndValidateListenerSupportedKinds(listener v1.Listener) ( return conds, validKinds } -func validateListenerAllowedRouteKind(listener v1.Listener) (conds conditions.Conditions, attachable bool) { +func validateListenerAllowedRouteKind(listener v1.Listener) (conds []conditions.Condition, attachable bool) { conds, _ = getAndValidateListenerSupportedKinds(listener) return conds, len(conds) == 0 } @@ -325,7 +325,7 @@ func getListenerSupportedKinds(listener v1.Listener) []v1.RouteGroupKind { return sk } -func validateListenerLabelSelector(listener v1.Listener) (conds conditions.Conditions, attachable bool) { +func validateListenerLabelSelector(listener v1.Listener) (conds []conditions.Condition, attachable bool) { if listener.AllowedRoutes != nil && listener.AllowedRoutes.Namespaces != nil && listener.AllowedRoutes.Namespaces.From != nil && @@ -339,7 +339,7 @@ func validateListenerLabelSelector(listener v1.Listener) (conds conditions.Condi } func createHTTPListenerValidator(protectedPorts ProtectedPorts) listenerValidator { - return func(listener v1.Listener) (conds conditions.Conditions, attachable bool) { + return func(listener v1.Listener) (conds []conditions.Condition, attachable bool) { if err := validateListenerPort(listener.Port, protectedPorts); err != nil { path := field.NewPath("port") valErr := field.Invalid(path, listener.Port, err.Error()) @@ -368,7 +368,7 @@ func validateListenerPort(port v1.PortNumber, protectedPorts ProtectedPorts) err return nil } -func validateTLSFieldOnTLSListener(listener v1.Listener) (conds conditions.Conditions, attachable bool) { +func validateTLSFieldOnTLSListener(listener v1.Listener) (conds []conditions.Condition, attachable bool) { tlspath := field.NewPath("TLS") if listener.TLS == nil { valErr := field.Required(tlspath, "tls must be defined for TLS listener") @@ -382,7 +382,7 @@ func validateTLSFieldOnTLSListener(listener v1.Listener) (conds conditions.Condi } func createHTTPSListenerValidator(protectedPorts ProtectedPorts) listenerValidator { - return func(listener v1.Listener) (conds conditions.Conditions, attachable bool) { + return func(listener v1.Listener) (conds []conditions.Condition, attachable bool) { if err := validateListenerPort(listener.Port, protectedPorts); err != nil { path := field.NewPath("port") valErr := field.Invalid(path, listener.Port, err.Error()) diff --git a/internal/controller/state/graph/gateway_listener_test.go b/internal/controller/state/graph/gateway_listener_test.go index f3c7937e58..804d4638b3 100644 --- a/internal/controller/state/graph/gateway_listener_test.go +++ b/internal/controller/state/graph/gateway_listener_test.go @@ -20,7 +20,7 @@ func TestValidateHTTPListener(t *testing.T) { tests := []struct { l v1.Listener name string - expected conditions.Conditions + expected []conditions.Condition }{ { l: v1.Listener{ @@ -101,7 +101,7 @@ func TestValidateHTTPSListener(t *testing.T) { tests := []struct { l v1.Listener name string - expected conditions.Conditions + expected []conditions.Condition }{ { l: v1.Listener{ @@ -607,7 +607,7 @@ func TestValidateTLSFieldOnTLSListener(t *testing.T) { tests := []struct { listener v1.Listener msg string - expectedCond conditions.Conditions + expectedCond []conditions.Condition expectValid bool }{ { diff --git a/internal/controller/state/graph/gateway_test.go b/internal/controller/state/graph/gateway_test.go index a8628c2054..8584d03d99 100644 --- a/internal/controller/state/graph/gateway_test.go +++ b/internal/controller/state/graph/gateway_test.go @@ -605,7 +605,7 @@ func TestBuildGateway(t *testing.T) { Port: helpers.GetPointer(int32(90)), }, }, - Conditions: conditions.Conditions{conditions.NewGatewayResolvedRefs()}, + Conditions: []conditions.Condition{conditions.NewGatewayResolvedRefs()}, }, }, name: "valid http listener with valid NginxProxy; GatewayClass has no NginxProxy", @@ -651,7 +651,7 @@ func TestBuildGateway(t *testing.T) { Port: helpers.GetPointer(int32(90)), }, }, - Conditions: conditions.Conditions{conditions.NewGatewayResolvedRefs()}, + Conditions: []conditions.Condition{conditions.NewGatewayResolvedRefs()}, }, }, name: "valid http listener with valid NginxProxy; GatewayClass has valid NginxProxy too", @@ -1335,7 +1335,7 @@ func TestBuildGateway(t *testing.T) { Name: controller.CreateNginxResourceName("gateway1", gcName), }, Valid: true, // invalid parametersRef does not invalidate Gateway. - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewGatewayRefInvalid( "spec.infrastructure.parametersRef.kind: Unsupported value: \"Invalid\": " + "supported values: \"NginxProxy\"", @@ -1378,7 +1378,7 @@ func TestBuildGateway(t *testing.T) { Name: controller.CreateNginxResourceName("gateway1", gcName), }, Valid: true, // invalid parametersRef does not invalidate Gateway. - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewGatewayRefNotFound(), conditions.NewGatewayInvalidParameters( "spec.infrastructure.parametersRef.name: Not found: \"does-not-exist\"", @@ -1424,7 +1424,7 @@ func TestBuildGateway(t *testing.T) { }, Valid: false, }, - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewGatewayRefInvalid("somePath: Required value: someField"), conditions.NewGatewayInvalidParameters("somePath: Required value: someField"), }, @@ -1484,7 +1484,7 @@ func TestBuildGateway(t *testing.T) { Name: controller.CreateNginxResourceName("gateway-addr-unspecified", gcName), }, Valid: false, - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewGatewayUnsupportedAddress("AddressType must be specified"), }, }, @@ -1511,7 +1511,7 @@ func TestBuildGateway(t *testing.T) { Name: controller.CreateNginxResourceName("gateway-addr-unsupported", gcName), }, Valid: false, - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewGatewayUnsupportedAddress("Only AddressType IPAddress is supported"), }, }, @@ -1560,7 +1560,7 @@ func TestBuildGateway(t *testing.T) { Port: helpers.GetPointer(int32(90)), }, }, - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewGatewayUnsupportedField("AllowedListeners are not supported"), conditions.NewGatewayResolvedRefs(), }, @@ -1602,7 +1602,7 @@ func TestBuildGateway(t *testing.T) { EffectiveNginxProxy: &EffectiveNginxProxy{ IPFamily: helpers.GetPointer(ngfAPIv1alpha2.Dual), }, - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewGatewayUnsupportedField("BackendTLS is not supported"), conditions.NewGatewayRefInvalid( "spec.infrastructure.parametersRef.kind: Unsupported value: \"wrong-kind\": supported values: \"NginxProxy\"", @@ -1649,14 +1649,14 @@ func TestValidateGatewayParametersRef(t *testing.T) { name string np *NginxProxy ref v1.LocalParametersReference - expConds conditions.Conditions + expConds []conditions.Condition }{ { name: "unsupported parameter ref kind", ref: v1.LocalParametersReference{ Kind: "wrong-kind", }, - expConds: conditions.Conditions{ + expConds: []conditions.Condition{ conditions.NewGatewayRefInvalid( "spec.infrastructure.parametersRef.kind: Unsupported value: \"wrong-kind\": " + "supported values: \"NginxProxy\"", @@ -1674,7 +1674,7 @@ func TestValidateGatewayParametersRef(t *testing.T) { Kind: kinds.NginxProxy, Name: "np", }, - expConds: conditions.Conditions{ + expConds: []conditions.Condition{ conditions.NewGatewayRefNotFound(), conditions.NewGatewayInvalidParameters("spec.infrastructure.parametersRef.name: Not found: \"np\""), }, @@ -1693,7 +1693,7 @@ func TestValidateGatewayParametersRef(t *testing.T) { Kind: kinds.NginxProxy, Name: "np", }, - expConds: conditions.Conditions{ + expConds: []conditions.Condition{ conditions.NewGatewayRefInvalid("somePath: Required value: someField"), conditions.NewGatewayInvalidParameters("somePath: Required value: someField"), }, @@ -1709,7 +1709,7 @@ func TestValidateGatewayParametersRef(t *testing.T) { Kind: kinds.NginxProxy, Name: "np", }, - expConds: conditions.Conditions{ + expConds: []conditions.Condition{ conditions.NewGatewayResolvedRefs(), }, }, @@ -1929,7 +1929,7 @@ func TestValidateUnsupportedGatewayFields(t *testing.T) { tests := []struct { name string gateway *v1.Gateway - expectedConds conditions.Conditions + expectedConds []conditions.Condition }{ { name: "No unsupported fields", @@ -1945,7 +1945,7 @@ func TestValidateUnsupportedGatewayFields(t *testing.T) { AllowedListeners: &v1.AllowedListeners{}, }, }, - expectedConds: conditions.Conditions{ + expectedConds: []conditions.Condition{ conditions.NewGatewayUnsupportedField("AllowedListeners are not supported"), }, }, @@ -1957,7 +1957,7 @@ func TestValidateUnsupportedGatewayFields(t *testing.T) { BackendTLS: &v1.GatewayBackendTLS{}, }, }, - expectedConds: conditions.Conditions{ + expectedConds: []conditions.Condition{ conditions.NewGatewayUnsupportedField("AllowedListeners are not supported"), conditions.NewGatewayUnsupportedField("BackendTLS is not supported"), }, diff --git a/internal/controller/state/graph/gatewayclass.go b/internal/controller/state/graph/gatewayclass.go index 0152f08004..e396395441 100644 --- a/internal/controller/state/graph/gatewayclass.go +++ b/internal/controller/state/graph/gatewayclass.go @@ -37,7 +37,7 @@ type GatewayClass struct { // NginxProxy is the NginxProxy resource referenced by this GatewayClass. NginxProxy *NginxProxy // Conditions include Conditions for the GatewayClass. - Conditions conditions.Conditions + Conditions []conditions.Condition // Valid shows whether the GatewayClass is valid. Valid bool } @@ -115,7 +115,7 @@ func getNginxProxyForGatewayClass( return nps[npName] } -func validateGatewayClassParametersRef(path *field.Path, ref v1.ParametersReference) conditions.Conditions { +func validateGatewayClassParametersRef(path *field.Path, ref v1.ParametersReference) []conditions.Condition { var errs field.ErrorList if _, ok := supportedParamKinds[string(ref.Kind)]; !ok { @@ -131,7 +131,7 @@ func validateGatewayClassParametersRef(path *field.Path, ref v1.ParametersRefere if len(errs) > 0 { msg := errs.ToAggregate().Error() - return conditions.Conditions{ + return []conditions.Condition{ conditions.NewGatewayClassRefInvalid(msg), conditions.NewGatewayClassInvalidParameters(msg), } @@ -144,8 +144,8 @@ func validateGatewayClass( gc *v1.GatewayClass, npCfg *NginxProxy, crdVersions map[types.NamespacedName]*metav1.PartialObjectMetadata, -) (conditions.Conditions, bool) { - var conds conditions.Conditions +) ([]conditions.Condition, bool) { + var conds []conditions.Condition supportedVersionConds, versionsValid := validateCRDVersions(crdVersions) conds = append(conds, supportedVersionConds...) @@ -198,7 +198,7 @@ type apiVersion struct { func validateCRDVersions( crdMetadata map[types.NamespacedName]*metav1.PartialObjectMetadata, -) (conds conditions.Conditions, valid bool) { +) (conds []conditions.Condition, valid bool) { installedAPIVersions := getBundleVersions(crdMetadata) supportedAPIVersion := parseVersionString(SupportedVersion) diff --git a/internal/controller/state/graph/gatewayclass_test.go b/internal/controller/state/graph/gatewayclass_test.go index aeadd996e1..b7b01f9d15 100644 --- a/internal/controller/state/graph/gatewayclass_test.go +++ b/internal/controller/state/graph/gatewayclass_test.go @@ -213,7 +213,7 @@ func TestBuildGatewayClass(t *testing.T) { expected: &GatewayClass{ Source: gcWithParams, Valid: true, - Conditions: conditions.Conditions{conditions.NewGatewayClassResolvedRefs()}, + Conditions: []conditions.Condition{conditions.NewGatewayClassResolvedRefs()}, NginxProxy: &NginxProxy{ Valid: true, Source: np, @@ -226,7 +226,7 @@ func TestBuildGatewayClass(t *testing.T) { expected: &GatewayClass{ Source: gcWithParamsNoNamespace, Valid: true, - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewGatewayClassRefInvalid( "spec.parametersRef.namespace: Required value: ParametersRef must specify Namespace", ), @@ -242,7 +242,7 @@ func TestBuildGatewayClass(t *testing.T) { expected: &GatewayClass{ Source: gcWithInvalidKind, Valid: true, - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewGatewayClassRefInvalid( "spec.parametersRef.kind: Unsupported value: \"Invalid\": supported values: \"NginxProxy\"", ), @@ -258,7 +258,7 @@ func TestBuildGatewayClass(t *testing.T) { expected: &GatewayClass{ Source: gcWithParams, Valid: true, - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewGatewayClassRefNotFound(), conditions.NewGatewayClassInvalidParameters( "spec.parametersRef.name: Not found: \"nginx-proxy\"", @@ -289,7 +289,7 @@ func TestBuildGatewayClass(t *testing.T) { expected: &GatewayClass{ Source: gcWithParams, Valid: true, - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewGatewayClassRefInvalid( "[spec.telemetry.serviceName: Invalid value: \"my-svc\": error" + ", spec.telemetry.exporter.endpoint: Invalid value: \"my-endpoint\": error]", @@ -363,7 +363,7 @@ func TestValidateCRDVersions(t *testing.T) { tests := []struct { crds map[types.NamespacedName]*metav1.PartialObjectMetadata name string - expConds conditions.Conditions + expConds []conditions.Condition valid bool }{ { diff --git a/internal/controller/state/graph/graph_test.go b/internal/controller/state/graph/graph_test.go index 8cfce2fa9f..ac5cfff3a2 100644 --- a/internal/controller/state/graph/graph_test.go +++ b/internal/controller/state/graph/graph_test.go @@ -49,7 +49,7 @@ func TestBuildGraph(t *testing.T) { }, } - btpAcceptedConds := conditions.Conditions{ + btpAcceptedConds := []conditions.Condition{ conditions.NewBackendTLSPolicyResolvedRefs(), conditions.NewPolicyAccepted(), conditions.NewPolicyAccepted(), @@ -767,7 +767,7 @@ func TestBuildGraph(t *testing.T) { Rules: []RouteRule{createValidRuleWithBackendRefsAndFilters(routeMatches, RouteTypeHTTP)}, }, Policies: []*Policy{processedRoutePolicy}, - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewClientSettingsPolicyAffected(), }, } @@ -786,7 +786,7 @@ func TestBuildGraph(t *testing.T) { Attachment: &ParentRefAttachmentStatus{ AcceptedHostnames: map[string][]string{}, Attached: false, - FailedConditions: conditions.Conditions{conditions.NewRouteNotAllowedByListeners()}, + FailedConditions: []conditions.Condition{conditions.NewRouteNotAllowedByListeners()}, }, SectionName: &gw1.Source.Spec.Listeners[0].Name, }, @@ -799,7 +799,7 @@ func TestBuildGraph(t *testing.T) { Attachment: &ParentRefAttachmentStatus{ AcceptedHostnames: map[string][]string{}, Attached: false, - FailedConditions: conditions.Conditions{conditions.NewRouteNotAllowedByListeners()}, + FailedConditions: []conditions.Condition{conditions.NewRouteNotAllowedByListeners()}, }, SectionName: &gw1.Source.Spec.Listeners[1].Name, }, @@ -868,7 +868,7 @@ func TestBuildGraph(t *testing.T) { Attachment: &ParentRefAttachmentStatus{ Attached: false, AcceptedHostnames: map[string][]string{}, - FailedConditions: conditions.Conditions{conditions.NewRouteNotAllowedByListeners()}, + FailedConditions: []conditions.Condition{conditions.NewRouteNotAllowedByListeners()}, }, SectionName: &gw1.Source.Spec.Listeners[0].Name, }, @@ -881,7 +881,7 @@ func TestBuildGraph(t *testing.T) { Attachment: &ParentRefAttachmentStatus{ AcceptedHostnames: map[string][]string{}, Attached: false, - FailedConditions: conditions.Conditions{conditions.NewRouteNotAllowedByListeners()}, + FailedConditions: []conditions.Condition{conditions.NewRouteNotAllowedByListeners()}, }, SectionName: &gw1.Source.Spec.Listeners[1].Name, }, @@ -894,7 +894,7 @@ func TestBuildGraph(t *testing.T) { Attachment: &ParentRefAttachmentStatus{ Attached: false, AcceptedHostnames: map[string][]string{}, - FailedConditions: conditions.Conditions{conditions.NewRouteHostnameConflict()}, + FailedConditions: []conditions.Condition{conditions.NewRouteHostnameConflict()}, }, SectionName: &gw1.Source.Spec.Listeners[2].Name, }, @@ -907,7 +907,7 @@ func TestBuildGraph(t *testing.T) { Attachment: &ParentRefAttachmentStatus{ Attached: false, AcceptedHostnames: map[string][]string{}, - FailedConditions: conditions.Conditions{conditions.NewRouteHostnameConflict()}, + FailedConditions: []conditions.Condition{conditions.NewRouteHostnameConflict()}, }, SectionName: &gw1.Source.Spec.Listeners[3].Name, }, @@ -1002,7 +1002,7 @@ func TestBuildGraph(t *testing.T) { GatewayClass: &GatewayClass{ Source: gc, Valid: true, - Conditions: conditions.Conditions{conditions.NewGatewayClassResolvedRefs()}, + Conditions: []conditions.Condition{conditions.NewGatewayClassResolvedRefs()}, NginxProxy: &NginxProxy{ Source: npGlobal, Valid: true, @@ -1085,7 +1085,7 @@ func TestBuildGraph(t *testing.T) { ErrorLevel: helpers.GetPointer(ngfAPIv1alpha2.NginxLogLevelError), }, }, - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewGatewayResolvedRefs(), conditions.NewClientSettingsPolicyAffected(), }, @@ -1164,7 +1164,7 @@ func TestBuildGraph(t *testing.T) { }, IPFamily: helpers.GetPointer(ngfAPIv1alpha2.IPv6), }, - Conditions: conditions.Conditions{conditions.NewGatewayResolvedRefs()}, + Conditions: []conditions.Condition{conditions.NewGatewayResolvedRefs()}, DeploymentName: types.NamespacedName{ Namespace: "test", Name: "gateway-2-my-class", diff --git a/internal/controller/state/graph/grpcroute.go b/internal/controller/state/graph/grpcroute.go index 25ee1b5b94..3cfbc3c76e 100644 --- a/internal/controller/state/graph/grpcroute.go +++ b/internal/controller/state/graph/grpcroute.go @@ -235,7 +235,7 @@ func processGRPCRouteRules( specRules []v1.GRPCRouteRule, validator validation.HTTPFieldsValidator, resolveExtRefFunc resolveExtRefFilter, -) (rules []RouteRule, valid bool, conds conditions.Conditions) { +) (rules []RouteRule, valid bool, conds []conditions.Condition) { rules = make([]RouteRule, len(specRules)) var ( @@ -262,7 +262,7 @@ func processGRPCRouteRules( rules[i] = rr } - conds = make(conditions.Conditions, 0, 2) + conds = make([]conditions.Condition, 0, 2) valid = true // add warning condition for unsupported fields if any @@ -458,21 +458,18 @@ func validateGRPCHeaderMatch( } func checkForUnsupportedGRPCFields(rule v1.GRPCRouteRule, rulePath *field.Path) field.ErrorList { - supportedFields := []string{"GRPCBackendRef", "GRPCRouteMatch", "GRPCRouteFilter"} var ruleErrors field.ErrorList if rule.Name != nil { ruleErrors = append(ruleErrors, field.Forbidden( rulePath.Child("name"), - "NGINX Gateway Fabric does not support \"SectionName\" field at the moment, supported fields are: "+ - strings.Join(supportedFields, ", "), + "NGINX Gateway Fabric does not support \"SectionName\" field at the moment", )) } if rule.SessionPersistence != nil { ruleErrors = append(ruleErrors, field.Forbidden( rulePath.Child("sessionPersistence"), - "NGINX Gateway Fabric does not support \"SessionPersistence\" field at the moment, supported fields are: "+ - strings.Join(supportedFields, ", "), + "NGINX Gateway Fabric does not support \"SessionPersistence\" field at the moment", )) } diff --git a/internal/controller/state/graph/grpcroute_test.go b/internal/controller/state/graph/grpcroute_test.go index c7717af1ee..2e664241dd 100644 --- a/internal/controller/state/graph/grpcroute_test.go +++ b/internal/controller/state/graph/grpcroute_test.go @@ -344,6 +344,46 @@ func TestBuildGRPCRoute(t *testing.T) { []v1.GRPCRouteRule{methodMatchRule, headersMatchInvalid}, ) + grValidWithUnsupportedField := createGRPCRoute( + "gr-valid-unsupported", + gatewayNsName.Name, + "example.com", + []v1.GRPCRouteRule{ + { + Name: helpers.GetPointer[v1.SectionName]("unsupported-name"), + Matches: []v1.GRPCRouteMatch{ + { + Method: &v1.GRPCMethodMatch{ + Type: helpers.GetPointer(v1.GRPCMethodMatchExact), + Service: helpers.GetPointer("myService"), + Method: helpers.GetPointer("myMethod"), + }, + }, + }, + }, + }, + ) + + grInvalidWithUnsupportedField := createGRPCRoute( + "gr-invalid-unsupported", + gatewayNsName.Name, + "example.com", + []v1.GRPCRouteRule{ + { + Name: helpers.GetPointer[v1.SectionName]("unsupported-name"), + Matches: []v1.GRPCRouteMatch{ + { + Method: &v1.GRPCMethodMatch{ + Type: helpers.GetPointer(v1.GRPCMethodMatchExact), + Service: helpers.GetPointer(""), + Method: helpers.GetPointer(""), + }, + }, + }, + }, + }, + ) + grDuplicateSectionName := createGRPCRoute( "gr", gatewayNsName.Name, @@ -646,7 +686,7 @@ func TestBuildGRPCRoute(t *testing.T) { SectionName: grInvalidMatchesEmptyMethodFields.Spec.ParentRefs[0].SectionName, }, }, - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewRouteUnsupportedValue( `All rules are invalid: ` + `[spec.rules[0].matches[0].method.type: Unsupported value: "": supported values: "Exact",` + @@ -690,7 +730,7 @@ func TestBuildGRPCRoute(t *testing.T) { SectionName: grInvalidMatchesInvalidMethodFields.Spec.ParentRefs[0].SectionName, }, }, - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewRouteUnsupportedValue( `All rules are invalid: ` + `[spec.rules[0].matches[0].method.service: Invalid value: "service{}": invalid path value,` + @@ -738,7 +778,7 @@ func TestBuildGRPCRoute(t *testing.T) { SectionName: grOneInvalid.Spec.ParentRefs[0].SectionName, }, }, - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewRoutePartiallyInvalid( `spec.rules[1].matches[0].headers[0].type: Unsupported value: "": supported values: "Exact", "RegularExpression"`, ), @@ -784,7 +824,7 @@ func TestBuildGRPCRoute(t *testing.T) { SectionName: grInvalidHeadersInvalidType.Spec.ParentRefs[0].SectionName, }, }, - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewRouteUnsupportedValue( `All rules are invalid: spec.rules[0].matches[0].headers[0].type: ` + `Unsupported value: "": supported values: "Exact", "RegularExpression"`, @@ -822,7 +862,7 @@ func TestBuildGRPCRoute(t *testing.T) { SectionName: grInvalidHeadersEmptyType.Spec.ParentRefs[0].SectionName, }, }, - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewRouteUnsupportedValue( `All rules are invalid: spec.rules[0].matches[0].headers[0].type: ` + `Required value: cannot be empty`, @@ -860,7 +900,7 @@ func TestBuildGRPCRoute(t *testing.T) { SectionName: grInvalidMatchesNilMethodType.Spec.ParentRefs[0].SectionName, }, }, - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewRouteUnsupportedValue( `All rules are invalid: spec.rules[0].matches[0].method.type: Required value: cannot be empty`, ), @@ -897,7 +937,7 @@ func TestBuildGRPCRoute(t *testing.T) { SectionName: grInvalidFilter.Spec.ParentRefs[0].SectionName, }, }, - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewRouteUnsupportedValue( `All rules are invalid: spec.rules[0].filters[0].type: Unsupported value: ` + `"InvalidFilter": supported values: "ResponseHeaderModifier", ` + @@ -942,7 +982,7 @@ func TestBuildGRPCRoute(t *testing.T) { SectionName: grInvalidHostname.Spec.ParentRefs[0].SectionName, }, }, - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewRouteUnsupportedValue( `spec.hostnames[0]: Invalid value: "": cannot be empty string`, ), @@ -965,7 +1005,7 @@ func TestBuildGRPCRoute(t *testing.T) { SectionName: grInvalidSnippetsFilter.Spec.ParentRefs[0].SectionName, }, }, - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewRouteUnsupportedValue( "All rules are invalid: spec.rules[0].filters[0].extensionRef: " + "Unsupported value: \"wrong\": supported values: \"gateway.nginx.org\"", @@ -1003,7 +1043,7 @@ func TestBuildGRPCRoute(t *testing.T) { SectionName: grUnresolvableSnippetsFilter.Spec.ParentRefs[0].SectionName, }, }, - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewRouteResolvedRefsInvalidFilter( "spec.rules[0].filters[0].extensionRef: Not found: " + `{"group":"gateway.nginx.org","kind":"SnippetsFilter",` + @@ -1042,7 +1082,7 @@ func TestBuildGRPCRoute(t *testing.T) { SectionName: grInvalidAndUnresolvableSnippetsFilter.Spec.ParentRefs[0].SectionName, }, }, - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewRouteUnsupportedValue( "All rules are invalid: spec.rules[0].filters[1].extensionRef: " + "Unsupported value: \"wrong\": supported values: \"gateway.nginx.org\"", @@ -1070,6 +1110,82 @@ func TestBuildGRPCRoute(t *testing.T) { }, name: "one invalid and one unresolvable snippet filter extension ref", }, + { + validator: createAllValidValidator(), + gr: grValidWithUnsupportedField, + expected: &L7Route{ + RouteType: RouteTypeGRPC, + Source: grValidWithUnsupportedField, + ParentRefs: []ParentRef{ + { + Idx: 0, + Gateway: CreateParentRefGateway(gw), + SectionName: grValidWithUnsupportedField.Spec.ParentRefs[0].SectionName, + }, + }, + Valid: true, + Attachable: true, + Spec: L7RouteSpec{ + Hostnames: grValidWithUnsupportedField.Spec.Hostnames, + Rules: []RouteRule{ + { + ValidMatches: true, + Filters: RouteRuleFilters{ + Valid: true, + Filters: []Filter{}, + }, + Matches: ConvertGRPCMatches(grValidWithUnsupportedField.Spec.Rules[0].Matches), + RouteBackendRefs: []RouteBackendRef{}, + }, + }, + }, + Conditions: []conditions.Condition{ + conditions.NewRouteUnsupportedField("There are rules with unsupported fields configurations: " + + "spec.rules[0].name: Forbidden: NGINX Gateway Fabric does not support \"SectionName\" field at the moment"), + }, + }, + name: "valid route with unsupported field", + }, + { + validator: createAllValidValidator(), + gr: grInvalidWithUnsupportedField, + expected: &L7Route{ + RouteType: RouteTypeGRPC, + Source: grInvalidWithUnsupportedField, + ParentRefs: []ParentRef{ + { + Idx: 0, + Gateway: CreateParentRefGateway(gw), + SectionName: grInvalidWithUnsupportedField.Spec.ParentRefs[0].SectionName, + }, + }, + Valid: false, + Attachable: true, + Spec: L7RouteSpec{ + Hostnames: grInvalidWithUnsupportedField.Spec.Hostnames, + Rules: []RouteRule{ + { + ValidMatches: false, + Filters: RouteRuleFilters{ + Valid: true, + Filters: []Filter{}, + }, + Matches: ConvertGRPCMatches(grInvalidWithUnsupportedField.Spec.Rules[0].Matches), + RouteBackendRefs: []RouteBackendRef{}, + }, + }, + }, + Conditions: []conditions.Condition{ + conditions.NewRouteUnsupportedField("There are rules with unsupported fields configurations: " + + "spec.rules[0].name: Forbidden: NGINX Gateway Fabric does not support \"SectionName\" field at the moment"), + conditions.NewRouteUnsupportedValue( + "All rules are invalid: [spec.rules[0].matches[0].method.service: Required value: service is required, " + + "spec.rules[0].matches[0].method.method: Required value: method is required]", + ), + }, + }, + name: "invalid route with unsupported field", + }, } gws := map[types.NamespacedName]*Gateway{ @@ -1437,7 +1553,7 @@ func TestProcessGRPCRouteRules_UnsupportedFields(t *testing.T) { tests := []struct { name string specRules []v1.GRPCRouteRule - expectedConds conditions.Conditions + expectedConds []conditions.Condition expectedWarns int expectedValid bool }{ @@ -1456,10 +1572,9 @@ func TestProcessGRPCRouteRules_UnsupportedFields(t *testing.T) { }, }, expectedValid: true, - expectedConds: conditions.Conditions{ + expectedConds: []conditions.Condition{ conditions.NewRouteUnsupportedField("There are rules with unsupported fields configurations: spec.rules[0].name: " + - "Forbidden: NGINX Gateway Fabric does not support \"SectionName\" field at the moment, supported fields are: " + - "GRPCBackendRef, GRPCRouteMatch, GRPCRouteFilter"), + "Forbidden: NGINX Gateway Fabric does not support \"SectionName\" field at the moment"), }, expectedWarns: 1, }, @@ -1474,12 +1589,11 @@ func TestProcessGRPCRouteRules_UnsupportedFields(t *testing.T) { }, }, expectedValid: true, - expectedConds: conditions.Conditions{ + expectedConds: []conditions.Condition{ conditions.NewRouteUnsupportedField("There are rules with unsupported fields configurations: " + "[spec.rules[0].name: Forbidden: NGINX Gateway Fabric does not support \"SectionName\" field at the moment, " + - "supported fields are: GRPCBackendRef, GRPCRouteMatch, GRPCRouteFilter, spec.rules[0].sessionPersistence: " + - "Forbidden: NGINX Gateway Fabric does not support \"SessionPersistence\" field at the moment, " + - "supported fields are: GRPCBackendRef, GRPCRouteMatch, GRPCRouteFilter]"), + "spec.rules[0].sessionPersistence: Forbidden: NGINX Gateway Fabric does not support \"SessionPersistence\" " + + "field at the moment]"), }, expectedWarns: 2, }, diff --git a/internal/controller/state/graph/httproute.go b/internal/controller/state/graph/httproute.go index ba532b5977..c849185207 100644 --- a/internal/controller/state/graph/httproute.go +++ b/internal/controller/state/graph/httproute.go @@ -169,13 +169,13 @@ func processHTTPRouteRule( ) (RouteRule, routeRuleErrors) { var errors routeRuleErrors - validMatches := true - unsupportedFieldsErrors := checkForUnsupportedHTTPFields(specRule, rulePath) if len(unsupportedFieldsErrors) > 0 { errors.warn = append(errors.warn, unsupportedFieldsErrors...) } + validMatches := true + for j, match := range specRule.Matches { matchPath := rulePath.Child("matches").Index(j) @@ -241,7 +241,7 @@ func processHTTPRouteRules( specRules []v1.HTTPRouteRule, validator validation.HTTPFieldsValidator, resolveExtRefFunc resolveExtRefFilter, -) (rules []RouteRule, valid bool, conds conditions.Conditions) { +) (rules []RouteRule, valid bool, conds []conditions.Condition) { rules = make([]RouteRule, len(specRules)) var ( @@ -268,7 +268,7 @@ func processHTTPRouteRules( rules[i] = rr } - conds = make(conditions.Conditions, 0, 2) + conds = make([]conditions.Condition, 0, 2) valid = true @@ -532,35 +532,30 @@ func validateFilterRewrite( } func checkForUnsupportedHTTPFields(rule v1.HTTPRouteRule, rulePath *field.Path) field.ErrorList { - supportedFields := []string{"HTTPBackendRef", "HTTPRouteMatch", "HTTPRouteFilter"} var ruleErrors field.ErrorList if rule.Name != nil { ruleErrors = append(ruleErrors, field.Forbidden( rulePath.Child("name"), - "NGINX Gateway Fabric does not support SectionName field at the moment, supported fields are: "+ - strings.Join(supportedFields, ", "), + "NGINX Gateway Fabric does not support SectionName field at the moment", )) } if rule.Timeouts != nil { ruleErrors = append(ruleErrors, field.Forbidden( rulePath.Child("timeouts"), - "NGINX Gateway Fabric does not support \"HTTPRouteTimeouts\" field at the moment, supported fields are: "+ - strings.Join(supportedFields, ", "), + "NGINX Gateway Fabric does not support \"HTTPRouteTimeouts\" field at the moment", )) } if rule.Retry != nil { ruleErrors = append(ruleErrors, field.Forbidden( rulePath.Child("retry"), - "NGINX Gateway Fabric does not support \"HTTPRouteRetry\" field at the moment, supported fields are: "+ - strings.Join(supportedFields, ", "), + "NGINX Gateway Fabric does not support \"HTTPRouteRetry\" field at the moment", )) } if rule.SessionPersistence != nil { ruleErrors = append(ruleErrors, field.Forbidden( rulePath.Child("sessionPersistence"), - "NGINX Gateway Fabric does not support \"SessionPersistence\" field at the moment, supported fields are: "+ - strings.Join(supportedFields, ", "), + "NGINX Gateway Fabric does not support \"SessionPersistence\" field at the moment", )) } diff --git a/internal/controller/state/graph/httproute_test.go b/internal/controller/state/graph/httproute_test.go index 80124a2b47..4fd0cb47f2 100644 --- a/internal/controller/state/graph/httproute_test.go +++ b/internal/controller/state/graph/httproute_test.go @@ -288,6 +288,10 @@ func TestBuildHTTPRoute(t *testing.T) { } gatewayNsName := client.ObjectKeyFromObject(gw.Source) + // Valid HTTPRoute with unsupported rule fields + hrValidWithUnsupportedField := createHTTPRoute("hr-valid-unsupported", gatewayNsName.Name, "example.com", "/") + hrValidWithUnsupportedField.Spec.Rules[0].Name = helpers.GetPointer[gatewayv1.SectionName]("unsupported-name") + // route with valid filter validFilter := gatewayv1.HTTPRouteFilter{ Type: gatewayv1.HTTPRouteFilterRequestRedirect, @@ -455,7 +459,7 @@ func TestBuildHTTPRoute(t *testing.T) { SectionName: hrInvalidMatchesEmptyPathType.Spec.ParentRefs[0].SectionName, }, }, - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewRouteUnsupportedValue( `All rules are invalid: spec.rules[0].matches[0].path.type: Required value: path type cannot be nil`, ), @@ -501,7 +505,7 @@ func TestBuildHTTPRoute(t *testing.T) { SectionName: hrInvalidMatchesEmptyPathValue.Spec.ParentRefs[0].SectionName, }, }, - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewRouteUnsupportedValue( `All rules are invalid: spec.rules[0].matches[0].path.value: Required value: path value cannot be nil`, ), @@ -544,7 +548,7 @@ func TestBuildHTTPRoute(t *testing.T) { SectionName: hrInvalidHostname.Spec.ParentRefs[0].SectionName, }, }, - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewRouteUnsupportedValue( `spec.hostnames[0]: Invalid value: "": cannot be empty string`, ), @@ -567,7 +571,7 @@ func TestBuildHTTPRoute(t *testing.T) { SectionName: hrInvalidMatches.Spec.ParentRefs[0].SectionName, }, }, - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewRouteUnsupportedValue( `All rules are invalid: spec.rules[0].matches[0].path.value: Invalid value: "/invalid": invalid path`, ), @@ -604,7 +608,7 @@ func TestBuildHTTPRoute(t *testing.T) { SectionName: hrInvalidFilters.Spec.ParentRefs[0].SectionName, }, }, - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewRouteUnsupportedValue( `All rules are invalid: spec.rules[0].filters[0].requestRedirect.hostname: ` + `Invalid value: "invalid.example.com": invalid hostname`, @@ -642,7 +646,7 @@ func TestBuildHTTPRoute(t *testing.T) { SectionName: hrDroppedInvalidMatches.Spec.ParentRefs[0].SectionName, }, }, - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewRoutePartiallyInvalid( `spec.rules[0].matches[0].path.value: Invalid value: "/invalid": invalid path`, ), @@ -689,7 +693,7 @@ func TestBuildHTTPRoute(t *testing.T) { SectionName: hrDroppedInvalidMatchesAndInvalidFilters.Spec.ParentRefs[0].SectionName, }, }, - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewRoutePartiallyInvalid( `[spec.rules[0].matches[0].path.value: Invalid value: "/invalid": invalid path, ` + `spec.rules[1].filters[0].requestRedirect.hostname: Invalid value: ` + @@ -748,7 +752,7 @@ func TestBuildHTTPRoute(t *testing.T) { SectionName: hrDroppedInvalidFilters.Spec.ParentRefs[0].SectionName, }, }, - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewRoutePartiallyInvalid( `spec.rules[1].filters[0].requestRedirect.hostname: Invalid value: ` + `"invalid.example.com": invalid hostname`, @@ -837,7 +841,7 @@ func TestBuildHTTPRoute(t *testing.T) { SectionName: hrInvalidSnippetsFilter.Spec.ParentRefs[0].SectionName, }, }, - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewRouteUnsupportedValue( "All rules are invalid: spec.rules[0].filters[0].extensionRef: " + "Unsupported value: \"wrong\": supported values: \"gateway.nginx.org\"", @@ -875,7 +879,7 @@ func TestBuildHTTPRoute(t *testing.T) { SectionName: hrUnresolvableSnippetsFilter.Spec.ParentRefs[0].SectionName, }, }, - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewRouteResolvedRefsInvalidFilter( "spec.rules[0].filters[0].extensionRef: Not found: " + `{"group":"gateway.nginx.org","kind":"SnippetsFilter",` + @@ -914,7 +918,7 @@ func TestBuildHTTPRoute(t *testing.T) { SectionName: hrInvalidAndUnresolvableSnippetsFilter.Spec.ParentRefs[0].SectionName, }, }, - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewRouteUnsupportedValue( "All rules are invalid: spec.rules[0].filters[0].extensionRef: " + "Unsupported value: \"wrong\": supported values: \"gateway.nginx.org\"", @@ -944,6 +948,42 @@ func TestBuildHTTPRoute(t *testing.T) { }, name: "rule with one invalid and one unresolvable snippets filter extension ref filter", }, + { + validator: &validationfakes.FakeHTTPFieldsValidator{}, + hr: hrValidWithUnsupportedField, + expected: &L7Route{ + RouteType: RouteTypeHTTP, + Source: hrValidWithUnsupportedField, + ParentRefs: []ParentRef{ + { + Idx: 0, + Gateway: CreateParentRefGateway(gw), + SectionName: hrValidWithUnsupportedField.Spec.ParentRefs[0].SectionName, + }, + }, + Valid: true, + Attachable: true, + Spec: L7RouteSpec{ + Hostnames: hrValidWithUnsupportedField.Spec.Hostnames, + Rules: []RouteRule{ + { + ValidMatches: true, + Filters: RouteRuleFilters{ + Valid: true, + Filters: []Filter{}, + }, + Matches: hrValidWithUnsupportedField.Spec.Rules[0].Matches, + RouteBackendRefs: []RouteBackendRef{expRouteBackendRef}, + }, + }, + }, + Conditions: []conditions.Condition{ + conditions.NewRouteUnsupportedField("There are rules with unsupported fields configurations: " + + "spec.rules[0].name: Forbidden: NGINX Gateway Fabric does not support SectionName field at the moment"), + }, + }, + name: "valid route with unsupported field", + }, } gws := map[types.NamespacedName]*Gateway{ @@ -1722,7 +1762,7 @@ func TestProcessHTTPRouteRules_UnsupportedFields(t *testing.T) { tests := []struct { name string specRules []gatewayv1.HTTPRouteRule - expectedConds conditions.Conditions + expectedConds []conditions.Condition expectedWarns int expectedValid bool }{ @@ -1741,10 +1781,9 @@ func TestProcessHTTPRouteRules_UnsupportedFields(t *testing.T) { }, }, expectedValid: true, - expectedConds: conditions.Conditions{ + expectedConds: []conditions.Condition{ conditions.NewRouteUnsupportedField("There are rules with unsupported fields configurations: spec.rules[0].name: " + - "Forbidden: NGINX Gateway Fabric does not support SectionName field at the moment, supported fields are: " + - "HTTPBackendRef, HTTPRouteMatch, HTTPRouteFilter"), + "Forbidden: NGINX Gateway Fabric does not support SectionName field at the moment"), }, expectedWarns: 1, }, @@ -1763,16 +1802,13 @@ func TestProcessHTTPRouteRules_UnsupportedFields(t *testing.T) { }, }, expectedValid: true, - expectedConds: conditions.Conditions{ + expectedConds: []conditions.Condition{ conditions.NewRouteUnsupportedField("There are rules with unsupported fields configurations: " + "[spec.rules[0].name: Forbidden: NGINX Gateway Fabric does not support SectionName field at the moment, " + - "supported fields are: HTTPBackendRef, HTTPRouteMatch, HTTPRouteFilter, spec.rules[0].timeouts: Forbidden: " + - "NGINX Gateway Fabric does not support \"HTTPRouteTimeouts\" field at the moment, supported fields are: " + - "HTTPBackendRef, HTTPRouteMatch, HTTPRouteFilter, spec.rules[0].retry: Forbidden: NGINX Gateway Fabric " + - "does not support \"HTTPRouteRetry\" field at the moment, supported fields are: HTTPBackendRef, " + - "HTTPRouteMatch, HTTPRouteFilter, spec.rules[0].sessionPersistence: Forbidden: NGINX Gateway Fabric " + - "does not support \"SessionPersistence\" field at the moment, supported fields are: HTTPBackendRef, " + - "HTTPRouteMatch, HTTPRouteFilter]"), + "spec.rules[0].timeouts: Forbidden: NGINX Gateway Fabric does not support \"HTTPRouteTimeouts\" " + + "field at the moment, spec.rules[0].retry: Forbidden: NGINX Gateway Fabric does not support " + + "\"HTTPRouteRetry\" field at the moment, spec.rules[0].sessionPersistence: Forbidden: NGINX Gateway " + + "Fabric does not support \"SessionPersistence\" field at the moment]"), }, expectedWarns: 4, }, diff --git a/internal/controller/state/graph/multiple_gateways_test.go b/internal/controller/state/graph/multiple_gateways_test.go index cde7f47926..80f4a10b04 100644 --- a/internal/controller/state/graph/multiple_gateways_test.go +++ b/internal/controller/state/graph/multiple_gateways_test.go @@ -150,7 +150,7 @@ func convertedGateway( nginxProxy *NginxProxy, effectiveNp *EffectiveNginxProxy, listeners []*Listener, - conds conditions.Conditions, + conds []conditions.Condition, ) *Gateway { return &Gateway{ Source: gw, @@ -244,7 +244,7 @@ func Test_MultipleGateways_WithNginxProxy(t *testing.T) { gateway1withNP := createGateway("gateway-1", testNs, "nginx-proxy-gateway-1", []gatewayv1.Listener{}) gateway3withNP := createGateway("gateway-3", "test2", "nginx-proxy-gateway-3", []gatewayv1.Listener{}) - gcConditions := conditions.Conditions{conditions.NewGatewayClassResolvedRefs()} + gcConditions := []conditions.Condition{conditions.NewGatewayClassResolvedRefs()} tests := []struct { clusterState ClusterState @@ -618,7 +618,7 @@ func Test_MultipleGateways_WithListeners(t *testing.T) { map[L4RouteKey]*L4Route{}, ), }, - conditions.Conditions{conditions.NewGatewayClassResolvedRefs()}, + []conditions.Condition{conditions.NewGatewayClassResolvedRefs()}, ), client.ObjectKeyFromObject(gateway2): convertedGateway( gateway2, @@ -634,7 +634,7 @@ func Test_MultipleGateways_WithListeners(t *testing.T) { map[L4RouteKey]*L4Route{}, ), }, - conditions.Conditions{conditions.NewGatewayClassResolvedRefs()}, + []conditions.Condition{conditions.NewGatewayClassResolvedRefs()}, ), }, Routes: map[RouteKey]*L7Route{}, @@ -707,7 +707,7 @@ func Test_MultipleGateways_WithListeners(t *testing.T) { map[L4RouteKey]*L4Route{}, ), }, - conditions.Conditions{conditions.NewGatewayClassResolvedRefs()}, + []conditions.Condition{conditions.NewGatewayClassResolvedRefs()}, ), client.ObjectKeyFromObject(gatewayMultipleListeners2): convertedGateway( gatewayMultipleListeners2, @@ -739,7 +739,7 @@ func Test_MultipleGateways_WithListeners(t *testing.T) { map[L4RouteKey]*L4Route{}, ), }, - conditions.Conditions{conditions.NewGatewayClassResolvedRefs()}, + []conditions.Condition{conditions.NewGatewayClassResolvedRefs()}, ), client.ObjectKeyFromObject(gatewayMultipleListeners3): convertedGateway( gatewayMultipleListeners3, @@ -771,7 +771,7 @@ func Test_MultipleGateways_WithListeners(t *testing.T) { map[L4RouteKey]*L4Route{}, ), }, - conditions.Conditions{conditions.NewGatewayClassResolvedRefs()}, + []conditions.Condition{conditions.NewGatewayClassResolvedRefs()}, ), }, Routes: map[RouteKey]*L7Route{}, @@ -826,7 +826,7 @@ func Test_MultipleGateways_WithListeners(t *testing.T) { map[L4RouteKey]*L4Route{}, ), }, - conditions.Conditions{conditions.NewGatewayClassResolvedRefs()}, + []conditions.Condition{conditions.NewGatewayClassResolvedRefs()}, ), client.ObjectKeyFromObject(gatewayHTTPSSamePortHostname): convertedGateway( gatewayHTTPSSamePortHostname, @@ -842,7 +842,7 @@ func Test_MultipleGateways_WithListeners(t *testing.T) { map[L4RouteKey]*L4Route{}, ), }, - conditions.Conditions{conditions.NewGatewayClassResolvedRefs()}, + []conditions.Condition{conditions.NewGatewayClassResolvedRefs()}, ), }, Routes: map[RouteKey]*L7Route{}, diff --git a/internal/controller/state/graph/policies.go b/internal/controller/state/graph/policies.go index 55236af6a0..f790f478e0 100644 --- a/internal/controller/state/graph/policies.go +++ b/internal/controller/state/graph/policies.go @@ -32,7 +32,7 @@ type Policy struct { // Conditions holds the conditions for the Policy. // These conditions apply to the entire Policy. // The conditions in the Ancestor apply only to the Policy in regard to the Ancestor. - Conditions conditions.Conditions + Conditions []conditions.Condition // Valid indicates whether the Policy is valid. Valid bool } @@ -42,7 +42,7 @@ type PolicyAncestor struct { // Ancestor is the ancestor object. Ancestor v1.ParentReference // Conditions contains the list of conditions of the Policy in relation to the ancestor. - Conditions conditions.Conditions + Conditions []conditions.Condition } // PolicyTargetRef represents the object that the Policy is targeting. @@ -205,7 +205,7 @@ func attachPolicyToService( if !gw.Valid { policy.InvalidForGateways[gwNsName] = struct{}{} - ancestor.Conditions = conditions.Conditions{conditions.NewPolicyTargetNotFound("Parent Gateway is invalid")} + ancestor.Conditions = []conditions.Condition{conditions.NewPolicyTargetNotFound("Parent Gateway is invalid")} policy.Ancestors = append(policy.Ancestors, ancestor) continue } @@ -254,7 +254,7 @@ func attachPolicyToRoute( } if !route.Valid || !route.Attachable || len(route.ParentRefs) == 0 { - ancestor.Conditions = conditions.Conditions{conditions.NewPolicyTargetNotFound("TargetRef is invalid")} + ancestor.Conditions = []conditions.Condition{conditions.NewPolicyTargetNotFound("TargetRef is invalid")} policy.Ancestors = append(policy.Ancestors, ancestor) return } @@ -334,14 +334,14 @@ func attachPolicyToGateway( if !exists || (gw != nil && gw.Source == nil) { policy.InvalidForGateways[ref.Nsname] = struct{}{} - ancestor.Conditions = conditions.Conditions{conditions.NewPolicyTargetNotFound("TargetRef is not found")} + ancestor.Conditions = []conditions.Condition{conditions.NewPolicyTargetNotFound("TargetRef is not found")} policy.Ancestors = append(policy.Ancestors, ancestor) return } if !gw.Valid { policy.InvalidForGateways[ref.Nsname] = struct{}{} - ancestor.Conditions = conditions.Conditions{conditions.NewPolicyTargetNotFound("TargetRef is invalid")} + ancestor.Conditions = []conditions.Condition{conditions.NewPolicyTargetNotFound("TargetRef is invalid")} policy.Ancestors = append(policy.Ancestors, ancestor) return } @@ -366,7 +366,7 @@ func processPolicies( processedPolicies := make(map[PolicyKey]*Policy) for key, policy := range pols { - var conds conditions.Conditions + var conds []conditions.Condition targetRefs := make([]PolicyTargetRef, 0, len(policy.GetTargetRefs())) targetedRoutes := make(map[types.NamespacedName]*L7Route) @@ -428,8 +428,8 @@ func processPolicies( func checkTargetRoutesForOverlap( targetedRoutes map[types.NamespacedName]*L7Route, graphRoutes map[RouteKey]*L7Route, -) conditions.Conditions { - var conds conditions.Conditions +) []conditions.Condition { + var conds []conditions.Condition for _, targetedRoute := range targetedRoutes { // We need to check if this route referenced in the policy has an overlapping @@ -622,7 +622,7 @@ func addPolicyAffectedStatusToTargetRefs( } } -func addStatusToTargetRefs(policyKind string, conditionsList *conditions.Conditions) { +func addStatusToTargetRefs(policyKind string, conditionsList *[]conditions.Condition) { if conditionsList == nil { return } diff --git a/internal/controller/state/graph/policies_test.go b/internal/controller/state/graph/policies_test.go index 346646f266..e84bbd6b56 100644 --- a/internal/controller/state/graph/policies_test.go +++ b/internal/controller/state/graph/policies_test.go @@ -299,9 +299,9 @@ func TestAttachPolicyToRoute(t *testing.T) { } validatorError := &policiesfakes.FakeValidator{ - ValidateGlobalSettingsStub: func(_ policies.Policy, gs *policies.GlobalSettings) conditions.Conditions { + ValidateGlobalSettingsStub: func(_ policies.Policy, gs *policies.GlobalSettings) []conditions.Condition { if !gs.TelemetryEnabled { - return conditions.Conditions{ + return []conditions.Condition{ conditions.NewPolicyNotAcceptedNginxProxyNotSet(conditions.PolicyMessageTelemetryNotEnabled), } } @@ -361,7 +361,7 @@ func TestAttachPolicyToRoute(t *testing.T) { expAncestors: []PolicyAncestor{ { Ancestor: createExpAncestor(kinds.HTTPRoute), - Conditions: conditions.Conditions{conditions.NewPolicyTargetNotFound("TargetRef is invalid")}, + Conditions: []conditions.Condition{conditions.NewPolicyTargetNotFound("TargetRef is invalid")}, }, }, expAttached: false, @@ -374,7 +374,7 @@ func TestAttachPolicyToRoute(t *testing.T) { expAncestors: []PolicyAncestor{ { Ancestor: createExpAncestor(kinds.HTTPRoute), - Conditions: conditions.Conditions{conditions.NewPolicyTargetNotFound("TargetRef is invalid")}, + Conditions: []conditions.Condition{conditions.NewPolicyTargetNotFound("TargetRef is invalid")}, }, }, expAttached: false, @@ -387,7 +387,7 @@ func TestAttachPolicyToRoute(t *testing.T) { expAncestors: []PolicyAncestor{ { Ancestor: createExpAncestor(kinds.HTTPRoute), - Conditions: conditions.Conditions{conditions.NewPolicyTargetNotFound("TargetRef is invalid")}, + Conditions: []conditions.Condition{conditions.NewPolicyTargetNotFound("TargetRef is invalid")}, }, }, expAttached: false, @@ -447,7 +447,7 @@ func TestAttachPolicyToRoute(t *testing.T) { expAncestors: []PolicyAncestor{ { Ancestor: createExpAncestor(kinds.HTTPRoute), - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewPolicyNotAcceptedNginxProxyNotSet(conditions.PolicyMessageTelemetryNotEnabled), }, }, @@ -486,7 +486,7 @@ func TestAttachPolicyToRoute(t *testing.T) { expAncestors: []PolicyAncestor{ { Ancestor: createExpAncestor(kinds.HTTPRoute), - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewPolicyNotAcceptedNginxProxyNotSet(conditions.PolicyMessageTelemetryNotEnabled), }, }, @@ -596,7 +596,7 @@ func TestAttachPolicyToGateway(t *testing.T) { expAncestors: []PolicyAncestor{ { Ancestor: getGatewayParentRef(gateway2NsName), - Conditions: conditions.Conditions{conditions.NewPolicyTargetNotFound("TargetRef is not found")}, + Conditions: []conditions.Condition{conditions.NewPolicyTargetNotFound("TargetRef is not found")}, }, }, expAttached: false, @@ -617,7 +617,7 @@ func TestAttachPolicyToGateway(t *testing.T) { expAncestors: []PolicyAncestor{ { Ancestor: getGatewayParentRef(gatewayNsName), - Conditions: conditions.Conditions{conditions.NewPolicyTargetNotFound("TargetRef is invalid")}, + Conditions: []conditions.Condition{conditions.NewPolicyTargetNotFound("TargetRef is invalid")}, }, }, expAttached: false, @@ -811,7 +811,7 @@ func TestAttachPolicyToService(t *testing.T) { expAncestors: []PolicyAncestor{ { Ancestor: getGatewayParentRef(gwNsname), - Conditions: conditions.Conditions{conditions.NewPolicyTargetNotFound("Parent Gateway is invalid")}, + Conditions: []conditions.Condition{conditions.NewPolicyTargetNotFound("Parent Gateway is invalid")}, }, }, }, @@ -1015,9 +1015,9 @@ func TestProcessPolicies(t *testing.T) { { name: "invalid and valid policies", validator: &policiesfakes.FakeValidator{ - ValidateStub: func(policy policies.Policy) conditions.Conditions { + ValidateStub: func(policy policies.Policy) []conditions.Condition { if policy.GetName() == "pol1" { - return conditions.Conditions{conditions.NewPolicyInvalid("invalid error")} + return []conditions.Condition{conditions.NewPolicyInvalid("invalid error")} } return nil @@ -1037,7 +1037,7 @@ func TestProcessPolicies(t *testing.T) { Group: v1.GroupName, }, }, - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewPolicyInvalid("invalid error"), }, Ancestors: []PolicyAncestor{}, @@ -1093,7 +1093,7 @@ func TestProcessPolicies(t *testing.T) { Group: v1.GroupName, }, }, - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewPolicyConflicted("Conflicts with another MyPolicy"), }, Ancestors: []PolicyAncestor{}, @@ -1174,7 +1174,7 @@ func TestProcessPolicies_RouteOverlap(t *testing.T) { policies map[PolicyKey]policies.Policy routes map[RouteKey]*L7Route name string - expConditions conditions.Conditions + expConditions []conditions.Condition valid bool }{ { @@ -1231,7 +1231,7 @@ func TestProcessPolicies_RouteOverlap(t *testing.T) { }: createTestRouteWithPaths("hr2", "/coffee"), }, valid: false, - expConditions: conditions.Conditions{ + expConditions: []conditions.Condition{ { Type: "Accepted", Status: "False", @@ -1280,7 +1280,7 @@ func TestProcessPolicies_RouteOverlap(t *testing.T) { }: createTestRouteWithPaths("hr-coffee-latte", "/coffee", "/latte"), }, valid: false, - expConditions: conditions.Conditions{ + expConditions: []conditions.Condition{ { Type: "Accepted", Status: "False", @@ -1715,7 +1715,7 @@ func TestAddPolicyAffectedStatusOnTargetRefs(t *testing.T) { policies map[PolicyKey]*Policy gws map[types.NamespacedName]*Gateway routes map[RouteKey]*L7Route - expectedConditions map[types.NamespacedName]conditions.Conditions + expectedConditions map[types.NamespacedName][]conditions.Condition name string missingKeys bool }{ @@ -1735,7 +1735,7 @@ func TestAddPolicyAffectedStatusOnTargetRefs(t *testing.T) { }, gws: createGatewayMap(types.NamespacedName{Namespace: testNs, Name: "gw1"}), routes: nil, - expectedConditions: map[types.NamespacedName]conditions.Conditions{ + expectedConditions: map[types.NamespacedName][]conditions.Condition{ {Namespace: testNs, Name: "gw1"}: { conditions.NewClientSettingsPolicyAffected(), }, @@ -1755,7 +1755,7 @@ func TestAddPolicyAffectedStatusOnTargetRefs(t *testing.T) { }, gws: createGatewayMap(types.NamespacedName{Namespace: testNs, Name: "gw2"}), routes: nil, - expectedConditions: map[types.NamespacedName]conditions.Conditions{ + expectedConditions: map[types.NamespacedName][]conditions.Condition{ {Namespace: testNs, Name: "gw2"}: { conditions.NewClientSettingsPolicyAffected(), conditions.NewObservabilityPolicyAffected(), @@ -1792,7 +1792,7 @@ func TestAddPolicyAffectedStatusOnTargetRefs(t *testing.T) { }, }, }, - expectedConditions: map[types.NamespacedName]conditions.Conditions{ + expectedConditions: map[types.NamespacedName][]conditions.Condition{ {Namespace: testNs, Name: "hr1"}: { conditions.NewObservabilityPolicyAffected(), }, @@ -1838,7 +1838,7 @@ func TestAddPolicyAffectedStatusOnTargetRefs(t *testing.T) { }, }, }, - expectedConditions: map[types.NamespacedName]conditions.Conditions{ + expectedConditions: map[types.NamespacedName][]conditions.Condition{ {Namespace: testNs, Name: "gw3"}: { conditions.NewClientSettingsPolicyAffected(), conditions.NewObservabilityPolicyAffected(), @@ -1874,7 +1874,7 @@ func TestAddPolicyAffectedStatusOnTargetRefs(t *testing.T) { }, }, }, - expectedConditions: map[types.NamespacedName]conditions.Conditions{ + expectedConditions: map[types.NamespacedName][]conditions.Condition{ {Namespace: testNs, Name: "hr3"}: { conditions.NewClientSettingsPolicyAffected(), }, @@ -1898,7 +1898,7 @@ func TestAddPolicyAffectedStatusOnTargetRefs(t *testing.T) { }, }, }, - expectedConditions: map[types.NamespacedName]conditions.Conditions{ + expectedConditions: map[types.NamespacedName][]conditions.Condition{ {Namespace: testNs, Name: "invalid"}: {}, }, }, @@ -1913,7 +1913,7 @@ func TestAddPolicyAffectedStatusOnTargetRefs(t *testing.T) { gws: createGatewayMap( types.NamespacedName{Namespace: testNs, Name: "gw2"}, ), - expectedConditions: map[types.NamespacedName]conditions.Conditions{ + expectedConditions: map[types.NamespacedName][]conditions.Condition{ {Namespace: testNs, Name: "gw1"}: {}, }, missingKeys: true, @@ -1929,7 +1929,7 @@ func TestAddPolicyAffectedStatusOnTargetRefs(t *testing.T) { gws: map[types.NamespacedName]*Gateway{ {Namespace: testNs, Name: "gw1"}: nil, }, - expectedConditions: map[types.NamespacedName]conditions.Conditions{ + expectedConditions: map[types.NamespacedName][]conditions.Condition{ {Namespace: testNs, Name: "gw1"}: {}, }, missingKeys: true, @@ -1952,7 +1952,7 @@ func TestAddPolicyAffectedStatusOnTargetRefs(t *testing.T) { }, }, }, - expectedConditions: map[types.NamespacedName]conditions.Conditions{ + expectedConditions: map[types.NamespacedName][]conditions.Condition{ {Namespace: testNs, Name: "hr1"}: {}, }, missingKeys: true, @@ -2225,13 +2225,13 @@ func TestNGFPolicyAncestorLimitHandling(t *testing.T) { Source: &v1.Gateway{ ObjectMeta: metav1.ObjectMeta{Name: "gateway1", Namespace: "test"}, }, - Conditions: conditions.Conditions{}, // Start with empty conditions + Conditions: []conditions.Condition{}, // Start with empty conditions }, {Namespace: "test", Name: "gateway2"}: { Source: &v1.Gateway{ ObjectMeta: metav1.ObjectMeta{Name: "gateway2", Namespace: "test"}, }, - Conditions: conditions.Conditions{}, // Start with empty conditions + Conditions: []conditions.Condition{}, // Start with empty conditions }, } @@ -2249,7 +2249,7 @@ func TestNGFPolicyAncestorLimitHandling(t *testing.T) { // Create fake validator validator := &policiesfakes.FakeValidator{ - ValidateStub: func(_ policies.Policy) conditions.Conditions { + ValidateStub: func(_ policies.Policy) []conditions.Condition { return nil }, ConflictsStub: func(_, _ policies.Policy) bool { diff --git a/internal/controller/state/graph/route_common.go b/internal/controller/state/graph/route_common.go index 77320a78da..c11c266fb2 100644 --- a/internal/controller/state/graph/route_common.go +++ b/internal/controller/state/graph/route_common.go @@ -44,7 +44,7 @@ type ParentRefAttachmentStatus struct { // FailedConditions are the conditions that describe why the ParentRef is not attached to the Gateway, or other // failures that may lead to partial attachments. For example, a backendRef could be invalid, but the route can // still attach. The backendRef condition would be displayed here. - FailedConditions conditions.Conditions + FailedConditions []conditions.Condition // ListenerPort is the port on the Listener that the Route is attached to. // FIXME(sarthyparty): https://github.com/nginx/nginx-gateway-fabric/issues/3811 // Needs to be a map of to port number @@ -98,7 +98,7 @@ type L4Route struct { // ParentRefs describe the references to the parents in a Route. ParentRefs []ParentRef // Conditions define the conditions to be reported in the status of the Route. - Conditions conditions.Conditions + Conditions []conditions.Condition // Spec is the L4RouteSpec of the Route Spec L4RouteSpec // Valid indicates if the Route is valid. @@ -126,7 +126,7 @@ type L7Route struct { // ParentRefs describe the references to the parents in a Route. ParentRefs []ParentRef // Conditions define the conditions to be reported in the status of the Route. - Conditions conditions.Conditions + Conditions []conditions.Condition // Policies holds the policies that are attached to the Route. Policies []*Policy // Valid indicates if the Route is valid. @@ -143,15 +143,24 @@ type L7RouteSpec struct { } type RouteRule struct { - Name *v1.SectionName - Timeouts *v1.HTTPRouteTimeouts - Retry *v1.HTTPRouteRetry + // SectionName is the name of a section in a Kubernetes resource. + Name *v1.SectionName + // HTTPRouteTimeouts defines timeouts that can be configured for an HTTPRoute. + Timeouts *v1.HTTPRouteTimeouts + // HTTPRouteRetry defines retry configuration for an HTTPRoute. + Retry *v1.HTTPRouteRetry + // SessionPersistence defines the desired state of SessionPersistence. SessionPersistence *v1.SessionPersistence - Matches []v1.HTTPRouteMatch - RouteBackendRefs []RouteBackendRef - BackendRefs []BackendRef - Filters RouteRuleFilters - ValidMatches bool + // Matches define the predicate used to match requests to a given action. + Matches []v1.HTTPRouteMatch + // RouteBackendRefs are a wrapper for v1.BackendRef and any BackendRef filters from the HTTPRoute or GRPCRoute. + RouteBackendRefs []RouteBackendRef + // BackendRefs is an internal representation of a backendRef in a Route. + BackendRefs []BackendRef + // Filters define processing steps that must be completed during the request or response lifecycle. + Filters RouteRuleFilters + // ValidMatches indicates if the matches are valid and accepted by the Route. + ValidMatches bool } // RouteBackendRef is a wrapper for v1.BackendRef and any BackendRef filters from the HTTPRoute or GRPCRoute. diff --git a/internal/controller/state/graph/route_common_test.go b/internal/controller/state/graph/route_common_test.go index 93f1bcd299..6ded299d73 100644 --- a/internal/controller/state/graph/route_common_test.go +++ b/internal/controller/state/graph/route_common_test.go @@ -574,7 +574,7 @@ func TestBindRouteToListeners(t *testing.T) { name string expectedGatewayListeners []*Listener expectedSectionNameRefs []ParentRef - expectedConditions conditions.Conditions + expectedConditions []conditions.Condition }{ { route: createNormalHTTPRoute(gw), @@ -704,7 +704,7 @@ func TestBindRouteToListeners(t *testing.T) { SectionName: hrWithEmptySectionName.Spec.ParentRefs[0].SectionName, Attachment: &ParentRefAttachmentStatus{ Attached: false, - FailedConditions: conditions.Conditions{conditions.NewRouteInvalidListener()}, + FailedConditions: []conditions.Condition{conditions.NewRouteInvalidListener()}, AcceptedHostnames: map[string][]string{}, }, }, @@ -768,7 +768,7 @@ func TestBindRouteToListeners(t *testing.T) { SectionName: hrWithNonExistingListener.Spec.ParentRefs[0].SectionName, Attachment: &ParentRefAttachmentStatus{ Attached: false, - FailedConditions: conditions.Conditions{conditions.NewRouteNoMatchingParent()}, + FailedConditions: []conditions.Condition{conditions.NewRouteNoMatchingParent()}, AcceptedHostnames: map[string][]string{}, }, }, @@ -794,7 +794,7 @@ func TestBindRouteToListeners(t *testing.T) { SectionName: hr.Spec.ParentRefs[0].SectionName, Attachment: &ParentRefAttachmentStatus{ Attached: false, - FailedConditions: conditions.Conditions{conditions.NewRouteInvalidListener()}, + FailedConditions: []conditions.Condition{conditions.NewRouteInvalidListener()}, AcceptedHostnames: map[string][]string{}, }, }, @@ -820,7 +820,7 @@ func TestBindRouteToListeners(t *testing.T) { SectionName: hr.Spec.ParentRefs[0].SectionName, Attachment: &ParentRefAttachmentStatus{ Attached: false, - FailedConditions: conditions.Conditions{conditions.NewRouteNoMatchingListenerHostname()}, + FailedConditions: []conditions.Condition{conditions.NewRouteNoMatchingListenerHostname()}, AcceptedHostnames: map[string][]string{}, }, }, @@ -868,7 +868,7 @@ func TestBindRouteToListeners(t *testing.T) { SectionName: hr.Spec.ParentRefs[0].SectionName, Attachment: &ParentRefAttachmentStatus{ Attached: false, - FailedConditions: conditions.Conditions{conditions.NewRouteInvalidGateway()}, + FailedConditions: []conditions.Condition{conditions.NewRouteInvalidGateway()}, AcceptedHostnames: map[string][]string{}, }, }, @@ -913,7 +913,7 @@ func TestBindRouteToListeners(t *testing.T) { } }), }, - expectedConditions: conditions.Conditions{conditions.NewRouteInvalidListener()}, + expectedConditions: []conditions.Condition{conditions.NewRouteInvalidListener()}, name: "invalid attachable listener", }, { @@ -985,7 +985,7 @@ func TestBindRouteToListeners(t *testing.T) { } }), }, - expectedConditions: conditions.Conditions{conditions.NewRouteInvalidListener()}, + expectedConditions: []conditions.Condition{conditions.NewRouteInvalidListener()}, name: "invalid attachable listener with invalid attachable route", }, { @@ -1012,7 +1012,7 @@ func TestBindRouteToListeners(t *testing.T) { SectionName: hr.Spec.ParentRefs[0].SectionName, Attachment: &ParentRefAttachmentStatus{ Attached: false, - FailedConditions: conditions.Conditions{conditions.NewRouteNotAllowedByListeners()}, + FailedConditions: []conditions.Condition{conditions.NewRouteNotAllowedByListeners()}, AcceptedHostnames: map[string][]string{}, }, }, @@ -1101,7 +1101,7 @@ func TestBindRouteToListeners(t *testing.T) { SectionName: hr.Spec.ParentRefs[0].SectionName, Attachment: &ParentRefAttachmentStatus{ Attached: false, - FailedConditions: conditions.Conditions{conditions.NewRouteNotAllowedByListeners()}, + FailedConditions: []conditions.Condition{conditions.NewRouteNotAllowedByListeners()}, AcceptedHostnames: map[string][]string{}, }, }, @@ -1230,7 +1230,7 @@ func TestBindRouteToListeners(t *testing.T) { SectionName: gr.Spec.ParentRefs[0].SectionName, Attachment: &ParentRefAttachmentStatus{ Attached: false, - FailedConditions: conditions.Conditions{conditions.NewRouteNotAllowedByListeners()}, + FailedConditions: []conditions.Condition{conditions.NewRouteNotAllowedByListeners()}, AcceptedHostnames: map[string][]string{}, }, }, @@ -1275,7 +1275,7 @@ func TestBindRouteToListeners(t *testing.T) { SectionName: gr.Spec.ParentRefs[0].SectionName, Attachment: &ParentRefAttachmentStatus{ Attached: false, - FailedConditions: conditions.Conditions{ + FailedConditions: []conditions.Condition{ conditions.NewRouteUnsupportedConfiguration( `HTTP2 is disabled - cannot configure GRPCRoutes`, ), @@ -1360,7 +1360,7 @@ func TestBindRouteToListeners(t *testing.T) { SectionName: hr.Spec.ParentRefs[0].SectionName, Attachment: &ParentRefAttachmentStatus{ Attached: true, - FailedConditions: conditions.Conditions{ + FailedConditions: []conditions.Condition{ {Message: "invalid backend"}, }, AcceptedHostnames: map[string][]string{ @@ -1763,7 +1763,7 @@ func TestBindL4RouteToListeners(t *testing.T) { noMatchingParentAttachment := ParentRefAttachmentStatus{ AcceptedHostnames: map[string][]string{}, - FailedConditions: conditions.Conditions{conditions.NewRouteNoMatchingParent()}, + FailedConditions: []conditions.Condition{conditions.NewRouteNoMatchingParent()}, } notAttachableRoute := &L4Route{ @@ -1810,7 +1810,7 @@ func TestBindL4RouteToListeners(t *testing.T) { expectedGatewayListeners []*Listener name string expectedSectionNameRefs []ParentRef - expectedConditions conditions.Conditions + expectedConditions []conditions.Condition }{ { route: createNormalRoute(gw), @@ -1920,7 +1920,7 @@ func TestBindL4RouteToListeners(t *testing.T) { { Attachment: &ParentRefAttachmentStatus{ AcceptedHostnames: map[string][]string{}, - FailedConditions: conditions.Conditions{ + FailedConditions: []conditions.Condition{ conditions.NewRouteNoMatchingParent(), }, Attached: false, @@ -1955,7 +1955,7 @@ func TestBindL4RouteToListeners(t *testing.T) { { Attachment: &ParentRefAttachmentStatus{ AcceptedHostnames: map[string][]string{}, - FailedConditions: conditions.Conditions{conditions.NewRouteInvalidGateway()}, + FailedConditions: []conditions.Condition{conditions.NewRouteInvalidGateway()}, Attached: false, }, SectionName: tr.Spec.ParentRefs[0].SectionName, @@ -1995,7 +1995,7 @@ func TestBindL4RouteToListeners(t *testing.T) { SectionName: tr.Spec.ParentRefs[0].SectionName, Attachment: &ParentRefAttachmentStatus{ AcceptedHostnames: map[string][]string{}, - FailedConditions: conditions.Conditions{conditions.NewRouteNotAllowedByListeners()}, + FailedConditions: []conditions.Condition{conditions.NewRouteNotAllowedByListeners()}, }, }, }, @@ -2068,7 +2068,7 @@ func TestBindL4RouteToListeners(t *testing.T) { } }), }, - expectedConditions: conditions.Conditions{conditions.NewRouteInvalidListener()}, + expectedConditions: []conditions.Condition{conditions.NewRouteInvalidListener()}, name: "invalid attachable listener", }, { @@ -2093,7 +2093,7 @@ func TestBindL4RouteToListeners(t *testing.T) { SectionName: tr.Spec.ParentRefs[0].SectionName, Attachment: &ParentRefAttachmentStatus{ AcceptedHostnames: map[string][]string{}, - FailedConditions: conditions.Conditions{conditions.NewRouteNoMatchingListenerHostname()}, + FailedConditions: []conditions.Condition{conditions.NewRouteNoMatchingListenerHostname()}, }, }, }, @@ -2266,7 +2266,7 @@ func TestBindL4RouteToListeners(t *testing.T) { Gateway: &ParentRefGateway{NamespacedName: client.ObjectKeyFromObject(gw)}, Attachment: &ParentRefAttachmentStatus{ AcceptedHostnames: map[string][]string{}, - FailedConditions: conditions.Conditions{conditions.NewRouteNotAllowedByListeners()}, + FailedConditions: []conditions.Condition{conditions.NewRouteNotAllowedByListeners()}, }, SectionName: helpers.GetPointer[gatewayv1.SectionName]("listener-443"), }, @@ -2298,7 +2298,7 @@ func TestBindL4RouteToListeners(t *testing.T) { SectionName: tr.Spec.ParentRefs[0].SectionName, Attachment: &ParentRefAttachmentStatus{ Attached: true, - FailedConditions: conditions.Conditions{ + FailedConditions: []conditions.Condition{ {Message: "invalid backend"}, }, AcceptedHostnames: map[string][]string{ diff --git a/internal/controller/state/graph/snippets_filter.go b/internal/controller/state/graph/snippets_filter.go index 4e0f2e5fd8..dd5727a77e 100644 --- a/internal/controller/state/graph/snippets_filter.go +++ b/internal/controller/state/graph/snippets_filter.go @@ -17,7 +17,7 @@ type SnippetsFilter struct { // Snippets stored as a map of nginx context to snippet value. Snippets map[ngfAPI.NginxContext]string // Conditions define the conditions to be reported in the status of the SnippetsFilter. - Conditions conditions.Conditions + Conditions []conditions.Condition // Valid indicates whether the SnippetsFilter is semantically and syntactically valid. Valid bool // Referenced indicates whether the SnippetsFilter is referenced by a Route. @@ -64,7 +64,7 @@ func processSnippetsFilters( if cond := validateSnippetsFilter(sf); cond != nil { processed[nsname] = &SnippetsFilter{ Source: sf, - Conditions: conditions.Conditions{*cond}, + Conditions: []conditions.Condition{*cond}, Valid: false, } diff --git a/internal/controller/state/graph/snippets_filter_test.go b/internal/controller/state/graph/snippets_filter_test.go index 0abe174e30..dcfdd62159 100644 --- a/internal/controller/state/graph/snippets_filter_test.go +++ b/internal/controller/state/graph/snippets_filter_test.go @@ -100,7 +100,7 @@ func TestProcessSnippetsFilters(t *testing.T) { }, invalidFilterNsName: { Source: invalidFilter, - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewSnippetsFilterInvalid( "spec.snippets[1].context: Unsupported value: \"invalid context\": " + "supported values: \"main\", \"http\", \"http.server\", \"http.server.location\"", diff --git a/internal/controller/state/graph/tlsroute.go b/internal/controller/state/graph/tlsroute.go index 9ebe4fb8b3..db0b87e11e 100644 --- a/internal/controller/state/graph/tlsroute.go +++ b/internal/controller/state/graph/tlsroute.go @@ -69,7 +69,7 @@ func validateBackendRefTLSRoute( services map[types.NamespacedName]*apiv1.Service, parentRefs []ParentRef, refGrantResolver func(resource toResource) bool, -) (BackendRef, conditions.Conditions) { +) (BackendRef, []conditions.Condition) { // Length of BackendRefs and Rules is guaranteed to be one due to earlier check in buildTLSRoute refPath := field.NewPath("spec").Child("rules").Index(0).Child("backendRefs").Index(0) @@ -86,7 +86,7 @@ func validateBackendRefTLSRoute( InvalidForGateways: make(map[types.NamespacedName]conditions.Condition), } - return backendRef, conditions.Conditions{cond} + return backendRef, []conditions.Condition{cond} } ns := gtr.Namespace @@ -116,7 +116,7 @@ func validateBackendRefTLSRoute( if err != nil { backendRef.Valid = false - return backendRef, conditions.Conditions{conditions.NewRouteBackendRefRefBackendNotFound(err.Error())} + return backendRef, []conditions.Condition{conditions.NewRouteBackendRefRefBackendNotFound(err.Error())} } if svcPort.AppProtocol != nil { @@ -124,11 +124,11 @@ func validateBackendRefTLSRoute( if err != nil { backendRef.Valid = false - return backendRef, conditions.Conditions{conditions.NewRouteBackendRefUnsupportedProtocol(err.Error())} + return backendRef, []conditions.Condition{conditions.NewRouteBackendRefUnsupportedProtocol(err.Error())} } } - var conds conditions.Conditions + var conds []conditions.Condition for _, parentRef := range parentRefs { if err := verifyIPFamily(parentRef.Gateway.EffectiveNginxProxy, svcIPFamily); err != nil { backendRef.Valid = backendRef.Valid || false diff --git a/internal/controller/state/graph/tlsroute_test.go b/internal/controller/state/graph/tlsroute_test.go index f0b4c117f3..29fac01b1e 100644 --- a/internal/controller/state/graph/tlsroute_test.go +++ b/internal/controller/state/graph/tlsroute_test.go @@ -330,7 +330,7 @@ func TestBuildTLSRoute(t *testing.T) { expected: &L4Route{ Source: invalidHostnameGtr, ParentRefs: []ParentRef{parentRefGraph}, - Conditions: conditions.Conditions{conditions.NewRouteUnsupportedValue( + Conditions: []conditions.Condition{conditions.NewRouteUnsupportedValue( "spec.hostnames[0]: Invalid value: \"hi....com\": a lowercase RFC 1" + "123 subdomain must consist of lower case alphanumeric characters" + ", '-' or '.', and must start and end with an alphanumeric charac" + @@ -354,7 +354,7 @@ func TestBuildTLSRoute(t *testing.T) { "app.example.com", }, }, - Conditions: conditions.Conditions{conditions.NewRouteBackendRefUnsupportedValue( + Conditions: []conditions.Condition{conditions.NewRouteBackendRefUnsupportedValue( "Must have exactly one Rule and BackendRef", )}, Valid: false, @@ -382,7 +382,7 @@ func TestBuildTLSRoute(t *testing.T) { }, Attachable: true, Valid: true, - Conditions: conditions.Conditions{conditions.NewRouteBackendRefUnsupportedProtocol( + Conditions: []conditions.Condition{conditions.NewRouteBackendRefUnsupportedProtocol( "route type tls does not support service port appProtocol kubernetes.io/h2c", )}, }, @@ -411,7 +411,7 @@ func TestBuildTLSRoute(t *testing.T) { }, Attachable: true, Valid: true, - Conditions: conditions.Conditions{conditions.NewRouteBackendRefUnsupportedProtocol( + Conditions: []conditions.Condition{conditions.NewRouteBackendRefUnsupportedProtocol( "route type tls does not support service port appProtocol kubernetes.io/ws", )}, }, @@ -440,7 +440,7 @@ func TestBuildTLSRoute(t *testing.T) { InvalidForGateways: map[types.NamespacedName]conditions.Condition{}, }, }, - Conditions: conditions.Conditions{conditions.NewRouteBackendRefRefBackendNotFound( + Conditions: []conditions.Condition{conditions.NewRouteBackendRefRefBackendNotFound( "spec.rules[0].backendRefs[0].name: Not found: \"hi\"", )}, Attachable: true, @@ -465,7 +465,7 @@ func TestBuildTLSRoute(t *testing.T) { InvalidForGateways: map[types.NamespacedName]conditions.Condition{}, }, }, - Conditions: conditions.Conditions{conditions.NewRouteBackendRefInvalidKind( + Conditions: []conditions.Condition{conditions.NewRouteBackendRefInvalidKind( "spec.rules[0].backendRefs[0].group:" + " Unsupported value: \"wrong\": supported values: \"core\", \"\"", )}, @@ -493,7 +493,7 @@ func TestBuildTLSRoute(t *testing.T) { InvalidForGateways: map[types.NamespacedName]conditions.Condition{}, }, }, - Conditions: conditions.Conditions{conditions.NewRouteBackendRefInvalidKind( + Conditions: []conditions.Condition{conditions.NewRouteBackendRefInvalidKind( "spec.rules[0].backendRefs[0].kind:" + " Unsupported value: \"not service\": supported values: \"Service\"", )}, @@ -521,7 +521,7 @@ func TestBuildTLSRoute(t *testing.T) { InvalidForGateways: map[types.NamespacedName]conditions.Condition{}, }, }, - Conditions: conditions.Conditions{conditions.NewRouteBackendRefRefNotPermitted( + Conditions: []conditions.Condition{conditions.NewRouteBackendRefRefNotPermitted( "spec.rules[0].backendRefs[0].namespace: Forbidden: Backend ref to Service " + "diff/hi not permitted by any ReferenceGrant", )}, @@ -549,7 +549,7 @@ func TestBuildTLSRoute(t *testing.T) { InvalidForGateways: map[types.NamespacedName]conditions.Condition{}, }, }, - Conditions: conditions.Conditions{conditions.NewRouteBackendRefUnsupportedValue( + Conditions: []conditions.Condition{conditions.NewRouteBackendRefUnsupportedValue( "spec.rules[0].backendRefs[0].port: Required value: port cannot be nil", )}, Attachable: true, diff --git a/internal/controller/state/graph/validation.go b/internal/controller/state/graph/validation.go index fab5d88c31..27d23bf135 100644 --- a/internal/controller/state/graph/validation.go +++ b/internal/controller/state/graph/validation.go @@ -5,7 +5,6 @@ import ( "strings" "k8s.io/apimachinery/pkg/util/validation" - v1 "sigs.k8s.io/gateway-api/apis/v1" ) func validateHostname(hostname string) error { @@ -30,12 +29,3 @@ func validateHostname(hostname string) error { return nil } - -// RuleWithUnsupportedFields defines an interface for rules with unsupported fields. -type RuleWithUnsupportedFields interface { - GetName() *v1.SectionName - GetTimeouts() *v1.HTTPRouteTimeouts - GetRetry() *v1.HTTPRouteRetry - GetSessionPersistence() *v1.SessionPersistence - GetSupportedFields() []string -} diff --git a/internal/controller/state/validation/validationfakes/fake_policy_validator.go b/internal/controller/state/validation/validationfakes/fake_policy_validator.go index 88b014e147..0cb8b7f232 100644 --- a/internal/controller/state/validation/validationfakes/fake_policy_validator.go +++ b/internal/controller/state/validation/validationfakes/fake_policy_validator.go @@ -22,28 +22,28 @@ type FakePolicyValidator struct { conflictsReturnsOnCall map[int]struct { result1 bool } - ValidateStub func(policies.Policy) conditions.Conditions + ValidateStub func(policies.Policy) []conditions.Condition validateMutex sync.RWMutex validateArgsForCall []struct { arg1 policies.Policy } validateReturns struct { - result1 conditions.Conditions + result1 []conditions.Condition } validateReturnsOnCall map[int]struct { - result1 conditions.Conditions + result1 []conditions.Condition } - ValidateGlobalSettingsStub func(policies.Policy, *policies.GlobalSettings) conditions.Conditions + ValidateGlobalSettingsStub func(policies.Policy, *policies.GlobalSettings) []conditions.Condition validateGlobalSettingsMutex sync.RWMutex validateGlobalSettingsArgsForCall []struct { arg1 policies.Policy arg2 *policies.GlobalSettings } validateGlobalSettingsReturns struct { - result1 conditions.Conditions + result1 []conditions.Condition } validateGlobalSettingsReturnsOnCall map[int]struct { - result1 conditions.Conditions + result1 []conditions.Condition } invocations map[string][][]interface{} invocationsMutex sync.RWMutex @@ -111,7 +111,7 @@ func (fake *FakePolicyValidator) ConflictsReturnsOnCall(i int, result1 bool) { }{result1} } -func (fake *FakePolicyValidator) Validate(arg1 policies.Policy) conditions.Conditions { +func (fake *FakePolicyValidator) Validate(arg1 policies.Policy) []conditions.Condition { fake.validateMutex.Lock() ret, specificReturn := fake.validateReturnsOnCall[len(fake.validateArgsForCall)] fake.validateArgsForCall = append(fake.validateArgsForCall, struct { @@ -136,7 +136,7 @@ func (fake *FakePolicyValidator) ValidateCallCount() int { return len(fake.validateArgsForCall) } -func (fake *FakePolicyValidator) ValidateCalls(stub func(policies.Policy) conditions.Conditions) { +func (fake *FakePolicyValidator) ValidateCalls(stub func(policies.Policy) []conditions.Condition) { fake.validateMutex.Lock() defer fake.validateMutex.Unlock() fake.ValidateStub = stub @@ -149,30 +149,30 @@ func (fake *FakePolicyValidator) ValidateArgsForCall(i int) policies.Policy { return argsForCall.arg1 } -func (fake *FakePolicyValidator) ValidateReturns(result1 conditions.Conditions) { +func (fake *FakePolicyValidator) ValidateReturns(result1 []conditions.Condition) { fake.validateMutex.Lock() defer fake.validateMutex.Unlock() fake.ValidateStub = nil fake.validateReturns = struct { - result1 conditions.Conditions + result1 []conditions.Condition }{result1} } -func (fake *FakePolicyValidator) ValidateReturnsOnCall(i int, result1 conditions.Conditions) { +func (fake *FakePolicyValidator) ValidateReturnsOnCall(i int, result1 []conditions.Condition) { fake.validateMutex.Lock() defer fake.validateMutex.Unlock() fake.ValidateStub = nil if fake.validateReturnsOnCall == nil { fake.validateReturnsOnCall = make(map[int]struct { - result1 conditions.Conditions + result1 []conditions.Condition }) } fake.validateReturnsOnCall[i] = struct { - result1 conditions.Conditions + result1 []conditions.Condition }{result1} } -func (fake *FakePolicyValidator) ValidateGlobalSettings(arg1 policies.Policy, arg2 *policies.GlobalSettings) conditions.Conditions { +func (fake *FakePolicyValidator) ValidateGlobalSettings(arg1 policies.Policy, arg2 *policies.GlobalSettings) []conditions.Condition { fake.validateGlobalSettingsMutex.Lock() ret, specificReturn := fake.validateGlobalSettingsReturnsOnCall[len(fake.validateGlobalSettingsArgsForCall)] fake.validateGlobalSettingsArgsForCall = append(fake.validateGlobalSettingsArgsForCall, struct { @@ -198,7 +198,7 @@ func (fake *FakePolicyValidator) ValidateGlobalSettingsCallCount() int { return len(fake.validateGlobalSettingsArgsForCall) } -func (fake *FakePolicyValidator) ValidateGlobalSettingsCalls(stub func(policies.Policy, *policies.GlobalSettings) conditions.Conditions) { +func (fake *FakePolicyValidator) ValidateGlobalSettingsCalls(stub func(policies.Policy, *policies.GlobalSettings) []conditions.Condition) { fake.validateGlobalSettingsMutex.Lock() defer fake.validateGlobalSettingsMutex.Unlock() fake.ValidateGlobalSettingsStub = stub @@ -211,26 +211,26 @@ func (fake *FakePolicyValidator) ValidateGlobalSettingsArgsForCall(i int) (polic return argsForCall.arg1, argsForCall.arg2 } -func (fake *FakePolicyValidator) ValidateGlobalSettingsReturns(result1 conditions.Conditions) { +func (fake *FakePolicyValidator) ValidateGlobalSettingsReturns(result1 []conditions.Condition) { fake.validateGlobalSettingsMutex.Lock() defer fake.validateGlobalSettingsMutex.Unlock() fake.ValidateGlobalSettingsStub = nil fake.validateGlobalSettingsReturns = struct { - result1 conditions.Conditions + result1 []conditions.Condition }{result1} } -func (fake *FakePolicyValidator) ValidateGlobalSettingsReturnsOnCall(i int, result1 conditions.Conditions) { +func (fake *FakePolicyValidator) ValidateGlobalSettingsReturnsOnCall(i int, result1 []conditions.Condition) { fake.validateGlobalSettingsMutex.Lock() defer fake.validateGlobalSettingsMutex.Unlock() fake.ValidateGlobalSettingsStub = nil if fake.validateGlobalSettingsReturnsOnCall == nil { fake.validateGlobalSettingsReturnsOnCall = make(map[int]struct { - result1 conditions.Conditions + result1 []conditions.Condition }) } fake.validateGlobalSettingsReturnsOnCall[i] = struct { - result1 conditions.Conditions + result1 []conditions.Condition }{result1} } diff --git a/internal/controller/state/validation/validator.go b/internal/controller/state/validation/validator.go index d03d42c3ca..d01a907e7f 100644 --- a/internal/controller/state/validation/validator.go +++ b/internal/controller/state/validation/validator.go @@ -55,9 +55,9 @@ type GenericValidator interface { //counterfeiter:generate . PolicyValidator type PolicyValidator interface { // Validate validates an NGF Policy. - Validate(policy policies.Policy) conditions.Conditions + Validate(policy policies.Policy) []conditions.Condition // ValidateGlobalSettings validates an NGF Policy with the NginxProxy settings. - ValidateGlobalSettings(policy policies.Policy, globalSettings *policies.GlobalSettings) conditions.Conditions + ValidateGlobalSettings(policy policies.Policy, globalSettings *policies.GlobalSettings) []conditions.Condition // Conflicts returns true if the two Policies conflict. Conflicts(a, b policies.Policy) bool } diff --git a/internal/controller/status/prepare_requests.go b/internal/controller/status/prepare_requests.go index 91b71912e2..87e3b441cc 100644 --- a/internal/controller/status/prepare_requests.go +++ b/internal/controller/status/prepare_requests.go @@ -136,7 +136,7 @@ func removeDuplicateIndexParentRefs(parentRefs []graph.ParentRef) []graph.Parent func prepareRouteStatus( gatewayCtlrName string, parentRefs []graph.ParentRef, - conds conditions.Conditions, + conds []conditions.Condition, transitionTime metav1.Time, srcGeneration int64, ) v1.RouteStatus { @@ -155,7 +155,7 @@ func prepareRouteStatus( if ref.Attachment != nil { failedAttachmentCondCount = len(ref.Attachment.FailedConditions) } - allConds := make(conditions.Conditions, 0, len(conds)+len(defaultConds)+failedAttachmentCondCount) + allConds := make([]conditions.Condition, 0, len(conds)+len(defaultConds)+failedAttachmentCondCount) // We add defaultConds first, so that any additional conditions will override them, which is // ensured by DeduplicateConditions. @@ -195,7 +195,7 @@ func PrepareGatewayClassRequests( if gc != nil { defaultConds := conditions.NewDefaultGatewayClassConditions() - conds := make(conditions.Conditions, 0, len(gc.Conditions)+len(defaultConds)) + conds := make([]conditions.Condition, 0, len(gc.Conditions)+len(defaultConds)) // We add default conds first, so that any additional conditions will override them, which is // ensured by DeduplicateConditions. @@ -223,7 +223,7 @@ func PrepareGatewayClassRequests( ResourceType: &v1.GatewayClass{}, Setter: newGatewayClassStatusSetter(v1.GatewayClassStatus{ Conditions: conditions.ConvertConditions( - conditions.Conditions{conditions.NewGatewayClassConflict()}, + []conditions.Condition{conditions.NewGatewayClassConflict()}, gwClass.Generation, transitionTime, ), @@ -370,7 +370,7 @@ func PrepareNGFPolicyRequests( } for _, ancestor := range pol.Ancestors { - allConds := make(conditions.Conditions, 0, len(pol.Conditions)+len(ancestor.Conditions)+1) + allConds := make([]conditions.Condition, 0, len(pol.Conditions)+len(ancestor.Conditions)+1) // The order of conditions matters here. // We add the default condition first, followed by the ancestor conditions, and finally the policy conditions. @@ -455,7 +455,7 @@ func PrepareSnippetsFilterRequests( reqs := make([]UpdateRequest, 0, len(snippetsFilters)) for nsname, snippetsFilter := range snippetsFilters { - allConds := make(conditions.Conditions, 0, len(snippetsFilter.Conditions)+1) + allConds := make([]conditions.Condition, 0, len(snippetsFilter.Conditions)+1) // The order of conditions matters here. // We add the default condition first, followed by the snippetsFilter conditions. @@ -501,14 +501,14 @@ func PrepareNginxGatewayStatus( return nil } - var conds conditions.Conditions + var conds []conditions.Condition if cpUpdateRes.Error != nil { msg := "Failed to update control plane configuration" - conds = conditions.Conditions{ + conds = []conditions.Condition{ conditions.NewNginxGatewayInvalid(fmt.Sprintf("%s: %v", msg, cpUpdateRes.Error)), } } else { - conds = conditions.Conditions{conditions.NewNginxGatewayValid()} + conds = []conditions.Condition{conditions.NewNginxGatewayValid()} } return &UpdateRequest{ diff --git a/internal/controller/status/prepare_requests_test.go b/internal/controller/status/prepare_requests_test.go index 7ebb39c560..3cb629d3c2 100644 --- a/internal/controller/status/prepare_requests_test.go +++ b/internal/controller/status/prepare_requests_test.go @@ -113,7 +113,7 @@ var ( SectionName: commonRouteSpecValid.ParentRefs[1].SectionName, Attachment: &graph.ParentRefAttachmentStatus{ Attached: false, - FailedConditions: conditions.Conditions{invalidAttachmentCondition}, + FailedConditions: []conditions.Condition{invalidAttachmentCondition}, }, }, { @@ -122,7 +122,7 @@ var ( SectionName: commonRouteSpecValid.ParentRefs[2].SectionName, Attachment: &graph.ParentRefAttachmentStatus{ Attached: true, - FailedConditions: conditions.Conditions{invalidAttachmentCondition}, + FailedConditions: []conditions.Condition{invalidAttachmentCondition}, }, }, { @@ -131,7 +131,7 @@ var ( SectionName: commonRouteSpecValid.ParentRefs[3].SectionName, Attachment: &graph.ParentRefAttachmentStatus{ Attached: false, - FailedConditions: conditions.Conditions{invalidAttachmentCondition}, + FailedConditions: []conditions.Condition{invalidAttachmentCondition}, }, }, { @@ -148,7 +148,7 @@ var ( SectionName: commonRouteSpecValid.ParentRefs[5].SectionName, Attachment: &graph.ParentRefAttachmentStatus{ Attached: true, - FailedConditions: conditions.Conditions{invalidAttachmentCondition}, + FailedConditions: []conditions.Condition{invalidAttachmentCondition}, }, }, { @@ -157,7 +157,7 @@ var ( SectionName: commonRouteSpecValid.ParentRefs[6].SectionName, Attachment: &graph.ParentRefAttachmentStatus{ Attached: false, - FailedConditions: conditions.Conditions{invalidAttachmentCondition}, + FailedConditions: []conditions.Condition{invalidAttachmentCondition}, }, }, { @@ -166,7 +166,7 @@ var ( SectionName: commonRouteSpecValid.ParentRefs[7].SectionName, Attachment: &graph.ParentRefAttachmentStatus{ Attached: false, - FailedConditions: conditions.Conditions{invalidAttachmentCondition}, + FailedConditions: []conditions.Condition{invalidAttachmentCondition}, }, }, } @@ -401,7 +401,7 @@ func TestBuildHTTPRouteStatuses(t *testing.T) { }, graph.CreateRouteKey(hrInvalid): { Valid: false, - Conditions: conditions.Conditions{invalidRouteCondition}, + Conditions: []conditions.Condition{invalidRouteCondition}, Source: hrInvalid, ParentRefs: parentRefsInvalid, RouteType: graph.RouteTypeHTTP, @@ -479,7 +479,7 @@ func TestBuildGRPCRouteStatuses(t *testing.T) { }, graph.CreateRouteKey(grInvalid): { Valid: false, - Conditions: conditions.Conditions{invalidRouteCondition}, + Conditions: []conditions.Condition{invalidRouteCondition}, Source: grInvalid, ParentRefs: parentRefsInvalid, RouteType: graph.RouteTypeGRPC, @@ -556,7 +556,7 @@ func TestBuildTLSRouteStatuses(t *testing.T) { }, graph.CreateRouteKeyL4(trInvalid): { Valid: false, - Conditions: conditions.Conditions{invalidRouteCondition}, + Conditions: []conditions.Condition{invalidRouteCondition}, Source: trInvalid, ParentRefs: parentRefsInvalid, }, @@ -1142,7 +1142,7 @@ func TestBuildGatewayStatuses(t *testing.T) { }, }, Valid: true, - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewGatewayResolvedRefs(), }, }, @@ -1197,7 +1197,7 @@ func TestBuildGatewayStatuses(t *testing.T) { }, }, Valid: true, - Conditions: conditions.Conditions{ + Conditions: []conditions.Condition{ conditions.NewGatewayRefNotFound(), conditions.NewGatewayInvalidParameters("ParametersRef not found"), }, @@ -1390,7 +1390,7 @@ func TestBuildBackendTLSPolicyStatuses(t *testing.T) { type policyCfg struct { Name string - Conditions conditions.Conditions + Conditions []conditions.Condition Gateways []types.NamespacedName Valid bool Ignored bool @@ -1414,8 +1414,8 @@ func TestBuildBackendTLSPolicyStatuses(t *testing.T) { } } - attachedConds := conditions.Conditions{conditions.NewPolicyAccepted()} - invalidConds := conditions.Conditions{conditions.NewPolicyInvalid("invalid backendTLSPolicy")} + attachedConds := []conditions.Condition{conditions.NewPolicyAccepted()} + invalidConds := []conditions.Condition{conditions.NewPolicyInvalid("invalid backendTLSPolicy")} validPolicyCfg := policyCfg{ Name: "valid-bt", @@ -1744,7 +1744,7 @@ func TestBuildNGFPolicyStatuses(t *testing.T) { type policyCfg struct { Ancestors []graph.PolicyAncestor Name string - Conditions conditions.Conditions + Conditions []conditions.Condition } // We have to use a real policy here because the test makes the status update using the k8sClient. @@ -1763,8 +1763,8 @@ func TestBuildNGFPolicyStatuses(t *testing.T) { } } - invalidConds := conditions.Conditions{conditions.NewPolicyInvalid("invalid")} - targetRefNotFoundConds := conditions.Conditions{conditions.NewPolicyTargetNotFound("target not found")} + invalidConds := []conditions.Condition{conditions.NewPolicyInvalid("invalid")} + targetRefNotFoundConds := []conditions.Condition{conditions.NewPolicyTargetNotFound("target not found")} validPolicyKey := graph.PolicyKey{ NsName: types.NamespacedName{Namespace: "test", Name: "valid-pol"}, @@ -2059,7 +2059,7 @@ func TestBuildSnippetsFilterStatuses(t *testing.T) { Generation: 1, }, }, - Conditions: conditions.Conditions{conditions.NewSnippetsFilterInvalid("invalid snippetsFilter")}, + Conditions: []conditions.Condition{conditions.NewSnippetsFilterInvalid("invalid snippetsFilter")}, Valid: false, } From 9fa020b8025b5aecbd0b522bb6c2a4022a47626a Mon Sep 17 00:00:00 2001 From: Tina Usova Date: Wed, 8 Oct 2025 12:03:16 +0100 Subject: [PATCH 3/7] fix misprints --- internal/controller/state/graph/httproute.go | 2 +- internal/controller/state/graph/httproute_test.go | 14 +++++++------- internal/controller/state/graph/route_common.go | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/internal/controller/state/graph/httproute.go b/internal/controller/state/graph/httproute.go index c849185207..0b9d5e2dae 100644 --- a/internal/controller/state/graph/httproute.go +++ b/internal/controller/state/graph/httproute.go @@ -537,7 +537,7 @@ func checkForUnsupportedHTTPFields(rule v1.HTTPRouteRule, rulePath *field.Path) if rule.Name != nil { ruleErrors = append(ruleErrors, field.Forbidden( rulePath.Child("name"), - "NGINX Gateway Fabric does not support SectionName field at the moment", + "NGINX Gateway Fabric does not support \"SectionName\" field at the moment", )) } if rule.Timeouts != nil { diff --git a/internal/controller/state/graph/httproute_test.go b/internal/controller/state/graph/httproute_test.go index 4fd0cb47f2..9756dcc7b9 100644 --- a/internal/controller/state/graph/httproute_test.go +++ b/internal/controller/state/graph/httproute_test.go @@ -979,7 +979,7 @@ func TestBuildHTTPRoute(t *testing.T) { }, Conditions: []conditions.Condition{ conditions.NewRouteUnsupportedField("There are rules with unsupported fields configurations: " + - "spec.rules[0].name: Forbidden: NGINX Gateway Fabric does not support SectionName field at the moment"), + "spec.rules[0].name: Forbidden: NGINX Gateway Fabric does not support \"SectionName\" field at the moment"), }, }, name: "valid route with unsupported field", @@ -1783,7 +1783,7 @@ func TestProcessHTTPRouteRules_UnsupportedFields(t *testing.T) { expectedValid: true, expectedConds: []conditions.Condition{ conditions.NewRouteUnsupportedField("There are rules with unsupported fields configurations: spec.rules[0].name: " + - "Forbidden: NGINX Gateway Fabric does not support SectionName field at the moment"), + "Forbidden: NGINX Gateway Fabric does not support \"SectionName\" field at the moment"), }, expectedWarns: 1, }, @@ -1804,11 +1804,11 @@ func TestProcessHTTPRouteRules_UnsupportedFields(t *testing.T) { expectedValid: true, expectedConds: []conditions.Condition{ conditions.NewRouteUnsupportedField("There are rules with unsupported fields configurations: " + - "[spec.rules[0].name: Forbidden: NGINX Gateway Fabric does not support SectionName field at the moment, " + - "spec.rules[0].timeouts: Forbidden: NGINX Gateway Fabric does not support \"HTTPRouteTimeouts\" " + - "field at the moment, spec.rules[0].retry: Forbidden: NGINX Gateway Fabric does not support " + - "\"HTTPRouteRetry\" field at the moment, spec.rules[0].sessionPersistence: Forbidden: NGINX Gateway " + - "Fabric does not support \"SessionPersistence\" field at the moment]"), + "[spec.rules[0].name: Forbidden: NGINX Gateway Fabric does not support \"SectionName\" " + + "field at the moment, spec.rules[0].timeouts: Forbidden: NGINX Gateway Fabric does not support " + + "\"HTTPRouteTimeouts\" field at the moment, spec.rules[0].retry: Forbidden: NGINX Gateway Fabric " + + "does not support \"HTTPRouteRetry\" field at the moment, spec.rules[0].sessionPersistence: Forbidden: " + + "NGINX Gateway Fabric does not support \"SessionPersistence\" field at the moment]"), }, expectedWarns: 4, }, diff --git a/internal/controller/state/graph/route_common.go b/internal/controller/state/graph/route_common.go index c11c266fb2..390be70928 100644 --- a/internal/controller/state/graph/route_common.go +++ b/internal/controller/state/graph/route_common.go @@ -143,11 +143,11 @@ type L7RouteSpec struct { } type RouteRule struct { - // SectionName is the name of a section in a Kubernetes resource. + // Name is the name of a section in a Kubernetes resource. Name *v1.SectionName - // HTTPRouteTimeouts defines timeouts that can be configured for an HTTPRoute. + // Timeouts defines timeouts that can be configured for an HTTPRoute. Timeouts *v1.HTTPRouteTimeouts - // HTTPRouteRetry defines retry configuration for an HTTPRoute. + // Retry defines retry configuration for an HTTPRoute. Retry *v1.HTTPRouteRetry // SessionPersistence defines the desired state of SessionPersistence. SessionPersistence *v1.SessionPersistence From 526de831dd02247bc1a36494e325f979b8d2143a Mon Sep 17 00:00:00 2001 From: Tina Usova Date: Wed, 8 Oct 2025 13:19:08 +0100 Subject: [PATCH 4/7] Minimize errors --- internal/controller/state/conditions/conditions.go | 4 ++-- internal/controller/state/graph/grpcroute.go | 5 ++--- internal/controller/state/graph/grpcroute_test.go | 11 +++++------ internal/controller/state/graph/httproute.go | 8 ++++---- internal/controller/state/graph/httproute_test.go | 12 +++++------- internal/controller/state/graph/route_common.go | 8 -------- 6 files changed, 18 insertions(+), 30 deletions(-) diff --git a/internal/controller/state/conditions/conditions.go b/internal/controller/state/conditions/conditions.go index 0e40194fd9..60f2c33260 100644 --- a/internal/controller/state/conditions/conditions.go +++ b/internal/controller/state/conditions/conditions.go @@ -376,7 +376,7 @@ func NewRouteUnsupportedField(msg string) Condition { Type: string(RouteConditionUnsupportedField), Status: metav1.ConditionTrue, Reason: string(RouteReasonUnsupportedField), - Message: fmt.Sprintf("Some parameters are ignored due to an error: %s", msg), + Message: fmt.Sprintf("Some parameters are ignored because they are not supported: %s", msg), } } @@ -978,7 +978,7 @@ func NewGatewayUnsupportedField(msg string) Condition { Type: string(GatewayConditionUnsupportedField), Status: metav1.ConditionTrue, Reason: string(GatewayReasonUnsupportedField), - Message: fmt.Sprintf("Gateway is accepted, but some parameters are ignored due to an error: %s", msg), + Message: fmt.Sprintf("Gateway is accepted, but some parameters are ignored because they are not supported: %s", msg), } } diff --git a/internal/controller/state/graph/grpcroute.go b/internal/controller/state/graph/grpcroute.go index 3cfbc3c76e..38d39b7572 100644 --- a/internal/controller/state/graph/grpcroute.go +++ b/internal/controller/state/graph/grpcroute.go @@ -269,7 +269,6 @@ func processGRPCRouteRules( if len(allRulesErrors.warn) > 0 { msg := "There are rules with unsupported fields configurations: " + allRulesErrors.warn.ToAggregate().Error() conds = append(conds, conditions.NewRouteUnsupportedField(msg)) - valid = true } if len(allRulesErrors.invalid) > 0 { @@ -463,13 +462,13 @@ func checkForUnsupportedGRPCFields(rule v1.GRPCRouteRule, rulePath *field.Path) if rule.Name != nil { ruleErrors = append(ruleErrors, field.Forbidden( rulePath.Child("name"), - "NGINX Gateway Fabric does not support \"SectionName\" field at the moment", + "rule names are not supported", )) } if rule.SessionPersistence != nil { ruleErrors = append(ruleErrors, field.Forbidden( rulePath.Child("sessionPersistence"), - "NGINX Gateway Fabric does not support \"SessionPersistence\" field at the moment", + "sessionPersistence is not supported", )) } diff --git a/internal/controller/state/graph/grpcroute_test.go b/internal/controller/state/graph/grpcroute_test.go index 2e664241dd..b704c341cd 100644 --- a/internal/controller/state/graph/grpcroute_test.go +++ b/internal/controller/state/graph/grpcroute_test.go @@ -1141,7 +1141,7 @@ func TestBuildGRPCRoute(t *testing.T) { }, Conditions: []conditions.Condition{ conditions.NewRouteUnsupportedField("There are rules with unsupported fields configurations: " + - "spec.rules[0].name: Forbidden: NGINX Gateway Fabric does not support \"SectionName\" field at the moment"), + "spec.rules[0].name: Forbidden: rule names are not supported"), }, }, name: "valid route with unsupported field", @@ -1177,7 +1177,7 @@ func TestBuildGRPCRoute(t *testing.T) { }, Conditions: []conditions.Condition{ conditions.NewRouteUnsupportedField("There are rules with unsupported fields configurations: " + - "spec.rules[0].name: Forbidden: NGINX Gateway Fabric does not support \"SectionName\" field at the moment"), + "spec.rules[0].name: Forbidden: rule names are not supported"), conditions.NewRouteUnsupportedValue( "All rules are invalid: [spec.rules[0].matches[0].method.service: Required value: service is required, " + "spec.rules[0].matches[0].method.method: Required value: method is required]", @@ -1574,7 +1574,7 @@ func TestProcessGRPCRouteRules_UnsupportedFields(t *testing.T) { expectedValid: true, expectedConds: []conditions.Condition{ conditions.NewRouteUnsupportedField("There are rules with unsupported fields configurations: spec.rules[0].name: " + - "Forbidden: NGINX Gateway Fabric does not support \"SectionName\" field at the moment"), + "Forbidden: rule names are not supported"), }, expectedWarns: 1, }, @@ -1591,9 +1591,8 @@ func TestProcessGRPCRouteRules_UnsupportedFields(t *testing.T) { expectedValid: true, expectedConds: []conditions.Condition{ conditions.NewRouteUnsupportedField("There are rules with unsupported fields configurations: " + - "[spec.rules[0].name: Forbidden: NGINX Gateway Fabric does not support \"SectionName\" field at the moment, " + - "spec.rules[0].sessionPersistence: Forbidden: NGINX Gateway Fabric does not support \"SessionPersistence\" " + - "field at the moment]"), + "[spec.rules[0].name: Forbidden: rule names are not supported, " + + "spec.rules[0].sessionPersistence: Forbidden: sessionPersistence is not supported]"), }, expectedWarns: 2, }, diff --git a/internal/controller/state/graph/httproute.go b/internal/controller/state/graph/httproute.go index 0b9d5e2dae..945bca8b50 100644 --- a/internal/controller/state/graph/httproute.go +++ b/internal/controller/state/graph/httproute.go @@ -537,25 +537,25 @@ func checkForUnsupportedHTTPFields(rule v1.HTTPRouteRule, rulePath *field.Path) if rule.Name != nil { ruleErrors = append(ruleErrors, field.Forbidden( rulePath.Child("name"), - "NGINX Gateway Fabric does not support \"SectionName\" field at the moment", + "rule names are not supported", )) } if rule.Timeouts != nil { ruleErrors = append(ruleErrors, field.Forbidden( rulePath.Child("timeouts"), - "NGINX Gateway Fabric does not support \"HTTPRouteTimeouts\" field at the moment", + "timeouts are not supported", )) } if rule.Retry != nil { ruleErrors = append(ruleErrors, field.Forbidden( rulePath.Child("retry"), - "NGINX Gateway Fabric does not support \"HTTPRouteRetry\" field at the moment", + "retry is not supported", )) } if rule.SessionPersistence != nil { ruleErrors = append(ruleErrors, field.Forbidden( rulePath.Child("sessionPersistence"), - "NGINX Gateway Fabric does not support \"SessionPersistence\" field at the moment", + "sessionPersistence is not supported", )) } diff --git a/internal/controller/state/graph/httproute_test.go b/internal/controller/state/graph/httproute_test.go index 9756dcc7b9..d337befb02 100644 --- a/internal/controller/state/graph/httproute_test.go +++ b/internal/controller/state/graph/httproute_test.go @@ -979,7 +979,7 @@ func TestBuildHTTPRoute(t *testing.T) { }, Conditions: []conditions.Condition{ conditions.NewRouteUnsupportedField("There are rules with unsupported fields configurations: " + - "spec.rules[0].name: Forbidden: NGINX Gateway Fabric does not support \"SectionName\" field at the moment"), + "spec.rules[0].name: Forbidden: rule names are not supported"), }, }, name: "valid route with unsupported field", @@ -1783,7 +1783,7 @@ func TestProcessHTTPRouteRules_UnsupportedFields(t *testing.T) { expectedValid: true, expectedConds: []conditions.Condition{ conditions.NewRouteUnsupportedField("There are rules with unsupported fields configurations: spec.rules[0].name: " + - "Forbidden: NGINX Gateway Fabric does not support \"SectionName\" field at the moment"), + "Forbidden: rule names are not supported"), }, expectedWarns: 1, }, @@ -1804,11 +1804,9 @@ func TestProcessHTTPRouteRules_UnsupportedFields(t *testing.T) { expectedValid: true, expectedConds: []conditions.Condition{ conditions.NewRouteUnsupportedField("There are rules with unsupported fields configurations: " + - "[spec.rules[0].name: Forbidden: NGINX Gateway Fabric does not support \"SectionName\" " + - "field at the moment, spec.rules[0].timeouts: Forbidden: NGINX Gateway Fabric does not support " + - "\"HTTPRouteTimeouts\" field at the moment, spec.rules[0].retry: Forbidden: NGINX Gateway Fabric " + - "does not support \"HTTPRouteRetry\" field at the moment, spec.rules[0].sessionPersistence: Forbidden: " + - "NGINX Gateway Fabric does not support \"SessionPersistence\" field at the moment]"), + "[spec.rules[0].name: Forbidden: rule names are not supported, spec.rules[0].timeouts: " + + "Forbidden: timeouts are not supported, spec.rules[0].retry: Forbidden: retry is not supported, " + + "spec.rules[0].sessionPersistence: Forbidden: sessionPersistence is not supported]"), }, expectedWarns: 4, }, diff --git a/internal/controller/state/graph/route_common.go b/internal/controller/state/graph/route_common.go index 390be70928..ba7e117c46 100644 --- a/internal/controller/state/graph/route_common.go +++ b/internal/controller/state/graph/route_common.go @@ -143,14 +143,6 @@ type L7RouteSpec struct { } type RouteRule struct { - // Name is the name of a section in a Kubernetes resource. - Name *v1.SectionName - // Timeouts defines timeouts that can be configured for an HTTPRoute. - Timeouts *v1.HTTPRouteTimeouts - // Retry defines retry configuration for an HTTPRoute. - Retry *v1.HTTPRouteRetry - // SessionPersistence defines the desired state of SessionPersistence. - SessionPersistence *v1.SessionPersistence // Matches define the predicate used to match requests to a given action. Matches []v1.HTTPRouteMatch // RouteBackendRefs are a wrapper for v1.BackendRef and any BackendRef filters from the HTTPRoute or GRPCRoute. From 1247ffdc781d03c63005ee608e9750fa83d5d701 Mon Sep 17 00:00:00 2001 From: Tina Usova Date: Thu, 9 Oct 2025 11:42:05 +0100 Subject: [PATCH 5/7] Fix error messages 2 --- internal/controller/state/conditions/conditions.go | 4 ++-- internal/controller/state/graph/gateway.go | 4 ++-- internal/controller/state/graph/gateway_test.go | 10 +++++----- internal/controller/state/graph/grpcroute.go | 7 +++---- internal/controller/state/graph/grpcroute_test.go | 14 +++++--------- internal/controller/state/graph/httproute.go | 11 +++++------ internal/controller/state/graph/httproute_test.go | 13 +++++-------- 7 files changed, 27 insertions(+), 36 deletions(-) diff --git a/internal/controller/state/conditions/conditions.go b/internal/controller/state/conditions/conditions.go index 60f2c33260..d3725de653 100644 --- a/internal/controller/state/conditions/conditions.go +++ b/internal/controller/state/conditions/conditions.go @@ -376,7 +376,7 @@ func NewRouteUnsupportedField(msg string) Condition { Type: string(RouteConditionUnsupportedField), Status: metav1.ConditionTrue, Reason: string(RouteReasonUnsupportedField), - Message: fmt.Sprintf("Some parameters are ignored because they are not supported: %s", msg), + Message: fmt.Sprintf("The following unsupported parameters were ignored: %s", msg), } } @@ -978,7 +978,7 @@ func NewGatewayUnsupportedField(msg string) Condition { Type: string(GatewayConditionUnsupportedField), Status: metav1.ConditionTrue, Reason: string(GatewayReasonUnsupportedField), - Message: fmt.Sprintf("Gateway is accepted, but some parameters are ignored because they are not supported: %s", msg), + Message: fmt.Sprintf("Gateway accepted but the following unsupported parameters were ignored: %s", msg), } } diff --git a/internal/controller/state/graph/gateway.go b/internal/controller/state/graph/gateway.go index 82d4a983e2..5e53c56d10 100644 --- a/internal/controller/state/graph/gateway.go +++ b/internal/controller/state/graph/gateway.go @@ -275,11 +275,11 @@ func validateUnsupportedGatewayFields(gw *v1.Gateway) []conditions.Condition { var conds []conditions.Condition if gw.Spec.AllowedListeners != nil { - conds = append(conds, conditions.NewGatewayUnsupportedField("AllowedListeners are not supported")) + conds = append(conds, conditions.NewGatewayUnsupportedField("AllowedListeners")) } if gw.Spec.BackendTLS != nil { - conds = append(conds, conditions.NewGatewayUnsupportedField("BackendTLS is not supported")) + conds = append(conds, conditions.NewGatewayUnsupportedField("BackendTLS")) } return conds diff --git a/internal/controller/state/graph/gateway_test.go b/internal/controller/state/graph/gateway_test.go index 8584d03d99..800f2e0bc7 100644 --- a/internal/controller/state/graph/gateway_test.go +++ b/internal/controller/state/graph/gateway_test.go @@ -1561,7 +1561,7 @@ func TestBuildGateway(t *testing.T) { }, }, Conditions: []conditions.Condition{ - conditions.NewGatewayUnsupportedField("AllowedListeners are not supported"), + conditions.NewGatewayUnsupportedField("AllowedListeners"), conditions.NewGatewayResolvedRefs(), }, }, @@ -1603,7 +1603,7 @@ func TestBuildGateway(t *testing.T) { IPFamily: helpers.GetPointer(ngfAPIv1alpha2.Dual), }, Conditions: []conditions.Condition{ - conditions.NewGatewayUnsupportedField("BackendTLS is not supported"), + conditions.NewGatewayUnsupportedField("BackendTLS"), conditions.NewGatewayRefInvalid( "spec.infrastructure.parametersRef.kind: Unsupported value: \"wrong-kind\": supported values: \"NginxProxy\"", ), @@ -1946,7 +1946,7 @@ func TestValidateUnsupportedGatewayFields(t *testing.T) { }, }, expectedConds: []conditions.Condition{ - conditions.NewGatewayUnsupportedField("AllowedListeners are not supported"), + conditions.NewGatewayUnsupportedField("AllowedListeners"), }, }, { @@ -1958,8 +1958,8 @@ func TestValidateUnsupportedGatewayFields(t *testing.T) { }, }, expectedConds: []conditions.Condition{ - conditions.NewGatewayUnsupportedField("AllowedListeners are not supported"), - conditions.NewGatewayUnsupportedField("BackendTLS is not supported"), + conditions.NewGatewayUnsupportedField("AllowedListeners"), + conditions.NewGatewayUnsupportedField("BackendTLS"), }, }, } diff --git a/internal/controller/state/graph/grpcroute.go b/internal/controller/state/graph/grpcroute.go index 38d39b7572..1ebdeaed0a 100644 --- a/internal/controller/state/graph/grpcroute.go +++ b/internal/controller/state/graph/grpcroute.go @@ -267,8 +267,7 @@ func processGRPCRouteRules( // add warning condition for unsupported fields if any if len(allRulesErrors.warn) > 0 { - msg := "There are rules with unsupported fields configurations: " + allRulesErrors.warn.ToAggregate().Error() - conds = append(conds, conditions.NewRouteUnsupportedField(msg)) + conds = append(conds, conditions.NewRouteUnsupportedField(allRulesErrors.warn.ToAggregate().Error())) } if len(allRulesErrors.invalid) > 0 { @@ -462,13 +461,13 @@ func checkForUnsupportedGRPCFields(rule v1.GRPCRouteRule, rulePath *field.Path) if rule.Name != nil { ruleErrors = append(ruleErrors, field.Forbidden( rulePath.Child("name"), - "rule names are not supported", + "Name", )) } if rule.SessionPersistence != nil { ruleErrors = append(ruleErrors, field.Forbidden( rulePath.Child("sessionPersistence"), - "sessionPersistence is not supported", + "SessionPersistence", )) } diff --git a/internal/controller/state/graph/grpcroute_test.go b/internal/controller/state/graph/grpcroute_test.go index b704c341cd..9dfc6e0a16 100644 --- a/internal/controller/state/graph/grpcroute_test.go +++ b/internal/controller/state/graph/grpcroute_test.go @@ -1140,8 +1140,7 @@ func TestBuildGRPCRoute(t *testing.T) { }, }, Conditions: []conditions.Condition{ - conditions.NewRouteUnsupportedField("There are rules with unsupported fields configurations: " + - "spec.rules[0].name: Forbidden: rule names are not supported"), + conditions.NewRouteUnsupportedField("spec.rules[0].name: Forbidden: Name"), }, }, name: "valid route with unsupported field", @@ -1176,8 +1175,7 @@ func TestBuildGRPCRoute(t *testing.T) { }, }, Conditions: []conditions.Condition{ - conditions.NewRouteUnsupportedField("There are rules with unsupported fields configurations: " + - "spec.rules[0].name: Forbidden: rule names are not supported"), + conditions.NewRouteUnsupportedField("spec.rules[0].name: Forbidden: Name"), conditions.NewRouteUnsupportedValue( "All rules are invalid: [spec.rules[0].matches[0].method.service: Required value: service is required, " + "spec.rules[0].matches[0].method.method: Required value: method is required]", @@ -1573,8 +1571,7 @@ func TestProcessGRPCRouteRules_UnsupportedFields(t *testing.T) { }, expectedValid: true, expectedConds: []conditions.Condition{ - conditions.NewRouteUnsupportedField("There are rules with unsupported fields configurations: spec.rules[0].name: " + - "Forbidden: rule names are not supported"), + conditions.NewRouteUnsupportedField("spec.rules[0].name: Forbidden: Name"), }, expectedWarns: 1, }, @@ -1590,9 +1587,8 @@ func TestProcessGRPCRouteRules_UnsupportedFields(t *testing.T) { }, expectedValid: true, expectedConds: []conditions.Condition{ - conditions.NewRouteUnsupportedField("There are rules with unsupported fields configurations: " + - "[spec.rules[0].name: Forbidden: rule names are not supported, " + - "spec.rules[0].sessionPersistence: Forbidden: sessionPersistence is not supported]"), + conditions.NewRouteUnsupportedField("[spec.rules[0].name: Forbidden: Name, " + + "spec.rules[0].sessionPersistence: Forbidden: SessionPersistence]"), }, expectedWarns: 2, }, diff --git a/internal/controller/state/graph/httproute.go b/internal/controller/state/graph/httproute.go index 945bca8b50..80b179969a 100644 --- a/internal/controller/state/graph/httproute.go +++ b/internal/controller/state/graph/httproute.go @@ -274,8 +274,7 @@ func processHTTPRouteRules( // add warning condition for unsupported fields if any if len(allRulesErrors.warn) > 0 { - msg := "There are rules with unsupported fields configurations: " + allRulesErrors.warn.ToAggregate().Error() - conds = append(conds, conditions.NewRouteUnsupportedField(msg)) + conds = append(conds, conditions.NewRouteUnsupportedField(allRulesErrors.warn.ToAggregate().Error())) valid = true } @@ -537,25 +536,25 @@ func checkForUnsupportedHTTPFields(rule v1.HTTPRouteRule, rulePath *field.Path) if rule.Name != nil { ruleErrors = append(ruleErrors, field.Forbidden( rulePath.Child("name"), - "rule names are not supported", + "Name", )) } if rule.Timeouts != nil { ruleErrors = append(ruleErrors, field.Forbidden( rulePath.Child("timeouts"), - "timeouts are not supported", + "Timeouts", )) } if rule.Retry != nil { ruleErrors = append(ruleErrors, field.Forbidden( rulePath.Child("retry"), - "retry is not supported", + "Retry", )) } if rule.SessionPersistence != nil { ruleErrors = append(ruleErrors, field.Forbidden( rulePath.Child("sessionPersistence"), - "sessionPersistence is not supported", + "SessionPersistence", )) } diff --git a/internal/controller/state/graph/httproute_test.go b/internal/controller/state/graph/httproute_test.go index d337befb02..4e7369ef09 100644 --- a/internal/controller/state/graph/httproute_test.go +++ b/internal/controller/state/graph/httproute_test.go @@ -978,8 +978,7 @@ func TestBuildHTTPRoute(t *testing.T) { }, }, Conditions: []conditions.Condition{ - conditions.NewRouteUnsupportedField("There are rules with unsupported fields configurations: " + - "spec.rules[0].name: Forbidden: rule names are not supported"), + conditions.NewRouteUnsupportedField("spec.rules[0].name: Forbidden: Name"), }, }, name: "valid route with unsupported field", @@ -1782,8 +1781,7 @@ func TestProcessHTTPRouteRules_UnsupportedFields(t *testing.T) { }, expectedValid: true, expectedConds: []conditions.Condition{ - conditions.NewRouteUnsupportedField("There are rules with unsupported fields configurations: spec.rules[0].name: " + - "Forbidden: rule names are not supported"), + conditions.NewRouteUnsupportedField("spec.rules[0].name: Forbidden: Name"), }, expectedWarns: 1, }, @@ -1803,10 +1801,9 @@ func TestProcessHTTPRouteRules_UnsupportedFields(t *testing.T) { }, expectedValid: true, expectedConds: []conditions.Condition{ - conditions.NewRouteUnsupportedField("There are rules with unsupported fields configurations: " + - "[spec.rules[0].name: Forbidden: rule names are not supported, spec.rules[0].timeouts: " + - "Forbidden: timeouts are not supported, spec.rules[0].retry: Forbidden: retry is not supported, " + - "spec.rules[0].sessionPersistence: Forbidden: sessionPersistence is not supported]"), + conditions.NewRouteUnsupportedField("[spec.rules[0].name: Forbidden: Name, spec.rules[0].timeouts: " + + "Forbidden: Timeouts, spec.rules[0].retry: Forbidden: Retry, " + + "spec.rules[0].sessionPersistence: Forbidden: SessionPersistence]"), }, expectedWarns: 4, }, From 89b9190e339440bbc751735c6390de84a63f4a09 Mon Sep 17 00:00:00 2001 From: Tina Usova Date: Thu, 9 Oct 2025 12:44:16 +0100 Subject: [PATCH 6/7] Fix conditions --- .../controller/state/conditions/conditions.go | 21 +++++++------------ internal/controller/state/graph/gateway.go | 4 ++-- .../controller/state/graph/gateway_test.go | 10 ++++----- internal/controller/state/graph/grpcroute.go | 2 +- .../controller/state/graph/grpcroute_test.go | 8 +++---- internal/controller/state/graph/httproute.go | 2 +- .../controller/state/graph/httproute_test.go | 6 +++--- 7 files changed, 23 insertions(+), 30 deletions(-) diff --git a/internal/controller/state/conditions/conditions.go b/internal/controller/state/conditions/conditions.go index d3725de653..d62b04ba1f 100644 --- a/internal/controller/state/conditions/conditions.go +++ b/internal/controller/state/conditions/conditions.go @@ -42,10 +42,6 @@ const ( // not yet supported. RouteReasonUnsupportedField v1.RouteConditionReason = "UnsupportedField" - // RouteConditionUnsupportedField indicates that the Route contains fields that are not yet supported. - // The route is still valid and processed, but these fields are ignored. - RouteConditionUnsupportedField v1.RouteConditionType = "UnsupportedField" - // RouteReasonInvalidGateway is used with the "Accepted" (false) condition when the Gateway the Route // references is invalid. RouteReasonInvalidGateway v1.RouteConditionReason = "InvalidGateway" @@ -74,10 +70,6 @@ const ( // that are not yet supported. GatewayReasonUnsupportedField v1.GatewayConditionReason = "UnsupportedField" - // GatewayConditionUnsupportedField indicates that the Gateway contains fields that are not yet supported. - // The gateway is still valid and processed, but these fields are ignored. - GatewayConditionUnsupportedField v1.GatewayConditionType = "UnsupportedField" - // GatewayReasonUnsupportedValue is used with GatewayConditionAccepted (false) when a value of a field in a Gateway // is invalid or not supported. GatewayReasonUnsupportedValue v1.GatewayConditionReason = "UnsupportedValue" @@ -370,10 +362,11 @@ func NewRouteUnsupportedValue(msg string) Condition { } } -// NewRouteUnsupportedField returns a Condition that indicates that the Route includes an unsupported field. -func NewRouteUnsupportedField(msg string) Condition { +// NewRouteAcceptedUnsupportedField returns a Condition that indicates that the Route is accepted but +// includes an unsupported field. +func NewRouteAcceptedUnsupportedField(msg string) Condition { return Condition{ - Type: string(RouteConditionUnsupportedField), + Type: string(v1.RouteConditionAccepted), Status: metav1.ConditionTrue, Reason: string(RouteReasonUnsupportedField), Message: fmt.Sprintf("The following unsupported parameters were ignored: %s", msg), @@ -971,11 +964,11 @@ func NewGatewayInvalidParameters(msg string) Condition { } } -// NewGatewayUnsupportedField returns a Condition that indicates the Gateway is accepted but +// NewGatewayAcceptedUnsupportedField returns a Condition that indicates the Gateway is accepted but // contains a field that is not supported. -func NewGatewayUnsupportedField(msg string) Condition { +func NewGatewayAcceptedUnsupportedField(msg string) Condition { return Condition{ - Type: string(GatewayConditionUnsupportedField), + Type: string(v1.GatewayConditionAccepted), Status: metav1.ConditionTrue, Reason: string(GatewayReasonUnsupportedField), Message: fmt.Sprintf("Gateway accepted but the following unsupported parameters were ignored: %s", msg), diff --git a/internal/controller/state/graph/gateway.go b/internal/controller/state/graph/gateway.go index 5e53c56d10..4486c58606 100644 --- a/internal/controller/state/graph/gateway.go +++ b/internal/controller/state/graph/gateway.go @@ -275,11 +275,11 @@ func validateUnsupportedGatewayFields(gw *v1.Gateway) []conditions.Condition { var conds []conditions.Condition if gw.Spec.AllowedListeners != nil { - conds = append(conds, conditions.NewGatewayUnsupportedField("AllowedListeners")) + conds = append(conds, conditions.NewGatewayAcceptedUnsupportedField("AllowedListeners")) } if gw.Spec.BackendTLS != nil { - conds = append(conds, conditions.NewGatewayUnsupportedField("BackendTLS")) + conds = append(conds, conditions.NewGatewayAcceptedUnsupportedField("BackendTLS")) } return conds diff --git a/internal/controller/state/graph/gateway_test.go b/internal/controller/state/graph/gateway_test.go index 800f2e0bc7..e3978df1a9 100644 --- a/internal/controller/state/graph/gateway_test.go +++ b/internal/controller/state/graph/gateway_test.go @@ -1561,7 +1561,7 @@ func TestBuildGateway(t *testing.T) { }, }, Conditions: []conditions.Condition{ - conditions.NewGatewayUnsupportedField("AllowedListeners"), + conditions.NewGatewayAcceptedUnsupportedField("AllowedListeners"), conditions.NewGatewayResolvedRefs(), }, }, @@ -1603,7 +1603,7 @@ func TestBuildGateway(t *testing.T) { IPFamily: helpers.GetPointer(ngfAPIv1alpha2.Dual), }, Conditions: []conditions.Condition{ - conditions.NewGatewayUnsupportedField("BackendTLS"), + conditions.NewGatewayAcceptedUnsupportedField("BackendTLS"), conditions.NewGatewayRefInvalid( "spec.infrastructure.parametersRef.kind: Unsupported value: \"wrong-kind\": supported values: \"NginxProxy\"", ), @@ -1946,7 +1946,7 @@ func TestValidateUnsupportedGatewayFields(t *testing.T) { }, }, expectedConds: []conditions.Condition{ - conditions.NewGatewayUnsupportedField("AllowedListeners"), + conditions.NewGatewayAcceptedUnsupportedField("AllowedListeners"), }, }, { @@ -1958,8 +1958,8 @@ func TestValidateUnsupportedGatewayFields(t *testing.T) { }, }, expectedConds: []conditions.Condition{ - conditions.NewGatewayUnsupportedField("AllowedListeners"), - conditions.NewGatewayUnsupportedField("BackendTLS"), + conditions.NewGatewayAcceptedUnsupportedField("AllowedListeners"), + conditions.NewGatewayAcceptedUnsupportedField("BackendTLS"), }, }, } diff --git a/internal/controller/state/graph/grpcroute.go b/internal/controller/state/graph/grpcroute.go index 1ebdeaed0a..5d6e7489e3 100644 --- a/internal/controller/state/graph/grpcroute.go +++ b/internal/controller/state/graph/grpcroute.go @@ -267,7 +267,7 @@ func processGRPCRouteRules( // add warning condition for unsupported fields if any if len(allRulesErrors.warn) > 0 { - conds = append(conds, conditions.NewRouteUnsupportedField(allRulesErrors.warn.ToAggregate().Error())) + conds = append(conds, conditions.NewRouteAcceptedUnsupportedField(allRulesErrors.warn.ToAggregate().Error())) } if len(allRulesErrors.invalid) > 0 { diff --git a/internal/controller/state/graph/grpcroute_test.go b/internal/controller/state/graph/grpcroute_test.go index 9dfc6e0a16..e78f22fe53 100644 --- a/internal/controller/state/graph/grpcroute_test.go +++ b/internal/controller/state/graph/grpcroute_test.go @@ -1140,7 +1140,7 @@ func TestBuildGRPCRoute(t *testing.T) { }, }, Conditions: []conditions.Condition{ - conditions.NewRouteUnsupportedField("spec.rules[0].name: Forbidden: Name"), + conditions.NewRouteAcceptedUnsupportedField("spec.rules[0].name: Forbidden: Name"), }, }, name: "valid route with unsupported field", @@ -1175,7 +1175,7 @@ func TestBuildGRPCRoute(t *testing.T) { }, }, Conditions: []conditions.Condition{ - conditions.NewRouteUnsupportedField("spec.rules[0].name: Forbidden: Name"), + conditions.NewRouteAcceptedUnsupportedField("spec.rules[0].name: Forbidden: Name"), conditions.NewRouteUnsupportedValue( "All rules are invalid: [spec.rules[0].matches[0].method.service: Required value: service is required, " + "spec.rules[0].matches[0].method.method: Required value: method is required]", @@ -1571,7 +1571,7 @@ func TestProcessGRPCRouteRules_UnsupportedFields(t *testing.T) { }, expectedValid: true, expectedConds: []conditions.Condition{ - conditions.NewRouteUnsupportedField("spec.rules[0].name: Forbidden: Name"), + conditions.NewRouteAcceptedUnsupportedField("spec.rules[0].name: Forbidden: Name"), }, expectedWarns: 1, }, @@ -1587,7 +1587,7 @@ func TestProcessGRPCRouteRules_UnsupportedFields(t *testing.T) { }, expectedValid: true, expectedConds: []conditions.Condition{ - conditions.NewRouteUnsupportedField("[spec.rules[0].name: Forbidden: Name, " + + conditions.NewRouteAcceptedUnsupportedField("[spec.rules[0].name: Forbidden: Name, " + "spec.rules[0].sessionPersistence: Forbidden: SessionPersistence]"), }, expectedWarns: 2, diff --git a/internal/controller/state/graph/httproute.go b/internal/controller/state/graph/httproute.go index 80b179969a..5258e217ff 100644 --- a/internal/controller/state/graph/httproute.go +++ b/internal/controller/state/graph/httproute.go @@ -274,7 +274,7 @@ func processHTTPRouteRules( // add warning condition for unsupported fields if any if len(allRulesErrors.warn) > 0 { - conds = append(conds, conditions.NewRouteUnsupportedField(allRulesErrors.warn.ToAggregate().Error())) + conds = append(conds, conditions.NewRouteAcceptedUnsupportedField(allRulesErrors.warn.ToAggregate().Error())) valid = true } diff --git a/internal/controller/state/graph/httproute_test.go b/internal/controller/state/graph/httproute_test.go index 4e7369ef09..3b4121ef9c 100644 --- a/internal/controller/state/graph/httproute_test.go +++ b/internal/controller/state/graph/httproute_test.go @@ -978,7 +978,7 @@ func TestBuildHTTPRoute(t *testing.T) { }, }, Conditions: []conditions.Condition{ - conditions.NewRouteUnsupportedField("spec.rules[0].name: Forbidden: Name"), + conditions.NewRouteAcceptedUnsupportedField("spec.rules[0].name: Forbidden: Name"), }, }, name: "valid route with unsupported field", @@ -1781,7 +1781,7 @@ func TestProcessHTTPRouteRules_UnsupportedFields(t *testing.T) { }, expectedValid: true, expectedConds: []conditions.Condition{ - conditions.NewRouteUnsupportedField("spec.rules[0].name: Forbidden: Name"), + conditions.NewRouteAcceptedUnsupportedField("spec.rules[0].name: Forbidden: Name"), }, expectedWarns: 1, }, @@ -1801,7 +1801,7 @@ func TestProcessHTTPRouteRules_UnsupportedFields(t *testing.T) { }, expectedValid: true, expectedConds: []conditions.Condition{ - conditions.NewRouteUnsupportedField("[spec.rules[0].name: Forbidden: Name, spec.rules[0].timeouts: " + + conditions.NewRouteAcceptedUnsupportedField("[spec.rules[0].name: Forbidden: Name, spec.rules[0].timeouts: " + "Forbidden: Timeouts, spec.rules[0].retry: Forbidden: Retry, " + "spec.rules[0].sessionPersistence: Forbidden: SessionPersistence]"), }, From 8356ef1bb1e925e704e502834c1426a00b1c2be8 Mon Sep 17 00:00:00 2001 From: Tina Usova Date: Thu, 9 Oct 2025 13:02:05 +0100 Subject: [PATCH 7/7] remove valid --- internal/controller/state/graph/httproute.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/controller/state/graph/httproute.go b/internal/controller/state/graph/httproute.go index 5258e217ff..7e3eaf6896 100644 --- a/internal/controller/state/graph/httproute.go +++ b/internal/controller/state/graph/httproute.go @@ -275,7 +275,6 @@ func processHTTPRouteRules( // add warning condition for unsupported fields if any if len(allRulesErrors.warn) > 0 { conds = append(conds, conditions.NewRouteAcceptedUnsupportedField(allRulesErrors.warn.ToAggregate().Error())) - valid = true } if len(allRulesErrors.invalid) > 0 {