Enable Istio as a service mesh for the Gerrit replica chart

The Gerrit replica chart could only use the Kubernetes Ingress as a
method to control incoming external traffic. Istio provides a lot
of additional control for network traffic in the cluster, which
will make it easier to build a scalable Gerrit setup.

This change adds a basic configuration for istio and expands the
chart, so that it can be configured to use the istio ingress gateway
instead of the Kubernetes Ingress.

Change-Id: Ic9d8c459c55fb0a7fd22c0ab26b9f6cec1bf2b24
diff --git a/Documentation/istio.md b/Documentation/istio.md
new file mode 100644
index 0000000..d9c7a31
--- /dev/null
+++ b/Documentation/istio.md
@@ -0,0 +1,57 @@
+# Istio
+
+Istio provides an alternative way to control ingress traffic into the cluster.
+In addition, it allows to finetune the traffic inside the cluster and provides
+a huge repertoire of load balancing and routing mechanisms.
+
+***note
+Currently, only the Gerrit replica chart allows using istio out of the box.
+***
+
+## Dependencies
+
+- istioctl \
+  To install follow these
+  [instructions](https://istio.io/docs/ops/diagnostic-tools/istioctl/#install-hahahugoshortcode-s2-hbhb)
+
+## Install istio
+
+An example configuration based on the default profile provided by istio can be
+found under `./istio/gerrit.profile.yaml`. To install istio with this profile,
+run:
+
+```sh
+istioctl manifest apply -f istio/gerrit.profile.yaml
+```
+
+To install Gerrit using istio for networking, the namespace running Gerrit has to
+be configured to enable sidecar injection, by setting the `istio-injection: enabled`
+label. An example for such a namespace can be found at `./istio/namespace.yaml`.
+
+To be able to use Kiali, credentials have to be provided. A secret for doing so,
+can be found at `./istio/kiali.secret.yaml`. Adapt the credentials and apply them:
+
+```sh
+kubectl apply -f ./istio/kiali.secret.yaml
+```
+
+## Uninstall istio
+
+To uninstall istio, run:
+
+```sh
+istioctl manifest generate -f istio/gerrit.profile.yaml > istio/gerrit.manifest.yaml
+kubectl delete -f istio/gerrit.manifest.yaml
+```
+
+## Restricting access to a list of allowed IPs
+
+In development setups, it might be wanted to allow access to the setup only from
+specified IPs. This can usually be done using an AuthorizationPolicy. On AWS this
+does not work, since the load balancer hides the original IP. However, the
+istio-ingressgateway can be patched to enable access only from a given range of
+IPs. To do this, use the following command:
+
+```sh
+kubectl patch service istio-ingressgateway -n istio-system -p '{"spec":{"loadBalancerSourceRanges":["1.2.3.4"]}}'
+```
diff --git a/helm-charts/gerrit-replica/README.md b/helm-charts/gerrit-replica/README.md
index df0ca91..dbc13b2 100644
--- a/helm-charts/gerrit-replica/README.md
+++ b/helm-charts/gerrit-replica/README.md
@@ -131,6 +131,27 @@
 |-----------------------------|-------------------------------------------------|---------|
 | `gitRepositoryStorage.size` | Size of the volume storing the Git repositories | `5Gi`   |
 
+
+### Istio
+
+Istio can be used as an alternative to Kubernetes Ingresses to manage the traffic
+into the cluster and also inside the cluster. This requires istio to be installed
+beforehand. Some guidance on how to set up istio can be found [here](/Documentation/istio.md).
+The helm chart expects `istio-injection` to be enabled in the namespace, in which
+it will be installed.
+
+In the case istio is used, all configuration for ingresses in the chart will be
+ignored.
+
+| Parameter           | Description                                                               | Default                           |
+|---------------------|---------------------------------------------------------------------------|-----------------------------------|
+| `istio.enabled`     | Whether istio should be used (requires istio to be installed)             | `false`                           |
+| `istio.host`        | Hostname (CNAME must point to istio ingress gateway loadbalancer service) | `nil`                             |
+| `istio.tls.enabled` | Whether to enable TLS                                                     | `false`                           |
+| `istio.tls.cert`    | TLS certificate                                                           | `-----BEGIN CERTIFICATE-----`     |
+| `istio.tls.key`     | TLS key                                                                   | `-----BEGIN RSA PRIVATE KEY-----` |
+
+
 ### Apache-Git-HTTP-Backend (Git-Backend)
 
 | Parameter                                  | Description                                                                        | Default                                                                   |
diff --git a/helm-charts/gerrit-replica/templates/NOTES.txt b/helm-charts/gerrit-replica/templates/NOTES.txt
index 8872a88..288a971 100644
--- a/helm-charts/gerrit-replica/templates/NOTES.txt
+++ b/helm-charts/gerrit-replica/templates/NOTES.txt
@@ -5,7 +5,9 @@
 primary Gerrit. Please configure the replication plugin of the primary Gerrit to
 push the repositories to:
 
-{{ if .Values.gitBackend.ingress.enabled -}}
+{{ if .Values.istio.enabled -}}
+  http {{- if .Values.istio.tls.enabled -}} s {{- end -}} :// {{- .Values.istio.host -}} /receiver/git/${name}.git
+{{ else if .Values.gitBackend.ingress.enabled -}}
   http {{- if .Values.gitBackend.ingress.tls.enabled -}} s {{- end -}} :// {{- .Values.gitBackend.ingress.host -}} /git/${name}.git
 {{- else }}
   {{ if .Values.gitBackend.service.https.enabled -}}
@@ -17,7 +19,8 @@
   kubectl get svc git-backend-service
 {{- end }}
 
-Requests to create new repositories have to be directed to the route /new/${name}.git
+Requests to create new repositories have to be directed to the route
+{{ if .Values.istio.enabled -}} /receiver {{- end -}} /new/${name}.git
 over HTTP(S). A detailed guide of how to configure Gerrit's replication plugin
 can be found here:
 
@@ -28,4 +31,8 @@
 The initialization process may take some time. Afterwards the git repositories
 will be available under:
 
-http {{- if .Values.gerritReplica.ingress.tls.enabled -}} s {{- end -}} :// {{- .Values.gerritReplica.ingress.host -}} /<repository-name>.git
+{{ if .Values.istio.enabled -}}
+  http {{- if .Values.istio.tls.enabled -}} s {{- end -}} :// {{- .Values.istio.host -}} /gerrit/<repository-name>.git
+{{- else }}
+  http {{- if .Values.gerritReplica.ingress.tls.enabled -}} s {{- end -}} :// {{- .Values.gerritReplica.ingress.host -}} /<repository-name>.git
+{{- end }}
diff --git a/helm-charts/gerrit-replica/templates/gerrit-replica.deployment.yaml b/helm-charts/gerrit-replica/templates/gerrit-replica.deployment.yaml
index 8e8f1a8..2998a64 100644
--- a/helm-charts/gerrit-replica/templates/gerrit-replica.deployment.yaml
+++ b/helm-charts/gerrit-replica/templates/gerrit-replica.deployment.yaml
@@ -20,6 +20,9 @@
     metadata:
       labels:
         app: gerrit-replica
+        chart: {{ template "gerrit-replica.chart" . }}
+        heritage: {{ .Release.Service }}
+        release: {{ .Release.Name }}
       annotations:
         chartRevision: "{{ .Release.Revision }}"
     spec:
diff --git a/helm-charts/gerrit-replica/templates/gerrit-replica.ingress.yaml b/helm-charts/gerrit-replica/templates/gerrit-replica.ingress.yaml
index 61331dc..161a0df 100644
--- a/helm-charts/gerrit-replica/templates/gerrit-replica.ingress.yaml
+++ b/helm-charts/gerrit-replica/templates/gerrit-replica.ingress.yaml
@@ -1,3 +1,4 @@
+{{ if and .Values.gerritReplica.ingress.enabled (not .Values.istio.enabled) -}}
 apiVersion: extensions/v1beta1
 kind: Ingress
 metadata:
@@ -29,3 +30,4 @@
       - backend:
           serviceName: {{ .Release.Name }}-gerrit-replica-service
           servicePort: {{ .Values.gerritReplica.service.http.port }}
+{{- end }}
diff --git a/helm-charts/gerrit-replica/templates/gerrit-replica.secrets.yaml b/helm-charts/gerrit-replica/templates/gerrit-replica.secrets.yaml
index 2ac0586..6569f28 100644
--- a/helm-charts/gerrit-replica/templates/gerrit-replica.secrets.yaml
+++ b/helm-charts/gerrit-replica/templates/gerrit-replica.secrets.yaml
@@ -14,7 +14,7 @@
   secure.config: {{ .Values.gerritReplica.config.secure | b64enc }}
 type: Opaque
 ---
-{{ if and .Values.gerritReplica.ingress.tls.enabled .Values.gerritReplica.ingress.tls.secret.create -}}
+{{ if and (and .Values.gerritReplica.ingress.tls.enabled .Values.gerritReplica.ingress.tls.secret.create) (not .Values.istio.enabled) -}}
 apiVersion: v1
 kind: Secret
 metadata:
diff --git a/helm-charts/gerrit-replica/templates/git-backend.deployment.yaml b/helm-charts/gerrit-replica/templates/git-backend.deployment.yaml
index 9d3e323..fb4d010 100644
--- a/helm-charts/gerrit-replica/templates/git-backend.deployment.yaml
+++ b/helm-charts/gerrit-replica/templates/git-backend.deployment.yaml
@@ -20,6 +20,9 @@
     metadata:
       labels:
         app: git-backend
+        chart: {{ template "gerrit-replica.chart" . }}
+        heritage: {{ .Release.Service }}
+        release: {{ .Release.Name }}
       annotations:
         chartRevision: "{{ .Release.Revision }}"
     spec:
diff --git a/helm-charts/gerrit-replica/templates/git-backend.ingress.yaml b/helm-charts/gerrit-replica/templates/git-backend.ingress.yaml
index 6c98102..87d64bd 100644
--- a/helm-charts/gerrit-replica/templates/git-backend.ingress.yaml
+++ b/helm-charts/gerrit-replica/templates/git-backend.ingress.yaml
@@ -1,4 +1,4 @@
-{{ if .Values.gitBackend.ingress.enabled -}}
+{{ if and .Values.gitBackend.ingress.enabled (not .Values.istio.enabled) -}}
 apiVersion: extensions/v1beta1
 kind: Ingress
 metadata:
diff --git a/helm-charts/gerrit-replica/templates/git-backend.secrets.yaml b/helm-charts/gerrit-replica/templates/git-backend.secrets.yaml
index cac30f7..370a902 100644
--- a/helm-charts/gerrit-replica/templates/git-backend.secrets.yaml
+++ b/helm-charts/gerrit-replica/templates/git-backend.secrets.yaml
@@ -11,7 +11,7 @@
   .htpasswd: {{ required "A .htpasswd-file is required for the git backend." .Values.gitBackend.credentials.htpasswd | b64enc }}
 type: Opaque
 ---
-{{ if and .Values.gitBackend.tls.secret.create (or .Values.gitBackend.ingress.tls.enabled .Values.gitBackend.service.https.enabled) -}}
+{{ if and (and .Values.gitBackend.tls.secret.create (or .Values.gitBackend.ingress.tls.enabled .Values.gitBackend.service.https.enabled)) (not .Values.istio.enabled) -}}
 apiVersion: v1
 kind: Secret
 metadata:
diff --git a/helm-charts/gerrit-replica/templates/git-gc.cronjob.yaml b/helm-charts/gerrit-replica/templates/git-gc.cronjob.yaml
index 81f6f1f..f14add6 100644
--- a/helm-charts/gerrit-replica/templates/git-gc.cronjob.yaml
+++ b/helm-charts/gerrit-replica/templates/git-gc.cronjob.yaml
@@ -12,6 +12,11 @@
   jobTemplate:
     spec:
       template:
+        {{ if .Values.istio.enabled -}}
+        metadata:
+          annotations:
+            sidecar.istio.io/inject: "false"
+        {{- end }}
         spec:
           restartPolicy: OnFailure
           securityContext:
diff --git a/helm-charts/gerrit-replica/templates/istio.ingressgateway.yaml b/helm-charts/gerrit-replica/templates/istio.ingressgateway.yaml
new file mode 100644
index 0000000..33e1698
--- /dev/null
+++ b/helm-charts/gerrit-replica/templates/istio.ingressgateway.yaml
@@ -0,0 +1,91 @@
+{{ if .Values.istio.enabled -}}
+{{ if .Values.istio.tls.enabled }}
+apiVersion: v1
+kind: Secret
+metadata:
+  name:  {{ .Release.Name }}-istio-tls-secret
+  namespace: istio-system
+  labels:
+    app: gerrit-replica
+    chart: {{ template "gerrit-replica.chart" . }}
+    heritage: {{ .Release.Service }}
+    release: {{ .Release.Name }}
+type: kubernetes.io/tls
+data:
+  {{ with .Values.istio.tls -}}
+  tls.crt: {{ .cert | b64enc }}
+  tls.key: {{ .key | b64enc }}
+  {{- end }}
+{{- end }}
+---
+apiVersion: networking.istio.io/v1alpha3
+kind: Gateway
+metadata:
+  name: {{ .Release.Name }}-istio-gateway
+  labels:
+    app: gerrit-replica
+    chart: {{ template "gerrit-replica.chart" . }}
+    heritage: {{ .Release.Service }}
+    release: {{ .Release.Name }}
+spec:
+  selector:
+    istio: ingressgateway
+  servers:
+  - port:
+      number: 80
+      name: http
+      protocol: HTTP
+    hosts:
+    - {{ .Values.istio.host }}
+  {{ if .Values.istio.tls.enabled }}
+    tls:
+      httpsRedirect: true
+  - port:
+      number: 443
+      name: https
+      protocol: HTTPS
+    hosts:
+    - {{ .Values.istio.host }}
+    tls:
+      mode: SIMPLE
+      credentialName: {{ .Release.Name }}-istio-tls-secret
+  {{- end }}
+---
+apiVersion: networking.istio.io/v1alpha3
+kind: VirtualService
+metadata:
+  name: {{ .Release.Name }}-istio-virtual-service
+  labels:
+    app: gerrit-replica
+    chart: {{ template "gerrit-replica.chart" . }}
+    heritage: {{ .Release.Service }}
+    release: {{ .Release.Name }}
+spec:
+  hosts:
+  - {{ .Values.istio.host }}
+  gateways:
+  - {{ .Release.Name }}-istio-gateway
+  http:
+  - match:
+    - uri:
+        prefix: /receiver/
+    rewrite:
+      uri: /
+    route:
+    - destination:
+        host: {{ .Release.Name }}-git-backend-service.{{ .Release.Namespace }}.svc.cluster.local
+        port:
+          number: 80
+  - match:
+    - uri:
+        prefix: /gerrit/
+    - uri:
+        prefix: /gerrit
+    rewrite:
+      uri: /
+    route:
+    - destination:
+        host: {{ .Release.Name }}-gerrit-replica-service.{{ .Release.Namespace }}.svc.cluster.local
+        port:
+          number: 80
+{{- end }}
diff --git a/helm-charts/gerrit-replica/values.yaml b/helm-charts/gerrit-replica/values.yaml
index 5564ceb..4f7ce7f 100644
--- a/helm-charts/gerrit-replica/values.yaml
+++ b/helm-charts/gerrit-replica/values.yaml
@@ -46,6 +46,21 @@
   size: 5Gi
 
 
+istio:
+  enabled: false
+  host:
+  tls:
+    enabled: false
+    cert: |-
+      -----BEGIN CERTIFICATE-----
+
+      -----END CERTIFICATE-----
+    key: |-
+      -----BEGIN RSA PRIVATE KEY-----
+
+      -----END RSA PRIVATE KEY-----
+
+
 gitBackend:
   image: k8sgerrit/apache-git-http-backend
 
diff --git a/istio/gerrit.profile.yaml b/istio/gerrit.profile.yaml
new file mode 100644
index 0000000..4574621
--- /dev/null
+++ b/istio/gerrit.profile.yaml
@@ -0,0 +1,577 @@
+apiVersion: install.istio.io/v1alpha1
+kind: IstioOperator
+spec:
+  addonComponents:
+    grafana:
+      enabled: false
+      k8s:
+        replicaCount: 1
+    istiocoredns:
+      enabled: false
+    # Dashboard to monitor service mesh. Requires at least prometheus to be
+    # enabled (see below) and a secret defining the user credentials.
+    kiali:
+      enabled: true
+      k8s:
+        replicaCount: 1
+    # Required for the kiali dashboard.
+    # TODO: Try to use Prometheus from gerrit-monitoring setup.
+    prometheus:
+      enabled: true
+      k8s:
+        replicaCount: 1
+    tracing:
+      enabled: false
+  components:
+    base:
+      enabled: true
+    citadel:
+      enabled: false
+      k8s:
+        strategy:
+          rollingUpdate:
+            maxSurge: 100%
+            maxUnavailable: 25%
+    cni:
+      enabled: false
+    egressGateways:
+    - enabled: false
+      k8s:
+        env:
+        - name: ISTIO_META_ROUTER_MODE
+          value: sni-dnat
+        hpaSpec:
+          maxReplicas: 5
+          metrics:
+          - resource:
+              name: cpu
+              targetAverageUtilization: 80
+            type: Resource
+          minReplicas: 1
+          scaleTargetRef:
+            apiVersion: apps/v1
+            kind: Deployment
+            name: istio-egressgateway
+        resources:
+          limits:
+            cpu: 2000m
+            memory: 1024Mi
+          requests:
+            cpu: 100m
+            memory: 128Mi
+        service:
+          ports:
+          - name: http2
+            port: 80
+          - name: https
+            port: 443
+          - name: tls
+            port: 15443
+            targetPort: 15443
+        strategy:
+          rollingUpdate:
+            maxSurge: 100%
+            maxUnavailable: 25%
+      name: istio-egressgateway
+    ingressGateways:
+    - enabled: true
+      k8s:
+        env:
+        - name: ISTIO_META_ROUTER_MODE
+          value: sni-dnat
+        hpaSpec:
+          maxReplicas: 5
+          metrics:
+          - resource:
+              name: cpu
+              targetAverageUtilization: 80
+            type: Resource
+          minReplicas: 1
+          scaleTargetRef:
+            apiVersion: apps/v1
+            kind: Deployment
+            name: istio-ingressgateway
+        resources:
+          limits:
+            cpu: 2000m
+            memory: 1024Mi
+          requests:
+            cpu: 100m
+            memory: 128Mi
+        service:
+          ports:
+          - name: status-port
+            port: 15021
+            targetPort: 15021
+          - name: http2
+            port: 80
+            targetPort: 8080
+          - name: https
+            port: 443
+            targetPort: 8443
+          # - name: tls
+          #   port: 15443
+          #   targetPort: 15443
+          - name: ssh
+            port: 29418
+        strategy:
+          rollingUpdate:
+            maxSurge: 100%
+            maxUnavailable: 25%
+      name: istio-ingressgateway
+    istiodRemote:
+      enabled: false
+    pilot:
+      enabled: true
+      k8s:
+        env:
+        - name: POD_NAME
+          valueFrom:
+            fieldRef:
+              apiVersion: v1
+              fieldPath: metadata.name
+        - name: POD_NAMESPACE
+          valueFrom:
+            fieldRef:
+              apiVersion: v1
+              fieldPath: metadata.namespace
+        readinessProbe:
+          httpGet:
+            path: /ready
+            port: 8080
+          initialDelaySeconds: 1
+          periodSeconds: 3
+          timeoutSeconds: 5
+        strategy:
+          rollingUpdate:
+            maxSurge: 100%
+            maxUnavailable: 25%
+    policy:
+      enabled: false
+      k8s:
+        env:
+        - name: POD_NAMESPACE
+          valueFrom:
+            fieldRef:
+              apiVersion: v1
+              fieldPath: metadata.namespace
+        hpaSpec:
+          maxReplicas: 5
+          metrics:
+          - resource:
+              name: cpu
+              targetAverageUtilization: 80
+            type: Resource
+          minReplicas: 1
+          scaleTargetRef:
+            apiVersion: apps/v1
+            kind: Deployment
+            name: istio-policy
+        strategy:
+          rollingUpdate:
+            maxSurge: 100%
+            maxUnavailable: 25%
+    telemetry:
+      enabled: false
+      k8s:
+        env:
+        - name: POD_NAMESPACE
+          valueFrom:
+            fieldRef:
+              apiVersion: v1
+              fieldPath: metadata.namespace
+        - name: GOMAXPROCS
+          value: "6"
+        hpaSpec:
+          maxReplicas: 5
+          metrics:
+          - resource:
+              name: cpu
+              targetAverageUtilization: 80
+            type: Resource
+          minReplicas: 1
+          scaleTargetRef:
+            apiVersion: apps/v1
+            kind: Deployment
+            name: istio-telemetry
+        replicaCount: 1
+        resources:
+          limits:
+            cpu: 4800m
+            memory: 4G
+          requests:
+            cpu: 1000m
+            memory: 1G
+        strategy:
+          rollingUpdate:
+            maxSurge: 100%
+            maxUnavailable: 25%
+  hub: docker.io/istio
+  profile: default
+  meshConfig:
+    defaultConfig:
+      proxyMetadata: {}
+    enablePrometheusMerge: false
+    # Log access logs to stdout in istio-proxy sidecars.
+    # TODO: This should probably be changed in the future, as soon as a more
+    # production ready logging solution is used.
+    accessLogFile: "/dev/stdout"
+  tag: 1.6.3
+  values:
+    base:
+      validationURL: ""
+    clusterResources: true
+    gateways:
+      istio-egressgateway:
+        autoscaleEnabled: true
+        env: {}
+        name: istio-egressgateway
+        secretVolumes:
+        - mountPath: /etc/istio/egressgateway-certs
+          name: egressgateway-certs
+          secretName: istio-egressgateway-certs
+        - mountPath: /etc/istio/egressgateway-ca-certs
+          name: egressgateway-ca-certs
+          secretName: istio-egressgateway-ca-certs
+        type: ClusterIP
+        zvpn: {}
+      istio-ingressgateway:
+        applicationPorts: ""
+        autoscaleEnabled: true
+        debug: info
+        domain: ""
+        env: {}
+        meshExpansionPorts:
+        - name: tcp-pilot-grpc-tls
+          port: 15011
+          targetPort: 15011
+        - name: tcp-istiod
+          port: 15012
+          targetPort: 15012
+        - name: tcp-citadel-grpc-tls
+          port: 8060
+          targetPort: 8060
+        - name: tcp-dns-tls
+          port: 853
+          targetPort: 8853
+        name: istio-ingressgateway
+        secretVolumes:
+        - mountPath: /etc/istio/ingressgateway-certs
+          name: ingressgateway-certs
+          secretName: istio-ingressgateway-certs
+        - mountPath: /etc/istio/ingressgateway-ca-certs
+          name: ingressgateway-ca-certs
+          secretName: istio-ingressgateway-ca-certs
+        type: LoadBalancer
+        zvpn: {}
+    global:
+      arch:
+        amd64: 2
+        ppc64le: 2
+        s390x: 2
+      configValidation: true
+      controlPlaneSecurityEnabled: true
+      defaultNodeSelector: {}
+      defaultPodDisruptionBudget:
+        enabled: true
+      defaultResources:
+        requests:
+          cpu: 10m
+      enableHelmTest: false
+      imagePullPolicy: ""
+      imagePullSecrets: []
+      istioNamespace: istio-system
+      istiod:
+        enableAnalysis: false
+        enabled: true
+      # Gardener does not support the third-party-jwt policy.
+      jwtPolicy: first-party-jwt
+      logAsJson: false
+      logging:
+        level: default:info
+      meshExpansion:
+        enabled: false
+        useILB: false
+      meshNetworks: {}
+      mountMtlsCerts: false
+      multiCluster:
+        clusterName: ""
+        enabled: false
+      network: ""
+      omitSidecarInjectorConfigMap: false
+      oneNamespace: false
+      operatorManageWebhooks: false
+      pilotCertProvider: istiod
+      priorityClassName: ""
+      proxy:
+        autoInject: enabled
+        clusterDomain: cluster.local
+        componentLogLevel: misc:error
+        enableCoreDump: false
+        envoyStatsd:
+          enabled: false
+        excludeIPRanges: ""
+        excludeInboundPorts: ""
+        excludeOutboundPorts: ""
+        image: proxyv2
+        includeIPRanges: '*'
+        # Use this value, if more detailed logging output is needed, e.g. for
+        # debugging.
+        logLevel: warning
+        privileged: false
+        readinessFailureThreshold: 30
+        readinessInitialDelaySeconds: 1
+        readinessPeriodSeconds: 2
+        resources:
+          limits:
+            cpu: 2000m
+            memory: 1024Mi
+          requests:
+            cpu: 100m
+            memory: 128Mi
+        statusPort: 15020
+        tracer: zipkin
+      proxy_init:
+        image: proxyv2
+        resources:
+          limits:
+            cpu: 100m
+            memory: 50Mi
+          requests:
+            cpu: 10m
+            memory: 10Mi
+      sds:
+        token:
+          aud: istio-ca
+      sts:
+        servicePort: 0
+      tracer:
+        datadog:
+          address: $(HOST_IP):8126
+        lightstep:
+          accessToken: ""
+          address: ""
+        stackdriver:
+          debug: false
+          maxNumberOfAnnotations: 200
+          maxNumberOfAttributes: 200
+          maxNumberOfMessageEvents: 200
+        zipkin:
+          address: ""
+      trustDomain: cluster.local
+      useMCP: false
+    grafana:
+      accessMode: ReadWriteMany
+      contextPath: /grafana
+      dashboardProviders:
+        dashboardproviders.yaml:
+          apiVersion: 1
+          providers:
+          - disableDeletion: false
+            folder: istio
+            name: istio
+            options:
+              path: /var/lib/grafana/dashboards/istio
+            orgId: 1
+            type: file
+      datasources:
+        datasources.yaml:
+          apiVersion: 1
+      env: {}
+      envSecrets: {}
+      image:
+        repository: grafana/grafana
+        tag: 6.7.4
+      nodeSelector: {}
+      persist: false
+      podAntiAffinityLabelSelector: []
+      podAntiAffinityTermLabelSelector: []
+      security:
+        enabled: false
+        passphraseKey: passphrase
+        secretName: grafana
+        usernameKey: username
+      service:
+        annotations: {}
+        externalPort: 3000
+        name: http
+        type: ClusterIP
+      storageClassName: ""
+      tolerations: []
+    istiocoredns:
+      coreDNSImage: coredns/coredns
+      coreDNSPluginImage: istio/coredns-plugin:0.2-istio-1.1
+      coreDNSTag: 1.6.2
+    istiodRemote:
+      injectionURL: ""
+    kiali:
+      contextPath: /kiali
+      createDemoSecret: false
+      dashboard:
+        auth:
+          strategy: login
+        grafanaInClusterURL: http://grafana:3000
+        jaegerInClusterURL: http://tracing/jaeger
+        passphraseKey: passphrase
+        secretName: kiali
+        usernameKey: username
+        viewOnlyMode: false
+      hub: quay.io/kiali
+      nodeSelector: {}
+      podAntiAffinityLabelSelector: []
+      podAntiAffinityTermLabelSelector: []
+      security:
+        cert_file: /kiali-cert/cert-chain.pem
+        enabled: false
+        private_key_file: /kiali-cert/key.pem
+      service:
+        annotations: {}
+      tag: v1.18
+    mixer:
+      adapters:
+        kubernetesenv:
+          enabled: true
+        prometheus:
+          enabled: true
+          metricsExpiryDuration: 10m
+        stackdriver:
+          auth:
+            apiKey: ""
+            appCredentials: false
+            serviceAccountPath: ""
+          enabled: false
+          tracer:
+            enabled: false
+            sampleProbability: 1
+        stdio:
+          enabled: false
+          outputAsJson: false
+        useAdapterCRDs: false
+      policy:
+        adapters:
+          kubernetesenv:
+            enabled: true
+          useAdapterCRDs: false
+        autoscaleEnabled: true
+        image: mixer
+        sessionAffinityEnabled: false
+      telemetry:
+        autoscaleEnabled: true
+        env:
+          GOMAXPROCS: "6"
+        image: mixer
+        loadshedding:
+          latencyThreshold: 100ms
+          mode: enforce
+        nodeSelector: {}
+        podAntiAffinityLabelSelector: []
+        podAntiAffinityTermLabelSelector: []
+        replicaCount: 1
+        sessionAffinityEnabled: false
+        tolerations: []
+    pilot:
+      appNamespaces: []
+      autoscaleEnabled: true
+      autoscaleMax: 5
+      autoscaleMin: 1
+      configMap: true
+      configNamespace: istio-config
+      cpu:
+        targetAverageUtilization: 80
+      enableProtocolSniffingForInbound: true
+      enableProtocolSniffingForOutbound: true
+      env: {}
+      image: pilot
+      keepaliveMaxServerConnectionAge: 30m
+      nodeSelector: {}
+      podAntiAffinityLabelSelector: []
+      podAntiAffinityTermLabelSelector: []
+      policy:
+        enabled: false
+      replicaCount: 1
+      tolerations: []
+      traceSampling: 1
+    prometheus:
+      contextPath: /prometheus
+      hub: docker.io/prom
+      nodeSelector: {}
+      podAntiAffinityLabelSelector: []
+      podAntiAffinityTermLabelSelector: []
+      provisionPrometheusCert: true
+      retention: 6h
+      scrapeInterval: 15s
+      security:
+        enabled: true
+      tag: v2.15.1
+      tolerations: []
+    sidecarInjectorWebhook:
+      enableNamespacesByDefault: false
+      injectLabel: istio-injection
+      objectSelector:
+        autoInject: true
+        enabled: false
+      rewriteAppHTTPProbe: false
+    telemetry:
+      enabled: true
+      v1:
+        enabled: false
+      v2:
+        enabled: true
+        metadataExchange: {}
+        prometheus:
+          enabled: true
+        stackdriver:
+          configOverride: {}
+          enabled: false
+          logging: false
+          monitoring: false
+          topology: false
+    tracing:
+      jaeger:
+        accessMode: ReadWriteMany
+        hub: docker.io/jaegertracing
+        memory:
+          max_traces: 50000
+        persist: false
+        spanStorageType: badger
+        storageClassName: ""
+        tag: "1.16"
+      nodeSelector: {}
+      opencensus:
+        exporters:
+          stackdriver:
+            enable_tracing: true
+        hub: docker.io/omnition
+        resources:
+          limits:
+            cpu: "1"
+            memory: 2Gi
+          requests:
+            cpu: 200m
+            memory: 400Mi
+        tag: 0.1.9
+      podAntiAffinityLabelSelector: []
+      podAntiAffinityTermLabelSelector: []
+      provider: jaeger
+      service:
+        annotations: {}
+        externalPort: 9411
+        name: http-query
+        type: ClusterIP
+      zipkin:
+        hub: docker.io/openzipkin
+        javaOptsHeap: 700
+        maxSpans: 500000
+        node:
+          cpus: 2
+        probeStartupDelay: 10
+        queryPort: 9411
+        resources:
+          limits:
+            cpu: 1000m
+            memory: 2048Mi
+          requests:
+            cpu: 150m
+            memory: 900Mi
+        tag: 2.20.0
+    version: ""
diff --git a/istio/kiali.secret.yaml b/istio/kiali.secret.yaml
new file mode 100644
index 0000000..68a2f89
--- /dev/null
+++ b/istio/kiali.secret.yaml
@@ -0,0 +1,11 @@
+apiVersion: v1
+kind: Secret
+metadata:
+  name: kiali
+  namespace: istio-system
+  labels:
+    app: kiali
+type: Opaque
+data:
+  username: YWRtaW4=
+  passphrase: c2VjcmV0
diff --git a/istio/namespace.yaml b/istio/namespace.yaml
new file mode 100644
index 0000000..6e9fb38
--- /dev/null
+++ b/istio/namespace.yaml
@@ -0,0 +1,6 @@
+apiVersion: v1
+kind: Namespace
+metadata:
+  name: gerrit-replica
+  labels:
+    istio-injection: enabled