diff --git a/apis/v1alpha1/nginxproxy_types.go b/apis/v1alpha1/nginxproxy_types.go
index ce3a7734c5..90d7fdecb7 100644
--- a/apis/v1alpha1/nginxproxy_types.go
+++ b/apis/v1alpha1/nginxproxy_types.go
@@ -45,6 +45,10 @@ type NginxProxySpec struct {
// +optional
//nolint:lll
RewriteClientIP *RewriteClientIP `json:"rewriteClientIP,omitempty"`
+ // Logging defines logging related settings for NGINX.
+ //
+ // +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.
//
@@ -202,3 +206,46 @@ const (
// HostnameAddressType specifies that the address is a Hostname.
HostnameAddressType AddressType = "Hostname"
)
+
+// NginxLogging defines logging related settings for NGINX.
+type NginxLogging struct {
+ // 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
+ //
+ // +optional
+ // +kubebuilder:default=info
+ ErrorLevel *NginxErrorLogLevel `json:"errorlevel,omitempty"`
+}
+
+// NginxErrorLogLevel type defines the log level of error logs for NGINX.
+//
+// +kubebuilder:validation:Enum=debug;info;notice;warn;error;crit;alert;emerg
+type NginxErrorLogLevel string
+
+const (
+ // NginxLogLevelDebug is the debug level for NGINX error logs.
+ NginxLogLevelDebug NginxErrorLogLevel = "debug"
+
+ // NginxLogLevelInfo is the info level for NGINX error logs.
+ NginxLogLevelInfo NginxErrorLogLevel = "info"
+
+ // NginxLogLevelNotice is the notice level for NGINX error logs.
+ NginxLogLevelNotice NginxErrorLogLevel = "notice"
+
+ // NginxLogLevelWarn is the warn level for NGINX error logs.
+ NginxLogLevelWarn NginxErrorLogLevel = "warn"
+
+ // NginxLogLevelError is the error level for NGINX error logs.
+ NginxLogLevelError NginxErrorLogLevel = "error"
+
+ // NginxLogLevelCrit is the crit level for NGINX error logs.
+ NginxLogLevelCrit NginxErrorLogLevel = "crit"
+
+ // NginxLogLevelAlert is the alert level for NGINX error logs.
+ NginxLogLevelAlert NginxErrorLogLevel = "alert"
+
+ // NginxLogLevelEmerg is the emerg level for NGINX error logs.
+ NginxLogLevelEmerg NginxErrorLogLevel = "emerg"
+)
diff --git a/apis/v1alpha1/zz_generated.deepcopy.go b/apis/v1alpha1/zz_generated.deepcopy.go
index bffbb7dfdb..b54f857c1a 100644
--- a/apis/v1alpha1/zz_generated.deepcopy.go
+++ b/apis/v1alpha1/zz_generated.deepcopy.go
@@ -311,6 +311,26 @@ 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
@@ -387,6 +407,11 @@ func (in *NginxProxySpec) DeepCopyInto(out *NginxProxySpec) {
*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.
diff --git a/build/Dockerfile.nginx b/build/Dockerfile.nginx
index 1e375bfe9e..c8fafa3071 100644
--- a/build/Dockerfile.nginx
+++ b/build/Dockerfile.nginx
@@ -7,8 +7,10 @@ ARG BUILD_AGENT
RUN apk add --no-cache libcap \
&& mkdir -p /var/lib/nginx /usr/lib/nginx/modules \
- && setcap 'cap_net_bind_service=+ep' /usr/sbin/nginx \
- && setcap -v 'cap_net_bind_service=+ep' /usr/sbin/nginx \
+ && setcap 'cap_net_bind_service=+ep' /usr/sbin/nginx \
+ && setcap -v 'cap_net_bind_service=+ep' /usr/sbin/nginx \
+ && setcap 'cap_net_bind_service=+ep' /usr/sbin/nginx-debug \
+ && setcap -v 'cap_net_bind_service=+ep' /usr/sbin/nginx-debug \
&& apk del libcap
COPY ${NJS_DIR}/httpmatches.js /usr/lib/nginx/modules/njs/httpmatches.js
@@ -22,4 +24,4 @@ LABEL org.nginx.ngf.image.build.agent="${BUILD_AGENT}"
USER 101:1001
-CMD ["sh", "-c", "rm -rf /var/run/nginx/*.sock && /docker-entrypoint.sh nginx -g 'daemon off;'"]
+CMD ["sh", "-c", "rm -rf /var/run/nginx/*.sock && nginx -g 'daemon off;'"]
diff --git a/build/Dockerfile.nginxplus b/build/Dockerfile.nginxplus
index f1957f1921..b87d86186e 100644
--- a/build/Dockerfile.nginxplus
+++ b/build/Dockerfile.nginxplus
@@ -20,8 +20,10 @@ RUN --mount=type=secret,id=nginx-repo.crt,dst=/etc/apk/cert.pem,mode=0644 \
&& printf "%s\n" "https://pkgs.nginx.com/plus/${NGINX_PLUS_VERSION}/alpine/v$(grep -E -o '^[0-9]+\.[0-9]+' /etc/alpine-release)/main" >> /etc/apk/repositories \
&& apk add --no-cache nginx-plus nginx-plus-module-njs nginx-plus-module-otel libcap \
&& mkdir -p /var/lib/nginx /usr/lib/nginx/modules \
- && setcap 'cap_net_bind_service=+ep' /usr/sbin/nginx \
- && setcap -v 'cap_net_bind_service=+ep' /usr/sbin/nginx \
+ && setcap 'cap_net_bind_service=+ep' /usr/sbin/nginx \
+ && setcap -v 'cap_net_bind_service=+ep' /usr/sbin/nginx \
+ && setcap 'cap_net_bind_service=+ep' /usr/sbin/nginx-debug \
+ && setcap -v 'cap_net_bind_service=+ep' /usr/sbin/nginx-debug \
&& apk del libcap \
# forward request and error logs to docker log collector
&& ln -sf /dev/stdout /var/log/nginx/access.log \
diff --git a/charts/nginx-gateway-fabric/README.md b/charts/nginx-gateway-fabric/README.md
index 6819034420..cd304962aa 100644
--- a/charts/nginx-gateway-fabric/README.md
+++ b/charts/nginx-gateway-fabric/README.md
@@ -261,6 +261,7 @@ The following table lists the configurable parameters of the NGINX Gateway Fabri
| `metrics.port` | Set the port where the Prometheus metrics are exposed. | int | `9113` |
| `metrics.secure` | Enable serving metrics via https. By default metrics are served via http. Please note that this endpoint will be secured with a self-signed certificate. | bool | `false` |
| `nginx.config` | The configuration for the data plane that is contained in the NginxProxy resource. | object | `{}` |
+| `nginx.debug` | Enable debugging for NGINX. Uses the nginx-debug binary. The NGINX error log level should be set to debug in the NginxProxy resource. | bool | `false` |
| `nginx.extraVolumeMounts` | extraVolumeMounts are the additional volume mounts for the nginx container. | list | `[]` |
| `nginx.image.pullPolicy` | | string | `"Always"` |
| `nginx.image.repository` | The NGINX image to use. | string | `"ghcr.io/nginxinc/nginx-gateway-fabric/nginx"` |
diff --git a/charts/nginx-gateway-fabric/templates/deployment.yaml b/charts/nginx-gateway-fabric/templates/deployment.yaml
index 6ce6240c29..4b0cb431b6 100644
--- a/charts/nginx-gateway-fabric/templates/deployment.yaml
+++ b/charts/nginx-gateway-fabric/templates/deployment.yaml
@@ -131,8 +131,8 @@ spec:
mountPath: /etc/nginx/conf.d
- name: nginx-stream-conf
mountPath: /etc/nginx/stream-conf.d
- - name: module-includes
- mountPath: /etc/nginx/module-includes
+ - name: nginx-main-includes
+ mountPath: /etc/nginx/main-includes
- name: nginx-secrets
mountPath: /etc/nginx/secrets
- name: nginx-run
@@ -170,8 +170,8 @@ spec:
mountPath: /etc/nginx/conf.d
- name: nginx-stream-conf
mountPath: /etc/nginx/stream-conf.d
- - name: module-includes
- mountPath: /etc/nginx/module-includes
+ - name: nginx-main-includes
+ mountPath: /etc/nginx/main-includes
- name: nginx-secrets
mountPath: /etc/nginx/secrets
- name: nginx-run
@@ -183,6 +183,13 @@ spec:
{{- with .Values.nginx.extraVolumeMounts -}}
{{ toYaml . | nindent 8 }}
{{- end }}
+ {{- if .Values.nginx.debug }}
+ command:
+ - "/bin/sh"
+ args:
+ - "-c"
+ - "rm -rf /var/run/nginx/*.sock && nginx-debug -g 'daemon off;'"
+ {{- end }}
terminationGracePeriodSeconds: {{ .Values.terminationGracePeriodSeconds }}
{{- if .Values.affinity }}
affinity:
@@ -206,7 +213,7 @@ spec:
emptyDir: {}
- name: nginx-stream-conf
emptyDir: {}
- - name: module-includes
+ - name: nginx-main-includes
emptyDir: {}
- name: nginx-secrets
emptyDir: {}
diff --git a/charts/nginx-gateway-fabric/values.schema.json b/charts/nginx-gateway-fabric/values.schema.json
index 2081b8c158..9d5068a3a8 100644
--- a/charts/nginx-gateway-fabric/values.schema.json
+++ b/charts/nginx-gateway-fabric/values.schema.json
@@ -73,6 +73,27 @@
"required": [],
"type": "string"
},
+ "logging": {
+ "description": "Logging defines logging related settings for NGINX.",
+ "properties": {
+ "errorlevel": {
+ "enum": [
+ "debug",
+ "info",
+ "notice",
+ "warn",
+ "error",
+ "crit",
+ "alert",
+ "emerg"
+ ],
+ "required": [],
+ "type": "string"
+ }
+ },
+ "required": [],
+ "type": "object"
+ },
"rewriteClientIP": {
"description": "RewriteClientIP defines configuration for rewriting the client IP to the original client's IP.",
"properties": {
@@ -176,6 +197,13 @@
"title": "config",
"type": "object"
},
+ "debug": {
+ "default": false,
+ "description": "Enable debugging for NGINX. Uses the nginx-debug binary. The NGINX error log level should be set to debug in the NginxProxy resource.",
+ "required": [],
+ "title": "debug",
+ "type": "boolean"
+ },
"extraVolumeMounts": {
"description": "extraVolumeMounts are the additional volume mounts for the nginx container.",
"items": {
diff --git a/charts/nginx-gateway-fabric/values.yaml b/charts/nginx-gateway-fabric/values.yaml
index 3029b68915..1fc9aa2f05 100644
--- a/charts/nginx-gateway-fabric/values.yaml
+++ b/charts/nginx-gateway-fabric/values.yaml
@@ -201,10 +201,28 @@ nginx:
# pattern: ^([^"$\\]|\\[^$])*$
# minLength: 1
# maxLength: 255
+ # logging:
+ # type: object
+ # description: Logging defines logging related settings for NGINX.
+ # properties:
+ # errorlevel:
+ # type: string
+ # enum:
+ # - debug
+ # - info
+ # - notice
+ # - warn
+ # - error
+ # - crit
+ # - alert
+ # - emerg
# @schema
# -- The configuration for the data plane that is contained in the NginxProxy resource.
config: {}
+ # -- Enable debugging for NGINX. Uses the nginx-debug binary. The NGINX error log level should be set to debug in the NginxProxy resource.
+ debug: false
+
# Configuration for NGINX Plus usage reporting.
usage:
# -- The namespace/name of the Secret containing the credentials for NGINX Plus usage reporting.
diff --git a/config/crd/bases/gateway.nginx.org_nginxproxies.yaml b/config/crd/bases/gateway.nginx.org_nginxproxies.yaml
index 485b7d0486..c514aa12d8 100644
--- a/config/crd/bases/gateway.nginx.org_nginxproxies.yaml
+++ b/config/crd/bases/gateway.nginx.org_nginxproxies.yaml
@@ -62,6 +62,27 @@ spec:
- ipv4
- ipv6
type: string
+ logging:
+ description: Logging defines logging related settings for NGINX.
+ properties:
+ errorlevel:
+ default: info
+ description: |-
+ 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
+ enum:
+ - debug
+ - info
+ - notice
+ - warn
+ - error
+ - crit
+ - alert
+ - emerg
+ type: string
+ type: object
rewriteClientIP:
description: RewriteClientIP defines configuration for rewriting the
client IP to the original client's IP.
diff --git a/config/tests/static-deployment.yaml b/config/tests/static-deployment.yaml
index bb2fb62765..30f85a9081 100644
--- a/config/tests/static-deployment.yaml
+++ b/config/tests/static-deployment.yaml
@@ -74,8 +74,8 @@ spec:
mountPath: /etc/nginx/conf.d
- name: nginx-stream-conf
mountPath: /etc/nginx/stream-conf.d
- - name: module-includes
- mountPath: /etc/nginx/module-includes
+ - name: nginx-main-includes
+ mountPath: /etc/nginx/main-includes
- name: nginx-secrets
mountPath: /etc/nginx/secrets
- name: nginx-run
@@ -106,8 +106,8 @@ spec:
mountPath: /etc/nginx/conf.d
- name: nginx-stream-conf
mountPath: /etc/nginx/stream-conf.d
- - name: module-includes
- mountPath: /etc/nginx/module-includes
+ - name: nginx-main-includes
+ mountPath: /etc/nginx/main-includes
- name: nginx-secrets
mountPath: /etc/nginx/secrets
- name: nginx-run
@@ -127,7 +127,7 @@ spec:
emptyDir: {}
- name: nginx-stream-conf
emptyDir: {}
- - name: module-includes
+ - name: nginx-main-includes
emptyDir: {}
- name: nginx-secrets
emptyDir: {}
diff --git a/deploy/aws-nlb/deploy.yaml b/deploy/aws-nlb/deploy.yaml
index 49b29bf988..e363b9066a 100644
--- a/deploy/aws-nlb/deploy.yaml
+++ b/deploy/aws-nlb/deploy.yaml
@@ -248,8 +248,8 @@ spec:
name: nginx-conf
- mountPath: /etc/nginx/stream-conf.d
name: nginx-stream-conf
- - mountPath: /etc/nginx/module-includes
- name: module-includes
+ - mountPath: /etc/nginx/main-includes
+ name: nginx-main-includes
- mountPath: /etc/nginx/secrets
name: nginx-secrets
- mountPath: /var/run/nginx
@@ -280,8 +280,8 @@ spec:
name: nginx-conf
- mountPath: /etc/nginx/stream-conf.d
name: nginx-stream-conf
- - mountPath: /etc/nginx/module-includes
- name: module-includes
+ - mountPath: /etc/nginx/main-includes
+ name: nginx-main-includes
- mountPath: /etc/nginx/secrets
name: nginx-secrets
- mountPath: /var/run/nginx
@@ -302,7 +302,7 @@ spec:
- emptyDir: {}
name: nginx-stream-conf
- emptyDir: {}
- name: module-includes
+ name: nginx-main-includes
- emptyDir: {}
name: nginx-secrets
- emptyDir: {}
diff --git a/deploy/azure/deploy.yaml b/deploy/azure/deploy.yaml
index 968c1a2926..faacc0332e 100644
--- a/deploy/azure/deploy.yaml
+++ b/deploy/azure/deploy.yaml
@@ -245,8 +245,8 @@ spec:
name: nginx-conf
- mountPath: /etc/nginx/stream-conf.d
name: nginx-stream-conf
- - mountPath: /etc/nginx/module-includes
- name: module-includes
+ - mountPath: /etc/nginx/main-includes
+ name: nginx-main-includes
- mountPath: /etc/nginx/secrets
name: nginx-secrets
- mountPath: /var/run/nginx
@@ -277,8 +277,8 @@ spec:
name: nginx-conf
- mountPath: /etc/nginx/stream-conf.d
name: nginx-stream-conf
- - mountPath: /etc/nginx/module-includes
- name: module-includes
+ - mountPath: /etc/nginx/main-includes
+ name: nginx-main-includes
- mountPath: /etc/nginx/secrets
name: nginx-secrets
- mountPath: /var/run/nginx
@@ -301,7 +301,7 @@ spec:
- emptyDir: {}
name: nginx-stream-conf
- emptyDir: {}
- name: module-includes
+ name: nginx-main-includes
- emptyDir: {}
name: nginx-secrets
- emptyDir: {}
diff --git a/deploy/crds.yaml b/deploy/crds.yaml
index 4aa1b7e2d6..3ee45ad868 100644
--- a/deploy/crds.yaml
+++ b/deploy/crds.yaml
@@ -647,6 +647,27 @@ spec:
- ipv4
- ipv6
type: string
+ logging:
+ description: Logging defines logging related settings for NGINX.
+ properties:
+ errorlevel:
+ default: info
+ description: |-
+ 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
+ enum:
+ - debug
+ - info
+ - notice
+ - warn
+ - error
+ - crit
+ - alert
+ - emerg
+ type: string
+ type: object
rewriteClientIP:
description: RewriteClientIP defines configuration for rewriting the
client IP to the original client's IP.
diff --git a/deploy/default/deploy.yaml b/deploy/default/deploy.yaml
index 6245a2bbc7..97e03edb4c 100644
--- a/deploy/default/deploy.yaml
+++ b/deploy/default/deploy.yaml
@@ -245,8 +245,8 @@ spec:
name: nginx-conf
- mountPath: /etc/nginx/stream-conf.d
name: nginx-stream-conf
- - mountPath: /etc/nginx/module-includes
- name: module-includes
+ - mountPath: /etc/nginx/main-includes
+ name: nginx-main-includes
- mountPath: /etc/nginx/secrets
name: nginx-secrets
- mountPath: /var/run/nginx
@@ -277,8 +277,8 @@ spec:
name: nginx-conf
- mountPath: /etc/nginx/stream-conf.d
name: nginx-stream-conf
- - mountPath: /etc/nginx/module-includes
- name: module-includes
+ - mountPath: /etc/nginx/main-includes
+ name: nginx-main-includes
- mountPath: /etc/nginx/secrets
name: nginx-secrets
- mountPath: /var/run/nginx
@@ -299,7 +299,7 @@ spec:
- emptyDir: {}
name: nginx-stream-conf
- emptyDir: {}
- name: module-includes
+ name: nginx-main-includes
- emptyDir: {}
name: nginx-secrets
- emptyDir: {}
diff --git a/deploy/experimental-nginx-plus/deploy.yaml b/deploy/experimental-nginx-plus/deploy.yaml
index ed9c748e1a..002027468d 100644
--- a/deploy/experimental-nginx-plus/deploy.yaml
+++ b/deploy/experimental-nginx-plus/deploy.yaml
@@ -260,8 +260,8 @@ spec:
name: nginx-conf
- mountPath: /etc/nginx/stream-conf.d
name: nginx-stream-conf
- - mountPath: /etc/nginx/module-includes
- name: module-includes
+ - mountPath: /etc/nginx/main-includes
+ name: nginx-main-includes
- mountPath: /etc/nginx/secrets
name: nginx-secrets
- mountPath: /var/run/nginx
@@ -292,8 +292,8 @@ spec:
name: nginx-conf
- mountPath: /etc/nginx/stream-conf.d
name: nginx-stream-conf
- - mountPath: /etc/nginx/module-includes
- name: module-includes
+ - mountPath: /etc/nginx/main-includes
+ name: nginx-main-includes
- mountPath: /etc/nginx/secrets
name: nginx-secrets
- mountPath: /var/run/nginx
@@ -314,7 +314,7 @@ spec:
- emptyDir: {}
name: nginx-stream-conf
- emptyDir: {}
- name: module-includes
+ name: nginx-main-includes
- emptyDir: {}
name: nginx-secrets
- emptyDir: {}
diff --git a/deploy/experimental/deploy.yaml b/deploy/experimental/deploy.yaml
index 28cc7b6d19..fbb09e917a 100644
--- a/deploy/experimental/deploy.yaml
+++ b/deploy/experimental/deploy.yaml
@@ -251,8 +251,8 @@ spec:
name: nginx-conf
- mountPath: /etc/nginx/stream-conf.d
name: nginx-stream-conf
- - mountPath: /etc/nginx/module-includes
- name: module-includes
+ - mountPath: /etc/nginx/main-includes
+ name: nginx-main-includes
- mountPath: /etc/nginx/secrets
name: nginx-secrets
- mountPath: /var/run/nginx
@@ -283,8 +283,8 @@ spec:
name: nginx-conf
- mountPath: /etc/nginx/stream-conf.d
name: nginx-stream-conf
- - mountPath: /etc/nginx/module-includes
- name: module-includes
+ - mountPath: /etc/nginx/main-includes
+ name: nginx-main-includes
- mountPath: /etc/nginx/secrets
name: nginx-secrets
- mountPath: /var/run/nginx
@@ -305,7 +305,7 @@ spec:
- emptyDir: {}
name: nginx-stream-conf
- emptyDir: {}
- name: module-includes
+ name: nginx-main-includes
- emptyDir: {}
name: nginx-secrets
- emptyDir: {}
diff --git a/deploy/nginx-plus/deploy.yaml b/deploy/nginx-plus/deploy.yaml
index 76249e80c2..33043d74d5 100644
--- a/deploy/nginx-plus/deploy.yaml
+++ b/deploy/nginx-plus/deploy.yaml
@@ -256,8 +256,8 @@ spec:
name: nginx-conf
- mountPath: /etc/nginx/stream-conf.d
name: nginx-stream-conf
- - mountPath: /etc/nginx/module-includes
- name: module-includes
+ - mountPath: /etc/nginx/main-includes
+ name: nginx-main-includes
- mountPath: /etc/nginx/secrets
name: nginx-secrets
- mountPath: /var/run/nginx
@@ -288,8 +288,8 @@ spec:
name: nginx-conf
- mountPath: /etc/nginx/stream-conf.d
name: nginx-stream-conf
- - mountPath: /etc/nginx/module-includes
- name: module-includes
+ - mountPath: /etc/nginx/main-includes
+ name: nginx-main-includes
- mountPath: /etc/nginx/secrets
name: nginx-secrets
- mountPath: /var/run/nginx
@@ -310,7 +310,7 @@ spec:
- emptyDir: {}
name: nginx-stream-conf
- emptyDir: {}
- name: module-includes
+ name: nginx-main-includes
- emptyDir: {}
name: nginx-secrets
- emptyDir: {}
diff --git a/deploy/nodeport/deploy.yaml b/deploy/nodeport/deploy.yaml
index db81fdf259..d45f2c51c2 100644
--- a/deploy/nodeport/deploy.yaml
+++ b/deploy/nodeport/deploy.yaml
@@ -245,8 +245,8 @@ spec:
name: nginx-conf
- mountPath: /etc/nginx/stream-conf.d
name: nginx-stream-conf
- - mountPath: /etc/nginx/module-includes
- name: module-includes
+ - mountPath: /etc/nginx/main-includes
+ name: nginx-main-includes
- mountPath: /etc/nginx/secrets
name: nginx-secrets
- mountPath: /var/run/nginx
@@ -277,8 +277,8 @@ spec:
name: nginx-conf
- mountPath: /etc/nginx/stream-conf.d
name: nginx-stream-conf
- - mountPath: /etc/nginx/module-includes
- name: module-includes
+ - mountPath: /etc/nginx/main-includes
+ name: nginx-main-includes
- mountPath: /etc/nginx/secrets
name: nginx-secrets
- mountPath: /var/run/nginx
@@ -299,7 +299,7 @@ spec:
- emptyDir: {}
name: nginx-stream-conf
- emptyDir: {}
- name: module-includes
+ name: nginx-main-includes
- emptyDir: {}
name: nginx-secrets
- emptyDir: {}
diff --git a/deploy/openshift/deploy.yaml b/deploy/openshift/deploy.yaml
index cb78ce0f39..742441ad18 100644
--- a/deploy/openshift/deploy.yaml
+++ b/deploy/openshift/deploy.yaml
@@ -253,8 +253,8 @@ spec:
name: nginx-conf
- mountPath: /etc/nginx/stream-conf.d
name: nginx-stream-conf
- - mountPath: /etc/nginx/module-includes
- name: module-includes
+ - mountPath: /etc/nginx/main-includes
+ name: nginx-main-includes
- mountPath: /etc/nginx/secrets
name: nginx-secrets
- mountPath: /var/run/nginx
@@ -285,8 +285,8 @@ spec:
name: nginx-conf
- mountPath: /etc/nginx/stream-conf.d
name: nginx-stream-conf
- - mountPath: /etc/nginx/module-includes
- name: module-includes
+ - mountPath: /etc/nginx/main-includes
+ name: nginx-main-includes
- mountPath: /etc/nginx/secrets
name: nginx-secrets
- mountPath: /var/run/nginx
@@ -307,7 +307,7 @@ spec:
- emptyDir: {}
name: nginx-stream-conf
- emptyDir: {}
- name: module-includes
+ name: nginx-main-includes
- emptyDir: {}
name: nginx-secrets
- emptyDir: {}
diff --git a/internal/mode/static/handler_test.go b/internal/mode/static/handler_test.go
index 93ad4e1b61..2b4ee9b537 100644
--- a/internal/mode/static/handler_test.go
+++ b/internal/mode/static/handler_test.go
@@ -155,11 +155,11 @@ var _ = Describe("eventHandler", func() {
handler.HandleEventBatch(context.Background(), ctlrZap.New(), batch)
- dcfg := &dataplane.Configuration{Version: 1}
+ dcfg := dataplane.GetDefaultConfiguration(1)
checkUpsertEventExpectations(e)
- expectReconfig(*dcfg, fakeCfgFiles)
- Expect(helpers.Diff(handler.GetLatestConfiguration(), dcfg)).To(BeEmpty())
+ expectReconfig(dcfg, fakeCfgFiles)
+ Expect(helpers.Diff(handler.GetLatestConfiguration(), &dcfg)).To(BeEmpty())
})
It("should process Delete", func() {
@@ -171,11 +171,11 @@ var _ = Describe("eventHandler", func() {
handler.HandleEventBatch(context.Background(), ctlrZap.New(), batch)
- dcfg := &dataplane.Configuration{Version: 1}
+ dcfg := dataplane.GetDefaultConfiguration(1)
checkDeleteEventExpectations(e)
- expectReconfig(*dcfg, fakeCfgFiles)
- Expect(helpers.Diff(handler.GetLatestConfiguration(), dcfg)).To(BeEmpty())
+ expectReconfig(dcfg, fakeCfgFiles)
+ Expect(helpers.Diff(handler.GetLatestConfiguration(), &dcfg)).To(BeEmpty())
})
})
@@ -194,7 +194,9 @@ var _ = Describe("eventHandler", func() {
checkDeleteEventExpectations(deleteEvent)
handler.HandleEventBatch(context.Background(), ctlrZap.New(), batch)
- Expect(helpers.Diff(handler.GetLatestConfiguration(), &dataplane.Configuration{Version: 2})).To(BeEmpty())
+
+ dcfg := dataplane.GetDefaultConfiguration(2)
+ Expect(helpers.Diff(handler.GetLatestConfiguration(), &dcfg)).To(BeEmpty())
})
})
})
@@ -517,7 +519,9 @@ var _ = Describe("eventHandler", func() {
fakeNginxRuntimeMgr.IsPlusReturns(true)
handler.HandleEventBatch(context.Background(), ctlrZap.New(), batch)
- Expect(helpers.Diff(handler.GetLatestConfiguration(), &dataplane.Configuration{Version: 1})).To(BeEmpty())
+
+ dcfg := dataplane.GetDefaultConfiguration(1)
+ Expect(helpers.Diff(handler.GetLatestConfiguration(), &dcfg)).To(BeEmpty())
Expect(fakeGenerator.GenerateCallCount()).To(Equal(1))
Expect(fakeNginxFileMgr.ReplaceFilesCallCount()).To(Equal(1))
@@ -528,7 +532,9 @@ var _ = Describe("eventHandler", func() {
When("not running NGINX Plus", func() {
It("should not call the NGINX Plus API", func() {
handler.HandleEventBatch(context.Background(), ctlrZap.New(), batch)
- Expect(helpers.Diff(handler.GetLatestConfiguration(), &dataplane.Configuration{Version: 1})).To(BeEmpty())
+
+ dcfg := dataplane.GetDefaultConfiguration(1)
+ Expect(helpers.Diff(handler.GetLatestConfiguration(), &dcfg)).To(BeEmpty())
Expect(fakeGenerator.GenerateCallCount()).To(Equal(1))
Expect(fakeNginxFileMgr.ReplaceFilesCallCount()).To(Equal(1))
@@ -623,7 +629,8 @@ var _ = Describe("eventHandler", func() {
Expect(handler.cfg.nginxConfiguredOnStartChecker.readyCheck(nil)).ToNot(Succeed())
handler.HandleEventBatch(context.Background(), ctlrZap.New(), batch)
- Expect(helpers.Diff(handler.GetLatestConfiguration(), &dataplane.Configuration{Version: 1})).To(BeEmpty())
+ dcfg := dataplane.GetDefaultConfiguration(1)
+ Expect(helpers.Diff(handler.GetLatestConfiguration(), &dcfg)).To(BeEmpty())
Expect(readyChannel).To(BeClosed())
@@ -670,7 +677,8 @@ var _ = Describe("eventHandler", func() {
handler.HandleEventBatch(context.Background(), ctlrZap.New(), batch)
- Expect(helpers.Diff(handler.GetLatestConfiguration(), &dataplane.Configuration{Version: 2})).To(BeEmpty())
+ dcfg := dataplane.GetDefaultConfiguration(2)
+ Expect(helpers.Diff(handler.GetLatestConfiguration(), &dcfg)).To(BeEmpty())
Expect(readyChannel).To(BeClosed())
diff --git a/internal/mode/static/nginx/conf/nginx-plus.conf b/internal/mode/static/nginx/conf/nginx-plus.conf
index 23d97717c9..77526ac8ad 100644
--- a/internal/mode/static/nginx/conf/nginx-plus.conf
+++ b/internal/mode/static/nginx/conf/nginx-plus.conf
@@ -1,10 +1,9 @@
load_module /usr/lib/nginx/modules/ngx_http_js_module.so;
-include /etc/nginx/module-includes/*.conf;
+include /etc/nginx/main-includes/*.conf;
worker_processes auto;
pid /var/run/nginx/nginx.pid;
-error_log stderr info;
events {
worker_connections 1024;
diff --git a/internal/mode/static/nginx/conf/nginx.conf b/internal/mode/static/nginx/conf/nginx.conf
index 2cbc09fa3f..5e13fc8dce 100644
--- a/internal/mode/static/nginx/conf/nginx.conf
+++ b/internal/mode/static/nginx/conf/nginx.conf
@@ -1,10 +1,9 @@
load_module /usr/lib/nginx/modules/ngx_http_js_module.so;
-include /etc/nginx/module-includes/*.conf;
+include /etc/nginx/main-includes/*.conf;
worker_processes auto;
pid /var/run/nginx/nginx.pid;
-error_log stderr info;
events {
worker_connections 1024;
diff --git a/internal/mode/static/nginx/config/generator.go b/internal/mode/static/nginx/config/generator.go
index 6e2e88194f..adc5ffb0ce 100644
--- a/internal/mode/static/nginx/config/generator.go
+++ b/internal/mode/static/nginx/config/generator.go
@@ -23,8 +23,8 @@ const (
// streamFolder is the folder where NGINX Stream configuration files are stored.
streamFolder = configFolder + "/stream-conf.d"
- // modulesIncludesFolder is the folder where the included "load_module" file is stored.
- modulesIncludesFolder = configFolder + "/module-includes"
+ // mainIncludesFolder is the folder where NGINX main context configuration files is stored.
+ mainIncludesFolder = configFolder + "/main-includes"
// secretsFolder is the folder where secrets (like TLS certs/keys) are stored.
secretsFolder = configFolder + "/secrets"
@@ -44,13 +44,13 @@ const (
// httpMatchVarsFile is the path to the http_match pairs configuration file.
httpMatchVarsFile = httpFolder + "/matches.json"
- // loadModulesFile is the path to the file containing any load_module directives.
- loadModulesFile = modulesIncludesFolder + "/load-modules.conf"
+ // mainIncludesConfigFile is the path to the file containing NGINX configuration in the main context.
+ mainIncludesConfigFile = mainIncludesFolder + "/main.conf"
)
// ConfigFolders is a list of folders where NGINX configuration files are stored.
// Volumes here also need to be added to our crossplane ephemeral test container.
-var ConfigFolders = []string{httpFolder, secretsFolder, includesFolder, modulesIncludesFolder, streamFolder}
+var ConfigFolders = []string{httpFolder, secretsFolder, includesFolder, mainIncludesFolder, streamFolder}
// Generator generates NGINX configuration files.
// This interface is used for testing purposes only.
@@ -100,16 +100,12 @@ func (g GeneratorImpl) Generate(conf dataplane.Configuration) []file.File {
observability.NewGenerator(conf.Telemetry),
)
- files = append(files, g.generateHTTPConfig(conf, policyGenerator)...)
-
- files = append(files, generateConfigVersion(conf.Version))
+ files = append(files, g.runExecuteFuncs(conf, policyGenerator)...)
for id, bundle := range conf.CertBundles {
files = append(files, generateCertBundle(id, bundle))
}
- files = append(files, generateLoadModulesConf(conf))
-
return files
}
@@ -142,7 +138,7 @@ func generateCertBundleFileName(id dataplane.CertBundleID) string {
return filepath.Join(secretsFolder, string(id)+".crt")
}
-func (g GeneratorImpl) generateHTTPConfig(
+func (g GeneratorImpl) runExecuteFuncs(
conf dataplane.Configuration,
generator policies.Generator,
) []file.File {
@@ -178,29 +174,7 @@ func (g GeneratorImpl) getExecuteFuncs(generator policies.Generator) []executeFu
g.executeStreamServers,
g.executeStreamUpstreams,
executeStreamMaps,
- }
-}
-
-// generateConfigVersion writes the config version file.
-func generateConfigVersion(configVersion int) file.File {
- c := executeVersion(configVersion)
-
- return file.File{
- Content: c,
- Path: configVersionFile,
- Type: file.TypeRegular,
- }
-}
-
-func generateLoadModulesConf(conf dataplane.Configuration) file.File {
- var c []byte
- if conf.Telemetry.Endpoint != "" {
- c = []byte("load_module modules/ngx_otel_module.so;")
- }
-
- return file.File{
- Content: c,
- Path: loadModulesFile,
- Type: file.TypeRegular,
+ executeVersion,
+ executeMainIncludesConfig,
}
}
diff --git a/internal/mode/static/nginx/config/generator_test.go b/internal/mode/static/nginx/config/generator_test.go
index 4940d72452..ed901af39f 100644
--- a/internal/mode/static/nginx/config/generator_test.go
+++ b/internal/mode/static/nginx/config/generator_test.go
@@ -90,6 +90,9 @@ func TestGenerate(t *testing.T) {
BatchSize: 512,
BatchCount: 4,
},
+ Logging: dataplane.Logging{
+ ErrorLevel: "debug",
+ },
BaseHTTPConfig: dataplane.BaseHTTPConfig{
HTTP2: true,
},
@@ -135,8 +138,11 @@ func TestGenerate(t *testing.T) {
expString := "{}"
g.Expect(string(files[2].Content)).To(Equal(expString))
- g.Expect(files[3].Path).To(Equal("/etc/nginx/module-includes/load-modules.conf"))
- g.Expect(files[3].Content).To(Equal([]byte("load_module modules/ngx_otel_module.so;")))
+ g.Expect(files[3].Path).To(Equal("/etc/nginx/main-includes/main.conf"))
+
+ mainCfg := string(files[3].Content)
+ g.Expect(mainCfg).To(ContainSubstring("load_module modules/ngx_otel_module.so;"))
+ g.Expect(mainCfg).To(ContainSubstring("error_log stderr debug;"))
g.Expect(files[4].Path).To(Equal("/etc/nginx/secrets/test-certbundle.crt"))
certBundle := string(files[4].Content)
diff --git a/internal/mode/static/nginx/config/main_includes.go b/internal/mode/static/nginx/config/main_includes.go
new file mode 100644
index 0000000000..04e3675dfc
--- /dev/null
+++ b/internal/mode/static/nginx/config/main_includes.go
@@ -0,0 +1,19 @@
+package config
+
+import (
+ gotemplate "text/template"
+
+ "github.com/nginxinc/nginx-gateway-fabric/internal/framework/helpers"
+ "github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/state/dataplane"
+)
+
+var mainIncludesTemplate = gotemplate.Must(gotemplate.New("mainIncludes").Parse(mainIncludesTemplateText))
+
+func executeMainIncludesConfig(conf dataplane.Configuration) []executeResult {
+ result := executeResult{
+ dest: mainIncludesConfigFile,
+ data: helpers.MustExecuteTemplate(mainIncludesTemplate, conf),
+ }
+
+ return []executeResult{result}
+}
diff --git a/internal/mode/static/nginx/config/main_includes_template.go b/internal/mode/static/nginx/config/main_includes_template.go
new file mode 100644
index 0000000000..c444b1efe2
--- /dev/null
+++ b/internal/mode/static/nginx/config/main_includes_template.go
@@ -0,0 +1,7 @@
+package config
+
+const mainIncludesTemplateText = `
+{{- if .Telemetry.Endpoint }}load_module modules/ngx_otel_module.so;{{ end -}}
+
+error_log stderr {{ .Logging.ErrorLevel }};
+`
diff --git a/internal/mode/static/nginx/config/main_includes_test.go b/internal/mode/static/nginx/config/main_includes_test.go
new file mode 100644
index 0000000000..b5f26f91fe
--- /dev/null
+++ b/internal/mode/static/nginx/config/main_includes_test.go
@@ -0,0 +1,72 @@
+package config
+
+import (
+ "strings"
+ "testing"
+
+ . "github.com/onsi/gomega"
+
+ "github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/state/dataplane"
+)
+
+func TestExecuteMainIncludesConfig(t *testing.T) {
+ t.Parallel()
+
+ completeConfiguration := dataplane.Configuration{
+ Telemetry: dataplane.Telemetry{
+ Endpoint: "1.2.3.4:123",
+ ServiceName: "ngf:gw-ns:gw-name:my-name",
+ Interval: "5s",
+ BatchSize: 512,
+ BatchCount: 4,
+ },
+ Logging: dataplane.Logging{
+ ErrorLevel: "info",
+ },
+ }
+
+ missingTelemetryEndpoint := dataplane.Configuration{
+ Logging: dataplane.Logging{
+ ErrorLevel: "info",
+ },
+ }
+
+ // Configuration.Logging will always be set, so no need to test if it is missing
+ tests := []struct {
+ name string
+ conf dataplane.Configuration
+ expTelemetryEndpointCount int
+ }{
+ {
+ name: "complete configuration",
+ conf: completeConfiguration,
+ expTelemetryEndpointCount: 1,
+ },
+ {
+ name: "missing telemetry endpoint",
+ conf: missingTelemetryEndpoint,
+ expTelemetryEndpointCount: 0,
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ t.Parallel()
+ g := NewWithT(t)
+
+ res := executeMainIncludesConfig(test.conf)
+
+ g.Expect(res).To(HaveLen(1))
+
+ g.Expect(strings.Count(
+ string(res[0].data),
+ "load_module modules/ngx_otel_module.so;"),
+ ).To(Equal(test.expTelemetryEndpointCount))
+
+ g.Expect(strings.Count(
+ string(res[0].data),
+ "error_log stderr "+test.conf.Logging.ErrorLevel+";",
+ )).To(Equal(1))
+ })
+ }
+}
diff --git a/internal/mode/static/nginx/config/version.go b/internal/mode/static/nginx/config/version.go
index 5baa7f24b8..6e29f36056 100644
--- a/internal/mode/static/nginx/config/version.go
+++ b/internal/mode/static/nginx/config/version.go
@@ -4,10 +4,16 @@ import (
gotemplate "text/template"
"github.com/nginxinc/nginx-gateway-fabric/internal/framework/helpers"
+ "github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/state/dataplane"
)
var versionTemplate = gotemplate.Must(gotemplate.New("version").Parse(versionTemplateText))
-func executeVersion(version int) []byte {
- return helpers.MustExecuteTemplate(versionTemplate, version)
+func executeVersion(conf dataplane.Configuration) []executeResult {
+ result := executeResult{
+ dest: configVersionFile,
+ data: helpers.MustExecuteTemplate(versionTemplate, conf.Version),
+ }
+
+ return []executeResult{result}
}
diff --git a/internal/mode/static/nginx/config/version_test.go b/internal/mode/static/nginx/config/version_test.go
index 176db3dfec..51008e625e 100644
--- a/internal/mode/static/nginx/config/version_test.go
+++ b/internal/mode/static/nginx/config/version_test.go
@@ -5,6 +5,8 @@ import (
"testing"
. "github.com/onsi/gomega"
+
+ "github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/state/dataplane"
)
func TestExecuteVersion(t *testing.T) {
@@ -14,7 +16,11 @@ func TestExecuteVersion(t *testing.T) {
"return 200 42;": 1,
}
- maps := string(executeVersion(42))
+ cfg := dataplane.Configuration{
+ Version: 42,
+ }
+
+ maps := string(executeVersion(cfg)[0].data)
for expSubStr, expCount := range expSubStrings {
g.Expect(expCount).To(Equal(strings.Count(maps, expSubStr)))
}
diff --git a/internal/mode/static/state/dataplane/configuration.go b/internal/mode/static/state/dataplane/configuration.go
index 5c6721f6ed..99fcec3d4f 100644
--- a/internal/mode/static/state/dataplane/configuration.go
+++ b/internal/mode/static/state/dataplane/configuration.go
@@ -15,14 +15,15 @@ import (
ngfAPI "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha1"
"github.com/nginxinc/nginx-gateway-fabric/internal/framework/helpers"
- policies "github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/nginx/config/policies"
+ "github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/nginx/config/policies"
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/state/graph"
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/state/resolver"
)
const (
- wildcardHostname = "~^"
- alpineSSLRootCAPath = "/etc/ssl/cert.pem"
+ wildcardHostname = "~^"
+ alpineSSLRootCAPath = "/etc/ssl/cert.pem"
+ defaultErrorLogLevel = "info"
)
// BuildConfiguration builds the Configuration from the Graph.
@@ -32,37 +33,28 @@ func BuildConfiguration(
serviceResolver resolver.ServiceResolver,
configVersion int,
) Configuration {
- if g.GatewayClass == nil || !g.GatewayClass.Valid {
- return Configuration{Version: configVersion}
- }
-
- if g.Gateway == nil {
- return Configuration{Version: configVersion}
+ if g.GatewayClass == nil || !g.GatewayClass.Valid || g.Gateway == nil {
+ return GetDefaultConfiguration(configVersion)
}
baseHTTPConfig := buildBaseHTTPConfig(g)
- upstreams := buildUpstreams(ctx, g.Gateway.Listeners, serviceResolver, baseHTTPConfig.IPFamily)
httpServers, sslServers := buildServers(g)
- passthroughServers := buildPassthroughServers(g)
- streamUpstreams := buildStreamUpstreams(ctx, g.Gateway.Listeners, serviceResolver, baseHTTPConfig.IPFamily)
backendGroups := buildBackendGroups(append(httpServers, sslServers...))
- keyPairs := buildSSLKeyPairs(g.ReferencedSecrets, g.Gateway.Listeners)
- certBundles := buildCertBundles(g.ReferencedCaCertConfigMaps, backendGroups)
- telemetry := buildTelemetry(g)
config := Configuration{
HTTPServers: httpServers,
SSLServers: sslServers,
- TLSPassthroughServers: passthroughServers,
- Upstreams: upstreams,
- StreamUpstreams: streamUpstreams,
+ TLSPassthroughServers: buildPassthroughServers(g),
+ Upstreams: buildUpstreams(ctx, g.Gateway.Listeners, serviceResolver, baseHTTPConfig.IPFamily),
+ StreamUpstreams: buildStreamUpstreams(ctx, g.Gateway.Listeners, serviceResolver, baseHTTPConfig.IPFamily),
BackendGroups: backendGroups,
- SSLKeyPairs: keyPairs,
+ SSLKeyPairs: buildSSLKeyPairs(g.ReferencedSecrets, g.Gateway.Listeners),
Version: configVersion,
- CertBundles: certBundles,
- Telemetry: telemetry,
+ CertBundles: buildCertBundles(g.ReferencedCaCertConfigMaps, backendGroups),
+ Telemetry: buildTelemetry(g),
BaseHTTPConfig: baseHTTPConfig,
+ Logging: buildLogging(g),
}
return config
@@ -901,3 +893,23 @@ func convertAddresses(addresses []ngfAPI.Address) []string {
}
return trustedAddresses
}
+
+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)
+ }
+ }
+
+ return logSettings
+}
+
+func GetDefaultConfiguration(configVersion int) Configuration {
+ return Configuration{
+ Version: configVersion,
+ Logging: Logging{ErrorLevel: "info"},
+ }
+}
diff --git a/internal/mode/static/state/dataplane/configuration_test.go b/internal/mode/static/state/dataplane/configuration_test.go
index fe7806d45b..8cf39e17b7 100644
--- a/internal/mode/static/state/dataplane/configuration_test.go
+++ b/internal/mode/static/state/dataplane/configuration_test.go
@@ -22,7 +22,7 @@ import (
ngfAPI "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha1"
"github.com/nginxinc/nginx-gateway-fabric/internal/framework/helpers"
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/nginx/config/policies"
- policiesfakes "github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/nginx/config/policies/policiesfakes"
+ "github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/nginx/config/policies/policiesfakes"
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/state/graph"
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/state/resolver"
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/state/resolver/resolverfakes"
@@ -61,6 +61,9 @@ func getExpectedConfiguration() Configuration {
},
},
CertBundles: map[CertBundleID]CertBundle{},
+ Logging: Logging{
+ ErrorLevel: "info",
+ },
}
}
@@ -1493,7 +1496,7 @@ func TestBuildConfiguration(t *testing.T) {
}
return g
}),
- expConf: Configuration{},
+ expConf: Configuration{Logging: Logging{ErrorLevel: "info"}},
msg: "invalid gatewayclass",
},
{
@@ -1512,7 +1515,7 @@ func TestBuildConfiguration(t *testing.T) {
}
return g
}),
- expConf: Configuration{},
+ expConf: Configuration{Logging: Logging{ErrorLevel: "info"}},
msg: "missing gatewayclass",
},
{
@@ -1520,7 +1523,7 @@ func TestBuildConfiguration(t *testing.T) {
g.Gateway = nil
return g
}),
- expConf: Configuration{},
+ expConf: Configuration{Logging: Logging{ErrorLevel: "info"}},
msg: "missing gateway",
},
{
@@ -2228,6 +2231,36 @@ func TestBuildConfiguration(t *testing.T) {
}),
msg: "NginxProxy with rewriteClientIP details set",
},
+ {
+ 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: true,
+ Source: &ngfAPI.NginxProxy{
+ Spec: ngfAPI.NginxProxySpec{
+ Logging: &ngfAPI.NginxLogging{ErrorLevel: helpers.GetPointer(ngfAPI.NginxLogLevelDebug)},
+ },
+ },
+ }
+ return g
+ }),
+ expConf: getModifiedExpectedConfiguration(func(conf Configuration) Configuration {
+ conf.SSLServers = []VirtualServer{}
+ conf.SSLKeyPairs = map[SSLKeyPairID]SSLKeyPair{}
+ conf.Logging = Logging{ErrorLevel: "debug"}
+ return conf
+ }),
+ msg: "NginxProxy with error log level set to debug",
+ },
}
for _, test := range tests {
@@ -2252,6 +2285,7 @@ func TestBuildConfiguration(t *testing.T) {
g.Expect(result.CertBundles).To(Equal(test.expConf.CertBundles))
g.Expect(result.Telemetry).To(Equal(test.expConf.Telemetry))
g.Expect(result.BaseHTTPConfig).To(Equal(test.expConf.BaseHTTPConfig))
+ g.Expect(result.Logging).To(Equal(test.expConf.Logging))
})
}
}
@@ -3744,3 +3778,153 @@ func TestBuildRewriteIPSettings(t *testing.T) {
})
}
}
+
+func TestBuildLogging(t *testing.T) {
+ defaultLogging := Logging{ErrorLevel: defaultErrorLogLevel}
+
+ t.Parallel()
+ tests := []struct {
+ msg string
+ g *graph.Graph
+ expLoggingSettings Logging
+ }{
+ {
+ msg: "NginxProxy is nil",
+ g: &graph.Graph{},
+ expLoggingSettings: defaultLogging,
+ },
+ {
+ msg: "NginxProxy does not specify log level",
+ g: &graph.Graph{
+ NginxProxy: &graph.NginxProxy{
+ Valid: true,
+ Source: &ngfAPI.NginxProxy{
+ Spec: ngfAPI.NginxProxySpec{},
+ },
+ },
+ },
+ expLoggingSettings: defaultLogging,
+ },
+ {
+ msg: "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)},
+ },
+ },
+ },
+ },
+ expLoggingSettings: Logging{ErrorLevel: "debug"},
+ },
+ {
+ msg: "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)},
+ },
+ },
+ },
+ },
+ expLoggingSettings: Logging{ErrorLevel: "info"},
+ },
+ {
+ msg: "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)},
+ },
+ },
+ },
+ },
+ expLoggingSettings: Logging{ErrorLevel: "notice"},
+ },
+ {
+ msg: "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)},
+ },
+ },
+ },
+ },
+ expLoggingSettings: Logging{ErrorLevel: "warn"},
+ },
+ {
+ msg: "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)},
+ },
+ },
+ },
+ },
+ expLoggingSettings: Logging{ErrorLevel: "error"},
+ },
+ {
+ msg: "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)},
+ },
+ },
+ },
+ },
+ expLoggingSettings: Logging{ErrorLevel: "crit"},
+ },
+ {
+ msg: "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)},
+ },
+ },
+ },
+ },
+ expLoggingSettings: Logging{ErrorLevel: "alert"},
+ },
+ {
+ msg: "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)},
+ },
+ },
+ },
+ },
+ expLoggingSettings: Logging{ErrorLevel: "emerg"},
+ },
+ }
+
+ for _, tc := range tests {
+ t.Run(tc.msg, func(t *testing.T) {
+ t.Parallel()
+ g := NewWithT(t)
+
+ g.Expect(buildLogging(tc.g)).To(Equal(tc.expLoggingSettings))
+ })
+ }
+}
diff --git a/internal/mode/static/state/dataplane/types.go b/internal/mode/static/state/dataplane/types.go
index 59110f8cbb..9ebc7a185f 100644
--- a/internal/mode/static/state/dataplane/types.go
+++ b/internal/mode/static/state/dataplane/types.go
@@ -40,6 +40,8 @@ type Configuration struct {
BackendGroups []BackendGroup
// Telemetry holds the Otel configuration.
Telemetry Telemetry
+ // Logging defines logging related settings for NGINX.
+ Logging Logging
// BaseHTTPConfig holds the configuration options at the http context.
BaseHTTPConfig BaseHTTPConfig
// Version represents the version of the generated configuration.
@@ -355,3 +357,9 @@ type Ratio struct {
// Value is the value of the ratio.
Value int32
}
+
+// Logging defines logging related settings for NGINX.
+type Logging struct {
+ // ErrorLevel defines the error log level.
+ ErrorLevel string
+}
diff --git a/internal/mode/static/state/graph/nginxproxy.go b/internal/mode/static/state/graph/nginxproxy.go
index e276e3b221..378946715b 100644
--- a/internal/mode/static/state/graph/nginxproxy.go
+++ b/internal/mode/static/state/graph/nginxproxy.go
@@ -1,6 +1,8 @@
package graph
import (
+ "slices"
+
"k8s.io/apimachinery/pkg/types"
k8svalidation "k8s.io/apimachinery/pkg/util/validation"
"k8s.io/apimachinery/pkg/util/validation/field"
@@ -127,11 +129,50 @@ func validateNginxProxy(
npCfg.Spec.IPFamily = helpers.GetPointer[ngfAPI.IPFamilyType](ngfAPI.Dual)
}
+ allErrs = append(allErrs, validateLogging(npCfg)...)
+
allErrs = append(allErrs, validateRewriteClientIP(npCfg)...)
return allErrs
}
+func validateLogging(npCfg *ngfAPI.NginxProxy) field.ErrorList {
+ var allErrs field.ErrorList
+ spec := field.NewPath("spec")
+
+ if npCfg.Spec.Logging != nil {
+ logging := npCfg.Spec.Logging
+ loggingPath := spec.Child("logging")
+
+ if logging.ErrorLevel != nil {
+ 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),
+ }
+
+ if !slices.Contains(validLogLevels, errLevel) {
+ allErrs = append(
+ allErrs,
+ field.NotSupported(
+ loggingPath.Child("errorlevel"),
+ logging.ErrorLevel,
+ validLogLevels,
+ ))
+ }
+ }
+ }
+
+ return allErrs
+}
+
func validateRewriteClientIP(npCfg *ngfAPI.NginxProxy) field.ErrorList {
var allErrs field.ErrorList
spec := field.NewPath("spec")
diff --git a/internal/mode/static/state/graph/nginxproxy_test.go b/internal/mode/static/state/graph/nginxproxy_test.go
index 22a67f7d79..dd221cdb82 100644
--- a/internal/mode/static/state/graph/nginxproxy_test.go
+++ b/internal/mode/static/state/graph/nginxproxy_test.go
@@ -628,3 +628,148 @@ func TestValidateRewriteClientIP(t *testing.T) {
})
}
}
+
+func TestValidateLogging(t *testing.T) {
+ t.Parallel()
+ invalidLogLevel := ngfAPI.NginxErrorLogLevel("invalid-log-level")
+
+ tests := []struct {
+ np *ngfAPI.NginxProxy
+ name string
+ errorString string
+ expectErrCount int
+ }{
+ {
+ np: &ngfAPI.NginxProxy{
+ Spec: ngfAPI.NginxProxySpec{
+ Logging: &ngfAPI.NginxLogging{
+ ErrorLevel: helpers.GetPointer(ngfAPI.NginxLogLevelDebug),
+ },
+ },
+ },
+ name: "valid debug log level",
+ errorString: "",
+ expectErrCount: 0,
+ },
+ {
+ np: &ngfAPI.NginxProxy{
+ Spec: ngfAPI.NginxProxySpec{
+ Logging: &ngfAPI.NginxLogging{
+ ErrorLevel: helpers.GetPointer(ngfAPI.NginxLogLevelInfo),
+ },
+ },
+ },
+ name: "valid info log level",
+ errorString: "",
+ expectErrCount: 0,
+ },
+ {
+ np: &ngfAPI.NginxProxy{
+ Spec: ngfAPI.NginxProxySpec{
+ Logging: &ngfAPI.NginxLogging{
+ ErrorLevel: helpers.GetPointer(ngfAPI.NginxLogLevelNotice),
+ },
+ },
+ },
+ name: "valid notice log level",
+ errorString: "",
+ expectErrCount: 0,
+ },
+ {
+ np: &ngfAPI.NginxProxy{
+ Spec: ngfAPI.NginxProxySpec{
+ Logging: &ngfAPI.NginxLogging{
+ ErrorLevel: helpers.GetPointer(ngfAPI.NginxLogLevelWarn),
+ },
+ },
+ },
+ name: "valid warn log level",
+ errorString: "",
+ expectErrCount: 0,
+ },
+ {
+ np: &ngfAPI.NginxProxy{
+ Spec: ngfAPI.NginxProxySpec{
+ Logging: &ngfAPI.NginxLogging{
+ ErrorLevel: helpers.GetPointer(ngfAPI.NginxLogLevelError),
+ },
+ },
+ },
+ name: "valid error log level",
+ errorString: "",
+ expectErrCount: 0,
+ },
+ {
+ np: &ngfAPI.NginxProxy{
+ Spec: ngfAPI.NginxProxySpec{
+ Logging: &ngfAPI.NginxLogging{
+ ErrorLevel: helpers.GetPointer(ngfAPI.NginxLogLevelCrit),
+ },
+ },
+ },
+ name: "valid crit log level",
+ errorString: "",
+ expectErrCount: 0,
+ },
+ {
+ np: &ngfAPI.NginxProxy{
+ Spec: ngfAPI.NginxProxySpec{
+ Logging: &ngfAPI.NginxLogging{
+ ErrorLevel: helpers.GetPointer(ngfAPI.NginxLogLevelAlert),
+ },
+ },
+ },
+ name: "valid alert log level",
+ errorString: "",
+ expectErrCount: 0,
+ },
+ {
+ np: &ngfAPI.NginxProxy{
+ Spec: ngfAPI.NginxProxySpec{
+ Logging: &ngfAPI.NginxLogging{
+ ErrorLevel: helpers.GetPointer(ngfAPI.NginxLogLevelEmerg),
+ },
+ },
+ },
+ name: "valid emerg log level",
+ errorString: "",
+ expectErrCount: 0,
+ },
+ {
+ np: &ngfAPI.NginxProxy{
+ Spec: ngfAPI.NginxProxySpec{
+ Logging: &ngfAPI.NginxLogging{
+ ErrorLevel: &invalidLogLevel,
+ },
+ },
+ },
+ name: "invalid log level",
+ errorString: "spec.logging.errorlevel: Unsupported value: \"invalid-log-level\": supported values:" +
+ " \"debug\", \"info\", \"notice\", \"warn\", \"error\", \"crit\", \"alert\", \"emerg\"",
+ expectErrCount: 1,
+ },
+ {
+ np: &ngfAPI.NginxProxy{
+ Spec: ngfAPI.NginxProxySpec{
+ Logging: &ngfAPI.NginxLogging{},
+ },
+ },
+ name: "empty log level",
+ errorString: "",
+ expectErrCount: 0,
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ t.Parallel()
+ g := NewWithT(t)
+
+ allErrs := validateLogging(test.np)
+ g.Expect(allErrs).To(HaveLen(test.expectErrCount))
+ if len(allErrs) > 0 {
+ g.Expect(allErrs.ToAggregate().Error()).To(Equal(test.errorString))
+ }
+ })
+ }
+}
diff --git a/site/content/how-to/data-plane-configuration.md b/site/content/how-to/data-plane-configuration.md
index 6274246dd7..2ce318ba62 100644
--- a/site/content/how-to/data-plane-configuration.md
+++ b/site/content/how-to/data-plane-configuration.md
@@ -9,7 +9,7 @@ Learn how to dynamically update the NGINX Gateway Fabric global data plane confi
## Overview
-NGINX Gateway Fabric can dynamically update the global data plane configuration without restarting. The data plane configuration is a global configuration for NGINX that has options that are not available using the standard Gateway API resources. This includes such things as setting an OpenTelemetry collector config, disabling http2, or changing the IP family.
+NGINX Gateway Fabric can dynamically update the global data plane configuration without restarting. The data plane configuration is a global configuration for NGINX that has options that are not available using the standard Gateway API resources. This includes such things as setting an OpenTelemetry collector config, disabling http2, changing the IP family, or setting the NGINX error log level.
The data plane configuration is stored in the NginxProxy custom resource, which is a cluster-scoped resource that is attached to the `nginx` GatewayClass.
@@ -112,3 +112,48 @@ Status:
```
If everything is valid, the `ResolvedRefs` condition should be `True`. Otherwise, you will see an `InvalidParameters` condition in the status.
+
+## Configure the Data Plane Log Level
+
+You can use the `NginxProxy` resource to dynamically configure the Data Plane Log Level.
+
+The following command creates a basic `NginxProxy` configuration that sets the log level to `warn` instead of the default value of `info`:
+
+```yaml
+kubectl apply -f - < Logging defines logging related settings for NGINX.
+
+
+logging
+
+
+NginxLogging
+
+
+
+(Optional)
+
+
+
+
disableHTTP2
bool
@@ -880,6 +894,49 @@ ControllerLogLevel
NginxErrorLogLevel
+(
string
alias)
+(Appears on: +NginxLogging) +
++
NginxErrorLogLevel type defines the log level of error logs for NGINX.
+ +Value | +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. + |
+
"warn" |
+NginxLogLevelWarn is the warn level for NGINX error logs. + |
+
string
alias)¶
+(Appears on: +NginxProxySpec) +
++
NginxLogging defines logging related settings for NGINX.
+ +Field | +Description | +
---|---|
+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 + |
+
logging
Logging defines logging related settings for NGINX.
+disableHTTP2