diff --git a/apis/config/v1alpha1/backend_types.go b/apis/config/v1alpha1/backend_types.go
index 6fb6813..ef2d9de 100644
--- a/apis/config/v1alpha1/backend_types.go
+++ b/apis/config/v1alpha1/backend_types.go
@@ -38,6 +38,9 @@ type BackendSpec struct {
// Redispatch enable or disable session redistribution in case of connection failure
// +optional
Redispatch *bool `json:"redispatch,omitempty"`
+ // Options contains additional backend options.
+ // +optional
+ Options *BackendOptions `json:"options,omitempty"`
// HashType specifies a method to use for mapping hashes to servers
// +optional
HashType *HashType `json:"hashType,omitempty"`
@@ -52,6 +55,12 @@ type BackendSpec struct {
TCPCheck *bool `json:"tcpCheck,omitempty"`
}
+type BackendOptions struct {
+ // LogHealthChecks enables logging of health checks.
+ // +optional
+ LogHealthChecks *bool `json:"logHealthChecks,omitempty"`
+}
+
//+kubebuilder:object:root=true
//+kubebuilder:subresource:status
//+kubebuilder:printcolumn:name=Mode,type=string,JSONPath=`.spec.mode`
@@ -126,6 +135,14 @@ func (b *Backend) Model() (models.Backend, error) {
}
}
+ if b.Spec.Options != nil && b.Spec.Options.LogHealthChecks != nil {
+ if *b.Spec.Options.LogHealthChecks {
+ model.LogHealthChecks = models.BackendBaseLogHealthChecksEnabled
+ } else {
+ model.LogHealthChecks = models.BackendBaseLogHealthChecksDisabled
+ }
+ }
+
if b.Spec.HashType != nil {
ht, err := b.Spec.HashType.Model()
if err == nil {
diff --git a/apis/config/v1alpha1/backend_types_test.go b/apis/config/v1alpha1/backend_types_test.go
index 3278c20..001f10f 100644
--- a/apis/config/v1alpha1/backend_types_test.go
+++ b/apis/config/v1alpha1/backend_types_test.go
@@ -87,6 +87,18 @@ var _ = Describe("Backend", Label("type"), func() {
Ω(backend.AddToParser(p)).ShouldNot(HaveOccurred())
Ω(p.String()).Should(ContainSubstring("server server 10.0.0.4:8080 check port 80"))
})
+ It("should set option log-health-checks", func() {
+ backend := &configv1alpha1.Backend{
+ ObjectMeta: metav1.ObjectMeta{Name: "foo"},
+ Spec: configv1alpha1.BackendSpec{
+ Options: &configv1alpha1.BackendOptions{
+ LogHealthChecks: ptr.To(true),
+ },
+ },
+ }
+ Ω(backend.AddToParser(p)).ShouldNot(HaveOccurred())
+ Ω(p.String()).Should(ContainSubstring("option log-health-checks"))
+ })
It("should set hash-type", func() {
backend := &configv1alpha1.Backend{
ObjectMeta: metav1.ObjectMeta{Name: "foo"},
diff --git a/apis/config/v1alpha1/zz_generated.deepcopy.go b/apis/config/v1alpha1/zz_generated.deepcopy.go
index 7f5e741..31670f4 100644
--- a/apis/config/v1alpha1/zz_generated.deepcopy.go
+++ b/apis/config/v1alpha1/zz_generated.deepcopy.go
@@ -168,6 +168,11 @@ func (in *BackendSpec) DeepCopyInto(out *BackendSpec) {
*out = new(bool)
**out = **in
}
+ if in.Options != nil {
+ in, out := &in.Options, &out.Options
+ *out = new(BackendOptions)
+ (*in).DeepCopyInto(*out)
+ }
if in.HashType != nil {
in, out := &in.HashType, &out.HashType
*out = new(HashType)
@@ -200,6 +205,26 @@ func (in *BackendSpec) DeepCopy() *BackendSpec {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *BackendOptions) DeepCopyInto(out *BackendOptions) {
+ *out = *in
+ if in.LogHealthChecks != nil {
+ in, out := &in.LogHealthChecks, &out.LogHealthChecks
+ *out = new(bool)
+ **out = **in
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackendOptions.
+func (in *BackendOptions) DeepCopy() *BackendOptions {
+ if in == nil {
+ return nil
+ }
+ out := new(BackendOptions)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *BackendSwitchingRule) DeepCopyInto(out *BackendSwitchingRule) {
*out = *in
diff --git a/apis/proxy/v1alpha1/instance_types.go b/apis/proxy/v1alpha1/instance_types.go
index 51e6ea0..1114915 100644
--- a/apis/proxy/v1alpha1/instance_types.go
+++ b/apis/proxy/v1alpha1/instance_types.go
@@ -8,6 +8,7 @@ import (
"github.com/go-openapi/strfmt"
parser "github.com/haproxytech/client-native/v6/config-parser"
configparseropts "github.com/haproxytech/client-native/v6/config-parser/options"
+ parsertypes "github.com/haproxytech/client-native/v6/config-parser/types"
"github.com/haproxytech/client-native/v6/configuration"
"github.com/haproxytech/client-native/v6/configuration/options"
"github.com/haproxytech/client-native/v6/models"
@@ -665,6 +666,7 @@ type DefaultsConfiguration struct {
// Logging is used to configure default logging for all proxies.
// +optional
Logging *DefaultsLoggingConfiguration `json:"logging,omitempty"`
+ // Options contains additional defaults options.
// Options configures additional HAProxy defaults options.
// +optional
Options *DefaultsOptions `json:"options,omitempty"`
@@ -677,6 +679,21 @@ type DefaultsConfiguration struct {
}
type DefaultsOptions struct {
+ // LogSeparateErrors causes error and normal logs to be emitted separately.
+ // +optional
+ LogSeparateErrors *bool `json:"logSeparateErrors,omitempty"`
+ // LogHealthChecks enables logging of health checks.
+ // +optional
+ LogHealthChecks *bool `json:"logHealthChecks,omitempty"`
+ // Dontlognull controls logging of null connections.
+ // +optional
+ Dontlognull *bool `json:"dontlognull,omitempty"`
+ // DontlogNormal controls logging of normal traffic.
+ // +optional
+ DontlogNormal *bool `json:"dontlogNormal,omitempty"`
+ // HTTPLogCLF enables HTTP logging in CLF format.
+ // +optional
+ HTTPLogCLF *bool `json:"httpLogClf,omitempty"`
// Redispatch enables or disables redispatching in defaults.
// +optional
Redispatch *bool `json:"redispatch,omitempty"`
@@ -758,6 +775,45 @@ func (d *DefaultsConfiguration) Model() (models.Defaults, error) {
defaults.H1CaseAdjustBogusServer = models.DefaultsBaseH1CaseAdjustBogusServerEnabled
}
+ if d.Options != nil {
+ if d.Options.LogSeparateErrors != nil {
+ if *d.Options.LogSeparateErrors {
+ defaults.LogSeparateErrors = models.DefaultsBaseLogSeparateErrorsEnabled
+ } else {
+ defaults.LogSeparateErrors = models.DefaultsBaseLogSeparateErrorsDisabled
+ }
+ }
+
+ if d.Options.LogHealthChecks != nil {
+ if *d.Options.LogHealthChecks {
+ defaults.LogHealthChecks = models.DefaultsBaseLogHealthChecksEnabled
+ } else {
+ defaults.LogHealthChecks = models.DefaultsBaseLogHealthChecksDisabled
+ }
+ }
+
+ if d.Options.Dontlognull != nil {
+ if *d.Options.Dontlognull {
+ defaults.Dontlognull = models.DefaultsBaseDontlognullEnabled
+ } else {
+ defaults.Dontlognull = models.DefaultsBaseDontlognullDisabled
+ }
+ }
+
+ if d.Options.DontlogNormal != nil {
+ if *d.Options.DontlogNormal {
+ defaults.DontlogNormal = models.DefaultsBaseDontlogNormalEnabled
+ } else {
+ defaults.DontlogNormal = models.DefaultsBaseDontlogNormalDisabled
+ }
+ }
+
+ if ptr.Deref(d.Options.HTTPLogCLF, false) {
+ defaults.Httplog = true
+ defaults.Clflog = true
+ }
+ }
+
return defaults, defaults.Validate(strfmt.Default)
}
@@ -776,6 +832,12 @@ func (d *DefaultsConfiguration) AddToParser(p parser.Parser) error {
return err
}
+ if d.Options != nil && ptr.Deref(d.Options.HTTPLogCLF, false) {
+ if err := p.Set(parser.Defaults, defaultsSectionName, "option httplog", &parsertypes.OptionHTTPLog{Clf: true}, 0); err != nil {
+ return err
+ }
+ }
+
if d.Logging != nil && d.Logging.Enabled {
logTarget, err := d.Logging.Model()
if err != nil {
diff --git a/apis/proxy/v1alpha1/instance_types_test.go b/apis/proxy/v1alpha1/instance_types_test.go
index 52be392..6fe283c 100644
--- a/apis/proxy/v1alpha1/instance_types_test.go
+++ b/apis/proxy/v1alpha1/instance_types_test.go
@@ -6,6 +6,7 @@ import (
"time"
parser "github.com/haproxytech/client-native/v6/config-parser"
+ "github.com/haproxytech/client-native/v6/models"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/utils/ptr"
)
@@ -155,3 +156,106 @@ func TestDefaultsConfiguration_AddToParser_H1CaseAdjustBogusServerDisabledByDefa
t.Fatalf("did not expect h1-case-adjust-bogus-server option in defaults section, got:\n%s", cfg)
}
}
+
+func TestDefaultsConfigurationModelWithOptions(t *testing.T) {
+ d := &DefaultsConfiguration{
+ Mode: "http",
+ Timeouts: map[string]metav1.Duration{},
+ Options: &DefaultsOptions{
+ LogSeparateErrors: ptr.To(true),
+ LogHealthChecks: ptr.To(true),
+ Dontlognull: ptr.To(false),
+ DontlogNormal: ptr.To(false),
+ HTTPLogCLF: ptr.To(true),
+ Redispatch: ptr.To(true),
+ },
+ }
+
+ model, err := d.Model()
+ if err != nil {
+ t.Fatalf("Model() returned error: %v", err)
+ }
+ if model.LogSeparateErrors != models.DefaultsBaseLogSeparateErrorsEnabled {
+ t.Fatalf("unexpected log-separate-errors value: %s", model.LogSeparateErrors)
+ }
+ if model.LogHealthChecks != models.DefaultsBaseLogHealthChecksEnabled {
+ t.Fatalf("unexpected log-health-checks value: %s", model.LogHealthChecks)
+ }
+ if model.Dontlognull != models.DefaultsBaseDontlognullDisabled {
+ t.Fatalf("unexpected dontlognull value: %s", model.Dontlognull)
+ }
+ if model.DontlogNormal != models.DefaultsBaseDontlogNormalDisabled {
+ t.Fatalf("unexpected dontlog-normal value: %s", model.DontlogNormal)
+ }
+ if !model.Httplog {
+ t.Fatalf("expected httplog to be enabled")
+ }
+ if !model.Clflog {
+ t.Fatalf("expected clflog to be enabled")
+ }
+ if model.Redispatch == nil || model.Redispatch.Enabled == nil || *model.Redispatch.Enabled != models.RedispatchEnabledEnabled {
+ t.Fatalf("expected redispatch to be enabled")
+ }
+}
+
+func TestDefaultsConfigurationAddToParserWithOptions(t *testing.T) {
+ d := &DefaultsConfiguration{
+ Mode: "http",
+ Timeouts: map[string]metav1.Duration{},
+ Options: &DefaultsOptions{
+ LogSeparateErrors: ptr.To(true),
+ LogHealthChecks: ptr.To(true),
+ Dontlognull: ptr.To(false),
+ DontlogNormal: ptr.To(false),
+ HTTPLogCLF: ptr.To(true),
+ },
+ }
+
+ p, err := parser.New()
+ if err != nil {
+ t.Fatalf("parser.New() returned error: %v", err)
+ }
+
+ if err := d.AddToParser(p); err != nil {
+ t.Fatalf("AddToParser() returned error: %v", err)
+ }
+
+ cfg := p.String()
+ checks := []string{
+ "option httplog",
+ "option log-separate-errors",
+ "option log-health-checks",
+ "no option dontlognull",
+ "no option dontlog-normal",
+ }
+
+ for _, check := range checks {
+ if !strings.Contains(cfg, check) {
+ t.Fatalf("expected generated config to contain %q, got:\n%s", check, cfg)
+ }
+ }
+}
+
+func TestDefaultsConfigurationAddToParserWithHTTPLogCLF(t *testing.T) {
+ d := &DefaultsConfiguration{
+ Mode: "http",
+ Timeouts: map[string]metav1.Duration{},
+ Options: &DefaultsOptions{
+ HTTPLogCLF: ptr.To(true),
+ },
+ }
+
+ p, err := parser.New()
+ if err != nil {
+ t.Fatalf("parser.New() returned error: %v", err)
+ }
+
+ if err := d.AddToParser(p); err != nil {
+ t.Fatalf("AddToParser() returned error: %v", err)
+ }
+
+ cfg := p.String()
+ if !strings.Contains(cfg, "option httplog clf") {
+ t.Fatalf("expected generated config to contain %q, got:\n%s", "option httplog clf", cfg)
+ }
+}
diff --git a/apis/proxy/v1alpha1/zz_generated.deepcopy.go b/apis/proxy/v1alpha1/zz_generated.deepcopy.go
index 4932a14..142103f 100644
--- a/apis/proxy/v1alpha1/zz_generated.deepcopy.go
+++ b/apis/proxy/v1alpha1/zz_generated.deepcopy.go
@@ -130,6 +130,31 @@ func (in *DefaultsLoggingConfiguration) DeepCopy() *DefaultsLoggingConfiguration
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DefaultsOptions) DeepCopyInto(out *DefaultsOptions) {
*out = *in
+ if in.LogSeparateErrors != nil {
+ in, out := &in.LogSeparateErrors, &out.LogSeparateErrors
+ *out = new(bool)
+ **out = **in
+ }
+ if in.LogHealthChecks != nil {
+ in, out := &in.LogHealthChecks, &out.LogHealthChecks
+ *out = new(bool)
+ **out = **in
+ }
+ if in.Dontlognull != nil {
+ in, out := &in.Dontlognull, &out.Dontlognull
+ *out = new(bool)
+ **out = **in
+ }
+ if in.DontlogNormal != nil {
+ in, out := &in.DontlogNormal, &out.DontlogNormal
+ *out = new(bool)
+ **out = **in
+ }
+ if in.HTTPLogCLF != nil {
+ in, out := &in.HTTPLogCLF, &out.HTTPLogCLF
+ *out = new(bool)
+ **out = **in
+ }
if in.Redispatch != nil {
in, out := &in.Redispatch, &out.Redispatch
*out = new(bool)
diff --git a/docs/api-reference.md b/docs/api-reference.md
index b70edbb..9675e92 100644
--- a/docs/api-reference.md
+++ b/docs/api-reference.md
@@ -104,12 +104,28 @@ _Appears in:_
| `hostRegex` _string_ | HostRegex specifies a regular expression used for backend switching rules. | | Optional: \{\}
|
| `hostCertificate` _[CertificateListElement](#certificatelistelement)_ | HostCertificate specifies a certificate for that host used in the crt-list of a frontend | | Optional: \{\}
|
| `redispatch` _boolean_ | Redispatch enable or disable session redistribution in case of connection failure | | Optional: \{\}
|
+| `options` _[BackendOptions](#backendoptions)_ | Options contains additional backend options. | | Optional: \{\}
|
| `hashType` _[HashType](#hashtype)_ | HashType specifies a method to use for mapping hashes to servers | | Optional: \{\}
|
| `cookie` _[Cookie](#cookie)_ | Cookie enables cookie-based persistence in a backend. | | Optional: \{\}
|
| `httpchk` _[HTTPChk](#httpchk)_ | HTTPChk Enables HTTP protocol to check on the servers health | | Optional: \{\}
|
| `tcpCheck` _boolean_ | TCPCheck Perform health checks using tcp-check send/expect sequences | | Optional: \{\}
|
+#### BackendOptions
+
+
+
+
+
+
+_Appears in:_
+- [BackendSpec](#backendspec)
+
+| Field | Description | Default | Validation |
+| --- | --- | --- | --- |
+| `logHealthChecks` _boolean_ | LogHealthChecks enables logging of health checks. | | Optional: \{\}
|
+
+
#### BackendSwitchingRule
@@ -1303,6 +1319,26 @@ _Appears in:_
| `tcpLog` _boolean_ | TCPLog enables advanced logging of TCP connections with session state and timers. By default, the log output format
is very poor, as it only contains the source and destination addresses, and the instance name. | | Optional: \{\}
|
+#### DefaultsOptions
+
+
+
+
+
+
+_Appears in:_
+- [DefaultsConfiguration](#defaultsconfiguration)
+
+| Field | Description | Default | Validation |
+| --- | --- | --- | --- |
+| `logSeparateErrors` _boolean_ | LogSeparateErrors causes error and normal logs to be emitted separately. | | Optional: \{\}
|
+| `logHealthChecks` _boolean_ | LogHealthChecks enables logging of health checks. | | Optional: \{\}
|
+| `dontlognull` _boolean_ | Dontlognull controls logging of null connections. | | Optional: \{\}
|
+| `dontlogNormal` _boolean_ | DontlogNormal controls logging of normal traffic. | | Optional: \{\}
|
+| `httpLogClf` _boolean_ | HTTPLogCLF enables HTTP logging in CLF format (emits `option httplog clf`). | | Optional: \{\}
|
+| `redispatch` _boolean_ | Redispatch enables or disables redispatching in defaults. | | Optional: \{\}
|
+
+
#### GlobalConfiguration
diff --git a/helm/haproxy-operator/crds/config.haproxy.com_backends.yaml b/helm/haproxy-operator/crds/config.haproxy.com_backends.yaml
index 7ff2a3d..4a5bb64 100644
--- a/helm/haproxy-operator/crds/config.haproxy.com_backends.yaml
+++ b/helm/haproxy-operator/crds/config.haproxy.com_backends.yaml
@@ -1325,6 +1325,13 @@ spec:
description: Redispatch enable or disable session redistribution in
case of connection failure
type: boolean
+ options:
+ description: Options contains additional backend options.
+ properties:
+ logHealthChecks:
+ description: LogHealthChecks enables logging of health checks.
+ type: boolean
+ type: object
serverTemplates:
description: ServerTemplates defines the backend server templates
and its configuration.
diff --git a/helm/haproxy-operator/crds/proxy.haproxy.com_instances.yaml b/helm/haproxy-operator/crds/proxy.haproxy.com_instances.yaml
index 6523258..22e3b86 100644
--- a/helm/haproxy-operator/crds/proxy.haproxy.com_instances.yaml
+++ b/helm/haproxy-operator/crds/proxy.haproxy.com_instances.yaml
@@ -151,6 +151,24 @@ spec:
description: Options configures additional HAProxy defaults
options.
properties:
+ dontlogNormal:
+ description: DontlogNormal controls logging of normal
+ traffic.
+ type: boolean
+ dontlognull:
+ description: Dontlognull controls logging of null connections.
+ type: boolean
+ httpLogClf:
+ description: HTTPLogCLF enables HTTP logging in CLF format.
+ type: boolean
+ logHealthChecks:
+ description: LogHealthChecks enables logging of health
+ checks.
+ type: boolean
+ logSeparateErrors:
+ description: LogSeparateErrors causes error and normal
+ logs to be emitted separately.
+ type: boolean
redispatch:
description: Redispatch enables or disables redispatching
in defaults.