diff --git a/deploy/crds/k8s.nginx.org_nginxingresscontrollers_crd.yaml b/deploy/crds/k8s.nginx.org_nginxingresscontrollers_crd.yaml index 83427bb9..b6450d14 100644 --- a/deploy/crds/k8s.nginx.org_nginxingresscontrollers_crd.yaml +++ b/deploy/crds/k8s.nginx.org_nginxingresscontrollers_crd.yaml @@ -207,12 +207,28 @@ spec: description: 'Specifies the name of the service with the type LoadBalancer through which the Ingress controller pods are exposed externally. The external address of the service is used when reporting the - status of Ingress resources. Note: Only if ServiceType is different - than LoadBalancer.' + status of Ingress resources. Note: Only if serviceType is NodePort.' + type: string + ingressLink: + description: 'Specifies the name of the IngressLink resource, which + exposes the Ingress Controller pods via a BIG-IP system. The IP + of the BIG-IP system is used when reporting the status of Ingress, + VirtualServer and VirtualServerRoute resources. Requires reportIngressStatus.enable + set to true. Note: Only if serviceType is NodePort and reportIngressStatus.externalService + is not set.' type: string required: - enable type: object + service: + description: The service of the Ingress controller. + properties: + extraLabels: + additionalProperties: + type: string + description: Specifies extra labels of the service. + type: object + type: object serviceType: description: 'The type of the Service for the Ingress Controller. Valid Service types are: NodePort and LoadBalancer.' diff --git a/docs/nginx-ingress-controller.md b/docs/nginx-ingress-controller.md index d999febd..8ffb82f3 100644 --- a/docs/nginx-ingress-controller.md +++ b/docs/nginx-ingress-controller.md @@ -43,6 +43,7 @@ spec: serviceType: NodePort enableCRDs: true enableSnippets: false + enablePreviewPolicies: false defaultSecret: my-nginx-ingress/default-secret ingressClass: my-nginx-ingress useIngressClassOnly: true @@ -83,8 +84,10 @@ spec: | `defaultSecret` | `string` | The TLS Secret for TLS termination of the default server. The format is namespace/name. The secret must be of the type kubernetes.io/tls. If not specified, the operator will generate and deploy a TLS Secret with a self-signed certificate and key. | No | | `serviceType` | `string` | The type of the Service for the Ingress Controller. Valid Service types are `NodePort` or `LoadBalancer`. | Yes | | `enableCRDs` | `boolean` | Enables the use of NGINX Ingress Resource Definitions (VirtualServer and VirtualServerRoute). | No | -| `enableSnippets` | `boolean` | Enable custom NGINX configuration snippets in VirtualServer and VirtualServerRoute resources. Requires enableCRDs set to true. | No | +| `enableSnippets` | `boolean` | Enable custom NGINX configuration snippets in VirtualServer and VirtualServerRoute resources. Requires `enableCRDs` set to `true`. | No | +| `enablePreviewPolicies` | `boolean` | Enables preview policies. Requires `enableCRDs` set to `true`. | No | | `ingressClass` | `string` | A class of the Ingress controller. For Kubernetes >= 1.18, the Ingress controller only processes resources that belong to its class - i.e. have the "ingressClassName" field resource equal to the class. Additionally the Ingress Controller processes all the VirtualServer/VirtualServerRoute resources that do not have the "ingressClassName" field. For Kubernetes < 1.18, the Ingress Controller only processes resources that belong to its class - i.e have the annotation "kubernetes.io/ingress.class" (for Ingress resources) or field "ingressClassName" (for VirtualServer/VirtualServerRoute resources) equal to the class. Additionally, the Ingress Controller processes resources that do not have the class set, which can be disabled by setting `useIngressClassOnly` to `true`. Default is `nginx`. | No | +| `service` | [service](#nginxingresscontrollerservice) | The service of the Ingress Controller. | No | | `useIngressClassOnly` | `boolean` | Ignore Ingress resources without the `"kubernetes.io/ingress.class"` annotation. For kubernetes versions >= 1.18 this flag will be IGNORED. | No | | `watchNamespace` | `boolean` | Namespace to watch for Ingress resources. By default the Ingress controller watches all namespaces. | No | | `healthStatus` | [healthStatus](#nginxingresscontrollerhealthstatus) | Adds a new location to the default server. The location responds with the 200 status code for any request. Useful for external health-checking of the Ingress Controller. | No | @@ -96,9 +99,9 @@ spec: | `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 | -| `globalConfiguration` | `string` | The GlobalConfiguration resource for global configuration of the Ingress Controller. Format is namespace/name. Requires enableCRDs set to true. | No | -| `enableTLSPassthrough` | `boolean` | Enable TLS Passthrough on port 443. Requires enableCRDs set to true. | No | -| `appprotect` | [appprotect](#nginxingresscontrollerappprotect) | App Protect support configuration. Requires nginxPlus set to true. | No | +| `globalConfiguration` | `string` | The GlobalConfiguration resource for global configuration of the Ingress Controller. Format is namespace/name. Requires `enableCRDs` set to `true`. | No | +| `enableTLSPassthrough` | `boolean` | Enable TLS Passthrough on port 443. Requires `enableCRDs` set to `true`. | No | +| `appprotect` | [appprotect](#nginxingresscontrollerappprotect) | App Protect support configuration. Requires `nginxPlus` set to `true`. | No | | `nginxReloadTimeout` | `int`| Timeout in milliseconds which the Ingress Controller will wait for a successful NGINX reload after a change or at the initial start. (default is 4000. Default is 20000 instead if `enable-app-protect` is true) | No | ## NginxIngressController.Image @@ -124,12 +127,19 @@ spec: | `port` | `int` | Set the port where the NGINX stub_status or the NGINX Plus API is exposed. Default is `8080`. Format is `1023 - 65535` | No | | `allowCidrs` | `string` | Whitelist IPv4 IP/CIDR blocks to allow access to NGINX stub_status or the NGINX Plus API. Separate multiple IP/CIDR by commas. (default `127.0.0.1`) | No | +## NginxIngressController.Service + +| Field | Type | Description | Required | +| --- | --- | --- | --- | +| `extraLabels` | `map[string]string` | Specifies extra labels of the service. | No | + ## NginxIngressController.ReportIngressStatus | Field | Type | Description | Required | | --- | --- | --- | --- | | `enable` | `boolean` | Enable reporting of the Ingress status. | Yes | -| `externalService` | `string` | Specifies the name of the service with the type LoadBalancer through which the Ingress controller pods are exposed externally. The external address of the service is used when reporting the status of Ingress resources. Note: Only if ServiceType is different than LoadBalancer. | No | +| `externalService` | `string` | Specifies the name of the service with the type LoadBalancer through which the Ingress controller pods are exposed externally. The external address of the service is used when reporting the status of Ingress resources. Note: Only if ServiceType is `NodePort`. | No | +| `ingressLink` | `string` | Specifies the name of the IngressLink resource, which exposes the Ingress Controller pods via a BIG-IP system. The IP of the BIG-IP system is used when reporting the status of Ingress, VirtualServer and VirtualServerRoute resources. Requires `reportIngressStatus.enable` set to `true`. Note: Only if ServiceType is `NodePort` and externalService is not set. | No | ## NginxIngressController.Prometheus diff --git a/pkg/apis/k8s/v1alpha1/nginxingresscontroller_types.go b/pkg/apis/k8s/v1alpha1/nginxingresscontroller_types.go index 3194bb0c..0b14b857 100644 --- a/pkg/apis/k8s/v1alpha1/nginxingresscontroller_types.go +++ b/pkg/apis/k8s/v1alpha1/nginxingresscontroller_types.go @@ -45,13 +45,17 @@ type NginxIngressControllerSpec struct { // +kubebuilder:validation:Optional // +operator-sdk:gen-csv:customresourcedefinitions.specDescriptors=true EnablePreviewPolicies bool `json:"enablePreviewPolicies"` - // +kubebuilder:validation:Optional // A class of the Ingress controller. The Ingress controller only processes Ingress resources that belong to its // class (in other words, have the annotation “kubernetes.io/ingress.class”). // Additionally, the Ingress controller processes Ingress resources that do not have that annotation, // which can be disabled by setting UseIngressClassOnly to true. Default is `nginx`. + // +kubebuilder:validation:Optional // +operator-sdk:gen-csv:customresourcedefinitions.specDescriptors=true IngressClass string `json:"ingressClass"` + // The service of the Ingress controller. + // +kubebuilder:validation:Optional + // +operator-sdk:gen-csv:customresourcedefinitions.specDescriptors=true + Service *Service `json:"service"` // Ignore Ingress resources without the “kubernetes.io/ingress.class” annotation. // +kubebuilder:validation:Optional // +operator-sdk:gen-csv:customresourcedefinitions.specDescriptors=true @@ -184,9 +188,16 @@ type ReportIngressStatus struct { Enable bool `json:"enable"` // Specifies the name of the service with the type LoadBalancer through which the Ingress controller pods are exposed externally. // The external address of the service is used when reporting the status of Ingress resources. - // Note: Only if ServiceType is different than LoadBalancer. + // Note: Only if serviceType is NodePort. // +kubebuilder:validation:Optional ExternalService string `json:"externalService"` + // Specifies the name of the IngressLink resource, which exposes the Ingress Controller pods via a BIG-IP system. + // The IP of the BIG-IP system is used when reporting the status of Ingress, VirtualServer and VirtualServerRoute resources. + // Requires reportIngressStatus.enable set to true. + // Note: Only if serviceType is NodePort and reportIngressStatus.externalService is not set. + // +kubebuilder:validation:Optional + // +operator-sdk:gen-csv:customresourcedefinitions.specDescriptors=true + IngressLink string `json:"ingressLink,omitempty"` } // Prometheus defines the Prometheus metrics for the Ingress Controller. @@ -202,12 +213,19 @@ type Prometheus struct { Port *uint16 `json:"port"` } -// App Protect support configuration. +// AppProtect support configuration. type AppProtect struct { // Enable App Protect. Enable bool `json:"enable"` } +// Service defines the Service for the Ingress Controller. +type Service struct { + // Specifies extra labels of the service. + // +kubebuilder:validation:Optional + ExtraLabels map[string]string `json:"extraLabels,omitempty"` +} + // NginxIngressControllerStatus defines the observed state of NginxIngressController type NginxIngressControllerStatus struct { // Deployed is true if the Operator has finished the deployment of the NginxIngressController. diff --git a/pkg/apis/k8s/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/k8s/v1alpha1/zz_generated.deepcopy.go index 3926a495..1ec61259 100644 --- a/pkg/apis/k8s/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/k8s/v1alpha1/zz_generated.deepcopy.go @@ -126,6 +126,11 @@ func (in *NginxIngressControllerSpec) DeepCopyInto(out *NginxIngressControllerSp *out = new(int32) **out = **in } + if in.Service != nil { + in, out := &in.Service, &out.Service + *out = new(Service) + (*in).DeepCopyInto(*out) + } if in.HealthStatus != nil { in, out := &in.HealthStatus, &out.HealthStatus *out = new(HealthStatus) @@ -244,3 +249,26 @@ func (in *ReportIngressStatus) DeepCopy() *ReportIngressStatus { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Service) DeepCopyInto(out *Service) { + *out = *in + if in.ExtraLabels != nil { + in, out := &in.ExtraLabels, &out.ExtraLabels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Service. +func (in *Service) DeepCopy() *Service { + if in == nil { + return nil + } + out := new(Service) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/controller/nginxingresscontroller/nginxingresscontroller_controller.go b/pkg/controller/nginxingresscontroller/nginxingresscontroller_controller.go index fe78c0aa..3eb2838c 100644 --- a/pkg/controller/nginxingresscontroller/nginxingresscontroller_controller.go +++ b/pkg/controller/nginxingresscontroller/nginxingresscontroller_controller.go @@ -32,9 +32,11 @@ import ( var log = logf.Log.WithName("controller_nginxingresscontroller") -const clusterRoleName = "nginx-ingress-role" -const sccName = "nginx-ingress-scc" -const finalizer = "finalizer.nginxingresscontroller.k8s.nginx.org" +const ( + clusterRoleName = "nginx-ingress-role" + sccName = "nginx-ingress-scc" + finalizer = "finalizer.nginxingresscontroller.k8s.nginx.org" +) // Add creates a new NginxIngressController Controller and adds it to the Manager. The Manager will set fields on the Controller // and Start it when the Manager is Started. diff --git a/pkg/controller/nginxingresscontroller/service.go b/pkg/controller/nginxingresscontroller/service.go index 3bd4e574..d07f66bf 100644 --- a/pkg/controller/nginxingresscontroller/service.go +++ b/pkg/controller/nginxingresscontroller/service.go @@ -12,6 +12,7 @@ func serviceForNginxIngressController(instance *k8sv1alpha1.NginxIngressControll ObjectMeta: v1.ObjectMeta{ Name: instance.Name, Namespace: instance.Namespace, + Labels: instance.Spec.Service.ExtraLabels, }, Spec: corev1.ServiceSpec{ Ports: []corev1.ServicePort{ diff --git a/pkg/controller/nginxingresscontroller/service_test.go b/pkg/controller/nginxingresscontroller/service_test.go index 074ddb24..9590483a 100644 --- a/pkg/controller/nginxingresscontroller/service_test.go +++ b/pkg/controller/nginxingresscontroller/service_test.go @@ -1,9 +1,9 @@ package nginxingresscontroller import ( - "reflect" "testing" + "github.com/google/go-cmp/cmp" k8sv1alpha1 "github.com/nginxinc/nginx-ingress-operator/pkg/apis/k8s/v1alpha1" corev1 "k8s.io/api/core/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -14,20 +14,26 @@ func TestServiceForNginxIngressController(t *testing.T) { name := "my-service" namespace := "my-nginx-ingress" serviceType := "LoadBalancer" + extraLabels := map[string]string{"app": "my-nginx-ingress"} instance := &k8sv1alpha1.NginxIngressController{ ObjectMeta: v1.ObjectMeta{ Name: name, Namespace: namespace, + Labels: extraLabels, }, Spec: k8sv1alpha1.NginxIngressControllerSpec{ ServiceType: serviceType, + Service: &k8sv1alpha1.Service{ + ExtraLabels: extraLabels, + }, }, } expected := &corev1.Service{ ObjectMeta: v1.ObjectMeta{ Name: name, Namespace: namespace, + Labels: extraLabels, }, Spec: corev1.ServiceSpec{ Ports: []corev1.ServicePort{ @@ -56,7 +62,7 @@ func TestServiceForNginxIngressController(t *testing.T) { } result := serviceForNginxIngressController(instance) - if !reflect.DeepEqual(result, expected) { - t.Errorf("serviceForNginxIngressController(%v) returned %+v but expected %+v", instance, result, expected) + if diff := cmp.Diff(expected, result); diff != "" { + t.Errorf("serviceForNginxIngressController() mismatch (-want +got):\n%s", diff) } } diff --git a/pkg/controller/nginxingresscontroller/utils.go b/pkg/controller/nginxingresscontroller/utils.go index adb428ae..155d776e 100644 --- a/pkg/controller/nginxingresscontroller/utils.go +++ b/pkg/controller/nginxingresscontroller/utils.go @@ -90,6 +90,8 @@ func generatePodArgs(instance *k8sv1alpha1.NginxIngressController) []string { args = append(args, fmt.Sprintf("-external-service=%v", instance.Spec.ReportIngressStatus.ExternalService)) } else if instance.Spec.ServiceType == "LoadBalancer" { args = append(args, fmt.Sprintf("-external-service=%v", instance.Name)) + } else if instance.Spec.ReportIngressStatus.IngressLink != "" { + args = append(args, fmt.Sprintf("-ingresslink=%v", instance.Spec.ReportIngressStatus.IngressLink)) } } diff --git a/pkg/controller/nginxingresscontroller/utils_test.go b/pkg/controller/nginxingresscontroller/utils_test.go index f8d8069c..d2954af9 100644 --- a/pkg/controller/nginxingresscontroller/utils_test.go +++ b/pkg/controller/nginxingresscontroller/utils_test.go @@ -111,9 +111,35 @@ func TestGeneratePodArgs(t *testing.T) { Namespace: namespace, }, Spec: k8sv1alpha1.NginxIngressControllerSpec{ - DefaultSecret: "my-nginx-ingress/my-secret", - ServiceType: "LoadBalancer", - ReportIngressStatus: &k8sv1alpha1.ReportIngressStatus{Enable: true}, + DefaultSecret: "my-nginx-ingress/my-secret", + ServiceType: "NodePort", + ReportIngressStatus: &k8sv1alpha1.ReportIngressStatus{ + Enable: true, + IngressLink: "my-ingresslink", + }, + }, + }, + expected: []string{ + "-nginx-configmaps=my-nginx-ingress/my-nginx-ingress", + "-default-server-tls-secret=my-nginx-ingress/my-secret", + "-enable-custom-resources=false", + "-report-ingress-status", + "-ingresslink=my-ingresslink", + }, + }, + { + instance: &k8sv1alpha1.NginxIngressController{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: k8sv1alpha1.NginxIngressControllerSpec{ + DefaultSecret: "my-nginx-ingress/my-secret", + ServiceType: "LoadBalancer", + ReportIngressStatus: &k8sv1alpha1.ReportIngressStatus{ + Enable: true, + IngressLink: "my-invalid-ingresslink", + }, }, }, expected: []string{ @@ -173,6 +199,7 @@ func TestGeneratePodArgs(t *testing.T) { ReportIngressStatus: &k8sv1alpha1.ReportIngressStatus{ Enable: true, ExternalService: "external", + IngressLink: "my-invalid-ingressLink", }, EnableLeaderElection: true, WildcardTLS: "my-nginx-ingress/wildcard-secret",