From cbf449da7f0b735a279fe71d90fc570a0706f2e5 Mon Sep 17 00:00:00 2001 From: Saylor Berman Date: Mon, 8 Apr 2024 09:04:48 -0600 Subject: [PATCH 1/5] Add NginxProxy CRD Problem: Users want to be able to configure global Gateway settings, such as the Otel tracing exporter, for all Gateways in a Class. Solution: Add the NginxProxy CRD, which provides a way to configure these settings. Note: this PR contains the CRD only. A subsequent PR will add the implementation. --- apis/v1alpha1/clientsettingspolicy_types.go | 7 - apis/v1alpha1/nginxproxy_types.go | 81 ++++++++++ apis/v1alpha1/register.go | 2 + apis/v1alpha1/shared_types.go | 8 + apis/v1alpha1/zz_generated.deepcopy.go | 140 ++++++++++++++++++ .../bases/gateway.nginx.org_nginxproxies.yaml | 102 +++++++++++++ 6 files changed, 333 insertions(+), 7 deletions(-) create mode 100644 apis/v1alpha1/nginxproxy_types.go create mode 100644 apis/v1alpha1/shared_types.go create mode 100644 config/crd/bases/gateway.nginx.org_nginxproxies.yaml diff --git a/apis/v1alpha1/clientsettingspolicy_types.go b/apis/v1alpha1/clientsettingspolicy_types.go index b2a783c4ec..370a6e2287 100644 --- a/apis/v1alpha1/clientsettingspolicy_types.go +++ b/apis/v1alpha1/clientsettingspolicy_types.go @@ -114,13 +114,6 @@ type ClientKeepAliveTimeout struct { Header *Duration `json:"header,omitempty"` } -// Duration is a string value representing a duration in time. -// Duration can be specified in milliseconds (ms) or seconds (s) A value without a suffix is seconds. -// Examples: 120s, 50ms. -// -// +kubebuilder:validation:Pattern=`^\d{1,4}(ms|s)?$` -type Duration string - // Size is a string value representing a size. Size can be specified in bytes, kilobytes (k), megabytes (m), // or gigabytes (g). // Examples: 1024, 8k, 1m. diff --git a/apis/v1alpha1/nginxproxy_types.go b/apis/v1alpha1/nginxproxy_types.go new file mode 100644 index 0000000000..fd32483092 --- /dev/null +++ b/apis/v1alpha1/nginxproxy_types.go @@ -0,0 +1,81 @@ +package v1alpha1 + +import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + +// +kubebuilder:object:root=true +// +kubebuilder:storageversion +// +kubebuilder:resource:categories=nginx-gateway-fabric,scope=Cluster +// +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. +type NginxProxy struct { //nolint:govet // standard field alignment, don't change it + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Spec defines the desired state of the NginxProxy. + Spec NginxProxySpec `json:"spec"` +} + +// +kubebuilder:object:root=true + +// NginxProxyList contains a list of NginxProxies. +type NginxProxyList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []NginxProxy `json:"items"` +} + +// NginxProxySpec defines the desired state of the NginxProxy. +type NginxProxySpec struct { + // Telemetry specifies the OpenTelemetry configuration. + // + // +optional + Telemetry *Telemetry `json:"telemetry,omitempty"` +} + +// Telemetry specifies the OpenTelemetry configuration. +type Telemetry struct { + // Exporter specifies OpenTelemetry export parameters. + // + // +optional + Exporter *TelemetryExporter `json:"exporter,omitempty"` + + // 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. + // + // +optional + // +kubebuilder:validation:MaxLength=127 + ServiceName *string `json:"serviceName,omitempty"` + + // SpanAttributes are custom key/value attributes that are added to each span. + // + // +optional + // +kubebuilder:validation:MaxProperties=64 + SpanAttributes map[string]string `json:"spanAttributes,omitempty"` +} + +// TelemetryExporter specifies OpenTelemetry export parameters. +type TelemetryExporter struct { + // Interval is the maximum interval between two exports, by default is 5 seconds. + // + // +optional + Interval *Duration `json:"interval,omitempty"` + + // BatchSize is the maximum number of spans to be sent in one batch per worker, by default is 512. + // + // +optional + // +kubebuilder:validation:Minimum=0 + BatchSize *int32 `json:"batchSize,omitempty"` + + // BatchCount is the number of pending batches per worker, spans exceeding the limit are dropped, + // by default is 4. + // + // +optional + // +kubebuilder:validation:Minimum=0 + BatchCount *int32 `json:"batchCount,omitempty"` + + // Endpoint is the address of OTLP/gRPC endpoint that will accept telemetry data. + Endpoint string `json:"endpoint"` +} diff --git a/apis/v1alpha1/register.go b/apis/v1alpha1/register.go index 16947b8a6d..ada0bc7a06 100644 --- a/apis/v1alpha1/register.go +++ b/apis/v1alpha1/register.go @@ -34,6 +34,8 @@ func addKnownTypes(scheme *runtime.Scheme) error { scheme.AddKnownTypes(SchemeGroupVersion, &NginxGateway{}, &NginxGatewayList{}, + &NginxProxy{}, + &NginxProxyList{}, &ClientSettingsPolicy{}, &ClientSettingsPolicyList{}, ) diff --git a/apis/v1alpha1/shared_types.go b/apis/v1alpha1/shared_types.go new file mode 100644 index 0000000000..59f9d13d4d --- /dev/null +++ b/apis/v1alpha1/shared_types.go @@ -0,0 +1,8 @@ +package v1alpha1 + +// Duration is a string value representing a duration in time. +// Duration can be specified in milliseconds (ms) or seconds (s) A value without a suffix is seconds. +// Examples: 120s, 50ms. +// +// +kubebuilder:validation:Pattern=`^\d{1,4}(ms|s)?$` +type Duration string diff --git a/apis/v1alpha1/zz_generated.deepcopy.go b/apis/v1alpha1/zz_generated.deepcopy.go index 18511c38b7..43c6d9293c 100644 --- a/apis/v1alpha1/zz_generated.deepcopy.go +++ b/apis/v1alpha1/zz_generated.deepcopy.go @@ -294,3 +294,143 @@ func (in *NginxGatewayStatus) DeepCopy() *NginxGatewayStatus { 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.Telemetry != nil { + in, out := &in.Telemetry, &out.Telemetry + *out = new(Telemetry) + (*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 *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(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// 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 +} diff --git a/config/crd/bases/gateway.nginx.org_nginxproxies.yaml b/config/crd/bases/gateway.nginx.org_nginxproxies.yaml new file mode 100644 index 0000000000..14b4b4e51b --- /dev/null +++ b/config/crd/bases/gateway.nginx.org_nginxproxies.yaml @@ -0,0 +1,102 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: nginxproxies.gateway.nginx.org +spec: + group: gateway.nginx.org + names: + categories: + - nginx-gateway-fabric + kind: NginxProxy + listKind: NginxProxyList + plural: nginxproxies + singular: nginxproxy + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + 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. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of the NginxProxy. + properties: + telemetry: + description: Telemetry specifies the OpenTelemetry configuration. + properties: + exporter: + description: Exporter specifies OpenTelemetry export parameters. + properties: + batchCount: + description: |- + BatchCount is the number of pending batches per worker, spans exceeding the limit are dropped, + by default is 4. + format: int32 + minimum: 0 + type: integer + batchSize: + description: BatchSize is the maximum number of spans to be + sent in one batch per worker, by default is 512. + format: int32 + minimum: 0 + type: integer + endpoint: + description: Endpoint is the address of OTLP/gRPC endpoint + that will accept telemetry data. + type: string + interval: + description: Interval is the maximum interval between two + exports, by default is 5 seconds. + pattern: ^\d{1,4}(ms|s)?$ + type: string + required: + - endpoint + type: object + serviceName: + description: |- + 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. + maxLength: 127 + type: string + spanAttributes: + additionalProperties: + type: string + description: SpanAttributes are custom key/value attributes that + are added to each span. + maxProperties: 64 + type: object + type: object + type: object + required: + - spec + type: object + served: true + storage: true + subresources: {} From 9f285d0970a8646b10a225a51c938daffe61839a Mon Sep 17 00:00:00 2001 From: Saylor Berman Date: Fri, 12 Apr 2024 14:34:42 -0600 Subject: [PATCH 2/5] Add default links; validation --- apis/v1alpha1/nginxproxy_types.go | 20 ++++++++++++++----- apis/v1alpha1/zz_generated.deepcopy.go | 2 +- .../bases/gateway.nginx.org_nginxproxies.yaml | 18 +++++++++++------ 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/apis/v1alpha1/nginxproxy_types.go b/apis/v1alpha1/nginxproxy_types.go index fd32483092..ee486f1a2a 100644 --- a/apis/v1alpha1/nginxproxy_types.go +++ b/apis/v1alpha1/nginxproxy_types.go @@ -53,29 +53,39 @@ type Telemetry struct { // // +optional // +kubebuilder:validation:MaxProperties=64 - SpanAttributes map[string]string `json:"spanAttributes,omitempty"` + SpanAttributes map[string]AttributeValue `json:"spanAttributes,omitempty"` } // TelemetryExporter specifies OpenTelemetry export parameters. type TelemetryExporter struct { - // Interval is the maximum interval between two exports, by default is 5 seconds. + // Interval is the maximum interval between two exports. + // Default: https://nginx.org/en/docs/ngx_otel_module.html#otel_exporter // // +optional Interval *Duration `json:"interval,omitempty"` - // BatchSize is the maximum number of spans to be sent in one batch per worker, by default is 512. + // 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 // // +optional // +kubebuilder:validation:Minimum=0 BatchSize *int32 `json:"batchSize,omitempty"` - // BatchCount is the number of pending batches per worker, spans exceeding the limit are dropped, - // by default is 4. + // 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 // // +optional // +kubebuilder:validation:Minimum=0 BatchCount *int32 `json:"batchCount,omitempty"` // Endpoint is the address of OTLP/gRPC endpoint that will accept telemetry data. + // + //nolint:lll + // +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"` } + +// AttributeValue is a value paired with a key and attached to a tracing span. +// +// +kubebuilder:validation:MaxLength=255 +type AttributeValue string diff --git a/apis/v1alpha1/zz_generated.deepcopy.go b/apis/v1alpha1/zz_generated.deepcopy.go index 43c6d9293c..fc49acad8c 100644 --- a/apis/v1alpha1/zz_generated.deepcopy.go +++ b/apis/v1alpha1/zz_generated.deepcopy.go @@ -388,7 +388,7 @@ func (in *Telemetry) DeepCopyInto(out *Telemetry) { } if in.SpanAttributes != nil { in, out := &in.SpanAttributes, &out.SpanAttributes - *out = make(map[string]string, len(*in)) + *out = make(map[string]AttributeValue, len(*in)) for key, val := range *in { (*out)[key] = val } diff --git a/config/crd/bases/gateway.nginx.org_nginxproxies.yaml b/config/crd/bases/gateway.nginx.org_nginxproxies.yaml index 14b4b4e51b..e4863fc70e 100644 --- a/config/crd/bases/gateway.nginx.org_nginxproxies.yaml +++ b/config/crd/bases/gateway.nginx.org_nginxproxies.yaml @@ -55,24 +55,27 @@ spec: properties: batchCount: description: |- - BatchCount is the number of pending batches per worker, spans exceeding the limit are dropped, - by default is 4. + 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 format: int32 minimum: 0 type: integer batchSize: - description: BatchSize is the maximum number of spans to be - sent in one batch per worker, by default is 512. + description: |- + 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 format: int32 minimum: 0 type: integer endpoint: description: Endpoint is the address of OTLP/gRPC endpoint that will accept telemetry data. + 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})?$ type: string interval: - description: Interval is the maximum interval between two - exports, by default is 5 seconds. + description: |- + Interval is the maximum interval between two exports. + Default: https://nginx.org/en/docs/ngx_otel_module.html#otel_exporter pattern: ^\d{1,4}(ms|s)?$ type: string required: @@ -87,6 +90,9 @@ spec: type: string spanAttributes: additionalProperties: + description: AttributeValue is a value paired with a key and + attached to a tracing span. + maxLength: 255 type: string description: SpanAttributes are custom key/value attributes that are added to each span. From e845bf0cd7ea0c8797f02ba9815fc95ec0215782 Mon Sep 17 00:00:00 2001 From: Saylor Berman Date: Mon, 15 Apr 2024 14:58:24 -0600 Subject: [PATCH 3/5] Update validation and attribute structure --- apis/v1alpha1/nginxproxy_types.go | 24 ++++++++++---- apis/v1alpha1/zz_generated.deepcopy.go | 21 +++++++++--- .../bases/gateway.nginx.org_nginxproxies.yaml | 33 ++++++++++++++----- 3 files changed, 59 insertions(+), 19 deletions(-) diff --git a/apis/v1alpha1/nginxproxy_types.go b/apis/v1alpha1/nginxproxy_types.go index ee486f1a2a..50dd9d46fe 100644 --- a/apis/v1alpha1/nginxproxy_types.go +++ b/apis/v1alpha1/nginxproxy_types.go @@ -47,13 +47,14 @@ type Telemetry struct { // // +optional // +kubebuilder:validation:MaxLength=127 + // +kubebuilder:validation:Pattern=`^[a-zA-Z0-9_-]+$` ServiceName *string `json:"serviceName,omitempty"` // SpanAttributes are custom key/value attributes that are added to each span. // // +optional - // +kubebuilder:validation:MaxProperties=64 - SpanAttributes map[string]AttributeValue `json:"spanAttributes,omitempty"` + // +kubebuilder:validation:MaxItems=64 + SpanAttributes []SpanAttribute `json:"spanAttributes,omitempty"` } // TelemetryExporter specifies OpenTelemetry export parameters. @@ -79,13 +80,24 @@ type TelemetryExporter struct { BatchCount *int32 `json:"batchCount,omitempty"` // Endpoint is the address of OTLP/gRPC endpoint that will accept telemetry data. + // Format: alphanumeric hostname with optional http scheme and optional port. // //nolint:lll // +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"` } -// AttributeValue is a value paired with a key and attached to a tracing span. -// -// +kubebuilder:validation:MaxLength=255 -type AttributeValue string +// SpanAttribute is a key value pair to be added to a tracing span. +type SpanAttribute struct { + // Key is the key for a span attribute. + // + // +kubebuilder:validation:MaxLength=255 + // +kubebuilder:validation:Pattern=`^[a-zA-Z0-9_-]+$` + Key string `json:"key"` + + // Value is the value for a span attribute. + // + // +kubebuilder:validation:MaxLength=255 + // +kubebuilder:validation:Pattern=`^[a-zA-Z0-9_-]+$` + Value string `json:"value"` +} diff --git a/apis/v1alpha1/zz_generated.deepcopy.go b/apis/v1alpha1/zz_generated.deepcopy.go index fc49acad8c..2304c2941a 100644 --- a/apis/v1alpha1/zz_generated.deepcopy.go +++ b/apis/v1alpha1/zz_generated.deepcopy.go @@ -373,6 +373,21 @@ func (in *NginxProxySpec) DeepCopy() *NginxProxySpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SpanAttribute) DeepCopyInto(out *SpanAttribute) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SpanAttribute. +func (in *SpanAttribute) DeepCopy() *SpanAttribute { + if in == nil { + return nil + } + out := new(SpanAttribute) + 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 @@ -388,10 +403,8 @@ func (in *Telemetry) DeepCopyInto(out *Telemetry) { } if in.SpanAttributes != nil { in, out := &in.SpanAttributes, &out.SpanAttributes - *out = make(map[string]AttributeValue, len(*in)) - for key, val := range *in { - (*out)[key] = val - } + *out = make([]SpanAttribute, len(*in)) + copy(*out, *in) } } diff --git a/config/crd/bases/gateway.nginx.org_nginxproxies.yaml b/config/crd/bases/gateway.nginx.org_nginxproxies.yaml index e4863fc70e..a5da714429 100644 --- a/config/crd/bases/gateway.nginx.org_nginxproxies.yaml +++ b/config/crd/bases/gateway.nginx.org_nginxproxies.yaml @@ -68,8 +68,9 @@ spec: minimum: 0 type: integer endpoint: - description: Endpoint is the address of OTLP/gRPC endpoint - that will accept telemetry data. + description: |- + Endpoint is the address of OTLP/gRPC endpoint that will accept telemetry data. + Format: alphanumeric hostname with optional http scheme and optional port. 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})?$ type: string interval: @@ -87,17 +88,31 @@ spec: Default is 'ngf::'. If a value is provided by the user, then the default becomes a prefix to that value. maxLength: 127 + pattern: ^[a-zA-Z0-9_-]+$ type: string spanAttributes: - additionalProperties: - description: AttributeValue is a value paired with a key and - attached to a tracing span. - maxLength: 255 - type: string description: SpanAttributes are custom key/value attributes that are added to each span. - maxProperties: 64 - type: object + items: + description: SpanAttribute is a key value pair to be added to + a tracing span. + properties: + key: + description: Key is the key for a span attribute. + maxLength: 255 + pattern: ^[a-zA-Z0-9_-]+$ + type: string + value: + description: Value is the value for a span attribute. + maxLength: 255 + pattern: ^[a-zA-Z0-9_-]+$ + type: string + required: + - key + - value + type: object + maxItems: 64 + type: array type: object type: object required: From bb949471a429e7effa75d6e64cfba7d4ed9c0c42 Mon Sep 17 00:00:00 2001 From: Saylor Berman Date: Mon, 15 Apr 2024 15:01:36 -0600 Subject: [PATCH 4/5] Require minLength for attributes --- apis/v1alpha1/nginxproxy_types.go | 2 ++ config/crd/bases/gateway.nginx.org_nginxproxies.yaml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/apis/v1alpha1/nginxproxy_types.go b/apis/v1alpha1/nginxproxy_types.go index 50dd9d46fe..7708da8e7c 100644 --- a/apis/v1alpha1/nginxproxy_types.go +++ b/apis/v1alpha1/nginxproxy_types.go @@ -91,12 +91,14 @@ type TelemetryExporter struct { type SpanAttribute struct { // Key is the key for a span attribute. // + // +kubebuilder:validation:MinLength=1 // +kubebuilder:validation:MaxLength=255 // +kubebuilder:validation:Pattern=`^[a-zA-Z0-9_-]+$` Key string `json:"key"` // Value is the value for a span attribute. // + // +kubebuilder:validation:MinLength=1 // +kubebuilder:validation:MaxLength=255 // +kubebuilder:validation:Pattern=`^[a-zA-Z0-9_-]+$` Value string `json:"value"` diff --git a/config/crd/bases/gateway.nginx.org_nginxproxies.yaml b/config/crd/bases/gateway.nginx.org_nginxproxies.yaml index a5da714429..b710fbaa38 100644 --- a/config/crd/bases/gateway.nginx.org_nginxproxies.yaml +++ b/config/crd/bases/gateway.nginx.org_nginxproxies.yaml @@ -100,11 +100,13 @@ spec: key: description: Key is the key for a span attribute. maxLength: 255 + minLength: 1 pattern: ^[a-zA-Z0-9_-]+$ type: string value: description: Value is the value for a span attribute. maxLength: 255 + minLength: 1 pattern: ^[a-zA-Z0-9_-]+$ type: string required: From 574d3853ee6ae206d35592ea8a3772a6f102702f Mon Sep 17 00:00:00 2001 From: Saylor Berman Date: Mon, 15 Apr 2024 15:04:38 -0600 Subject: [PATCH 5/5] List type fields --- apis/v1alpha1/nginxproxy_types.go | 2 ++ config/crd/bases/gateway.nginx.org_nginxproxies.yaml | 3 +++ 2 files changed, 5 insertions(+) diff --git a/apis/v1alpha1/nginxproxy_types.go b/apis/v1alpha1/nginxproxy_types.go index 7708da8e7c..20a870b9d8 100644 --- a/apis/v1alpha1/nginxproxy_types.go +++ b/apis/v1alpha1/nginxproxy_types.go @@ -53,6 +53,8 @@ type Telemetry struct { // SpanAttributes are custom key/value attributes that are added to each span. // // +optional + // +listType=map + // +listMapKey=key // +kubebuilder:validation:MaxItems=64 SpanAttributes []SpanAttribute `json:"spanAttributes,omitempty"` } diff --git a/config/crd/bases/gateway.nginx.org_nginxproxies.yaml b/config/crd/bases/gateway.nginx.org_nginxproxies.yaml index b710fbaa38..6119184d92 100644 --- a/config/crd/bases/gateway.nginx.org_nginxproxies.yaml +++ b/config/crd/bases/gateway.nginx.org_nginxproxies.yaml @@ -115,6 +115,9 @@ spec: type: object maxItems: 64 type: array + x-kubernetes-list-map-keys: + - key + x-kubernetes-list-type: map type: object type: object required: