diff --git a/deploy/crds/k8s.nginx.org_nginxingresscontrollers_crd.yaml b/deploy/crds/k8s.nginx.org_nginxingresscontrollers_crd.yaml index c49f4098..651d0d73 100644 --- a/deploy/crds/k8s.nginx.org_nginxingresscontrollers_crd.yaml +++ b/deploy/crds/k8s.nginx.org_nginxingresscontrollers_crd.yaml @@ -72,7 +72,8 @@ spec: enableLeaderElection: description: Enables Leader election to avoid multiple replicas of the controller reporting the status of Ingress resources – only one replica - will report status. + will report status. Default is true. + nullable: true type: boolean enablePreviewPolicies: description: Enables preview policies. Requires enableCRDs set to true. diff --git a/docs/nginx-ingress-controller.md b/docs/nginx-ingress-controller.md index a1c5694f..99b932e5 100644 --- a/docs/nginx-ingress-controller.md +++ b/docs/nginx-ingress-controller.md @@ -95,7 +95,7 @@ spec: | `logLevel` | `int` | Log level for V logs. Format is `0 - 3` | No | | `nginxStatus` | [nginxStatus](#nginxingresscontrollernginxstatus) | Configures NGINX stub_status, or the NGINX Plus API. | No | | `reportIngressStatus` | [reportIngressStatus](#nginxingresscontrollerreportingressstatus) | Update the address field in the status of Ingresses resources. | No | -| `enableLeaderElection` | `boolean` | Enables Leader election to avoid multiple replicas of the controller reporting the status of Ingress resources – only one replica will report status. | No | +| `enableLeaderElection` | `boolean` | Enables Leader election to avoid multiple replicas of the controller reporting the status of Ingress resources – only one replica will report status. Default is `true`. | No | | `wildcardTLS` | `string` | A Secret with a TLS certificate and key for TLS termination of every Ingress host for which TLS termination is enabled but the Secret is not specified. The secret must be of the type kubernetes.io/tls. If the argument is not set, for such Ingress hosts NGINX will break any attempt to establish a TLS connection. If the argument is set, but the Ingress controller is not able to fetch the Secret from Kubernetes API, the Ingress Controller will fail to start. Format is `namespace/name`. | No | | `prometheus` | [prometheus](#nginxingresscontrollerprometheus) | Configures NGINX or NGINX Plus metrics in the Prometheus format. | No | | `configMapData` | `map[string]string` | Initial values of the Ingress Controller ConfigMap. Check the [ConfigMap docs](https://docs.nginx.com/nginx-ingress-controller/configuration/global-configuration/configmap-resource/) for more information about possible values. | No | diff --git a/pkg/apis/k8s/v1alpha1/nginxingresscontroller_types.go b/pkg/apis/k8s/v1alpha1/nginxingresscontroller_types.go index 0450cab6..edf0df43 100644 --- a/pkg/apis/k8s/v1alpha1/nginxingresscontroller_types.go +++ b/pkg/apis/k8s/v1alpha1/nginxingresscontroller_types.go @@ -97,9 +97,11 @@ type NginxIngressControllerSpec struct { ReportIngressStatus *ReportIngressStatus `json:"reportIngressStatus,omitempty"` // Enables Leader election to avoid multiple replicas of the controller reporting the status of Ingress resources // – only one replica will report status. + // Default is true. // +kubebuilder:validation:Optional + // +nullable // +operator-sdk:gen-csv:customresourcedefinitions.specDescriptors=true - EnableLeaderElection bool `json:"enableLeaderElection"` + EnableLeaderElection *bool `json:"enableLeaderElection"` // A Secret with a TLS certificate and key for TLS termination of every Ingress host for which TLS termination is enabled but the Secret is not specified. // The secret must be of the type kubernetes.io/tls. // If the argument is not set, for such Ingress hosts NGINX will break any attempt to establish a TLS connection. diff --git a/pkg/apis/k8s/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/k8s/v1alpha1/zz_generated.deepcopy.go index 28497488..f4bd5b8c 100644 --- a/pkg/apis/k8s/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/k8s/v1alpha1/zz_generated.deepcopy.go @@ -151,6 +151,11 @@ func (in *NginxIngressControllerSpec) DeepCopyInto(out *NginxIngressControllerSp *out = new(ReportIngressStatus) **out = **in } + if in.EnableLeaderElection != nil { + in, out := &in.EnableLeaderElection, &out.EnableLeaderElection + *out = new(bool) + **out = **in + } if in.Prometheus != nil { in, out := &in.Prometheus, &out.Prometheus *out = new(Prometheus) diff --git a/pkg/controller/nginxingresscontroller/utils.go b/pkg/controller/nginxingresscontroller/utils.go index ff0f0a0d..4c7eb982 100644 --- a/pkg/controller/nginxingresscontroller/utils.go +++ b/pkg/controller/nginxingresscontroller/utils.go @@ -91,8 +91,10 @@ func generatePodArgs(instance *k8sv1alpha1.NginxIngressController) []string { } } - if instance.Spec.EnableLeaderElection { - args = append(args, "-enable-leader-election") + if instance.Spec.EnableLeaderElection == nil || *instance.Spec.EnableLeaderElection { + args = append(args, fmt.Sprintf("-leader-election-lock-name=%v-lock", instance.Name)) + } else { + args = append(args, "-enable-leader-election=false") } if instance.Spec.WildcardTLS != "" { diff --git a/pkg/controller/nginxingresscontroller/utils_test.go b/pkg/controller/nginxingresscontroller/utils_test.go index 74024e43..fa5f153f 100644 --- a/pkg/controller/nginxingresscontroller/utils_test.go +++ b/pkg/controller/nginxingresscontroller/utils_test.go @@ -2,7 +2,6 @@ package nginxingresscontroller import ( "fmt" - "reflect" "testing" "github.com/google/go-cmp/cmp" @@ -17,8 +16,8 @@ func TestGeneratePodArgs(t *testing.T) { statusPort = 9090 name := "my-nginx-ingress" namespace := "my-nginx-ingress" - enableCRDs := true - disableCRDs := false + enable := true + disable := false tests := []struct { instance *k8sv1alpha1.NginxIngressController expected []string @@ -34,6 +33,7 @@ func TestGeneratePodArgs(t *testing.T) { expected: []string{ "-nginx-configmaps=my-nginx-ingress/my-nginx-ingress", "-default-server-tls-secret=my-nginx-ingress/my-nginx-ingress", + "-leader-election-lock-name=my-nginx-ingress-lock", }, }, { @@ -49,6 +49,7 @@ func TestGeneratePodArgs(t *testing.T) { expected: []string{ "-nginx-configmaps=my-nginx-ingress/my-nginx-ingress", "-default-server-tls-secret=my-nginx-ingress/my-secret", + "-leader-election-lock-name=my-nginx-ingress-lock", }, }, { @@ -65,6 +66,7 @@ func TestGeneratePodArgs(t *testing.T) { "-nginx-configmaps=my-nginx-ingress/my-nginx-ingress", "-default-server-tls-secret=my-nginx-ingress/my-nginx-ingress", "-nginx-plus", + "-leader-election-lock-name=my-nginx-ingress-lock", }, }, { @@ -74,12 +76,13 @@ func TestGeneratePodArgs(t *testing.T) { Namespace: namespace, }, Spec: k8sv1alpha1.NginxIngressControllerSpec{ - EnableCRDs: &disableCRDs, + EnableCRDs: &disable, }, }, expected: []string{ "-nginx-configmaps=my-nginx-ingress/my-nginx-ingress", "-default-server-tls-secret=my-nginx-ingress/my-nginx-ingress", + "-leader-election-lock-name=my-nginx-ingress-lock", "-enable-custom-resources=false", }, }, @@ -91,7 +94,7 @@ func TestGeneratePodArgs(t *testing.T) { }, Spec: k8sv1alpha1.NginxIngressControllerSpec{ NginxPlus: true, - EnableCRDs: &disableCRDs, + EnableCRDs: &disable, DefaultSecret: "my-nginx-ingress/my-secret", }, }, @@ -99,6 +102,7 @@ func TestGeneratePodArgs(t *testing.T) { "-nginx-configmaps=my-nginx-ingress/my-nginx-ingress", "-default-server-tls-secret=my-nginx-ingress/my-secret", "-nginx-plus", + "-leader-election-lock-name=my-nginx-ingress-lock", "-enable-custom-resources=false", }, }, @@ -122,6 +126,7 @@ func TestGeneratePodArgs(t *testing.T) { "-default-server-tls-secret=my-nginx-ingress/my-secret", "-report-ingress-status", "-ingresslink=my-ingresslink", + "-leader-election-lock-name=my-nginx-ingress-lock", }, }, { @@ -144,6 +149,7 @@ func TestGeneratePodArgs(t *testing.T) { "-default-server-tls-secret=my-nginx-ingress/my-secret", "-report-ingress-status", fmt.Sprintf("-external-service=%v", name), + "-leader-election-lock-name=my-nginx-ingress-lock", }, }, { @@ -153,7 +159,7 @@ func TestGeneratePodArgs(t *testing.T) { Namespace: namespace, }, Spec: k8sv1alpha1.NginxIngressControllerSpec{ - EnableCRDs: &enableCRDs, + EnableCRDs: &enable, EnableSnippets: true, EnablePreviewPolicies: true, EnableTLSPassthrough: true, @@ -163,12 +169,29 @@ func TestGeneratePodArgs(t *testing.T) { expected: []string{ "-nginx-configmaps=my-nginx-ingress/my-nginx-ingress", "-default-server-tls-secret=my-nginx-ingress/my-nginx-ingress", + "-leader-election-lock-name=my-nginx-ingress-lock", "-enable-tls-passthrough", "-global-configuration=my-nginx-ingress/globalconfiguration", "-enable-snippets", "-enable-preview-policies", }, }, + { + instance: &k8sv1alpha1.NginxIngressController{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: k8sv1alpha1.NginxIngressControllerSpec{ + EnableLeaderElection: &disable, + }, + }, + expected: []string{ + "-nginx-configmaps=my-nginx-ingress/my-nginx-ingress", + "-default-server-tls-secret=my-nginx-ingress/my-nginx-ingress", + "-enable-leader-election=false", + }, + }, { instance: &k8sv1alpha1.NginxIngressController{ ObjectMeta: metav1.ObjectMeta{ @@ -197,7 +220,7 @@ func TestGeneratePodArgs(t *testing.T) { ExternalService: "external", IngressLink: "my-invalid-ingressLink", }, - EnableLeaderElection: true, + EnableLeaderElection: &enable, WildcardTLS: "my-nginx-ingress/wildcard-secret", Prometheus: &k8sv1alpha1.Prometheus{ Enable: true, @@ -210,7 +233,7 @@ func TestGeneratePodArgs(t *testing.T) { Enable: true, }, NginxReloadTimeout: 5000, - EnableCRDs: &disableCRDs, + EnableCRDs: &disable, EnableSnippets: true, EnablePreviewPolicies: true, }, @@ -232,7 +255,7 @@ func TestGeneratePodArgs(t *testing.T) { "-nginx-status-allow-cidrs=127.0.0.1", "-report-ingress-status", "-external-service=external", - "-enable-leader-election", + "-leader-election-lock-name=my-nginx-ingress-lock", "-wildcard-tls-secret=my-nginx-ingress/wildcard-secret", "-enable-prometheus-metrics", "-prometheus-metrics-listen-port=9114", @@ -265,6 +288,7 @@ func TestHasDifferentArguments(t *testing.T) { fmt.Sprintf("-nginx-configmaps=%v/%v", namespace, name), fmt.Sprintf("-default-server-tls-secret=%v/%v", namespace, name), "-nginx-plus", + "-leader-election-lock-name=my-nginx-ingress-lock", }, }, instance: &k8sv1alpha1.NginxIngressController{ @@ -284,6 +308,7 @@ func TestHasDifferentArguments(t *testing.T) { fmt.Sprintf("-nginx-configmaps=%v/%v", namespace, name), fmt.Sprintf("-default-server-tls-secret=%v/%v", namespace, name), "-nginx-plus=false", + "-leader-election-lock-name=my-nginx-ingress-lock", }, }, instance: &k8sv1alpha1.NginxIngressController{ @@ -303,6 +328,7 @@ func TestHasDifferentArguments(t *testing.T) { fmt.Sprintf("-nginx-configmaps=%v/%v", namespace, name), "-default-server-tls-secret=default/mysecret", "-nginx-plus", + "-leader-election-lock-name=my-nginx-ingress-lock", }, }, instance: &k8sv1alpha1.NginxIngressController{ @@ -323,6 +349,7 @@ func TestHasDifferentArguments(t *testing.T) { "-nginx-configmaps=%v/%v", namespace, name), "-default-server-tls-secret=default/mysecret", "-nginx-plus", + "-leader-election-lock-name=my-nginx-ingress-lock", "-enable-custom-resources=false", }, }, @@ -342,8 +369,8 @@ func TestHasDifferentArguments(t *testing.T) { for _, test := range tests { result := hasDifferentArguments(test.container, test.instance) - if !reflect.DeepEqual(result, test.expected) { - t.Errorf("hasDifferentArguments(%+v, %+v) returned %v but expected %v", test.container, test.instance, result, test.expected) + if diff := cmp.Diff(test.expected, result); diff != "" { + t.Errorf("hasDifferentArguments(%+v, %+v) mismatch (-want +got):\n%s", test.container, test.instance, diff) } } }