diff --git a/deploy/crds/k8s.nginx.org_nginxingresscontrollers_crd.yaml b/deploy/crds/k8s.nginx.org_nginxingresscontrollers_crd.yaml index fda0cb97..4526efe7 100644 --- a/deploy/crds/k8s.nginx.org_nginxingresscontrollers_crd.yaml +++ b/deploy/crds/k8s.nginx.org_nginxingresscontrollers_crd.yaml @@ -55,6 +55,15 @@ spec: controller reporting the status of Ingress resources – only one replica will report status. type: boolean + enableTLSPassthrough: + description: Enable TLS Passthrough on port 443. Requires enableCRDs + set to true. + type: boolean + globalConfiguration: + description: The GlobalConfiguration resource for global configuration + of the Ingress Controller. Format is namespace/name. Requires enableCRDs + set to true. + type: string healthStatus: description: Adds a new location to the default server. The location responds with the 200 status code for any request. Useful for external @@ -123,7 +132,7 @@ spec: commas. (default “127.0.0.1”) type: string enable: - description: Enable the NginxStatus. Default is true. + description: Enable the NginxStatus. type: boolean port: description: Set the port where the NGINX stub_status or the NGINX @@ -152,7 +161,7 @@ spec: type: object replicas: description: The number of replicas of the Ingress Controller pod. The - default is 1. Only applies if the Kind is set to deployment. + default is 1. Only applies if the type is set to deployment. format: int32 type: integer reportIngressStatus: diff --git a/docs/nginx-ingress-controller.md b/docs/nginx-ingress-controller.md index de3f5964..1afdbcfa 100644 --- a/docs/nginx-ingress-controller.md +++ b/docs/nginx-ingress-controller.md @@ -65,6 +65,8 @@ spec: port: 9114 configMapData: error-log-level: debug + enableTLSPassthrough: true + globalConfiguration: my-nginx-ingress/nginx-configuration ``` | Field | Type | Description | Required | @@ -88,7 +90,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. 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 | + ## NginxIngressController.Image | Field | Type | Description | Required | diff --git a/examples/deployment-oss-min/nginx-ingress-controller.yaml b/examples/deployment-oss-min/nginx-ingress-controller.yaml index fea09631..c8eb9eab 100644 --- a/examples/deployment-oss-min/nginx-ingress-controller.yaml +++ b/examples/deployment-oss-min/nginx-ingress-controller.yaml @@ -9,9 +9,7 @@ spec: image: repository: nginx/nginx-ingress tag: edge - pullPolicy: Always + pullPolicy: IfNotPresent replicas: 1 serviceType: NodePort - enableCRDs: true - nginxStatus: - enable: true \ No newline at end of file + enableCRDs: true \ No newline at end of file diff --git a/examples/deployment-plus-min/nginx-ingress-controller.yaml b/examples/deployment-plus-min/nginx-ingress-controller.yaml index 7fbb912b..7d70d40a 100644 --- a/examples/deployment-plus-min/nginx-ingress-controller.yaml +++ b/examples/deployment-plus-min/nginx-ingress-controller.yaml @@ -9,9 +9,7 @@ spec: image: repository: nginx-plus-ingress tag: edge - pullPolicy: Always + pullPolicy: IfNotPresent replicas: 1 serviceType: NodePort - enableCRDs: true - nginxStatus: - enable: true \ No newline at end of file + enableCRDs: true \ No newline at end of file diff --git a/pkg/apis/k8s/v1alpha1/nginxingresscontroller_types.go b/pkg/apis/k8s/v1alpha1/nginxingresscontroller_types.go index df183f5f..185b41f2 100644 --- a/pkg/apis/k8s/v1alpha1/nginxingresscontroller_types.go +++ b/pkg/apis/k8s/v1alpha1/nginxingresscontroller_types.go @@ -80,6 +80,15 @@ type NginxIngressControllerSpec struct { // +kubebuilder:validation:Optional // +nullable ConfigMapData map[string]string `json:"configMapData"` + // The GlobalConfiguration resource for global configuration of the Ingress Controller. + // Format is namespace/name. + // Requires enableCRDs set to true. + // +kubebuilder:validation:Optional + GlobalConfiguration string `json:"globalConfiguration"` + // Enable TLS Passthrough on port 443. + // Requires enableCRDs set to true. + // +kubebuilder:validation:Optional + EnableTLSPassthrough bool `json:"enableTLSPassthrough"` } // Image defines the Repository, Tag and ImagePullPolicy of the Ingress Controller Image. diff --git a/pkg/controller/nginxingresscontroller/crds.go b/pkg/controller/nginxingresscontroller/crds.go index cc9f1bd4..1d41a95b 100644 --- a/pkg/controller/nginxingresscontroller/crds.go +++ b/pkg/controller/nginxingresscontroller/crds.go @@ -54,3 +54,53 @@ func vsrForNginxIngressController() *v1beta1.CustomResourceDefinition { }, } } + +func gcForNginxIngressController() *v1beta1.CustomResourceDefinition { + return &v1beta1.CustomResourceDefinition{ + ObjectMeta: v1.ObjectMeta{ + Name: "globalconfigurations.k8s.nginx.org", + }, + Spec: v1beta1.CustomResourceDefinitionSpec{ + Group: "k8s.nginx.org", + Names: v1beta1.CustomResourceDefinitionNames{ + Plural: "globalconfigurations", + Singular: "globalconfiguration", + ShortNames: []string{"gc"}, + Kind: "GlobalConfiguration", + }, + Scope: "Namespaced", + Versions: []v1beta1.CustomResourceDefinitionVersion{ + { + Name: "v1alpha1", + Served: true, + Storage: true, + }, + }, + }, + } +} + +func tsForNginxIngressController() *v1beta1.CustomResourceDefinition { + return &v1beta1.CustomResourceDefinition{ + ObjectMeta: v1.ObjectMeta{ + Name: "transportservers.k8s.nginx.org", + }, + Spec: v1beta1.CustomResourceDefinitionSpec{ + Group: "k8s.nginx.org", + Names: v1beta1.CustomResourceDefinitionNames{ + Plural: "transportservers", + Singular: "transportserver", + ShortNames: []string{"ts"}, + Kind: "TransportServer", + }, + Scope: "Namespaced", + Versions: []v1beta1.CustomResourceDefinitionVersion{ + { + Name: "v1alpha1", + Served: true, + Storage: true, + }, + }, + }, + } +} diff --git a/pkg/controller/nginxingresscontroller/crds_test.go b/pkg/controller/nginxingresscontroller/crds_test.go index 50588b55..8ec71431 100644 --- a/pkg/controller/nginxingresscontroller/crds_test.go +++ b/pkg/controller/nginxingresscontroller/crds_test.go @@ -67,3 +67,63 @@ func TestVsrForNginxIngressController(t *testing.T) { t.Errorf("vsrForNginxIngressController() returned %+v but expected %+v", result, expected) } } + +func TestGcForNginxIngressController(t *testing.T) { + expected := &v1beta1.CustomResourceDefinition{ + ObjectMeta: v1.ObjectMeta{ + Name: "globalconfigurations.k8s.nginx.org", + }, + Spec: v1beta1.CustomResourceDefinitionSpec{ + Group: "k8s.nginx.org", + Names: v1beta1.CustomResourceDefinitionNames{ + Plural: "globalconfigurations", + Singular: "globalconfiguration", + ShortNames: []string{"gc"}, + Kind: "GlobalConfiguration", + }, + Scope: "Namespaced", + Versions: []v1beta1.CustomResourceDefinitionVersion{ + { + Name: "v1alpha1", + Served: true, + Storage: true, + }, + }, + }, + } + + result := gcForNginxIngressController() + if !reflect.DeepEqual(result, expected) { + t.Errorf("gcForNginxIngressController() returned %+v but expected %+v", result, expected) + } +} + +func TestTsForNginxIngressController(t *testing.T) { + expected := &v1beta1.CustomResourceDefinition{ + ObjectMeta: v1.ObjectMeta{ + Name: "transportservers.k8s.nginx.org", + }, + Spec: v1beta1.CustomResourceDefinitionSpec{ + Group: "k8s.nginx.org", + Names: v1beta1.CustomResourceDefinitionNames{ + Plural: "transportservers", + Singular: "transportserver", + ShortNames: []string{"ts"}, + Kind: "TransportServer", + }, + Scope: "Namespaced", + Versions: []v1beta1.CustomResourceDefinitionVersion{ + { + Name: "v1alpha1", + Served: true, + Storage: true, + }, + }, + }, + } + + result := tsForNginxIngressController() + if !reflect.DeepEqual(result, expected) { + t.Errorf("tsForNginxIngressController() returned %+v but expected %+v", result, expected) + } +} diff --git a/pkg/controller/nginxingresscontroller/nginxingresscontroller_controller.go b/pkg/controller/nginxingresscontroller/nginxingresscontroller_controller.go index dedf00ce..174c8bc7 100644 --- a/pkg/controller/nginxingresscontroller/nginxingresscontroller_controller.go +++ b/pkg/controller/nginxingresscontroller/nginxingresscontroller_controller.go @@ -59,13 +59,24 @@ func createCommonResources(mgr manager.Manager, sccAPIExists bool) error { err := clientReader.Get(context.TODO(), types.NamespacedName{Name: clusterRoleName, Namespace: v1.NamespaceAll}, cr) - if err != nil && errors.IsNotFound(err) { - reqLogger.Info("no previous ClusterRole found, creating a new one.") - err = clientWriter.Create(context.TODO(), cr) - } - if err != nil { - return fmt.Errorf("error creating ClusterRole: %v", err) + if errors.IsNotFound(err) { + reqLogger.Info("no previous ClusterRole found, creating a new one.") + err = clientWriter.Create(context.TODO(), cr) + if err != nil { + return fmt.Errorf("error creating ClusterRole: %v", err) + } + } else { + return fmt.Errorf("error getting ClusterRole: %v", err) + } + } else { + // For updates in the ClusterRole permissions (eg new CRDs of the Ingress Controller). + reqLogger.Info("previous ClusterRole found, updating.") + cr := clusterRoleForNginxIngressController(clusterRoleName) + err = clientWriter.Update(context.TODO(), cr) + if err != nil { + return fmt.Errorf("error updating ClusterRole: %v", err) + } } crb := clusterRoleBindingForNginxIngressController(clusterRoleName) @@ -91,7 +102,6 @@ func createCommonResources(mgr manager.Manager, sccAPIExists bool) error { vs := vsForNginxIngressController() _, err = crdsClient.Create(vs) - // if already exists, pass the error silently if err != nil && errors.IsAlreadyExists(err) { reqLogger.Info("VirtualServer CRD already present, skipping creation.") } else if err != nil { @@ -106,6 +116,22 @@ func createCommonResources(mgr manager.Manager, sccAPIExists bool) error { return err } + gc := gcForNginxIngressController() + _, err = crdsClient.Create(gc) + if err != nil && errors.IsAlreadyExists(err) { + reqLogger.Info("GlobalConfiguration CRD already present, skipping creation.") + } else if err != nil { + return err + } + + ts := tsForNginxIngressController() + _, err = crdsClient.Create(ts) + if err != nil && errors.IsAlreadyExists(err) { + reqLogger.Info("TransportServer CRD already present, skipping creation.") + } else if err != nil { + return err + } + if sccAPIExists { reqLogger.Info("OpenShift detected as platform.") diff --git a/pkg/controller/nginxingresscontroller/rbac.go b/pkg/controller/nginxingresscontroller/rbac.go index 2db6a347..c9bbed9f 100644 --- a/pkg/controller/nginxingresscontroller/rbac.go +++ b/pkg/controller/nginxingresscontroller/rbac.go @@ -45,7 +45,7 @@ func clusterRoleForNginxIngressController(name string) *rbacv1.ClusterRole { { Verbs: []string{"get", "list", "watch"}, APIGroups: []string{"k8s.nginx.org"}, - Resources: []string{"virtualservers", "virtualserverroutes"}, + Resources: []string{"virtualservers", "virtualserverroutes", "globalconfigurations", "transportservers"}, }, } diff --git a/pkg/controller/nginxingresscontroller/rbac_test.go b/pkg/controller/nginxingresscontroller/rbac_test.go index 9307c1e5..2c3442e1 100644 --- a/pkg/controller/nginxingresscontroller/rbac_test.go +++ b/pkg/controller/nginxingresscontroller/rbac_test.go @@ -53,7 +53,7 @@ func TestClusterRoleForNginxIngressController(t *testing.T) { { Verbs: []string{"get", "list", "watch"}, APIGroups: []string{"k8s.nginx.org"}, - Resources: []string{"virtualservers", "virtualserverroutes"}, + Resources: []string{"virtualservers", "virtualserverroutes", "globalconfigurations", "transportservers"}, }, }, } diff --git a/pkg/controller/nginxingresscontroller/utils.go b/pkg/controller/nginxingresscontroller/utils.go index cf33da7b..b4ce6079 100644 --- a/pkg/controller/nginxingresscontroller/utils.go +++ b/pkg/controller/nginxingresscontroller/utils.go @@ -3,6 +3,7 @@ package nginxingresscontroller import ( "fmt" "reflect" + "strings" k8sv1alpha1 "github.com/nginxinc/nginx-ingress-operator/pkg/apis/k8s/v1alpha1" secv1 "github.com/openshift/api/security/v1" @@ -13,6 +14,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client/config" ) +const apiVersionUnsupportedError = "server does not support API version" + // generatePodArgs generate a list of arguments for the Ingress Controller pods based on the CRD. func generatePodArgs(instance *k8sv1alpha1.NginxIngressController) []string { var args []string @@ -98,6 +101,16 @@ func generatePodArgs(instance *k8sv1alpha1.NginxIngressController) []string { } } + if instance.Spec.EnableCRDs { + if instance.Spec.EnableTLSPassthrough { + args = append(args, "-enable-tls-passthrough") + } + + if instance.Spec.GlobalConfiguration != "" { + args = append(args, fmt.Sprintf("-global-configuration=%v", instance.Spec.GlobalConfiguration)) + } + } + return args } @@ -143,6 +156,10 @@ func VerifySCCAPIExists() (bool, error) { err = discovery.ServerSupportsVersion(clientSet, gv) if err != nil { + // This error means the call could not find SCC in the API, but there was no API error. + if strings.Contains(err.Error(), apiVersionUnsupportedError) { + return false, nil + } return false, err } diff --git a/pkg/controller/nginxingresscontroller/utils_test.go b/pkg/controller/nginxingresscontroller/utils_test.go index ee906411..491409c0 100644 --- a/pkg/controller/nginxingresscontroller/utils_test.go +++ b/pkg/controller/nginxingresscontroller/utils_test.go @@ -120,6 +120,25 @@ func TestGeneratePodArgs(t *testing.T) { fmt.Sprintf("-external-service=%v", name), }, }, + { + instance: &k8sv1alpha1.NginxIngressController{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: k8sv1alpha1.NginxIngressControllerSpec{ + EnableCRDs: true, + EnableTLSPassthrough: true, + GlobalConfiguration: "my-nginx-ingress/globalconfiguration", + }, + }, + expected: []string{ + "-nginx-configmaps=my-nginx-ingress/my-nginx-ingress", + "-default-server-tls-secret=my-nginx-ingress/my-nginx-ingress", + "-enable-tls-passthrough", + "-global-configuration=my-nginx-ingress/globalconfiguration", + }, + }, { instance: &k8sv1alpha1.NginxIngressController{ ObjectMeta: metav1.ObjectMeta{ @@ -154,6 +173,8 @@ func TestGeneratePodArgs(t *testing.T) { Enable: true, Port: 9114, }, + GlobalConfiguration: "my-nginx-ingress/globalconfiguration", + EnableTLSPassthrough: true, }, }, expected: []string{