diff --git a/apis/v1alpha1/register.go b/apis/v1alpha1/register.go index 0d18c29eaa..7deb5bfb5c 100644 --- a/apis/v1alpha1/register.go +++ b/apis/v1alpha1/register.go @@ -34,8 +34,6 @@ func addKnownTypes(scheme *runtime.Scheme) error { scheme.AddKnownTypes(SchemeGroupVersion, &NginxGateway{}, &NginxGatewayList{}, - &NginxProxy{}, - &NginxProxyList{}, &ObservabilityPolicy{}, &ObservabilityPolicyList{}, &ClientSettingsPolicy{}, diff --git a/apis/v1alpha1/zz_generated.deepcopy.go b/apis/v1alpha1/zz_generated.deepcopy.go index 9624b658aa..a70a3d38a6 100644 --- a/apis/v1alpha1/zz_generated.deepcopy.go +++ b/apis/v1alpha1/zz_generated.deepcopy.go @@ -10,21 +10,6 @@ import ( "sigs.k8s.io/gateway-api/apis/v1alpha2" ) -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Address) DeepCopyInto(out *Address) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Address. -func (in *Address) DeepCopy() *Address { - if in == nil { - return nil - } - out := new(Address) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ClientBody) DeepCopyInto(out *ClientBody) { *out = *in @@ -333,119 +318,6 @@ func (in *NginxGatewayStatus) DeepCopy() *NginxGatewayStatus { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *NginxLogging) DeepCopyInto(out *NginxLogging) { - *out = *in - if in.ErrorLevel != nil { - in, out := &in.ErrorLevel, &out.ErrorLevel - *out = new(NginxErrorLogLevel) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NginxLogging. -func (in *NginxLogging) DeepCopy() *NginxLogging { - if in == nil { - return nil - } - out := new(NginxLogging) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *NginxProxy) DeepCopyInto(out *NginxProxy) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NginxProxy. -func (in *NginxProxy) DeepCopy() *NginxProxy { - if in == nil { - return nil - } - out := new(NginxProxy) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *NginxProxy) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *NginxProxyList) DeepCopyInto(out *NginxProxyList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]NginxProxy, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NginxProxyList. -func (in *NginxProxyList) DeepCopy() *NginxProxyList { - if in == nil { - return nil - } - out := new(NginxProxyList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *NginxProxyList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *NginxProxySpec) DeepCopyInto(out *NginxProxySpec) { - *out = *in - if in.IPFamily != nil { - in, out := &in.IPFamily, &out.IPFamily - *out = new(IPFamilyType) - **out = **in - } - if in.Telemetry != nil { - in, out := &in.Telemetry, &out.Telemetry - *out = new(Telemetry) - (*in).DeepCopyInto(*out) - } - if in.RewriteClientIP != nil { - in, out := &in.RewriteClientIP, &out.RewriteClientIP - *out = new(RewriteClientIP) - (*in).DeepCopyInto(*out) - } - if in.Logging != nil { - in, out := &in.Logging, &out.Logging - *out = new(NginxLogging) - (*in).DeepCopyInto(*out) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NginxProxySpec. -func (in *NginxProxySpec) DeepCopy() *NginxProxySpec { - if in == nil { - return nil - } - out := new(NginxProxySpec) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ObservabilityPolicy) DeepCopyInto(out *ObservabilityPolicy) { *out = *in @@ -530,36 +402,6 @@ func (in *ObservabilityPolicySpec) DeepCopy() *ObservabilityPolicySpec { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RewriteClientIP) DeepCopyInto(out *RewriteClientIP) { - *out = *in - if in.Mode != nil { - in, out := &in.Mode, &out.Mode - *out = new(RewriteClientIPModeType) - **out = **in - } - if in.SetIPRecursively != nil { - in, out := &in.SetIPRecursively, &out.SetIPRecursively - *out = new(bool) - **out = **in - } - if in.TrustedAddresses != nil { - in, out := &in.TrustedAddresses, &out.TrustedAddresses - *out = make([]Address, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RewriteClientIP. -func (in *RewriteClientIP) DeepCopy() *RewriteClientIP { - if in == nil { - return nil - } - out := new(RewriteClientIP) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Snippet) DeepCopyInto(out *Snippet) { *out = *in @@ -691,66 +533,6 @@ func (in *SpanAttribute) DeepCopy() *SpanAttribute { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Telemetry) DeepCopyInto(out *Telemetry) { - *out = *in - if in.Exporter != nil { - in, out := &in.Exporter, &out.Exporter - *out = new(TelemetryExporter) - (*in).DeepCopyInto(*out) - } - if in.ServiceName != nil { - in, out := &in.ServiceName, &out.ServiceName - *out = new(string) - **out = **in - } - if in.SpanAttributes != nil { - in, out := &in.SpanAttributes, &out.SpanAttributes - *out = make([]SpanAttribute, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Telemetry. -func (in *Telemetry) DeepCopy() *Telemetry { - if in == nil { - return nil - } - out := new(Telemetry) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *TelemetryExporter) DeepCopyInto(out *TelemetryExporter) { - *out = *in - if in.Interval != nil { - in, out := &in.Interval, &out.Interval - *out = new(Duration) - **out = **in - } - if in.BatchSize != nil { - in, out := &in.BatchSize, &out.BatchSize - *out = new(int32) - **out = **in - } - if in.BatchCount != nil { - in, out := &in.BatchCount, &out.BatchCount - *out = new(int32) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TelemetryExporter. -func (in *TelemetryExporter) DeepCopy() *TelemetryExporter { - if in == nil { - return nil - } - out := new(TelemetryExporter) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Tracing) DeepCopyInto(out *Tracing) { *out = *in diff --git a/apis/v1alpha2/doc.go b/apis/v1alpha2/doc.go new file mode 100644 index 0000000000..6e93a89ac7 --- /dev/null +++ b/apis/v1alpha2/doc.go @@ -0,0 +1,6 @@ +// Package v1alpha2 contains API Schema definitions for the +// gateway.nginx.org API group. +// +// +kubebuilder:object:generate=true +// +groupName=gateway.nginx.org +package v1alpha2 diff --git a/apis/v1alpha1/nginxproxy_types.go b/apis/v1alpha2/nginxproxy_types.go similarity index 86% rename from apis/v1alpha1/nginxproxy_types.go rename to apis/v1alpha2/nginxproxy_types.go index 6292962ec2..b3a7835887 100644 --- a/apis/v1alpha1/nginxproxy_types.go +++ b/apis/v1alpha2/nginxproxy_types.go @@ -1,15 +1,23 @@ -package v1alpha1 +package v1alpha2 -import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha1" +) // +genclient // +kubebuilder:object:root=true // +kubebuilder:storageversion -// +kubebuilder:resource:categories=nginx-gateway-fabric,scope=Cluster +// +kubebuilder:resource:categories=nginx-gateway-fabric,scope=Namespaced // +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` -// NginxProxy is a configuration object that is attached to a GatewayClass parametersRef. It provides a way -// to configure global settings for all Gateways defined from the GatewayClass. +// NginxProxy is a configuration object that can be referenced from a GatewayClass parametersRef +// or a Gateway infrastructure.parametersRef. It provides a way to configure data plane settings. +// If referenced from a GatewayClass, the settings apply to all Gateways attached to the GatewayClass. +// If referenced from a Gateway, the settings apply to that Gateway alone. If both a Gateway and its GatewayClass +// reference an NginxProxy, the settings are merged. Settings specified on the Gateway NginxProxy override those +// set on the GatewayClass NginxProxy. type NginxProxy struct { //nolint:govet // standard field alignment, don't change it metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` @@ -50,14 +58,18 @@ type NginxProxySpec struct { // +optional Logging *NginxLogging `json:"logging,omitempty"` // DisableHTTP2 defines if http2 should be disabled for all servers. - // Default is false, meaning http2 will be enabled for all servers. + // If not specified, or set to false, http2 will be enabled for all servers. // // +optional - DisableHTTP2 bool `json:"disableHTTP2,omitempty"` + DisableHTTP2 *bool `json:"disableHTTP2,omitempty"` } // Telemetry specifies the OpenTelemetry configuration. type Telemetry struct { + // DisabledFeatures specifies OpenTelemetry features to be disabled. + // + // +optional + DisabledFeatures []DisableTelemetryFeature `json:"disabledFeatures,omitempty"` // Exporter specifies OpenTelemetry export parameters. // // +optional @@ -78,7 +90,7 @@ type Telemetry struct { // +listType=map // +listMapKey=key // +kubebuilder:validation:MaxItems=64 - SpanAttributes []SpanAttribute `json:"spanAttributes,omitempty"` + SpanAttributes []v1alpha1.SpanAttribute `json:"spanAttributes,omitempty"` } // TelemetryExporter specifies OpenTelemetry export parameters. @@ -87,7 +99,7 @@ type TelemetryExporter struct { // Default: https://nginx.org/en/docs/ngx_otel_module.html#otel_exporter // // +optional - Interval *Duration `json:"interval,omitempty"` + Interval *v1alpha1.Duration `json:"interval,omitempty"` // BatchSize is the maximum number of spans to be sent in one batch per worker. // Default: https://nginx.org/en/docs/ngx_otel_module.html#otel_exporter @@ -107,8 +119,9 @@ type TelemetryExporter struct { // Format: alphanumeric hostname with optional http scheme and optional port. // //nolint:lll + // +optional // +kubebuilder:validation:Pattern=`^(?:http?:\/\/)?[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)*(?::\d{1,5})?$` - Endpoint string `json:"endpoint"` + Endpoint *string `json:"endpoint,omitempty"` } // RewriteClientIP specifies the configuration for rewriting the client's IP address. @@ -139,15 +152,12 @@ type RewriteClientIP struct { // If a request comes from a trusted address, NGINX will rewrite the client IP information, // and forward it to the backend in the X-Forwarded-For* and X-Real-IP headers. // If the request does not come from a trusted address, NGINX will not rewrite the client IP information. - // TrustedAddresses only supports CIDR blocks: 192.33.21.1/24, fe80::1/64. // To trust all addresses (not recommended for production), set to 0.0.0.0/0. // If no addresses are provided, NGINX will not rewrite the client IP information. // Sets NGINX directive set_real_ip_from: https://nginx.org/en/docs/http/ngx_http_realip_module.html#set_real_ip_from // This field is required if mode is set. // // +optional - // +listType=map - // +listMapKey=type // +kubebuilder:validation:MaxItems=16 TrustedAddresses []Address `json:"trustedAddresses,omitempty"` } @@ -249,3 +259,13 @@ const ( // NginxLogLevelEmerg is the emerg level for NGINX error logs. NginxLogLevelEmerg NginxErrorLogLevel = "emerg" ) + +// DisableTelemetryFeature is a telemetry feature that can be disabled. +// +// +kubebuilder:validation:Enum=DisableTracing +type DisableTelemetryFeature string + +const ( + // DisableTracing disables the OpenTelemetry tracing feature. + DisableTracing DisableTelemetryFeature = "DisableTracing" +) diff --git a/apis/v1alpha2/register.go b/apis/v1alpha2/register.go new file mode 100644 index 0000000000..f4dd5ace32 --- /dev/null +++ b/apis/v1alpha2/register.go @@ -0,0 +1,41 @@ +package v1alpha2 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// GroupName specifies the group name used to register the objects. +const GroupName = "gateway.nginx.org" + +// SchemeGroupVersion is group version used to register these objects. +var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"} + +// Resource takes an unqualified resource and returns a Group qualified GroupResource. +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +var ( + // SchemeBuilder collects functions that add things to a scheme. It's to allow + // code to compile without explicitly referencing generated types. You should + // declare one in each package that will have generated deep copy or conversion + // functions. + SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) + + // AddToScheme applies all the stored functions to the scheme. A non-nil error + // indicates that one function failed and the attempt was abandoned. + AddToScheme = SchemeBuilder.AddToScheme +) + +// Adds the list of known types to Scheme. +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + &NginxProxy{}, + &NginxProxyList{}, + ) + // AddToGroupVersion allows the serialization of client types like ListOptions. + metav1.AddToGroupVersion(scheme, SchemeGroupVersion) + return nil +} diff --git a/apis/v1alpha2/zz_generated.deepcopy.go b/apis/v1alpha2/zz_generated.deepcopy.go new file mode 100644 index 0000000000..0518e7f0c2 --- /dev/null +++ b/apis/v1alpha2/zz_generated.deepcopy.go @@ -0,0 +1,243 @@ +//go:build !ignore_autogenerated + +// Code generated by controller-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha1" + "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Address) DeepCopyInto(out *Address) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Address. +func (in *Address) DeepCopy() *Address { + if in == nil { + return nil + } + out := new(Address) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NginxLogging) DeepCopyInto(out *NginxLogging) { + *out = *in + if in.ErrorLevel != nil { + in, out := &in.ErrorLevel, &out.ErrorLevel + *out = new(NginxErrorLogLevel) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NginxLogging. +func (in *NginxLogging) DeepCopy() *NginxLogging { + if in == nil { + return nil + } + out := new(NginxLogging) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NginxProxy) DeepCopyInto(out *NginxProxy) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NginxProxy. +func (in *NginxProxy) DeepCopy() *NginxProxy { + if in == nil { + return nil + } + out := new(NginxProxy) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *NginxProxy) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NginxProxyList) DeepCopyInto(out *NginxProxyList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]NginxProxy, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NginxProxyList. +func (in *NginxProxyList) DeepCopy() *NginxProxyList { + if in == nil { + return nil + } + out := new(NginxProxyList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *NginxProxyList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NginxProxySpec) DeepCopyInto(out *NginxProxySpec) { + *out = *in + if in.IPFamily != nil { + in, out := &in.IPFamily, &out.IPFamily + *out = new(IPFamilyType) + **out = **in + } + if in.Telemetry != nil { + in, out := &in.Telemetry, &out.Telemetry + *out = new(Telemetry) + (*in).DeepCopyInto(*out) + } + if in.RewriteClientIP != nil { + in, out := &in.RewriteClientIP, &out.RewriteClientIP + *out = new(RewriteClientIP) + (*in).DeepCopyInto(*out) + } + if in.Logging != nil { + in, out := &in.Logging, &out.Logging + *out = new(NginxLogging) + (*in).DeepCopyInto(*out) + } + if in.DisableHTTP2 != nil { + in, out := &in.DisableHTTP2, &out.DisableHTTP2 + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NginxProxySpec. +func (in *NginxProxySpec) DeepCopy() *NginxProxySpec { + if in == nil { + return nil + } + out := new(NginxProxySpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RewriteClientIP) DeepCopyInto(out *RewriteClientIP) { + *out = *in + if in.Mode != nil { + in, out := &in.Mode, &out.Mode + *out = new(RewriteClientIPModeType) + **out = **in + } + if in.SetIPRecursively != nil { + in, out := &in.SetIPRecursively, &out.SetIPRecursively + *out = new(bool) + **out = **in + } + if in.TrustedAddresses != nil { + in, out := &in.TrustedAddresses, &out.TrustedAddresses + *out = make([]Address, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RewriteClientIP. +func (in *RewriteClientIP) DeepCopy() *RewriteClientIP { + if in == nil { + return nil + } + out := new(RewriteClientIP) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Telemetry) DeepCopyInto(out *Telemetry) { + *out = *in + if in.DisabledFeatures != nil { + in, out := &in.DisabledFeatures, &out.DisabledFeatures + *out = make([]DisableTelemetryFeature, len(*in)) + copy(*out, *in) + } + if in.Exporter != nil { + in, out := &in.Exporter, &out.Exporter + *out = new(TelemetryExporter) + (*in).DeepCopyInto(*out) + } + if in.ServiceName != nil { + in, out := &in.ServiceName, &out.ServiceName + *out = new(string) + **out = **in + } + if in.SpanAttributes != nil { + in, out := &in.SpanAttributes, &out.SpanAttributes + *out = make([]v1alpha1.SpanAttribute, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Telemetry. +func (in *Telemetry) DeepCopy() *Telemetry { + if in == nil { + return nil + } + out := new(Telemetry) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TelemetryExporter) DeepCopyInto(out *TelemetryExporter) { + *out = *in + if in.Interval != nil { + in, out := &in.Interval, &out.Interval + *out = new(v1alpha1.Duration) + **out = **in + } + if in.BatchSize != nil { + in, out := &in.BatchSize, &out.BatchSize + *out = new(int32) + **out = **in + } + if in.BatchCount != nil { + in, out := &in.BatchCount, &out.BatchCount + *out = new(int32) + **out = **in + } + if in.Endpoint != nil { + in, out := &in.Endpoint, &out.Endpoint + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TelemetryExporter. +func (in *TelemetryExporter) DeepCopy() *TelemetryExporter { + if in == nil { + return nil + } + out := new(TelemetryExporter) + in.DeepCopyInto(out) + return out +} diff --git a/charts/nginx-gateway-fabric/templates/gatewayclass.yaml b/charts/nginx-gateway-fabric/templates/gatewayclass.yaml index ee08e1a726..aecd54e8ad 100644 --- a/charts/nginx-gateway-fabric/templates/gatewayclass.yaml +++ b/charts/nginx-gateway-fabric/templates/gatewayclass.yaml @@ -17,4 +17,5 @@ spec: group: gateway.nginx.org kind: NginxProxy name: {{ include "nginx-gateway.proxy-config-name" . }} + namespace: {{ .Release.Namespace }} {{- end }} diff --git a/charts/nginx-gateway-fabric/templates/nginxproxy.yaml b/charts/nginx-gateway-fabric/templates/nginxproxy.yaml index 4214158b75..bc4105ee37 100644 --- a/charts/nginx-gateway-fabric/templates/nginxproxy.yaml +++ b/charts/nginx-gateway-fabric/templates/nginxproxy.yaml @@ -1,8 +1,9 @@ {{- if .Values.nginx.config }} -apiVersion: gateway.nginx.org/v1alpha1 +apiVersion: gateway.nginx.org/v1alpha2 kind: NginxProxy metadata: name: {{ include "nginx-gateway.proxy-config-name" . }} + namespace: {{ .Release.Namespace }} labels: {{- include "nginx-gateway.labels" . | nindent 4 }} spec: diff --git a/charts/nginx-gateway-fabric/values.schema.json b/charts/nginx-gateway-fabric/values.schema.json index 651fea311c..2cbf5f3082 100644 --- a/charts/nginx-gateway-fabric/values.schema.json +++ b/charts/nginx-gateway-fabric/values.schema.json @@ -137,6 +137,17 @@ "telemetry": { "description": "Telemetry specifies the OpenTelemetry configuration.", "properties": { + "disabledFeatures": { + "items": { + "enum": [ + "DisableTracing" + ], + "required": [], + "type": "string" + }, + "required": [], + "type": "array" + }, "exporter": { "properties": { "batchCount": { diff --git a/charts/nginx-gateway-fabric/values.yaml b/charts/nginx-gateway-fabric/values.yaml index 4168c3d66e..e59b764c4e 100644 --- a/charts/nginx-gateway-fabric/values.yaml +++ b/charts/nginx-gateway-fabric/values.yaml @@ -233,6 +233,12 @@ nginx: # pattern: ^([^"$\\]|\\[^$])*$ # minLength: 1 # maxLength: 255 + # disabledFeatures: + # type: array + # items: + # type: string + # enum: + # - DisableTracing # logging: # type: object # description: Logging defines logging related settings for NGINX. diff --git a/config/crd/bases/gateway.nginx.org_nginxproxies.yaml b/config/crd/bases/gateway.nginx.org_nginxproxies.yaml index 21b61ce37f..9a641ea996 100644 --- a/config/crd/bases/gateway.nginx.org_nginxproxies.yaml +++ b/config/crd/bases/gateway.nginx.org_nginxproxies.yaml @@ -14,18 +14,22 @@ spec: listKind: NginxProxyList plural: nginxproxies singular: nginxproxy - scope: Cluster + scope: Namespaced versions: - additionalPrinterColumns: - jsonPath: .metadata.creationTimestamp name: Age type: date - name: v1alpha1 + name: v1alpha2 schema: openAPIV3Schema: description: |- - NginxProxy is a configuration object that is attached to a GatewayClass parametersRef. It provides a way - to configure global settings for all Gateways defined from the GatewayClass. + NginxProxy is a configuration object that can be referenced from a GatewayClass parametersRef + or a Gateway infrastructure.parametersRef. It provides a way to configure data plane settings. + If referenced from a GatewayClass, the settings apply to all Gateways attached to the GatewayClass. + If referenced from a Gateway, the settings apply to that Gateway alone. If both a Gateway and its GatewayClass + reference an NginxProxy, the settings are merged. Settings specified on the Gateway NginxProxy override those + set on the GatewayClass NginxProxy. properties: apiVersion: description: |- @@ -50,7 +54,7 @@ spec: disableHTTP2: description: |- DisableHTTP2 defines if http2 should be disabled for all servers. - Default is false, meaning http2 will be enabled for all servers. + If not specified, or set to false, http2 will be enabled for all servers. type: boolean ipFamily: default: dual @@ -116,7 +120,6 @@ spec: If a request comes from a trusted address, NGINX will rewrite the client IP information, and forward it to the backend in the X-Forwarded-For* and X-Real-IP headers. If the request does not come from a trusted address, NGINX will not rewrite the client IP information. - TrustedAddresses only supports CIDR blocks: 192.33.21.1/24, fe80::1/64. To trust all addresses (not recommended for production), set to 0.0.0.0/0. If no addresses are provided, NGINX will not rewrite the client IP information. Sets NGINX directive set_real_ip_from: https://nginx.org/en/docs/http/ngx_http_realip_module.html#set_real_ip_from @@ -141,9 +144,6 @@ spec: type: object maxItems: 16 type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map type: object x-kubernetes-validations: - message: if mode is set, trustedAddresses is a required field @@ -152,6 +152,16 @@ spec: telemetry: description: Telemetry specifies the OpenTelemetry configuration. properties: + disabledFeatures: + description: DisabledFeatures specifies OpenTelemetry features + to be disabled. + items: + description: DisableTelemetryFeature is a telemetry feature + that can be disabled. + enum: + - DisableTracing + type: string + type: array exporter: description: Exporter specifies OpenTelemetry export parameters. properties: @@ -181,8 +191,6 @@ spec: Default: https://nginx.org/en/docs/ngx_otel_module.html#otel_exporter pattern: ^[0-9]{1,4}(ms|s|m|h)?$ type: string - required: - - endpoint type: object serviceName: description: |- diff --git a/deploy/crds.yaml b/deploy/crds.yaml index 5a9d708c60..b6d058d53a 100644 --- a/deploy/crds.yaml +++ b/deploy/crds.yaml @@ -599,18 +599,22 @@ spec: listKind: NginxProxyList plural: nginxproxies singular: nginxproxy - scope: Cluster + scope: Namespaced versions: - additionalPrinterColumns: - jsonPath: .metadata.creationTimestamp name: Age type: date - name: v1alpha1 + name: v1alpha2 schema: openAPIV3Schema: description: |- - NginxProxy is a configuration object that is attached to a GatewayClass parametersRef. It provides a way - to configure global settings for all Gateways defined from the GatewayClass. + NginxProxy is a configuration object that can be referenced from a GatewayClass parametersRef + or a Gateway infrastructure.parametersRef. It provides a way to configure data plane settings. + If referenced from a GatewayClass, the settings apply to all Gateways attached to the GatewayClass. + If referenced from a Gateway, the settings apply to that Gateway alone. If both a Gateway and its GatewayClass + reference an NginxProxy, the settings are merged. Settings specified on the Gateway NginxProxy override those + set on the GatewayClass NginxProxy. properties: apiVersion: description: |- @@ -635,7 +639,7 @@ spec: disableHTTP2: description: |- DisableHTTP2 defines if http2 should be disabled for all servers. - Default is false, meaning http2 will be enabled for all servers. + If not specified, or set to false, http2 will be enabled for all servers. type: boolean ipFamily: default: dual @@ -701,7 +705,6 @@ spec: If a request comes from a trusted address, NGINX will rewrite the client IP information, and forward it to the backend in the X-Forwarded-For* and X-Real-IP headers. If the request does not come from a trusted address, NGINX will not rewrite the client IP information. - TrustedAddresses only supports CIDR blocks: 192.33.21.1/24, fe80::1/64. To trust all addresses (not recommended for production), set to 0.0.0.0/0. If no addresses are provided, NGINX will not rewrite the client IP information. Sets NGINX directive set_real_ip_from: https://nginx.org/en/docs/http/ngx_http_realip_module.html#set_real_ip_from @@ -726,9 +729,6 @@ spec: type: object maxItems: 16 type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map type: object x-kubernetes-validations: - message: if mode is set, trustedAddresses is a required field @@ -737,6 +737,16 @@ spec: telemetry: description: Telemetry specifies the OpenTelemetry configuration. properties: + disabledFeatures: + description: DisabledFeatures specifies OpenTelemetry features + to be disabled. + items: + description: DisableTelemetryFeature is a telemetry feature + that can be disabled. + enum: + - DisableTracing + type: string + type: array exporter: description: Exporter specifies OpenTelemetry export parameters. properties: @@ -766,8 +776,6 @@ spec: Default: https://nginx.org/en/docs/ngx_otel_module.html#otel_exporter pattern: ^[0-9]{1,4}(ms|s|m|h)?$ type: string - required: - - endpoint type: object serviceName: description: |- diff --git a/internal/mode/static/manager.go b/internal/mode/static/manager.go index b6da949c52..c5df4d1495 100644 --- a/internal/mode/static/manager.go +++ b/internal/mode/static/manager.go @@ -34,7 +34,8 @@ import ( gatewayv1alpha3 "sigs.k8s.io/gateway-api/apis/v1alpha3" gatewayv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" - ngfAPI "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha1" + ngfAPIv1alpha1 "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha1" + ngfAPIv1alpha2 "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha2" "github.com/nginxinc/nginx-gateway-fabric/internal/framework/controller" "github.com/nginxinc/nginx-gateway-fabric/internal/framework/controller/filter" "github.com/nginxinc/nginx-gateway-fabric/internal/framework/controller/index" @@ -84,7 +85,8 @@ func init() { utilruntime.Must(gatewayv1alpha2.Install(scheme)) utilruntime.Must(apiv1.AddToScheme(scheme)) utilruntime.Must(discoveryV1.AddToScheme(scheme)) - utilruntime.Must(ngfAPI.AddToScheme(scheme)) + utilruntime.Must(ngfAPIv1alpha1.AddToScheme(scheme)) + utilruntime.Must(ngfAPIv1alpha2.AddToScheme(scheme)) utilruntime.Must(apiext.AddToScheme(scheme)) utilruntime.Must(appsv1.AddToScheme(scheme)) } @@ -276,15 +278,15 @@ func createPolicyManager( ) *policies.CompositeValidator { cfgs := []policies.ManagerConfig{ { - GVK: mustExtractGVK(&ngfAPI.ClientSettingsPolicy{}), + GVK: mustExtractGVK(&ngfAPIv1alpha1.ClientSettingsPolicy{}), Validator: clientsettings.NewValidator(validator), }, { - GVK: mustExtractGVK(&ngfAPI.ObservabilityPolicy{}), + GVK: mustExtractGVK(&ngfAPIv1alpha1.ObservabilityPolicy{}), Validator: observability.NewValidator(validator), }, { - GVK: mustExtractGVK(&ngfAPI.UpstreamSettingsPolicy{}), + GVK: mustExtractGVK(&ngfAPIv1alpha1.UpstreamSettingsPolicy{}), Validator: upstreamsettings.NewValidator(validator), }, } @@ -445,7 +447,7 @@ func registerControllers( }, }, { - objectType: &ngfAPI.NginxProxy{}, + objectType: &ngfAPIv1alpha2.NginxProxy{}, options: []controller.Option{ controller.WithK8sPredicate(k8spredicate.GenerationChangedPredicate{}), }, @@ -457,19 +459,19 @@ func registerControllers( }, }, { - objectType: &ngfAPI.ClientSettingsPolicy{}, + objectType: &ngfAPIv1alpha1.ClientSettingsPolicy{}, options: []controller.Option{ controller.WithK8sPredicate(k8spredicate.GenerationChangedPredicate{}), }, }, { - objectType: &ngfAPI.ObservabilityPolicy{}, + objectType: &ngfAPIv1alpha1.ObservabilityPolicy{}, options: []controller.Option{ controller.WithK8sPredicate(k8spredicate.GenerationChangedPredicate{}), }, }, { - objectType: &ngfAPI.UpstreamSettingsPolicy{}, + objectType: &ngfAPIv1alpha1.UpstreamSettingsPolicy{}, options: []controller.Option{ controller.WithK8sPredicate(k8spredicate.GenerationChangedPredicate{}), }, @@ -502,7 +504,7 @@ func registerControllers( if cfg.ConfigName != "" { controllerRegCfgs = append(controllerRegCfgs, ctlrCfg{ - objectType: &ngfAPI.NginxGateway{}, + objectType: &ngfAPIv1alpha1.NginxGateway{}, options: []controller.Option{ controller.WithNamespacedNameFilter(filter.CreateSingleResourceFilter(controlConfigNSName)), }, @@ -521,7 +523,7 @@ func registerControllers( if cfg.SnippetsFilters { controllerRegCfgs = append(controllerRegCfgs, ctlrCfg{ - objectType: &ngfAPI.SnippetsFilter{}, + objectType: &ngfAPIv1alpha1.SnippetsFilter{}, options: []controller.Option{ controller.WithK8sPredicate(k8spredicate.GenerationChangedPredicate{}), }, @@ -706,11 +708,11 @@ func prepareFirstEventBatchPreparerArgs(cfg config.Config) ([]client.Object, []c &discoveryV1.EndpointSliceList{}, &gatewayv1.HTTPRouteList{}, &gatewayv1beta1.ReferenceGrantList{}, - &ngfAPI.NginxProxyList{}, + &ngfAPIv1alpha2.NginxProxyList{}, &gatewayv1.GRPCRouteList{}, - &ngfAPI.ClientSettingsPolicyList{}, - &ngfAPI.ObservabilityPolicyList{}, - &ngfAPI.UpstreamSettingsPolicyList{}, + &ngfAPIv1alpha1.ClientSettingsPolicyList{}, + &ngfAPIv1alpha1.ObservabilityPolicyList{}, + &ngfAPIv1alpha1.UpstreamSettingsPolicyList{}, partialObjectMetadataList, } @@ -726,7 +728,7 @@ func prepareFirstEventBatchPreparerArgs(cfg config.Config) ([]client.Object, []c if cfg.SnippetsFilters { objectLists = append( objectLists, - &ngfAPI.SnippetsFilterList{}, + &ngfAPIv1alpha1.SnippetsFilterList{}, ) } @@ -754,7 +756,7 @@ func setInitialConfig( ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - var conf ngfAPI.NginxGateway + var conf ngfAPIv1alpha1.NginxGateway // Polling to wait for CRD to exist if the Deployment is created first. if err := wait.PollUntilContextCancel( ctx, diff --git a/internal/mode/static/manager_test.go b/internal/mode/static/manager_test.go index 0f2d802d32..d2206a633c 100644 --- a/internal/mode/static/manager_test.go +++ b/internal/mode/static/manager_test.go @@ -19,7 +19,8 @@ import ( gatewayv1alpha3 "sigs.k8s.io/gateway-api/apis/v1alpha3" gatewayv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" - ngfAPI "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha1" + ngfAPIv1alpha1 "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha1" + ngfAPIv1alpha2 "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha2" "github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/config" "github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/state/graph" ) @@ -62,12 +63,12 @@ func TestPrepareFirstEventBatchPreparerArgs(t *testing.T) { &gatewayv1.HTTPRouteList{}, &gatewayv1.GatewayList{}, &gatewayv1beta1.ReferenceGrantList{}, - &ngfAPI.NginxProxyList{}, + &ngfAPIv1alpha2.NginxProxyList{}, &gatewayv1.GRPCRouteList{}, partialObjectMetadataList, - &ngfAPI.ClientSettingsPolicyList{}, - &ngfAPI.ObservabilityPolicyList{}, - &ngfAPI.UpstreamSettingsPolicyList{}, + &ngfAPIv1alpha1.ClientSettingsPolicyList{}, + &ngfAPIv1alpha1.ObservabilityPolicyList{}, + &ngfAPIv1alpha1.UpstreamSettingsPolicyList{}, }, }, { @@ -92,12 +93,12 @@ func TestPrepareFirstEventBatchPreparerArgs(t *testing.T) { &discoveryV1.EndpointSliceList{}, &gatewayv1.HTTPRouteList{}, &gatewayv1beta1.ReferenceGrantList{}, - &ngfAPI.NginxProxyList{}, + &ngfAPIv1alpha2.NginxProxyList{}, &gatewayv1.GRPCRouteList{}, partialObjectMetadataList, - &ngfAPI.ClientSettingsPolicyList{}, - &ngfAPI.ObservabilityPolicyList{}, - &ngfAPI.UpstreamSettingsPolicyList{}, + &ngfAPIv1alpha1.ClientSettingsPolicyList{}, + &ngfAPIv1alpha1.ObservabilityPolicyList{}, + &ngfAPIv1alpha1.UpstreamSettingsPolicyList{}, }, }, { @@ -123,14 +124,14 @@ func TestPrepareFirstEventBatchPreparerArgs(t *testing.T) { &discoveryV1.EndpointSliceList{}, &gatewayv1.HTTPRouteList{}, &gatewayv1beta1.ReferenceGrantList{}, - &ngfAPI.NginxProxyList{}, + &ngfAPIv1alpha2.NginxProxyList{}, partialObjectMetadataList, &gatewayv1alpha3.BackendTLSPolicyList{}, &gatewayv1alpha2.TLSRouteList{}, &gatewayv1.GRPCRouteList{}, - &ngfAPI.ClientSettingsPolicyList{}, - &ngfAPI.ObservabilityPolicyList{}, - &ngfAPI.UpstreamSettingsPolicyList{}, + &ngfAPIv1alpha1.ClientSettingsPolicyList{}, + &ngfAPIv1alpha1.ObservabilityPolicyList{}, + &ngfAPIv1alpha1.UpstreamSettingsPolicyList{}, }, }, { @@ -155,13 +156,13 @@ func TestPrepareFirstEventBatchPreparerArgs(t *testing.T) { &discoveryV1.EndpointSliceList{}, &gatewayv1.HTTPRouteList{}, &gatewayv1beta1.ReferenceGrantList{}, - &ngfAPI.NginxProxyList{}, + &ngfAPIv1alpha2.NginxProxyList{}, partialObjectMetadataList, &gatewayv1.GRPCRouteList{}, - &ngfAPI.ClientSettingsPolicyList{}, - &ngfAPI.ObservabilityPolicyList{}, - &ngfAPI.SnippetsFilterList{}, - &ngfAPI.UpstreamSettingsPolicyList{}, + &ngfAPIv1alpha1.ClientSettingsPolicyList{}, + &ngfAPIv1alpha1.ObservabilityPolicyList{}, + &ngfAPIv1alpha1.SnippetsFilterList{}, + &ngfAPIv1alpha1.UpstreamSettingsPolicyList{}, }, }, { @@ -187,15 +188,15 @@ func TestPrepareFirstEventBatchPreparerArgs(t *testing.T) { &discoveryV1.EndpointSliceList{}, &gatewayv1.HTTPRouteList{}, &gatewayv1beta1.ReferenceGrantList{}, - &ngfAPI.NginxProxyList{}, + &ngfAPIv1alpha2.NginxProxyList{}, partialObjectMetadataList, &gatewayv1alpha3.BackendTLSPolicyList{}, &gatewayv1alpha2.TLSRouteList{}, &gatewayv1.GRPCRouteList{}, - &ngfAPI.ClientSettingsPolicyList{}, - &ngfAPI.ObservabilityPolicyList{}, - &ngfAPI.SnippetsFilterList{}, - &ngfAPI.UpstreamSettingsPolicyList{}, + &ngfAPIv1alpha1.ClientSettingsPolicyList{}, + &ngfAPIv1alpha1.ObservabilityPolicyList{}, + &ngfAPIv1alpha1.SnippetsFilterList{}, + &ngfAPIv1alpha1.UpstreamSettingsPolicyList{}, }, }, } diff --git a/internal/mode/static/state/change_processor.go b/internal/mode/static/state/change_processor.go index 82edbcc7b1..8a889ae81d 100644 --- a/internal/mode/static/state/change_processor.go +++ b/internal/mode/static/state/change_processor.go @@ -16,7 +16,8 @@ import ( "sigs.k8s.io/gateway-api/apis/v1alpha3" "sigs.k8s.io/gateway-api/apis/v1beta1" - ngfAPI "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha1" + ngfAPIv1alpha1 "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha1" + ngfAPIv1alpha2 "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha2" "github.com/nginxinc/nginx-gateway-fabric/internal/framework/gatewayclass" "github.com/nginxinc/nginx-gateway-fabric/internal/framework/kinds" ngftypes "github.com/nginxinc/nginx-gateway-fabric/internal/framework/types" @@ -108,11 +109,11 @@ func NewChangeProcessorImpl(cfg ChangeProcessorConfig) *ChangeProcessorImpl { CRDMetadata: make(map[types.NamespacedName]*metav1.PartialObjectMetadata), BackendTLSPolicies: make(map[types.NamespacedName]*v1alpha3.BackendTLSPolicy), ConfigMaps: make(map[types.NamespacedName]*apiv1.ConfigMap), - NginxProxies: make(map[types.NamespacedName]*ngfAPI.NginxProxy), + NginxProxies: make(map[types.NamespacedName]*ngfAPIv1alpha2.NginxProxy), GRPCRoutes: make(map[types.NamespacedName]*v1.GRPCRoute), TLSRoutes: make(map[types.NamespacedName]*v1alpha2.TLSRoute), NGFPolicies: make(map[graph.PolicyKey]policies.Policy), - SnippetsFilters: make(map[types.NamespacedName]*ngfAPI.SnippetsFilter), + SnippetsFilters: make(map[types.NamespacedName]*ngfAPIv1alpha1.SnippetsFilter), } processor := &ChangeProcessorImpl{ @@ -202,22 +203,22 @@ func NewChangeProcessorImpl(cfg ChangeProcessorConfig) *ChangeProcessorImpl { predicate: annotationChangedPredicate{annotation: gatewayclass.BundleVersionAnnotation}, }, { - gvk: cfg.MustExtractGVK(&ngfAPI.NginxProxy{}), + gvk: cfg.MustExtractGVK(&ngfAPIv1alpha2.NginxProxy{}), store: newObjectStoreMapAdapter(clusterStore.NginxProxies), predicate: funcPredicate{stateChanged: isReferenced}, }, { - gvk: cfg.MustExtractGVK(&ngfAPI.ClientSettingsPolicy{}), + gvk: cfg.MustExtractGVK(&ngfAPIv1alpha1.ClientSettingsPolicy{}), store: commonPolicyObjectStore, predicate: funcPredicate{stateChanged: isNGFPolicyRelevant}, }, { - gvk: cfg.MustExtractGVK(&ngfAPI.ObservabilityPolicy{}), + gvk: cfg.MustExtractGVK(&ngfAPIv1alpha1.ObservabilityPolicy{}), store: commonPolicyObjectStore, predicate: funcPredicate{stateChanged: isNGFPolicyRelevant}, }, { - gvk: cfg.MustExtractGVK(&ngfAPI.UpstreamSettingsPolicy{}), + gvk: cfg.MustExtractGVK(&ngfAPIv1alpha1.UpstreamSettingsPolicy{}), store: commonPolicyObjectStore, predicate: funcPredicate{stateChanged: isNGFPolicyRelevant}, }, @@ -227,7 +228,7 @@ func NewChangeProcessorImpl(cfg ChangeProcessorConfig) *ChangeProcessorImpl { predicate: nil, }, { - gvk: cfg.MustExtractGVK(&ngfAPI.SnippetsFilter{}), + gvk: cfg.MustExtractGVK(&ngfAPIv1alpha1.SnippetsFilter{}), store: newObjectStoreMapAdapter(clusterStore.SnippetsFilters), predicate: nil, // we always want to write status to SnippetsFilters so we don't filter them out }, diff --git a/internal/mode/static/state/change_processor_test.go b/internal/mode/static/state/change_processor_test.go index 03d86a377b..85c59099c1 100644 --- a/internal/mode/static/state/change_processor_test.go +++ b/internal/mode/static/state/change_processor_test.go @@ -19,7 +19,8 @@ import ( "sigs.k8s.io/gateway-api/apis/v1alpha3" "sigs.k8s.io/gateway-api/apis/v1beta1" - ngfAPI "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha1" + ngfAPIv1alpha1 "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha1" + ngfAPIv1alpha2 "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha2" "github.com/nginxinc/nginx-gateway-fabric/internal/framework/conditions" "github.com/nginxinc/nginx-gateway-fabric/internal/framework/controller/index" "github.com/nginxinc/nginx-gateway-fabric/internal/framework/gatewayclass" @@ -326,7 +327,8 @@ func createScheme() *runtime.Scheme { utilruntime.Must(apiv1.AddToScheme(scheme)) utilruntime.Must(discoveryV1.AddToScheme(scheme)) utilruntime.Must(apiext.AddToScheme(scheme)) - utilruntime.Must(ngfAPI.AddToScheme(scheme)) + utilruntime.Must(ngfAPIv1alpha1.AddToScheme(scheme)) + utilruntime.Must(ngfAPIv1alpha2.AddToScheme(scheme)) return scheme } @@ -2289,56 +2291,135 @@ var _ = Describe("ChangeProcessor", func() { }) Describe("NginxProxy resource changes", Ordered, func() { - paramGC := gc.DeepCopy() - paramGC.Spec.ParametersRef = &v1beta1.ParametersReference{ - Group: ngfAPI.GroupName, - Kind: kinds.NginxProxy, - Name: "np", - } + Context("referenced by a GatewayClass", func() { + paramGC := gc.DeepCopy() + paramGC.Spec.ParametersRef = &v1beta1.ParametersReference{ + Group: ngfAPIv1alpha1.GroupName, + Kind: kinds.NginxProxy, + Name: "np", + Namespace: helpers.GetPointer[v1.Namespace]("test"), + } - np := &ngfAPI.NginxProxy{ - ObjectMeta: metav1.ObjectMeta{ - Name: "np", - }, - } + np := &ngfAPIv1alpha2.NginxProxy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "np", + Namespace: "test", + }, + } - npUpdated := &ngfAPI.NginxProxy{ - ObjectMeta: metav1.ObjectMeta{ - Name: "np", - }, - Spec: ngfAPI.NginxProxySpec{ - Telemetry: &ngfAPI.Telemetry{ - Exporter: &ngfAPI.TelemetryExporter{ - Endpoint: "my-svc:123", - BatchSize: helpers.GetPointer(int32(512)), - BatchCount: helpers.GetPointer(int32(4)), - Interval: helpers.GetPointer(ngfAPI.Duration("5s")), + npUpdated := &ngfAPIv1alpha2.NginxProxy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "np", + Namespace: "test", + }, + Spec: ngfAPIv1alpha2.NginxProxySpec{ + Telemetry: &ngfAPIv1alpha2.Telemetry{ + Exporter: &ngfAPIv1alpha2.TelemetryExporter{ + Endpoint: helpers.GetPointer("my-svc:123"), + BatchSize: helpers.GetPointer(int32(512)), + BatchCount: helpers.GetPointer(int32(4)), + Interval: helpers.GetPointer(ngfAPIv1alpha1.Duration("5s")), + }, }, }, - }, - } - It("handles upserts for an NginxProxy", func() { - processor.CaptureUpsertChange(np) - processor.CaptureUpsertChange(paramGC) + } + It("handles upserts for an NginxProxy", func() { + processor.CaptureUpsertChange(np) + processor.CaptureUpsertChange(paramGC) - changed, graph := processor.Process() - Expect(changed).To(Equal(state.ClusterStateChange)) - Expect(graph.NginxProxy.Source).To(Equal(np)) - }) - It("captures changes for an NginxProxy", func() { - processor.CaptureUpsertChange(npUpdated) - processor.CaptureUpsertChange(paramGC) + changed, graph := processor.Process() + Expect(changed).To(Equal(state.ClusterStateChange)) + Expect(graph.GatewayClass.NginxProxy.Source).To(Equal(np)) + }) + It("captures changes for an NginxProxy", func() { + processor.CaptureUpsertChange(npUpdated) + processor.CaptureUpsertChange(paramGC) - changed, graph := processor.Process() - Expect(changed).To(Equal(state.ClusterStateChange)) - Expect(graph.NginxProxy.Source).To(Equal(npUpdated)) + changed, graph := processor.Process() + Expect(changed).To(Equal(state.ClusterStateChange)) + Expect(graph.GatewayClass.NginxProxy.Source).To(Equal(npUpdated)) + }) + It("handles deletes for an NginxProxy", func() { + processor.CaptureDeleteChange(np, client.ObjectKeyFromObject(np)) + + changed, graph := processor.Process() + Expect(changed).To(Equal(state.ClusterStateChange)) + Expect(graph.GatewayClass.NginxProxy).To(BeNil()) + }) }) - It("handles deletes for an NginxProxy", func() { - processor.CaptureDeleteChange(np, client.ObjectKeyFromObject(np)) + Context("referenced by a Gateway", func() { + paramGW := &v1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "param-gw", + Generation: 1, + }, + Spec: v1.GatewaySpec{ + GatewayClassName: gcName, + Listeners: []v1.Listener{ + { + Name: httpListenerName, + Hostname: nil, + Port: 80, + Protocol: v1.HTTPProtocolType, + }, + }, + Infrastructure: &v1.GatewayInfrastructure{ + ParametersRef: &v1.LocalParametersReference{ + Group: ngfAPIv1alpha1.GroupName, + Kind: kinds.NginxProxy, + Name: "np-gw", + }, + }, + }, + } - changed, graph := processor.Process() - Expect(changed).To(Equal(state.ClusterStateChange)) - Expect(graph.NginxProxy).To(BeNil()) + np := &ngfAPIv1alpha2.NginxProxy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "np-gw", + Namespace: "test", + }, + } + + npUpdated := &ngfAPIv1alpha2.NginxProxy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "np-gw", + Namespace: "test", + }, + Spec: ngfAPIv1alpha2.NginxProxySpec{ + Telemetry: &ngfAPIv1alpha2.Telemetry{ + Exporter: &ngfAPIv1alpha2.TelemetryExporter{ + Endpoint: helpers.GetPointer("my-svc:123"), + BatchSize: helpers.GetPointer(int32(512)), + BatchCount: helpers.GetPointer(int32(4)), + Interval: helpers.GetPointer(ngfAPIv1alpha1.Duration("5s")), + }, + }, + }, + } + It("handles upserts for an NginxProxy", func() { + processor.CaptureUpsertChange(np) + processor.CaptureUpsertChange(paramGW) + + changed, graph := processor.Process() + Expect(changed).To(Equal(state.ClusterStateChange)) + Expect(graph.Gateway.NginxProxy.Source).To(Equal(np)) + }) + It("captures changes for an NginxProxy", func() { + processor.CaptureUpsertChange(npUpdated) + processor.CaptureUpsertChange(paramGW) + + changed, graph := processor.Process() + Expect(changed).To(Equal(state.ClusterStateChange)) + Expect(graph.Gateway.NginxProxy.Source).To(Equal(npUpdated)) + }) + It("handles deletes for an NginxProxy", func() { + processor.CaptureDeleteChange(np, client.ObjectKeyFromObject(np)) + + changed, graph := processor.Process() + Expect(changed).To(Equal(state.ClusterStateChange)) + Expect(graph.Gateway.NginxProxy).To(BeNil()) + }) }) }) @@ -2347,9 +2428,9 @@ var _ = Describe("ChangeProcessor", func() { gw *v1.Gateway route *v1.HTTPRoute svc *apiv1.Service - csp, cspUpdated *ngfAPI.ClientSettingsPolicy - obs, obsUpdated *ngfAPI.ObservabilityPolicy - usp, uspUpdated *ngfAPI.UpstreamSettingsPolicy + csp, cspUpdated *ngfAPIv1alpha1.ClientSettingsPolicy + obs, obsUpdated *ngfAPIv1alpha1.ObservabilityPolicy + usp, uspUpdated *ngfAPIv1alpha1.UpstreamSettingsPolicy cspKey, obsKey, uspKey graph.PolicyKey ) @@ -2384,41 +2465,41 @@ var _ = Describe("ChangeProcessor", func() { }, } - csp = &ngfAPI.ClientSettingsPolicy{ + csp = &ngfAPIv1alpha1.ClientSettingsPolicy{ ObjectMeta: metav1.ObjectMeta{ Name: "csp", Namespace: "test", }, - Spec: ngfAPI.ClientSettingsPolicySpec{ + Spec: ngfAPIv1alpha1.ClientSettingsPolicySpec{ TargetRef: v1alpha2.LocalPolicyTargetReference{ Group: v1.GroupName, Kind: kinds.Gateway, Name: "gw", }, - Body: &ngfAPI.ClientBody{ - MaxSize: helpers.GetPointer[ngfAPI.Size]("10m"), + Body: &ngfAPIv1alpha1.ClientBody{ + MaxSize: helpers.GetPointer[ngfAPIv1alpha1.Size]("10m"), }, }, } cspUpdated = csp.DeepCopy() - cspUpdated.Spec.Body.MaxSize = helpers.GetPointer[ngfAPI.Size]("20m") + cspUpdated.Spec.Body.MaxSize = helpers.GetPointer[ngfAPIv1alpha1.Size]("20m") cspKey = graph.PolicyKey{ NsName: types.NamespacedName{Name: "csp", Namespace: "test"}, GVK: schema.GroupVersionKind{ - Group: ngfAPI.GroupName, + Group: ngfAPIv1alpha1.GroupName, Kind: kinds.ClientSettingsPolicy, Version: "v1alpha1", }, } - obs = &ngfAPI.ObservabilityPolicy{ + obs = &ngfAPIv1alpha1.ObservabilityPolicy{ ObjectMeta: metav1.ObjectMeta{ Name: "obs", Namespace: "test", }, - Spec: ngfAPI.ObservabilityPolicySpec{ + Spec: ngfAPIv1alpha1.ObservabilityPolicySpec{ TargetRefs: []v1alpha2.LocalPolicyTargetReference{ { Group: v1.GroupName, @@ -2426,31 +2507,31 @@ var _ = Describe("ChangeProcessor", func() { Name: "hr-1", }, }, - Tracing: &ngfAPI.Tracing{ - Strategy: ngfAPI.TraceStrategyRatio, + Tracing: &ngfAPIv1alpha1.Tracing{ + Strategy: ngfAPIv1alpha1.TraceStrategyRatio, }, }, } obsUpdated = obs.DeepCopy() - obsUpdated.Spec.Tracing.Strategy = ngfAPI.TraceStrategyParent + obsUpdated.Spec.Tracing.Strategy = ngfAPIv1alpha1.TraceStrategyParent obsKey = graph.PolicyKey{ NsName: types.NamespacedName{Name: "obs", Namespace: "test"}, GVK: schema.GroupVersionKind{ - Group: ngfAPI.GroupName, + Group: ngfAPIv1alpha1.GroupName, Kind: kinds.ObservabilityPolicy, Version: "v1alpha1", }, } - usp = &ngfAPI.UpstreamSettingsPolicy{ + usp = &ngfAPIv1alpha1.UpstreamSettingsPolicy{ ObjectMeta: metav1.ObjectMeta{ Name: "usp", Namespace: "test", }, - Spec: ngfAPI.UpstreamSettingsPolicySpec{ - ZoneSize: helpers.GetPointer[ngfAPI.Size]("10m"), + Spec: ngfAPIv1alpha1.UpstreamSettingsPolicySpec{ + ZoneSize: helpers.GetPointer[ngfAPIv1alpha1.Size]("10m"), TargetRefs: []v1alpha2.LocalPolicyTargetReference{ { Group: "core", @@ -2462,12 +2543,12 @@ var _ = Describe("ChangeProcessor", func() { } uspUpdated = usp.DeepCopy() - uspUpdated.Spec.ZoneSize = helpers.GetPointer[ngfAPI.Size]("20m") + uspUpdated.Spec.ZoneSize = helpers.GetPointer[ngfAPIv1alpha1.Size]("20m") uspKey = graph.PolicyKey{ NsName: types.NamespacedName{Name: "usp", Namespace: "test"}, GVK: schema.GroupVersionKind{ - Group: ngfAPI.GroupName, + Group: ngfAPIv1alpha1.GroupName, Kind: kinds.UpstreamSettingsPolicy, Version: "v1alpha1", }, @@ -2531,9 +2612,9 @@ var _ = Describe("ChangeProcessor", func() { }) When("the policy is deleted", func() { It("removes the policy from the graph", func() { - processor.CaptureDeleteChange(&ngfAPI.ClientSettingsPolicy{}, client.ObjectKeyFromObject(csp)) - processor.CaptureDeleteChange(&ngfAPI.ObservabilityPolicy{}, client.ObjectKeyFromObject(obs)) - processor.CaptureDeleteChange(&ngfAPI.UpstreamSettingsPolicy{}, client.ObjectKeyFromObject(usp)) + processor.CaptureDeleteChange(&ngfAPIv1alpha1.ClientSettingsPolicy{}, client.ObjectKeyFromObject(csp)) + processor.CaptureDeleteChange(&ngfAPIv1alpha1.ObservabilityPolicy{}, client.ObjectKeyFromObject(obs)) + processor.CaptureDeleteChange(&ngfAPIv1alpha1.UpstreamSettingsPolicy{}, client.ObjectKeyFromObject(usp)) changed, graph := processor.Process() Expect(changed).To(Equal(state.ClusterStateChange)) @@ -2548,34 +2629,34 @@ var _ = Describe("ChangeProcessor", func() { Namespace: "test", } - sf := &ngfAPI.SnippetsFilter{ + sf := &ngfAPIv1alpha1.SnippetsFilter{ ObjectMeta: metav1.ObjectMeta{ Name: sfNsName.Name, Namespace: sfNsName.Namespace, }, - Spec: ngfAPI.SnippetsFilterSpec{ - Snippets: []ngfAPI.Snippet{ + Spec: ngfAPIv1alpha1.SnippetsFilterSpec{ + Snippets: []ngfAPIv1alpha1.Snippet{ { - Context: ngfAPI.NginxContextMain, + Context: ngfAPIv1alpha1.NginxContextMain, Value: "main snippet", }, }, }, } - sfUpdated := &ngfAPI.SnippetsFilter{ + sfUpdated := &ngfAPIv1alpha1.SnippetsFilter{ ObjectMeta: metav1.ObjectMeta{ Name: sfNsName.Name, Namespace: sfNsName.Namespace, }, - Spec: ngfAPI.SnippetsFilterSpec{ - Snippets: []ngfAPI.Snippet{ + Spec: ngfAPIv1alpha1.SnippetsFilterSpec{ + Snippets: []ngfAPIv1alpha1.Snippet{ { - Context: ngfAPI.NginxContextMain, + Context: ngfAPIv1alpha1.NginxContextMain, Value: "main snippet", }, { - Context: ngfAPI.NginxContextHTTP, + Context: ngfAPIv1alpha1.NginxContextHTTP, Value: "http snippet", }, }, @@ -2631,7 +2712,7 @@ var _ = Describe("ChangeProcessor", func() { secret, secretUpdated, unrelatedSecret, barSecret, barSecretUpdated *apiv1.Secret cm, cmUpdated, unrelatedCM *apiv1.ConfigMap btls, btlsUpdated *v1alpha3.BackendTLSPolicy - np, npUpdated *ngfAPI.NginxProxy + np, npUpdated *ngfAPIv1alpha2.NginxProxy ) BeforeEach(OncePerOrdered, func() { @@ -2930,12 +3011,12 @@ var _ = Describe("ChangeProcessor", func() { btlsUpdated = btls.DeepCopy() npNsName = types.NamespacedName{Name: "np-1"} - np = &ngfAPI.NginxProxy{ + np = &ngfAPIv1alpha2.NginxProxy{ ObjectMeta: metav1.ObjectMeta{ Name: npNsName.Name, }, - Spec: ngfAPI.NginxProxySpec{ - Telemetry: &ngfAPI.Telemetry{ + Spec: ngfAPIv1alpha2.NginxProxySpec{ + Telemetry: &ngfAPIv1alpha2.Telemetry{ ServiceName: helpers.GetPointer("my-svc"), }, }, @@ -3010,7 +3091,7 @@ var _ = Describe("ChangeProcessor", func() { processor.CaptureDeleteChange(&v1beta1.ReferenceGrant{}, rgNsName) processor.CaptureDeleteChange(&v1alpha3.BackendTLSPolicy{}, btlsNsName) processor.CaptureDeleteChange(&apiv1.ConfigMap{}, cmNsName) - processor.CaptureDeleteChange(&ngfAPI.NginxProxy{}, npNsName) + processor.CaptureDeleteChange(&ngfAPIv1alpha2.NginxProxy{}, npNsName) // these are non-changing changes processor.CaptureUpsertChange(gw2) diff --git a/internal/mode/static/state/conditions/conditions.go b/internal/mode/static/state/conditions/conditions.go index c97f9efa2d..f49a755d9a 100644 --- a/internal/mode/static/state/conditions/conditions.go +++ b/internal/mode/static/state/conditions/conditions.go @@ -87,6 +87,10 @@ const ( // parametersRef resource does not exist. GatewayClassReasonParamsRefNotFound v1.GatewayClassConditionReason = "ParametersRefNotFound" + // GatewayClassReasonParamsRefInvalid is used with the "GatewayClassResolvedRefs" condition when the + // parametersRef resource is invalid. + GatewayClassReasonParamsRefInvalid v1.GatewayClassConditionReason = "ParametersRefInvalid" + // PolicyReasonNginxProxyConfigNotSet is used with the "PolicyAccepted" condition when the // NginxProxy resource is missing or invalid. PolicyReasonNginxProxyConfigNotSet v1alpha2.PolicyConditionReason = "NginxProxyConfigNotSet" @@ -106,6 +110,21 @@ const ( // GatewayIgnoredReason is used with v1.RouteConditionAccepted when the route references a Gateway that is ignored // by NGF. GatewayIgnoredReason v1.RouteConditionReason = "GatewayIgnored" + + // GatewayResolvedRefs condition indicates whether the controller was able to resolve the + // parametersRef on the Gateway. + GatewayResolvedRefs v1.GatewayConditionType = "ResolvedRefs" + + // GatewayReasonResolvedRefs is used with the "GatewayResolvedRefs" condition when the condition is true. + GatewayReasonResolvedRefs v1.GatewayConditionReason = "ResolvedRefs" + + // GatewayReasonParamsRefNotFound is used with the "GatewayResolvedRefs" condition when the + // parametersRef resource does not exist. + GatewayReasonParamsRefNotFound v1.GatewayConditionReason = "ParametersRefNotFound" + + // GatewayReasonParamsRefInvalid is used with the "GatewayResolvedRefs" condition when the + // parametersRef resource is invalid. + GatewayReasonParamsRefInvalid v1.GatewayConditionReason = "ParametersRefInvalid" ) // NewRouteNotAcceptedGatewayIgnored returns a Condition that indicates that the Route is not accepted by the Gateway @@ -514,7 +533,7 @@ func NewGatewayClassResolvedRefs() conditions.Condition { Type: string(GatewayClassResolvedRefs), Status: metav1.ConditionTrue, Reason: string(GatewayClassReasonResolvedRefs), - Message: "parametersRef resource is resolved", + Message: "ParametersRef resource is resolved", } } @@ -525,7 +544,18 @@ func NewGatewayClassRefNotFound() conditions.Condition { Type: string(GatewayClassResolvedRefs), Status: metav1.ConditionFalse, Reason: string(GatewayClassReasonParamsRefNotFound), - Message: "parametersRef resource could not be found", + Message: "ParametersRef resource could not be found", + } +} + +// NewGatewayClassRefInvalid returns a Condition that indicates that the parametersRef +// on the GatewayClass could not be resolved because the resource it references is invalid. +func NewGatewayClassRefInvalid(msg string) conditions.Condition { + return conditions.Condition{ + Type: string(GatewayClassResolvedRefs), + Status: metav1.ConditionFalse, + Reason: string(GatewayClassReasonParamsRefInvalid), + Message: msg, } } @@ -537,7 +567,7 @@ func NewGatewayClassInvalidParameters(msg string) conditions.Condition { Type: string(v1.GatewayClassConditionStatusAccepted), Status: metav1.ConditionTrue, Reason: string(v1.GatewayClassReasonInvalidParameters), - Message: fmt.Sprintf("GatewayClass is accepted, but parametersRef is ignored due to an error: %s", msg), + Message: fmt.Sprintf("GatewayClass is accepted, but ParametersRef is ignored due to an error: %s", msg), } } @@ -684,6 +714,51 @@ func NewNginxGatewayInvalid(msg string) conditions.Condition { } } +// NewGatewayResolvedRefs returns a Condition that indicates that the parametersRef +// on the Gateway is resolved. +func NewGatewayResolvedRefs() conditions.Condition { + return conditions.Condition{ + Type: string(GatewayResolvedRefs), + Status: metav1.ConditionTrue, + Reason: string(GatewayReasonResolvedRefs), + Message: "ParametersRef resource is resolved", + } +} + +// NewGatewayRefNotFound returns a Condition that indicates that the parametersRef +// on the Gateway could not be resolved. +func NewGatewayRefNotFound() conditions.Condition { + return conditions.Condition{ + Type: string(GatewayResolvedRefs), + Status: metav1.ConditionFalse, + Reason: string(GatewayReasonParamsRefNotFound), + Message: "ParametersRef resource could not be found", + } +} + +// NewGatewayRefInvalid returns a Condition that indicates that the parametersRef +// on the Gateway could not be resolved because the referenced resource is invalid. +func NewGatewayRefInvalid(msg string) conditions.Condition { + return conditions.Condition{ + Type: string(GatewayResolvedRefs), + Status: metav1.ConditionFalse, + Reason: string(GatewayReasonParamsRefInvalid), + Message: msg, + } +} + +// NewGatewayInvalidParameters returns a Condition that indicates that the Gateway has invalid parameters. +// We are allowing Accepted to still be true to prevent nullifying the entire Gateway config if a parametersRef +// is updated to something invalid. +func NewGatewayInvalidParameters(msg string) conditions.Condition { + return conditions.Condition{ + Type: string(v1.GatewayConditionAccepted), + Status: metav1.ConditionTrue, + Reason: string(v1.GatewayReasonInvalidParameters), + Message: fmt.Sprintf("Gateway is accepted, but ParametersRef is ignored due to an error: %s", msg), + } +} + // NewPolicyAccepted returns a Condition that indicates that the Policy is accepted. func NewPolicyAccepted() conditions.Condition { return conditions.Condition{ diff --git a/internal/mode/static/state/dataplane/configuration.go b/internal/mode/static/state/dataplane/configuration.go index cafcbc0db8..4eb8ef80fe 100644 --- a/internal/mode/static/state/dataplane/configuration.go +++ b/internal/mode/static/state/dataplane/configuration.go @@ -4,6 +4,7 @@ import ( "context" "encoding/base64" "fmt" + "slices" "sort" apiv1 "k8s.io/api/core/v1" @@ -13,7 +14,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" v1 "sigs.k8s.io/gateway-api/apis/v1" - ngfAPI "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha1" + ngfAPIv1alpha1 "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha1" + ngfAPIv1alpha2 "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha2" "github.com/nginxinc/nginx-gateway-fabric/internal/framework/helpers" "github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/nginx/config/policies" "github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/state/graph" @@ -62,7 +64,7 @@ func BuildConfiguration( Telemetry: buildTelemetry(g), BaseHTTPConfig: baseHTTPConfig, Logging: buildLogging(g), - MainSnippets: buildSnippetsForContext(g.SnippetsFilters, ngfAPI.NginxContextMain), + MainSnippets: buildSnippetsForContext(g.SnippetsFilters, ngfAPIv1alpha1.NginxContextMain), AuxiliarySecrets: buildAuxiliarySecrets(g.PlusSecrets), } @@ -773,22 +775,42 @@ func generateCertBundleID(configMap types.NamespacedName) CertBundleID { return CertBundleID(fmt.Sprintf("cert_bundle_%s_%s", configMap.Namespace, configMap.Name)) } +func telemetryEnabled(gw *graph.Gateway) bool { + if gw == nil { + return false + } + + if gw.EffectiveNginxProxy == nil || gw.EffectiveNginxProxy.Telemetry == nil { + return false + } + + tel := gw.EffectiveNginxProxy.Telemetry + + if slices.Contains(tel.DisabledFeatures, ngfAPIv1alpha2.DisableTracing) { + return false + } + + if tel.Exporter == nil || tel.Exporter.Endpoint == nil { + return false + } + + return true +} + // buildTelemetry generates the Otel configuration. func buildTelemetry(g *graph.Graph) Telemetry { - if g.NginxProxy == nil || !g.NginxProxy.Valid || - g.NginxProxy.Source.Spec.Telemetry == nil || - g.NginxProxy.Source.Spec.Telemetry.Exporter == nil { + if !telemetryEnabled(g.Gateway) { return Telemetry{} } serviceName := fmt.Sprintf("ngf:%s:%s", g.Gateway.Source.Namespace, g.Gateway.Source.Name) - telemetry := g.NginxProxy.Source.Spec.Telemetry + telemetry := g.Gateway.EffectiveNginxProxy.Telemetry if telemetry.ServiceName != nil { serviceName = serviceName + ":" + *telemetry.ServiceName } tel := Telemetry{ - Endpoint: telemetry.Exporter.Endpoint, + Endpoint: *telemetry.Exporter.Endpoint, // safe to deref here since we verified that telemetry is enabled ServiceName: serviceName, } @@ -809,7 +831,7 @@ func buildTelemetry(g *graph.Graph) Telemetry { // logic in this function ratioMap := make(map[string]int32) for _, pol := range g.NGFPolicies { - if obsPol, ok := pol.Source.(*ngfAPI.ObservabilityPolicy); ok { + if obsPol, ok := pol.Source.(*ngfAPIv1alpha1.ObservabilityPolicy); ok { if obsPol.Spec.Tracing != nil && obsPol.Spec.Tracing.Ratio != nil && *obsPol.Spec.Tracing.Ratio > 0 { ratioName := CreateRatioVarName(*obsPol.Spec.Tracing.Ratio) ratioMap[ratioName] = *obsPol.Spec.Tracing.Ratio @@ -825,7 +847,7 @@ func buildTelemetry(g *graph.Graph) Telemetry { return tel } -func setSpanAttributes(spanAttributes []ngfAPI.SpanAttribute) []SpanAttribute { +func setSpanAttributes(spanAttributes []ngfAPIv1alpha1.SpanAttribute) []SpanAttribute { spanAttrs := make([]SpanAttribute, 0, len(spanAttributes)) for _, spanAttr := range spanAttributes { sa := SpanAttribute{ @@ -850,50 +872,53 @@ func buildBaseHTTPConfig(g *graph.Graph) BaseHTTPConfig { // HTTP2 should be enabled by default HTTP2: true, IPFamily: Dual, - Snippets: buildSnippetsForContext(g.SnippetsFilters, ngfAPI.NginxContextHTTP), + Snippets: buildSnippetsForContext(g.SnippetsFilters, ngfAPIv1alpha1.NginxContextHTTP), } - if g.NginxProxy == nil || !g.NginxProxy.Valid { + + // safe to access EffectiveNginxProxy since we only call this function when the Gateway is not nil. + np := g.Gateway.EffectiveNginxProxy + if np == nil { return baseConfig } - if g.NginxProxy.Source.Spec.DisableHTTP2 { + if np.DisableHTTP2 != nil && *np.DisableHTTP2 { baseConfig.HTTP2 = false } - if g.NginxProxy.Source.Spec.IPFamily != nil { - switch *g.NginxProxy.Source.Spec.IPFamily { - case ngfAPI.IPv4: + if np.IPFamily != nil { + switch *np.IPFamily { + case ngfAPIv1alpha2.IPv4: baseConfig.IPFamily = IPv4 - case ngfAPI.IPv6: + case ngfAPIv1alpha2.IPv6: baseConfig.IPFamily = IPv6 } } - if g.NginxProxy.Source.Spec.RewriteClientIP != nil { - if g.NginxProxy.Source.Spec.RewriteClientIP.Mode != nil { - switch *g.NginxProxy.Source.Spec.RewriteClientIP.Mode { - case ngfAPI.RewriteClientIPModeProxyProtocol: + if np.RewriteClientIP != nil { + if np.RewriteClientIP.Mode != nil { + switch *np.RewriteClientIP.Mode { + case ngfAPIv1alpha2.RewriteClientIPModeProxyProtocol: baseConfig.RewriteClientIPSettings.Mode = RewriteIPModeProxyProtocol - case ngfAPI.RewriteClientIPModeXForwardedFor: + case ngfAPIv1alpha2.RewriteClientIPModeXForwardedFor: baseConfig.RewriteClientIPSettings.Mode = RewriteIPModeXForwardedFor } } - if len(g.NginxProxy.Source.Spec.RewriteClientIP.TrustedAddresses) > 0 { + if len(np.RewriteClientIP.TrustedAddresses) > 0 { baseConfig.RewriteClientIPSettings.TrustedAddresses = convertAddresses( - g.NginxProxy.Source.Spec.RewriteClientIP.TrustedAddresses, + np.RewriteClientIP.TrustedAddresses, ) } - if g.NginxProxy.Source.Spec.RewriteClientIP.SetIPRecursively != nil { - baseConfig.RewriteClientIPSettings.IPRecursive = *g.NginxProxy.Source.Spec.RewriteClientIP.SetIPRecursively + if np.RewriteClientIP.SetIPRecursively != nil { + baseConfig.RewriteClientIPSettings.IPRecursive = *np.RewriteClientIP.SetIPRecursively } } return baseConfig } -func createSnippetName(nc ngfAPI.NginxContext, nsname types.NamespacedName) string { +func createSnippetName(nc ngfAPIv1alpha1.NginxContext, nsname types.NamespacedName) string { return fmt.Sprintf( "SnippetsFilter_%s_%s_%s", nc, @@ -904,7 +929,7 @@ func createSnippetName(nc ngfAPI.NginxContext, nsname types.NamespacedName) stri func buildSnippetsForContext( snippetFilters map[types.NamespacedName]*graph.SnippetsFilter, - nc ngfAPI.NginxContext, + nc ngfAPIv1alpha1.NginxContext, ) []Snippet { if len(snippetFilters) == 0 { return nil @@ -950,7 +975,7 @@ func buildPolicies(graphPolicies []*graph.Policy) []policies.Policy { return finalPolicies } -func convertAddresses(addresses []ngfAPI.Address) []string { +func convertAddresses(addresses []ngfAPIv1alpha2.Address) []string { trustedAddresses := make([]string, len(addresses)) for i, addr := range addresses { trustedAddresses[i] = addr.Value @@ -961,10 +986,14 @@ func convertAddresses(addresses []ngfAPI.Address) []string { func buildLogging(g *graph.Graph) Logging { logSettings := Logging{ErrorLevel: defaultErrorLogLevel} - ngfProxy := g.NginxProxy - if ngfProxy != nil && ngfProxy.Source.Spec.Logging != nil { - if ngfProxy.Source.Spec.Logging.ErrorLevel != nil { - logSettings.ErrorLevel = string(*ngfProxy.Source.Spec.Logging.ErrorLevel) + if g.Gateway == nil || g.Gateway.EffectiveNginxProxy == nil { + return logSettings + } + + ngfProxy := g.Gateway.EffectiveNginxProxy + if ngfProxy.Logging != nil { + if ngfProxy.Logging.ErrorLevel != nil { + logSettings.ErrorLevel = string(*ngfProxy.Logging.ErrorLevel) } } diff --git a/internal/mode/static/state/dataplane/configuration_test.go b/internal/mode/static/state/dataplane/configuration_test.go index 037bcd7d9d..3c61ed0ff3 100644 --- a/internal/mode/static/state/dataplane/configuration_test.go +++ b/internal/mode/static/state/dataplane/configuration_test.go @@ -19,7 +19,8 @@ import ( "sigs.k8s.io/gateway-api/apis/v1alpha2" "sigs.k8s.io/gateway-api/apis/v1alpha3" - ngfAPI "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha1" + ngfAPIv1alpha1 "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha1" + ngfAPIv1alpha2 "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha2" "github.com/nginxinc/nginx-gateway-fabric/internal/framework/helpers" "github.com/nginxinc/nginx-gateway-fabric/internal/framework/kinds" "github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/nginx/config/policies" @@ -337,7 +338,7 @@ func TestBuildConfiguration(t *testing.T) { ) sf1 := &graph.SnippetsFilter{ - Source: &ngfAPI.SnippetsFilter{ + Source: &ngfAPIv1alpha1.SnippetsFilter{ ObjectMeta: metav1.ObjectMeta{ Name: "sf", Namespace: "test", @@ -345,16 +346,16 @@ func TestBuildConfiguration(t *testing.T) { }, Valid: true, Referenced: true, - Snippets: map[ngfAPI.NginxContext]string{ - ngfAPI.NginxContextHTTPServerLocation: "location snippet", - ngfAPI.NginxContextHTTPServer: "server snippet", - ngfAPI.NginxContextMain: "main snippet", - ngfAPI.NginxContextHTTP: "http snippet", + Snippets: map[ngfAPIv1alpha1.NginxContext]string{ + ngfAPIv1alpha1.NginxContextHTTPServerLocation: "location snippet", + ngfAPIv1alpha1.NginxContextHTTPServer: "server snippet", + ngfAPIv1alpha1.NginxContextMain: "main snippet", + ngfAPIv1alpha1.NginxContextHTTP: "http snippet", }, } sfNotReferenced := &graph.SnippetsFilter{ - Source: &ngfAPI.SnippetsFilter{ + Source: &ngfAPIv1alpha1.SnippetsFilter{ ObjectMeta: metav1.ObjectMeta{ Name: "sf-not-referenced", Namespace: "test", @@ -362,9 +363,9 @@ func TestBuildConfiguration(t *testing.T) { }, Valid: true, Referenced: false, - Snippets: map[ngfAPI.NginxContext]string{ - ngfAPI.NginxContextMain: "main snippet no ref", - ngfAPI.NginxContextHTTP: "http snippet no ref", + Snippets: map[ngfAPIv1alpha1.NginxContext]string{ + ngfAPIv1alpha1.NginxContextMain: "main snippet no ref", + ngfAPIv1alpha1.NginxContextHTTP: "http snippet no ref", }, } @@ -377,7 +378,7 @@ func TestBuildConfiguration(t *testing.T) { extRefFilter := graph.Filter{ FilterType: graph.FilterExtensionRef, ExtensionRef: &v1.LocalObjectReference{ - Group: ngfAPI.GroupName, + Group: ngfAPIv1alpha1.GroupName, Kind: kinds.SnippetsFilter, Name: "sf", }, @@ -393,14 +394,14 @@ func TestBuildConfiguration(t *testing.T) { expExtRefFilter := SnippetsFilter{ LocationSnippet: &Snippet{ Name: createSnippetName( - ngfAPI.NginxContextHTTPServerLocation, + ngfAPIv1alpha1.NginxContextHTTPServerLocation, client.ObjectKeyFromObject(extRefFilter.ResolvedExtensionRef.SnippetsFilter.Source), ), Contents: "location snippet", }, ServerSnippet: &Snippet{ Name: createSnippetName( - ngfAPI.NginxContextHTTPServer, + ngfAPIv1alpha1.NginxContextHTTPServer, client.ObjectKeyFromObject(extRefFilter.ResolvedExtensionRef.SnippetsFilter.Source), ), Contents: "server snippet", @@ -825,43 +826,26 @@ func TestBuildConfiguration(t *testing.T) { }, } - nginxProxy := &graph.NginxProxy{ - Source: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - Telemetry: &ngfAPI.Telemetry{ - Exporter: &ngfAPI.TelemetryExporter{ - Endpoint: "my-otel.svc:4563", - BatchSize: helpers.GetPointer(int32(512)), - BatchCount: helpers.GetPointer(int32(4)), - Interval: helpers.GetPointer(ngfAPI.Duration("5s")), - }, - ServiceName: helpers.GetPointer("my-svc"), - }, - DisableHTTP2: true, - IPFamily: helpers.GetPointer(ngfAPI.Dual), + nginxProxy := &graph.EffectiveNginxProxy{ + Telemetry: &ngfAPIv1alpha2.Telemetry{ + Exporter: &ngfAPIv1alpha2.TelemetryExporter{ + Endpoint: helpers.GetPointer("my-otel.svc:4563"), + BatchSize: helpers.GetPointer(int32(512)), + BatchCount: helpers.GetPointer(int32(4)), + Interval: helpers.GetPointer(ngfAPIv1alpha1.Duration("5s")), }, + ServiceName: helpers.GetPointer("my-svc"), }, - Valid: true, + DisableHTTP2: helpers.GetPointer(true), + IPFamily: helpers.GetPointer(ngfAPIv1alpha2.Dual), } - nginxProxyIPv4 := &graph.NginxProxy{ - Source: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - Telemetry: &ngfAPI.Telemetry{}, - IPFamily: helpers.GetPointer(ngfAPI.IPv4), - }, - }, - Valid: true, + nginxProxyIPv4 := &graph.EffectiveNginxProxy{ + IPFamily: helpers.GetPointer(ngfAPIv1alpha2.IPv4), } - nginxProxyIPv6 := &graph.NginxProxy{ - Source: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - Telemetry: &ngfAPI.Telemetry{}, - IPFamily: helpers.GetPointer(ngfAPI.IPv6), - }, - }, - Valid: true, + nginxProxyIPv6 := &graph.EffectiveNginxProxy{ + IPFamily: helpers.GetPointer(ngfAPIv1alpha2.IPv6), } tests := []struct { @@ -2056,7 +2040,7 @@ func TestBuildConfiguration(t *testing.T) { Valid: true, Routes: map[graph.RouteKey]*graph.L7Route{}, }) - g.NginxProxy = nginxProxy + g.Gateway.EffectiveNginxProxy = nginxProxy return g }), expConf: getModifiedExpectedConfiguration(func(conf Configuration) Configuration { @@ -2074,42 +2058,7 @@ func TestBuildConfiguration(t *testing.T) { conf.BaseHTTPConfig = BaseHTTPConfig{HTTP2: false, IPFamily: Dual} return conf }), - msg: "NginxProxy with tracing config and http2 disabled", - }, - { - graph: getModifiedGraph(func(g *graph.Graph) *graph.Graph { - g.Gateway.Source.ObjectMeta = metav1.ObjectMeta{ - Name: "gw", - Namespace: "ns", - } - g.Gateway.Listeners = append(g.Gateway.Listeners, &graph.Listener{ - Name: "listener-80-1", - Source: listener80, - Valid: true, - Routes: map[graph.RouteKey]*graph.L7Route{}, - }) - g.NginxProxy = &graph.NginxProxy{ - Valid: false, - Source: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - DisableHTTP2: true, - IPFamily: helpers.GetPointer(ngfAPI.Dual), - Telemetry: &ngfAPI.Telemetry{ - Exporter: &ngfAPI.TelemetryExporter{ - Endpoint: "some-endpoint", - }, - }, - }, - }, - } - return g - }), - expConf: getModifiedExpectedConfiguration(func(conf Configuration) Configuration { - conf.SSLServers = []VirtualServer{} - conf.SSLKeyPairs = map[SSLKeyPairID]SSLKeyPair{} - return conf - }), - msg: "invalid NginxProxy", + msg: "EffectiveNginxProxy with tracing config and http2 disabled", }, { graph: getModifiedGraph(func(g *graph.Graph) *graph.Graph { @@ -2218,7 +2167,7 @@ func TestBuildConfiguration(t *testing.T) { Valid: true, Routes: map[graph.RouteKey]*graph.L7Route{}, }) - g.NginxProxy = nginxProxyIPv4 + g.Gateway.EffectiveNginxProxy = nginxProxyIPv4 return g }), expConf: getModifiedExpectedConfiguration(func(conf Configuration) Configuration { @@ -2227,7 +2176,7 @@ func TestBuildConfiguration(t *testing.T) { conf.BaseHTTPConfig = BaseHTTPConfig{HTTP2: true, IPFamily: IPv4} return conf }), - msg: "NginxProxy with IPv4 IPFamily and no routes", + msg: "GatewayClass has NginxProxy with IPv4 IPFamily and no routes", }, { graph: getModifiedGraph(func(g *graph.Graph) *graph.Graph { @@ -2241,7 +2190,7 @@ func TestBuildConfiguration(t *testing.T) { Valid: true, Routes: map[graph.RouteKey]*graph.L7Route{}, }) - g.NginxProxy = nginxProxyIPv6 + g.Gateway.EffectiveNginxProxy = nginxProxyIPv6 return g }), expConf: getModifiedExpectedConfiguration(func(conf Configuration) Configuration { @@ -2250,7 +2199,7 @@ func TestBuildConfiguration(t *testing.T) { conf.BaseHTTPConfig = BaseHTTPConfig{HTTP2: true, IPFamily: IPv6} return conf }), - msg: "NginxProxy with IPv6 IPFamily and no routes", + msg: "GatewayClass has NginxProxy with IPv6 IPFamily and no routes", }, { graph: getModifiedGraph(func(g *graph.Graph) *graph.Graph { @@ -2264,21 +2213,16 @@ func TestBuildConfiguration(t *testing.T) { Valid: true, Routes: map[graph.RouteKey]*graph.L7Route{}, }) - g.NginxProxy = &graph.NginxProxy{ - Valid: true, - Source: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - RewriteClientIP: &ngfAPI.RewriteClientIP{ - SetIPRecursively: helpers.GetPointer(true), - TrustedAddresses: []ngfAPI.Address{ - { - Type: ngfAPI.CIDRAddressType, - Value: "1.1.1.1/32", - }, - }, - Mode: helpers.GetPointer(ngfAPI.RewriteClientIPModeProxyProtocol), + g.Gateway.EffectiveNginxProxy = &graph.EffectiveNginxProxy{ + RewriteClientIP: &ngfAPIv1alpha2.RewriteClientIP{ + SetIPRecursively: helpers.GetPointer(true), + TrustedAddresses: []ngfAPIv1alpha2.Address{ + { + Type: ngfAPIv1alpha2.CIDRAddressType, + Value: "1.1.1.1/32", }, }, + Mode: helpers.GetPointer(ngfAPIv1alpha2.RewriteClientIPModeProxyProtocol), }, } return g @@ -2297,7 +2241,7 @@ func TestBuildConfiguration(t *testing.T) { } return conf }), - msg: "NginxProxy with rewriteClientIP details set", + msg: "GatewayClass has NginxProxy with rewriteClientIP details set", }, { graph: getModifiedGraph(func(g *graph.Graph) *graph.Graph { @@ -2311,12 +2255,9 @@ func TestBuildConfiguration(t *testing.T) { Valid: true, Routes: map[graph.RouteKey]*graph.L7Route{}, }) - g.NginxProxy = &graph.NginxProxy{ - Valid: true, - Source: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - Logging: &ngfAPI.NginxLogging{ErrorLevel: helpers.GetPointer(ngfAPI.NginxLogLevelDebug)}, - }, + g.Gateway.EffectiveNginxProxy = &graph.EffectiveNginxProxy{ + Logging: &ngfAPIv1alpha2.NginxLogging{ + ErrorLevel: helpers.GetPointer(ngfAPIv1alpha2.NginxLogLevelDebug), }, } return g @@ -2327,7 +2268,7 @@ func TestBuildConfiguration(t *testing.T) { conf.Logging = Logging{ErrorLevel: "debug"} return conf }), - msg: "NginxProxy with error log level set to debug", + msg: "GatewayClass has NginxProxy with error log level set to debug", }, { graph: getModifiedGraph(func(g *graph.Graph) *graph.Graph { @@ -2342,7 +2283,7 @@ func TestBuildConfiguration(t *testing.T) { conf.MainSnippets = []Snippet{ { Name: createSnippetName( - ngfAPI.NginxContextMain, + ngfAPIv1alpha1.NginxContextMain, client.ObjectKeyFromObject(sf1.Source), ), Contents: "main snippet", @@ -2351,7 +2292,7 @@ func TestBuildConfiguration(t *testing.T) { conf.BaseHTTPConfig.Snippets = []Snippet{ { Name: createSnippetName( - ngfAPI.NginxContextHTTP, + ngfAPIv1alpha1.NginxContextHTTP, client.ObjectKeyFromObject(sf1.Source), ), Contents: "http snippet", @@ -2534,14 +2475,14 @@ func TestCreateFilters(t *testing.T) { snippetsFilter1 := graph.Filter{ FilterType: graph.FilterExtensionRef, ExtensionRef: &v1.LocalObjectReference{ - Group: ngfAPI.GroupName, + Group: ngfAPIv1alpha1.GroupName, Kind: kinds.SnippetsFilter, Name: "sf1", }, ResolvedExtensionRef: &graph.ExtensionRefFilter{ Valid: true, SnippetsFilter: &graph.SnippetsFilter{ - Source: &ngfAPI.SnippetsFilter{ + Source: &ngfAPIv1alpha1.SnippetsFilter{ ObjectMeta: metav1.ObjectMeta{ Name: "sf1", Namespace: "default", @@ -2549,11 +2490,11 @@ func TestCreateFilters(t *testing.T) { }, Valid: true, Referenced: true, - Snippets: map[ngfAPI.NginxContext]string{ - ngfAPI.NginxContextHTTPServerLocation: "location snippet 1", - ngfAPI.NginxContextMain: "main snippet 1", - ngfAPI.NginxContextHTTPServer: "server snippet 1", - ngfAPI.NginxContextHTTP: "http snippet 1", + Snippets: map[ngfAPIv1alpha1.NginxContext]string{ + ngfAPIv1alpha1.NginxContextHTTPServerLocation: "location snippet 1", + ngfAPIv1alpha1.NginxContextMain: "main snippet 1", + ngfAPIv1alpha1.NginxContextHTTPServer: "server snippet 1", + ngfAPIv1alpha1.NginxContextHTTP: "http snippet 1", }, }, }, @@ -2562,14 +2503,14 @@ func TestCreateFilters(t *testing.T) { snippetsFilter2 := graph.Filter{ FilterType: graph.FilterExtensionRef, ExtensionRef: &v1.LocalObjectReference{ - Group: ngfAPI.GroupName, + Group: ngfAPIv1alpha1.GroupName, Kind: kinds.SnippetsFilter, Name: "sf2", }, ResolvedExtensionRef: &graph.ExtensionRefFilter{ Valid: true, SnippetsFilter: &graph.SnippetsFilter{ - Source: &ngfAPI.SnippetsFilter{ + Source: &ngfAPIv1alpha1.SnippetsFilter{ ObjectMeta: metav1.ObjectMeta{ Name: "sf2", Namespace: "default", @@ -2577,11 +2518,11 @@ func TestCreateFilters(t *testing.T) { }, Valid: true, Referenced: true, - Snippets: map[ngfAPI.NginxContext]string{ - ngfAPI.NginxContextHTTPServerLocation: "location snippet 2", - ngfAPI.NginxContextMain: "main snippet 2", - ngfAPI.NginxContextHTTPServer: "server snippet 2", - ngfAPI.NginxContextHTTP: "http snippet 2", + Snippets: map[ngfAPIv1alpha1.NginxContext]string{ + ngfAPIv1alpha1.NginxContextHTTPServerLocation: "location snippet 2", + ngfAPIv1alpha1.NginxContextMain: "main snippet 2", + ngfAPIv1alpha1.NginxContextHTTPServer: "server snippet 2", + ngfAPIv1alpha1.NginxContextHTTP: "http snippet 2", }, }, }, @@ -2628,14 +2569,14 @@ func TestCreateFilters(t *testing.T) { { LocationSnippet: &Snippet{ Name: createSnippetName( - ngfAPI.NginxContextHTTPServerLocation, + ngfAPIv1alpha1.NginxContextHTTPServerLocation, types.NamespacedName{Namespace: "default", Name: "sf1"}, ), Contents: "location snippet 1", }, ServerSnippet: &Snippet{ Name: createSnippetName( - ngfAPI.NginxContextHTTPServer, + ngfAPIv1alpha1.NginxContextHTTPServer, types.NamespacedName{Namespace: "default", Name: "sf1"}, ), Contents: "server snippet 1", @@ -2644,14 +2585,14 @@ func TestCreateFilters(t *testing.T) { { LocationSnippet: &Snippet{ Name: createSnippetName( - ngfAPI.NginxContextHTTPServerLocation, + ngfAPIv1alpha1.NginxContextHTTPServerLocation, types.NamespacedName{Namespace: "default", Name: "sf2"}, ), Contents: "location snippet 2", }, ServerSnippet: &Snippet{ Name: createSnippetName( - ngfAPI.NginxContextHTTPServer, + ngfAPIv1alpha1.NginxContextHTTPServer, types.NamespacedName{Namespace: "default", Name: "sf2"}, ), Contents: "server snippet 2", @@ -3277,24 +3218,19 @@ func TestConvertBackendTLS(t *testing.T) { func TestBuildTelemetry(t *testing.T) { t.Parallel() - telemetryConfigured := &graph.NginxProxy{ - Source: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - Telemetry: &ngfAPI.Telemetry{ - Exporter: &ngfAPI.TelemetryExporter{ - Endpoint: "my-otel.svc:4563", - BatchSize: helpers.GetPointer(int32(512)), - BatchCount: helpers.GetPointer(int32(4)), - Interval: helpers.GetPointer(ngfAPI.Duration("5s")), - }, - ServiceName: helpers.GetPointer("my-svc"), - SpanAttributes: []ngfAPI.SpanAttribute{ - {Key: "key", Value: "value"}, - }, - }, + telemetryConfigured := &graph.EffectiveNginxProxy{ + Telemetry: &ngfAPIv1alpha2.Telemetry{ + Exporter: &ngfAPIv1alpha2.TelemetryExporter{ + Endpoint: helpers.GetPointer("my-otel.svc:4563"), + BatchSize: helpers.GetPointer(int32(512)), + BatchCount: helpers.GetPointer(int32(4)), + Interval: helpers.GetPointer(ngfAPIv1alpha1.Duration("5s")), + }, + ServiceName: helpers.GetPointer("my-svc"), + SpanAttributes: []ngfAPIv1alpha1.SpanAttribute{ + {Key: "key", Value: "value"}, }, }, - Valid: true, } createTelemetry := func() Telemetry { @@ -3320,10 +3256,24 @@ func TestBuildTelemetry(t *testing.T) { msg string expTelemetry Telemetry }{ + { + g: &graph.Graph{}, + expTelemetry: Telemetry{}, + msg: "nil Gateway", + }, + { + g: &graph.Graph{ + Gateway: &graph.Gateway{ + EffectiveNginxProxy: nil, + }, + }, + expTelemetry: Telemetry{}, + msg: "nil effective NginxProxy", + }, { g: &graph.Graph{ - NginxProxy: &graph.NginxProxy{ - Source: &ngfAPI.NginxProxy{}, + Gateway: &graph.Gateway{ + EffectiveNginxProxy: &graph.EffectiveNginxProxy{}, }, }, expTelemetry: Telemetry{}, @@ -3331,19 +3281,49 @@ func TestBuildTelemetry(t *testing.T) { }, { g: &graph.Graph{ - NginxProxy: &graph.NginxProxy{ - Source: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - Telemetry: &ngfAPI.Telemetry{ - Exporter: &ngfAPI.TelemetryExporter{}, + Gateway: &graph.Gateway{ + EffectiveNginxProxy: &graph.EffectiveNginxProxy{ + Telemetry: &ngfAPIv1alpha2.Telemetry{ + Exporter: &ngfAPIv1alpha2.TelemetryExporter{ + Endpoint: helpers.GetPointer("my-otel.svc:4563"), + }, + DisabledFeatures: []ngfAPIv1alpha2.DisableTelemetryFeature{ + ngfAPIv1alpha2.DisableTracing, }, }, }, - Valid: false, }, }, expTelemetry: Telemetry{}, - msg: "Invalid NginxProxy configured", + msg: "Telemetry disabled explicitly", + }, + { + g: &graph.Graph{ + Gateway: &graph.Gateway{ + EffectiveNginxProxy: &graph.EffectiveNginxProxy{ + Telemetry: &ngfAPIv1alpha2.Telemetry{ + Exporter: nil, + }, + }, + }, + }, + expTelemetry: Telemetry{}, + msg: "Telemetry disabled implicitly (nil exporter)", + }, + { + g: &graph.Graph{ + Gateway: &graph.Gateway{ + EffectiveNginxProxy: &graph.EffectiveNginxProxy{ + Telemetry: &ngfAPIv1alpha2.Telemetry{ + Exporter: &ngfAPIv1alpha2.TelemetryExporter{ + Endpoint: nil, + }, + }, + }, + }, + }, + expTelemetry: Telemetry{}, + msg: "Telemetry disabled implicitly (nil exporter endpoint)", }, { g: &graph.Graph{ @@ -3354,8 +3334,8 @@ func TestBuildTelemetry(t *testing.T) { Namespace: "ns", }, }, + EffectiveNginxProxy: telemetryConfigured, }, - NginxProxy: telemetryConfigured, }, expTelemetry: createTelemetry(), msg: "Telemetry configured", @@ -3369,17 +3349,17 @@ func TestBuildTelemetry(t *testing.T) { Namespace: "ns", }, }, + EffectiveNginxProxy: telemetryConfigured, }, - NginxProxy: telemetryConfigured, NGFPolicies: map[graph.PolicyKey]*graph.Policy{ {NsName: types.NamespacedName{Name: "obsPolicy"}}: { - Source: &ngfAPI.ObservabilityPolicy{ + Source: &ngfAPIv1alpha1.ObservabilityPolicy{ ObjectMeta: metav1.ObjectMeta{ Name: "obsPolicy", Namespace: "custom-ns", }, - Spec: ngfAPI.ObservabilityPolicySpec{ - Tracing: &ngfAPI.Tracing{ + Spec: ngfAPIv1alpha1.ObservabilityPolicySpec{ + Tracing: &ngfAPIv1alpha1.Tracing{ Ratio: helpers.GetPointer[int32](25), }, }, @@ -3404,50 +3384,50 @@ func TestBuildTelemetry(t *testing.T) { Namespace: "ns", }, }, + EffectiveNginxProxy: telemetryConfigured, }, - NginxProxy: telemetryConfigured, NGFPolicies: map[graph.PolicyKey]*graph.Policy{ {NsName: types.NamespacedName{Name: "obsPolicy"}}: { - Source: &ngfAPI.ObservabilityPolicy{ + Source: &ngfAPIv1alpha1.ObservabilityPolicy{ ObjectMeta: metav1.ObjectMeta{ Name: "obsPolicy", Namespace: "custom-ns", }, - Spec: ngfAPI.ObservabilityPolicySpec{ - Tracing: &ngfAPI.Tracing{ + Spec: ngfAPIv1alpha1.ObservabilityPolicySpec{ + Tracing: &ngfAPIv1alpha1.Tracing{ Ratio: helpers.GetPointer[int32](25), }, }, }, }, {NsName: types.NamespacedName{Name: "obsPolicy2"}}: { - Source: &ngfAPI.ObservabilityPolicy{ + Source: &ngfAPIv1alpha1.ObservabilityPolicy{ ObjectMeta: metav1.ObjectMeta{ Name: "obsPolicy2", Namespace: "custom-ns", }, - Spec: ngfAPI.ObservabilityPolicySpec{ - Tracing: &ngfAPI.Tracing{ + Spec: ngfAPIv1alpha1.ObservabilityPolicySpec{ + Tracing: &ngfAPIv1alpha1.Tracing{ Ratio: helpers.GetPointer[int32](50), }, }, }, }, {NsName: types.NamespacedName{Name: "obsPolicy3"}}: { - Source: &ngfAPI.ObservabilityPolicy{ + Source: &ngfAPIv1alpha1.ObservabilityPolicy{ ObjectMeta: metav1.ObjectMeta{ Name: "obsPolicy3", Namespace: "custom-ns", }, - Spec: ngfAPI.ObservabilityPolicySpec{ - Tracing: &ngfAPI.Tracing{ + Spec: ngfAPIv1alpha1.ObservabilityPolicySpec{ + Tracing: &ngfAPIv1alpha1.Tracing{ Ratio: helpers.GetPointer[int32](25), }, }, }, }, {NsName: types.NamespacedName{Name: "csPolicy"}}: { - Source: &ngfAPI.ClientSettingsPolicy{ + Source: &ngfAPIv1alpha1.ClientSettingsPolicy{ ObjectMeta: metav1.ObjectMeta{ Name: "csPolicy", Namespace: "custom-ns", @@ -3474,17 +3454,17 @@ func TestBuildTelemetry(t *testing.T) { Namespace: "ns", }, }, + EffectiveNginxProxy: telemetryConfigured, }, - NginxProxy: telemetryConfigured, NGFPolicies: map[graph.PolicyKey]*graph.Policy{ {NsName: types.NamespacedName{Name: "obsPolicy"}}: { - Source: &ngfAPI.ObservabilityPolicy{ + Source: &ngfAPIv1alpha1.ObservabilityPolicy{ ObjectMeta: metav1.ObjectMeta{ Name: "obsPolicy", Namespace: "custom-ns", }, - Spec: ngfAPI.ObservabilityPolicySpec{ - Tracing: &ngfAPI.Tracing{ + Spec: ngfAPIv1alpha1.ObservabilityPolicySpec{ + Tracing: &ngfAPIv1alpha1.Tracing{ Ratio: helpers.GetPointer[int32](0), }, }, @@ -3911,9 +3891,8 @@ func TestBuildRewriteIPSettings(t *testing.T) { { msg: "no rewrite IP settings configured", g: &graph.Graph{ - NginxProxy: &graph.NginxProxy{ - Valid: true, - Source: &ngfAPI.NginxProxy{}, + Gateway: &graph.Gateway{ + EffectiveNginxProxy: &graph.EffectiveNginxProxy{}, }, }, expRewriteIPSettings: RewriteClientIPSettings{}, @@ -3921,20 +3900,17 @@ func TestBuildRewriteIPSettings(t *testing.T) { { msg: "rewrite IP settings configured with proxyProtocol", g: &graph.Graph{ - NginxProxy: &graph.NginxProxy{ - Valid: true, - Source: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - RewriteClientIP: &ngfAPI.RewriteClientIP{ - Mode: helpers.GetPointer(ngfAPI.RewriteClientIPModeProxyProtocol), - TrustedAddresses: []ngfAPI.Address{ - { - Type: ngfAPI.CIDRAddressType, - Value: "10.9.9.4/32", - }, + Gateway: &graph.Gateway{ + EffectiveNginxProxy: &graph.EffectiveNginxProxy{ + RewriteClientIP: &ngfAPIv1alpha2.RewriteClientIP{ + Mode: helpers.GetPointer(ngfAPIv1alpha2.RewriteClientIPModeProxyProtocol), + TrustedAddresses: []ngfAPIv1alpha2.Address{ + { + Type: ngfAPIv1alpha2.CIDRAddressType, + Value: "10.9.9.4/32", }, - SetIPRecursively: helpers.GetPointer(true), }, + SetIPRecursively: helpers.GetPointer(true), }, }, }, @@ -3948,20 +3924,17 @@ func TestBuildRewriteIPSettings(t *testing.T) { { msg: "rewrite IP settings configured with xForwardedFor", g: &graph.Graph{ - NginxProxy: &graph.NginxProxy{ - Valid: true, - Source: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - RewriteClientIP: &ngfAPI.RewriteClientIP{ - Mode: helpers.GetPointer(ngfAPI.RewriteClientIPModeXForwardedFor), - TrustedAddresses: []ngfAPI.Address{ - { - Type: ngfAPI.CIDRAddressType, - Value: "76.89.90.11/24", - }, + Gateway: &graph.Gateway{ + EffectiveNginxProxy: &graph.EffectiveNginxProxy{ + RewriteClientIP: &ngfAPIv1alpha2.RewriteClientIP{ + Mode: helpers.GetPointer(ngfAPIv1alpha2.RewriteClientIPModeXForwardedFor), + TrustedAddresses: []ngfAPIv1alpha2.Address{ + { + Type: ngfAPIv1alpha2.CIDRAddressType, + Value: "76.89.90.11/24", }, - SetIPRecursively: helpers.GetPointer(true), }, + SetIPRecursively: helpers.GetPointer(true), }, }, }, @@ -3975,32 +3948,29 @@ func TestBuildRewriteIPSettings(t *testing.T) { { msg: "rewrite IP settings configured with recursive set to false and multiple trusted addresses", g: &graph.Graph{ - NginxProxy: &graph.NginxProxy{ - Valid: true, - Source: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - RewriteClientIP: &ngfAPI.RewriteClientIP{ - Mode: helpers.GetPointer(ngfAPI.RewriteClientIPModeXForwardedFor), - TrustedAddresses: []ngfAPI.Address{ - { - Type: ngfAPI.CIDRAddressType, - Value: "5.5.5.5/12", - }, - { - Type: ngfAPI.CIDRAddressType, - Value: "1.1.1.1/26", - }, - { - Type: ngfAPI.CIDRAddressType, - Value: "2.2.2.2/32", - }, - { - Type: ngfAPI.CIDRAddressType, - Value: "3.3.3.3/24", - }, + Gateway: &graph.Gateway{ + EffectiveNginxProxy: &graph.EffectiveNginxProxy{ + RewriteClientIP: &ngfAPIv1alpha2.RewriteClientIP{ + Mode: helpers.GetPointer(ngfAPIv1alpha2.RewriteClientIPModeXForwardedFor), + TrustedAddresses: []ngfAPIv1alpha2.Address{ + { + Type: ngfAPIv1alpha2.CIDRAddressType, + Value: "5.5.5.5/12", + }, + { + Type: ngfAPIv1alpha2.CIDRAddressType, + Value: "1.1.1.1/26", + }, + { + Type: ngfAPIv1alpha2.CIDRAddressType, + Value: "2.2.2.2/32", + }, + { + Type: ngfAPIv1alpha2.CIDRAddressType, + Value: "3.3.3.3/24", }, - SetIPRecursively: helpers.GetPointer(false), }, + SetIPRecursively: helpers.GetPointer(false), }, }, }, @@ -4033,30 +4003,39 @@ func TestBuildLogging(t *testing.T) { expLoggingSettings Logging }{ { - msg: "NginxProxy is nil", - g: &graph.Graph{}, + msg: "Gateway is nil", + g: &graph.Graph{ + Gateway: nil, + }, expLoggingSettings: defaultLogging, }, { - msg: "NginxProxy does not specify log level", + msg: "Gateway has no effective NginxProxy", g: &graph.Graph{ - NginxProxy: &graph.NginxProxy{ - Valid: true, - Source: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{}, + Gateway: &graph.Gateway{ + EffectiveNginxProxy: nil, + }, + }, + expLoggingSettings: defaultLogging, + }, + { + msg: "Effective NginxProxy does not specify log level", + g: &graph.Graph{ + Gateway: &graph.Gateway{ + EffectiveNginxProxy: &graph.EffectiveNginxProxy{ + IPFamily: helpers.GetPointer(ngfAPIv1alpha2.Dual), }, }, }, expLoggingSettings: defaultLogging, }, { - msg: "NginxProxy log level set to debug", + msg: "Effective NginxProxy log level set to debug", g: &graph.Graph{ - NginxProxy: &graph.NginxProxy{ - Valid: true, - Source: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - Logging: &ngfAPI.NginxLogging{ErrorLevel: helpers.GetPointer(ngfAPI.NginxLogLevelDebug)}, + Gateway: &graph.Gateway{ + EffectiveNginxProxy: &graph.EffectiveNginxProxy{ + Logging: &ngfAPIv1alpha2.NginxLogging{ + ErrorLevel: helpers.GetPointer(ngfAPIv1alpha2.NginxLogLevelDebug), }, }, }, @@ -4064,13 +4043,12 @@ func TestBuildLogging(t *testing.T) { expLoggingSettings: Logging{ErrorLevel: "debug"}, }, { - msg: "NginxProxy log level set to info", + msg: "Effective NginxProxy log level set to info", g: &graph.Graph{ - NginxProxy: &graph.NginxProxy{ - Valid: true, - Source: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - Logging: &ngfAPI.NginxLogging{ErrorLevel: helpers.GetPointer(ngfAPI.NginxLogLevelInfo)}, + Gateway: &graph.Gateway{ + EffectiveNginxProxy: &graph.EffectiveNginxProxy{ + Logging: &ngfAPIv1alpha2.NginxLogging{ + ErrorLevel: helpers.GetPointer(ngfAPIv1alpha2.NginxLogLevelInfo), }, }, }, @@ -4078,13 +4056,12 @@ func TestBuildLogging(t *testing.T) { expLoggingSettings: Logging{ErrorLevel: defaultErrorLogLevel}, }, { - msg: "NginxProxy log level set to notice", + msg: "Effective NginxProxy log level set to notice", g: &graph.Graph{ - NginxProxy: &graph.NginxProxy{ - Valid: true, - Source: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - Logging: &ngfAPI.NginxLogging{ErrorLevel: helpers.GetPointer(ngfAPI.NginxLogLevelNotice)}, + Gateway: &graph.Gateway{ + EffectiveNginxProxy: &graph.EffectiveNginxProxy{ + Logging: &ngfAPIv1alpha2.NginxLogging{ + ErrorLevel: helpers.GetPointer(ngfAPIv1alpha2.NginxLogLevelNotice), }, }, }, @@ -4092,13 +4069,12 @@ func TestBuildLogging(t *testing.T) { expLoggingSettings: Logging{ErrorLevel: "notice"}, }, { - msg: "NginxProxy log level set to warn", + msg: "Effective NginxProxy log level set to warn", g: &graph.Graph{ - NginxProxy: &graph.NginxProxy{ - Valid: true, - Source: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - Logging: &ngfAPI.NginxLogging{ErrorLevel: helpers.GetPointer(ngfAPI.NginxLogLevelWarn)}, + Gateway: &graph.Gateway{ + EffectiveNginxProxy: &graph.EffectiveNginxProxy{ + Logging: &ngfAPIv1alpha2.NginxLogging{ + ErrorLevel: helpers.GetPointer(ngfAPIv1alpha2.NginxLogLevelWarn), }, }, }, @@ -4106,13 +4082,12 @@ func TestBuildLogging(t *testing.T) { expLoggingSettings: Logging{ErrorLevel: "warn"}, }, { - msg: "NginxProxy log level set to error", + msg: "Effective NginxProxy log level set to error", g: &graph.Graph{ - NginxProxy: &graph.NginxProxy{ - Valid: true, - Source: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - Logging: &ngfAPI.NginxLogging{ErrorLevel: helpers.GetPointer(ngfAPI.NginxLogLevelError)}, + Gateway: &graph.Gateway{ + EffectiveNginxProxy: &graph.EffectiveNginxProxy{ + Logging: &ngfAPIv1alpha2.NginxLogging{ + ErrorLevel: helpers.GetPointer(ngfAPIv1alpha2.NginxLogLevelError), }, }, }, @@ -4120,13 +4095,12 @@ func TestBuildLogging(t *testing.T) { expLoggingSettings: Logging{ErrorLevel: "error"}, }, { - msg: "NginxProxy log level set to crit", + msg: "Effective NginxProxy log level set to crit", g: &graph.Graph{ - NginxProxy: &graph.NginxProxy{ - Valid: true, - Source: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - Logging: &ngfAPI.NginxLogging{ErrorLevel: helpers.GetPointer(ngfAPI.NginxLogLevelCrit)}, + Gateway: &graph.Gateway{ + EffectiveNginxProxy: &graph.EffectiveNginxProxy{ + Logging: &ngfAPIv1alpha2.NginxLogging{ + ErrorLevel: helpers.GetPointer(ngfAPIv1alpha2.NginxLogLevelCrit), }, }, }, @@ -4134,13 +4108,12 @@ func TestBuildLogging(t *testing.T) { expLoggingSettings: Logging{ErrorLevel: "crit"}, }, { - msg: "NginxProxy log level set to alert", + msg: "Effective NginxProxy log level set to alert", g: &graph.Graph{ - NginxProxy: &graph.NginxProxy{ - Valid: true, - Source: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - Logging: &ngfAPI.NginxLogging{ErrorLevel: helpers.GetPointer(ngfAPI.NginxLogLevelAlert)}, + Gateway: &graph.Gateway{ + EffectiveNginxProxy: &graph.EffectiveNginxProxy{ + Logging: &ngfAPIv1alpha2.NginxLogging{ + ErrorLevel: helpers.GetPointer(ngfAPIv1alpha2.NginxLogLevelAlert), }, }, }, @@ -4148,13 +4121,12 @@ func TestBuildLogging(t *testing.T) { expLoggingSettings: Logging{ErrorLevel: "alert"}, }, { - msg: "NginxProxy log level set to emerg", + msg: "Effective NginxProxy log level set to emerg", g: &graph.Graph{ - NginxProxy: &graph.NginxProxy{ - Valid: true, - Source: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - Logging: &ngfAPI.NginxLogging{ErrorLevel: helpers.GetPointer(ngfAPI.NginxLogLevelEmerg)}, + Gateway: &graph.Gateway{ + EffectiveNginxProxy: &graph.EffectiveNginxProxy{ + Logging: &ngfAPIv1alpha2.NginxLogging{ + ErrorLevel: helpers.GetPointer(ngfAPIv1alpha2.NginxLogLevelEmerg), }, }, }, @@ -4179,7 +4151,7 @@ func TestCreateSnippetName(t *testing.T) { g := NewWithT(t) name := createSnippetName( - ngfAPI.NginxContextHTTPServerLocation, + ngfAPIv1alpha1.NginxContextHTTPServerLocation, types.NamespacedName{Namespace: "some-ns", Name: "some-name"}, ) g.Expect(name).To(Equal("SnippetsFilter_http.server.location_some-ns_some-name")) @@ -4189,7 +4161,7 @@ func TestBuildSnippetForContext(t *testing.T) { t.Parallel() validUnreferenced := &graph.SnippetsFilter{ - Source: &ngfAPI.SnippetsFilter{ + Source: &ngfAPIv1alpha1.SnippetsFilter{ ObjectMeta: metav1.ObjectMeta{ Name: "valid-unreferenced", Namespace: "default", @@ -4197,13 +4169,13 @@ func TestBuildSnippetForContext(t *testing.T) { }, Valid: true, Referenced: false, - Snippets: map[ngfAPI.NginxContext]string{ - ngfAPI.NginxContextHTTPServerLocation: "valid unreferenced", + Snippets: map[ngfAPIv1alpha1.NginxContext]string{ + ngfAPIv1alpha1.NginxContextHTTPServerLocation: "valid unreferenced", }, } invalidUnreferenced := &graph.SnippetsFilter{ - Source: &ngfAPI.SnippetsFilter{ + Source: &ngfAPIv1alpha1.SnippetsFilter{ ObjectMeta: metav1.ObjectMeta{ Name: "invalid-unreferenced", Namespace: "default", @@ -4211,13 +4183,13 @@ func TestBuildSnippetForContext(t *testing.T) { }, Valid: false, Referenced: false, - Snippets: map[ngfAPI.NginxContext]string{ - ngfAPI.NginxContextHTTPServerLocation: "invalid unreferenced", + Snippets: map[ngfAPIv1alpha1.NginxContext]string{ + ngfAPIv1alpha1.NginxContextHTTPServerLocation: "invalid unreferenced", }, } invalidReferenced := &graph.SnippetsFilter{ - Source: &ngfAPI.SnippetsFilter{ + Source: &ngfAPIv1alpha1.SnippetsFilter{ ObjectMeta: metav1.ObjectMeta{ Name: "invalid-referenced", Namespace: "default", @@ -4225,13 +4197,13 @@ func TestBuildSnippetForContext(t *testing.T) { }, Valid: false, Referenced: true, - Snippets: map[ngfAPI.NginxContext]string{ - ngfAPI.NginxContextHTTPServerLocation: "invalid referenced", + Snippets: map[ngfAPIv1alpha1.NginxContext]string{ + ngfAPIv1alpha1.NginxContextHTTPServerLocation: "invalid referenced", }, } validReferenced1 := &graph.SnippetsFilter{ - Source: &ngfAPI.SnippetsFilter{ + Source: &ngfAPIv1alpha1.SnippetsFilter{ ObjectMeta: metav1.ObjectMeta{ Name: "valid-referenced1", Namespace: "default", @@ -4239,14 +4211,14 @@ func TestBuildSnippetForContext(t *testing.T) { }, Valid: true, Referenced: true, - Snippets: map[ngfAPI.NginxContext]string{ - ngfAPI.NginxContextHTTP: "http valid referenced 1", - ngfAPI.NginxContextMain: "main valid referenced 1", + Snippets: map[ngfAPIv1alpha1.NginxContext]string{ + ngfAPIv1alpha1.NginxContextHTTP: "http valid referenced 1", + ngfAPIv1alpha1.NginxContextMain: "main valid referenced 1", }, } validReferenced2 := &graph.SnippetsFilter{ - Source: &ngfAPI.SnippetsFilter{ + Source: &ngfAPIv1alpha1.SnippetsFilter{ ObjectMeta: metav1.ObjectMeta{ Name: "valid-referenced2", Namespace: "other-ns", @@ -4254,14 +4226,14 @@ func TestBuildSnippetForContext(t *testing.T) { }, Valid: true, Referenced: true, - Snippets: map[ngfAPI.NginxContext]string{ - ngfAPI.NginxContextMain: "main valid referenced 2", - ngfAPI.NginxContextHTTP: "http valid referenced 2", + Snippets: map[ngfAPIv1alpha1.NginxContext]string{ + ngfAPIv1alpha1.NginxContextMain: "main valid referenced 2", + ngfAPIv1alpha1.NginxContextHTTP: "http valid referenced 2", }, } validReferenced3 := &graph.SnippetsFilter{ - Source: &ngfAPI.SnippetsFilter{ + Source: &ngfAPIv1alpha1.SnippetsFilter{ ObjectMeta: metav1.ObjectMeta{ Name: "valid-referenced3", Namespace: "other-ns", @@ -4269,29 +4241,29 @@ func TestBuildSnippetForContext(t *testing.T) { }, Valid: true, Referenced: true, - Snippets: map[ngfAPI.NginxContext]string{ - ngfAPI.NginxContextHTTPServerLocation: "location valid referenced 2", + Snippets: map[ngfAPIv1alpha1.NginxContext]string{ + ngfAPIv1alpha1.NginxContextHTTPServerLocation: "location valid referenced 2", }, } expMainSnippets := []Snippet{ { - Name: createSnippetName(ngfAPI.NginxContextMain, client.ObjectKeyFromObject(validReferenced1.Source)), + Name: createSnippetName(ngfAPIv1alpha1.NginxContextMain, client.ObjectKeyFromObject(validReferenced1.Source)), Contents: "main valid referenced 1", }, { - Name: createSnippetName(ngfAPI.NginxContextMain, client.ObjectKeyFromObject(validReferenced2.Source)), + Name: createSnippetName(ngfAPIv1alpha1.NginxContextMain, client.ObjectKeyFromObject(validReferenced2.Source)), Contents: "main valid referenced 2", }, } expHTTPSnippets := []Snippet{ { - Name: createSnippetName(ngfAPI.NginxContextHTTP, client.ObjectKeyFromObject(validReferenced1.Source)), + Name: createSnippetName(ngfAPIv1alpha1.NginxContextHTTP, client.ObjectKeyFromObject(validReferenced1.Source)), Contents: "http valid referenced 1", }, { - Name: createSnippetName(ngfAPI.NginxContextHTTP, client.ObjectKeyFromObject(validReferenced2.Source)), + Name: createSnippetName(ngfAPIv1alpha1.NginxContextHTTP, client.ObjectKeyFromObject(validReferenced2.Source)), Contents: "http valid referenced 2", }, } @@ -4310,25 +4282,25 @@ func TestBuildSnippetForContext(t *testing.T) { tests := []struct { name string snippetsFilters map[types.NamespacedName]*graph.SnippetsFilter - ctx ngfAPI.NginxContext + ctx ngfAPIv1alpha1.NginxContext expSnippets []Snippet }{ { name: "no snippets filters", snippetsFilters: nil, - ctx: ngfAPI.NginxContextMain, + ctx: ngfAPIv1alpha1.NginxContextMain, expSnippets: nil, }, { name: "main context: mix of invalid, unreferenced, and valid, referenced snippets filters", snippetsFilters: getSnippetsFilters(), - ctx: ngfAPI.NginxContextMain, + ctx: ngfAPIv1alpha1.NginxContextMain, expSnippets: expMainSnippets, }, { name: "http context: mix of invalid, unreferenced, and valid, referenced snippets filters", snippetsFilters: getSnippetsFilters(), - ctx: ngfAPI.NginxContextHTTP, + ctx: ngfAPIv1alpha1.NginxContextHTTP, expSnippets: expHTTPSnippets, }, } diff --git a/internal/mode/static/state/graph/backend_refs.go b/internal/mode/static/state/graph/backend_refs.go index e8c5120b48..683983cfd3 100644 --- a/internal/mode/static/state/graph/backend_refs.go +++ b/internal/mode/static/state/graph/backend_refs.go @@ -11,8 +11,7 @@ import ( gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" "sigs.k8s.io/gateway-api/apis/v1alpha3" - ngfAPI "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha1" - + ngfAPIv1alpha2 "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha2" "github.com/nginxinc/nginx-gateway-fabric/internal/framework/conditions" "github.com/nginxinc/nginx-gateway-fabric/internal/framework/helpers" "github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/sort" @@ -47,7 +46,7 @@ func addBackendRefsToRouteRules( refGrantResolver *referenceGrantResolver, services map[types.NamespacedName]*v1.Service, backendTLSPolicies map[types.NamespacedName]*BackendTLSPolicy, - npCfg *NginxProxy, + npCfg *EffectiveNginxProxy, ) { for _, r := range routes { addBackendRefsToRules(r, refGrantResolver, services, backendTLSPolicies, npCfg) @@ -61,7 +60,7 @@ func addBackendRefsToRules( refGrantResolver *referenceGrantResolver, services map[types.NamespacedName]*v1.Service, backendTLSPolicies map[types.NamespacedName]*BackendTLSPolicy, - npCfg *NginxProxy, + npCfg *EffectiveNginxProxy, ) { if !route.Valid { return @@ -123,7 +122,7 @@ func createBackendRef( services map[types.NamespacedName]*v1.Service, refPath *field.Path, backendTLSPolicies map[types.NamespacedName]*BackendTLSPolicy, - npCfg *NginxProxy, + npCfg *EffectiveNginxProxy, ) (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. @@ -316,30 +315,32 @@ func getIPFamilyAndPortFromRef( return svc.Spec.IPFamilies, svcPort, nil } -func verifyIPFamily(npCfg *NginxProxy, svcIPFamily []v1.IPFamily) error { - if npCfg == nil || npCfg.Source == nil || !npCfg.Valid { +func verifyIPFamily(npCfg *EffectiveNginxProxy, svcIPFamily []v1.IPFamily) error { + if npCfg == nil { return nil } - // we can access this field since we have already validated that ipFamily is not nil in validateNginxProxy. - npIPFamily := npCfg.Source.Spec.IPFamily - if *npIPFamily == ngfAPI.IPv4 { - if slices.Contains(svcIPFamily, v1.IPv6Protocol) { - // capitalizing error message to match the rest of the error messages associated with a condition - //nolint: stylecheck - return errors.New( - "Service configured with IPv6 family but NginxProxy is configured with IPv4", - ) - } + containsIPv6 := slices.Contains(svcIPFamily, v1.IPv6Protocol) + containsIPv4 := slices.Contains(svcIPFamily, v1.IPv4Protocol) + + //nolint: stylecheck // used in status condition which is normally capitalized + errIPv6Mismatch := errors.New("Service configured with IPv6 family but NginxProxy is configured with IPv4") + //nolint: stylecheck // used in status condition which is normally capitalized + errIPv4Mismatch := errors.New("Service configured with IPv4 family but NginxProxy is configured with IPv6") + + npIPFamily := npCfg.IPFamily + + if npIPFamily == nil { + // default is dual so we don't need to check the service IPFamily. + return nil } - if *npIPFamily == ngfAPI.IPv6 { - if slices.Contains(svcIPFamily, v1.IPv4Protocol) { - // capitalizing error message to match the rest of the error messages associated with a condition - //nolint: stylecheck - return errors.New( - "Service configured with IPv4 family but NginxProxy is configured with IPv6", - ) - } + + if *npIPFamily == ngfAPIv1alpha2.IPv4 && containsIPv6 { + return errIPv6Mismatch + } + + if *npIPFamily == ngfAPIv1alpha2.IPv6 && containsIPv4 { + return errIPv4Mismatch } return nil diff --git a/internal/mode/static/state/graph/backend_refs_test.go b/internal/mode/static/state/graph/backend_refs_test.go index b7c49648da..a0ebfbd9cd 100644 --- a/internal/mode/static/state/graph/backend_refs_test.go +++ b/internal/mode/static/state/graph/backend_refs_test.go @@ -14,7 +14,7 @@ import ( "sigs.k8s.io/gateway-api/apis/v1alpha2" "sigs.k8s.io/gateway-api/apis/v1alpha3" - ngfAPI "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha1" + ngfAPIv1alpha2 "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha2" "github.com/nginxinc/nginx-gateway-fabric/internal/framework/conditions" "github.com/nginxinc/nginx-gateway-fabric/internal/framework/helpers" staticConds "github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/state/conditions" @@ -324,55 +324,35 @@ func TestVerifyIPFamily(t *testing.T) { test := []struct { name string expErr error - npCfg *NginxProxy + npCfg *EffectiveNginxProxy svcIPFamily []v1.IPFamily }{ { name: "Valid - IPv6 and IPv4 configured for NGINX, service has only IPv4", - npCfg: &NginxProxy{ - Source: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - IPFamily: helpers.GetPointer(ngfAPI.Dual), - }, - }, - Valid: true, + npCfg: &EffectiveNginxProxy{ + IPFamily: helpers.GetPointer(ngfAPIv1alpha2.Dual), }, svcIPFamily: []v1.IPFamily{v1.IPv4Protocol}, }, { name: "Valid - IPv6 and IPv4 configured for NGINX, service has only IPv6", - npCfg: &NginxProxy{ - Source: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - IPFamily: helpers.GetPointer(ngfAPI.Dual), - }, - }, - Valid: true, + npCfg: &EffectiveNginxProxy{ + IPFamily: helpers.GetPointer(ngfAPIv1alpha2.Dual), }, svcIPFamily: []v1.IPFamily{v1.IPv6Protocol}, }, { name: "Invalid - IPv4 configured for NGINX, service has only IPv6", - npCfg: &NginxProxy{ - Source: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - IPFamily: helpers.GetPointer(ngfAPI.IPv4), - }, - }, - Valid: true, + npCfg: &EffectiveNginxProxy{ + IPFamily: helpers.GetPointer(ngfAPIv1alpha2.IPv4), }, svcIPFamily: []v1.IPFamily{v1.IPv6Protocol}, expErr: errors.New("Service configured with IPv6 family but NginxProxy is configured with IPv4"), }, { name: "Invalid - IPv6 configured for NGINX, service has only IPv4", - npCfg: &NginxProxy{ - Source: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - IPFamily: helpers.GetPointer(ngfAPI.IPv6), - }, - }, - Valid: true, + npCfg: &EffectiveNginxProxy{ + IPFamily: helpers.GetPointer(ngfAPIv1alpha2.IPv6), }, svcIPFamily: []v1.IPFamily{v1.IPv4Protocol}, expErr: errors.New("Service configured with IPv4 family but NginxProxy is configured with IPv6"), @@ -845,7 +825,7 @@ func TestCreateBackend(t *testing.T) { tests := []struct { expectedCondition *conditions.Condition - nginxProxy *NginxProxy + nginxProxySpec *EffectiveNginxProxy name string expectedServicePortReference string ref gatewayv1.HTTPBackendRef @@ -958,12 +938,7 @@ func TestCreateBackend(t *testing.T) { Weight: 5, Valid: false, }, - nginxProxy: &NginxProxy{ - Source: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{IPFamily: helpers.GetPointer(ngfAPI.IPv6)}, - }, - Valid: true, - }, + nginxProxySpec: &EffectiveNginxProxy{IPFamily: helpers.GetPointer(ngfAPIv1alpha2.IPv6)}, expectedCondition: helpers.GetPointer( staticConds.NewRouteInvalidIPFamily(`Service configured with IPv4 family but NginxProxy is configured with IPv6`), ), @@ -1042,7 +1017,7 @@ func TestCreateBackend(t *testing.T) { services, refPath, policies, - test.nginxProxy, + test.nginxProxySpec, ) g.Expect(helpers.Diff(test.expectedBackend, backend)).To(BeEmpty()) diff --git a/internal/mode/static/state/graph/gateway.go b/internal/mode/static/state/graph/gateway.go index 2b73b46be2..e487a74184 100644 --- a/internal/mode/static/state/graph/gateway.go +++ b/internal/mode/static/state/graph/gateway.go @@ -9,6 +9,7 @@ import ( v1 "sigs.k8s.io/gateway-api/apis/v1" "github.com/nginxinc/nginx-gateway-fabric/internal/framework/conditions" + "github.com/nginxinc/nginx-gateway-fabric/internal/framework/kinds" ngfsort "github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/sort" staticConds "github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/state/conditions" ) @@ -17,6 +18,12 @@ import ( type Gateway struct { // Source is the corresponding Gateway resource. Source *v1.Gateway + // NginxProxy is the NginxProxy referenced by this Gateway. + NginxProxy *NginxProxy + /// EffectiveNginxProxy holds the result of merging the NginxProxySpec on this resource with the NginxProxySpec on + // the GatewayClass resource. This is the effective set of config that should be applied to the Gateway. + // If non-nil, then this config is valid. + EffectiveNginxProxy *EffectiveNginxProxy // Listeners include the listeners of the Gateway. Listeners []*Listener // Conditions holds the conditions for the Gateway. @@ -98,29 +105,91 @@ func buildGateway( gc *GatewayClass, refGrantResolver *referenceGrantResolver, protectedPorts ProtectedPorts, + nps map[types.NamespacedName]*NginxProxy, ) *Gateway { if gw == nil { return nil } - conds := validateGateway(gw, gc) + var np *NginxProxy + if gw.Spec.Infrastructure != nil && gw.Spec.Infrastructure.ParametersRef != nil { + npName := types.NamespacedName{Namespace: gw.Namespace, Name: gw.Spec.Infrastructure.ParametersRef.Name} + np = nps[npName] + } - if len(conds) > 0 { + var gcNp *NginxProxy + if gc != nil { + gcNp = gc.NginxProxy + } + + effectiveNginxProxy := buildEffectiveNginxProxy(gcNp, np) + + conds, valid := validateGateway(gw, gc, np) + + if !valid { return &Gateway{ - Source: gw, - Valid: false, - Conditions: conds, + Source: gw, + Valid: false, + NginxProxy: np, + EffectiveNginxProxy: effectiveNginxProxy, + Conditions: conds, } } return &Gateway{ - Source: gw, - Listeners: buildListeners(gw, secretResolver, refGrantResolver, protectedPorts), - Valid: true, + Source: gw, + Listeners: buildListeners(gw, secretResolver, refGrantResolver, protectedPorts), + NginxProxy: np, + EffectiveNginxProxy: effectiveNginxProxy, + Valid: true, + Conditions: conds, } } -func validateGateway(gw *v1.Gateway, gc *GatewayClass) []conditions.Condition { +func validateGatewayParametersRef(npCfg *NginxProxy, ref v1.LocalParametersReference) []conditions.Condition { + var conds []conditions.Condition + + path := field.NewPath("spec.infrastructure.parametersRef") + + if _, ok := supportedParamKinds[string(ref.Kind)]; !ok { + err := field.NotSupported(path.Child("kind"), string(ref.Kind), []string{kinds.NginxProxy}) + conds = append( + conds, + staticConds.NewGatewayRefInvalid(err.Error()), + staticConds.NewGatewayInvalidParameters(err.Error()), + ) + + return conds + } + + if npCfg == nil { + conds = append( + conds, + staticConds.NewGatewayRefNotFound(), + staticConds.NewGatewayInvalidParameters( + field.NotFound(path.Child("name"), ref.Name).Error(), + ), + ) + + return conds + } + + if !npCfg.Valid { + msg := npCfg.ErrMsgs.ToAggregate().Error() + conds = append( + conds, + staticConds.NewGatewayRefInvalid(msg), + staticConds.NewGatewayInvalidParameters(msg), + ) + + return conds + } + + conds = append(conds, staticConds.NewGatewayResolvedRefs()) + return conds +} + +func validateGateway(gw *v1.Gateway, gc *GatewayClass, npCfg *NginxProxy) ([]conditions.Condition, bool) { var conds []conditions.Condition if gc == nil { @@ -136,5 +205,17 @@ func validateGateway(gw *v1.Gateway, gc *GatewayClass) []conditions.Condition { conds = append(conds, staticConds.NewGatewayUnsupportedValue(valErr.Error())...) } - return conds + valid := true + // we evaluate validity before validating parametersRef because an invalid parametersRef/NginxProxy does not + // invalidate the entire Gateway. + if len(conds) > 0 { + valid = false + } + + if gw.Spec.Infrastructure != nil && gw.Spec.Infrastructure.ParametersRef != nil { + paramConds := validateGatewayParametersRef(npCfg, *gw.Spec.Infrastructure.ParametersRef) + conds = append(conds, paramConds...) + } + + return conds, valid } diff --git a/internal/mode/static/state/graph/gateway_test.go b/internal/mode/static/state/graph/gateway_test.go index 36021e6dc3..456c21e29a 100644 --- a/internal/mode/static/state/graph/gateway_test.go +++ b/internal/mode/static/state/graph/gateway_test.go @@ -8,10 +8,13 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "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" - v1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" + "sigs.k8s.io/gateway-api/apis/v1beta1" + ngfAPIv1alpha2 "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha2" + "github.com/nginxinc/nginx-gateway-fabric/internal/framework/conditions" "github.com/nginxinc/nginx-gateway-fabric/internal/framework/helpers" "github.com/nginxinc/nginx-gateway-fabric/internal/framework/kinds" staticConds "github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/state/conditions" @@ -338,6 +341,7 @@ func TestBuildGateway(t *testing.T) { ) type gatewayCfg struct { + ref *v1.LocalParametersReference listeners []v1.Listener addresses []v1.GatewayAddress } @@ -354,12 +358,64 @@ func TestBuildGateway(t *testing.T) { Addresses: cfg.addresses, }, } + + if cfg.ref != nil { + lastCreatedGateway.Spec.Infrastructure = &v1.GatewayInfrastructure{ + ParametersRef: cfg.ref, + } + } return lastCreatedGateway } getLastCreatedGateway := func() *v1.Gateway { return lastCreatedGateway } + validGwNp := &ngfAPIv1alpha2.NginxProxy{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "valid-gw-np", + }, + Spec: ngfAPIv1alpha2.NginxProxySpec{ + Logging: &ngfAPIv1alpha2.NginxLogging{ErrorLevel: helpers.GetPointer(ngfAPIv1alpha2.NginxLogLevelError)}, + }, + } + validGwNpRef := &v1.LocalParametersReference{ + Group: ngfAPIv1alpha2.GroupName, + Kind: kinds.NginxProxy, + Name: validGwNp.Name, + } + invalidGwNp := &ngfAPIv1alpha2.NginxProxy{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "invalid-gw-np", + }, + } + invalidGwNpRef := &v1.LocalParametersReference{ + Group: ngfAPIv1alpha2.GroupName, + Kind: kinds.NginxProxy, + Name: invalidGwNp.Name, + } + invalidKindRef := &v1.LocalParametersReference{ + Group: ngfAPIv1alpha2.GroupName, + Kind: "Invalid", + Name: "invalid-kind", + } + npDoesNotExistRef := &v1.LocalParametersReference{ + Group: ngfAPIv1alpha2.GroupName, + Kind: kinds.NginxProxy, + Name: "does-not-exist", + } + + validGcNp := &ngfAPIv1alpha2.NginxProxy{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "valid-gc-np", + }, + Spec: ngfAPIv1alpha2.NginxProxySpec{ + IPFamily: helpers.GetPointer(ngfAPIv1alpha2.Dual), + }, + } + validGC := &GatewayClass{ Valid: true, } @@ -367,6 +423,14 @@ func TestBuildGateway(t *testing.T) { Valid: false, } + validGCWithNp := &GatewayClass{ + Valid: true, + NginxProxy: &NginxProxy{ + Source: validGcNp, + Valid: true, + }, + } + supportedKindsForListeners := []v1.RouteGroupKind{ {Kind: v1.Kind(kinds.HTTPRoute), Group: helpers.GetPointer[v1.Group](v1.GroupName)}, {Kind: v1.Kind(kinds.GRPCRoute), Group: helpers.GetPointer[v1.Group](v1.GroupName)}, @@ -509,6 +573,90 @@ func TestBuildGateway(t *testing.T) { }, name: "valid https listener with cross-namespace secret; allowed by reference grant", }, + { + gateway: createGateway(gatewayCfg{listeners: []v1.Listener{foo80Listener1}, ref: validGwNpRef}), + gatewayClass: validGC, + expected: &Gateway{ + Source: getLastCreatedGateway(), + Listeners: []*Listener{ + { + Name: "foo-80-1", + Source: foo80Listener1, + Valid: true, + Attachable: true, + Routes: map[RouteKey]*L7Route{}, + L4Routes: map[L4RouteKey]*L4Route{}, + SupportedKinds: supportedKindsForListeners, + }, + }, + Valid: true, + NginxProxy: &NginxProxy{ + Source: validGwNp, + Valid: true, + }, + EffectiveNginxProxy: &EffectiveNginxProxy{ + Logging: &ngfAPIv1alpha2.NginxLogging{ + ErrorLevel: helpers.GetPointer(ngfAPIv1alpha2.NginxLogLevelError), + }, + }, + Conditions: []conditions.Condition{staticConds.NewGatewayResolvedRefs()}, + }, + name: "valid http listener with valid NginxProxy; GatewayClass has no NginxProxy", + }, + { + gateway: createGateway(gatewayCfg{listeners: []v1.Listener{foo80Listener1}, ref: validGwNpRef}), + gatewayClass: validGCWithNp, + expected: &Gateway{ + Source: getLastCreatedGateway(), + Listeners: []*Listener{ + { + Name: "foo-80-1", + Source: foo80Listener1, + Valid: true, + Attachable: true, + Routes: map[RouteKey]*L7Route{}, + L4Routes: map[L4RouteKey]*L4Route{}, + SupportedKinds: supportedKindsForListeners, + }, + }, + Valid: true, + NginxProxy: &NginxProxy{ + Source: validGwNp, + Valid: true, + }, + EffectiveNginxProxy: &EffectiveNginxProxy{ + Logging: &ngfAPIv1alpha2.NginxLogging{ + ErrorLevel: helpers.GetPointer(ngfAPIv1alpha2.NginxLogLevelError), + }, + IPFamily: helpers.GetPointer(ngfAPIv1alpha2.Dual), + }, + Conditions: []conditions.Condition{staticConds.NewGatewayResolvedRefs()}, + }, + name: "valid http listener with valid NginxProxy; GatewayClass has valid NginxProxy too", + }, + { + gateway: createGateway(gatewayCfg{listeners: []v1.Listener{foo80Listener1}}), + gatewayClass: validGCWithNp, + expected: &Gateway{ + Source: getLastCreatedGateway(), + Listeners: []*Listener{ + { + Name: "foo-80-1", + Source: foo80Listener1, + Valid: true, + Attachable: true, + Routes: map[RouteKey]*L7Route{}, + L4Routes: map[L4RouteKey]*L4Route{}, + SupportedKinds: supportedKindsForListeners, + }, + }, + Valid: true, + EffectiveNginxProxy: &EffectiveNginxProxy{ + IPFamily: helpers.GetPointer(ngfAPIv1alpha2.Dual), + }, + }, + name: "valid http listener; GatewayClass has valid NginxProxy", + }, { gateway: createGateway(gatewayCfg{listeners: []v1.Listener{crossNamespaceSecretListener}}), gatewayClass: validGC, @@ -1024,6 +1172,116 @@ func TestBuildGateway(t *testing.T) { }, name: "https listener and tls listener with non overlapping hostnames", }, + { + gateway: createGateway(gatewayCfg{listeners: []v1.Listener{foo80Listener1}, ref: invalidKindRef}), + gatewayClass: validGC, + expected: &Gateway{ + Source: getLastCreatedGateway(), + Listeners: []*Listener{ + { + Name: "foo-80-1", + Source: foo80Listener1, + Valid: true, + Attachable: true, + Routes: map[RouteKey]*L7Route{}, + L4Routes: map[L4RouteKey]*L4Route{}, + SupportedKinds: supportedKindsForListeners, + }, + }, + Valid: true, // invalid parametersRef does not invalidate Gateway. + Conditions: []conditions.Condition{ + staticConds.NewGatewayRefInvalid( + "spec.infrastructure.parametersRef.kind: Unsupported value: \"Invalid\": " + + "supported values: \"NginxProxy\"", + ), + staticConds.NewGatewayInvalidParameters( + "spec.infrastructure.parametersRef.kind: Unsupported value: \"Invalid\": " + + "supported values: \"NginxProxy\"", + ), + }, + }, + name: "invalid parameters ref kind", + }, + { + gateway: createGateway(gatewayCfg{listeners: []v1.Listener{foo80Listener1}, ref: npDoesNotExistRef}), + gatewayClass: validGC, + expected: &Gateway{ + Source: getLastCreatedGateway(), + Listeners: []*Listener{ + { + Name: "foo-80-1", + Source: foo80Listener1, + Valid: true, + Attachable: true, + Routes: map[RouteKey]*L7Route{}, + L4Routes: map[L4RouteKey]*L4Route{}, + SupportedKinds: supportedKindsForListeners, + }, + }, + Valid: true, // invalid parametersRef does not invalidate Gateway. + Conditions: []conditions.Condition{ + staticConds.NewGatewayRefNotFound(), + staticConds.NewGatewayInvalidParameters( + "spec.infrastructure.parametersRef.name: Not found: \"does-not-exist\"", + ), + }, + }, + name: "referenced NginxProxy doesn't exist", + }, + { + gateway: createGateway(gatewayCfg{listeners: []v1.Listener{foo80Listener1}, ref: invalidGwNpRef}), + gatewayClass: validGC, + expected: &Gateway{ + Source: getLastCreatedGateway(), + Listeners: []*Listener{ + { + Name: "foo-80-1", + Source: foo80Listener1, + Valid: true, + Attachable: true, + Routes: map[RouteKey]*L7Route{}, + L4Routes: map[L4RouteKey]*L4Route{}, + SupportedKinds: supportedKindsForListeners, + }, + }, + Valid: true, // invalid NginxProxy does not invalidate Gateway. + NginxProxy: &NginxProxy{ + Source: invalidGwNp, + ErrMsgs: field.ErrorList{ + field.Required(field.NewPath("somePath"), "someField"), // fake error + }, + Valid: false, + }, + Conditions: []conditions.Condition{ + staticConds.NewGatewayRefInvalid("somePath: Required value: someField"), + staticConds.NewGatewayInvalidParameters("somePath: Required value: someField"), + }, + }, + name: "invalid NginxProxy", + }, + { + gateway: createGateway( + gatewayCfg{listeners: []v1.Listener{foo80Listener1, invalidProtocolListener}, ref: invalidGwNpRef}, + ), + gatewayClass: invalidGC, + expected: &Gateway{ + Source: getLastCreatedGateway(), + Valid: false, + NginxProxy: &NginxProxy{ + Source: invalidGwNp, + ErrMsgs: field.ErrorList{ + field.Required(field.NewPath("somePath"), "someField"), // fake error + }, + Valid: false, + }, + Conditions: append( + staticConds.NewGatewayInvalid("GatewayClass is invalid"), + staticConds.NewGatewayRefInvalid("somePath: Required value: someField"), + staticConds.NewGatewayInvalidParameters("somePath: Required value: someField"), + ), + }, + name: "invalid gatewayclass and invalid NginxProxy", + }, } secretResolver := newSecretResolver( @@ -1032,12 +1290,106 @@ func TestBuildGateway(t *testing.T) { client.ObjectKeyFromObject(secretDiffNamespace): secretDiffNamespace, }) + nginxProxies := map[types.NamespacedName]*NginxProxy{ + client.ObjectKeyFromObject(validGwNp): {Valid: true, Source: validGwNp}, + client.ObjectKeyFromObject(validGcNp): {Valid: true, Source: validGcNp}, + client.ObjectKeyFromObject(invalidGwNp): { + Source: invalidGwNp, + ErrMsgs: append(field.ErrorList{}, field.Required(field.NewPath("somePath"), "someField")), + Valid: false, + }, + } + for _, test := range tests { t.Run(test.name, func(t *testing.T) { g := NewWithT(t) resolver := newReferenceGrantResolver(test.refGrants) - result := buildGateway(test.gateway, secretResolver, test.gatewayClass, resolver, protectedPorts) + result := buildGateway(test.gateway, secretResolver, test.gatewayClass, resolver, protectedPorts, nginxProxies) g.Expect(helpers.Diff(test.expected, result)).To(BeEmpty()) }) } } + +func TestValidateGatewayParametersRef(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + np *NginxProxy + ref v1.LocalParametersReference + expConds []conditions.Condition + }{ + { + name: "unsupported parameter ref kind", + ref: v1.LocalParametersReference{ + Kind: "wrong-kind", + }, + expConds: []conditions.Condition{ + staticConds.NewGatewayRefInvalid( + "spec.infrastructure.parametersRef.kind: Unsupported value: \"wrong-kind\": " + + "supported values: \"NginxProxy\"", + ), + staticConds.NewGatewayInvalidParameters( + "spec.infrastructure.parametersRef.kind: Unsupported value: \"wrong-kind\": " + + "supported values: \"NginxProxy\"", + ), + }, + }, + { + name: "nil nginx proxy", + ref: v1.LocalParametersReference{ + Group: ngfAPIv1alpha2.GroupName, + Kind: kinds.NginxProxy, + Name: "np", + }, + expConds: []conditions.Condition{ + staticConds.NewGatewayRefNotFound(), + staticConds.NewGatewayInvalidParameters("spec.infrastructure.parametersRef.name: Not found: \"np\""), + }, + }, + { + name: "invalid nginx proxy", + np: &NginxProxy{ + Source: &ngfAPIv1alpha2.NginxProxy{}, + ErrMsgs: field.ErrorList{ + field.Required(field.NewPath("somePath"), "someField"), // fake error + }, + Valid: false, + }, + ref: v1.LocalParametersReference{ + Group: ngfAPIv1alpha2.GroupName, + Kind: kinds.NginxProxy, + Name: "np", + }, + expConds: []conditions.Condition{ + staticConds.NewGatewayRefInvalid("somePath: Required value: someField"), + staticConds.NewGatewayInvalidParameters("somePath: Required value: someField"), + }, + }, + { + name: "valid", + np: &NginxProxy{ + Source: &ngfAPIv1alpha2.NginxProxy{}, + Valid: true, + }, + ref: v1.LocalParametersReference{ + Group: ngfAPIv1alpha2.GroupName, + Kind: kinds.NginxProxy, + Name: "np", + }, + expConds: []conditions.Condition{ + staticConds.NewGatewayResolvedRefs(), + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + t.Parallel() + g := NewWithT(t) + + conds := validateGatewayParametersRef(test.np, test.ref) + g.Expect(conds).To(BeEquivalentTo(test.expConds)) + }) + } +} diff --git a/internal/mode/static/state/graph/gatewayclass.go b/internal/mode/static/state/graph/gatewayclass.go index 9fc1f67259..95e7c684c8 100644 --- a/internal/mode/static/state/graph/gatewayclass.go +++ b/internal/mode/static/state/graph/gatewayclass.go @@ -1,8 +1,6 @@ package graph import ( - "errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/validation/field" @@ -19,6 +17,8 @@ import ( type GatewayClass struct { // Source is the source resource. Source *v1.GatewayClass + // NginxProxy is the NginxProxy resource referenced by this GatewayClass. + NginxProxy *NginxProxy // Conditions include Conditions for the GatewayClass. Conditions []conditions.Condition // Valid shows whether the GatewayClass is valid. @@ -34,7 +34,7 @@ type processedGatewayClasses struct { // processGatewayClasses returns the "Winner" GatewayClass, which is defined in // the command-line argument and references this controller, and a list of "Ignored" GatewayClasses // that reference this controller, but are not named in the command-line argument. -// Also returns a boolean that says whether or not the GatewayClass defined +// Also returns a boolean that says whether the GatewayClass defined // in the command-line argument exists, regardless of which controller it references. func processGatewayClasses( gcs map[types.NamespacedName]*v1.GatewayClass, @@ -63,22 +63,66 @@ func processGatewayClasses( func buildGatewayClass( gc *v1.GatewayClass, - npCfg *NginxProxy, + nps map[types.NamespacedName]*NginxProxy, crdVersions map[types.NamespacedName]*metav1.PartialObjectMetadata, ) *GatewayClass { if gc == nil { return nil } - conds, valid := validateGatewayClass(gc, npCfg, crdVersions) + var np *NginxProxy + if gc.Spec.ParametersRef != nil { + np = getNginxProxyForGatewayClass(*gc.Spec.ParametersRef, nps) + } + + conds, valid := validateGatewayClass(gc, np, crdVersions) return &GatewayClass{ Source: gc, + NginxProxy: np, Valid: valid, Conditions: conds, } } +func getNginxProxyForGatewayClass( + ref v1.ParametersReference, + nps map[types.NamespacedName]*NginxProxy, +) *NginxProxy { + if ref.Namespace == nil { + return nil + } + + npName := types.NamespacedName{Name: ref.Name, Namespace: string(*ref.Namespace)} + + return nps[npName] +} + +func validateGatewayClassParametersRef(path *field.Path, ref v1.ParametersReference) []conditions.Condition { + var errs field.ErrorList + + if _, ok := supportedParamKinds[string(ref.Kind)]; !ok { + errs = append( + errs, + field.NotSupported(path.Child("kind"), string(ref.Kind), []string{kinds.NginxProxy}), + ) + } + + if ref.Namespace == nil { + errs = append(errs, field.Required(path.Child("namespace"), "ParametersRef must specify Namespace")) + } + + if len(errs) > 0 { + msg := errs.ToAggregate().Error() + return []conditions.Condition{ + staticConds.NewGatewayClassRefInvalid(msg), + staticConds.NewGatewayClassInvalidParameters(msg), + } + } + + return nil +} + func validateGatewayClass( gc *v1.GatewayClass, npCfg *NginxProxy, @@ -86,28 +130,44 @@ func validateGatewayClass( ) ([]conditions.Condition, bool) { var conds []conditions.Condition - if gc.Spec.ParametersRef != nil { - var err error - path := field.NewPath("spec").Child("parametersRef") - if _, ok := supportedParamKinds[string(gc.Spec.ParametersRef.Kind)]; !ok { - err = field.NotSupported(path.Child("kind"), string(gc.Spec.ParametersRef.Kind), []string{kinds.NginxProxy}) - } else if npCfg == nil { - err = field.NotFound(path.Child("name"), gc.Spec.ParametersRef.Name) - conds = append(conds, staticConds.NewGatewayClassRefNotFound()) - } else if !npCfg.Valid { - err = errors.New(npCfg.ErrMsgs.ToAggregate().Error()) - } + supportedVersionConds, versionsValid := gatewayclass.ValidateCRDVersions(crdVersions) + conds = append(conds, supportedVersionConds...) - if err != nil { - conds = append(conds, staticConds.NewGatewayClassInvalidParameters(err.Error())) - } else { - conds = append(conds, staticConds.NewGatewayClassResolvedRefs()) - } + if gc.Spec.ParametersRef == nil { + return conds, versionsValid } - supportedVersionConds, versionsValid := gatewayclass.ValidateCRDVersions(crdVersions) + path := field.NewPath("spec").Child("parametersRef") + refConds := validateGatewayClassParametersRef(path, *gc.Spec.ParametersRef) + + // return early since parametersRef isn't valid + if len(refConds) > 0 { + conds = append(conds, refConds...) + return conds, versionsValid + } + + if npCfg == nil { + conds = append( + conds, + staticConds.NewGatewayClassRefNotFound(), + staticConds.NewGatewayClassInvalidParameters( + field.NotFound(path.Child("name"), gc.Spec.ParametersRef.Name).Error(), + ), + ) + return conds, versionsValid + } + + if !npCfg.Valid { + msg := npCfg.ErrMsgs.ToAggregate().Error() + conds = append( + conds, + staticConds.NewGatewayClassRefInvalid(msg), + staticConds.NewGatewayClassInvalidParameters(msg), + ) + return conds, versionsValid + } - return append(conds, supportedVersionConds...), versionsValid + return append(conds, staticConds.NewGatewayClassResolvedRefs()), versionsValid } var supportedParamKinds = map[string]struct{}{ diff --git a/internal/mode/static/state/graph/gatewayclass_test.go b/internal/mode/static/state/graph/gatewayclass_test.go index 35e7f04764..c765047e24 100644 --- a/internal/mode/static/state/graph/gatewayclass_test.go +++ b/internal/mode/static/state/graph/gatewayclass_test.go @@ -10,7 +10,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" v1 "sigs.k8s.io/gateway-api/apis/v1" - ngfAPI "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha1" + ngfAPIv1alpha2 "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha2" "github.com/nginxinc/nginx-gateway-fabric/internal/framework/conditions" "github.com/nginxinc/nginx-gateway-fabric/internal/framework/gatewayclass" "github.com/nginxinc/nginx-gateway-fabric/internal/framework/helpers" @@ -127,17 +127,32 @@ func TestProcessGatewayClasses(t *testing.T) { func TestBuildGatewayClass(t *testing.T) { t.Parallel() validGC := &v1.GatewayClass{} + npNsName := types.NamespacedName{Namespace: "test", Name: "nginx-proxy"} + + np := &ngfAPIv1alpha2.NginxProxy{ + TypeMeta: metav1.TypeMeta{ + Kind: kinds.NginxProxy, + }, + Spec: ngfAPIv1alpha2.NginxProxySpec{ + Telemetry: &ngfAPIv1alpha2.Telemetry{ + ServiceName: helpers.GetPointer("my-svc"), + }, + }, + } gcWithParams := &v1.GatewayClass{ Spec: v1.GatewayClassSpec{ ParametersRef: &v1.ParametersReference{ Kind: v1.Kind(kinds.NginxProxy), - Namespace: helpers.GetPointer(v1.Namespace("test")), - Name: "nginx-proxy", + Namespace: helpers.GetPointer(v1.Namespace(npNsName.Namespace)), + Name: npNsName.Name, }, }, } + gcWithParamsNoNamespace := gcWithParams.DeepCopy() + gcWithParamsNoNamespace.Spec.ParametersRef.Namespace = nil + gcWithInvalidKind := &v1.GatewayClass{ Spec: v1.GatewayClassSpec{ ParametersRef: &v1.ParametersReference{ @@ -168,12 +183,11 @@ func TestBuildGatewayClass(t *testing.T) { } tests := []struct { - gc *v1.GatewayClass - np *NginxProxy - crdMetadata map[types.NamespacedName]*metav1.PartialObjectMetadata - expected *GatewayClass - name string - expNPInvalid bool + gc *v1.GatewayClass + nps map[types.NamespacedName]*NginxProxy + crdMetadata map[types.NamespacedName]*metav1.PartialObjectMetadata + expected *GatewayClass + name string }{ { gc: validGC, @@ -191,46 +205,54 @@ func TestBuildGatewayClass(t *testing.T) { }, { gc: gcWithParams, - np: &NginxProxy{ - Source: &ngfAPI.NginxProxy{ - TypeMeta: metav1.TypeMeta{ - Kind: kinds.NginxProxy, - }, - Spec: ngfAPI.NginxProxySpec{ - Telemetry: &ngfAPI.Telemetry{ - ServiceName: helpers.GetPointer("my-svc"), - }, - }, + nps: map[types.NamespacedName]*NginxProxy{ + npNsName: { + Source: np, + Valid: true, }, - Valid: true, }, expected: &GatewayClass{ Source: gcWithParams, Valid: true, Conditions: []conditions.Condition{staticConds.NewGatewayClassResolvedRefs()}, + NginxProxy: &NginxProxy{ + Valid: true, + Source: np, + }, }, name: "valid gatewayclass with paramsRef", }, { - gc: gcWithInvalidKind, - np: &NginxProxy{ - Source: &ngfAPI.NginxProxy{ - TypeMeta: metav1.TypeMeta{ - Kind: kinds.NginxProxy, - }, + gc: gcWithParamsNoNamespace, + expected: &GatewayClass{ + Source: gcWithParamsNoNamespace, + Valid: true, + Conditions: []conditions.Condition{ + staticConds.NewGatewayClassRefInvalid( + "spec.parametersRef.namespace: Required value: ParametersRef must specify Namespace", + ), + staticConds.NewGatewayClassInvalidParameters( + "spec.parametersRef.namespace: Required value: ParametersRef must specify Namespace", + ), }, - Valid: true, }, + name: "valid gatewayclass with paramsRef missing namespace", + }, + { + gc: gcWithInvalidKind, expected: &GatewayClass{ Source: gcWithInvalidKind, Valid: true, Conditions: []conditions.Condition{ + staticConds.NewGatewayClassRefInvalid( + "spec.parametersRef.kind: Unsupported value: \"Invalid\": supported values: \"NginxProxy\"", + ), staticConds.NewGatewayClassInvalidParameters( "spec.parametersRef.kind: Unsupported value: \"Invalid\": supported values: \"NginxProxy\"", ), }, }, - name: "invalid gatewayclass with unsupported paramsRef Kind", + name: "valid gatewayclass with unsupported paramsRef Kind", }, { gc: gcWithParams, @@ -244,38 +266,57 @@ func TestBuildGatewayClass(t *testing.T) { ), }, }, - expNPInvalid: true, - name: "invalid gatewayclass with paramsRef resource that doesn't exist", + name: "valid gatewayclass with paramsRef resource that doesn't exist", }, { gc: gcWithParams, - np: &NginxProxy{ - Valid: false, - ErrMsgs: field.ErrorList{ - field.Invalid( - field.NewPath("spec", "telemetry", "serviceName"), - "my-svc", - "error", - ), - field.Invalid( - field.NewPath("spec", "telemetry", "exporter", "endpoint"), - "my-endpoint", - "error", - ), + nps: map[types.NamespacedName]*NginxProxy{ + npNsName: { + Valid: false, + ErrMsgs: field.ErrorList{ + field.Invalid( + field.NewPath("spec", "telemetry", "serviceName"), + "my-svc", + "error", + ), + field.Invalid( + field.NewPath("spec", "telemetry", "exporter", "endpoint"), + "my-endpoint", + "error", + ), + }, }, }, expected: &GatewayClass{ Source: gcWithParams, Valid: true, Conditions: []conditions.Condition{ + staticConds.NewGatewayClassRefInvalid( + "[spec.telemetry.serviceName: Invalid value: \"my-svc\": error" + + ", spec.telemetry.exporter.endpoint: Invalid value: \"my-endpoint\": error]", + ), staticConds.NewGatewayClassInvalidParameters( "[spec.telemetry.serviceName: Invalid value: \"my-svc\": error" + ", spec.telemetry.exporter.endpoint: Invalid value: \"my-endpoint\": error]", ), }, + NginxProxy: &NginxProxy{ + Valid: false, + ErrMsgs: field.ErrorList{ + field.Invalid( + field.NewPath("spec", "telemetry", "serviceName"), + "my-svc", + "error", + ), + field.Invalid( + field.NewPath("spec", "telemetry", "exporter", "endpoint"), + "my-endpoint", + "error", + ), + }, + }, }, - expNPInvalid: true, - name: "invalid gatewayclass with invalid paramsRef resource", + name: "valid gatewayclass with invalid paramsRef resource", }, { gc: validGC, @@ -294,11 +335,8 @@ func TestBuildGatewayClass(t *testing.T) { t.Parallel() g := NewWithT(t) - result := buildGatewayClass(test.gc, test.np, test.crdMetadata) + result := buildGatewayClass(test.gc, test.nps, test.crdMetadata) g.Expect(helpers.Diff(test.expected, result)).To(BeEmpty()) - if test.np != nil { - g.Expect(test.np.Valid).ToNot(Equal(test.expNPInvalid)) - } }) } } diff --git a/internal/mode/static/state/graph/graph.go b/internal/mode/static/state/graph/graph.go index 0b56ec1018..6e1f6b78c0 100644 --- a/internal/mode/static/state/graph/graph.go +++ b/internal/mode/static/state/graph/graph.go @@ -14,7 +14,8 @@ import ( "sigs.k8s.io/gateway-api/apis/v1alpha3" "sigs.k8s.io/gateway-api/apis/v1beta1" - ngfAPI "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha1" + ngfAPIv1alpha1 "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha1" + ngfAPIv1alpha2 "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha2" "github.com/nginxinc/nginx-gateway-fabric/internal/framework/controller/index" "github.com/nginxinc/nginx-gateway-fabric/internal/framework/kinds" ngftypes "github.com/nginxinc/nginx-gateway-fabric/internal/framework/types" @@ -35,10 +36,10 @@ type ClusterState struct { CRDMetadata map[types.NamespacedName]*metav1.PartialObjectMetadata BackendTLSPolicies map[types.NamespacedName]*v1alpha3.BackendTLSPolicy ConfigMaps map[types.NamespacedName]*v1.ConfigMap - NginxProxies map[types.NamespacedName]*ngfAPI.NginxProxy + NginxProxies map[types.NamespacedName]*ngfAPIv1alpha2.NginxProxy GRPCRoutes map[types.NamespacedName]*gatewayv1.GRPCRoute NGFPolicies map[PolicyKey]policies.Policy - SnippetsFilters map[types.NamespacedName]*ngfAPI.SnippetsFilter + SnippetsFilters map[types.NamespacedName]*ngfAPIv1alpha1.SnippetsFilter } // Graph is a Graph-like representation of Gateway API resources. @@ -70,10 +71,10 @@ type Graph struct { ReferencedServices map[types.NamespacedName]*ReferencedService // ReferencedCaCertConfigMaps includes ConfigMaps that have been referenced by any BackendTLSPolicies. ReferencedCaCertConfigMaps map[types.NamespacedName]*CaCertConfigMap + // ReferencedNginxProxies includes NginxProxies that have been referenced by a GatewayClass or the winning Gateway. + ReferencedNginxProxies map[types.NamespacedName]*NginxProxy // BackendTLSPolicies holds BackendTLSPolicy resources. BackendTLSPolicies map[types.NamespacedName]*BackendTLSPolicy - // NginxProxy holds the NginxProxy config for the GatewayClass. - NginxProxy *NginxProxy // NGFPolicies holds all NGF Policies. NGFPolicies map[PolicyKey]*Policy // GlobalSettings contains global settings from the current state of the graph that may be @@ -126,9 +127,10 @@ func (g *Graph) IsReferenced(resourceType ngftypes.ObjectType, nsname types.Name // Service Namespace should be the same Namespace as the EndpointSlice _, exists := g.ReferencedServices[types.NamespacedName{Namespace: nsname.Namespace, Name: svcName}] return exists - // NginxProxy reference exists if it is linked to a GatewayClass. - case *ngfAPI.NginxProxy: - return isNginxProxyReferenced(nsname, g.GatewayClass) + // NginxProxy reference exists if the GatewayClass or winning Gateway references it. + case *ngfAPIv1alpha2.NginxProxy: + _, exists := g.ReferencedNginxProxies[nsname] + return exists default: return false } @@ -200,32 +202,39 @@ func BuildGraph( validators validation.Validators, protectedPorts ProtectedPorts, ) *Graph { - var globalSettings *policies.GlobalSettings - processedGwClasses, gcExists := processGatewayClasses(state.GatewayClasses, gcName, controllerName) if gcExists && processedGwClasses.Winner == nil { // configured GatewayClass does not reference this controller return &Graph{} } - npCfg := buildNginxProxy(state.NginxProxies, processedGwClasses.Winner, validators.GenericValidator) - gc := buildGatewayClass(processedGwClasses.Winner, npCfg, state.CRDMetadata) - if gc != nil && npCfg != nil && npCfg.Source != nil { - spec := npCfg.Source.Spec - globalSettings = &policies.GlobalSettings{ - NginxProxyValid: npCfg.Valid, - TelemetryEnabled: spec.Telemetry != nil && spec.Telemetry.Exporter != nil, - } - } + processedGws := processGateways(state.Gateways, gcName) + processedNginxProxies := processNginxProxies( + state.NginxProxies, + validators.GenericValidator, + processedGwClasses.Winner, + processedGws.Winner, + ) + + gc := buildGatewayClass( + processedGwClasses.Winner, + processedNginxProxies, + state.CRDMetadata, + ) secretResolver := newSecretResolver(state.Secrets) configMapResolver := newConfigMapResolver(state.ConfigMaps) - processedGws := processGateways(state.Gateways, gcName) - refGrantResolver := newReferenceGrantResolver(state.ReferenceGrants) - gw := buildGateway(processedGws.Winner, secretResolver, gc, refGrantResolver, protectedPorts) + gw := buildGateway( + processedGws.Winner, + secretResolver, + gc, + refGrantResolver, + protectedPorts, + processedNginxProxies, + ) processedBackendTLSPolicies := processBackendTLSPolicies( state.BackendTLSPolicies, @@ -235,13 +244,17 @@ func BuildGraph( ) processedSnippetsFilters := processSnippetsFilters(state.SnippetsFilters) + var effectiveNginxProxy *EffectiveNginxProxy + if gw != nil { + effectiveNginxProxy = gw.EffectiveNginxProxy + } routes := buildRoutesForGateways( validators.HTTPFieldsValidator, state.HTTPRoutes, state.GRPCRoutes, processedGws.GetAllNsNames(), - npCfg, + effectiveNginxProxy, processedSnippetsFilters, ) @@ -249,17 +262,30 @@ func BuildGraph( state.TLSRoutes, processedGws.GetAllNsNames(), state.Services, - npCfg, + effectiveNginxProxy, refGrantResolver, ) bindRoutesToListeners(routes, l4routes, gw, state.Namespaces) - addBackendRefsToRouteRules(routes, refGrantResolver, state.Services, processedBackendTLSPolicies, npCfg) + addBackendRefsToRouteRules( + routes, + refGrantResolver, + state.Services, + processedBackendTLSPolicies, + effectiveNginxProxy, + ) referencedNamespaces := buildReferencedNamespaces(state.Namespaces, gw) referencedServices := buildReferencedServices(routes, l4routes, gw) + var globalSettings *policies.GlobalSettings + if gw != nil && gw.EffectiveNginxProxy != nil { + globalSettings = &policies.GlobalSettings{ + NginxProxyValid: true, // for effective nginx proxy to be set, the config must be valid + TelemetryEnabled: telemetryEnabledForNginxProxy(gw.EffectiveNginxProxy), + } + } // policies must be processed last because they rely on the state of the other resources in the graph processedPolicies := processPolicies( state.NGFPolicies, @@ -283,8 +309,8 @@ func BuildGraph( ReferencedNamespaces: referencedNamespaces, ReferencedServices: referencedServices, ReferencedCaCertConfigMaps: configMapResolver.getResolvedConfigMaps(), + ReferencedNginxProxies: processedNginxProxies, BackendTLSPolicies: processedBackendTLSPolicies, - NginxProxy: npCfg, NGFPolicies: processedPolicies, GlobalSettings: globalSettings, SnippetsFilters: processedSnippetsFilters, diff --git a/internal/mode/static/state/graph/graph_test.go b/internal/mode/static/state/graph/graph_test.go index f2b71d05f8..0c5a1ef052 100644 --- a/internal/mode/static/state/graph/graph_test.go +++ b/internal/mode/static/state/graph/graph_test.go @@ -17,7 +17,8 @@ import ( "sigs.k8s.io/gateway-api/apis/v1alpha3" "sigs.k8s.io/gateway-api/apis/v1beta1" - ngfAPI "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha1" + ngfAPIv1alpha1 "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha1" + ngfAPIv1alpha2 "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha2" "github.com/nginxinc/nginx-gateway-fabric/internal/framework/conditions" "github.com/nginxinc/nginx-gateway-fabric/internal/framework/controller/index" "github.com/nginxinc/nginx-gateway-fabric/internal/framework/helpers" @@ -110,35 +111,35 @@ func TestBuildGraph(t *testing.T) { } refSnippetsFilterExtensionRef := &gatewayv1.LocalObjectReference{ - Group: ngfAPI.GroupName, + Group: ngfAPIv1alpha1.GroupName, Kind: kinds.SnippetsFilter, Name: "ref-snippets-filter", } - unreferencedSnippetsFilter := &ngfAPI.SnippetsFilter{ + unreferencedSnippetsFilter := &ngfAPIv1alpha1.SnippetsFilter{ ObjectMeta: metav1.ObjectMeta{ Name: "unref-snippets-filter", Namespace: testNs, }, - Spec: ngfAPI.SnippetsFilterSpec{ - Snippets: []ngfAPI.Snippet{ + Spec: ngfAPIv1alpha1.SnippetsFilterSpec{ + Snippets: []ngfAPIv1alpha1.Snippet{ { - Context: ngfAPI.NginxContextMain, + Context: ngfAPIv1alpha1.NginxContextMain, Value: "main snippet", }, }, }, } - referencedSnippetsFilter := &ngfAPI.SnippetsFilter{ + referencedSnippetsFilter := &ngfAPIv1alpha1.SnippetsFilter{ ObjectMeta: metav1.ObjectMeta{ Name: "ref-snippets-filter", Namespace: testNs, }, - Spec: ngfAPI.SnippetsFilterSpec{ - Snippets: []ngfAPI.Snippet{ + Spec: ngfAPIv1alpha1.SnippetsFilterSpec{ + Snippets: []ngfAPIv1alpha1.Snippet{ { - Context: ngfAPI.NginxContextHTTPServer, + Context: ngfAPIv1alpha1.NginxContextHTTPServer, Value: "server snippet", }, }, @@ -149,8 +150,8 @@ func TestBuildGraph(t *testing.T) { Source: unreferencedSnippetsFilter, Valid: true, Referenced: false, - Snippets: map[ngfAPI.NginxContext]string{ - ngfAPI.NginxContextMain: "main snippet", + Snippets: map[ngfAPIv1alpha1.NginxContext]string{ + ngfAPIv1alpha1.NginxContextMain: "main snippet", }, } @@ -158,8 +159,8 @@ func TestBuildGraph(t *testing.T) { Source: referencedSnippetsFilter, Valid: true, Referenced: true, - Snippets: map[ngfAPI.NginxContext]string{ - ngfAPI.NginxContextHTTPServer: "server snippet", + Snippets: map[ngfAPIv1alpha1.NginxContext]string{ + ngfAPIv1alpha1.NginxContextHTTPServer: "server snippet", }, } @@ -368,7 +369,7 @@ func TestBuildGraph(t *testing.T) { }, } - createGateway := func(name string) *gatewayv1.Gateway { + createGateway := func(name, nginxProxyName string) *gatewayv1.Gateway { return &gatewayv1.Gateway{ ObjectMeta: metav1.ObjectMeta{ Namespace: testNs, @@ -376,6 +377,13 @@ func TestBuildGraph(t *testing.T) { }, Spec: gatewayv1.GatewaySpec{ GatewayClassName: gcName, + Infrastructure: &gatewayv1.GatewayInfrastructure{ + ParametersRef: &gatewayv1.LocalParametersReference{ + Group: ngfAPIv1alpha2.GroupName, + Kind: kinds.NginxProxy, + Name: nginxProxyName, + }, + }, Listeners: []gatewayv1.Listener{ { Name: "listener-80-1", @@ -434,8 +442,35 @@ func TestBuildGraph(t *testing.T) { } } - gw1 := createGateway("gateway-1") - gw2 := createGateway("gateway-2") + gw1 := createGateway("gateway-1", "np-1") + gw2 := createGateway("gateway-2", "np-2") + + // np1 is referenced by gw1 and sets the nginx error log to error. + // Since gw1 is the winning gateway, we expect this nginx proxy to be configured and merged with the gateway class + // nginx proxy configuration. + np1 := &ngfAPIv1alpha2.NginxProxy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "np-1", + Namespace: testNs, + }, + Spec: ngfAPIv1alpha2.NginxProxySpec{ + Logging: &ngfAPIv1alpha2.NginxLogging{ + ErrorLevel: helpers.GetPointer(ngfAPIv1alpha2.NginxLogLevelError), + }, + }, + } + + // np2 is referenced by gw2 and sets the IPFamily to IPv6. + // Since gw2 is not the winning gateway, we do not expect this nginx proxy to be configured. + np2 := &ngfAPIv1alpha2.NginxProxy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "np-2", + Namespace: testNs, + }, + Spec: ngfAPIv1alpha2.NginxProxySpec{ + IPFamily: helpers.GetPointer(ngfAPIv1alpha2.IPv6), + }, + } svc := &v1.Service{ ObjectMeta: metav1.ObjectMeta{ @@ -526,20 +561,22 @@ func TestBuildGraph(t *testing.T) { }, } - proxy := &ngfAPI.NginxProxy{ + // npGlobal is referenced by the gateway class, and we expect it to be configured and merged with np1. + npGlobal := &ngfAPIv1alpha2.NginxProxy{ ObjectMeta: metav1.ObjectMeta{ - Name: "nginx-proxy", + Name: "np-global", + Namespace: testNs, }, - Spec: ngfAPI.NginxProxySpec{ - Telemetry: &ngfAPI.Telemetry{ - Exporter: &ngfAPI.TelemetryExporter{ - Endpoint: "1.2.3.4:123", - Interval: helpers.GetPointer(ngfAPI.Duration("5s")), + Spec: ngfAPIv1alpha2.NginxProxySpec{ + Telemetry: &ngfAPIv1alpha2.Telemetry{ + Exporter: &ngfAPIv1alpha2.TelemetryExporter{ + Endpoint: helpers.GetPointer("1.2.3.4:123"), + Interval: helpers.GetPointer(ngfAPIv1alpha1.Duration("5s")), BatchSize: helpers.GetPointer(int32(512)), BatchCount: helpers.GetPointer(int32(4)), }, ServiceName: helpers.GetPointer("my-svc"), - SpanAttributes: []ngfAPI.SpanAttribute{ + SpanAttributes: []ngfAPIv1alpha1.SpanAttribute{ {Key: "key", Value: "value"}, }, }, @@ -553,13 +590,13 @@ func TestBuildGraph(t *testing.T) { // Testing one type of policy per attachment point should suffice. polGVK := schema.GroupVersionKind{Kind: kinds.ClientSettingsPolicy} hrPolicyKey := PolicyKey{GVK: polGVK, NsName: types.NamespacedName{Namespace: testNs, Name: "hrPolicy"}} - hrPolicy := &ngfAPI.ClientSettingsPolicy{ + hrPolicy := &ngfAPIv1alpha1.ClientSettingsPolicy{ ObjectMeta: metav1.ObjectMeta{ Name: "hrPolicy", Namespace: testNs, }, TypeMeta: metav1.TypeMeta{Kind: kinds.ClientSettingsPolicy}, - Spec: ngfAPI.ClientSettingsPolicySpec{ + Spec: ngfAPIv1alpha1.ClientSettingsPolicySpec{ TargetRef: createTestRef(kinds.HTTPRoute, gatewayv1.GroupName, "hr-1"), }, } @@ -586,13 +623,13 @@ func TestBuildGraph(t *testing.T) { } gwPolicyKey := PolicyKey{GVK: polGVK, NsName: types.NamespacedName{Namespace: testNs, Name: "gwPolicy"}} - gwPolicy := &ngfAPI.ClientSettingsPolicy{ + gwPolicy := &ngfAPIv1alpha1.ClientSettingsPolicy{ ObjectMeta: metav1.ObjectMeta{ Name: "gwPolicy", Namespace: testNs, }, TypeMeta: metav1.TypeMeta{Kind: kinds.ClientSettingsPolicy}, - Spec: ngfAPI.ClientSettingsPolicySpec{ + Spec: ngfAPIv1alpha1.ClientSettingsPolicySpec{ TargetRef: createTestRef(kinds.Gateway, gatewayv1.GroupName, "gateway-1"), }, } @@ -661,14 +698,16 @@ func TestBuildGraph(t *testing.T) { ConfigMaps: map[types.NamespacedName]*v1.ConfigMap{ client.ObjectKeyFromObject(cm): cm, }, - NginxProxies: map[types.NamespacedName]*ngfAPI.NginxProxy{ - client.ObjectKeyFromObject(proxy): proxy, + NginxProxies: map[types.NamespacedName]*ngfAPIv1alpha2.NginxProxy{ + client.ObjectKeyFromObject(npGlobal): npGlobal, + client.ObjectKeyFromObject(np1): np1, + client.ObjectKeyFromObject(np2): np2, }, NGFPolicies: map[PolicyKey]policies.Policy{ hrPolicyKey: hrPolicy, gwPolicyKey: gwPolicy, }, - SnippetsFilters: map[types.NamespacedName]*ngfAPI.SnippetsFilter{ + SnippetsFilters: map[types.NamespacedName]*ngfAPIv1alpha1.SnippetsFilter{ client.ObjectKeyFromObject(unreferencedSnippetsFilter): unreferencedSnippetsFilter, client.ObjectKeyFromObject(referencedSnippetsFilter): referencedSnippetsFilter, }, @@ -820,6 +859,10 @@ func TestBuildGraph(t *testing.T) { Source: gc, Valid: true, Conditions: []conditions.Condition{staticConds.NewGatewayClassResolvedRefs()}, + NginxProxy: &NginxProxy{ + Source: npGlobal, + Valid: true, + }, }, Gateway: &Gateway{ Source: gw1, @@ -872,6 +915,28 @@ func TestBuildGraph(t *testing.T) { }, Valid: true, Policies: []*Policy{processedGwPolicy}, + NginxProxy: &NginxProxy{ + Source: np1, + Valid: true, + }, + EffectiveNginxProxy: &EffectiveNginxProxy{ + Telemetry: &ngfAPIv1alpha2.Telemetry{ + Exporter: &ngfAPIv1alpha2.TelemetryExporter{ + Endpoint: helpers.GetPointer("1.2.3.4:123"), + Interval: helpers.GetPointer(ngfAPIv1alpha1.Duration("5s")), + BatchSize: helpers.GetPointer(int32(512)), + BatchCount: helpers.GetPointer(int32(4)), + }, + ServiceName: helpers.GetPointer("my-svc"), + SpanAttributes: []ngfAPIv1alpha1.SpanAttribute{ + {Key: "key", Value: "value"}, + }, + }, + Logging: &ngfAPIv1alpha2.NginxLogging{ + ErrorLevel: helpers.GetPointer(ngfAPIv1alpha2.NginxLogLevelError), + }, + }, + Conditions: []conditions.Condition{staticConds.NewGatewayResolvedRefs()}, }, IgnoredGateways: map[types.NamespacedName]*gatewayv1.Gateway{ {Namespace: testNs, Name: "gateway-2"}: gw2, @@ -906,9 +971,15 @@ func TestBuildGraph(t *testing.T) { BackendTLSPolicies: map[types.NamespacedName]*BackendTLSPolicy{ client.ObjectKeyFromObject(btp.Source): &btp, }, - NginxProxy: &NginxProxy{ - Source: proxy, - Valid: true, + ReferencedNginxProxies: map[types.NamespacedName]*NginxProxy{ + client.ObjectKeyFromObject(npGlobal): { + Source: npGlobal, + Valid: true, + }, + client.ObjectKeyFromObject(np1): { + Source: np1, + Valid: true, + }, }, NGFPolicies: map[PolicyKey]*Policy{ hrPolicyKey: processedRoutePolicy, @@ -941,9 +1012,10 @@ func TestBuildGraph(t *testing.T) { Spec: gatewayv1.GatewayClassSpec{ ControllerName: controllerName, ParametersRef: &gatewayv1.ParametersReference{ - Group: gatewayv1.Group("gateway.nginx.org"), - Kind: gatewayv1.Kind(kinds.NginxProxy), - Name: "nginx-proxy", + Group: gatewayv1.Group("gateway.nginx.org"), + Kind: gatewayv1.Kind(kinds.NginxProxy), + Name: "np-global", + Namespace: helpers.GetPointer(gatewayv1.Namespace(testNs)), }, }, } @@ -1122,27 +1194,15 @@ func TestIsReferenced(t *testing.T) { }, } - gcWithNginxProxy := &GatewayClass{ - Source: &gatewayv1.GatewayClass{ - Spec: gatewayv1.GatewayClassSpec{ - ParametersRef: &gatewayv1.ParametersReference{ - Group: ngfAPI.GroupName, - Kind: gatewayv1.Kind(kinds.NginxProxy), - Name: "nginx-proxy-in-gc", - }, - }, - }, - } - - npNotInGatewayClass := &ngfAPI.NginxProxy{ + npNotReferenced := &ngfAPIv1alpha2.NginxProxy{ ObjectMeta: metav1.ObjectMeta{ - Name: "nginx-proxy", + Name: "nginx-proxy-not-ref", }, } - npInGatewayClass := &ngfAPI.NginxProxy{ + npReferenced := &ngfAPIv1alpha2.NginxProxy{ ObjectMeta: metav1.ObjectMeta{ - Name: "nginx-proxy-in-gc", + Name: "nginx-proxy-ref", }, } @@ -1165,6 +1225,11 @@ func TestIsReferenced(t *testing.T) { CACert: []byte(caBlock), }, }, + ReferencedNginxProxies: map[types.NamespacedName]*NginxProxy{ + client.ObjectKeyFromObject(npReferenced): { + Source: npReferenced, + }, + }, } tests := []struct { @@ -1295,16 +1360,14 @@ func TestIsReferenced(t *testing.T) { // NginxProxy tests { - name: "NginxProxy is referenced in GatewayClass", - resource: npInGatewayClass, - gc: gcWithNginxProxy, + name: "NginxProxy is referenced", + resource: npReferenced, graph: graph, expected: true, }, { - name: "NginxProxy is not referenced in GatewayClass", - resource: npNotInGatewayClass, - gc: gcWithNginxProxy, + name: "NginxProxy is not referenced", + resource: npNotReferenced, graph: graph, expected: false, }, diff --git a/internal/mode/static/state/graph/grpcroute_test.go b/internal/mode/static/state/graph/grpcroute_test.go index 793458f837..19b14e4cc0 100644 --- a/internal/mode/static/state/graph/grpcroute_test.go +++ b/internal/mode/static/state/graph/grpcroute_test.go @@ -10,7 +10,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" v1 "sigs.k8s.io/gateway-api/apis/v1" - ngfAPI "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha1" + ngfAPIv1alpha1 "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha1" "github.com/nginxinc/nginx-gateway-fabric/internal/framework/conditions" "github.com/nginxinc/nginx-gateway-fabric/internal/framework/helpers" "github.com/nginxinc/nginx-gateway-fabric/internal/framework/kinds" @@ -88,7 +88,7 @@ func TestBuildGRPCRoutes(t *testing.T) { ExtensionRef: &v1.LocalObjectReference{ Name: "sf", Kind: kinds.SnippetsFilter, - Group: ngfAPI.GroupName, + Group: ngfAPIv1alpha1.GroupName, }, } @@ -110,15 +110,15 @@ func TestBuildGRPCRoutes(t *testing.T) { client.ObjectKeyFromObject(grWrongGateway): grWrongGateway, } - sf := &ngfAPI.SnippetsFilter{ + sf := &ngfAPIv1alpha1.SnippetsFilter{ ObjectMeta: metav1.ObjectMeta{ Namespace: "test", Name: "sf", }, - Spec: ngfAPI.SnippetsFilterSpec{ - Snippets: []ngfAPI.Snippet{ + Spec: ngfAPIv1alpha1.SnippetsFilterSpec{ + Snippets: []ngfAPIv1alpha1.Snippet{ { - Context: ngfAPI.NginxContextHTTP, + Context: ngfAPIv1alpha1.NginxContextHTTP, Value: "http snippet", }, }, @@ -158,8 +158,8 @@ func TestBuildGRPCRoutes(t *testing.T) { ResolvedExtensionRef: &ExtensionRefFilter{ SnippetsFilter: &SnippetsFilter{ Source: sf, - Snippets: map[ngfAPI.NginxContext]string{ - ngfAPI.NginxContextHTTP: "http snippet", + Snippets: map[ngfAPIv1alpha1.NginxContext]string{ + ngfAPIv1alpha1.NginxContextHTTP: "http snippet", }, Valid: true, Referenced: true, @@ -194,12 +194,8 @@ func TestBuildGRPCRoutes(t *testing.T) { validator := &validationfakes.FakeHTTPFieldsValidator{} - npCfg := &NginxProxy{ - Source: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - DisableHTTP2: false, - }, - }, + npCfg := &EffectiveNginxProxy{ + DisableHTTP2: helpers.GetPointer(false), } for _, test := range tests { @@ -211,8 +207,8 @@ func TestBuildGRPCRoutes(t *testing.T) { client.ObjectKeyFromObject(sf): { Source: sf, Valid: true, - Snippets: map[ngfAPI.NginxContext]string{ - ngfAPI.NginxContextHTTP: "http snippet", + Snippets: map[ngfAPIv1alpha1.NginxContext]string{ + ngfAPIv1alpha1.NginxContextHTTP: "http snippet", }, }, } @@ -351,7 +347,7 @@ func TestBuildGRPCRoute(t *testing.T) { grValidFilterRule := createGRPCMethodMatch("myService", "myMethod", "Exact") validSnippetsFilterRef := &v1.LocalObjectReference{ - Group: ngfAPI.GroupName, + Group: ngfAPIv1alpha1.GroupName, Kind: kinds.SnippetsFilter, Name: "sf", } @@ -409,7 +405,7 @@ func TestBuildGRPCRoute(t *testing.T) { { Type: v1.GRPCRouteFilterExtensionRef, ExtensionRef: &v1.LocalObjectReference{ - Group: ngfAPI.GroupName, + Group: ngfAPIv1alpha1.GroupName, Kind: kinds.SnippetsFilter, Name: "does-not-exist", }, @@ -428,7 +424,7 @@ func TestBuildGRPCRoute(t *testing.T) { { Type: v1.GRPCRouteFilterExtensionRef, ExtensionRef: &v1.LocalObjectReference{ - Group: ngfAPI.GroupName, + Group: ngfAPIv1alpha1.GroupName, Kind: kinds.SnippetsFilter, Name: "does-not-exist", }, diff --git a/internal/mode/static/state/graph/nginxproxy.go b/internal/mode/static/state/graph/nginxproxy.go index fa55f209c0..c2b65e4e9a 100644 --- a/internal/mode/static/state/graph/nginxproxy.go +++ b/internal/mode/static/state/graph/nginxproxy.go @@ -1,15 +1,18 @@ package graph import ( + "encoding/json" + "fmt" "slices" "k8s.io/apimachinery/pkg/types" k8svalidation "k8s.io/apimachinery/pkg/util/validation" "k8s.io/apimachinery/pkg/util/validation/field" + "sigs.k8s.io/controller-runtime/pkg/client" v1 "sigs.k8s.io/gateway-api/apis/v1" - ngfAPI "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha1" - "github.com/nginxinc/nginx-gateway-fabric/internal/framework/helpers" + ngfAPIv1alpha1 "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha1" + ngfAPIv1alpha2 "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha2" "github.com/nginxinc/nginx-gateway-fabric/internal/framework/kinds" "github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/state/validation" ) @@ -17,45 +20,169 @@ import ( // NginxProxy represents the NginxProxy resource. type NginxProxy struct { // Source is the source resource. - Source *ngfAPI.NginxProxy + Source *ngfAPIv1alpha2.NginxProxy // ErrMsgs contains the validation errors if they exist, to be included in the GatewayClass condition. ErrMsgs field.ErrorList // Valid shows whether the NginxProxy is valid. Valid bool } -// buildNginxProxy validates and returns the NginxProxy associated with the GatewayClass (if it exists). -func buildNginxProxy( - nps map[types.NamespacedName]*ngfAPI.NginxProxy, - gc *v1.GatewayClass, +// EffectiveNginxProxy holds the result of merging the NginxProxySpec on this resource with the NginxProxySpec on the +// GatewayClass resource. This is the effective set of config that should be applied to the Gateway. +type EffectiveNginxProxy ngfAPIv1alpha2.NginxProxySpec + +// buildEffectiveNginxProxy builds the effective NginxProxy for the Gateway by merging the GatewayClass and Gateway +// NginxProxy resources. Fields specified on the Gateway NginxProxy override those set on the GatewayClass NginxProxy. +func buildEffectiveNginxProxy(gatewayClassNp, gatewayNp *NginxProxy) *EffectiveNginxProxy { + gcNpValid, gwNpValid := nginxProxyValid(gatewayClassNp), nginxProxyValid(gatewayNp) + if !gcNpValid && !gwNpValid { + return nil + } + + if !gcNpValid { + enp := EffectiveNginxProxy(*gatewayNp.Source.Spec.DeepCopy()) + return &enp + } + + if !gwNpValid { + enp := EffectiveNginxProxy(*gatewayClassNp.Source.Spec.DeepCopy()) + return &enp + } + + global := EffectiveNginxProxy(*gatewayClassNp.Source.Spec.DeepCopy()) + local := EffectiveNginxProxy(*gatewayNp.Source.Spec.DeepCopy()) + + // by marshaling the local config and then unmarshaling on top of the global config, + // we ensure that any unset local values are set with the global values + localBytes, err := json.Marshal(local) + if err != nil { + panic( + fmt.Sprintf( + "could not marshal NginxProxy resource referenced by Gateway %s", + client.ObjectKeyFromObject(gatewayNp.Source), + ), + ) + } + + err = json.Unmarshal(localBytes, &global) + if err != nil { + panic( + fmt.Sprintf( + "could not unmarshal NginxProxy resource referenced by GatewayClass %s", + client.ObjectKeyFromObject(gatewayClassNp.Source), + ), + ) + } + + // this json trick doesn't work for unsetting slices, so we need to do that manually. + if local.Telemetry != nil { + if local.Telemetry.DisabledFeatures != nil && len(local.Telemetry.DisabledFeatures) == 0 { + global.Telemetry.DisabledFeatures = []ngfAPIv1alpha2.DisableTelemetryFeature{} + } + + if local.Telemetry.SpanAttributes != nil && len(local.Telemetry.SpanAttributes) == 0 { + global.Telemetry.SpanAttributes = []ngfAPIv1alpha1.SpanAttribute{} + } + } + + if local.RewriteClientIP != nil { + if local.RewriteClientIP.TrustedAddresses != nil && len(local.RewriteClientIP.TrustedAddresses) == 0 { + global.RewriteClientIP.TrustedAddresses = []ngfAPIv1alpha2.Address{} + } + } + + return &global +} + +func nginxProxyValid(np *NginxProxy) bool { + return np != nil && np.Source != nil && np.Valid +} + +func telemetryEnabledForNginxProxy(np *EffectiveNginxProxy) bool { + if np.Telemetry == nil || np.Telemetry.Exporter == nil || np.Telemetry.Exporter.Endpoint == nil { + return false + } + + if slices.Contains(np.Telemetry.DisabledFeatures, ngfAPIv1alpha2.DisableTracing) { + return false + } + + return true +} + +func processNginxProxies( + nps map[types.NamespacedName]*ngfAPIv1alpha2.NginxProxy, validator validation.GenericValidator, -) *NginxProxy { + gc *v1.GatewayClass, + winningGateway *v1.Gateway, +) map[types.NamespacedName]*NginxProxy { + referencedNginxProxies := make(map[types.NamespacedName]*NginxProxy) + if gcReferencesAnyNginxProxy(gc) { - npCfg := nps[types.NamespacedName{Name: gc.Spec.ParametersRef.Name}] - if npCfg != nil { - errs := validateNginxProxy(validator, npCfg) - - return &NginxProxy{ - Source: npCfg, - Valid: len(errs) == 0, - ErrMsgs: errs, + // we will ignore references without namespaces + // the gateway class status will contain an error message about the missing namespace + if gc.Spec.ParametersRef.Namespace != nil { + refNp := types.NamespacedName{ + Name: gc.Spec.ParametersRef.Name, + Namespace: string(*gc.Spec.ParametersRef.Namespace), + } + + if np, ok := nps[refNp]; ok { + referencedNginxProxies[refNp] = buildNginxProxy(np, validator) } } } - return nil + if gwReferencesAnyNginxProxy(winningGateway) { + refNp := types.NamespacedName{ + Name: winningGateway.Spec.Infrastructure.ParametersRef.Name, + Namespace: winningGateway.Namespace, + } + + if np, ok := nps[refNp]; ok { + referencedNginxProxies[refNp] = buildNginxProxy(np, validator) + } + } + + if len(referencedNginxProxies) == 0 { + return nil + } + + return referencedNginxProxies } -// isNginxProxyReferenced returns whether or not a specific NginxProxy is referenced in the GatewayClass. -func isNginxProxyReferenced(npNSName types.NamespacedName, gc *GatewayClass) bool { - return gc != nil && gcReferencesAnyNginxProxy(gc.Source) && gc.Source.Spec.ParametersRef.Name == npNSName.Name +// buildNginxProxy validates and returns the NginxProxy associated with the GatewayClass (if it exists). +func buildNginxProxy( + np *ngfAPIv1alpha2.NginxProxy, + validator validation.GenericValidator, +) *NginxProxy { + if np != nil { + errs := validateNginxProxy(validator, np) + + return &NginxProxy{ + Source: np, + Valid: len(errs) == 0, + ErrMsgs: errs, + } + } + + return nil } // gcReferencesNginxProxy returns whether a GatewayClass references any NginxProxy resource. func gcReferencesAnyNginxProxy(gc *v1.GatewayClass) bool { if gc != nil { ref := gc.Spec.ParametersRef - return ref != nil && ref.Group == ngfAPI.GroupName && ref.Kind == v1.Kind(kinds.NginxProxy) + return ref != nil && ref.Group == ngfAPIv1alpha2.GroupName && ref.Kind == kinds.NginxProxy + } + + return false +} + +func gwReferencesAnyNginxProxy(gw *v1.Gateway) bool { + if gw != nil && gw.Spec.Infrastructure != nil { + ref := gw.Spec.Infrastructure.ParametersRef + return ref != nil && ref.Group == ngfAPIv1alpha2.GroupName && ref.Kind == kinds.NginxProxy } return false @@ -64,7 +191,7 @@ func gcReferencesAnyNginxProxy(gc *v1.GatewayClass) bool { // validateNginxProxy performs re-validation on string values in the case of CRD validation failure. func validateNginxProxy( validator validation.GenericValidator, - npCfg *ngfAPI.NginxProxy, + npCfg *ngfAPIv1alpha2.NginxProxy, ) field.ErrorList { var allErrs field.ErrorList spec := field.NewPath("spec") @@ -85,8 +212,8 @@ func validateNginxProxy( exp := telemetry.Exporter expPath := telPath.Child("exporter") - if exp.Endpoint != "" { - if err := validator.ValidateEndpoint(exp.Endpoint); err != nil { + if exp.Endpoint != nil { + if err := validator.ValidateEndpoint(*exp.Endpoint); err != nil { allErrs = append(allErrs, field.Invalid(expPath.Child("endpoint"), exp.Endpoint, err.Error())) } } @@ -116,17 +243,15 @@ func validateNginxProxy( ipFamily := npCfg.Spec.IPFamily ipFamilyPath := spec.Child("ipFamily") switch *ipFamily { - case ngfAPI.Dual, ngfAPI.IPv4, ngfAPI.IPv6: + case ngfAPIv1alpha2.Dual, ngfAPIv1alpha2.IPv4, ngfAPIv1alpha2.IPv6: default: allErrs = append( allErrs, field.NotSupported( ipFamilyPath, ipFamily, - []string{string(ngfAPI.Dual), string(ngfAPI.IPv4), string(ngfAPI.IPv6)})) + []string{string(ngfAPIv1alpha2.Dual), string(ngfAPIv1alpha2.IPv4), string(ngfAPIv1alpha2.IPv6)})) } - } else { - npCfg.Spec.IPFamily = helpers.GetPointer[ngfAPI.IPFamilyType](ngfAPI.Dual) } allErrs = append(allErrs, validateLogging(npCfg)...) @@ -136,7 +261,7 @@ func validateNginxProxy( return allErrs } -func validateLogging(npCfg *ngfAPI.NginxProxy) field.ErrorList { +func validateLogging(npCfg *ngfAPIv1alpha2.NginxProxy) field.ErrorList { var allErrs field.ErrorList spec := field.NewPath("spec") @@ -148,14 +273,14 @@ func validateLogging(npCfg *ngfAPI.NginxProxy) field.ErrorList { errLevel := string(*logging.ErrorLevel) validLogLevels := []string{ - string(ngfAPI.NginxLogLevelDebug), - string(ngfAPI.NginxLogLevelInfo), - string(ngfAPI.NginxLogLevelNotice), - string(ngfAPI.NginxLogLevelWarn), - string(ngfAPI.NginxLogLevelError), - string(ngfAPI.NginxLogLevelCrit), - string(ngfAPI.NginxLogLevelAlert), - string(ngfAPI.NginxLogLevelEmerg), + string(ngfAPIv1alpha2.NginxLogLevelDebug), + string(ngfAPIv1alpha2.NginxLogLevelInfo), + string(ngfAPIv1alpha2.NginxLogLevelNotice), + string(ngfAPIv1alpha2.NginxLogLevelWarn), + string(ngfAPIv1alpha2.NginxLogLevelError), + string(ngfAPIv1alpha2.NginxLogLevelCrit), + string(ngfAPIv1alpha2.NginxLogLevelAlert), + string(ngfAPIv1alpha2.NginxLogLevelEmerg), } if !slices.Contains(validLogLevels, errLevel) { @@ -173,7 +298,7 @@ func validateLogging(npCfg *ngfAPI.NginxProxy) field.ErrorList { return allErrs } -func validateRewriteClientIP(npCfg *ngfAPI.NginxProxy) field.ErrorList { +func validateRewriteClientIP(npCfg *ngfAPIv1alpha2.NginxProxy) field.ErrorList { var allErrs field.ErrorList spec := field.NewPath("spec") @@ -192,14 +317,17 @@ func validateRewriteClientIP(npCfg *ngfAPI.NginxProxy) field.ErrorList { } switch mode { - case ngfAPI.RewriteClientIPModeProxyProtocol, ngfAPI.RewriteClientIPModeXForwardedFor: + case ngfAPIv1alpha2.RewriteClientIPModeProxyProtocol, ngfAPIv1alpha2.RewriteClientIPModeXForwardedFor: default: allErrs = append( allErrs, field.NotSupported( rewriteClientIPPath.Child("mode"), mode, - []string{string(ngfAPI.RewriteClientIPModeProxyProtocol), string(ngfAPI.RewriteClientIPModeXForwardedFor)}, + []string{ + string(ngfAPIv1alpha2.RewriteClientIPModeProxyProtocol), + string(ngfAPIv1alpha2.RewriteClientIPModeXForwardedFor), + }, ), ) } @@ -216,15 +344,15 @@ func validateRewriteClientIP(npCfg *ngfAPI.NginxProxy) field.ErrorList { valuePath := trustedAddressesPath.Child("value") switch addr.Type { - case ngfAPI.CIDRAddressType: + case ngfAPIv1alpha2.CIDRAddressType: if err := k8svalidation.IsValidCIDR(valuePath, addr.Value); err != nil { allErrs = append(allErrs, err...) } - case ngfAPI.IPAddressType: + case ngfAPIv1alpha2.IPAddressType: if err := k8svalidation.IsValidIP(valuePath, addr.Value); err != nil { allErrs = append(allErrs, err...) } - case ngfAPI.HostnameAddressType: + case ngfAPIv1alpha2.HostnameAddressType: if errs := k8svalidation.IsDNS1123Subdomain(addr.Value); len(errs) > 0 { for _, e := range errs { allErrs = append(allErrs, field.Invalid(valuePath, addr.Value, e)) @@ -236,9 +364,9 @@ func validateRewriteClientIP(npCfg *ngfAPI.NginxProxy) field.ErrorList { field.NotSupported(trustedAddressesPath.Child("type"), addr.Type, []string{ - string(ngfAPI.CIDRAddressType), - string(ngfAPI.IPAddressType), - string(ngfAPI.HostnameAddressType), + string(ngfAPIv1alpha2.CIDRAddressType), + string(ngfAPIv1alpha2.IPAddressType), + string(ngfAPIv1alpha2.HostnameAddressType), }, ), ) diff --git a/internal/mode/static/state/graph/nginxproxy_test.go b/internal/mode/static/state/graph/nginxproxy_test.go index 54447f3b31..e2cf12d952 100644 --- a/internal/mode/static/state/graph/nginxproxy_test.go +++ b/internal/mode/static/state/graph/nginxproxy_test.go @@ -7,78 +7,281 @@ 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" v1 "sigs.k8s.io/gateway-api/apis/v1" - ngfAPI "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha1" + ngfAPIv1alpha1 "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha1" + ngfAPIv1alpha2 "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha2" "github.com/nginxinc/nginx-gateway-fabric/internal/framework/helpers" "github.com/nginxinc/nginx-gateway-fabric/internal/framework/kinds" + "github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/state/validation" "github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/state/validation/validationfakes" ) -func TestGetNginxProxy(t *testing.T) { +func createValidValidator() *validationfakes.FakeGenericValidator { + v := &validationfakes.FakeGenericValidator{} + v.ValidateEscapedStringNoVarExpansionReturns(nil) + v.ValidateEndpointReturns(nil) + v.ValidateServiceNameReturns(nil) + v.ValidateNginxDurationReturns(nil) + + return v +} + +func createInvalidValidator() *validationfakes.FakeGenericValidator { + v := &validationfakes.FakeGenericValidator{} + v.ValidateEscapedStringNoVarExpansionReturns(errors.New("error")) + v.ValidateEndpointReturns(errors.New("error")) + v.ValidateServiceNameReturns(errors.New("error")) + v.ValidateNginxDurationReturns(errors.New("error")) + + return v +} + +func TestBuildEffectiveNginxProxy(t *testing.T) { t.Parallel() + + newTestNginxProxy := func( + ipFam ngfAPIv1alpha2.IPFamilyType, + disableFeats []ngfAPIv1alpha2.DisableTelemetryFeature, + interval ngfAPIv1alpha1.Duration, + batchSize int32, + batchCount int32, + endpoint string, + serviceName string, + spanAttr ngfAPIv1alpha1.SpanAttribute, + mode ngfAPIv1alpha2.RewriteClientIPModeType, + trustedAddr []ngfAPIv1alpha2.Address, + logLevel ngfAPIv1alpha2.NginxErrorLogLevel, + setIP bool, + disableHTTP bool, + ) *ngfAPIv1alpha2.NginxProxy { + return &ngfAPIv1alpha2.NginxProxy{ + Spec: ngfAPIv1alpha2.NginxProxySpec{ + IPFamily: &ipFam, + Telemetry: &ngfAPIv1alpha2.Telemetry{ + DisabledFeatures: disableFeats, + Exporter: &ngfAPIv1alpha2.TelemetryExporter{ + Interval: &interval, + BatchSize: &batchSize, + BatchCount: &batchCount, + Endpoint: &endpoint, + }, + ServiceName: &serviceName, + SpanAttributes: []ngfAPIv1alpha1.SpanAttribute{spanAttr}, + }, + RewriteClientIP: &ngfAPIv1alpha2.RewriteClientIP{ + Mode: &mode, + SetIPRecursively: &setIP, + TrustedAddresses: trustedAddr, + }, + Logging: &ngfAPIv1alpha2.NginxLogging{ + ErrorLevel: &logLevel, + }, + DisableHTTP2: &disableHTTP, + }, + } + } + + getNginxProxy := func() *ngfAPIv1alpha2.NginxProxy { + return newTestNginxProxy( + ngfAPIv1alpha2.Dual, + []ngfAPIv1alpha2.DisableTelemetryFeature{ngfAPIv1alpha2.DisableTracing}, + "10s", + 10, + 5, + "endpoint:1234", + "my-service", + ngfAPIv1alpha1.SpanAttribute{Key: "key", Value: "val"}, + ngfAPIv1alpha2.RewriteClientIPModeXForwardedFor, + []ngfAPIv1alpha2.Address{{Type: ngfAPIv1alpha2.IPAddressType, Value: "10.0.0.1"}}, + ngfAPIv1alpha2.NginxLogLevelAlert, + true, + false, + ) + } + + getNginxProxyAllFieldsSetDifferently := func() *ngfAPIv1alpha2.NginxProxy { + return newTestNginxProxy( + ngfAPIv1alpha2.IPv6, + []ngfAPIv1alpha2.DisableTelemetryFeature{}, + "5s", + 8, + 2, + "diff-endpoint:1234", + "diff-service", + ngfAPIv1alpha1.SpanAttribute{Key: "diff-key", Value: "diff-val"}, + ngfAPIv1alpha2.RewriteClientIPModeXForwardedFor, + []ngfAPIv1alpha2.Address{{Type: ngfAPIv1alpha2.CIDRAddressType, Value: "10.0.0.1/24"}}, + ngfAPIv1alpha2.NginxLogLevelError, + false, + true, + ) + } + + getExpSpec := func() *EffectiveNginxProxy { + enp := EffectiveNginxProxy(getNginxProxy().Spec) + return &enp + } + + getModifiedExpSpec := func(mod func(*ngfAPIv1alpha2.NginxProxy) *ngfAPIv1alpha2.NginxProxy) *EffectiveNginxProxy { + enp := EffectiveNginxProxy(mod(getNginxProxy()).Spec) + return &enp + } + tests := []struct { - nps map[types.NamespacedName]*ngfAPI.NginxProxy - gc *v1.GatewayClass - expNP *NginxProxy - name string + gcNp *NginxProxy + gwNp *NginxProxy + exp *EffectiveNginxProxy + name string }{ { - nps: map[types.NamespacedName]*ngfAPI.NginxProxy{ - {Name: "np1"}: {}, - }, - gc: nil, - expNP: nil, - name: "nil gatewayclass", + name: "both gateway class and gateway nginx proxies are nil", + gcNp: nil, + gwNp: nil, + exp: nil, }, { - nps: map[types.NamespacedName]*ngfAPI.NginxProxy{}, - gc: &v1.GatewayClass{ - Spec: v1.GatewayClassSpec{ - ParametersRef: &v1.ParametersReference{ - Group: ngfAPI.GroupName, - Kind: v1.Kind(kinds.NginxProxy), - Name: "np1", + name: "nil gateway class nginx proxy", + gcNp: nil, + gwNp: &NginxProxy{Valid: true, Source: getNginxProxy()}, + exp: getExpSpec(), + }, + { + name: "nil gateway class nginx proxy; invalid gateway nginx proxy", + gcNp: nil, + gwNp: &NginxProxy{Valid: false, Source: getNginxProxy()}, + exp: nil, + }, + { + name: "nil gateway class nginx proxy; nil gateway nginx proxy source", + gcNp: nil, + gwNp: &NginxProxy{Valid: true, Source: nil}, + exp: nil, + }, + { + name: "invalid gateway class nginx proxy", + gcNp: &NginxProxy{Valid: false}, + gwNp: &NginxProxy{Valid: true, Source: getNginxProxy()}, + exp: getExpSpec(), + }, + { + name: "nil gateway class nginx proxy source", + gcNp: &NginxProxy{Valid: true, Source: nil}, + gwNp: &NginxProxy{Valid: true, Source: getNginxProxy()}, + exp: getExpSpec(), + }, + { + name: "nil gateway nginx proxy", + gcNp: &NginxProxy{Valid: true, Source: getNginxProxy()}, + gwNp: nil, + exp: getExpSpec(), + }, + { + name: "invalid gateway nginx proxy", + gcNp: &NginxProxy{Valid: true, Source: getNginxProxy()}, + gwNp: &NginxProxy{Valid: false}, + exp: getExpSpec(), + }, + { + name: "nil gateway nginx proxy source", + gcNp: &NginxProxy{Valid: true, Source: getNginxProxy()}, + gwNp: &NginxProxy{Valid: true, Source: nil}, + exp: getExpSpec(), + }, + { + name: "both have all fields set; gateway values should win", + gcNp: &NginxProxy{Valid: true, Source: getNginxProxy()}, + gwNp: &NginxProxy{Valid: true, Source: getNginxProxyAllFieldsSetDifferently()}, + exp: getModifiedExpSpec(func(_ *ngfAPIv1alpha2.NginxProxy) *ngfAPIv1alpha2.NginxProxy { + return getNginxProxyAllFieldsSetDifferently() + }), + }, + { + name: "gateway nginx proxy overrides nginx error log level", + gcNp: &NginxProxy{Valid: true, Source: getNginxProxy()}, + gwNp: &NginxProxy{ + Valid: true, + Source: &ngfAPIv1alpha2.NginxProxy{ + Spec: ngfAPIv1alpha2.NginxProxySpec{ + Logging: &ngfAPIv1alpha2.NginxLogging{ + ErrorLevel: helpers.GetPointer(ngfAPIv1alpha2.NginxLogLevelDebug), + }, }, }, }, - expNP: nil, - name: "no nginxproxy resources", + exp: getModifiedExpSpec(func(np *ngfAPIv1alpha2.NginxProxy) *ngfAPIv1alpha2.NginxProxy { + np.Spec.Logging.ErrorLevel = helpers.GetPointer(ngfAPIv1alpha2.NginxLogLevelDebug) + return np + }), }, { - nps: map[types.NamespacedName]*ngfAPI.NginxProxy{ - {Name: "np1"}: { - ObjectMeta: metav1.ObjectMeta{ - Name: "np1", - }, - }, - {Name: "np2"}: { - ObjectMeta: metav1.ObjectMeta{ - Name: "np2", + name: "gateway nginx proxy overrides select telemetry values", + gcNp: &NginxProxy{Valid: true, Source: getNginxProxy()}, + gwNp: &NginxProxy{ + Valid: true, + Source: &ngfAPIv1alpha2.NginxProxy{ + Spec: ngfAPIv1alpha2.NginxProxySpec{ + Telemetry: &ngfAPIv1alpha2.Telemetry{ + ServiceName: helpers.GetPointer("new-service-name"), + Exporter: &ngfAPIv1alpha2.TelemetryExporter{ + BatchSize: helpers.GetPointer[int32](20), + Endpoint: helpers.GetPointer("new-endpoint"), + }, + }, }, }, }, - gc: &v1.GatewayClass{ - Spec: v1.GatewayClassSpec{ - ParametersRef: &v1.ParametersReference{ - Group: ngfAPI.GroupName, - Kind: v1.Kind(kinds.NginxProxy), - Name: "np2", + exp: getModifiedExpSpec(func(np *ngfAPIv1alpha2.NginxProxy) *ngfAPIv1alpha2.NginxProxy { + np.Spec.Telemetry.ServiceName = helpers.GetPointer("new-service-name") + np.Spec.Telemetry.Exporter.Endpoint = helpers.GetPointer("new-endpoint") + np.Spec.Telemetry.Exporter.BatchSize = helpers.GetPointer[int32](20) + return np + }), + }, + { + name: "gateway nginx proxy overrides select rewrite client IP values", + gcNp: &NginxProxy{Valid: true, Source: getNginxProxy()}, + gwNp: &NginxProxy{ + Valid: true, + Source: &ngfAPIv1alpha2.NginxProxy{ + Spec: ngfAPIv1alpha2.NginxProxySpec{ + RewriteClientIP: &ngfAPIv1alpha2.RewriteClientIP{ + Mode: helpers.GetPointer(ngfAPIv1alpha2.RewriteClientIPModeProxyProtocol), + SetIPRecursively: helpers.GetPointer(false), + }, }, }, }, - expNP: &NginxProxy{ - Source: &ngfAPI.NginxProxy{ - ObjectMeta: metav1.ObjectMeta{ - Name: "np2", - }, - Spec: ngfAPI.NginxProxySpec{ - IPFamily: helpers.GetPointer(ngfAPI.Dual), + exp: getModifiedExpSpec(func(np *ngfAPIv1alpha2.NginxProxy) *ngfAPIv1alpha2.NginxProxy { + np.Spec.RewriteClientIP.Mode = helpers.GetPointer(ngfAPIv1alpha2.RewriteClientIPModeProxyProtocol) + np.Spec.RewriteClientIP.SetIPRecursively = helpers.GetPointer(false) + return np + }), + }, + { + name: "gateway nginx proxy unsets slices values", + gcNp: &NginxProxy{Valid: true, Source: getNginxProxy()}, + gwNp: &NginxProxy{ + Valid: true, + Source: &ngfAPIv1alpha2.NginxProxy{ + Spec: ngfAPIv1alpha2.NginxProxySpec{ + Telemetry: &ngfAPIv1alpha2.Telemetry{ + DisabledFeatures: []ngfAPIv1alpha2.DisableTelemetryFeature{}, + SpanAttributes: []ngfAPIv1alpha1.SpanAttribute{}, + }, + RewriteClientIP: &ngfAPIv1alpha2.RewriteClientIP{ + TrustedAddresses: []ngfAPIv1alpha2.Address{}, + }, }, }, - Valid: true, }, - name: "returns correct resource", + exp: getModifiedExpSpec(func(np *ngfAPIv1alpha2.NginxProxy) *ngfAPIv1alpha2.NginxProxy { + np.Spec.RewriteClientIP.TrustedAddresses = []ngfAPIv1alpha2.Address{} + np.Spec.Telemetry.DisabledFeatures = []ngfAPIv1alpha2.DisableTelemetryFeature{} + np.Spec.Telemetry.SpanAttributes = []ngfAPIv1alpha1.SpanAttribute{} + return np + }), }, } @@ -87,64 +290,71 @@ func TestGetNginxProxy(t *testing.T) { t.Parallel() g := NewWithT(t) - g.Expect(buildNginxProxy(test.nps, test.gc, &validationfakes.FakeGenericValidator{})).To(Equal(test.expNP)) + enp := buildEffectiveNginxProxy(test.gcNp, test.gwNp) + g.Expect(enp).To(Equal(test.exp)) }) } } -func TestIsNginxProxyReferenced(t *testing.T) { +func TestTelemetryEnabledForNginxProxy(t *testing.T) { t.Parallel() + tests := []struct { - gc *GatewayClass - npName types.NamespacedName - name string - expRes bool + ep *EffectiveNginxProxy + name string + enabled bool }{ { - gc: &GatewayClass{ - Source: &v1.GatewayClass{ - Spec: v1.GatewayClassSpec{ - ParametersRef: &v1.ParametersReference{ - Group: ngfAPI.GroupName, - Kind: v1.Kind(kinds.NginxProxy), - Name: "nginx-proxy", - }, - }, + name: "telemetry struct is nil", + ep: &EffectiveNginxProxy{ + Telemetry: nil, + }, + enabled: false, + }, + { + name: "telemetry exporter is nil", + ep: &EffectiveNginxProxy{ + Telemetry: &ngfAPIv1alpha2.Telemetry{ + Exporter: nil, }, }, - npName: types.NamespacedName{}, - expRes: false, - name: "nil nginxproxy", + enabled: false, }, { - gc: nil, - npName: types.NamespacedName{Name: "nginx-proxy"}, - expRes: false, - name: "nil gatewayclass", + name: "tracing is disabled", + ep: &EffectiveNginxProxy{ + Telemetry: &ngfAPIv1alpha2.Telemetry{ + DisabledFeatures: []ngfAPIv1alpha2.DisableTelemetryFeature{ + ngfAPIv1alpha2.DisableTracing, + }, + Exporter: &ngfAPIv1alpha2.TelemetryExporter{ + Endpoint: helpers.GetPointer("new-endpoint"), + }, + }, + }, + enabled: false, }, { - gc: &GatewayClass{ - Source: nil, + name: "exporter endpoint is nil", + ep: &EffectiveNginxProxy{ + Telemetry: &ngfAPIv1alpha2.Telemetry{ + Exporter: &ngfAPIv1alpha2.TelemetryExporter{ + Endpoint: nil, + }, + }, }, - npName: types.NamespacedName{Name: "nginx-proxy"}, - expRes: false, - name: "nil gatewayclass source", + enabled: false, }, { - gc: &GatewayClass{ - Source: &v1.GatewayClass{ - Spec: v1.GatewayClassSpec{ - ParametersRef: &v1.ParametersReference{ - Group: ngfAPI.GroupName, - Kind: v1.Kind(kinds.NginxProxy), - Name: "nginx-proxy", - }, + name: "normal case; enabled", + ep: &EffectiveNginxProxy{ + Telemetry: &ngfAPIv1alpha2.Telemetry{ + Exporter: &ngfAPIv1alpha2.TelemetryExporter{ + Endpoint: helpers.GetPointer("new-endpoint"), }, }, }, - npName: types.NamespacedName{Name: "nginx-proxy"}, - expRes: true, - name: "references the NginxProxy", + enabled: true, }, } @@ -153,7 +363,162 @@ func TestIsNginxProxyReferenced(t *testing.T) { t.Parallel() g := NewWithT(t) - g.Expect(isNginxProxyReferenced(test.npName, test.gc)).To(Equal(test.expRes)) + enabled := telemetryEnabledForNginxProxy(test.ep) + g.Expect(enabled).To(Equal(test.enabled)) + }) + } +} + +func TestProcessNginxProxies(t *testing.T) { + t.Parallel() + + gatewayClassNpName := types.NamespacedName{Namespace: "gc-ns", Name: "gc-np"} + gatewayNpName := types.NamespacedName{Namespace: "gw-ns", Name: "gw-np"} + unreferencedNpName := types.NamespacedName{Namespace: "test", Name: "unref"} + + getTestNp := func(nsname types.NamespacedName) *ngfAPIv1alpha2.NginxProxy { + return &ngfAPIv1alpha2.NginxProxy{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: nsname.Namespace, + Name: nsname.Name, + }, + Spec: ngfAPIv1alpha2.NginxProxySpec{ + Telemetry: &ngfAPIv1alpha2.Telemetry{ + ServiceName: helpers.GetPointer("service-name"), + }, + }, + } + } + + gateway := &v1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "gw-ns", + }, + Spec: v1.GatewaySpec{ + Infrastructure: &v1.GatewayInfrastructure{ + ParametersRef: &v1.LocalParametersReference{ + Group: ngfAPIv1alpha2.GroupName, + Kind: kinds.NginxProxy, + Name: gatewayNpName.Name, + }, + }, + }, + } + + gatewayClass := &v1.GatewayClass{ + Spec: v1.GatewayClassSpec{ + ParametersRef: &v1.ParametersReference{ + Group: ngfAPIv1alpha2.GroupName, + Kind: kinds.NginxProxy, + Name: gatewayClassNpName.Name, + Namespace: helpers.GetPointer[v1.Namespace]("gc-ns"), + }, + }, + } + + gatewayClassRefMissingNs := &v1.GatewayClass{ + Spec: v1.GatewayClassSpec{ + ParametersRef: &v1.ParametersReference{ + Group: ngfAPIv1alpha2.GroupName, + Kind: kinds.NginxProxy, + Name: gatewayClassNpName.Name, + }, + }, + } + + getNpMap := func() map[types.NamespacedName]*ngfAPIv1alpha2.NginxProxy { + return map[types.NamespacedName]*ngfAPIv1alpha2.NginxProxy{ + gatewayClassNpName: getTestNp(gatewayClassNpName), + gatewayNpName: getTestNp(gatewayNpName), + unreferencedNpName: getTestNp(unreferencedNpName), + } + } + + getExpResult := func(valid bool) map[types.NamespacedName]*NginxProxy { + var errMsgs field.ErrorList + if !valid { + errMsgs = field.ErrorList{ + field.Invalid(field.NewPath("spec.telemetry.serviceName"), "service-name", "error"), + } + } + + return map[types.NamespacedName]*NginxProxy{ + gatewayNpName: { + Valid: valid, + ErrMsgs: errMsgs, + Source: getTestNp(gatewayNpName), + }, + gatewayClassNpName: { + Valid: valid, + ErrMsgs: errMsgs, + Source: getTestNp(gatewayClassNpName), + }, + } + } + + tests := []struct { + validator validation.GenericValidator + nps map[types.NamespacedName]*ngfAPIv1alpha2.NginxProxy + gc *v1.GatewayClass + gw *v1.Gateway + expResult map[types.NamespacedName]*NginxProxy + name string + }{ + { + name: "no nginx proxies", + nps: nil, + gc: gatewayClass, + gw: gateway, + validator: createValidValidator(), + expResult: nil, + }, + { + name: "gateway class param ref is missing namespace", + nps: map[types.NamespacedName]*ngfAPIv1alpha2.NginxProxy{ + gatewayClassNpName: getTestNp(gatewayClassNpName), + gatewayNpName: getTestNp(gatewayNpName), + }, + gc: gatewayClassRefMissingNs, + gw: gateway, + validator: createValidValidator(), + expResult: map[types.NamespacedName]*NginxProxy{ + gatewayNpName: { + Valid: true, + Source: getTestNp(gatewayNpName), + }, + }, + }, + { + name: "normal case; both nginx proxies are valid", + nps: getNpMap(), + gc: gatewayClass, + gw: gateway, + validator: createValidValidator(), + expResult: getExpResult(true), + }, + { + name: "normal case; both nginx proxies are invalid", + nps: getNpMap(), + gc: gatewayClass, + gw: gateway, + validator: createInvalidValidator(), + expResult: getExpResult(false), + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + t.Parallel() + g := NewWithT(t) + + result := processNginxProxies( + test.nps, + test.validator, + test.gc, + test.gw, + ) + + g.Expect(helpers.Diff(test.expResult, result)).To(BeEmpty()) }) } } @@ -194,7 +559,7 @@ func TestGCReferencesAnyNginxProxy(t *testing.T) { gc: &v1.GatewayClass{ Spec: v1.GatewayClassSpec{ ParametersRef: &v1.ParametersReference{ - Group: ngfAPI.GroupName, + Group: ngfAPIv1alpha2.GroupName, Kind: v1.Kind("WrongKind"), Name: "wrong-kind", }, @@ -207,7 +572,7 @@ func TestGCReferencesAnyNginxProxy(t *testing.T) { gc: &v1.GatewayClass{ Spec: v1.GatewayClassSpec{ ParametersRef: &v1.ParametersReference{ - Group: ngfAPI.GroupName, + Group: ngfAPIv1alpha2.GroupName, Kind: v1.Kind(kinds.NginxProxy), Name: "nginx-proxy", }, @@ -228,30 +593,95 @@ func TestGCReferencesAnyNginxProxy(t *testing.T) { } } -func createValidValidator() *validationfakes.FakeGenericValidator { - v := &validationfakes.FakeGenericValidator{} - v.ValidateEscapedStringNoVarExpansionReturns(nil) - v.ValidateEndpointReturns(nil) - v.ValidateServiceNameReturns(nil) - v.ValidateNginxDurationReturns(nil) - - return v -} +func TestGWReferencesAnyNginxProxy(t *testing.T) { + t.Parallel() + tests := []struct { + gw *v1.Gateway + name string + expRes bool + }{ + { + gw: nil, + expRes: false, + name: "nil gateway", + }, + { + gw: &v1.Gateway{ + Spec: v1.GatewaySpec{}, + }, + expRes: false, + name: "nil infrastructure", + }, + { + gw: &v1.Gateway{ + Spec: v1.GatewaySpec{ + Infrastructure: &v1.GatewayInfrastructure{}, + }, + }, + expRes: false, + name: "nil parametersRef", + }, + { + gw: &v1.Gateway{ + Spec: v1.GatewaySpec{ + Infrastructure: &v1.GatewayInfrastructure{ + ParametersRef: &v1.LocalParametersReference{ + Group: v1.Group("wrong-group"), + Kind: v1.Kind(kinds.NginxProxy), + Name: "wrong-group", + }, + }, + }, + }, + expRes: false, + name: "wrong group name", + }, + { + gw: &v1.Gateway{ + Spec: v1.GatewaySpec{ + Infrastructure: &v1.GatewayInfrastructure{ + ParametersRef: &v1.LocalParametersReference{ + Group: v1.Group(ngfAPIv1alpha2.GroupName), + Kind: v1.Kind("wrong-kind"), + Name: "wrong-kind", + }, + }, + }, + }, + expRes: false, + name: "wrong kind", + }, + { + gw: &v1.Gateway{ + Spec: v1.GatewaySpec{ + Infrastructure: &v1.GatewayInfrastructure{ + ParametersRef: &v1.LocalParametersReference{ + Group: v1.Group(ngfAPIv1alpha2.GroupName), + Kind: v1.Kind(kinds.NginxProxy), + Name: "normal", + }, + }, + }, + }, + expRes: true, + name: "references an NginxProxy", + }, + } -func createInvalidValidator() *validationfakes.FakeGenericValidator { - v := &validationfakes.FakeGenericValidator{} - v.ValidateEscapedStringNoVarExpansionReturns(errors.New("error")) - v.ValidateEndpointReturns(errors.New("error")) - v.ValidateServiceNameReturns(errors.New("error")) - v.ValidateNginxDurationReturns(errors.New("error")) + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + t.Parallel() + g := NewWithT(t) - return v + g.Expect(gwReferencesAnyNginxProxy(test.gw)).To(Equal(test.expRes)) + }) + } } func TestValidateNginxProxy(t *testing.T) { t.Parallel() tests := []struct { - np *ngfAPI.NginxProxy + np *ngfAPIv1alpha2.NginxProxy validator *validationfakes.FakeGenericValidator name string expErrSubstring string @@ -260,36 +690,36 @@ func TestValidateNginxProxy(t *testing.T) { { name: "valid nginxproxy", validator: createValidValidator(), - np: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - Telemetry: &ngfAPI.Telemetry{ + np: &ngfAPIv1alpha2.NginxProxy{ + Spec: ngfAPIv1alpha2.NginxProxySpec{ + Telemetry: &ngfAPIv1alpha2.Telemetry{ ServiceName: helpers.GetPointer("my-svc"), - Exporter: &ngfAPI.TelemetryExporter{ - Interval: helpers.GetPointer[ngfAPI.Duration]("5ms"), - Endpoint: "my-endpoint", + Exporter: &ngfAPIv1alpha2.TelemetryExporter{ + Interval: helpers.GetPointer[ngfAPIv1alpha1.Duration]("5ms"), + Endpoint: helpers.GetPointer("my-endpoint"), }, - SpanAttributes: []ngfAPI.SpanAttribute{ + SpanAttributes: []ngfAPIv1alpha1.SpanAttribute{ {Key: "key", Value: "value"}, }, }, - IPFamily: helpers.GetPointer[ngfAPI.IPFamilyType](ngfAPI.Dual), - RewriteClientIP: &ngfAPI.RewriteClientIP{ + IPFamily: helpers.GetPointer[ngfAPIv1alpha2.IPFamilyType](ngfAPIv1alpha2.Dual), + RewriteClientIP: &ngfAPIv1alpha2.RewriteClientIP{ SetIPRecursively: helpers.GetPointer(true), - TrustedAddresses: []ngfAPI.Address{ + TrustedAddresses: []ngfAPIv1alpha2.Address{ { - Type: ngfAPI.CIDRAddressType, + Type: ngfAPIv1alpha2.CIDRAddressType, Value: "2001:db8:a0b:12f0::1/32", }, { - Type: ngfAPI.IPAddressType, + Type: ngfAPIv1alpha2.IPAddressType, Value: "1.1.1.1", }, { - Type: ngfAPI.HostnameAddressType, + Type: ngfAPIv1alpha2.HostnameAddressType, Value: "example.com", }, }, - Mode: helpers.GetPointer(ngfAPI.RewriteClientIPModeProxyProtocol), + Mode: helpers.GetPointer(ngfAPIv1alpha2.RewriteClientIPModeProxyProtocol), }, }, }, @@ -298,9 +728,9 @@ func TestValidateNginxProxy(t *testing.T) { { name: "invalid serviceName", validator: createInvalidValidator(), - np: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - Telemetry: &ngfAPI.Telemetry{ + np: &ngfAPIv1alpha2.NginxProxy{ + Spec: ngfAPIv1alpha2.NginxProxySpec{ + Telemetry: &ngfAPIv1alpha2.Telemetry{ ServiceName: helpers.GetPointer("my-svc"), // any value is invalid by the validator }, }, @@ -311,11 +741,11 @@ func TestValidateNginxProxy(t *testing.T) { { name: "invalid endpoint", validator: createInvalidValidator(), - np: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - Telemetry: &ngfAPI.Telemetry{ - Exporter: &ngfAPI.TelemetryExporter{ - Endpoint: "my-endpoint", // any value is invalid by the validator + np: &ngfAPIv1alpha2.NginxProxy{ + Spec: ngfAPIv1alpha2.NginxProxySpec{ + Telemetry: &ngfAPIv1alpha2.Telemetry{ + Exporter: &ngfAPIv1alpha2.TelemetryExporter{ + Endpoint: helpers.GetPointer("my-endpoint"), // any value is invalid by the validator }, }, }, @@ -326,11 +756,11 @@ func TestValidateNginxProxy(t *testing.T) { { name: "invalid interval", validator: createInvalidValidator(), - np: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - Telemetry: &ngfAPI.Telemetry{ - Exporter: &ngfAPI.TelemetryExporter{ - Interval: helpers.GetPointer[ngfAPI.Duration]( + np: &ngfAPIv1alpha2.NginxProxy{ + Spec: ngfAPIv1alpha2.NginxProxySpec{ + Telemetry: &ngfAPIv1alpha2.Telemetry{ + Exporter: &ngfAPIv1alpha2.TelemetryExporter{ + Interval: helpers.GetPointer[ngfAPIv1alpha1.Duration]( "my-interval", ), // any value is invalid by the validator }, @@ -343,10 +773,10 @@ func TestValidateNginxProxy(t *testing.T) { { name: "invalid spanAttributes", validator: createInvalidValidator(), - np: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - Telemetry: &ngfAPI.Telemetry{ - SpanAttributes: []ngfAPI.SpanAttribute{ + np: &ngfAPIv1alpha2.NginxProxy{ + Spec: ngfAPIv1alpha2.NginxProxySpec{ + Telemetry: &ngfAPIv1alpha2.Telemetry{ + SpanAttributes: []ngfAPIv1alpha1.SpanAttribute{ {Key: "my-key", Value: "my-value"}, // any value is invalid by the validator }, }, @@ -358,10 +788,10 @@ func TestValidateNginxProxy(t *testing.T) { { name: "invalid ipFamily type", validator: createInvalidValidator(), - np: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - Telemetry: &ngfAPI.Telemetry{}, - IPFamily: helpers.GetPointer[ngfAPI.IPFamilyType]("invalid"), + np: &ngfAPIv1alpha2.NginxProxy{ + Spec: ngfAPIv1alpha2.NginxProxySpec{ + Telemetry: &ngfAPIv1alpha2.Telemetry{}, + IPFamily: helpers.GetPointer[ngfAPIv1alpha2.IPFamilyType]("invalid"), }, }, expErrSubstring: "spec.ipFamily", @@ -386,7 +816,7 @@ func TestValidateNginxProxy(t *testing.T) { func TestValidateRewriteClientIP(t *testing.T) { t.Parallel() tests := []struct { - np *ngfAPI.NginxProxy + np *ngfAPIv1alpha2.NginxProxy validator *validationfakes.FakeGenericValidator name string errorString string @@ -395,33 +825,33 @@ func TestValidateRewriteClientIP(t *testing.T) { { name: "valid rewriteClientIP", validator: createValidValidator(), - np: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - RewriteClientIP: &ngfAPI.RewriteClientIP{ + np: &ngfAPIv1alpha2.NginxProxy{ + Spec: ngfAPIv1alpha2.NginxProxySpec{ + RewriteClientIP: &ngfAPIv1alpha2.RewriteClientIP{ SetIPRecursively: helpers.GetPointer(true), - TrustedAddresses: []ngfAPI.Address{ + TrustedAddresses: []ngfAPIv1alpha2.Address{ { - Type: ngfAPI.CIDRAddressType, + Type: ngfAPIv1alpha2.CIDRAddressType, Value: "2001:db8:a0b:12f0::1/32", }, { - Type: ngfAPI.CIDRAddressType, + Type: ngfAPIv1alpha2.CIDRAddressType, Value: "10.56.32.11/32", }, { - Type: ngfAPI.IPAddressType, + Type: ngfAPIv1alpha2.IPAddressType, Value: "1.1.1.1", }, { - Type: ngfAPI.IPAddressType, + Type: ngfAPIv1alpha2.IPAddressType, Value: "2001:db8:a0b:12f0::1", }, { - Type: ngfAPI.HostnameAddressType, + Type: ngfAPIv1alpha2.HostnameAddressType, Value: "example.com", }, }, - Mode: helpers.GetPointer(ngfAPI.RewriteClientIPModeProxyProtocol), + Mode: helpers.GetPointer(ngfAPIv1alpha2.RewriteClientIPModeProxyProtocol), }, }, }, @@ -430,21 +860,21 @@ func TestValidateRewriteClientIP(t *testing.T) { { name: "invalid CIDR in trustedAddresses", validator: createInvalidValidator(), - np: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - RewriteClientIP: &ngfAPI.RewriteClientIP{ + np: &ngfAPIv1alpha2.NginxProxy{ + Spec: ngfAPIv1alpha2.NginxProxySpec{ + RewriteClientIP: &ngfAPIv1alpha2.RewriteClientIP{ SetIPRecursively: helpers.GetPointer(true), - TrustedAddresses: []ngfAPI.Address{ + TrustedAddresses: []ngfAPIv1alpha2.Address{ { - Type: ngfAPI.CIDRAddressType, + Type: ngfAPIv1alpha2.CIDRAddressType, Value: "2001:db8::/129", }, { - Type: ngfAPI.CIDRAddressType, + Type: ngfAPIv1alpha2.CIDRAddressType, Value: "10.0.0.1/32", }, }, - Mode: helpers.GetPointer(ngfAPI.RewriteClientIPModeProxyProtocol), + Mode: helpers.GetPointer(ngfAPIv1alpha2.RewriteClientIPModeProxyProtocol), }, }, }, @@ -455,21 +885,21 @@ func TestValidateRewriteClientIP(t *testing.T) { { name: "invalid IP address in trustedAddresses", validator: createInvalidValidator(), - np: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - RewriteClientIP: &ngfAPI.RewriteClientIP{ + np: &ngfAPIv1alpha2.NginxProxy{ + Spec: ngfAPIv1alpha2.NginxProxySpec{ + RewriteClientIP: &ngfAPIv1alpha2.RewriteClientIP{ SetIPRecursively: helpers.GetPointer(true), - TrustedAddresses: []ngfAPI.Address{ + TrustedAddresses: []ngfAPIv1alpha2.Address{ { - Type: ngfAPI.IPAddressType, + Type: ngfAPIv1alpha2.IPAddressType, Value: "1.2.3.4.5", }, { - Type: ngfAPI.IPAddressType, + Type: ngfAPIv1alpha2.IPAddressType, Value: "10.0.0.1", }, }, - Mode: helpers.GetPointer(ngfAPI.RewriteClientIPModeProxyProtocol), + Mode: helpers.GetPointer(ngfAPIv1alpha2.RewriteClientIPModeProxyProtocol), }, }, }, @@ -480,21 +910,21 @@ func TestValidateRewriteClientIP(t *testing.T) { { name: "invalid hostname in trustedAddresses", validator: createInvalidValidator(), - np: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - RewriteClientIP: &ngfAPI.RewriteClientIP{ + np: &ngfAPIv1alpha2.NginxProxy{ + Spec: ngfAPIv1alpha2.NginxProxySpec{ + RewriteClientIP: &ngfAPIv1alpha2.RewriteClientIP{ SetIPRecursively: helpers.GetPointer(true), - TrustedAddresses: []ngfAPI.Address{ + TrustedAddresses: []ngfAPIv1alpha2.Address{ { - Type: ngfAPI.HostnameAddressType, + Type: ngfAPIv1alpha2.HostnameAddressType, Value: "bad-host$%^", }, { - Type: ngfAPI.HostnameAddressType, + Type: ngfAPIv1alpha2.HostnameAddressType, Value: "example.com", }, }, - Mode: helpers.GetPointer(ngfAPI.RewriteClientIPModeProxyProtocol), + Mode: helpers.GetPointer(ngfAPIv1alpha2.RewriteClientIPModeProxyProtocol), }, }, }, @@ -507,10 +937,10 @@ func TestValidateRewriteClientIP(t *testing.T) { { name: "invalid when mode is set and trustedAddresses is empty", validator: createInvalidValidator(), - np: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - RewriteClientIP: &ngfAPI.RewriteClientIP{ - Mode: helpers.GetPointer(ngfAPI.RewriteClientIPModeProxyProtocol), + np: &ngfAPIv1alpha2.NginxProxy{ + Spec: ngfAPIv1alpha2.NginxProxySpec{ + RewriteClientIP: &ngfAPIv1alpha2.RewriteClientIP{ + Mode: helpers.GetPointer(ngfAPIv1alpha2.RewriteClientIPModeProxyProtocol), }, }, }, @@ -520,32 +950,32 @@ func TestValidateRewriteClientIP(t *testing.T) { { name: "invalid when trustedAddresses is greater in length than 16", validator: createInvalidValidator(), - np: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - RewriteClientIP: &ngfAPI.RewriteClientIP{ - Mode: helpers.GetPointer(ngfAPI.RewriteClientIPModeProxyProtocol), - TrustedAddresses: []ngfAPI.Address{ - {Type: ngfAPI.CIDRAddressType, Value: "2001:db8:a0b:12f0::1/32"}, - {Type: ngfAPI.CIDRAddressType, Value: "2001:db8:a0b:12f0::1/32"}, - {Type: ngfAPI.CIDRAddressType, Value: "2001:db8:a0b:12f0::1/32"}, - {Type: ngfAPI.CIDRAddressType, Value: "2001:db8:a0b:12f0::1/32"}, - {Type: ngfAPI.CIDRAddressType, Value: "2001:db8:a0b:12f0::1/32"}, - {Type: ngfAPI.CIDRAddressType, Value: "2001:db8:a0b:12f0::1/32"}, - {Type: ngfAPI.CIDRAddressType, Value: "2001:db8:a0b:12f0::1/32"}, - {Type: ngfAPI.CIDRAddressType, Value: "2001:db8:a0b:12f0::1/32"}, - {Type: ngfAPI.CIDRAddressType, Value: "2001:db8:a0b:12f0::1/32"}, - {Type: ngfAPI.CIDRAddressType, Value: "2001:db8:a0b:12f0::1/32"}, - {Type: ngfAPI.CIDRAddressType, Value: "2001:db8:a0b:12f0::1/32"}, - {Type: ngfAPI.CIDRAddressType, Value: "2001:db8:a0b:12f0::1/32"}, - {Type: ngfAPI.CIDRAddressType, Value: "2001:db8:a0b:12f0::1/32"}, - {Type: ngfAPI.CIDRAddressType, Value: "2001:db8:a0b:12f0::1/32"}, - {Type: ngfAPI.CIDRAddressType, Value: "2001:db8:a0b:12f0::1/32"}, - {Type: ngfAPI.CIDRAddressType, Value: "2001:db8:a0b:12f0::1/32"}, - {Type: ngfAPI.CIDRAddressType, Value: "2001:db8:a0b:12f0::1/32"}, - {Type: ngfAPI.CIDRAddressType, Value: "2001:db8:a0b:12f0::1/32"}, - {Type: ngfAPI.CIDRAddressType, Value: "2001:db8:a0b:12f0::1/32"}, - {Type: ngfAPI.CIDRAddressType, Value: "2001:db8:a0b:12f0::1/32"}, - {Type: ngfAPI.CIDRAddressType, Value: "2001:db8:a0b:12f0::1/32"}, + np: &ngfAPIv1alpha2.NginxProxy{ + Spec: ngfAPIv1alpha2.NginxProxySpec{ + RewriteClientIP: &ngfAPIv1alpha2.RewriteClientIP{ + Mode: helpers.GetPointer(ngfAPIv1alpha2.RewriteClientIPModeProxyProtocol), + TrustedAddresses: []ngfAPIv1alpha2.Address{ + {Type: ngfAPIv1alpha2.CIDRAddressType, Value: "2001:db8:a0b:12f0::1/32"}, + {Type: ngfAPIv1alpha2.CIDRAddressType, Value: "2001:db8:a0b:12f0::1/32"}, + {Type: ngfAPIv1alpha2.CIDRAddressType, Value: "2001:db8:a0b:12f0::1/32"}, + {Type: ngfAPIv1alpha2.CIDRAddressType, Value: "2001:db8:a0b:12f0::1/32"}, + {Type: ngfAPIv1alpha2.CIDRAddressType, Value: "2001:db8:a0b:12f0::1/32"}, + {Type: ngfAPIv1alpha2.CIDRAddressType, Value: "2001:db8:a0b:12f0::1/32"}, + {Type: ngfAPIv1alpha2.CIDRAddressType, Value: "2001:db8:a0b:12f0::1/32"}, + {Type: ngfAPIv1alpha2.CIDRAddressType, Value: "2001:db8:a0b:12f0::1/32"}, + {Type: ngfAPIv1alpha2.CIDRAddressType, Value: "2001:db8:a0b:12f0::1/32"}, + {Type: ngfAPIv1alpha2.CIDRAddressType, Value: "2001:db8:a0b:12f0::1/32"}, + {Type: ngfAPIv1alpha2.CIDRAddressType, Value: "2001:db8:a0b:12f0::1/32"}, + {Type: ngfAPIv1alpha2.CIDRAddressType, Value: "2001:db8:a0b:12f0::1/32"}, + {Type: ngfAPIv1alpha2.CIDRAddressType, Value: "2001:db8:a0b:12f0::1/32"}, + {Type: ngfAPIv1alpha2.CIDRAddressType, Value: "2001:db8:a0b:12f0::1/32"}, + {Type: ngfAPIv1alpha2.CIDRAddressType, Value: "2001:db8:a0b:12f0::1/32"}, + {Type: ngfAPIv1alpha2.CIDRAddressType, Value: "2001:db8:a0b:12f0::1/32"}, + {Type: ngfAPIv1alpha2.CIDRAddressType, Value: "2001:db8:a0b:12f0::1/32"}, + {Type: ngfAPIv1alpha2.CIDRAddressType, Value: "2001:db8:a0b:12f0::1/32"}, + {Type: ngfAPIv1alpha2.CIDRAddressType, Value: "2001:db8:a0b:12f0::1/32"}, + {Type: ngfAPIv1alpha2.CIDRAddressType, Value: "2001:db8:a0b:12f0::1/32"}, + {Type: ngfAPIv1alpha2.CIDRAddressType, Value: "2001:db8:a0b:12f0::1/32"}, }, }, }, @@ -556,17 +986,17 @@ func TestValidateRewriteClientIP(t *testing.T) { { name: "invalid when mode is not proxyProtocol or XForwardedFor", validator: createInvalidValidator(), - np: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - RewriteClientIP: &ngfAPI.RewriteClientIP{ - Mode: helpers.GetPointer(ngfAPI.RewriteClientIPModeType("invalid")), - TrustedAddresses: []ngfAPI.Address{ + np: &ngfAPIv1alpha2.NginxProxy{ + Spec: ngfAPIv1alpha2.NginxProxySpec{ + RewriteClientIP: &ngfAPIv1alpha2.RewriteClientIP{ + Mode: helpers.GetPointer(ngfAPIv1alpha2.RewriteClientIPModeType("invalid")), + TrustedAddresses: []ngfAPIv1alpha2.Address{ { - Type: ngfAPI.CIDRAddressType, + Type: ngfAPIv1alpha2.CIDRAddressType, Value: "2001:db8:a0b:12f0::1/32", }, { - Type: ngfAPI.CIDRAddressType, + Type: ngfAPIv1alpha2.CIDRAddressType, Value: "10.0.0.1/32", }, }, @@ -580,10 +1010,10 @@ func TestValidateRewriteClientIP(t *testing.T) { { name: "invalid when mode is not proxyProtocol or XForwardedFor and trustedAddresses is empty", validator: createInvalidValidator(), - np: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - RewriteClientIP: &ngfAPI.RewriteClientIP{ - Mode: helpers.GetPointer(ngfAPI.RewriteClientIPModeType("invalid")), + np: &ngfAPIv1alpha2.NginxProxy{ + Spec: ngfAPIv1alpha2.NginxProxySpec{ + RewriteClientIP: &ngfAPIv1alpha2.RewriteClientIP{ + Mode: helpers.GetPointer(ngfAPIv1alpha2.RewriteClientIPModeType("invalid")), }, }, }, @@ -595,17 +1025,17 @@ func TestValidateRewriteClientIP(t *testing.T) { { name: "invalid address type in trustedAddresses", validator: createInvalidValidator(), - np: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - RewriteClientIP: &ngfAPI.RewriteClientIP{ + np: &ngfAPIv1alpha2.NginxProxy{ + Spec: ngfAPIv1alpha2.NginxProxySpec{ + RewriteClientIP: &ngfAPIv1alpha2.RewriteClientIP{ SetIPRecursively: helpers.GetPointer(true), - TrustedAddresses: []ngfAPI.Address{ + TrustedAddresses: []ngfAPIv1alpha2.Address{ { - Type: ngfAPI.AddressType("invalid"), + Type: ngfAPIv1alpha2.AddressType("invalid"), Value: "2001:db8::/129", }, }, - Mode: helpers.GetPointer(ngfAPI.RewriteClientIPModeProxyProtocol), + Mode: helpers.GetPointer(ngfAPIv1alpha2.RewriteClientIPModeProxyProtocol), }, }, }, @@ -631,19 +1061,19 @@ func TestValidateRewriteClientIP(t *testing.T) { func TestValidateLogging(t *testing.T) { t.Parallel() - invalidLogLevel := ngfAPI.NginxErrorLogLevel("invalid-log-level") + invalidLogLevel := ngfAPIv1alpha2.NginxErrorLogLevel("invalid-log-level") tests := []struct { - np *ngfAPI.NginxProxy + np *ngfAPIv1alpha2.NginxProxy name string errorString string expectErrCount int }{ { - np: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - Logging: &ngfAPI.NginxLogging{ - ErrorLevel: helpers.GetPointer(ngfAPI.NginxLogLevelDebug), + np: &ngfAPIv1alpha2.NginxProxy{ + Spec: ngfAPIv1alpha2.NginxProxySpec{ + Logging: &ngfAPIv1alpha2.NginxLogging{ + ErrorLevel: helpers.GetPointer(ngfAPIv1alpha2.NginxLogLevelDebug), }, }, }, @@ -652,10 +1082,10 @@ func TestValidateLogging(t *testing.T) { expectErrCount: 0, }, { - np: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - Logging: &ngfAPI.NginxLogging{ - ErrorLevel: helpers.GetPointer(ngfAPI.NginxLogLevelInfo), + np: &ngfAPIv1alpha2.NginxProxy{ + Spec: ngfAPIv1alpha2.NginxProxySpec{ + Logging: &ngfAPIv1alpha2.NginxLogging{ + ErrorLevel: helpers.GetPointer(ngfAPIv1alpha2.NginxLogLevelInfo), }, }, }, @@ -664,10 +1094,10 @@ func TestValidateLogging(t *testing.T) { expectErrCount: 0, }, { - np: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - Logging: &ngfAPI.NginxLogging{ - ErrorLevel: helpers.GetPointer(ngfAPI.NginxLogLevelNotice), + np: &ngfAPIv1alpha2.NginxProxy{ + Spec: ngfAPIv1alpha2.NginxProxySpec{ + Logging: &ngfAPIv1alpha2.NginxLogging{ + ErrorLevel: helpers.GetPointer(ngfAPIv1alpha2.NginxLogLevelNotice), }, }, }, @@ -676,10 +1106,10 @@ func TestValidateLogging(t *testing.T) { expectErrCount: 0, }, { - np: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - Logging: &ngfAPI.NginxLogging{ - ErrorLevel: helpers.GetPointer(ngfAPI.NginxLogLevelWarn), + np: &ngfAPIv1alpha2.NginxProxy{ + Spec: ngfAPIv1alpha2.NginxProxySpec{ + Logging: &ngfAPIv1alpha2.NginxLogging{ + ErrorLevel: helpers.GetPointer(ngfAPIv1alpha2.NginxLogLevelWarn), }, }, }, @@ -688,10 +1118,10 @@ func TestValidateLogging(t *testing.T) { expectErrCount: 0, }, { - np: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - Logging: &ngfAPI.NginxLogging{ - ErrorLevel: helpers.GetPointer(ngfAPI.NginxLogLevelError), + np: &ngfAPIv1alpha2.NginxProxy{ + Spec: ngfAPIv1alpha2.NginxProxySpec{ + Logging: &ngfAPIv1alpha2.NginxLogging{ + ErrorLevel: helpers.GetPointer(ngfAPIv1alpha2.NginxLogLevelError), }, }, }, @@ -700,10 +1130,10 @@ func TestValidateLogging(t *testing.T) { expectErrCount: 0, }, { - np: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - Logging: &ngfAPI.NginxLogging{ - ErrorLevel: helpers.GetPointer(ngfAPI.NginxLogLevelCrit), + np: &ngfAPIv1alpha2.NginxProxy{ + Spec: ngfAPIv1alpha2.NginxProxySpec{ + Logging: &ngfAPIv1alpha2.NginxLogging{ + ErrorLevel: helpers.GetPointer(ngfAPIv1alpha2.NginxLogLevelCrit), }, }, }, @@ -712,10 +1142,10 @@ func TestValidateLogging(t *testing.T) { expectErrCount: 0, }, { - np: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - Logging: &ngfAPI.NginxLogging{ - ErrorLevel: helpers.GetPointer(ngfAPI.NginxLogLevelAlert), + np: &ngfAPIv1alpha2.NginxProxy{ + Spec: ngfAPIv1alpha2.NginxProxySpec{ + Logging: &ngfAPIv1alpha2.NginxLogging{ + ErrorLevel: helpers.GetPointer(ngfAPIv1alpha2.NginxLogLevelAlert), }, }, }, @@ -724,10 +1154,10 @@ func TestValidateLogging(t *testing.T) { expectErrCount: 0, }, { - np: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - Logging: &ngfAPI.NginxLogging{ - ErrorLevel: helpers.GetPointer(ngfAPI.NginxLogLevelEmerg), + np: &ngfAPIv1alpha2.NginxProxy{ + Spec: ngfAPIv1alpha2.NginxProxySpec{ + Logging: &ngfAPIv1alpha2.NginxLogging{ + ErrorLevel: helpers.GetPointer(ngfAPIv1alpha2.NginxLogLevelEmerg), }, }, }, @@ -736,9 +1166,9 @@ func TestValidateLogging(t *testing.T) { expectErrCount: 0, }, { - np: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - Logging: &ngfAPI.NginxLogging{ + np: &ngfAPIv1alpha2.NginxProxy{ + Spec: ngfAPIv1alpha2.NginxProxySpec{ + Logging: &ngfAPIv1alpha2.NginxLogging{ ErrorLevel: &invalidLogLevel, }, }, @@ -749,9 +1179,9 @@ func TestValidateLogging(t *testing.T) { expectErrCount: 1, }, { - np: &ngfAPI.NginxProxy{ - Spec: ngfAPI.NginxProxySpec{ - Logging: &ngfAPI.NginxLogging{}, + np: &ngfAPIv1alpha2.NginxProxy{ + Spec: ngfAPIv1alpha2.NginxProxySpec{ + Logging: &ngfAPIv1alpha2.NginxLogging{}, }, }, name: "empty log level", @@ -773,3 +1203,11 @@ func TestValidateLogging(t *testing.T) { }) } } + +func TestValidateNginxProxy_NilCase(t *testing.T) { + t.Parallel() + g := NewWithT(t) + + // Just testing the nil case for coverage reasons. The rest of the function is covered by other tests. + g.Expect(buildNginxProxy(nil, &validationfakes.FakeGenericValidator{})).To(BeNil()) +} diff --git a/internal/mode/static/state/graph/route_common.go b/internal/mode/static/state/graph/route_common.go index 4b2e767c67..3c8227f37d 100644 --- a/internal/mode/static/state/graph/route_common.go +++ b/internal/mode/static/state/graph/route_common.go @@ -187,7 +187,7 @@ func buildL4RoutesForGateways( tlsRoutes map[types.NamespacedName]*v1alpha.TLSRoute, gatewayNsNames []types.NamespacedName, services map[types.NamespacedName]*apiv1.Service, - npCfg *NginxProxy, + npCfg *EffectiveNginxProxy, resolver *referenceGrantResolver, ) map[L4RouteKey]*L4Route { if len(gatewayNsNames) == 0 { @@ -216,7 +216,7 @@ func buildRoutesForGateways( httpRoutes map[types.NamespacedName]*v1.HTTPRoute, grpcRoutes map[types.NamespacedName]*v1.GRPCRoute, gatewayNsNames []types.NamespacedName, - npCfg *NginxProxy, + effectiveNginxProxy *EffectiveNginxProxy, snippetsFilters map[types.NamespacedName]*SnippetsFilter, ) map[RouteKey]*L7Route { if len(gatewayNsNames) == 0 { @@ -225,7 +225,7 @@ func buildRoutesForGateways( routes := make(map[RouteKey]*L7Route) - http2disabled := isHTTP2Disabled(npCfg) + http2disabled := isHTTP2Disabled(effectiveNginxProxy) for _, route := range httpRoutes { r := buildHTTPRoute(validator, route, gatewayNsNames, snippetsFilters) @@ -244,11 +244,16 @@ func buildRoutesForGateways( return routes } -func isHTTP2Disabled(npCfg *NginxProxy) bool { +func isHTTP2Disabled(npCfg *EffectiveNginxProxy) bool { if npCfg == nil { return false } - return npCfg.Source.Spec.DisableHTTP2 + + if npCfg.DisableHTTP2 == nil { + return false + } + + return *npCfg.DisableHTTP2 } func buildSectionNameRefs( diff --git a/internal/mode/static/state/graph/tlsroute.go b/internal/mode/static/state/graph/tlsroute.go index 03a8ca8a36..ec59f5058a 100644 --- a/internal/mode/static/state/graph/tlsroute.go +++ b/internal/mode/static/state/graph/tlsroute.go @@ -15,7 +15,7 @@ func buildTLSRoute( gtr *v1alpha2.TLSRoute, gatewayNsNames []types.NamespacedName, services map[types.NamespacedName]*apiv1.Service, - npCfg *NginxProxy, + npCfg *EffectiveNginxProxy, refGrantResolver func(resource toResource) bool, ) *L4Route { r := &L4Route{ @@ -69,7 +69,7 @@ func buildTLSRoute( func validateBackendRefTLSRoute(gtr *v1alpha2.TLSRoute, services map[types.NamespacedName]*apiv1.Service, - npCfg *NginxProxy, + npCfg *EffectiveNginxProxy, refGrantResolver func(resource toResource) bool, ) (BackendRef, *conditions.Condition) { // Length of BackendRefs and Rules is guaranteed to be one due to earlier check in buildTLSRoute diff --git a/internal/mode/static/state/graph/tlsroute_test.go b/internal/mode/static/state/graph/tlsroute_test.go index edd6536ace..ff6ee787f2 100644 --- a/internal/mode/static/state/graph/tlsroute_test.go +++ b/internal/mode/static/state/graph/tlsroute_test.go @@ -10,7 +10,7 @@ import ( gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" "sigs.k8s.io/gateway-api/apis/v1alpha2" - ngfAPI "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha1" + ngfAPI "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha2" "github.com/nginxinc/nginx-gateway-fabric/internal/framework/conditions" "github.com/nginxinc/nginx-gateway-fabric/internal/framework/helpers" staticConds "github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/state/conditions" @@ -271,9 +271,9 @@ func TestBuildTLSRoute(t *testing.T) { gtr *v1alpha2.TLSRoute services map[types.NamespacedName]*apiv1.Service resolver func(resource toResource) bool + npCfg *EffectiveNginxProxy name string gatewayNsNames []types.NamespacedName - npCfg NginxProxy }{ { gtr: duplicateParentRefsGtr, @@ -491,10 +491,7 @@ func TestBuildTLSRoute(t *testing.T) { services: map[types.NamespacedName]*apiv1.Service{ svcNsName: ipv4Svc, }, - npCfg: NginxProxy{ - Source: &ngfAPI.NginxProxy{Spec: ngfAPI.NginxProxySpec{IPFamily: helpers.GetPointer(ngfAPI.IPv6)}}, - Valid: true, - }, + npCfg: &EffectiveNginxProxy{IPFamily: helpers.GetPointer(ngfAPI.IPv6)}, resolver: alwaysTrueRefGrantResolver, name: "service and npcfg ip family mismatch", }, @@ -559,7 +556,7 @@ func TestBuildTLSRoute(t *testing.T) { test.gtr, test.gatewayNsNames, test.services, - &test.npCfg, + test.npCfg, test.resolver, ) g.Expect(helpers.Diff(test.expected, r)).To(BeEmpty()) diff --git a/internal/mode/static/status/prepare_requests.go b/internal/mode/static/status/prepare_requests.go index e0add956a8..df8586151b 100644 --- a/internal/mode/static/status/prepare_requests.go +++ b/internal/mode/static/status/prepare_requests.go @@ -293,6 +293,8 @@ func prepareGatewayRequest( } gwConds := staticConds.NewDefaultGatewayConditions() + gwConds = append(gwConds, gateway.Conditions...) + if validListenerCount == 0 { gwConds = append(gwConds, staticConds.NewGatewayNotAcceptedListenersNotValid()...) } else if validListenerCount < len(gateway.Listeners) { diff --git a/internal/mode/static/status/prepare_requests_test.go b/internal/mode/static/status/prepare_requests_test.go index d52b43e7a8..510b4e756b 100644 --- a/internal/mode/static/status/prepare_requests_test.go +++ b/internal/mode/static/status/prepare_requests_test.go @@ -1134,6 +1134,117 @@ func TestBuildGatewayStatuses(t *testing.T) { }, nginxReloadRes: NginxReloadResult{Error: errors.New("test error")}, }, + { + name: "valid gateway with valid parametersRef; all valid listeners", + gateway: &graph.Gateway{ + Source: createGateway(), + Listeners: []*graph.Listener{ + { + Name: "listener-valid-1", + Valid: true, + Routes: map[graph.RouteKey]*graph.L7Route{routeKey: {}}, + }, + }, + Valid: true, + Conditions: []conditions.Condition{ + staticConds.NewGatewayResolvedRefs(), + }, + }, + expected: map[types.NamespacedName]v1.GatewayStatus{ + {Namespace: "test", Name: "gateway"}: { + Addresses: addr, + Conditions: []metav1.Condition{ + { + Type: string(v1.GatewayConditionAccepted), + Status: metav1.ConditionTrue, + ObservedGeneration: 2, + LastTransitionTime: transitionTime, + Reason: string(v1.GatewayReasonAccepted), + Message: "Gateway is accepted", + }, + { + Type: string(v1.GatewayConditionProgrammed), + Status: metav1.ConditionTrue, + ObservedGeneration: 2, + LastTransitionTime: transitionTime, + Reason: string(v1.GatewayReasonProgrammed), + Message: "Gateway is programmed", + }, + { + Type: string(staticConds.GatewayResolvedRefs), + Status: metav1.ConditionTrue, + ObservedGeneration: 2, + LastTransitionTime: transitionTime, + Reason: string(staticConds.GatewayReasonResolvedRefs), + Message: "ParametersRef resource is resolved", + }, + }, + Listeners: []v1.ListenerStatus{ + { + Name: "listener-valid-1", + AttachedRoutes: 1, + Conditions: validListenerConditions, + }, + }, + }, + }, + }, + { + name: "valid gateway with invalid parametersRef; all valid listeners", + gateway: &graph.Gateway{ + Source: createGateway(), + Listeners: []*graph.Listener{ + { + Name: "listener-valid-1", + Valid: true, + Routes: map[graph.RouteKey]*graph.L7Route{routeKey: {}}, + }, + }, + Valid: true, + Conditions: []conditions.Condition{ + staticConds.NewGatewayRefNotFound(), + staticConds.NewGatewayInvalidParameters("ParametersRef not found"), + }, + }, + expected: map[types.NamespacedName]v1.GatewayStatus{ + {Namespace: "test", Name: "gateway"}: { + Addresses: addr, + Conditions: []metav1.Condition{ + { + Type: string(v1.GatewayConditionProgrammed), + Status: metav1.ConditionTrue, + ObservedGeneration: 2, + LastTransitionTime: transitionTime, + Reason: string(v1.GatewayReasonProgrammed), + Message: "Gateway is programmed", + }, + { + Type: string(staticConds.GatewayResolvedRefs), + Status: metav1.ConditionFalse, + ObservedGeneration: 2, + LastTransitionTime: transitionTime, + Reason: string(staticConds.GatewayReasonParamsRefNotFound), + Message: "ParametersRef resource could not be found", + }, + { + Type: string(v1.GatewayConditionAccepted), + Status: metav1.ConditionTrue, + ObservedGeneration: 2, + LastTransitionTime: transitionTime, + Reason: string(v1.GatewayReasonInvalidParameters), + Message: "Gateway is accepted, but ParametersRef is ignored due to an error: ParametersRef not found", + }, + }, + Listeners: []v1.ListenerStatus{ + { + Name: "listener-valid-1", + AttachedRoutes: 1, + Conditions: validListenerConditions, + }, + }, + }, + }, + }, } for _, test := range tests { diff --git a/internal/mode/static/telemetry/collector.go b/internal/mode/static/telemetry/collector.go index d809ab342c..e334216e14 100644 --- a/internal/mode/static/telemetry/collector.go +++ b/internal/mode/static/telemetry/collector.go @@ -246,10 +246,7 @@ func collectGraphResourceCount( } } - if g.NginxProxy != nil { - ngfResourceCounts.NginxProxyCount = 1 - } - + ngfResourceCounts.NginxProxyCount = int64(len(g.ReferencedNginxProxies)) ngfResourceCounts.SnippetsFilterCount = int64(len(g.SnippetsFilters)) return ngfResourceCounts, nil diff --git a/internal/mode/static/telemetry/collector_test.go b/internal/mode/static/telemetry/collector_test.go index 5a7b11b582..93a3ba616b 100644 --- a/internal/mode/static/telemetry/collector_test.go +++ b/internal/mode/static/telemetry/collector_test.go @@ -334,8 +334,10 @@ var _ = Describe("Collector", Ordered, func() { GVK: schema.GroupVersionKind{Kind: kinds.UpstreamSettingsPolicy}, }: {}, }, - NginxProxy: &graph.NginxProxy{}, - SnippetsFilters: map[types.NamespacedName]*graph.SnippetsFilter{ + ReferencedNginxProxies: map[types.NamespacedName]*graph.NginxProxy{ + {Namespace: "test", Name: "NginxProxy-1"}: {}, + {Namespace: "test", Name: "NginxProxy-2"}: {}, + }, SnippetsFilters: map[types.NamespacedName]*graph.SnippetsFilter{ {Namespace: "test", Name: "sf-1"}: { Snippets: map[ngfAPI.NginxContext]string{ ngfAPI.NginxContextMain: "worker_priority 0;", @@ -414,7 +416,7 @@ var _ = Describe("Collector", Ordered, func() { GatewayAttachedClientSettingsPolicyCount: 1, RouteAttachedClientSettingsPolicyCount: 2, ObservabilityPolicyCount: 1, - NginxProxyCount: 1, + NginxProxyCount: 2, SnippetsFilterCount: 3, UpstreamSettingsPolicyCount: 1, } @@ -613,7 +615,10 @@ var _ = Describe("Collector", Ordered, func() { GVK: schema.GroupVersionKind{Kind: kinds.UpstreamSettingsPolicy}, }: {}, }, - NginxProxy: &graph.NginxProxy{}, + ReferencedNginxProxies: map[types.NamespacedName]*graph.NginxProxy{ + {Namespace: "test", Name: "NginxProxy-1"}: {}, + {Namespace: "test", Name: "NginxProxy-2"}: {}, + }, SnippetsFilters: map[types.NamespacedName]*graph.SnippetsFilter{ {Namespace: "test", Name: "sf-1"}: {}, }, @@ -689,7 +694,7 @@ var _ = Describe("Collector", Ordered, func() { GatewayAttachedClientSettingsPolicyCount: 1, RouteAttachedClientSettingsPolicyCount: 1, ObservabilityPolicyCount: 1, - NginxProxyCount: 1, + NginxProxyCount: 2, SnippetsFilterCount: 1, UpstreamSettingsPolicyCount: 1, } diff --git a/site/content/how-to/data-plane-configuration.md b/site/content/how-to/data-plane-configuration.md index 1db8f26632..8466732dc3 100644 --- a/site/content/how-to/data-plane-configuration.md +++ b/site/content/how-to/data-plane-configuration.md @@ -69,7 +69,7 @@ The following command creates a basic `NginxProxy` configuration that sets the I ```yaml kubectl apply -f - < gateway.nginx.org/v1alpha1 +
  • +gateway.nginx.org/v1alpha2 +
  • gateway.nginx.org/v1alpha1

    @@ -22,8 +25,6 @@ Resource Types:

  • NginxGateway
  • -NginxProxy -
  • ObservabilityPolicy
  • SnippetsFilter @@ -242,141 +243,6 @@ NginxGatewayStatus -

    NginxProxy - -

    -

    -

    NginxProxy is a configuration object that is attached to a GatewayClass parametersRef. It provides a way -to configure global settings for all Gateways defined from the GatewayClass.

    -

    - - - - - - - - - - - - - - - - - - - - - - - - - -
    FieldDescription
    -apiVersion
    -string
    - -gateway.nginx.org/v1alpha1 - -
    -kind
    -string -
    NginxProxy
    -metadata
    - - -Kubernetes meta/v1.ObjectMeta - - -
    -Refer to the Kubernetes API documentation for the fields of the -metadata field. -
    -spec
    - - -NginxProxySpec - - -
    -

    Spec defines the desired state of the NginxProxy.

    -
    -
    - - - - - - - - - - - - - - - - - - - - - -
    -ipFamily
    - - -IPFamilyType - - -
    -(Optional) -

    IPFamily specifies the IP family to be used by the NGINX. -Default is “dual”, meaning the server will use both IPv4 and IPv6.

    -
    -telemetry
    - - -Telemetry - - -
    -(Optional) -

    Telemetry specifies the OpenTelemetry configuration.

    -
    -rewriteClientIP
    - - -RewriteClientIP - - -
    -(Optional) -

    RewriteClientIP defines configuration for rewriting the client IP to the original client’s IP.

    -
    -logging
    - - -NginxLogging - - -
    -(Optional) -

    Logging defines logging related settings for NGINX.

    -
    -disableHTTP2
    - -bool - -
    -(Optional) -

    DisableHTTP2 defines if http2 should be disabled for all servers. -Default is false, meaning http2 will be enabled for all servers.

    -
    -

    ObservabilityPolicy

    @@ -703,78 +569,6 @@ sigs.k8s.io/gateway-api/apis/v1alpha2.PolicyStatus -

    Address - -

    -

    -(Appears on: -RewriteClientIP) -

    -

    -

    Address is a struct that specifies address type and value.

    -

    - - - - - - - - - - - - - - - - - -
    FieldDescription
    -type
    - - -AddressType - - -
    -

    Type specifies the type of address.

    -
    -value
    - -string - -
    -

    Value specifies the address value.

    -
    -

    AddressType -(string alias)

    -

    -

    -(Appears on: -Address) -

    -

    -

    AddressType specifies the type of address.

    -

    - - - - - - - - - - - - - - -
    ValueDescription

    "CIDR"

    CIDRAddressType specifies that the address is a CIDR block.

    -

    "Hostname"

    HostnameAddressType specifies that the address is a Hostname.

    -

    "IPAddress"

    IPAddressType specifies that the address is an IP address.

    -

    ClientBody

    @@ -1101,8 +895,8 @@ longer necessary.

    ClientBody, ClientKeepAlive, ClientKeepAliveTimeout, -TelemetryExporter, -UpstreamKeepAlive) +UpstreamKeepAlive, +TelemetryExporter)

    Duration is a string value representing a duration in time. @@ -1110,34 +904,6 @@ Duration can be specified in milliseconds (ms), seconds (s), minutes (m), hours A value without a suffix is seconds. Examples: 120s, 50ms, 5m, 1h.

    -

    IPFamilyType -(string alias)

    -

    -

    -(Appears on: -NginxProxySpec) -

    -

    -

    IPFamilyType specifies the IP family to be used by NGINX.

    -

    - - - - - - - - - - - - - - -
    ValueDescription

    "dual"

    Dual specifies that NGINX will use both IPv4 and IPv6.

    -

    "ipv4"

    IPv4 specifies that NGINX will use only IPv4.

    -

    "ipv6"

    IPv6 specifies that NGINX will use only IPv6.

    -

    Logging

    @@ -1206,15 +972,12 @@ ControllerLogLevel -

    NginxErrorLogLevel -(string alias)

    +

    NginxGatewayConditionReason +(string alias)

    -(Appears on: -NginxLogging) -

    -

    -

    NginxErrorLogLevel type defines the log level of error logs for NGINX.

    +

    NginxGatewayConditionReason defines the set of reasons that explain why a +particular NginxGateway condition type has been raised.

    @@ -1223,56 +986,16 @@ ControllerLogLevel - - - - - - - - - - - - - - + - - +
    Description

    "alert"

    NginxLogLevelAlert is the alert level for NGINX error logs.

    -

    "crit"

    NginxLogLevelCrit is the crit level for NGINX error logs.

    -

    "debug"

    NginxLogLevelDebug is the debug level for NGINX error logs.

    -

    "emerg"

    NginxLogLevelEmerg is the emerg level for NGINX error logs.

    -

    "error"

    NginxLogLevelError is the error level for NGINX error logs.

    -

    "info"

    NginxLogLevelInfo is the info level for NGINX error logs.

    -

    "notice"

    NginxLogLevelNotice is the notice level for NGINX error logs.

    +

    "Invalid"

    NginxGatewayReasonInvalid is a reason that is used with the “Valid” condition when the condition is False.

    "warn"

    NginxLogLevelWarn is the warn level for NGINX error logs.

    +

    "Valid"

    NginxGatewayReasonValid is a reason that is used with the “Valid” condition when the condition is True.

    -

    NginxGatewayConditionReason -(string alias)

    -

    -

    -

    NginxGatewayConditionReason defines the set of reasons that explain why a -particular NginxGateway condition type has been raised.

    -

    - - - - - - - - - - - - -
    ValueDescription

    "Invalid"

    NginxGatewayReasonInvalid is a reason that is used with the “Valid” condition when the condition is False.

    -

    "Valid"

    NginxGatewayReasonValid is a reason that is used with the “Valid” condition when the condition is True.

    -
    -

    NginxGatewayConditionType -(string alias)

    +

    NginxGatewayConditionType +(string alias)

    NginxGatewayConditionType is a type of condition associated with an @@ -1358,133 +1081,6 @@ Logging -

    NginxLogging - -

    -

    -(Appears on: -NginxProxySpec) -

    -

    -

    NginxLogging defines logging related settings for NGINX.

    -

    - - - - - - - - - - - - - -
    FieldDescription
    -errorLevel
    - - -NginxErrorLogLevel - - -
    -(Optional) -

    ErrorLevel defines the error log level. Possible log levels listed in order of increasing severity are -debug, info, notice, warn, error, crit, alert, and emerg. Setting a certain log level will cause all messages -of the specified and more severe log levels to be logged. For example, the log level ‘error’ will cause error, -crit, alert, and emerg messages to be logged. https://nginx.org/en/docs/ngx_core_module.html#error_log

    -
    -

    NginxProxySpec - -

    -

    -(Appears on: -NginxProxy) -

    -

    -

    NginxProxySpec defines the desired state of the NginxProxy.

    -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    FieldDescription
    -ipFamily
    - - -IPFamilyType - - -
    -(Optional) -

    IPFamily specifies the IP family to be used by the NGINX. -Default is “dual”, meaning the server will use both IPv4 and IPv6.

    -
    -telemetry
    - - -Telemetry - - -
    -(Optional) -

    Telemetry specifies the OpenTelemetry configuration.

    -
    -rewriteClientIP
    - - -RewriteClientIP - - -
    -(Optional) -

    RewriteClientIP defines configuration for rewriting the client IP to the original client’s IP.

    -
    -logging
    - - -NginxLogging - - -
    -(Optional) -

    Logging defines logging related settings for NGINX.

    -
    -disableHTTP2
    - -bool - -
    -(Optional) -

    DisableHTTP2 defines if http2 should be disabled for all servers. -Default is false, meaning http2 will be enabled for all servers.

    -

    ObservabilityPolicySpec

    @@ -1534,116 +1130,6 @@ Support: HTTPRoute, GRPCRoute.

    -

    RewriteClientIP - -

    -

    -(Appears on: -NginxProxySpec) -

    -

    -

    RewriteClientIP specifies the configuration for rewriting the client’s IP address.

    -

    - - - - - - - - - - - - - - - - - - - - - -
    FieldDescription
    -mode
    - - -RewriteClientIPModeType - - -
    -(Optional) -

    Mode defines how NGINX will rewrite the client’s IP address. -There are two possible modes: -- ProxyProtocol: NGINX will rewrite the client’s IP using the PROXY protocol header. -- XForwardedFor: NGINX will rewrite the client’s IP using the X-Forwarded-For header. -Sets NGINX directive real_ip_header: https://nginx.org/en/docs/http/ngx_http_realip_module.html#real_ip_header

    -
    -setIPRecursively
    - -bool - -
    -(Optional) -

    SetIPRecursively configures whether recursive search is used when selecting the client’s address from -the X-Forwarded-For header. It is used in conjunction with TrustedAddresses. -If enabled, NGINX will recurse on the values in X-Forwarded-Header from the end of array -to start of array and select the first untrusted IP. -For example, if X-Forwarded-For is [11.11.11.11, 22.22.22.22, 55.55.55.1], -and TrustedAddresses is set to 55.55.55.132, NGINX will rewrite the client IP to 22.22.22.22. -If disabled, NGINX will select the IP at the end of the array. -In the previous example, 55.55.55.1 would be selected. -Sets NGINX directive real_ip_recursive: https://nginx.org/en/docs/http/ngx_http_realip_module.html#real_ip_recursive

    -
    -trustedAddresses
    - - -[]Address - - -
    -(Optional) -

    TrustedAddresses specifies the addresses that are trusted to send correct client IP information. -If a request comes from a trusted address, NGINX will rewrite the client IP information, -and forward it to the backend in the X-Forwarded-For* and X-Real-IP headers. -If the request does not come from a trusted address, NGINX will not rewrite the client IP information. -TrustedAddresses only supports CIDR blocks: 192.33.21.124, fe80::164. -To trust all addresses (not recommended for production), set to 0.0.0.0/0. -If no addresses are provided, NGINX will not rewrite the client IP information. -Sets NGINX directive set_real_ip_from: https://nginx.org/en/docs/http/ngx_http_realip_module.html#set_real_ip_from -This field is required if mode is set.

    -
    -

    RewriteClientIPModeType -(string alias)

    -

    -

    -(Appears on: -RewriteClientIP) -

    -

    -

    RewriteClientIPModeType defines how NGINX Gateway Fabric will determine the client’s original IP address.

    -

    - - - - - - - - - - - - -
    ValueDescription

    "ProxyProtocol"

    RewriteClientIPModeProxyProtocol configures NGINX to accept PROXY protocol and -set the client’s IP address to the IP address in the PROXY protocol header. -Sets the proxy_protocol parameter on the listen directive of all servers and sets real_ip_header -to proxy_protocol: https://nginx.org/en/docs/http/ngx_http_realip_module.html#real_ip_header.

    -

    "XForwardedFor"

    RewriteClientIPModeXForwardedFor configures NGINX to set the client’s IP address to the -IP address in the X-Forwarded-For HTTP header. -https://nginx.org/en/docs/http/ngx_http_realip_module.html#real_ip_header.

    -

    Size (string alias)

    @@ -1824,8 +1310,8 @@ and the status of the SnippetsFilter with respect to each controller.

    (Appears on: -Telemetry, -Tracing) +Tracing, +Telemetry)

    SpanAttribute is a key value pair to be added to a tracing span.

    @@ -1864,155 +1350,20 @@ Format: must have all ‘“’ escaped and must not contain any &ls -

    Telemetry - +

    TraceContext +(string alias)

    (Appears on: -NginxProxySpec) +Tracing)

    -

    Telemetry specifies the OpenTelemetry configuration.

    +

    TraceContext specifies how to propagate traceparent/tracestate headers.

    - - - - - - - - - - - - - - - - - - -
    FieldDescription
    -exporter
    - - -TelemetryExporter - - -
    -(Optional) -

    Exporter specifies OpenTelemetry export parameters.

    -
    -serviceName
    - -string - -
    -(Optional) -

    ServiceName is the “service.name” attribute of the OpenTelemetry resource. -Default is ‘ngf::’. If a value is provided by the user, -then the default becomes a prefix to that value.

    -
    -spanAttributes
    - - -[]SpanAttribute - - -
    -(Optional) -

    SpanAttributes are custom key/value attributes that are added to each span.

    -
    -

    TelemetryExporter - -

    -

    -(Appears on: -Telemetry) -

    -

    -

    TelemetryExporter specifies OpenTelemetry export parameters.

    -

    - - - - - - - - - - - - - - - - - - - - - - - - - -
    FieldDescription
    -interval
    - - -Duration - - -
    -(Optional) -

    Interval is the maximum interval between two exports. -Default: https://nginx.org/en/docs/ngx_otel_module.html#otel_exporter

    -
    -batchSize
    - -int32 - -
    -(Optional) -

    BatchSize is the maximum number of spans to be sent in one batch per worker. -Default: https://nginx.org/en/docs/ngx_otel_module.html#otel_exporter

    -
    -batchCount
    - -int32 - -
    -(Optional) -

    BatchCount is the number of pending batches per worker, spans exceeding the limit are dropped. -Default: https://nginx.org/en/docs/ngx_otel_module.html#otel_exporter

    -
    -endpoint
    - -string - -
    -

    Endpoint is the address of OTLP/gRPC endpoint that will accept telemetry data. -Format: alphanumeric hostname with optional http scheme and optional port.

    -
    -

    TraceContext -(string alias)

    -

    -

    -(Appears on: -Tracing) -

    -

    -

    TraceContext specifies how to propagate traceparent/tracestate headers.

    -

    - - - - + @@ -2295,6 +1646,706 @@ Support: Service

    ValueValue Description

    +

    gateway.nginx.org/v1alpha2

    +

    +

    Package v1alpha2 contains API Schema definitions for the +gateway.nginx.org API group.

    +

    +Resource Types: + +

    NginxProxy + +

    +

    +

    NginxProxy is a configuration object that can be referenced from a GatewayClass parametersRef +or a Gateway infrastructure.parametersRef. It provides a way to configure data plane settings. +If referenced from a GatewayClass, the settings apply to all Gateways attached to the GatewayClass. +If referenced from a Gateway, the settings apply to that Gateway alone. If both a Gateway and its GatewayClass +reference an NginxProxy, the settings are merged. Settings specified on the Gateway NginxProxy override those +set on the GatewayClass NginxProxy.

    +

    + + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldDescription
    +apiVersion
    +string
    + +gateway.nginx.org/v1alpha2 + +
    +kind
    +string +
    NginxProxy
    +metadata
    + + +Kubernetes meta/v1.ObjectMeta + + +
    +Refer to the Kubernetes API documentation for the fields of the +metadata field. +
    +spec
    + + +NginxProxySpec + + +
    +

    Spec defines the desired state of the NginxProxy.

    +
    +
    + + + + + + + + + + + + + + + + + + + + + +
    +ipFamily
    + + +IPFamilyType + + +
    +(Optional) +

    IPFamily specifies the IP family to be used by the NGINX. +Default is “dual”, meaning the server will use both IPv4 and IPv6.

    +
    +telemetry
    + + +Telemetry + + +
    +(Optional) +

    Telemetry specifies the OpenTelemetry configuration.

    +
    +rewriteClientIP
    + + +RewriteClientIP + + +
    +(Optional) +

    RewriteClientIP defines configuration for rewriting the client IP to the original client’s IP.

    +
    +logging
    + + +NginxLogging + + +
    +(Optional) +

    Logging defines logging related settings for NGINX.

    +
    +disableHTTP2
    + +bool + +
    +(Optional) +

    DisableHTTP2 defines if http2 should be disabled for all servers. +If not specified, or set to false, http2 will be enabled for all servers.

    +
    +
    +

    Address + +

    +

    +(Appears on: +RewriteClientIP) +

    +

    +

    Address is a struct that specifies address type and value.

    +

    + + + + + + + + + + + + + + + + + +
    FieldDescription
    +type
    + + +AddressType + + +
    +

    Type specifies the type of address.

    +
    +value
    + +string + +
    +

    Value specifies the address value.

    +
    +

    AddressType +(string alias)

    +

    +

    +(Appears on: +Address) +

    +

    +

    AddressType specifies the type of address.

    +

    + + + + + + + + + + + + + + +
    ValueDescription

    "CIDR"

    CIDRAddressType specifies that the address is a CIDR block.

    +

    "Hostname"

    HostnameAddressType specifies that the address is a Hostname.

    +

    "IPAddress"

    IPAddressType specifies that the address is an IP address.

    +
    +

    DisableTelemetryFeature +(string alias)

    +

    +

    +(Appears on: +Telemetry) +

    +

    +

    DisableTelemetryFeature is a telemetry feature that can be disabled.

    +

    + + + + + + + + + + +
    ValueDescription

    "DisableTracing"

    DisableTracing disables the OpenTelemetry tracing feature.

    +
    +

    IPFamilyType +(string alias)

    +

    +

    +(Appears on: +NginxProxySpec) +

    +

    +

    IPFamilyType specifies the IP family to be used by NGINX.

    +

    + + + + + + + + + + + + + + +
    ValueDescription

    "dual"

    Dual specifies that NGINX will use both IPv4 and IPv6.

    +

    "ipv4"

    IPv4 specifies that NGINX will use only IPv4.

    +

    "ipv6"

    IPv6 specifies that NGINX will use only IPv6.

    +
    +

    NginxErrorLogLevel +(string alias)

    +

    +

    +(Appears on: +NginxLogging) +

    +

    +

    NginxErrorLogLevel type defines the log level of error logs for NGINX.

    +

    + + + + + + + + + + + + + + + + + + + + + + + + +
    ValueDescription

    "alert"

    NginxLogLevelAlert is the alert level for NGINX error logs.

    +

    "crit"

    NginxLogLevelCrit is the crit level for NGINX error logs.

    +

    "debug"

    NginxLogLevelDebug is the debug level for NGINX error logs.

    +

    "emerg"

    NginxLogLevelEmerg is the emerg level for NGINX error logs.

    +

    "error"

    NginxLogLevelError is the error level for NGINX error logs.

    +

    "info"

    NginxLogLevelInfo is the info level for NGINX error logs.

    +

    "notice"

    NginxLogLevelNotice is the notice level for NGINX error logs.

    +

    "warn"

    NginxLogLevelWarn is the warn level for NGINX error logs.

    +
    +

    NginxLogging + +

    +

    +(Appears on: +NginxProxySpec) +

    +

    +

    NginxLogging defines logging related settings for NGINX.

    +

    + + + + + + + + + + + + + +
    FieldDescription
    +errorLevel
    + + +NginxErrorLogLevel + + +
    +(Optional) +

    ErrorLevel defines the error log level. Possible log levels listed in order of increasing severity are +debug, info, notice, warn, error, crit, alert, and emerg. Setting a certain log level will cause all messages +of the specified and more severe log levels to be logged. For example, the log level ‘error’ will cause error, +crit, alert, and emerg messages to be logged. https://nginx.org/en/docs/ngx_core_module.html#error_log

    +
    +

    NginxProxySpec + +

    +

    +(Appears on: +NginxProxy) +

    +

    +

    NginxProxySpec defines the desired state of the NginxProxy.

    +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldDescription
    +ipFamily
    + + +IPFamilyType + + +
    +(Optional) +

    IPFamily specifies the IP family to be used by the NGINX. +Default is “dual”, meaning the server will use both IPv4 and IPv6.

    +
    +telemetry
    + + +Telemetry + + +
    +(Optional) +

    Telemetry specifies the OpenTelemetry configuration.

    +
    +rewriteClientIP
    + + +RewriteClientIP + + +
    +(Optional) +

    RewriteClientIP defines configuration for rewriting the client IP to the original client’s IP.

    +
    +logging
    + + +NginxLogging + + +
    +(Optional) +

    Logging defines logging related settings for NGINX.

    +
    +disableHTTP2
    + +bool + +
    +(Optional) +

    DisableHTTP2 defines if http2 should be disabled for all servers. +If not specified, or set to false, http2 will be enabled for all servers.

    +
    +

    RewriteClientIP + +

    +

    +(Appears on: +NginxProxySpec) +

    +

    +

    RewriteClientIP specifies the configuration for rewriting the client’s IP address.

    +

    + + + + + + + + + + + + + + + + + + + + + +
    FieldDescription
    +mode
    + + +RewriteClientIPModeType + + +
    +(Optional) +

    Mode defines how NGINX will rewrite the client’s IP address. +There are two possible modes: +- ProxyProtocol: NGINX will rewrite the client’s IP using the PROXY protocol header. +- XForwardedFor: NGINX will rewrite the client’s IP using the X-Forwarded-For header. +Sets NGINX directive real_ip_header: https://nginx.org/en/docs/http/ngx_http_realip_module.html#real_ip_header

    +
    +setIPRecursively
    + +bool + +
    +(Optional) +

    SetIPRecursively configures whether recursive search is used when selecting the client’s address from +the X-Forwarded-For header. It is used in conjunction with TrustedAddresses. +If enabled, NGINX will recurse on the values in X-Forwarded-Header from the end of array +to start of array and select the first untrusted IP. +For example, if X-Forwarded-For is [11.11.11.11, 22.22.22.22, 55.55.55.1], +and TrustedAddresses is set to 55.55.55.132, NGINX will rewrite the client IP to 22.22.22.22. +If disabled, NGINX will select the IP at the end of the array. +In the previous example, 55.55.55.1 would be selected. +Sets NGINX directive real_ip_recursive: https://nginx.org/en/docs/http/ngx_http_realip_module.html#real_ip_recursive

    +
    +trustedAddresses
    + + +[]Address + + +
    +(Optional) +

    TrustedAddresses specifies the addresses that are trusted to send correct client IP information. +If a request comes from a trusted address, NGINX will rewrite the client IP information, +and forward it to the backend in the X-Forwarded-For* and X-Real-IP headers. +If the request does not come from a trusted address, NGINX will not rewrite the client IP information. +To trust all addresses (not recommended for production), set to 0.0.0.0/0. +If no addresses are provided, NGINX will not rewrite the client IP information. +Sets NGINX directive set_real_ip_from: https://nginx.org/en/docs/http/ngx_http_realip_module.html#set_real_ip_from +This field is required if mode is set.

    +
    +

    RewriteClientIPModeType +(string alias)

    +

    +

    +(Appears on: +RewriteClientIP) +

    +

    +

    RewriteClientIPModeType defines how NGINX Gateway Fabric will determine the client’s original IP address.

    +

    + + + + + + + + + + + + +
    ValueDescription

    "ProxyProtocol"

    RewriteClientIPModeProxyProtocol configures NGINX to accept PROXY protocol and +set the client’s IP address to the IP address in the PROXY protocol header. +Sets the proxy_protocol parameter on the listen directive of all servers and sets real_ip_header +to proxy_protocol: https://nginx.org/en/docs/http/ngx_http_realip_module.html#real_ip_header.

    +

    "XForwardedFor"

    RewriteClientIPModeXForwardedFor configures NGINX to set the client’s IP address to the +IP address in the X-Forwarded-For HTTP header. +https://nginx.org/en/docs/http/ngx_http_realip_module.html#real_ip_header.

    +
    +

    Telemetry + +

    +

    +(Appears on: +NginxProxySpec) +

    +

    +

    Telemetry specifies the OpenTelemetry configuration.

    +

    + + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldDescription
    +disabledFeatures
    + + +[]DisableTelemetryFeature + + +
    +(Optional) +

    DisabledFeatures specifies OpenTelemetry features to be disabled.

    +
    +exporter
    + + +TelemetryExporter + + +
    +(Optional) +

    Exporter specifies OpenTelemetry export parameters.

    +
    +serviceName
    + +string + +
    +(Optional) +

    ServiceName is the “service.name” attribute of the OpenTelemetry resource. +Default is ‘ngf::’. If a value is provided by the user, +then the default becomes a prefix to that value.

    +
    +spanAttributes
    + + +[]SpanAttribute + + +
    +(Optional) +

    SpanAttributes are custom key/value attributes that are added to each span.

    +
    +

    TelemetryExporter + +

    +

    +(Appears on: +Telemetry) +

    +

    +

    TelemetryExporter specifies OpenTelemetry export parameters.

    +

    + + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldDescription
    +interval
    + + +Duration + + +
    +(Optional) +

    Interval is the maximum interval between two exports. +Default: https://nginx.org/en/docs/ngx_otel_module.html#otel_exporter

    +
    +batchSize
    + +int32 + +
    +(Optional) +

    BatchSize is the maximum number of spans to be sent in one batch per worker. +Default: https://nginx.org/en/docs/ngx_otel_module.html#otel_exporter

    +
    +batchCount
    + +int32 + +
    +(Optional) +

    BatchCount is the number of pending batches per worker, spans exceeding the limit are dropped. +Default: https://nginx.org/en/docs/ngx_otel_module.html#otel_exporter

    +
    +endpoint
    + +string + +
    +(Optional) +

    Endpoint is the address of OTLP/gRPC endpoint that will accept telemetry data. +Format: alphanumeric hostname with optional http scheme and optional port.

    +
    +

    Generated with gen-crd-api-reference-docs

    diff --git a/tests/suite/manifests/tracing/nginxproxy.yaml b/tests/suite/manifests/tracing/nginxproxy.yaml index ed1f621047..f4876eb186 100644 --- a/tests/suite/manifests/tracing/nginxproxy.yaml +++ b/tests/suite/manifests/tracing/nginxproxy.yaml @@ -1,4 +1,4 @@ -apiVersion: gateway.nginx.org/v1alpha1 +apiVersion: gateway.nginx.org/v1alpha2 kind: NginxProxy metadata: name: nginx-proxy