diff --git a/modules/infra/kiali/gateway.yaml b/modules/infra/kiali/gateway.yaml new file mode 100644 index 0000000..b711d1e --- /dev/null +++ b/modules/infra/kiali/gateway.yaml @@ -0,0 +1,16 @@ +apiVersion: networking.istio.io/v1beta1 +kind: VirtualService +metadata: + name: kiali-vs + namespace: istio-system +spec: + hosts: + - kiali.goboolean.io + gateways: + - istio-system/istio-ingressgateway + http: + - route: + - destination: + host: kiali.istio-system.svc.cluster.local + port: + number: 20001 diff --git a/modules/infra/kiali/kiali.yaml b/modules/infra/kiali/kiali.yaml new file mode 100644 index 0000000..add57e8 --- /dev/null +++ b/modules/infra/kiali/kiali.yaml @@ -0,0 +1,534 @@ +apiVersion: kiali.io/v1alpha1 +kind: Kiali +metadata: + name: kiali + namespace: istio-system + annotations: + ansible.sdk.operatorframework.io/verbosity: "1" +spec: + additional_display_details: + - title: "API Documentation" + annotation: "kiali.io/api-spec" + icon_annotation: "kiali.io/api-type" + + installation_tag: "" + + istio_namespace: "" + + version: "default" + + auth: + strategy: "" + openid: + # default: additional_request_params is empty + additional_request_params: + openIdReqParam: "openIdReqParamValue" + # default: allowed_domains is an empty list + allowed_domains: ["allowed.domain"] + api_proxy: "" + api_proxy_ca_data: "" + api_token: "id_token" + authentication_timeout: 300 + authorization_endpoint: "" + client_id: "" + disable_rbac: false + http_proxy: "" + https_proxy: "" + insecure_skip_verify_tls: false + issuer_uri: "" + scopes: ["openid", "profile", "email"] + username_claim: "sub" + openshift: + redirect_uris: [] + token_inactivity_timeout: 0 + token_max_age: 0 + + clustering: + autodetect_secrets: + enabled: true + label: "kiali.io/multiCluster=true" + clusters: [] + kiali_urls: [] + + # default: custom_dashboards is an empty list + custom_dashboards: + - name: "envoy" + + deployment: + # default: additional_service_yaml is empty + additional_service_yaml: + externalName: "kiali.example.com" + affinity: + # default: node is empty + node: + preferredDuringSchedulingIgnoredDuringExecution: # ← Changed to "preferred" + - weight: 100 + preference: + matchExpressions: + - key: topology.kubernetes.io/zone + operator: In + values: + - asia-northeast3-a + # default: pod is empty + pod: + preferredDuringSchedulingIgnoredDuringExecution: # ← Changed to "preferred" + - weight: 100 + podAffinityTerm: + labelSelector: + matchExpressions: + - key: security + operator: In + values: + - S1 + topologyKey: topology.kubernetes.io/zone + # default: pod_anti is empty + pod_anti: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + podAffinityTerm: + labelSelector: + matchExpressions: + - key: security + operator: In + values: + - S2 + topologyKey: topology.kubernetes.io/zone + cluster_wide_access: true + # default: configmap_annotations is empty + configmap_annotations: + strategy.spinnaker.io/versioned: "false" + # default: custom_envs is an empty list + custom_envs: + - name: "HTTP_PROXY" + value: "http://my.proxy.com:1234" + - name: "NO_PROXY" + value: "hostname.example.com" + # default: custom_secrets is an empty list + custom_secrets: [] + #- name: "a-custom-secret" + # mount: "/a-custom-secret-path" + # optional: true + #- name: "a-csi-secret" + # mount: "/a-csi-secret-path" + # csi: + # driver: secrets-store.csi.k8s.io + # readOnly: true + # volumeAttributes: + # secretProviderClass: kiali-secretprovider + # default: discovery_selectors is empty + discovery_selectors: + default: + - matchLabels: + region: north + - matchExpressions: + - key: organization + operator: "In" + values: ["engineering", "accounting"] + - matchLabels: + region: south + matchExpressions: + - key: app + operator: "DoesNotExist" + - key: domain + operator: "NotIn" + values: ["production"] + overrides: + myRemoteCluster: + - matchLabels: + region: world + - matchExpressions: + - key: organization + operator: "NotIn" + values: ["marketing"] + - matchLabels: + region: antarctica + matchExpressions: + - key: app + operator: "DoesNotExist" + - key: domain + operator: "In" + values: ["staging"] + dns: + # default: config is empty + config: + options: + - name: ndots + value: "1" + # default: policy is empty + policy: "ClusterFirst" + hpa: + api_version: "" + # default: spec is empty + spec: + maxReplicas: 2 + minReplicas: 1 + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 50 + # default: host_aliases is an empty list + host_aliases: + - ip: "192.168.1.100" + hostnames: + - "foo.local" + - "bar.local" + image_digest: "" + image_name: "" + image_pull_policy: "IfNotPresent" + # default: image_pull_secrets is an empty list + image_pull_secrets: ["image.pull.secret"] + image_version: "" + ingress: + # default: additional_labels is empty + additional_labels: + ingressAdditionalLabel: "ingressAdditionalLabelValue" + class_name: "nginx" + # default: enabled is undefined + enabled: false + # default: override_yaml is undefined + override_yaml: + metadata: + annotations: + nginx.ingress.kubernetes.io/secure-backends: "true" + nginx.ingress.kubernetes.io/backend-protocol: "HTTPS" + spec: + rules: + - http: + paths: + - path: "/kiali" + pathType: Prefix + backend: + service: + name: "kiali" + port: + number: 20001 + instance_name: "kiali" + logger: + log_level: "info" + log_format: "text" + sampler_rate: "1" + time_field_format: "2006-01-02T15:04:05Z07:00" + namespace: "istio-system" + # default: node_selector is empty + node_selector: + kubernetes.io/os: linux # default: pod_annotations is empty + pod_annotations: + podAnnotation: "podAnnotationValue" + # default: pod_labels is empty + pod_labels: + sidecar.istio.io/inject: "true" + priority_class_name: "" + probes: + liveness: + initial_delay_seconds: 5 + period_seconds: 30 + readiness: + initial_delay_seconds: 5 + period_seconds: 30 + startup: + failure_threshold: 6 + initial_delay_seconds: 30 + period_seconds: 10 + remote_cluster_resources_only: false + replicas: 1 + # default: resources is undefined + resources: + requests: + cpu: "10m" + memory: "64Mi" + limits: + memory: "1Gi" + secret_name: "kiali" + security_context: {} + # default: service_annotations is empty + service_annotations: + svcAnnotation: "svcAnnotationValue" + # default: service_type is undefined + service_type: "ClusterIP" + # default: tolerations is an empty list + tolerations: + - key: "example-key" + operator: "Exists" + effect: "NoSchedule" + topology_spread_constraints: [] + version_label: "" + view_only_mode: false + + # default: extensions is an empty list + extensions: + - enabled: true + name: "skupper" + + external_services: + custom_dashboards: + discovery_auto_threshold: 10 + discovery_enabled: "auto" + enabled: true + is_core: false + namespace_label: "namespace" + prometheus: + auth: + ca_file: "" + insecure_skip_verify: true + password: ${prometheus_password} + token: "" + type: "none" + use_kiali_token: false + username: "${prometheus_username}" + cache_duration: 10 + cache_enabled: true + cache_expiration: 300 + # default: custom_headers is empty + custom_headers: + customHeader1: "customHeader1Value" + health_check_url: "" + is_core: true + # default: query_scope is empty + query_scope: + mesh_id: "mesh-1" + cluster: "cluster-east" + thanos_proxy: + enabled: false + retention_period: "7d" + scrape_interval: "30s" + url: ${prometheus_url} + grafana: + auth: + ca_file: "" + insecure_skip_verify: false + password: ${grafana_password} + token: "" + type: "none" + use_kiali_token: false + username: ${grafana_username} + dashboards: + - name: "Istio Service Dashboard" + variables: + namespace: "var-namespace" + service: "var-service" + - name: "Istio Workload Dashboard" + variables: + namespace: "var-namespace" + workload: "var-workload" + - name: "Istio Mesh Dashboard" + - name: "Istio Control Plane Dashboard" + - name: "Istio Performance Dashboard" + - name: "Istio Wasm Extension Dashboard" + enabled: true + external_url: ${grafana_url} + health_check_url: "" + # default: internal_url is undefined + internal_url: "" + is_core: false + istio: + component_status: + enabled: true + config_map_name: "istio" + egress_gateway_namespace: "" + envoy_admin_local_port: 15000 + gateway_api_classes: [] + ingress_gateway_namespace: "" + istio_api_enabled: true + # default: istio_canary_revision is undefined + istio_canary_revision: + current: "1-9-9" + upgrade: "1-10-2" + istio_identity_domain: "svc.cluster.local" + istio_injection_annotation: "sidecar.istio.io/inject" + istio_sidecar_annotation: "sidecar.istio.io/status" + istio_sidecar_injector_config_map_name: "istio-sidecar-injector" + istiod_deployment_name: "istiod" + istiod_pod_monitoring_port: 15014 + root_namespace: "" + url_service_version: "" + prometheus: + auth: + ca_file: "" + insecure_skip_verify: false + password: ${prometheus_password} + token: "" + type: "none" + use_kiali_token: false + username: ${prometheus_username} + cache_duration: 10 + cache_enabled: true + cache_expiration: 300 + # default: custom_headers is empty + custom_headers: + customHeader1: "customHeader1Value" + health_check_url: "" + is_core: true + # default: query_scope is empty + query_scope: + mesh_id: "mesh-1" + cluster: "cluster-east" + thanos_proxy: + enabled: false + retention_period: "7d" + scrape_interval: "30s" + url: ${prometheus_url} + tracing: + auth: + ca_file: "" + insecure_skip_verify: false + password: "" + token: "" + type: "none" + use_kiali_token: false + username: "" + # default: custom_headers is empty + custom_headers: + customHeader1: "customHeader1Value" + enabled: false + external_url: "" + grpc_port: 9095 + health_check_url: "" + internal_url: "" + is_core: false + namespace_selector: true + provider: "jaeger" + # default: query_scope is empty + query_scope: + mesh_id: "mesh-1" + cluster: "cluster-east" + query_timeout: 5 + tempo_config: + cache_capacity: 200 + cache_enabled: true + datasource_uid: "" + org_id: "" + url_format: "" + use_grpc: true + whitelist_istio_system: ["jaeger-query", "istio-ingressgateway"] + + health_config: + # default: rate is an empty list + rate: + - namespace: ".*" + kind: ".*" + name: ".*" + tolerance: + - protocol: "http" + direction: ".*" + code: "[1234]00" + degraded: 5 + failure: 10 + + identity: + # default: cert_file is undefined + cert_file: "" + # default: private_key_file is undefined + private_key_file: "" + + istio_labels: + app_label_name: "app" + egress_gateway_label: "istio=egressgateway" + ingress_gateway_label: "istio=ingressgateway" + injection_label_name: "istio-injection" + injection_label_rev: "istio.io/rev" + version_label_name: "version" + + kiali_feature_flags: + disabled_features: [] + istio_annotation_action: true + istio_injection_action: true + istio_upgrade_action: false + ui_defaults: + graph: + find_options: + - description: "Find: slow edges (> 1s)" + expression: "rt > 1000" + - description: "Find: unhealthy nodes" + expression: "! healthy" + - description: "Find: unknown nodes" + expression: "name = unknown" + hide_options: + - description: "Hide: healthy nodes" + expression: "healthy" + - description: "Hide: unknown nodes" + expression: "name = unknown" + settings: + animation: "point" + traffic: + ambient: "total" + grpc: "requests" + http: "requests" + tcp: "sent" + i18n: + language: "en" + show_selector: false + list: + include_health: true + include_istio_resources: true + include_validations: true + show_include_toggles: false + metrics_per_refresh: "1m" + # default: metrics_inbound is undefined + metrics_inbound: + aggregations: + - display_name: "Istio Network" + label: "topology_istio_io_network" + - display_name: "Istio Revision" + label: "istio_io_rev" + # default: metrics_outbound is undefined + metrics_outbound: + aggregations: + - display_name: "Istio Network" + label: "topology_istio_io_network" + - display_name: "Istio Revision" + label: "istio_io_rev" + # default: namespaces is an empty list + namespaces: ["istio-system"] + refresh_interval: "1m" + validations: + ignore: ["KIA1301"] + skip_wildcard_gateway_hosts: false + + kubernetes_config: + burst: 200 + cache_duration: 300 + cache_token_namespace_duration: 10 + excluded_workloads: + - "CronJob" + - "DeploymentConfig" + - "Job" + - "ReplicationController" + qps: 175 + + login_token: + expiration_seconds: 86400 + signing_key: "" + + server: + address: "" + audit_log: true + cors_allow_all: false + gzip_enabled: true + # default: node_port is undefined + node_port: 32475 + observability: + metrics: + enabled: true + port: 9090 + tracing: + collector_type: "jaeger" + collector_url: "http://jaeger-collector.istio-system:14268/api/traces" + enabled: false + otel: + ca_name: "" + protocol: "http" + skip_verify: false + tls_enabled: false + port: 20001 + profiler: + enabled: false + require_auth: false + web_fqdn: "" + web_history_mode: "" + web_port: "" + web_root: "" + web_schema: "" + write_timeout: 30 diff --git a/modules/infra/kiali/main.tf b/modules/infra/kiali/main.tf new file mode 100644 index 0000000..faf6700 --- /dev/null +++ b/modules/infra/kiali/main.tf @@ -0,0 +1,25 @@ +resource "helm_release" "kiali_operator" { + name = "kiali-operator" + repository = "https://kiali.org/helm-charts" + chart = "kiali-operator" + namespace = "kiali" +} + +resource "kubernetes_manifest" "kiali_gateway" { + manifest = yamldecode(file("${path.module}/gateway.yaml")) +} + +locals { + kiali_values = templatefile("${path.module}/kiali.yaml", { + prometheus_url = "http://prometheus.monitoring.svc.cluster.local:9090" + prometheus_username = "" + prometheus_password = "" + grafana_url = "http://grafana.monitoring.svc.cluster.local:3000" + grafana_username = var.grafana_username + grafana_password = var.grafana_password + }) +} + +resource "kubernetes_manifest" "kiali" { + manifest = yamldecode(file("${path.module}/kiali.yaml")) +} diff --git a/modules/infra/kiali/values.yaml b/modules/infra/kiali/values.yaml new file mode 100644 index 0000000..936cd58 --- /dev/null +++ b/modules/infra/kiali/values.yaml @@ -0,0 +1,105 @@ +nameOverride: "" +fullnameOverride: "" + +image: # see: https://quay.io/repository/kiali/kiali-operator?tab=tags + repo: ${HELM_IMAGE_REPO} # quay.io/kiali/kiali-operator + tag: ${HELM_IMAGE_TAG} # version string like v1.39.0 or a digest hash + digest: "" # use "sha256" if tag is a sha256 hash (do NOT prefix this value with a "@") + pullPolicy: Always + pullSecrets: [] + +# Deployment options for the operator pod. +nodeSelector: {} +podAnnotations: {} +podLabels: {} +env: [] +tolerations: [] +resources: + requests: + cpu: "10m" + memory: "64Mi" +affinity: {} +replicaCount: 1 +priorityClassName: "" +securityContext: {} + +# metrics.enabled: set to true if you want Prometheus to collect metrics from the operator +metrics: + enabled: true + +# debug.enabled: when true the full ansible logs are dumped after each reconciliation run +# debug.verbosity: defines the amount of details the operator will log (higher numbers are more noisy) +# debug.enableProfiler: when true (regardless of debug.enabled), timings for the most expensive tasks will be logged after each reconciliation loop +debug: + enabled: true + verbosity: "1" + enableProfiler: false + +# Defines where the operator will look for Kial CR resources. "" means "all namespaces". +watchNamespace: "" + +# Set to true if you want the operator to be able to create cluster roles. This is necessary +# if you want to support Kiali CRs with spec.deployment.cluster_wide_access=true. +# Setting this to "true" requires allowAllAccessibleNamespaces to be "true" also. +# Note that this will be overriden to "true" if cr.create is true and cr.spec.deployment.cluster_wide_access=true. +clusterRoleCreator: true + +# Set to true if you want to allow the operator to only be able to install Kiali in view-only-mode. +# The purpose for this setting is to allow you to restrict the permissions given to the operator itself. +onlyViewOnlyMode: false + +# allowAdHocKialiNamespace tells the operator to allow a user to be able to install a Kiali CR in one namespace but +# be able to install Kiali in another namespace. In other words, it will allow the Kiali CR spec.deployment.namespace +# to be something other than the namespace where the CR is installed. You may want to disable this if you are +# running in a multi-tenant scenario in which you only want a user to be able to install Kiali in the same namespace +# where the user has permissions to install a Kiali CR. +allowAdHocKialiNamespace: true + +# allowAdHocKialiImage tells the operator to allow a user to be able to install a custom Kiali image as opposed +# to the image the operator will install by default. In other words, it will allow the +# Kiali CR spec.deployment.image_name and spec.deployment.image_version to be configured by the user. +# You may want to disable this if you do not want users to install their own Kiali images. +allowAdHocKialiImage: false + +# allowAdHocOSSMConsoleImage tells the operator to allow a user to be able to install a custom OSSMC image as opposed +# to the image the operator will install by default. In other words, it will allow the +# OSSMConsole CR spec.deployment.imageName and spec.deployment.imageVersion to be configured by the user. +# You may want to disable this if you do not want users to install their own OSSMC images. +# This is only applicable when running on OpenShift. +allowAdHocOSSMConsoleImage: false + +# allowSecurityContextOverride tells the operator to allow a user to be able to fully override the Kiali +# container securityContext. If this is false, certain securityContext settings must exist on the Kiali +# container and any attempt to override them will be ignored. +allowSecurityContextOverride: false + +# allowAllAccessibleNamespaces tells the operator to allow a user to be able to configure Kiali +# to access all namespaces in the cluster via spec.deployment.cluster_wide_access=true. +# If this is false, the user must specify an explicit set of namespaces in the Kiali CR via spec.deployment.discovery_selectors. +# Setting this to "true" requires clusterRoleCreator to be "true" also. +# Note that this will be overriden to "true" if cr.create is true and cr.spec.deployment.cluster_wide_access=true. +allowAllAccessibleNamespaces: true + +# watchesFile: If specified, this determines what watches file will be used to configure the operator. There are four different +# files that can be selected: (a) `watches-os.yaml`, (b) `watches-os-ns.yaml`, (c) `watches-k8s.yaml` or (d) `watches-k8s-ns.yaml`. +# The first two are for OpenShift only, the last two are for non-OpenShift Kubernetes clusters. The two with "-ns" in their name +# enable the operator to automatically update the Kiali Server with access to new namespaces as those namespaces are created in +# the cluster. This namespace watching feature provides some advanced capabilities but is never required. It is also not +# the default behavior and is not necessary if your Kiali CRs will have `spec.deployment.cluster_wide_access` set to `true`. +watchesFile: "" + +# For what a Kiali CR spec can look like, see: https://kiali.io/docs/configuration/kialis.kiali.io/ +cr: + create: false + name: kiali + # If you elect to create a Kiali CR (--set cr.create=true) + # and the operator is watching all namespaces (--set watchNamespace="") + # then this is the namespace where the CR will be created (the default will be the operator namespace). + namespace: "" + + # Annotations to place in the Kiali CR metadata. + annotations: {} + + spec: + deployment: + cluster_wide_access: true diff --git a/modules/infra/kiali/variables.tf b/modules/infra/kiali/variables.tf new file mode 100644 index 0000000..e21f588 --- /dev/null +++ b/modules/infra/kiali/variables.tf @@ -0,0 +1,7 @@ +variable "grafana_username" { + type = string +} + +variable "grafana_password" { + type = string +} diff --git a/projects/infra/main.tf b/projects/infra/main.tf index 16f1ed3..0052eee 100644 --- a/projects/infra/main.tf +++ b/projects/infra/main.tf @@ -131,12 +131,12 @@ module "atlantis" { password = data.vault_kv_secret_v2.atlantis.data["password"] } -#module "kiali" { -# source = "../../modules/infra/kiali" -# -# grafana_username = data.vault_kv_secret_v2.grafana.data["username"] -# grafana_password = data.vault_kv_secret_v2.grafana.data["password"] -#} +module "kiali" { + source = "../../modules/infra/kiali" + + grafana_username = data.vault_kv_secret_v2.grafana.data["username"] + grafana_password = data.vault_kv_secret_v2.grafana.data["password"] +} module "redis" { source = "../../modules/infra/redis"