diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d12a115..d773e39 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,2 +1,2 @@ # Primary owner should be listed first in list of global owners, followed by any secondary owners -* @vbontempi @toddgiguere +* @vbontempi @vkuma17 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 577cfcf..95b2acb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,8 +8,5 @@ on: jobs: call-terraform-ci-pipeline: - uses: terraform-ibm-modules/common-pipeline-assets/.github/workflows/common-terraform-module-ci-v2.yml@v1.22.5 + uses: terraform-ibm-modules/common-pipeline-assets/.github/workflows/common-terraform-module-ci-v2.yml@v1.24.0 secrets: inherit - with: - craSCCv2: true - craConfigYamlFile: "cra-config.yaml" diff --git a/.gitignore b/.gitignore index f0743a6..6ccec10 100644 --- a/.gitignore +++ b/.gitignore @@ -73,7 +73,6 @@ terraform.rc # Go workspace file go.work -**/ge_topology - -# to remove -modules/sm-istio-ingress-sdnlb +# ge examples +examples/ge_topology +examples/securetopology diff --git a/.secrets.baseline b/.secrets.baseline index 40af044..397ecb2 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -3,7 +3,7 @@ "files": "go.sum|^.secrets.baseline$", "lines": null }, - "generated_at": "2025-11-14T11:16:57Z", + "generated_at": "2025-12-12T17:11:00Z", "plugins_used": [ { "name": "AWSKeyDetector" @@ -82,7 +82,7 @@ "hashed_secret": "ff9ee043d85595eb255c05dfe32ece02a53efbb2", "is_secret": false, "is_verified": false, - "line_number": 73, + "line_number": 97, "type": "Secret Keyword", "verified_result": null } diff --git a/README.md b/README.md index ed7517a..f59ecfb 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -# Terraform modules template project +# Red Hat OpenShift Container Platform Service Mesh module +For more details about the Red Hat OpenShift Service Mesh, see [Red Hat OpenShift Service Mesh 3.0](https://docs.redhat.com/en/documentation/red_hat_openshift_service_mesh/3.0) and [Installing Red Hat OpenShift Service Mesh](https://docs.redhat.com/en/documentation/red_hat_openshift_service_mesh/3.0/html/installing/ossm-installing-service-mesh) + +### Service Mesh discovery selectors + +The submodule [modules/sm-istio](./modules/sm-istio) supports configuring Service Mesh discovery selectors, to configure each Istio controlplane workloads discovery attributes. + +For more details about Service Mesh discovery selectors, see [Scoping the Service Mesh with discovery selectors](https://docs.redhat.com/en/documentation/red_hat_openshift_service_mesh/3.0/html/installing/ossm-installing-service-mesh#ossm-scoping-service-mesh-with-discoveryselectors_ossm-installing-openshift-service-mesh) + +### Service Mesh sidecar injection + +The submodule [modules/sm-istio](./modules/sm-istio) supports configuring Service Mesh sidecar injection, to configure each Istio controlplane to inject with sidecar proxies the workloads according to specific attributes + +This module supports sidecar inject at namespace level in this moment, following the rules below: + +| IstioRevision name | Enabled label & value | Disabled value | +| --- | --- | --- | +| default | istio-injection=enabled | istio-injection=disabled | +| not default - i.e. `my-mesh-1` | istio.io/rev=my-mesh-1 | istio-injection=disabled | + +For more details about Service Mesh sidecar injection, see [Sidecar injection](https://docs.redhat.com/en/documentation/red_hat_openshift_service_mesh/3.0/html/installing/ossm-sidecar-injection#ossm-sidecar-injection) + +For more details about excluding single workload from the Service Mesh, see [Exclude a workload from the mesh](https://docs.redhat.com/en/documentation/red_hat_openshift_service_mesh/3.0/html/installing/ossm-sidecar-injection#ossm-enabling-sidecar-injection-exclude-workload-from-mesh_ossm-sidecar-injection) + +### Multiple Service Mesh controlplanes deployment on the same cluster -TODO: Replace this with a description of the modules in this repo. +By appropriately configuring the controlplanes discovery selectors and sidecar injection properties with multiple instances of [modules/sm-istio](./modules/sm-istio) this module allows to deploy multiple controlplanes on the sidecar, each one discovering the appropriate workloads and injecting the related sidecars. +https://docs.redhat.com/en/documentation/red_hat_openshift_service_mesh/3.0/html/installing/ossm-deploying-multiple-service-meshes-on-single-cluster#ossm-about-deploying-multiple-control-planes_ossm-deploying-multiple-service-meshes-on-single-cluster + +#### Gateway injection + +The submodule [modules/sm-istio-ingress](./modules/sm-istio-ingress) and [modules/sm-istio-egress](./modules/sm-istio-egress), through allows to deploy ingress and egress istio gateways into the cluster through the Gateway injection. Gateway injection relies upon the same mechanism as sidecar injection to inject the Envoy proxy into gateway pods. To install a gateway using gateway injection, you create a Kubernetes Deployment object and an associated Kubernetes Service object in a namespace that is visible to the Istio control plane. When creating the Deployment object you label and annotate it so that the Istio control plane injects a proxy, and the proxy is configured as a gateway. After installing the gateway, you configure it to control ingress and egress traffic using the Istio Gateway and VirtualService resources. + +For more details about Gateway injection, see [Gateways](https://docs.redhat.com/en/documentation/red_hat_openshift_service_mesh/3.0/html/gateways/index) and [About gateway injection](https://docs.redhat.com/en/documentation/red_hat_openshift_service_mesh/3.0/html/gateways/ossm-about-gateways#ossm-about-gateway-injection_ossm-about-gateways) ## Overview * [terraform-ibm-ocp-service-mesh](#terraform-ibm-ocp-service-mesh) * [Submodules](./modules) + * [sm-istio-egress](./modules/sm-istio-egress) + * [sm-istio-ingress](./modules/sm-istio-ingress) + * [sm-istio](./modules/sm-istio) * [Examples](./examples) + *
Basic OCP cluster single zone and single subnet with RedHat ServiceMesh v3
Deploy to IBM Cloud button
+ *
RedHat ServiceMesh v3 on existing cluster
Deploy to IBM Cloud button
* [Contributing](#contributing) - - ```hcl terraform { required_version = ">= 1.9.0" @@ -74,45 +98,131 @@ provider "ibm" { region = local.region } -module "module_template" { - source = "terraform-ibm-modules//ibm" - version = "X.Y.Z" # Replace "X.Y.Z" with a release version to lock into a specific release - region = local.region - name = "instance-name" - resource_group_id = "xxXXxxXXxXxXXXXxxXxxxXXXXxXXXXX" # Replace with the actual ID of resource group to use +provider "helm" { + kubernetes = { + host = data.ibm_container_cluster_config.cluster_config.host + token = data.ibm_container_cluster_config.cluster_config.token + cluster_ca_certificate = data.ibm_container_cluster_config.cluster_config.ca_certificate + } +} + +provider "kubernetes" { + host = data.ibm_container_cluster_config.cluster_config.host + token = data.ibm_container_cluster_config.cluster_config.token + cluster_ca_certificate = data.ibm_container_cluster_config.cluster_config.ca_certificate +} + +data "ibm_container_cluster_config" "cluster_config" { + cluster_name_id = var.cluster_id + resource_group_id = var.resource_group_id + endpoint_type = "default" +} + +# deploy servicemesh operator +module "service_mesh_operator" { + source = "terraform-ibm-modules/ocp-service-mesh/ibm" + version = "X.Y.Z" + cluster_id = var.cluster_id + develop_mode = var.develop_mode + cluster_config_endpoint_type = var.cluster_config_endpoint_type +} + +# deploy servicemesh controlplane with istio resource +module "deploy_istio" { + depends_on = [module.service_mesh_operator] + source = "terraform-ibm-modules/ocp-service-mesh/ibm//modules/sm-istio" + version = "X.Y.Z" + name = "default" + namespace = "istio-system" + create_namespace = true + cluster_config_file_path = data.ibm_container_cluster_config.cluster_config.config_file_path +} + +# deploy servicemesh cni with istiocni resource +module "deploy_istio_cni" { + depends_on = [module.service_mesh_operator] + source = "terraform-ibm-modules/ocp-service-mesh/ibm//modules/sm-istio-cni" + version = "X.Y.Z" + namespace = "istio-system-cni" + create_namespace = true +} + +# wait for istio components to complete deployment and start +resource "time_sleep" "wait_istio" { + depends_on = [module.deploy_istio, module.deploy_istio_cni] + + create_duration = "300s" + destroy_duration = "60s" +} + +# deploy standard ingress gateway +module "basic_workload_ingress" { + depends_on = [time_sleep.wait_istio] + source = "terraform-ibm-modules/ocp-service-mesh/ibm//modules/sm-istio-ingress" + version = "X.Y.Z" + name = "basic-ingress" + namespace = "basic-ingress" + create_namespace = true + force_dataplane_update = true + ingress_loadbalancer_type = "alb" + ingress_service_type = "LoadBalancer" + ingress_ip_type = "public" + istio_mesh_enrollment = "default" + ingress_selectors = { + "istio" : "ingress-gateway", + } + ingress_ports = [ + { + "name" : "http2" + "port" : "80" + "targetPort" : "8000" + "proto" : "TCP" + } + ] + cluster_config_file_path = data.ibm_container_cluster_config.cluster_config.config_file_path +} + +# deploy standard egress gateway +module "default_workload_egress" { + depends_on = [time_sleep.wait_istio] + source = "terraform-ibm-modules/ocp-service-mesh/ibm//modules/sm-istio-egress" + version = "X.Y.Z" + name = "basic-egress" + namespace = "basic-egress" + create_namespace = false + force_dataplane_update = true + istio_mesh_enrollment = "default" + egress_selectors = { + "istio" : "egress-gateway", + } + egress_ports = [ + { + "name" : "http2" + "port" : "80" + "targetPort" : "8000" + "proto" : "TCP" + }, + { + "name" : "https" + "port" : "443" + "targetPort" : "443" + "proto" : "TCP" + } + ] + cluster_config_file_path = data.ibm_container_cluster_config.cluster_config.config_file_path } ``` ### Required access policies - +You need the following permissions to run this module. - - - +- IAM Services + - **Kubernetes** service + - `Viewer` platform access + - `Manager` service access - +For more information about the access you need to run Terraform IBM modules, see [IBM Cloud IAM roles](https://cloud.ibm.com/docs/account?topic=account-userroles). @@ -146,8 +256,7 @@ No modules. |------|-------------|------|---------|:--------:| | [cluster\_config\_endpoint\_type](#input\_cluster\_config\_endpoint\_type) | Specify which type of endpoint to use for for cluster config access: 'default', 'private', 'vpe', 'link'. 'default' value will use the default endpoint of the cluster. | `string` | `"default"` | no | | [cluster\_id](#input\_cluster\_id) | Id of the target IBM Cloud OpenShift Cluster | `string` | n/a | yes | -| [deploy\_operator](#input\_deploy\_operator) | Enable installing RedHat Service Mesh Operator | `bool` | `true` | no | -| [develop\_mode](#input\_develop\_mode) | If true, output more logs, and reduce some wait periods | `bool` | `false` | no | +| [develop\_mode](#input\_develop\_mode) | If true raise time waited for operator deployment and undeployment to allow to debug the cluster | `bool` | `false` | no | ### Outputs diff --git a/chart/istio-egress/.helmignore b/chart/istio-egress/.helmignore new file mode 100644 index 0000000..0e8a0eb --- /dev/null +++ b/chart/istio-egress/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/chart/istio-egress/Chart.yaml b/chart/istio-egress/Chart.yaml new file mode 100644 index 0000000..3faefcc --- /dev/null +++ b/chart/istio-egress/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: istio-ingress +description: A Helm chart for Kubernetes to deploy dataplane ingress resources for istio +type: application +version: 0.0.1 +appVersion: "0.0.1" diff --git a/chart/istio-egress/templates/deployment.yaml b/chart/istio-egress/templates/deployment.yaml new file mode 100644 index 0000000..2fc6099 --- /dev/null +++ b/chart/istio-egress/templates/deployment.yaml @@ -0,0 +1,90 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: egress-{{ .Values.egress.name }} + namespace: {{ .Values.egress.namespace }} +spec: + {{- if .Values.egress.replicacount }} + replicas: {{ .Values.egress.replicacount }} + {{- end }} + {{- if .Values.egress.istioselectors }} + selector: + matchLabels: + {{- toYaml .Values.egress.istioselectors | nindent 6 }} + {{- end }} + template: + metadata: + annotations: + inject.istio.io/templates: gateway + labels: + {{- if .Values.egress.istioselectors }} + {{- toYaml .Values.egress.istioselectors | nindent 8 }} + {{- end }} + sidecar.istio.io/inject: "true" + istio.io/gateway: "{{ .Values.egress.name }}.{{ .Values.egress.namespace }}" + spec: + {{- if .Values.egress.affinity }} + affinity: + {{- if .Values.egress.affinity.podAffinity }} + podAffinity: + {{- toYaml .Values.egress.affinity.podAffinity | nindent 10 }} + {{- end }} + {{- if .Values.egress.affinity.nodeAffinity }} + nodeAffinity: + {{- toYaml .Values.egress.affinity.nodeAffinity | nindent 10 }} + {{- end }} + {{- if .Values.egress.affinity.podAntiAffinity }} + podAntiAffinity: + {{- toYaml .Values.egress.affinity.podAntiAffinity | nindent 10 }} + {{- end }} + {{- end }} + serviceAccountName: egress-{{ .Values.egress.name }} + serviceAccount: egress-{{ .Values.egress.name }} + {{- if .Values.egress.terminationGracePeriodSeconds }} + terminationGracePeriodSeconds: {{ .Values.egress.terminationGracePeriodSeconds }} + {{- end }} + tolerations: + {{- if .Values.egress.tolerations }} + {{- toYaml .Values.egress.tolerations | nindent 6 }} + {{- end }} + containers: + - name: istio-proxy + image: auto # The image will automatically update each time the pod starts. + securityContext: + capabilities: + drop: + - ALL + allowPrivilegeEscalation: false + privileged: false + readOnlyRootFilesystem: true + runAsNonRoot: true + resources: + {{- if .Values.egress.resources }} + {{- toYaml .Values.egress.resources | nindent 10 }} + {{- end }} + ports: + - containerPort: 15090 + protocol: TCP + name: http-envoy-prom + {{- if .Values.egress.ports }} + {{- range .Values.egress.ports }} + - containerPort: {{ .port }} + protocol: {{ .proto | toString }} + name: {{ .name }} + {{- end }} + {{- end }} +#--- +# Allow outside traffic to access the gateway +# This is optional, only needed in case your cluster contains restrictive NetworkPolicies +#apiVersion: networking.k8s.io/v1 +#kind: NetworkPolicy +#metadata: +# name: gatewayegress +#spec: +# podSelector: +# matchLabels: +# istio: egressgateway +# egress: +# - {} +# policyTypes: +# - Egress diff --git a/chart/istio-egress/templates/hpa.yaml b/chart/istio-egress/templates/hpa.yaml new file mode 100644 index 0000000..cf8663c --- /dev/null +++ b/chart/istio-egress/templates/hpa.yaml @@ -0,0 +1,40 @@ +{{- if (.Values.egress.autoscale).enabled | default false -}} +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + labels: + {{- if .Values.egress.istioselectors }} + {{- toYaml .Values.egress.istioselectors | nindent 4 }} + {{- end }} + release: istio + name: egress-{{ .Values.egress.name }} + namespace: {{ .Values.egress.namespace }} +spec: + metrics: + {{- if .Values.egress.autoscale.cpu }} + {{- if .Values.egress.autoscale.cpu.targetavgutil }} + - resource: + name: cpu + target: + averageUtilization: {{ .Values.egress.autoscale.cpu.targetavgutil }} + type: Utilization + type: Resource + {{- end }} + {{- end }} + {{- if .Values.egress.autoscale.memory }} + {{- if .Values.egress.autoscale.memory.targetavgutil }} + - resource: + name: memory + target: + averageUtilization: {{ .Values.egress.autoscale.memory.targetavgutil }} + type: Utilization + type: Resource + {{- end }} + {{- end }} + minReplicas: {{ .Values.egress.autoscale.autoscaleMin }} + maxReplicas: {{ .Values.egress.autoscale.autoscaleMax }} + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: egress-{{ .Values.egress.name }} +{{- end }} diff --git a/chart/istio-egress/templates/networkpolicy.yaml b/chart/istio-egress/templates/networkpolicy.yaml new file mode 100644 index 0000000..5cb1a50 --- /dev/null +++ b/chart/istio-egress/templates/networkpolicy.yaml @@ -0,0 +1,14 @@ +# Allow outside traffic to access the gateway +# This is optional, only needed in case your cluster contains restrictive NetworkPolicies +#apiVersion: networking.k8s.io/v1 +#kind: NetworkPolicy +#metadata: +# name: gatewayegress +#spec: +# podSelector: +# matchLabels: +# istio: egressgateway +# egress: +# - {} +# policyTypes: +# - Egress diff --git a/chart/istio-egress/templates/pdb.yaml b/chart/istio-egress/templates/pdb.yaml new file mode 100644 index 0000000..9c7a70b --- /dev/null +++ b/chart/istio-egress/templates/pdb.yaml @@ -0,0 +1,20 @@ +{{- if .Values.egress.pdb }} +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + labels: + {{- toYaml .Values.egress.istioselectors | nindent 4 }} + release: istio + name: egress-{{ .Values.egress.name }} + namespace: {{ .Values.egress.namespace }} +spec: + {{- if .Values.egress.pdb.minAvailable }} + minAvailable: {{ .Values.egress.pdb.minAvailable }} + {{- end }} + {{- if .Values.egress.pdb.maxUnavailable }} + maxUnavailable: {{ .Values.egress.pdb.maxUnavailable }} + {{- end }} + selector: + matchLabels: + {{- toYaml .Values.egress.istioselectors | nindent 6 }} +{{- end }} diff --git a/chart/istio-egress/templates/sa.yaml b/chart/istio-egress/templates/sa.yaml new file mode 100644 index 0000000..4ab325c --- /dev/null +++ b/chart/istio-egress/templates/sa.yaml @@ -0,0 +1,28 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: egress-{{ .Values.egress.name }} + namespace: {{ .Values.egress.namespace }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: egress-{{ .Values.egress.name }} + namespace: {{ .Values.egress.namespace }} +rules: +- apiGroups: [""] + resources: ["secrets"] + verbs: ["get", "watch", "list"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: egress-{{ .Values.egress.name }} + namespace: {{ .Values.egress.namespace }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: egress-{{ .Values.egress.name }} +subjects: +- kind: ServiceAccount + name: egress-{{ .Values.egress.name }} diff --git a/chart/istio-egress/templates/svc.yaml b/chart/istio-egress/templates/svc.yaml new file mode 100644 index 0000000..aaa0c0f --- /dev/null +++ b/chart/istio-egress/templates/svc.yaml @@ -0,0 +1,23 @@ +apiVersion: v1 +kind: Service +metadata: + name: egress-{{ .Values.egress.name }} + namespace: {{ .Values.egress.namespace }} + annotations: +spec: + type: {{ .Values.egress.svctype }} + # externalTrafficPolicy: {{ .Values.egress.externalTrafficPolicy }} + # internalTrafficPolicy: {{ .Values.egress.internalTrafficPolicy }} + selector: + {{- if .Values.egress.istioselectors }} + {{- toYaml .Values.egress.istioselectors | nindent 4 }} + {{- end }} + ports: + {{- if .Values.egress.ports }} + {{- range .Values.egress.ports }} + - port: {{ .port }} + protocol: {{ .proto | toString }} + name: {{ .name | toString }} + targetPort: {{ .targetPort }} + {{- end }} + {{- end }} diff --git a/chart/istio-egress/values.yaml b/chart/istio-egress/values.yaml new file mode 100755 index 0000000..94f9b1c --- /dev/null +++ b/chart/istio-egress/values.yaml @@ -0,0 +1,43 @@ +egress: + name: default + namespace: istio-system + istioNamespaceEnrollmentLabels: {} + istioselectors: {} + ports: + - port: 80 + name: svc-http + proto: TCP # UDP + targetPort: 8080 + externalTrafficPolicy: Cluster + internalTrafficPolicy: Local + autoscale: + enabled: false + # autoscaleMin: 1 + # autoscaleMax: 3 + # cpu: + # targetavgutil: 80 + # memory: + # targetavgutil: 80 + pdb: + # minAvailable: 1 + # maxUnavailable: 2 + replicacount: 1 + resources: + # limits: + # cpu: 2000m + # memory: 1024Mi + # requests: + # cpu: 100m + # memory: 128Mi + # nodeselector: { "ibm-cloud.kubernetes.io/worker-pool-name": "default" } + terminationGracePeriodSeconds: 30 + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: ibm-cloud.kubernetes.io/worker-pool-name + operator: In + values: + - edge + tolerations: diff --git a/chart/istio-ingress/.helmignore b/chart/istio-ingress/.helmignore new file mode 100644 index 0000000..0e8a0eb --- /dev/null +++ b/chart/istio-ingress/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/chart/istio-ingress/Chart.yaml b/chart/istio-ingress/Chart.yaml new file mode 100644 index 0000000..3faefcc --- /dev/null +++ b/chart/istio-ingress/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: istio-ingress +description: A Helm chart for Kubernetes to deploy dataplane ingress resources for istio +type: application +version: 0.0.1 +appVersion: "0.0.1" diff --git a/chart/istio-ingress/templates/albdeployment.yaml b/chart/istio-ingress/templates/albdeployment.yaml new file mode 100644 index 0000000..9c2d137 --- /dev/null +++ b/chart/istio-ingress/templates/albdeployment.yaml @@ -0,0 +1,100 @@ +{{- if or (eq .Values.ingress.lbtype "alb") (eq .Values.ingress.lbtype "sdnlb") }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: ingress-{{ .Values.ingress.name }} + namespace: {{ .Values.ingress.namespace }} +spec: + {{- if .Values.ingress.replicacount }} + replicas: {{ .Values.ingress.replicacount }} + {{- end }} + {{- if .Values.ingress.istioselectors }} + selector: + matchLabels: + {{- if .Values.ingress.istioselectors }} + {{- toYaml .Values.ingress.istioselectors | nindent 6 }} + {{- end }} + {{- end }} + template: + metadata: + annotations: + inject.istio.io/templates: gateway + labels: + {{- if .Values.ingress.istioselectors }} + {{- toYaml .Values.ingress.istioselectors | nindent 8 }} + {{- end }} + sidecar.istio.io/inject: "true" + istio.io/gateway: "{{ .Values.ingress.name }}.{{ .Values.ingress.namespace }}" + spec: + {{- if .Values.ingress.nlbzones }} + nodeSelector: + {{- range $zone := .Values.ingress.nlbzones }} + ibm-cloud.kubernetes.io/zone: {{ $zone | quote }} + {{- end }} + {{- end }} + {{- if .Values.ingress.affinity }} + affinity: + {{- if .Values.ingress.affinity.podAffinity }} + podAffinity: + {{- toYaml .Values.ingress.affinity.podAffinity | nindent 10 }} + {{- end }} + {{- if .Values.ingress.affinity.nodeAffinity }} + nodeAffinity: + {{- toYaml .Values.ingress.affinity.nodeAffinity | nindent 10 }} + {{- end }} + {{- if .Values.ingress.affinity.podAntiAffinity }} + podAntiAffinity: + {{- toYaml .Values.ingress.affinity.podAntiAffinity | nindent 10 }} + {{- end }} + {{- end }} + serviceAccountName: ingress-{{ .Values.ingress.name }} + serviceAccount: ingress-{{ .Values.ingress.name }} + {{- if .Values.ingress.terminationGracePeriodSeconds }} + terminationGracePeriodSeconds: {{ .Values.ingress.terminationGracePeriodSeconds }} + {{- end }} + tolerations: + {{- if .Values.ingress.tolerations }} + {{- toYaml .Values.ingress.tolerations | nindent 6 }} + {{- end }} + containers: + - name: istio-proxy + image: auto # The image will automatically update each time the pod starts. + securityContext: + capabilities: + drop: + - ALL + allowPrivilegeEscalation: false + privileged: false + readOnlyRootFilesystem: true + runAsNonRoot: true + resources: + {{- if .Values.ingress.resources }} + {{- toYaml .Values.ingress.resources | nindent 10 }} + {{- end }} + ports: + - containerPort: 15090 + protocol: TCP + name: http-envoy-prom + {{- if .Values.ingress.ports }} + {{- range .Values.ingress.ports }} + - containerPort: {{ .port }} + protocol: {{ .proto | toString }} + name: {{ .name }} + {{- end }} + {{- end }} +{{- end }} +#--- +# Allow outside traffic to access the gateway +# This is optional, only needed in case your cluster contains restrictive NetworkPolicies +#apiVersion: networking.k8s.io/v1 +#kind: NetworkPolicy +#metadata: +# name: gatewayingress +#spec: +# podSelector: +# matchLabels: +# istio: ingressgateway +# ingress: +# - {} +# policyTypes: +# - Ingress diff --git a/chart/istio-ingress/templates/albsvc.yaml b/chart/istio-ingress/templates/albsvc.yaml new file mode 100644 index 0000000..2d0f63a --- /dev/null +++ b/chart/istio-ingress/templates/albsvc.yaml @@ -0,0 +1,40 @@ +{{- if or (eq .Values.ingress.lbtype "alb") (eq .Values.ingress.lbtype "sdnlb") }} +apiVersion: v1 +kind: Service +metadata: + name: ingress-{{ .Values.ingress.name }} + namespace: {{ .Values.ingress.namespace }} + annotations: + {{- if (eq .Values.ingress.lbtype "alb") }} + service.kubernetes.io/ibm-load-balancer-cloud-provider-enable-features: "alb" + service.kubernetes.io/ibm-load-balancer-cloud-provider-vpc-subnets: {{ join "," .Values.ingress.albsubnets | quote }} + service.kubernetes.io/ibm-load-balancer-cloud-provider-ip-type: {{ .Values.ingress.lbiptype }} + {{- if .Values.ingress.proxyProtocol }} + {{- if eq (default .Values.ingress.proxyProtocol.enabled false) true }} + service.kubernetes.io/ibm-load-balancer-cloud-provider-enable-features: "proxy-protocol" + {{- end }} + {{- end }} + {{- end }} + {{- if (eq .Values.ingress.lbtype "sdnlb") }} + service.kubernetes.io/ibm-load-balancer-cloud-provider-enable-features: service-dnlb + service.kubernetes.io/ibm-load-balancer-cloud-provider-ip-type: private + service.kubernetes.io/ibm-load-balancer-cloud-provider-vpc-node-selector: {{ .Values.ingress.sdnlbWorkerPoolSelector.name | quote }}={{ .Values.ingress.sdnlbWorkerPoolSelector.value | quote }} + {{- end }} +spec: + type: {{ .Values.ingress.svctype }} + externalTrafficPolicy: {{ .Values.ingress.externalTrafficPolicy }} + internalTrafficPolicy: {{ .Values.ingress.internalTrafficPolicy }} + selector: + {{- if .Values.ingress.istioselectors }} + {{- toYaml .Values.ingress.istioselectors | nindent 4 }} + {{- end }} + ports: + {{- if .Values.ingress.ports }} + {{- range .Values.ingress.ports }} + - port: {{ .port }} + protocol: {{ .proto | toString }} + name: {{ .name | toString }} + targetPort: {{ .targetPort }} + {{- end }} + {{- end }} +{{- end }} diff --git a/chart/istio-ingress/templates/envoyproxy.yaml b/chart/istio-ingress/templates/envoyproxy.yaml new file mode 100644 index 0000000..73c7e49 --- /dev/null +++ b/chart/istio-ingress/templates/envoyproxy.yaml @@ -0,0 +1,40 @@ +# ingress-proxy-envoy-filter.yaml +{{- if .Values.ingress.proxyProtocol }} +{{- $ppwithout := .Values.ingress.proxyProtocol.allowWithoutProxyProtocol -}} +{{- if eq (default .Values.ingress.proxyProtocol.enabled false) true }} +apiVersion: networking.istio.io/v1alpha3 +kind: EnvoyFilter +metadata: + name: ingress-{{ .Values.ingress.name }} + namespace: {{ .Values.ingress.namespace }} +spec: + configPatches: + {{- if .Values.ingress.ports }} + {{- range $ports := .Values.ingress.ports }} + - applyTo: LISTENER + match: + listener: + portNumber: {{ $ports.targetPort }} + patch: + operation: MERGE + value: + listener_filters: + - name: envoy.filters.listener.proxy_protocol + typed_config: + '@type': type.googleapis.com/envoy.extensions.filters.listener.proxy_protocol.v3.ProxyProtocol + {{- if eq (default $ppwithout false) true }} + allow_requests_without_proxy_protocol: true + {{- end }} + - name: envoy.filters.listener.tls_inspector + typed_config: + '@type': type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector + {{- end }} + {{- end }} + workloadSelector: + {{- if .Values.ingress.istioselectors }} + labels: + {{- toYaml .Values.ingress.istioselectors | nindent 6 }} + {{- end }} +--- +{{- end }} +{{- end }} diff --git a/chart/istio-ingress/templates/hpa.yaml b/chart/istio-ingress/templates/hpa.yaml new file mode 100644 index 0000000..456c6ce --- /dev/null +++ b/chart/istio-ingress/templates/hpa.yaml @@ -0,0 +1,40 @@ +{{- if (.Values.ingress.autoscale).enabled | default false -}} +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + labels: + {{- if .Values.ingress.istioselectors }} + {{- toYaml .Values.ingress.istioselectors | nindent 4 }} + {{- end }} + release: istio + name: ingress-{{ .Values.ingress.name }} + namespace: {{ .Values.ingress.namespace }} +spec: + metrics: + {{- if .Values.ingress.autoscale.cpu }} + {{- if .Values.ingress.autoscale.cpu.targetavgutil }} + - resource: + name: cpu + target: + averageUtilization: {{ .Values.ingress.autoscale.cpu.targetavgutil }} + type: Utilization + type: Resource + {{- end }} + {{- end }} + {{- if .Values.ingress.autoscale.memory }} + {{- if .Values.ingress.autoscale.memory.targetavgutil }} + - resource: + name: memory + target: + averageUtilization: {{ .Values.ingress.autoscale.memory.targetavgutil }} + type: Utilization + type: Resource + {{- end }} + {{- end }} + minReplicas: {{ .Values.ingress.autoscale.autoscaleMin }} + maxReplicas: {{ .Values.ingress.autoscale.autoscaleMax }} + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: ingress-{{ .Values.ingress.name }} +{{- end }} diff --git a/chart/istio-ingress/templates/networkpolicy.yaml b/chart/istio-ingress/templates/networkpolicy.yaml new file mode 100644 index 0000000..0975477 --- /dev/null +++ b/chart/istio-ingress/templates/networkpolicy.yaml @@ -0,0 +1,14 @@ +# Allow outside traffic to access the gateway +# This is optional, only needed in case your cluster contains restrictive NetworkPolicies +#apiVersion: networking.k8s.io/v1 +#kind: NetworkPolicy +#metadata: +# name: gatewayingress +#spec: +# podSelector: +# matchLabels: +# istio: egressgateway +# ingress: +# - {} +# policyTypes: +# - Ingress diff --git a/chart/istio-ingress/templates/nlbdeployment.yaml b/chart/istio-ingress/templates/nlbdeployment.yaml new file mode 100644 index 0000000..ba97b46 --- /dev/null +++ b/chart/istio-ingress/templates/nlbdeployment.yaml @@ -0,0 +1,101 @@ +{{- if (eq .Values.ingress.lbtype "nlb") }} +{{- range $lb_subnet, $lb_subnet_zone := .Values.ingress.nlbzonessubnets }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: ingress-{{ $.Values.ingress.name }}-{{ $lb_subnet_zone }} + namespace: {{ $.Values.ingress.namespace }} +spec: + {{- if $.Values.ingress.replicacount }} + replicas: {{ $.Values.ingress.replicacount }} + {{- end }} + {{- if $.Values.ingress.istioselectors }} + selector: + matchLabels: + {{- if $.Values.ingress.istioselectors }} + {{- toYaml $.Values.ingress.istioselectors | nindent 6 }} + {{- end }} + {{- end }} + template: + metadata: + annotations: + inject.istio.io/templates: gateway + labels: + {{- if $.Values.ingress.istioselectors }} + {{- toYaml $.Values.ingress.istioselectors | nindent 8 }} + {{- end }} + sidecar.istio.io/inject: "true" + istio.io/gateway: "{{ $.Values.ingress.name }}.{{ $.Values.ingress.namespace }}" + spec: + {{- if $lb_subnet_zone }} + nodeSelector: + ibm-cloud.kubernetes.io/zone: {{ $lb_subnet_zone | quote }} + {{- end }} + {{- if $.Values.ingress.affinity }} + affinity: + {{- if $.Values.ingress.affinity.podAffinity }} + podAffinity: + {{- toYaml $.Values.ingress.affinity.podAffinity | nindent 10 }} + {{- end }} + {{- if $.Values.ingress.affinity.nodeAffinity }} + nodeAffinity: + {{- toYaml $.Values.ingress.affinity.nodeAffinity | nindent 10 }} + {{- end }} + {{- if $.Values.ingress.affinity.podAntiAffinity }} + podAntiAffinity: + {{- toYaml $.Values.ingress.affinity.podAntiAffinity | nindent 10 }} + {{- end }} + {{- end }} + serviceAccountName: ingress-{{ $.Values.ingress.name }} + serviceAccount: ingress-{{ $.Values.ingress.name }} + {{- if $.Values.ingress.terminationGracePeriodSeconds }} + terminationGracePeriodSeconds: {{ $.Values.ingress.terminationGracePeriodSeconds }} + {{- end }} + tolerations: + {{- if $.Values.ingress.tolerations }} + {{- toYaml $.Values.ingress.tolerations | nindent 6 }} + {{- end }} + containers: + - name: istio-proxy + image: auto + securityContext: + capabilities: + drop: + - ALL + allowPrivilegeEscalation: false + privileged: false + readOnlyRootFilesystem: true + runAsNonRoot: true + resources: + {{- if $.Values.ingress.resources }} + {{- toYaml $.Values.ingress.resources | nindent 10 }} + {{- end }} + ports: + - containerPort: 15090 + protocol: TCP + name: http-envoy-prom + {{- if $.Values.ingress.ports }} + {{- range $.Values.ingress.ports }} + - containerPort: {{ .port }} + protocol: {{ .proto | toString }} + name: {{ .name }} + {{- end }} + {{- end }} +--- +{{- end }} +{{- end }} +#--- +# Allow outside traffic to access the gateway +# This is optional, only needed in case your cluster contains restrictive NetworkPolicies +#apiVersion: networking.k8s.io/v1 +#kind: NetworkPolicy +#metadata: +# name: gatewayingress +#spec: +# podSelector: +# matchLabels: +# istio: ingressgateway +# ingress: +# - {} +# policyTypes: +# - Ingress diff --git a/chart/istio-ingress/templates/nlbsvc.yaml b/chart/istio-ingress/templates/nlbsvc.yaml new file mode 100644 index 0000000..01e5c34 --- /dev/null +++ b/chart/istio-ingress/templates/nlbsvc.yaml @@ -0,0 +1,31 @@ +{{- if (eq .Values.ingress.lbtype "nlb") }} +{{- range $lb_subnet, $lb_subnet_zone := .Values.ingress.nlbzonessubnets }} +apiVersion: v1 +kind: Service +metadata: + name: ingress-{{ $.Values.ingress.name }}-{{ $lb_subnet_zone }} + namespace: {{ $.Values.ingress.namespace }} + annotations: + service.kubernetes.io/ibm-load-balancer-cloud-provider-enable-features: "nlb" + service.kubernetes.io/ibm-load-balancer-cloud-provider-vpc-subnets: {{ $lb_subnet | quote}} + service.kubernetes.io/ibm-load-balancer-cloud-provider-ip-type: {{ $.Values.ingress.lbiptype }} +spec: + type: {{ $.Values.ingress.svctype }} + externalTrafficPolicy: {{ $.Values.ingress.externalTrafficPolicy }} + internalTrafficPolicy: {{ $.Values.ingress.internalTrafficPolicy }} + selector: + {{- if $.Values.ingress.istioselectors }} + {{- toYaml $.Values.ingress.istioselectors | nindent 4 }} + {{- end }} + ports: + {{- if $.Values.ingress.ports }} + {{- range $.Values.ingress.ports }} + - port: {{ .port }} + protocol: {{ .proto | toString }} + name: {{ .name | toString }} + targetPort: {{ .targetPort }} + {{- end }} + {{- end }} +--- +{{- end }} +{{- end }} diff --git a/chart/istio-ingress/templates/pdb.yaml b/chart/istio-ingress/templates/pdb.yaml new file mode 100644 index 0000000..1e8c51c --- /dev/null +++ b/chart/istio-ingress/templates/pdb.yaml @@ -0,0 +1,20 @@ +{{- if .Values.ingress.pdb }} +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + labels: + {{- toYaml .Values.ingress.istioselectors | nindent 4 }} + release: istio + name: ingress-{{ .Values.ingress.name }} + namespace: {{ .Values.ingress.namespace }} +spec: + {{- if .Values.ingress.pdb.minAvailable }} + minAvailable: {{ .Values.ingress.pdb.minAvailable }} + {{- end }} + {{- if .Values.ingress.pdb.maxUnavailable }} + maxUnavailable: {{ .Values.ingress.pdb.maxUnavailable }} + {{- end }} + selector: + matchLabels: + {{- toYaml .Values.ingress.istioselectors | nindent 6 }} +{{- end }} diff --git a/chart/istio-ingress/templates/sa.yaml b/chart/istio-ingress/templates/sa.yaml new file mode 100644 index 0000000..aff5235 --- /dev/null +++ b/chart/istio-ingress/templates/sa.yaml @@ -0,0 +1,28 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: ingress-{{ .Values.ingress.name }} + namespace: {{ .Values.ingress.namespace }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: ingress-{{ .Values.ingress.name }} + namespace: {{ .Values.ingress.namespace }} +rules: +- apiGroups: [""] + resources: ["secrets"] + verbs: ["get", "watch", "list"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: ingress-{{ .Values.ingress.name }} + namespace: {{ .Values.ingress.namespace }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: ingress-{{ .Values.ingress.name }} +subjects: +- kind: ServiceAccount + name: ingress-{{ .Values.ingress.name }} diff --git a/chart/istio-ingress/values.yaml b/chart/istio-ingress/values.yaml new file mode 100755 index 0000000..7ad4990 --- /dev/null +++ b/chart/istio-ingress/values.yaml @@ -0,0 +1,54 @@ +ingress: + name: default + namespace: istio-system + proxyProtocol: + enabled: false + allowWithoutProxyProtocol: false + svctype: LoadBalancer + lbtype: alb # nlb sdnlb + lbiptype: public # private + istioNamespaceEnrollmentLabels: {} + istioselectors: {} + albsubnets: + nlbzonessubnets: + sdnlbWorkerPoolSelector: + name: "ibm-cloud.kubernetes.io/worker-pool-name" + value: "transit" + ports: + - port: 80 + name: svc-http + proto: TCP # UDP + targetPort: 8080 + externalTrafficPolicy: Cluster + internalTrafficPolicy: Local + autoscale: + enabled: false + # autoscaleMin: 1 + # autoscaleMax: 3 + # cpu: + # targetavgutil: 80 + # memory: + # targetavgutil: 80 + pdb: + # minAvailable: 1 + # maxUnavailable: 2 + replicacount: 1 + resources: + # limits: + # cpu: 2000m + # memory: 1024Mi + # requests: + # cpu: 100m + # memory: 128Mi + # nodeselector: { "ibm-cloud.kubernetes.io/worker-pool-name": "default" } + terminationGracePeriodSeconds: 30 + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: ibm-cloud.kubernetes.io/worker-pool-name + operator: In + values: + - edge + tolerations: diff --git a/cra-config.yaml b/cra-config.yaml deleted file mode 100644 index 9a4c7fa..0000000 --- a/cra-config.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# -# Developer tips: -# - CRA = Code Risk Analyzer (more info on CRA: https://cloud.ibm.com/docs/code-risk-analyzer-cli-plugin?topic=code-risk-analyzer-cli-plugin-cra-cli-plugin) -# - Multiple directories can be scanned by CRA. Ensure if there are any deployable architecture in the repository that they are all scanned -# - More info about supported configurations at https://github.com/terraform-ibm-modules/common-pipeline-assets/blob/main/.github/workflows/terraform-test-pipeline.md#cra-config-yaml -# - -version: "v1" -CRA_TARGETS: - - CRA_TARGET: "examples/advanced" # Target directory for CRA scan. If not provided, the CRA Scan will not be run. - CRA_IGNORE_RULES_FILE: "cra-tf-validate-ignore-rules.json" - PROFILE_ID: "fe96bd4d-9b37-40f2-b39f-a62760e326a3" # SCC profile ID (currently set to 'IBM Cloud Framework for Financial Services' '1.7.0' profile). - # SCC_INSTANCE_ID: "" # The SCC instance ID to use to download profile for CRA scan. If not provided, a default global value will be used. - # SCC_REGION: "" # The IBM Cloud region that the SCC instance is in. If not provided, a default global value will be used. - CRA_ENVIRONMENT_VARIABLES: # An optional map of environment variables for CRA, where the key is the variable name and value is the value. Useful for providing TF_VARs. - TF_VAR_prefix: "mock" - TF_VAR_region: "us-south" diff --git a/cra-tf-validate-ignore-rules.json b/cra-tf-validate-ignore-rules.json deleted file mode 100644 index adbff6e..0000000 --- a/cra-tf-validate-ignore-rules.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "scc_rules": [] -} diff --git a/examples/basic/README.md b/examples/basic/README.md new file mode 100644 index 0000000..3420a98 --- /dev/null +++ b/examples/basic/README.md @@ -0,0 +1,3 @@ +# Basic OCP cluster single zone and single subnet with RedHat ServiceMesh v3 + +This sample deploys the RedHat Service Mesh operators, and a Service Mesh / istio control plane, a basic ingress and a basic egress in different namespaces diff --git a/examples/basic/main.tf b/examples/basic/main.tf index a85b19f..3ca94ce 100644 --- a/examples/basic/main.tf +++ b/examples/basic/main.tf @@ -98,7 +98,6 @@ data "ibm_container_cluster_config" "cluster_config" { module "service_mesh_operator" { source = "../.." cluster_id = module.ocp_base.cluster_id - deploy_operator = var.deploy_operator develop_mode = var.develop_mode cluster_config_endpoint_type = var.cluster_config_endpoint_type } @@ -125,3 +124,57 @@ resource "time_sleep" "wait_istio" { create_duration = "300s" destroy_duration = "60s" } + + +module "basic_workload_ingress" { + depends_on = [time_sleep.wait_istio] + source = "../../modules/sm-istio-ingress" + name = "basic-ingress" + namespace = "basic-ingress" + create_namespace = true + force_dataplane_update = false + ingress_loadbalancer_type = "alb" + ingress_service_type = "LoadBalancer" + ingress_ip_type = "public" + istio_mesh_enrollment = "default" + ingress_selectors = { + "istio" : "ingress-gateway", + } + ingress_ports = [ + { + "name" : "http2" + "port" : "80" + "targetPort" : "8000" + "proto" : "TCP" + } + ] + cluster_config_file_path = data.ibm_container_cluster_config.cluster_config.config_file_path +} + +module "default_workload_egress" { + depends_on = [time_sleep.wait_istio] + source = "../../modules/sm-istio-egress" + name = "basic-egress" + namespace = "basic-egress" + create_namespace = true + force_dataplane_update = true + istio_mesh_enrollment = "default" + egress_selectors = { + "istio" : "egress-gateway", + } + egress_ports = [ + { + "name" : "http2" + "port" : "80" + "targetPort" : "8000" + "proto" : "TCP" + }, + { + "name" : "https" + "port" : "443" + "targetPort" : "443" + "proto" : "TCP" + } + ] + cluster_config_file_path = data.ibm_container_cluster_config.cluster_config.config_file_path +} diff --git a/examples/basic/variables.tf b/examples/basic/variables.tf index ba39764..a61cbb8 100644 --- a/examples/basic/variables.tf +++ b/examples/basic/variables.tf @@ -27,12 +27,6 @@ variable "resource_tags" { default = [] } -variable "deploy_operator" { - type = bool - description = "Enable installing RedHat Service Mesh Operator" - default = true -} - variable "develop_mode" { type = bool description = "If true, output more logs, and reduce some wait periods" diff --git a/examples/exiting_cluster/README.md b/examples/exiting_cluster/README.md new file mode 100644 index 0000000..381112b --- /dev/null +++ b/examples/exiting_cluster/README.md @@ -0,0 +1,3 @@ +# RedHat ServiceMesh v3 on existing cluster + +This sample deploys the RedHat Service Mesh operators, and a Service Mesh / istio control plane, a customised ingress and a customised egress gateways in the same namespace diff --git a/examples/exiting_cluster/main.tf b/examples/exiting_cluster/main.tf index eb4dd50..2acd84c 100644 --- a/examples/exiting_cluster/main.tf +++ b/examples/exiting_cluster/main.tf @@ -30,7 +30,6 @@ data "ibm_container_cluster_config" "cluster_config" { module "service_mesh_operator" { source = "../.." cluster_id = var.existing_cluster_id - deploy_operator = var.deploy_operator develop_mode = var.develop_mode cluster_config_endpoint_type = var.cluster_config_endpoint_type } @@ -57,3 +56,81 @@ resource "time_sleep" "wait_istio" { create_duration = "300s" destroy_duration = "60s" } + +module "default_workload_ingress" { + depends_on = [time_sleep.wait_istio] + source = "../../modules/sm-istio-ingress" + name = "workload-ingress" + namespace = "default-workload" + create_namespace = true + force_dataplane_update = false + ingress_loadbalancer_type = "alb" + ingress_service_type = "LoadBalancer" + ingress_ip_type = "public" + istio_mesh_enrollment = "default" + ingress_selectors = { + "istio" : "default-workload-ingress", + } + ingress_ports = [ + { + "name" : "http2" + "port" : "80" + "targetPort" : "8000" + "proto" : "TCP" + }, + { + "name" : "istio-health" + "port" : "15021" + "targetPort" : "15021" + "proto" : "TCP" + } + ] + ingress_autoscale_configuration = { + "enabled" : false + } + ingress_pdb_configuration = { + "minAvailable" = "1" + } + ingress_replicas = 3 + ingress_resources_configuration = { + "limits" : { + "cpu" : "200m" + "memory" : "1024Mi" + }, + "requests" : { + "cpu" : "100m" + "memory" : "128Mi" + } + } + ingress_termination_grace_period = 30 + cluster_config_file_path = data.ibm_container_cluster_config.cluster_config.config_file_path +} + + +module "default_workload_egress" { + depends_on = [time_sleep.wait_istio] + source = "../../modules/sm-istio-egress" + name = "workload-eress" + namespace = "workload-default" + create_namespace = false + force_dataplane_update = true + istio_mesh_enrollment = "default" + egress_selectors = { + "istio" : "egress-gateway", + } + egress_ports = [ + { + "name" : "http2" + "port" : "80" + "targetPort" : "8000" + "proto" : "TCP" + }, + { + "name" : "https" + "port" : "443" + "targetPort" : "443" + "proto" : "TCP" + } + ] + cluster_config_file_path = data.ibm_container_cluster_config.cluster_config.config_file_path +} diff --git a/examples/exiting_cluster/variables.tf b/examples/exiting_cluster/variables.tf index fa80484..84fdbff 100644 --- a/examples/exiting_cluster/variables.tf +++ b/examples/exiting_cluster/variables.tf @@ -27,12 +27,6 @@ variable "resource_group" { default = null } -variable "deploy_operator" { - type = bool - description = "Enable installing RedHat Service Mesh Operator" - default = true -} - variable "develop_mode" { type = bool description = "If true, output more logs, and reduce some wait periods" diff --git a/examples/securetopology/main.tf b/examples/securetopology/main.tf deleted file mode 100644 index 68aee0a..0000000 --- a/examples/securetopology/main.tf +++ /dev/null @@ -1,231 +0,0 @@ -######################################################################################################################## -# VPC + Subnet + Public Gateway -# -# NOTE: This example deploys secure VPC deployment with 3 zones and 3 subnets in each zone with a public gateway enabled, that will allow -# all traffic ingress/egress by default. -# The three subnets allow to isolate the cluster nodes for their purpose: edge for public access enabled workers, default for workload deployment, transit for internal traffic -# For production use cases this would need to be enhanced by adding ACLs/Security Groups for network security. -######################################################################################################################## - -############################################################################## -# Locals -############################################################################## - -locals { - - # VPC Configuration - acl_rules_map = { - private = concat( - module.acl_profile.base_acl, - module.acl_profile.https_acl, - [ - { - name = "allow-workload-http-inbound" - source = "0.0.0.0/0" - action = "allow" - destination = "0.0.0.0/0" - direction = "inbound" - tcp = { - source_port_min = 1 - source_port_max = 65535 - port_min = 80 - port_max = 80 - } - }, - { - name = "allow-workload-http-outbound" - source = "0.0.0.0/0" - action = "allow" - destination = "0.0.0.0/0" - direction = "outbound" - tcp = { - source_port_min = 80 - source_port_max = 80 - port_min = 1 - port_max = 65535 - } - } - ], - module.acl_profile.deny_all_acl - ) - } - vpc_cidr_bases = { - private = "192.168.0.0/20", - transit = "192.168.16.0/20", - edge = "192.168.32.0/20" - } - - # OCP Configuration - ocp_worker_pools = [ - { - subnet_prefix = "private" - pool_name = "default" - machine_type = "bx2.4x16" - workers_per_zone = 1 - operating_system = "RHEL_9_64" - }, - { - subnet_prefix = "edge" - pool_name = "edge" - machine_type = "bx2.4x16" - workers_per_zone = 1 - operating_system = "RHEL_9_64" - } - , - { - subnet_prefix = "transit" - pool_name = "transit" - machine_type = "bx2.4x16" - workers_per_zone = 1 - operating_system = "RHEL_9_64" - } - ] - - worker_pools_taints = { - all = [] - transit = [ - { - key = "dedicated" - value = "transit" - # Pod is evicted from the node if it is already running on the node, - # and is not scheduled onto the node if it is not yet running on the node. - effect = "NoExecute" - } - ] - edge = [ - { - key = "dedicated" - value = "edge" - # Pod is evicted from the node if it is already running on the node, - # and is not scheduled onto the node if it is not yet running on the node. - effect = "NoExecute" - } - ] - default = [] - } - -} - -############################################################################## -# Resource Group -############################################################################## - -module "resource_group" { - source = "git::https://github.com/terraform-ibm-modules/terraform-ibm-resource-group.git?ref=v1.3.0" - # if an existing resource group is not set (null) create a new one using prefix - resource_group_name = var.resource_group == null ? "${var.prefix}-resource-group" : null - existing_resource_group_name = var.resource_group -} - -############################################################################## -# VPC ACLs -############################################################################## - -module "acl_profile" { - source = "git::https://github.ibm.com/GoldenEye/acl-profile-ocp.git?ref=1.3.5" -} - -############################################################################## -# VPC -############################################################################## - -module "vpc" { - source = "git::https://github.ibm.com/GoldenEye/vpc-module.git?ref=6.7.3" - unique_name = var.prefix - ibm_region = var.region - resource_group_id = module.resource_group.resource_group_id - cidr_bases = local.vpc_cidr_bases - acl_rules_map = local.acl_rules_map - virtual_private_endpoints = {} - vpc_tags = var.resource_tags -} - -############################################################################## -# OCP CLUSTER -############################################################################## - -module "ocp_base" { - source = "terraform-ibm-modules/base-ocp-vpc/ibm" - version = "3.71.4" - cluster_name = "${var.prefix}-cluster" - resource_group_id = module.resource_group.resource_group_id - region = var.region - force_delete_storage = true - vpc_id = module.vpc.vpc_id - vpc_subnets = module.vpc.subnets - worker_pools = local.ocp_worker_pools - worker_pools_taints = local.worker_pools_taints - tags = var.resource_tags - # outbound required by cluster proxy - disable_outbound_traffic_protection = true -} - -############################################################################ -# CLUSTER PROXY -############################################################################ - -module "cluster_proxy" { - source = "git::https://github.ibm.com/GoldenEye/cluster-proxy-module.git?ref=4.2.4" - cluster_id = module.ocp_base.cluster_id -} - -############################################################################## -# Init cluster config for helm and kubernetes providers -############################################################################## - -data "ibm_container_cluster_config" "cluster_config" { - cluster_name_id = module.ocp_base.cluster_id - resource_group_id = module.resource_group.resource_group_id -} - -# deploying servicemesh operator - -module "service_mesh_operator" { - source = "../.." - cluster_id = module.ocp_base.cluster_id - deploy_operator = var.deploy_operator - develop_mode = var.develop_mode - cluster_config_endpoint_type = var.cluster_config_endpoint_type -} - -# deploying istio cni - -module "istio_cni" { - depends_on = [module.service_mesh_operator] - source = "../../modules/sm-istio-cni" - namespace = "istio-system-cni" - create_namespace = true -} - -# deploying istio controlplane by setting customised configuration - -module "istio" { - depends_on = [module.service_mesh_operator] - source = "../../modules/sm-istio" - name = "default" - namespace = "istio-system" - force_controlplane_update = false - mesh_config_enable_mtls = true - istio_discovery_custom_configuration = var.istio_discovery_configuration - istio_namespace_discovery_custom_labels = var.istio_namespace_discovery_labels - pilot_autoscaling_enabled = var.pilot_autoscaling_enabled - pilot_autoscaling_max_pods = var.pilot_autoscaling_max_pods - pilot_autoscaling_min_pods = var.pilot_autoscaling_min_pods - pilot_replicas = var.pilot_replicas - istio_enable_default_pod_disruption_budget = var.istio_enable_default_pod_disruption_budget - pilot_autoscaling_target_memory = var.pilot_autoscaling_target_memory - pilot_autoscaling_target_cpu = var.pilot_autoscaling_target_cpu - pilot_node_selector = var.pilot_node_selector - pilot_tolerations = var.pilot_tolerations - pilot_resources = var.pilot_resources - pilot_affinity = var.pilot_affinity - mesh_config_tcp_keep_alive = var.mesh_config_tcp_keep_alive - cluster_config_file_path = data.ibm_container_cluster_config.cluster_config.config_file_path -} - -resource "time_sleep" "wait_istio" { - depends_on = [module.istio_cni, module.istio] - - create_duration = "300s" - destroy_duration = "60s" -} diff --git a/examples/securetopology/outputs.tf b/examples/securetopology/outputs.tf deleted file mode 100644 index a38cc7c..0000000 --- a/examples/securetopology/outputs.tf +++ /dev/null @@ -1,19 +0,0 @@ -output "cluster_id" { - description = "ID of the deployed cluster" - value = module.ocp_base.cluster_id -} - -output "vpc_id" { - description = "ID of the deployed VPC" - value = module.ocp_base.vpc_id -} - -output "subnets" { - description = "Details of the subnets deployed in the VPC and attached to the cluster" - value = module.vpc.subnets -} - -output "ingress_alb_subnets" { - description = "Details of the subnets deployed in the VPC and attached to the cluster to be attached to the ALB loadbalancer" - value = [for subnet in module.vpc.subnets["edge"] : subnet["id"]] -} diff --git a/examples/securetopology/provider.tf b/examples/securetopology/provider.tf deleted file mode 100644 index 2d42889..0000000 --- a/examples/securetopology/provider.tf +++ /dev/null @@ -1,23 +0,0 @@ -############################################################################## -# Config providers -############################################################################## - -provider "ibm" { - ibmcloud_api_key = var.ibmcloud_api_key - region = var.region -} - -provider "helm" { - kubernetes = { - host = data.ibm_container_cluster_config.cluster_config.host - token = data.ibm_container_cluster_config.cluster_config.token - } - # experiments = { - # manifest = true - # } -} - -provider "kubernetes" { - host = data.ibm_container_cluster_config.cluster_config.host - token = data.ibm_container_cluster_config.cluster_config.token -} diff --git a/examples/securetopology/variables.tf b/examples/securetopology/variables.tf deleted file mode 100644 index d14423c..0000000 --- a/examples/securetopology/variables.tf +++ /dev/null @@ -1,196 +0,0 @@ -variable "ibmcloud_api_key" { - type = string - description = "IBM Cloud API Key for a user / serviceId with write access to the corresponding namespace in the OCP cluster" - sensitive = true -} - -variable "prefix" { - type = string - description = "Prefix for name of all resource created by this example" - default = "ocp-smv3" -} - -variable "region" { - type = string - description = "Region where resources are created" -} - -variable "resource_group" { - type = string - description = "Optionally pass an existing resource group name to be used. If not passed a new one will be created" - default = null -} - -variable "resource_tags" { - type = list(string) - description = "Optional list of tags to be added to created resources" - default = [] -} - -variable "deploy_operator" { - type = bool - description = "Enable installing RedHat Service Mesh Operator" - default = true -} - -variable "develop_mode" { - type = bool - description = "If true, output more logs, and reduce some wait periods" - default = false -} - -variable "cluster_config_endpoint_type" { - description = "Specify which type of endpoint to use for for cluster config access: 'default', 'private', 'vpe', 'link'. 'default' value will use the default endpoint of the cluster." - type = string - default = "default" - nullable = false - validation { - error_message = "Invalid Endpoint Type! Valid values are 'default', 'private', 'vpe', or 'link'" - condition = contains(["default", "private", "vpe", "link"], var.cluster_config_endpoint_type) - } -} - -variable "istio_discovery_configuration" { - type = object({ - matchLabels : optional(map(string), null), - matchExpressions : optional(list(object({ - key : string - operator : string - values : list(string) - })), []) - }) - default = { - matchLabels : { - "istio-discovery" : "enabled" - } - } - description = "Istio controlplane discovery label." -} - -variable "istio_namespace_discovery_labels" { - type = map(string) - default = { - "istio-discovery" = "enabled" - } - description = "Istio controlplane discovery label to apply to controlplane namespace." -} - -variable "pilot_autoscaling_enabled" { - default = true - type = bool - description = "Enable Istio pilot autoscaling through HorizontalPodAutoscaler." -} - -variable "pilot_autoscaling_max_pods" { - type = number - description = "If var.pilot_autoscaling_enabled is enabled this sets the maximum amount of pods for Istio pilot HorizontalPodAutoscaler." - default = 6 -} - -variable "pilot_autoscaling_min_pods" { - type = number - default = 2 - description = "If var.pilot_autoscaling_enabled is enabled this sets the minimum amount of pods for Istio pilot HorizontalPodAutoscaler." -} - -variable "pilot_replicas" { - description = "Sets the number of replicas to deploy the Istio Pilot." - default = 3 - type = number -} - -variable "istio_enable_default_pod_disruption_budget" { - type = bool - default = true - description = "Controls whether a PodDisruptionBudget with a default minAvailable value of 1 is created for each deployment." -} - -variable "pilot_autoscaling_target_memory" { - type = number - description = "If var.pilot_autoscaling_enabled is enabled this sets the target memory average load." - default = 85 -} - -variable "pilot_autoscaling_target_cpu" { - type = number - description = "If var.pilot_autoscaling_enabled is enabled this sets the target CPU average load" - default = 75 -} - -variable "pilot_node_selector" { - default = { "ibm-cloud.kubernetes.io/worker-pool-name" : "default" } - description = "Node selector configuration for Istio pilot pods" - type = map(string) -} - -variable "pilot_resources" { - type = object({ - limits : optional(map(string), null), - requests : optional(map(string), null) - }) - description = "Istio pilot pods resources requests and limits for memory and CPU." - default = { - "requests" : { - "cpu" : "200m" - "memory" : "128Mi" - } - "limits" : { - "cpu" : "500m" - "memory" : "256Mi" - } - } -} - -variable "mesh_config_tcp_keep_alive" { - type = object({ - probes : optional(number, 9), - time : optional(string, "7200s") - interval : optional(string, "75s") - }) - description = "Istio configuration for TCP keepalive" - default = { - probes = 10 - } -} - -variable "pilot_affinity" { - type = object({ - podAntiAffinity : optional(any, null), - podAffinity : optional(any, null), - nodeAffinity : optional(any, null) - }) - default = { - podAntiAffinity : { - preferredDuringSchedulingIgnoredDuringExecution : [ - { - weight : 100, - podAffinityTerm : { - labelSelector : { - matchExpressions : [ - { - key : "istio", - operator : "In", - values : ["istiod"] - } - ] - } - topologyKey : "topology.kubernetes.io/zone" - } - } - ] - } - } - description = "Istio pilot pods affinity configuration." -} - -variable "pilot_tolerations" { - type = list(any) - default = [ - { - key : "dedicated" - value : "transit" - effect : "NoExecute" - } - ] - description = "Istio pilot pods tolerations configuration." -} diff --git a/examples/securetopology/version.tf b/examples/securetopology/version.tf deleted file mode 100644 index d64aeab..0000000 --- a/examples/securetopology/version.tf +++ /dev/null @@ -1,22 +0,0 @@ -terraform { - required_version = ">= 1.9.0" - required_providers { - # Pin to the lowest provider version of the range defined in the main module to ensure lowest version still works - ibm = { - source = "IBM-Cloud/ibm" - version = ">= 1.59.0" - } - helm = { - source = "hashicorp/helm" - version = ">= 3.0.0" - } - time = { - source = "hashicorp/time" - version = ">= 0.9.1, < 1.0.0" - } - kubernetes = { - source = "hashicorp/kubernetes" - version = ">= 2.16.1, < 3.0.0" - } - } -} diff --git a/main.tf b/main.tf index f289f20..7d54e7f 100644 --- a/main.tf +++ b/main.tf @@ -29,7 +29,7 @@ locals { ############################################################################## # Retrieve information about all the Cluster configuration files and -# certificates to access the cluster through the kubernets provider +# certificates to access the cluster through the kubernetes provider ############################################################################## data "ibm_container_cluster_config" "cluster_config" { @@ -45,7 +45,6 @@ data "ibm_container_cluster_config" "cluster_config" { # installing helm chart to enable subscriptions for openshift servicemesh v3 operator resource "helm_release" "service_mesh_operator" { depends_on = [data.ibm_container_cluster_config.cluster_config, null_resource.undeploy_servicemesh] - count = var.deploy_operator == true ? 1 : 0 name = local.sm_operator_release_name chart = "${path.module}/chart/${local.sm_operator_chart_path}" @@ -104,8 +103,7 @@ resource "null_resource" "undeploy_servicemesh" { # On delete: give time for the crd sm instance to be removed (which depends on running finalizer) # Cheap for now - replace with polling of specific resources resource "time_sleep" "wait_operators" { - depends_on = [helm_release.service_mesh_operator[0]] - count = var.deploy_operator == true ? 1 : 0 + depends_on = [helm_release.service_mesh_operator] create_duration = "${local.sleep_create}s" destroy_duration = "${local.sleep_destroy}s" diff --git a/modules/sm-istio-egress/README.md b/modules/sm-istio-egress/README.md new file mode 100644 index 0000000..93219f8 --- /dev/null +++ b/modules/sm-istio-egress/README.md @@ -0,0 +1,233 @@ +# Service Mesh Istio module for egress gateway + +## Overview +This module deploys the Istio egress gateway resources configured to leverage on Istio gateway injection, to set up the gateway's namespace labels according to user input for the expected controlplane's discovery selectors and to setup the expected istio selectors for the traffic routing configuration. + +### Discovery selectors and sidecar injection configuration + +This module allows to setup the gateway's namespace discovery selectors according to the desired controlplane attributes following the Red Hat Service Mesh [requirements](https://docs.redhat.com/en/documentation/red_hat_openshift_service_mesh/3.0/html/installing/ossm-installing-service-mesh#ossm-scoping-service-mesh-with-discoveryselectors_ossm-installing-openshift-service-mesh) + +- if the input parameter `var.egress_discovery_custom_configuration` is left to its default value `null` the gateway's namespace discovery selector is configured with the following logic: + - if the Istio mesh name to enroll the gateway's namespace to (`var.istio_mesh_enrollment`) is left to its default value `default` the discovery selector and sidecar injection labels are configured with + ``` + { + "istio-discovery" : "enabled", + "istio-injection" : "enabled", + } + ``` + - if the Istio mesh name to enroll the gateway's namespace to (`var.istio_mesh_enrollment`) is not left to its default value `default` the discovery selector and sidecar injection labels are configured with + ``` + { + "istio-discovery" : var.istio_mesh_enrollment, + "istio.io/rev" : var.istio_mesh_enrollment, + } + ``` +- if the input parameter `var.egress_discovery_custom_configuration` is customised its values are configured into the gateways' labels + +### Example: basic egress gateway configuration + +This configuration deploys a Istio egress gateway with the default configurations for controlplane named `default` and discovery selectors +``` + { + "istio-discovery" : "enabled", + "istio-injection" : "enabled", + } +``` +The gateway is created in the `basic-egress` namespace which is created at gateway deployment time, and opens the ports TCP/80 (mapped internally to port 8000) and TCP/443 (mapped internally to port 443). Traffic routing selector is `"istio" : "egress-gateway"` + +``` + +module "default_workload_egress" { + source = "terraform-ibm-modules/ocp-service-mesh/ibm//modules/sm-istio-egress" + version = "X.Y.Z" + name = "basic-egress" + namespace = "basic-egress" + create_namespace = true + istio_mesh_enrollment = "default" + egress_selectors = { + "istio" : "egress-gateway", + } + egress_ports = [ + { + "name" : "http2" + "port" : "80" + "targetPort" : "8000" + "proto" : "TCP" + }, + { + "name" : "https" + "port" : "443" + "targetPort" : "443" + "proto" : "TCP" + } + ] + cluster_config_file_path = data.ibm_container_cluster_config.cluster_config.config_file_path +} +``` + +### Example: Advanced egress gateway configuration + +This configuration deploys a Istio egress gateway to enroll in the controlplane named `mesh-1` with namespace labels set to + +``` + { + "istio-discovery" : "mesh-1", + "istio.io/rev" : "mesh-1", + } +``` + +The gateway is created in the `default-workload` namespace which is NOT created at gateway deployment time, which is recreated if an update is identified by terraform, traffic routing selector is `"istio" : "default-workload-egress"`, opens the ports TCP/80 (mapped internally to port 8000), TCP/443 (mapped internally to port 443), TCP/5432 (mapped internally to port 5432), disable egress deployment autoscaling, sets the replicas to 3 pods, sets the resources configuration requests and limits for CPU and memory, the egress pods termination grace period, and the pods affinity and toleration to make the pods to run on the worker nodes with label `dedicated=edge` and the pods antiaffinity rule to have the 3 replicas to run on different nodes according to the pods label `"istio.io/gateway" = "def-workload-egress.default-workload"` (set by the module at deployment definition time with the format `namespace.gatewayname`) + +``` + +locals { + affinity = { + nodeAffinity : { + requiredDuringSchedulingIgnoredDuringExecution : { + nodeSelectorTerms : [ + { + matchExpressions : [ + { + key : "ibm-cloud.kubernetes.io/worker-pool-name", + operator : "In", + values : ["edge"] + } + ] + } + ] + } + }, + podAntiAffinity : { + preferredDuringSchedulingIgnoredDuringExecution : [ + { + podAffinityTerm : { + labelSelector : { + matchExpressions : [ + { + key : "istio.io/gateway", + operator : "In", + values : ["def-workload-egress.default-workload"] + } + ] + } + topologyKey : "topology.kubernetes.io/zone" + } + weight : 100 + } + ] + } + } +} + +module "default_workload_egress" { + source = "terraform-ibm-modules/ocp-service-mesh/ibm//modules/sm-istio-egress" + version = "X.Y.Z" + name = "def-workload-egress" + namespace = "default-workload" + create_namespace = false + force_dataplane_update = true + istio_mesh_enrollment = "mesh-1" + egress_selectors = { + "istio" : "default-workload-egress", + } + + egress_ports = [ + { + "name" : "http2" + "port" : "80" + "targetPort" : "8000" + "proto" : "TCP" + }, + { + "name" : "https" + "port" : "443" + "targetPort" : "443" + "proto" : "TCP" + }, + { + "name" : "tcp" + "port" : "5432" + "targetPort" : "5432" + "proto" : "TCP" + } + ] + egress_autoscale_configuration = { + "enabled" : false + } + egress_replicas = 3 + egress_resources_configuration = { + "limits" : { + "cpu" : "200m" + "memory" : "1024Mi" + }, + "requests" : { + "cpu" : "100m" + "memory" : "128Mi" + } + } + egress_termination_grace_period = 30 + egress_affinity = local.affinity + egress_tolerations = [ + { + key : "dedicated" + value : "edge" + effect : "NoExecute" + } + ] + cluster_config_file_path = data.ibm_container_cluster_config.cluster_config.config_file_path +} +``` + +For all the configuration parameters details refer to the section below + + +### Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.9.0 | +| [helm](#requirement\_helm) | >= 3.0.0 | +| [null](#requirement\_null) | >= 3.2.1, < 4.0.0 | + +### Modules + +| Name | Source | Version | +|------|--------|---------| +| [egress\_namespace](#module\_egress\_namespace) | terraform-ibm-modules/namespace/ibm | v1.0.3 | + +### Resources + +| Name | Type | +|------|------| +| [helm_release.istio_egress](https://registry.terraform.io/providers/hashicorp/helm/latest/docs/resources/release) | resource | +| [null_resource.confirm_ingress_operational](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource | + +### Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [cluster\_config\_file\_path](#input\_cluster\_config\_file\_path) | Cluster config file path to use with kubernetes provider to run checks on the resources deployment. Set to null to skip this check. | `string` | n/a | yes | +| [create\_namespace](#input\_create\_namespace) | Flag to create the namespace where to install istio egress dataplane. Default to true | `bool` | `true` | no | +| [egress\_affinity](#input\_egress\_affinity) | Istio egress affinity configuration. For more details https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#affinity-v1-core. Egress pods are provided of a label with key "istio.io/gateway" and value "[DEPLOYMENT NAME].[DEPLOYMENT NAMESPACE]" in order to allow to set them as antiAffinity labels. |
object({
podAntiAffinity : optional(any, null),
podAffinity : optional(any, null),
nodeAffinity : optional(any, null)
})
|
{
"nodeAffinity": {
"requiredDuringSchedulingIgnoredDuringExecution": {
"nodeSelectorTerms": [
{
"matchExpressions": [
{
"key": "ibm-cloud.kubernetes.io/worker-pool-name",
"operator": "In",
"values": [
"edge"
]
}
]
}
]
}
}
}
| no | +| [egress\_autoscale\_configuration](#input\_egress\_autoscale\_configuration) | egress autoscale configuration defined through HPA. If enabled is set to true the HPA definition is deployed. Otherwise if false the HPA configuration is not deployed. Default to enabled=false. |
object({
enabled : optional(bool, false),
autoscaleMin : optional(number, 1),
autoscaleMax : optional(number, 5),
cpu : optional(object(
{
targetavgutil : optional(number, 80)
}
))
memory : optional(object(
{
targetavgutil : optional(number, 80)
}
))
})
|
{
"enabled": false
}
| no | +| [egress\_discovery\_custom\_configuration](#input\_egress\_discovery\_custom\_configuration) | Map of key-value entries to set custom istio discovery labels. Default to null to autogenerate the labels according to var.istio\_mesh\_enrollment value. For more details about istio discovery configuration refer to https://docs.redhat.com/en/documentation/red_hat_openshift_service_mesh/3.0/html/installing/ossm-sidecar-injection#ossm-about-sidecar-injection_ossm-sidecar-injection and https://docs.redhat.com/en/documentation/red_hat_openshift_service_mesh/3.0/html/installing/ossm-deploying-multiple-service-meshes-on-single-cluster. | `map(string)` | `null` | no | +| [egress\_internal\_traffic\_policy](#input\_egress\_internal\_traffic\_policy) | Internal traffic policy configuration for the egress. Allowed values are Cluster and Local. Default to Cluster. For more details refer to https://istio.io/latest/docs/tasks/security/authorization/authz-egress/. | `string` | `"Cluster"` | no | +| [egress\_pdb\_configuration](#input\_egress\_pdb\_configuration) | Configuration of the PodDisruptionBudget for the istio egress definition. Default to null to leverage on Istio default configuration. |
object({
minAvailable = optional(string, null)
maxUnavailable = optional(string, null)
})
| `null` | no | +| [egress\_ports](#input\_egress\_ports) | List of ports to configured on egress for outbound traffic. Default to port 443:443 on TCP. |
list(object(
{
port : number,
name : string
proto : string,
targetPort : number
}
))
|
[
{
"name": "https",
"port": 443,
"proto": "TCP",
"targetPort": 443
}
]
| no | +| [egress\_replicas](#input\_egress\_replicas) | Istio egress deployment replicaset configuration. If the var.egress\_autoscale\_configuration.enabled is true this value is ignored. Default to 3. | `number` | `3` | no | +| [egress\_resources\_configuration](#input\_egress\_resources\_configuration) | Istio egress resources deployment configuration. Default configuration is null and leverages on Istio default setting. |
object(
{
limits : optional(object(
{
cpu : optional(string, null),
memory : optional(string, null)
}), null),
requests : optional(object(
{
cpu : optional(string, null)
memory : optional(string, null)
}), null)
}
)
| `null` | no | +| [egress\_selectors](#input\_egress\_selectors) | Istio egress selectors to route outbound egress traffic to the expected istio gateway and to the expected workload. Default to "app": "istio-egress" "istio": "istio-egress" "gateway-instance": "istio-egressgateway". Null not allowed | `map(string)` |
{
"app": "istio-egress",
"istio": "istio-egress"
}
| no | +| [egress\_termination\_grace\_period](#input\_egress\_termination\_grace\_period) | Number of seconds for the Istio egress deployment for the grace period before terminating the pods and dropping the connections. Default to null to leverage on Istio default. | `number` | `null` | no | +| [egress\_tolerations](#input\_egress\_tolerations) | Istio egress tolerations configuration. Default to tolerate 'dedicated: edge' taint. For more details # https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#toleration-v1-core | `list(any)` | `[]` | no | +| [force\_dataplane\_update](#input\_force\_dataplane\_update) | Force dataplane to be updated | `bool` | `false` | no | +| [istio\_egress\_deployment\_timeout](#input\_istio\_egress\_deployment\_timeout) | Timeout for the helm release deployment for the egress gateway | `string` | `null` | no | +| [istio\_mesh\_enrollment](#input\_istio\_mesh\_enrollment) | Name of the Istio mesh controlplane to enroll this dataplane with. Default value to default. This value is used to generate discovery selectors, to override the computed values customise var.egress\_discovery\_custom\_configuration. | `string` | `"default"` | no | +| [name](#input\_name) | Name of the Istio egress deployment | `string` | n/a | yes | +| [namespace](#input\_namespace) | Namespace where to install istio egress dataplane. | `string` | n/a | yes | + +### Outputs + +| Name | Description | +|------|-------------| +| [istio\_egress\_metadata](#output\_istio\_egress\_metadata) | istio\_egress definition metadata | + diff --git a/modules/sm-istio-egress/main.tf b/modules/sm-istio-egress/main.tf new file mode 100644 index 0000000..309c0ff --- /dev/null +++ b/modules/sm-istio-egress/main.tf @@ -0,0 +1,139 @@ +locals { + istio_egress_release_name = "${var.namespace}-${var.name}" + istio_egress_chart_path = "istio-egress" + + egress_discovery_configuration = var.egress_discovery_custom_configuration != null ? var.egress_discovery_custom_configuration : ( + var.istio_mesh_enrollment == "default" ? { + "istio-discovery" : "enabled", + "istio-injection" : "enabled", + } : { + "istio-discovery" : var.istio_mesh_enrollment, + "istio.io/rev" : var.istio_mesh_enrollment, + } + ) + + egress_selectors = { + "egress" : { + "istioselectors" : var.egress_selectors + } + } + + egress_ports = { + "egress" : { + "ports" : var.egress_ports + } + } + + egress_autoscale_configuration = { + "egress" : { + "autoscale" : var.egress_autoscale_configuration + } + } + + egress_pdb_configuration = var.egress_pdb_configuration == null ? {} : { + "egress" : { + "pdb" : var.egress_pdb_configuration + } + } + + egress_resources_configuration = var.egress_resources_configuration == null ? {} : { + "egress" : { + "resources" : var.egress_resources_configuration + } + } + + egress_affinity = var.egress_affinity == null ? {} : { + "egress" : { + "affinity" : var.egress_affinity + } + } + + egress_tolerations = var.egress_tolerations == null ? {} : { + "egress" : { + "tolerations" : var.egress_tolerations + } + } + +} + +module "egress_namespace" { + count = var.create_namespace ? 1 : 0 + source = "terraform-ibm-modules/namespace/ibm" + version = "v1.0.3" + namespaces = [ + { + name = var.namespace + metadata = { + labels = local.egress_discovery_configuration + annotations = local.egress_discovery_configuration + } + } + ] +} + +# installing helm chart for istio deployment +resource "helm_release" "istio_egress" { + depends_on = [module.egress_namespace[0]] + name = local.istio_egress_release_name + chart = "${path.module}/../../chart/${local.istio_egress_chart_path}" + namespace = var.namespace + create_namespace = false + timeout = var.istio_egress_deployment_timeout + dependency_update = true + force_update = var.force_dataplane_update + cleanup_on_fail = false + wait = true + + disable_openapi_validation = false + + set = [ + { + name = "egress.name" + type = "string" + value = var.name + }, + { + name = "egress.namespace" + type = "string" + value = var.namespace + }, + { + name = "egress.internalTrafficPolicy" + type = "string" + value = var.egress_internal_traffic_policy + }, + { + name = "egress.replicacount" + type = "string" + value = var.egress_replicas + }, + { + name = "egress.terminationGracePeriodSeconds" + type = "string" + value = var.egress_termination_grace_period + } + ] + + values = [ + yamlencode(local.egress_selectors), + yamlencode(local.egress_ports), + yamlencode(local.egress_autoscale_configuration), + yamlencode(local.egress_pdb_configuration), + yamlencode(local.egress_resources_configuration), + yamlencode(local.egress_affinity), + yamlencode(local.egress_tolerations), + ] + +} + +resource "null_resource" "confirm_ingress_operational" { + depends_on = [helm_release.istio_egress] + count = var.cluster_config_file_path != null ? 1 : 0 + provisioner "local-exec" { + command = "${path.module}/scripts/confirm-egress-operational.sh \"${var.namespace}\" \"egress-${var.name}\"" + interpreter = ["/bin/bash", "-c"] + environment = { + KUBECONFIG = var.cluster_config_file_path + } + } +} diff --git a/modules/sm-istio-egress/outputs.tf b/modules/sm-istio-egress/outputs.tf new file mode 100644 index 0000000..b5af08f --- /dev/null +++ b/modules/sm-istio-egress/outputs.tf @@ -0,0 +1,4 @@ +output "istio_egress_metadata" { + description = "istio_egress definition metadata" + value = helm_release.istio_egress.metadata +} diff --git a/modules/sm-istio-egress/scripts/confirm-egress-operational.sh b/modules/sm-istio-egress/scripts/confirm-egress-operational.sh new file mode 100755 index 0000000..a49946b --- /dev/null +++ b/modules/sm-istio-egress/scripts/confirm-egress-operational.sh @@ -0,0 +1,64 @@ +#!/usr/bin/env bash +# This script is designed to verify that all key components of Istio dataplane for egress gateway are up and running. +# This is needed before workload is deployed and injected + +set -e + +namespace="${1}" +service="${2}" +fail=false +initialsleep=15 + +echo "Checking egress gateway ${service} successful deployment in namespace ${namespace}" + +echo "Initial sleep of ${initialsleep} seconds before starting to check" +sleep "${initialsleep}" + +echo "Checking deployment ${service} in namespace ${namespace}" +# Get list of deployments in control plane namespace +DEPLOYMENTS=() +while IFS='' read -r line; do DEPLOYMENTS+=("$line"); done < <(kubectl get deployment "${service}" -n "${namespace}" --no-headers | cut -f1 -d ' ') + +# Wait for all deployments to come up - timeout after 5 mins +# shellcheck disable=SC2068 +for dep in ${DEPLOYMENTS[@]}; do + echo "Checking deployment ${dep} status in namespace ${namespace}" + if ! kubectl rollout status deployment "$dep" -n "${namespace}" --timeout 5m; then + echo "Deployment ${dep} in namespace ${namespace} seems to fail to deploy" + fail=true + fi +done + +sleeptime=30 +echo "Checking for svc ${service} in namespace ${namespace} correctly deployed" +while [ -z "${clusterIP}" ]; do + # Get the clusterIP from the kube service (retry needed on service lookup, as sometimes service may not exist yet) + attempts=10 # 10 x 30 = 300 secs = 5 mins + n=1 + until [ "$n" -gt $attempts ]; do + echo "Attempt # ${n}" + clusterIP=$(kubectl get svc "${service}" -n "${namespace}" --template="{{.spec.clusterIP}}") && echo "${service} svc in namespace ${namespace} correctly deployed" && break + n=$((n+1)) + if [ "$n" -gt $attempts ]; then + echo "Maximum attempts ${attempts} reached for gateway ${service} in namespace ${namespace}. Giving up!" + fail=true + exit 1 + else + echo "Retrying in ${sleeptime} secs .." + sleep "${sleeptime}" + fi + done + +done + +# Fail with some debug prints if issues detected +if [ ${fail} == true ]; then + echo "Problem detected with deployments of gateway ${service} in namespace ${namespace}. Printing some debug info.." + set +e + kubectl get svc -n "${namespace}" -o wide + kubectl get deployment "${service}" -n "${namespace}" -o wide + kubectl get pods -n "${namespace}" -o wide + kubectl describe svc "${service}" -n "${namespace}" + kubectl describe deployments -n "${service}" "${namespace}" + # exit 1 +fi diff --git a/modules/sm-istio-egress/variables.tf b/modules/sm-istio-egress/variables.tf new file mode 100644 index 0000000..b05a54f --- /dev/null +++ b/modules/sm-istio-egress/variables.tf @@ -0,0 +1,215 @@ +variable "name" { + type = string + description = "Name of the Istio egress deployment" +} + +variable "create_namespace" { + type = bool + description = "Flag to create the namespace where to install istio egress dataplane. Default to true" + default = true +} + +variable "namespace" { + type = string + description = "Namespace where to install istio egress dataplane." +} + +variable "force_dataplane_update" { + description = "Force dataplane to be updated" + default = false + type = bool + nullable = false +} + +variable "istio_mesh_enrollment" { + type = string + default = "default" + description = "Name of the Istio mesh controlplane to enroll this dataplane with. Default value to default. This value is used to generate discovery selectors, to override the computed values customise var.egress_discovery_custom_configuration." +} + +variable "istio_egress_deployment_timeout" { + type = string + default = null + description = "Timeout for the helm release deployment for the egress gateway" +} + +variable "egress_discovery_custom_configuration" { + type = map(string) + default = null + description = "Map of key-value entries to set custom istio discovery labels. Default to null to autogenerate the labels according to var.istio_mesh_enrollment value. For more details about istio discovery configuration refer to https://docs.redhat.com/en/documentation/red_hat_openshift_service_mesh/3.0/html/installing/ossm-sidecar-injection#ossm-about-sidecar-injection_ossm-sidecar-injection and https://docs.redhat.com/en/documentation/red_hat_openshift_service_mesh/3.0/html/installing/ossm-deploying-multiple-service-meshes-on-single-cluster." +} + +variable "egress_selectors" { + type = map(string) + default = { + "app" : "istio-egress", + "istio" : "istio-egress", + } + nullable = false + description = "Istio egress selectors to route outbound egress traffic to the expected istio gateway and to the expected workload. Default to \"app\": \"istio-egress\" \"istio\": \"istio-egress\" \"gateway-instance\": \"istio-egressgateway\". Null not allowed" +} + +variable "egress_ports" { + type = list(object( + { + port : number, + name : string + proto : string, + targetPort : number + } + )) + default = [{ + port : 443, + name : "https", + proto : "TCP", + targetPort : 443 + }] + description = "List of ports to configured on egress for outbound traffic. Default to port 443:443 on TCP." +} + +variable "egress_internal_traffic_policy" { + type = string + default = "Cluster" + description = "Internal traffic policy configuration for the egress. Allowed values are Cluster and Local. Default to Cluster. For more details refer to https://istio.io/latest/docs/tasks/security/authorization/authz-egress/." + nullable = false +} + +variable "egress_autoscale_configuration" { + type = object({ + enabled : optional(bool, false), + autoscaleMin : optional(number, 1), + autoscaleMax : optional(number, 5), + cpu : optional(object( + { + targetavgutil : optional(number, 80) + } + )) + memory : optional(object( + { + targetavgutil : optional(number, 80) + } + )) + }) + default = { + enabled : false + } + description = "egress autoscale configuration defined through HPA. If enabled is set to true the HPA definition is deployed. Otherwise if false the HPA configuration is not deployed. Default to enabled=false." +} + +variable "egress_pdb_configuration" { + description = "Configuration of the PodDisruptionBudget for the istio egress definition. Default to null to leverage on Istio default configuration." + default = null + type = object({ + minAvailable = optional(string, null) + maxUnavailable = optional(string, null) + }) + validation { + condition = var.egress_pdb_configuration == null ? true : (var.egress_pdb_configuration.minAvailable != null && var.egress_pdb_configuration.maxUnavailable != null ? false : true) + error_message = "only one of minAvailable and maxUnavailable for var.egress_pdb_configuration can be set to a value not null" + } + validation { + condition = var.egress_pdb_configuration == null ? true : (var.egress_pdb_configuration.minAvailable == null ? true : can(regex("^([0-9]{1,3}%?)$", var.egress_pdb_configuration.minAvailable))) + error_message = "minAvailable for var.egress_pdb_configuration value is not valid. It can be set only to a number or percentage (regex ^([0-9]{1,3}%?)$)" + } + validation { + condition = var.egress_pdb_configuration == null ? true : (var.egress_pdb_configuration.maxUnavailable == null ? true : can(regex("^([0-9]{1,3}%?)$", var.egress_pdb_configuration.maxUnavailable))) + error_message = "maxUnavailable for var.egress_pdb_configuration value is not valid. It can be set only to a number or percentage (regex ^([0-9]{1,3}%?)$)" + } + validation { + condition = var.egress_pdb_configuration == null ? true : (var.egress_pdb_configuration.minAvailable == null ? true : tostring(var.egress_pdb_configuration.minAvailable) != "0") + error_message = "minAvailable for var.egress_pdb_configuration must be set to a value greater than 0" + } + validation { + condition = var.egress_pdb_configuration == null ? true : (var.egress_pdb_configuration.minAvailable == null ? true : var.egress_pdb_configuration.minAvailable != "0%") + error_message = "minAvailable for var.egress_pdb_configuration must be set to a value greater than 0%" + } +} + +variable "egress_replicas" { + type = number + default = 3 + description = "Istio egress deployment replicaset configuration. If the var.egress_autoscale_configuration.enabled is true this value is ignored. Default to 3." + nullable = false +} + +variable "egress_resources_configuration" { + type = object( + { + limits : optional(object( + { + cpu : optional(string, null), + memory : optional(string, null) + }), null), + requests : optional(object( + { + cpu : optional(string, null) + memory : optional(string, null) + }), null) + } + ) + description = "Istio egress resources deployment configuration. Default configuration is null and leverages on Istio default setting." + default = null +} + +variable "egress_termination_grace_period" { + type = number + description = "Number of seconds for the Istio egress deployment for the grace period before terminating the pods and dropping the connections. Default to null to leverage on Istio default." + default = null +} + +variable "egress_affinity" { + type = object({ + podAntiAffinity : optional(any, null), + podAffinity : optional(any, null), + nodeAffinity : optional(any, null) + }) + default = { + nodeAffinity : { + requiredDuringSchedulingIgnoredDuringExecution : { + nodeSelectorTerms : [ + { + matchExpressions : [ + { + key : "ibm-cloud.kubernetes.io/worker-pool-name", + operator : "In", + values : ["edge"] + } + ] + } + ] + } + } + } + description = "Istio egress affinity configuration. For more details https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#affinity-v1-core. Egress pods are provided of a label with key \"istio.io/gateway\" and value \"[DEPLOYMENT NAME].[DEPLOYMENT NAMESPACE]\" in order to allow to set them as antiAffinity labels." + # podAntiAffinity example + # podAntiAffinity : { + # preferredDuringSchedulingIgnoredDuringExecution : [ + # { + # podAffinityTerm : { + # labelSelector : { + # matchExpressions : [ + # { + # key : "istio.io/gateway", + # operator : "In", + # values : ["def-workload-ingress.default-workload"] + # } + # ] + # } + # } + # topologyKey : "topology.kubernetes.io/zone" + # weight : 100 + # } + # ] + # } +} + +variable "egress_tolerations" { + type = list(any) + default = [] + description = "Istio egress tolerations configuration. Default to tolerate 'dedicated: edge' taint. For more details # https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#toleration-v1-core" +} + +variable "cluster_config_file_path" { + type = string + description = "Cluster config file path to use with kubernetes provider to run checks on the resources deployment. Set to null to skip this check." +} diff --git a/modules/sm-istio-egress/version.tf b/modules/sm-istio-egress/version.tf new file mode 100644 index 0000000..4970b25 --- /dev/null +++ b/modules/sm-istio-egress/version.tf @@ -0,0 +1,14 @@ +terraform { + required_version = ">= 1.9.0" + required_providers { + # Use "greater than or equal to" range in modules + helm = { + source = "hashicorp/helm" + version = ">= 3.0.0" + } + null = { + source = "hashicorp/null" + version = ">= 3.2.1, < 4.0.0" + } + } +} diff --git a/modules/sm-istio-ingress/README.md b/modules/sm-istio-ingress/README.md new file mode 100644 index 0000000..2c07145 --- /dev/null +++ b/modules/sm-istio-ingress/README.md @@ -0,0 +1,241 @@ +# Service Mesh Istio module for egress gateway + +## Overview +This module deploys the Istio ingress gateway resources configured to leverage on Istio gateway injection, to set up the gateway's namespace labels according to user input for the expected controlplane's discovery selectors and to setup the expected istio selectors for the traffic routing configuration. + +### Discovery selectors and sidecar injection configuration + +This module allows to setup the gateway's namespace discovery selectors according to the desired controlplane attributes following the Red Hat Service Mesh [requirements](https://docs.redhat.com/en/documentation/red_hat_openshift_service_mesh/3.0/html/installing/ossm-installing-service-mesh#ossm-scoping-service-mesh-with-discoveryselectors_ossm-installing-openshift-service-mesh) + +- if the input parameter `var.ingress_discovery_custom_configuration` is left to its default value `null` the gateway's namespace discovery selector is configured with the following logic: + - if the Istio mesh name to enroll the gateway's namespace to (`var.istio_mesh_enrollment`) is left to its default value `default` the discovery selector and sidecar injection labels are configured with + ``` + { + "istio-discovery" : "enabled", + "istio-injection" : "enabled", + } + ``` + - if the Istio mesh name to enroll the gateway's namespace to (`var.istio_mesh_enrollment`) is not left to its default value `default` the discovery selector and sidecar injection labels are configured with + ``` + { + "istio-discovery" : var.istio_mesh_enrollment, + "istio.io/rev" : var.istio_mesh_enrollment, + } + ``` +- if the input parameter `var.ingress_discovery_custom_configuration` is customised its values are configured into the gateways' labels + +### Example: basic ingress gateway configuration + +This configuration deploys a Istio ingress gateway with the default configurations for controlplane named `default` and discovery selectors +``` + { + "istio-discovery" : "enabled", + "istio-injection" : "enabled", + } +``` +The gateway is created in the `basic-ingress` namespace which is created at gateway deployment time, and opens the port TCP/80 (mapped internally to port 8000). Traffic routing selector is `"istio" : "ingress-gateway"`. Ingress Loadbalancer type is Application Load Balancer, its IP type is public. + +``` + +module "default_workload_ingress" { + source = "terraform-ibm-modules/ocp-service-mesh/ibm//modules/sm-istio-ingress" + name = "basic-ingress" + namespace = "basic-ingress" + create_namespace = true + force_dataplane_update = false + ingress_loadbalancer_type = "alb" + ingress_service_type = "LoadBalancer" + ingress_ip_type = "public" + istio_mesh_enrollment = "default" + ingress_selectors = { + "istio" : "ingress-gateway", + } + ingress_ports = [ + { + "name" : "http2" + "port" : "80" + "targetPort" : "8000" + "proto" : "TCP" + } + ] + cluster_config_file_path = data.ibm_container_cluster_config.cluster_config.config_file_path +} +``` + +### Example: Advanced egress gateway configuration + +This configuration deploys a Istio egress gateway to enroll in the controlplane named `mesh-1` with namespace labels set to + +``` + { + "istio-discovery" : "mesh-1", + "istio.io/rev" : "mesh-1", + } +``` + +The gateway is created in the `default-workload` namespace which is created at gateway deployment time, which is recreated if an update is identified by terraform, the ingress is attached to a LoadBalaner of type Application LoadBalancer, the traffic routing selector is `"istio" : "default-workload-ingress"`, opens the ports TCP/80 (mapped internally to port 8000), TCP/15021 (mapped internally to port 15021), disable egress deployment autoscaling, sets the replicas to 3 pods, sets the default pods disruption budget to minAvailable = 1, sets the resources configuration requests and limits for CPU and memory, the ingress pods termination grace period, enables the proxy protocol on ingress and LoadBalancer, enables the ingress and LoadBalancer to support requests with and without the proxy protocol, and the pods affinity and toleration to make the pods to run on the worker nodes with label `dedicated=edge` and the pods antiaffinity rule to have the 3 replicas to run on different nodes according to the pods label `"istio.io/gateway" = "def-workload-ingress.default-workload"` (set by the module at deployment definition time with the format `namespace.gatewayname`) + +``` + +locals { + affinity = { + nodeAffinity : { + requiredDuringSchedulingIgnoredDuringExecution : { + nodeSelectorTerms : [ + { + matchExpressions : [ + { + key : "ibm-cloud.kubernetes.io/worker-pool-name", + operator : "In", + values : ["edge"] + } + ] + } + ] + } + }, + podAntiAffinity : { + preferredDuringSchedulingIgnoredDuringExecution : [ + { + podAffinityTerm : { + labelSelector : { + matchExpressions : [ + { + key : "istio.io/gateway", + operator : "In", + values : ["def-workload-ingress.default-workload"] + } + ] + } + topologyKey : "topology.kubernetes.io/zone" + } + weight : 100 + } + ] + } + } +} + +module "default_workload_ingress" { + source = "terraform-ibm-modules/ocp-service-mesh/ibm//modules/sm-istio-ingress" + version = "X.Y.Z" + name = "def-workload-ingress" + namespace = "default-workload" + create_namespace = true + force_dataplane_update = true + ingress_loadbalancer_type = "alb" + ingress_service_type = "LoadBalancer" + ingress_ip_type = "public" + istio_mesh_enrollment = "default" + ingress_selectors = { + "istio" : "default-workload-ingress", + } + ingress_alb_subnets = [for subnet in module.vpc.subnets["edge"] : subnet["id"]] + ingress_ports = [ + { + "name" : "http2" + "port" : "80" + "targetPort" : "8000" + "proto" : "TCP" + }, + { + "name" : "istio-health" + "port" : "15021" + "targetPort" : "15021" + "proto" : "TCP" + } + ] + ingress_autoscale_configuration = { + "enabled" : false + } + ingress_pdb_configuration = { + "minAvailable" = "1" + } + ingress_replicas = 3 + ingress_resources_configuration = { + "limits" : { + "cpu" : "200m" + "memory" : "1024Mi" + }, + "requests" : { + "cpu" : "100m" + "memory" : "128Mi" + } + } + ingress_termination_grace_period = 30 + ingress_affinity = local.affinity + ingress_tolerations = [ + { + key : "dedicated" + value : "edge" + effect : "NoExecute" + } + ] + ingress_enable_proxy_protocol = true + ingress_proxy_protocol_allow_without = true + cluster_config_file_path = data.ibm_container_cluster_config.cluster_config.config_file_path +} +``` + +For all the configuration parameters details refer to the section below + + +### Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.9.0 | +| [helm](#requirement\_helm) | >= 3.0.0 | +| [null](#requirement\_null) | >= 3.2.1, < 4.0.0 | + +### Modules + +| Name | Source | Version | +|------|--------|---------| +| [ingress\_namespace](#module\_ingress\_namespace) | terraform-ibm-modules/namespace/ibm | v1.0.3 | + +### Resources + +| Name | Type | +|------|------| +| [helm_release.istio_ingress](https://registry.terraform.io/providers/hashicorp/helm/latest/docs/resources/release) | resource | +| [null_resource.confirm_ingress_operational_alb](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource | +| [null_resource.confirm_ingress_operational_nlb](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource | + +### Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [cluster\_config\_file\_path](#input\_cluster\_config\_file\_path) | Cluster config file path to use with kubernetes provider to run checks on the resources deployment. Set to null to skip this check. | `string` | n/a | yes | +| [create\_namespace](#input\_create\_namespace) | Flag to create the namespace where to install istio ingress dataplane. Default to true | `bool` | `true` | no | +| [force\_dataplane\_update](#input\_force\_dataplane\_update) | Force dataplane to be updated | `bool` | `false` | no | +| [ingress\_affinity](#input\_ingress\_affinity) | Istio ingress affinity configuration. For more details https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#affinity-v1-core. Ingress pods are provided of a label with key "istio.io/gateway" and value "[DEPLOYMENT NAME].[DEPLOYMENT NAMESPACE]" in order to allow to set them as antiAffinity labels. |
object({
podAntiAffinity : optional(any, null),
podAffinity : optional(any, null),
nodeAffinity : optional(any, null)
})
|
{
"nodeAffinity": {
"requiredDuringSchedulingIgnoredDuringExecution": {
"nodeSelectorTerms": [
{
"matchExpressions": [
{
"key": "ibm-cloud.kubernetes.io/worker-pool-name",
"operator": "In",
"values": [
"edge"
]
}
]
}
]
}
}
}
| no | +| [ingress\_alb\_subnets](#input\_ingress\_alb\_subnets) | List of VPC subnets to attach to the IBM Cloud Application LoadBalancer bound to the cluster. Null value is not allowed. Default to empty list. | `list(string)` | `[]` | no | +| [ingress\_autoscale\_configuration](#input\_ingress\_autoscale\_configuration) | Ingress autoscale configuration defined through HPA. If enabled is set to true the HPA definition is deployed. Otherwise if false the HPA configuration is not deployed. Default to enabled=false. |
object({
enabled : optional(bool, false),
autoscaleMin : optional(number, 1),
autoscaleMax : optional(number, 5),
cpu : optional(object(
{
targetavgutil : optional(number, 80)
}
))
memory : optional(object(
{
targetavgutil : optional(number, 80)
}
))
})
|
{
"enabled": false
}
| no | +| [ingress\_discovery\_custom\_configuration](#input\_ingress\_discovery\_custom\_configuration) | Map of key-value entries to set custom istio discovery labels. Default to null to autogenerate the labels according to var.istio\_mesh\_enrollment value. For more details about istio discovery configuration refer to https://docs.redhat.com/en/documentation/red_hat_openshift_service_mesh/3.0/html/installing/ossm-sidecar-injection#ossm-about-sidecar-injection_ossm-sidecar-injection and https://docs.redhat.com/en/documentation/red_hat_openshift_service_mesh/3.0/html/installing/ossm-deploying-multiple-service-meshes-on-single-cluster. | `map(string)` | `null` | no | +| [ingress\_enable\_proxy\_protocol](#input\_ingress\_enable\_proxy\_protocol) | Flag to enable Proxy Protocol on ingress LoadBalancer (only ALB type) and to enable the EnvoyFilter to implement Proxy Protocol on ingress gateway | `bool` | `false` | no | +| [ingress\_external\_traffic\_policy](#input\_ingress\_external\_traffic\_policy) | External traffic policy configuration for the ingress. Allowed values are Cluster and Local. Default to Cluster. For more details refer to https://istio.io/latest/docs/tasks/security/authorization/authz-ingress/. | `string` | `"Cluster"` | no | +| [ingress\_internal\_traffic\_policy](#input\_ingress\_internal\_traffic\_policy) | Internal traffic policy configuration for the ingress. Allowed values are Cluster and Local. Default to Local. For more details refer to https://istio.io/latest/docs/tasks/security/authorization/authz-ingress/. | `string` | `"Local"` | no | +| [ingress\_ip\_type](#input\_ingress\_ip\_type) | IBM Cloud LoadBalancer IP type: valid values are public and private. Default to public. If var.ingress\_service\_type == "ClusterIP" this value hasn't effect. | `string` | `"public"` | no | +| [ingress\_loadbalancer\_type](#input\_ingress\_loadbalancer\_type) | IBM Cloud LoadBalancer type bound to the ingress: valid values are "alb" for Application Load Balancer and "nlb" for Network Load Balancer. If var.ingress\_service\_type == "ClusterIP" this value hasn't effect. For more details refer to https://cloud.ibm.com/docs/vpc?topic=vpc-nlb-vs-elb. Default to alb. | `string` | `"alb"` | no | +| [ingress\_nlb\_zones\_subnets](#input\_ingress\_nlb\_zones\_subnets) | Map of tuples "subnetID": "VPC zone" to configure IBM Cloud Network LoadBalancer instances on the expected zone and subnet. Null value is not allowed. Default to empty map. | `map(string)` | `{}` | no | +| [ingress\_pdb\_configuration](#input\_ingress\_pdb\_configuration) | Configuration of the PodDisruptionBudget for the istio ingress definition. Default to null to leverage on Istio default configuration. |
object({
minAvailable = optional(string, null)
maxUnavailable = optional(string, null)
})
| `null` | no | +| [ingress\_ports](#input\_ingress\_ports) | List of ports to configured on ingress and LoadBalancer to list for inbound traffic. Default to port 443:8443 on TCP. |
list(object(
{
port : number,
name : string
proto : string,
targetPort : number
}
))
|
[
{
"name": "https",
"port": 443,
"proto": "TCP",
"targetPort": 8443
}
]
| no | +| [ingress\_proxy\_protocol\_allow\_without](#input\_ingress\_proxy\_protocol\_allow\_without) | Flag to support traffic with or without Proxy Protocol on ingress LoadBalancer (only ALB type) and on the EnvoyFilter that implements Proxy Protocol on ingress gateway | `bool` | `false` | no | +| [ingress\_replicas](#input\_ingress\_replicas) | Istio ingress deployment replicaset configuration. If the var.ingress\_autoscale\_configuration.enabled is true this value is ignored. Default to 3. | `number` | `3` | no | +| [ingress\_resources\_configuration](#input\_ingress\_resources\_configuration) | Istio ingress resources deployment configuration. Default configuration is null and leverages on Istio default setting. |
object(
{
limits : optional(object(
{
cpu : optional(string, null),
memory : optional(string, null)
}), null),
requests : optional(object(
{
cpu : optional(string, null)
memory : optional(string, null)
}), null)
}
)
| `null` | no | +| [ingress\_selectors](#input\_ingress\_selectors) | Istio ingress selectors to route inbound ingress traffic to the expected istio gateway and to the expected workload. Default to "app": "istio-ingress" "istio": "istio-ingress". Null not allowed | `map(string)` |
{
"app": "istio-ingress",
"istio": "istio-ingress"
}
| no | +| [ingress\_service\_type](#input\_ingress\_service\_type) | Istio ingress type for the service (svc) resource definition: possible values are LoadBalancer and ClusterIP. Default to LoadBalancer | `string` | `"LoadBalancer"` | no | +| [ingress\_termination\_grace\_period](#input\_ingress\_termination\_grace\_period) | Number of seconds for the Istio ingress deployment for the grace period before terminating the pods and dropping the connections. Default to null to leverage on Istio default. | `number` | `null` | no | +| [ingress\_tolerations](#input\_ingress\_tolerations) | Istio ingress tolerations configuration. Default to tolerate 'dedicated: edge' taint. For more details # https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#toleration-v1-core | `list(any)` | `[]` | no | +| [istio\_ingress\_deployment\_timeout](#input\_istio\_ingress\_deployment\_timeout) | Timeout for the helm release deployment for the ingress gateway | `string` | `null` | no | +| [istio\_mesh\_enrollment](#input\_istio\_mesh\_enrollment) | Name of the Istio mesh controlplane to enroll this dataplane with. Default value to default. This value is used to generate discovery selectors, to override the computed values customise var.ingress\_discovery\_custom\_configuration. | `string` | `"default"` | no | +| [name](#input\_name) | Name of the Istio ingress deployment | `string` | n/a | yes | +| [namespace](#input\_namespace) | Namespace where to install istio ingress dataplane. | `string` | n/a | yes | + +### Outputs + +| Name | Description | +|------|-------------| +| [istio\_ingress\_metadata](#output\_istio\_ingress\_metadata) | istio\_ingress definition metadata | + diff --git a/modules/sm-istio-ingress/main.tf b/modules/sm-istio-ingress/main.tf new file mode 100644 index 0000000..2ea44de --- /dev/null +++ b/modules/sm-istio-ingress/main.tf @@ -0,0 +1,197 @@ +locals { + istio_ingress_release_name = "${var.namespace}-${var.name}" + istio_ingress_chart_path = "istio-ingress" + + ingress_discovery_configuration = var.ingress_discovery_custom_configuration != null ? var.ingress_discovery_custom_configuration : ( + var.istio_mesh_enrollment == "default" ? { + "istio-discovery" : "enabled", + "istio-injection" : "enabled", + } : { + "istio-discovery" : var.istio_mesh_enrollment, + "istio.io/rev" : var.istio_mesh_enrollment, + } + ) + + ingress_selectors = { + "ingress" : { + "istioselectors" : var.ingress_selectors + } + } + + ingress_alb_subnets = { + "ingress" : { + "albsubnets" : var.ingress_alb_subnets + } + } + + ingress_nlb_zones_subnets = { + "ingress" : { + "nlbzonessubnets" : var.ingress_nlb_zones_subnets + } + } + + ingress_ports = { + "ingress" : { + "ports" : var.ingress_ports + } + } + + ingress_autoscale_configuration = { + "ingress" : { + "autoscale" : var.ingress_autoscale_configuration + } + } + + ingress_pdb_configuration = var.ingress_pdb_configuration == null ? {} : { + "ingress" : { + "pdb" : var.ingress_pdb_configuration + } + } + + ingress_resources_configuration = var.ingress_resources_configuration == null ? {} : { + "ingress" : { + "resources" : var.ingress_resources_configuration + } + } + + ingress_affinity = var.ingress_affinity == null ? {} : { + "ingress" : { + "affinity" : var.ingress_affinity + } + } + + ingress_tolerations = var.ingress_tolerations == null ? {} : { + "ingress" : { + "tolerations" : var.ingress_tolerations + } + } + +} + +module "ingress_namespace" { + count = var.create_namespace ? 1 : 0 + source = "terraform-ibm-modules/namespace/ibm" + version = "v1.0.3" + namespaces = [ + { + name = var.namespace + metadata = { + labels = local.ingress_discovery_configuration + annotations = local.ingress_discovery_configuration + } + } + ] +} + +# installing helm chart for istio deployment +resource "helm_release" "istio_ingress" { + depends_on = [module.ingress_namespace[0]] + name = local.istio_ingress_release_name + chart = "${path.module}/../../chart/${local.istio_ingress_chart_path}" + namespace = var.namespace + create_namespace = false + timeout = var.istio_ingress_deployment_timeout + dependency_update = true + force_update = var.force_dataplane_update + cleanup_on_fail = false + wait = true + + disable_openapi_validation = false + + set = [ + { + name = "ingress.name" + type = "string" + value = var.name + }, + { + name = "ingress.namespace" + type = "string" + value = var.namespace + }, + { + name = "ingress.svctype" + type = "string" + value = var.ingress_service_type # LoadBalancer + }, + { + name = "ingress.lbtype" + type = "string" + value = var.ingress_loadbalancer_type # alb nlb sdnlb + }, + { + name = "ingress.lbiptype" + type = "string" + value = var.ingress_ip_type + }, + { + name = "ingress.externalTrafficPolicy" + type = "string" + value = var.ingress_external_traffic_policy + }, + { + name = "ingress.internalTrafficPolicy" + type = "string" + value = var.ingress_internal_traffic_policy + }, + { + name = "ingress.replicacount" + type = "string" + value = var.ingress_replicas + }, + { + name = "ingress.terminationGracePeriodSeconds" + type = "string" + value = var.ingress_termination_grace_period + }, + { + name = "ingress.proxyProtocol.enabled" + value = var.ingress_enable_proxy_protocol + }, + { + name = "ingress.proxyProtocol.allowWithoutProxyProtocol" + value = var.ingress_proxy_protocol_allow_without + } + + ] + + # yamlencode(local.ingress_namespace_enrollment_labels), + values = [ + yamlencode(local.ingress_selectors), + yamlencode(local.ingress_alb_subnets), + yamlencode(local.ingress_nlb_zones_subnets), + yamlencode(local.ingress_ports), + yamlencode(local.ingress_autoscale_configuration), + yamlencode(local.ingress_pdb_configuration), + yamlencode(local.ingress_resources_configuration), + yamlencode(local.ingress_affinity), + yamlencode(local.ingress_tolerations), + ] + +} + + +resource "null_resource" "confirm_ingress_operational_alb" { + depends_on = [helm_release.istio_ingress] + count = var.ingress_loadbalancer_type == "alb" && var.cluster_config_file_path != null ? 1 : 0 + provisioner "local-exec" { + command = "${path.module}/scripts/confirm-ingress-operational.sh \"${var.namespace}\" \"ingress-${var.name}\"" + interpreter = ["/bin/bash", "-c"] + environment = { + KUBECONFIG = var.cluster_config_file_path + } + } +} + +# for nlb the ingress svc are created for each zone so there are a set of svc to check named "ingress-[svc name]-[zone]" +resource "null_resource" "confirm_ingress_operational_nlb" { + depends_on = [helm_release.istio_ingress] + for_each = var.ingress_loadbalancer_type == "nlb" && var.cluster_config_file_path != null ? var.ingress_nlb_zones_subnets : {} + provisioner "local-exec" { + command = "${path.module}/scripts/confirm-ingress-operational.sh \"${var.namespace}\" \"ingress-${var.name}-${each.value}\"" + interpreter = ["/bin/bash", "-c"] + environment = { + KUBECONFIG = var.cluster_config_file_path + } + } +} diff --git a/modules/sm-istio-ingress/outputs.tf b/modules/sm-istio-ingress/outputs.tf new file mode 100644 index 0000000..fb8c5e2 --- /dev/null +++ b/modules/sm-istio-ingress/outputs.tf @@ -0,0 +1,4 @@ +output "istio_ingress_metadata" { + description = "istio_ingress definition metadata" + value = helm_release.istio_ingress.metadata +} diff --git a/modules/sm-istio-ingress/scripts/confirm-ingress-operational.sh b/modules/sm-istio-ingress/scripts/confirm-ingress-operational.sh new file mode 100755 index 0000000..c92e42b --- /dev/null +++ b/modules/sm-istio-ingress/scripts/confirm-ingress-operational.sh @@ -0,0 +1,87 @@ +#!/usr/bin/env bash +# This script is designed to verify that all key components of Istio dataplane for ingress gateway are up and running. +# This is needed before apps with inject-sidecar are deployed + +set -e + +namespace="${1}" +service="${2}" +fail=false +initialsleep=15 + +# if service is not set using default value +if [[ -z "${service}" ]]; then + service="istio-ingressgateway" +fi + +echo "Checking ingress gateway ${service} successful deployment in namespace ${namespace}" + +echo "Initial sleep of ${initialsleep} seconds before starting to check" +sleep "${initialsleep}" + +echo "Checking deployments ${service} in namespace ${namespace}" + +DEPLOYMENTS=() +while IFS='' read -r line; do DEPLOYMENTS+=("$line"); done < <(kubectl get deployment "${service}" -n "${namespace}" --no-headers | cut -f1 -d ' ') + +# Wait for all deployments to come up - timeout after 5 mins +# shellcheck disable=SC2068 +for dep in ${DEPLOYMENTS[@]}; do + echo "Checking deployment ${dep} status in namespace ${namespace}" + if ! kubectl rollout status deployment "$dep" -n "${namespace}" --timeout 5m; then + echo "Deployment ${dep} in namespace ${namespace} seems failing to deploy" + fail=true + fi +done + + +# Ensure the load balancer hostname is set +counter=0 +sleeptime=30 +retries=60 # 60 x 30 = 1800 secs = 30 mins +ext_hostname="" +while [ -z "${ext_hostname}" ]; do + # Get the hostname from the kube service (retry needed on service lookup, as sometimes service may not exist yet) + attempts=20 # 20 x 30 = 600 secs = 10 mins + n=0 + until [ "$n" -ge $attempts ]; do + ext_hostname=$(kubectl get svc "${service}" -n "${namespace}" --template="{{range .status.loadBalancer.ingress}}{{.hostname}}{{end}}") && break + n=$((n+1)) + if [ "$n" = $attempts ]; then + echo "Maximum attempts reached for gateway ${service} in namespace ${namespace}. Giving up!" + exit 1 + else + echo "Retrying in ${sleeptime} secs .." + sleep "${sleeptime}" + fi + done + + # If not set yet, retry + if [ -z "${ext_hostname}" ]; then + # Give up when number of retries are reached + if [ ${counter} == ${retries} ]; then + echo "ERROR: Unable to detect external hostname for ${service} in namespace ${namespace}" + fail=true + break + fi + counter=$((counter+1)) + sleep "${sleeptime}" + else + # break the loop if hostname value detected + echo "istio gateway ${service} in namespace ${namespace} assigned with hostname: ${ext_hostname}" + # TODO: Add some health checks against the LB + break + fi +done + +# Fail with some debug prints if issues detected +if [ ${fail} == true ]; then + echo "Problem detected with gateway ${service} in namespace ${namespace}. Printing some debug info.." + set +e + kubectl get svc -n "${namespace}" -o wide + kubectl get deployment "${service}" -n "${namespace}" -o wide + kubectl get pods -n "${namespace}" -o wide + kubectl describe svc "${service}" -n "${namespace}" + kubectl describe svc "${service}" -n "${namespace}" + # exit 1 +fi diff --git a/modules/sm-istio-ingress/variables.tf b/modules/sm-istio-ingress/variables.tf new file mode 100644 index 0000000..76c3560 --- /dev/null +++ b/modules/sm-istio-ingress/variables.tf @@ -0,0 +1,281 @@ +variable "name" { + type = string + description = "Name of the Istio ingress deployment" +} + +variable "create_namespace" { + type = bool + description = "Flag to create the namespace where to install istio ingress dataplane. Default to true" + default = true +} + +variable "namespace" { + type = string + description = "Namespace where to install istio ingress dataplane." +} + +variable "force_dataplane_update" { + description = "Force dataplane to be updated" + default = false + type = bool + nullable = false +} + +variable "ingress_service_type" { + type = string + description = "Istio ingress type for the service (svc) resource definition: possible values are LoadBalancer and ClusterIP. Default to LoadBalancer" + default = "LoadBalancer" + nullable = false + validation { + condition = contains(["LoadBalancer", "ClusterIP"], var.ingress_service_type) + error_message = "The allowed values for var.ingress_service_type are LoadBalancer and ClusterIP." + } +} + +variable "ingress_loadbalancer_type" { + type = string + default = "alb" + nullable = false + description = "IBM Cloud LoadBalancer type bound to the ingress: valid values are \"alb\" for Application Load Balancer and \"nlb\" for Network Load Balancer. If var.ingress_service_type == \"ClusterIP\" this value hasn't effect. For more details refer to https://cloud.ibm.com/docs/vpc?topic=vpc-nlb-vs-elb. Default to alb." + validation { + condition = contains(["alb", "nlb"], var.ingress_loadbalancer_type) + error_message = "The allowed values for var.ingress_service_type are alb and nlb." + } +} + +variable "ingress_ip_type" { + type = string + default = "public" + description = "IBM Cloud LoadBalancer IP type: valid values are public and private. Default to public. If var.ingress_service_type == \"ClusterIP\" this value hasn't effect." + nullable = false + validation { + condition = contains(["public", "private"], var.ingress_ip_type) + error_message = "The allowed values for var.ingress_ip_type are public and private." + } +} + +variable "istio_mesh_enrollment" { + type = string + default = "default" + description = "Name of the Istio mesh controlplane to enroll this dataplane with. Default value to default. This value is used to generate discovery selectors, to override the computed values customise var.ingress_discovery_custom_configuration." +} + +variable "istio_ingress_deployment_timeout" { + type = string + default = null + description = "Timeout for the helm release deployment for the ingress gateway" +} + +variable "ingress_discovery_custom_configuration" { + type = map(string) + default = null + description = "Map of key-value entries to set custom istio discovery labels. Default to null to autogenerate the labels according to var.istio_mesh_enrollment value. For more details about istio discovery configuration refer to https://docs.redhat.com/en/documentation/red_hat_openshift_service_mesh/3.0/html/installing/ossm-sidecar-injection#ossm-about-sidecar-injection_ossm-sidecar-injection and https://docs.redhat.com/en/documentation/red_hat_openshift_service_mesh/3.0/html/installing/ossm-deploying-multiple-service-meshes-on-single-cluster." +} + +variable "ingress_selectors" { + type = map(string) + default = { + "app" : "istio-ingress", + "istio" : "istio-ingress", + } + nullable = false + description = "Istio ingress selectors to route inbound ingress traffic to the expected istio gateway and to the expected workload. Default to \"app\": \"istio-ingress\" \"istio\": \"istio-ingress\". Null not allowed" +} + +variable "ingress_alb_subnets" { + type = list(string) + default = [] + nullable = false + description = "List of VPC subnets to attach to the IBM Cloud Application LoadBalancer bound to the cluster. Null value is not allowed. Default to empty list." +} + +variable "ingress_nlb_zones_subnets" { + type = map(string) + default = {} + nullable = false + description = "Map of tuples \"subnetID\": \"VPC zone\" to configure IBM Cloud Network LoadBalancer instances on the expected zone and subnet. Null value is not allowed. Default to empty map." +} + +variable "ingress_ports" { + type = list(object( + { + port : number, + name : string + proto : string, + targetPort : number + } + )) + default = [{ + port : 443, + name : "https", + proto : "TCP", + targetPort : 8443 + }] + description = "List of ports to configured on ingress and LoadBalancer to list for inbound traffic. Default to port 443:8443 on TCP." +} + +variable "ingress_external_traffic_policy" { + type = string + default = "Cluster" + description = "External traffic policy configuration for the ingress. Allowed values are Cluster and Local. Default to Cluster. For more details refer to https://istio.io/latest/docs/tasks/security/authorization/authz-ingress/." + nullable = false +} + +variable "ingress_internal_traffic_policy" { + type = string + default = "Local" + description = "Internal traffic policy configuration for the ingress. Allowed values are Cluster and Local. Default to Local. For more details refer to https://istio.io/latest/docs/tasks/security/authorization/authz-ingress/." + nullable = false +} + +variable "ingress_autoscale_configuration" { + type = object({ + enabled : optional(bool, false), + autoscaleMin : optional(number, 1), + autoscaleMax : optional(number, 5), + cpu : optional(object( + { + targetavgutil : optional(number, 80) + } + )) + memory : optional(object( + { + targetavgutil : optional(number, 80) + } + )) + }) + default = { + enabled : false + } + description = "Ingress autoscale configuration defined through HPA. If enabled is set to true the HPA definition is deployed. Otherwise if false the HPA configuration is not deployed. Default to enabled=false." +} + +variable "ingress_pdb_configuration" { + description = "Configuration of the PodDisruptionBudget for the istio ingress definition. Default to null to leverage on Istio default configuration." + default = null + type = object({ + minAvailable = optional(string, null) + maxUnavailable = optional(string, null) + }) + validation { + condition = var.ingress_pdb_configuration == null ? true : (var.ingress_pdb_configuration.minAvailable != null && var.ingress_pdb_configuration.maxUnavailable != null ? false : true) + error_message = "only one of minAvailable and maxUnavailable for var.ingress_pdb_configuration can be set to a value not null" + } + validation { + condition = var.ingress_pdb_configuration == null ? true : (var.ingress_pdb_configuration.minAvailable == null ? true : can(regex("^([0-9]{1,3}%?)$", var.ingress_pdb_configuration.minAvailable))) + error_message = "minAvailable for var.ingress_pdb_configuration value is not valid. It can be set only to a number or percentage (regex ^([0-9]{1,3}%?)$)" + } + validation { + condition = var.ingress_pdb_configuration == null ? true : (var.ingress_pdb_configuration.maxUnavailable == null ? true : can(regex("^([0-9]{1,3}%?)$", var.ingress_pdb_configuration.maxUnavailable))) + error_message = "maxUnavailable for var.ingress_pdb_configuration value is not valid. It can be set only to a number or percentage (regex ^([0-9]{1,3}%?)$)" + } + validation { + condition = var.ingress_pdb_configuration == null ? true : (var.ingress_pdb_configuration.minAvailable == null ? true : tostring(var.ingress_pdb_configuration.minAvailable) != "0") + error_message = "minAvailable for var.ingress_pdb_configuration must be set to a value greater than 0" + } + validation { + condition = var.ingress_pdb_configuration == null ? true : (var.ingress_pdb_configuration.minAvailable == null ? true : var.ingress_pdb_configuration.minAvailable != "0%") + error_message = "minAvailable for var.ingress_pdb_configuration must be set to a value greater than 0%" + } +} + +variable "ingress_replicas" { + type = number + default = 3 + description = "Istio ingress deployment replicaset configuration. If the var.ingress_autoscale_configuration.enabled is true this value is ignored. Default to 3." + nullable = false +} + +variable "ingress_resources_configuration" { + type = object( + { + limits : optional(object( + { + cpu : optional(string, null), + memory : optional(string, null) + }), null), + requests : optional(object( + { + cpu : optional(string, null) + memory : optional(string, null) + }), null) + } + ) + description = "Istio ingress resources deployment configuration. Default configuration is null and leverages on Istio default setting." + default = null +} + +variable "ingress_termination_grace_period" { + type = number + description = "Number of seconds for the Istio ingress deployment for the grace period before terminating the pods and dropping the connections. Default to null to leverage on Istio default." + default = null +} + +variable "ingress_affinity" { + type = object({ + podAntiAffinity : optional(any, null), + podAffinity : optional(any, null), + nodeAffinity : optional(any, null) + }) + default = { + nodeAffinity : { + requiredDuringSchedulingIgnoredDuringExecution : { + nodeSelectorTerms : [ + { + matchExpressions : [ + { + key : "ibm-cloud.kubernetes.io/worker-pool-name", + operator : "In", + values : ["edge"] + } + ] + } + ] + } + } + } + description = "Istio ingress affinity configuration. For more details https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#affinity-v1-core. Ingress pods are provided of a label with key \"istio.io/gateway\" and value \"[DEPLOYMENT NAME].[DEPLOYMENT NAMESPACE]\" in order to allow to set them as antiAffinity labels." + # podAntiAffinity example + # podAntiAffinity : { + # preferredDuringSchedulingIgnoredDuringExecution : [ + # { + # podAffinityTerm : { + # labelSelector : { + # matchExpressions : [ + # { + # key : "istio.io/gateway", + # operator : "In", + # values : ["def-workload-ingress.default-workload"] + # } + # ] + # } + # } + # topologyKey : "topology.kubernetes.io/zone" + # weight : 100 + # } + # ] + # } +} + +variable "ingress_tolerations" { + type = list(any) + default = [] + description = "Istio ingress tolerations configuration. Default to tolerate 'dedicated: edge' taint. For more details # https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#toleration-v1-core" +} + +variable "cluster_config_file_path" { + type = string + description = "Cluster config file path to use with kubernetes provider to run checks on the resources deployment. Set to null to skip this check." +} + +variable "ingress_enable_proxy_protocol" { + description = "Flag to enable Proxy Protocol on ingress LoadBalancer (only ALB type) and to enable the EnvoyFilter to implement Proxy Protocol on ingress gateway" + type = bool + default = false +} + +variable "ingress_proxy_protocol_allow_without" { + description = "Flag to support traffic with or without Proxy Protocol on ingress LoadBalancer (only ALB type) and on the EnvoyFilter that implements Proxy Protocol on ingress gateway" + type = bool + default = false +} diff --git a/modules/sm-istio-ingress/version.tf b/modules/sm-istio-ingress/version.tf new file mode 100644 index 0000000..4970b25 --- /dev/null +++ b/modules/sm-istio-ingress/version.tf @@ -0,0 +1,14 @@ +terraform { + required_version = ">= 1.9.0" + required_providers { + # Use "greater than or equal to" range in modules + helm = { + source = "hashicorp/helm" + version = ">= 3.0.0" + } + null = { + source = "hashicorp/null" + version = ">= 3.2.1, < 4.0.0" + } + } +} diff --git a/modules/sm-istio/README.md b/modules/sm-istio/README.md new file mode 100644 index 0000000..6343ab6 --- /dev/null +++ b/modules/sm-istio/README.md @@ -0,0 +1,157 @@ +# Service Mesh Istio module + +## Overview +This module deploys the Istio resource that defines a single Istio controlplane on a cluster + +### Discovery selectors configuration + +This module allows to setup the controlplane discovery selectors according to the controlplane name or to customise the selectors according to user's own labels and keys, following the Red Hat Service Mesh [requirements](https://docs.redhat.com/en/documentation/red_hat_openshift_service_mesh/3.0/html/installing/ossm-installing-service-mesh#ossm-scoping-service-mesh-with-discoveryselectors_ossm-installing-openshift-service-mesh) + +- if the input parameter `var.istio_discovery_custom_configuration` is left to its default value `null` the controlplane discovery selector is configured with the following logic: + - if the Istio controlplane name (`var.name`) is left to its default value `default` the discovery selectors are set with `"matchLabels" : { "istio-discovery" : "enabled" }` configuration + - if the Istio controlplane name (`var.name`) is not set to `default` the discovery selectors are set with `"matchLabels" : { "istio-discovery" : var.name }` +- if the input parameter `var.istio_discovery_custom_configuration` is customised its values are configured into the discovery selectors of the controlplane + +For the controlplane namespace, that must be configured with the same discovery selectors set for the controlplane discovery, it is possible to follow a similar logic through the input variable `var.istio_namespace_discovery_custom_labels`: +- if left to its default `null` value the controlplane namespace is labeled with `{ "istio-discovery" = "enabled" }` if the controlplane name is the default value `default` or with `{ "istio-discovery" : var.name }` if the controlplane name is not `default` +- if not null, it will be used to label the controlplane namespace + +### Example: Basic default configuration + +This configuration deploys a default Istio controlplane with the default configurations for controlplane named `default` and discovery selectors `"matchLabels" : { "istio-discovery" : "enabled" }` + +``` +module "deploy_istio" { + depends_on = [module.service_mesh_operator] + source = "terraform-ibm-modules/ocp-service-mesh/ibm//modules/sm-istio" + version = "X.Y.Z" + name = "default" + namespace = "istio-system" + create_namespace = true + cluster_config_file_path = data.ibm_container_cluster_config.cluster_config.config_file_path +} +``` + +### Example: Advanced configuration + +The following controlplane is configured with the following attributes: +- name: `mesh-2` +- namespace: `istio-system-2` +- discovery selector: match on the label `istio-discovery = "mesh-2"` +- controlplane force to be recreated when the resource is updated (default to not force, which may need to taint the resource to update) +- ServiceMesh mTLS enabled (default) +- pilot autoscaling enabled with max and min pods, target memory and cpu +- pilot pods node selector set to nodes label `"ibm-cloud.kubernetes.io/worker-pool-name": "default"` +- pilot pods toleration +- pilot resources requests and limits for memory and CPU +- tcp keep alive for the mesh + +``` +module "istio" { + depends_on = [module.service_mesh] + source = "terraform-ibm-modules/ocp-service-mesh/ibm//modules/sm-istio" + version = "X.Y.Z" + name = "mesh-2" + namespace = "istio-system-2" + istio_discovery_custom_configuration = { + matchLabels = { + istio-discovery = "mesh-2" + } + force_controlplane_update = true + mesh_config_enable_mtls = true + pilot_autoscaling_enabled = true + pilot_autoscaling_max_pods = 10 + pilot_autoscaling_min_pods = 3 + pilot_autoscaling_target_memory = 75 + pilot_autoscaling_target_cpu = 70 + pilot_node_selector = { "ibm-cloud.kubernetes.io/worker-pool-name": "default" } + pilot_tolerations = [ + { + key : "dedicated" + value : "transit" + effect : "NoExecute" + } + ] + pilot_resources = { + "requests": { + "cpu": "200m" + "memory": "128Mi" + } + "limits": { + "cpu": "500m" + "memory": "256Mi" + } + } + mesh_config_tcp_keep_alive = { + probes = 10 + } + cluster_config_file_path = data.ibm_container_cluster_config.cluster_config.config_file_path +} +``` + +For all the configuration parameters details refer to the section below + + +### Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.9.0 | +| [helm](#requirement\_helm) | >= 3.0.0 | +| [null](#requirement\_null) | >= 3.2.1, < 4.0.0 | + +### Modules + +| Name | Source | Version | +|------|--------|---------| +| [istio\_namespace](#module\_istio\_namespace) | terraform-ibm-modules/namespace/ibm | v1.0.3 | + +### Resources + +| Name | Type | +|------|------| +| [helm_release.istio_controlplane](https://registry.terraform.io/providers/hashicorp/helm/latest/docs/resources/release) | resource | +| [null_resource.confirm_istio_operational](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource | + +### Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [cluster\_config\_file\_path](#input\_cluster\_config\_file\_path) | Cluster config file path to use with kubernetes provider to run checks on the resources deployment | `string` | n/a | yes | +| [create\_namespace](#input\_create\_namespace) | Flag to create the namespace where to install istio controlplane. Default to true | `bool` | `true` | no | +| [force\_controlplane\_update](#input\_force\_controlplane\_update) | Force controlplane to be recreated when updated. Default to false (may require to taint the resource to apply changes) | `bool` | `false` | no | +| [istio\_discovery\_custom\_configuration](#input\_istio\_discovery\_custom\_configuration) | Istio controlplane discovery label. Default to null to autogenerate the labels according to var.name value to matchLabels: {"istio-discovery" : "enabled"}. For more details https://istio.io/latest/blog/2021/discovery-selectors/ https://github.com/istio/api/blob/master/mesh/v1alpha1/config.proto#L1411 https://docs.redhat.com/en/documentation/red_hat_openshift_service_mesh/3.0/html/installing/ossm-installing-service-mesh#ossm-discoveryselectors-scope-service-mesh_ossm-installing-openshift-service-mesh |
object({
matchLabels : optional(map(string), null),
matchExpressions : optional(list(object({
key : string
operator : string
values : list(string)
})), [])
})
| `null` | no | +| [istio\_enable\_default\_pod\_disruption\_budget](#input\_istio\_enable\_default\_pod\_disruption\_budget) | Controls whether a PodDisruptionBudget with a default minAvailable value of 1 is created for each deployment. Default to null, using Istio default configuration. More details at https://github.com/istio-ecosystem/sail-operator/blob/main/docs/api-reference/sailoperator.io.md#defaultpoddisruptionbudgetconfig | `bool` | `null` | no | +| [istio\_namespace\_discovery\_custom\_labels](#input\_istio\_namespace\_discovery\_custom\_labels) | Istio controlplane discovery label to apply to controlplane namespace. Default to null to autogenerate the labels according to var.name to {"istio-discovery" : "enabled"}. If overridden consider it to be coherent with selectors of var.istio\_discovery\_configuration. For more details https://istio.io/latest/blog/2021/discovery-selectors/ | `map(string)` | `null` | no | +| [mesh\_config\_access\_log\_encoding](#input\_mesh\_config\_access\_log\_encoding) | Encoding for the Istio proxy access log. Default value set to JSON. Allowed values TEXT or JSON | `string` | `"JSON"` | no | +| [mesh\_config\_access\_log\_file](#input\_mesh\_config\_access\_log\_file) | File address for the Istio proxy access log. Empty value disables access logging. Default to /dev/stdout | `string` | `"/dev/stdout"` | no | +| [mesh\_config\_access\_log\_format](#input\_mesh\_config\_access\_log\_format) | Format for the Istio proxy access log. Set to empty or null to use proxy's default access log format. | `string` | `"[%START_TIME%] [%REQ(:AUTHORITY)%] [%BYTES_RECEIVED%] [%BYTES_SENT%] [%DOWNSTREAM_LOCAL_ADDRESS%] [%DOWNSTREAM_LOCAL_ADDRESS%] [%DOWNSTREAM_REMOTE_ADDRESS%] [%DOWNSTREAM_TLS_VERSION%] [%DURATION%] [%REQUEST_DURATION%] [%RESPONSE_DURATION%] [%RESPONSE_TX_DURATION%] [%DYNAMIC_METADATA(istio.mixer:status)%] [%REQ(:METHOD)%] [%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%] [%PROTOCOL%] [%REQ(X-REQUEST-ID)%] [%REQUESTED_SERVER_NAME%] [%RESPONSE_CODE%] [%RESPONSE_CODE_DETAILS%] [%RESPONSE_FLAGS%] [%ROUTE_NAME%] [%START_TIME%] [%UPSTREAM_CLUSTER%] [%UPSTREAM_HOST%] [%UPSTREAM_LOCAL_ADDRESS%] [%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%] [%UPSTREAM_TRANSPORT_FAILURE_REASON%] [%REQ(USER-AGENT)%] [%REQ(X-FORWARDED-FOR)%] [%REQ(X-ENVOY-ATTEMPT-COUNT)%]"` | no | +| [mesh\_config\_connect\_timeout](#input\_mesh\_config\_connect\_timeout) | Connection timeout used by Envoy. Default to 10s | `string` | `"10s"` | no | +| [mesh\_config\_enable\_mtls](#input\_mesh\_config\_enable\_mtls) | Enable mTLS in the Istio controlplane. Default to true | `bool` | `true` | no | +| [mesh\_config\_ingress\_controller\_mode](#input\_mesh\_config\_ingress\_controller\_mode) | Istio Mesh configuration for ingress controller mode. Default to STRICT. More details at https://github.com/istio-ecosystem/sail-operator/blob/main/docs/api-reference/sailoperator.io.md#meshconfigingresscontrollermode | `string` | `"STRICT"` | no | +| [mesh\_config\_ingress\_selector](#input\_mesh\_config\_ingress\_selector) | Defines which gateway deployment to use as the Ingress controller. This field corresponds to the Gateway.selector field, and will be set as istio: INGRESS\_SELECTOR. By default, ingressgateway is used, which will select the default IngressGateway as it has the istio: ingressgateway labels. It is recommended that this is the same value as ingressService. More details at https://github.com/istio-ecosystem/sail-operator/blob/main/docs/api-reference/sailoperator.io.md#meshconfig | `string` | `"ingressgateway"` | no | +| [mesh\_config\_ingress\_service](#input\_mesh\_config\_ingress\_service) | Name of the Kubernetes service used for the istio ingress controller. If no ingress controller is specified, the default value istio-ingressgateway is used. Default to istio-ingressgateway. More details at https://github.com/istio-ecosystem/sail-operator/blob/main/docs/api-reference/sailoperator.io.md#meshconfig | `string` | `"istio-ingressgateway"` | no | +| [mesh\_config\_mesh\_mtls](#input\_mesh\_config\_mesh\_mtls) | Defines the mesh mTLS configuration. For more details https://github.com/istio-ecosystem/sail-operator/blob/main/docs/api-reference/sailoperator.io.md#meshconfig and https://github.com/istio-ecosystem/sail-operator/blob/main/docs/api-reference/sailoperator.io.md#meshconfigtlsconfig. |
object({
minProtocolVersion : optional(string, "TLSV1_2")
ecdhCurves : optional(list(string), null)
cipherSuites : optional(list(string), ["TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"])
})
|
{
"cipherSuites": [
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"
],
"minProtocolVersion": "TLSV1_2"
}
| no | +| [mesh\_config\_mesh\_tls\_defaults](#input\_mesh\_config\_mesh\_tls\_defaults) | Defines the TLS for all traffic except for ISTIO\_MUTUAL mode For ISTIO\_MUTUAL TLS settings, use var.mesh\_config\_mesh\_mtls . For more details https://github.com/istio-ecosystem/sail-operator/blob/main/docs/api-reference/sailoperator.io.md#meshconfig and https://github.com/istio-ecosystem/sail-operator/blob/main/docs/api-reference/sailoperator.io.md#meshconfigtlsconfig. |
object({
minProtocolVersion : optional(string, "TLSV1_2")
ecdhCurves : optional(list(string), null)
cipherSuites : optional(list(string), ["TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"])
})
|
{
"cipherSuites": [
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"
],
"minProtocolVersion": "TLSV1_2"
}
| no | +| [mesh\_config\_tcp\_keep\_alive](#input\_mesh\_config\_tcp\_keep\_alive) | Istio configuration for TCP keepalive. Default to null, using the Istio default configuration. More details at https://github.com/istio-ecosystem/sail-operator/blob/main/docs/api-reference/sailoperator.io.md#connectionpoolsettingstcpsettingstcpkeepalive |
object({
probes : optional(number, 9),
time : optional(string, "7200s")
interval : optional(string, "75s")
})
| `null` | no | +| [name](#input\_name) | Name of the Istio controlplane revision | `string` | n/a | yes | +| [namespace](#input\_namespace) | Namespace where to install istio controlplane. | `string` | n/a | yes | +| [outboundtrafficpolicy](#input\_outboundtrafficpolicy) | Istio controlplane output traffic policy configuration. Default to ALLOW\_ANY. Values allowed ALLOW\_ANY or REGISTRY\_ONLY | `string` | `"ALLOW_ANY"` | no | +| [pilot\_affinity](#input\_pilot\_affinity) | Istio pilot pods affinity configuration. For more details https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#affinity-v1-core. |
object({
podAntiAffinity : optional(any, null),
podAffinity : optional(any, null),
nodeAffinity : optional(any, null)
})
|
{
"podAntiAffinity": {
"preferredDuringSchedulingIgnoredDuringExecution": [
{
"podAffinityTerm": {
"labelSelector": {
"matchExpressions": [
{
"key": "istio",
"operator": "In",
"values": [
"istiod"
]
}
]
},
"topologyKey": "topology.kubernetes.io/zone"
},
"weight": 100
}
]
}
}
| no | +| [pilot\_autoscaling\_enabled](#input\_pilot\_autoscaling\_enabled) | Enable Istio pilot autoscaling through HorizontalPodAutoscaler. Default to false | `bool` | `false` | no | +| [pilot\_autoscaling\_max\_pods](#input\_pilot\_autoscaling\_max\_pods) | If var.pilot\_autoscaling\_enabled is enabled this sets the maximum amount of pods for Istio pilot HorizontalPodAutoscaler. Default to 5 | `number` | `5` | no | +| [pilot\_autoscaling\_min\_pods](#input\_pilot\_autoscaling\_min\_pods) | If var.pilot\_autoscaling\_enabled is enabled this sets the minimum amount of pods for Istio pilot HorizontalPodAutoscaler. Default to 1 | `number` | `1` | no | +| [pilot\_autoscaling\_target\_cpu](#input\_pilot\_autoscaling\_target\_cpu) | If var.pilot\_autoscaling\_enabled is enabled this sets the target CPU average load. Default to 80 (%). Set to null to leverage on Istio default value. | `number` | `80` | no | +| [pilot\_autoscaling\_target\_memory](#input\_pilot\_autoscaling\_target\_memory) | If var.pilot\_autoscaling\_enabled is enabled this sets the target memory average load. Default to 80 (%). Set to null to leverage on Istio default value. | `number` | `80` | no | +| [pilot\_enabled](#input\_pilot\_enabled) | Enable Istio pilot. Default to true. | `bool` | `true` | no | +| [pilot\_node\_selector](#input\_pilot\_node\_selector) | Node selector configuration for Istio pilot pods. Default to null. For more details https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector | `map(string)` | `null` | no | +| [pilot\_replicas](#input\_pilot\_replicas) | Sets the number of replicas to deploy the Istio Pilot. Valid only if var.pilot\_autoscaling\_enabled is false. Default to 1 | `number` | `1` | no | +| [pilot\_resources](#input\_pilot\_resources) | Istio pilot pods resources requests and limits for memory and CPU. Default to requests CPU 10m memory 128M limits CPU 100m memory 256M, using the default Istio values. For more details # https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#resourcerequirements-v1-core |
object({
limits : optional(map(string), null),
requests : optional(map(string), null)
})
|
{
"limits": {
"cpu": "100m",
"memory": "256M"
},
"requests": {
"cpu": "10m",
"memory": "128M"
}
}
| no | +| [pilot\_tolerations](#input\_pilot\_tolerations) | Istio pilot pods tolerations configuration. Default to empty list. For more details # https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#toleration-v1-core | `list(any)` | `[]` | no | + +### Outputs + +| Name | Description | +|------|-------------| +| [istio\_metadata](#output\_istio\_metadata) | Istio definition metadata | + diff --git a/modules/sm-istio/main.tf b/modules/sm-istio/main.tf index f5aca21..ef8f5f5 100644 --- a/modules/sm-istio/main.tf +++ b/modules/sm-istio/main.tf @@ -223,6 +223,7 @@ resource "helm_release" "istio_controlplane" { resource "null_resource" "confirm_istio_operational" { depends_on = [helm_release.istio_controlplane] + count = var.cluster_config_file_path != null ? 1 : 0 provisioner "local-exec" { command = "${path.module}/scripts/confirm-istio-operational.sh \"${var.namespace}\" \"${var.name}\"" interpreter = ["/bin/bash", "-c"] diff --git a/modules/sm-istio/scripts/confirm-istio-operational.sh b/modules/sm-istio/scripts/confirm-istio-operational.sh index bd8430c..c651201 100755 --- a/modules/sm-istio/scripts/confirm-istio-operational.sh +++ b/modules/sm-istio/scripts/confirm-istio-operational.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash # This script is designed to verify that all key components of Istio control plane are up and running. -# This is needs before workload is deployed and injected +# This is needed before workload is deployed and injected set -e @@ -9,7 +9,7 @@ name="${2}" fail=false initialsleep=15 -echo "Checking istiod ${name} successfull deployment in namespace ${namespace}" +echo "Checking istiod ${name} successful deployment in namespace ${namespace}" echo "Initial sleep of ${initialsleep} seconds before starting to check" sleep "${initialsleep}" diff --git a/modules/sm-istio/variables.tf b/modules/sm-istio/variables.tf index 30f262d..f347dfd 100644 --- a/modules/sm-istio/variables.tf +++ b/modules/sm-istio/variables.tf @@ -202,7 +202,7 @@ variable "mesh_config_ingress_selector" { } variable "force_controlplane_update" { - description = "Force controlplane to be updated" + description = "Force controlplane to be recreated when updated. Default to false (may require to taint the resource to apply changes)" default = false type = bool nullable = false diff --git a/tests/pr_test.go b/tests/pr_test.go index 8867ed0..41f1e35 100644 --- a/tests/pr_test.go +++ b/tests/pr_test.go @@ -9,11 +9,11 @@ import ( ) // Use existing resource group -const resourceGroup = "geretain-test-resources" +const resourceGroup = "geretain-test-ocp-service-mesh" // Ensure every example directory has a corresponding test -const advancedExampleDir = "examples/advanced" const basicExampleDir = "examples/basic" +const advancedExampleDir = "examples/securetopology" func setupOptions(t *testing.T, prefix string, dir string) *testhelper.TestOptions { options := testhelper.TestOptionsDefaultWithVars(&testhelper.TestOptions{ @@ -29,7 +29,7 @@ func setupOptions(t *testing.T, prefix string, dir string) *testhelper.TestOptio func TestRunBasicExample(t *testing.T) { t.Parallel() - options := setupOptions(t, "mod-template-basic", basicExampleDir) + options := setupOptions(t, "ocpsm-basic", basicExampleDir) output, err := options.RunTestConsistency() assert.Nil(t, err, "This should not have errored") @@ -39,7 +39,7 @@ func TestRunBasicExample(t *testing.T) { func TestRunAdvancedExample(t *testing.T) { t.Parallel() - options := setupOptions(t, "mod-template-adv", advancedExampleDir) + options := setupOptions(t, "ocpsm-secure", advancedExampleDir) output, err := options.RunTestConsistency() assert.Nil(t, err, "This should not have errored") @@ -50,7 +50,7 @@ func TestRunAdvancedExample(t *testing.T) { func TestRunUpgradeExample(t *testing.T) { t.Parallel() - options := setupOptions(t, "mod-template-adv-upg", advancedExampleDir) + options := setupOptions(t, "ocpsm-basic-upg", advancedExampleDir) output, err := options.RunTestUpgrade() if !options.UpgradeTestSkipped { diff --git a/variables.tf b/variables.tf index dd1cbbf..21d7293 100644 --- a/variables.tf +++ b/variables.tf @@ -7,15 +7,9 @@ variable "cluster_id" { description = "Id of the target IBM Cloud OpenShift Cluster" } -variable "deploy_operator" { - type = bool - description = "Enable installing RedHat Service Mesh Operator" - default = true -} - variable "develop_mode" { type = bool - description = "If true, output more logs, and reduce some wait periods" + description = "If true raise time waited for operator deployment and undeployment to allow to debug the cluster" default = false }