diff --git a/artifacts/routing/destination-rule.yaml b/artifacts/routing/destination-rule.yaml
new file mode 100644
index 00000000..2bdea8f2
--- /dev/null
+++ b/artifacts/routing/destination-rule.yaml
@@ -0,0 +1,45 @@
+apiVersion: networking.istio.io/v1alpha3
+kind: VirtualService
+metadata:
+ name: podinfo
+ namespace: test
+spec:
+ gateways:
+ - public-gateway.istio-system.svc.cluster.local
+ - mesh
+ hosts:
+ - podinfo.istio.weavedx.com
+ - podinfo
+ http:
+ - route:
+ - destination:
+ host: podinfo
+ subset: primary
+ weight: 50
+ - destination:
+ host: podinfo
+ subset: canary
+ weight: 50
+---
+apiVersion: networking.istio.io/v1alpha3
+kind: DestinationRule
+metadata:
+ name: podinfo-destination
+ namespace: test
+spec:
+ host: podinfo
+ trafficPolicy:
+ loadBalancer:
+ consistentHash:
+ httpCookie:
+ name: istiouser
+ ttl: 30s
+ subsets:
+ - name: primary
+ labels:
+ app: podinfo
+ role: primary
+ - name: canary
+ labels:
+ app: podinfo
+ role: canary
diff --git a/charts/grafana/Chart.yaml b/charts/grafana/Chart.yaml
index 3b952c18..2e317aec 100644
--- a/charts/grafana/Chart.yaml
+++ b/charts/grafana/Chart.yaml
@@ -1,7 +1,7 @@
apiVersion: v1
name: grafana
-version: 0.1.0
-appVersion: 5.4.2
+version: 1.0.0
+appVersion: 5.4.3
description: Grafana dashboards for monitoring Flagger canary deployments
icon: https://raw.githubusercontent.com/stefanprodan/flagger/master/docs/logo/flagger-icon.png
home: https://flagger.app
diff --git a/charts/grafana/dashboards/canary.json b/charts/grafana/dashboards/canary.json
index d3647bcd..5a1d969e 100644
--- a/charts/grafana/dashboards/canary.json
+++ b/charts/grafana/dashboards/canary.json
@@ -2,7 +2,6 @@
"annotations": {
"list": [
{
- "$$hashKey": "object:1587",
"builtIn": 1,
"datasource": "-- Grafana --",
"enable": true,
@@ -16,8 +15,8 @@
"editable": true,
"gnetId": null,
"graphTooltip": 0,
- "id": null,
- "iteration": 1534587617141,
+ "id": 1,
+ "iteration": 1549736611069,
"links": [],
"panels": [
{
@@ -179,7 +178,6 @@
"tableColumn": "",
"targets": [
{
- "$$hashKey": "object:2857",
"expr": "sum(irate(istio_requests_total{reporter=\"destination\",destination_workload_namespace=~\"$namespace\",destination_workload=~\"$primary\",response_code!~\"5.*\"}[30s])) / sum(irate(istio_requests_total{reporter=\"destination\",destination_workload_namespace=~\"$namespace\",destination_workload=~\"$primary\"}[30s]))",
"format": "time_series",
"intervalFactor": 1,
@@ -344,7 +342,6 @@
"tableColumn": "",
"targets": [
{
- "$$hashKey": "object:2810",
"expr": "sum(irate(istio_requests_total{reporter=\"destination\",destination_workload_namespace=~\"$namespace\",destination_workload=~\"$canary\",response_code!~\"5.*\"}[30s])) / sum(irate(istio_requests_total{reporter=\"destination\",destination_workload_namespace=~\"$namespace\",destination_workload=~\"$canary\"}[30s]))",
"format": "time_series",
"intervalFactor": 1,
@@ -363,7 +360,7 @@
"value": "null"
}
],
- "valueName": "avg"
+ "valueName": "current"
},
{
"aliasColors": {},
@@ -432,6 +429,7 @@
],
"thresholds": [],
"timeFrom": null,
+ "timeRegions": [],
"timeShift": null,
"title": "Primary: Request Duration",
"tooltip": {
@@ -464,7 +462,11 @@
"min": null,
"show": false
}
- ]
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
},
{
"aliasColors": {},
@@ -533,6 +535,7 @@
],
"thresholds": [],
"timeFrom": null,
+ "timeRegions": [],
"timeShift": null,
"title": "Canary: Request Duration",
"tooltip": {
@@ -565,7 +568,11 @@
"min": null,
"show": false
}
- ]
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
},
{
"content": "
",
@@ -623,7 +630,6 @@
"steppedLine": false,
"targets": [
{
- "$$hashKey": "object:1685",
"expr": "sum(rate(container_cpu_usage_seconds_total{cpu=\"total\",namespace=\"$namespace\",pod_name=~\"$primary.*\", container_name!~\"POD|istio-proxy\"}[1m])) by (pod_name)",
"format": "time_series",
"hide": false,
@@ -634,6 +640,7 @@
],
"thresholds": [],
"timeFrom": null,
+ "timeRegions": [],
"timeShift": null,
"title": "Primary: CPU Usage by Pod",
"tooltip": {
@@ -651,7 +658,6 @@
},
"yaxes": [
{
- "$$hashKey": "object:1845",
"format": "s",
"label": "CPU seconds / second",
"logBase": 1,
@@ -660,7 +666,6 @@
"show": true
},
{
- "$$hashKey": "object:1846",
"format": "short",
"label": null,
"logBase": 1,
@@ -668,7 +673,11 @@
"min": null,
"show": false
}
- ]
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
},
{
"aliasColors": {},
@@ -711,7 +720,6 @@
"steppedLine": false,
"targets": [
{
- "$$hashKey": "object:1685",
"expr": "sum(rate(container_cpu_usage_seconds_total{cpu=\"total\",namespace=\"$namespace\",pod_name=~\"$canary.*\", pod_name!~\"$primary.*\", container_name!~\"POD|istio-proxy\"}[1m])) by (pod_name)",
"format": "time_series",
"hide": false,
@@ -722,6 +730,7 @@
],
"thresholds": [],
"timeFrom": null,
+ "timeRegions": [],
"timeShift": null,
"title": "Canary: CPU Usage by Pod",
"tooltip": {
@@ -739,7 +748,6 @@
},
"yaxes": [
{
- "$$hashKey": "object:1845",
"format": "s",
"label": "CPU seconds / second",
"logBase": 1,
@@ -748,7 +756,6 @@
"show": true
},
{
- "$$hashKey": "object:1846",
"format": "short",
"label": null,
"logBase": 1,
@@ -756,7 +763,11 @@
"min": null,
"show": false
}
- ]
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
},
{
"aliasColors": {},
@@ -799,7 +810,6 @@
"steppedLine": false,
"targets": [
{
- "$$hashKey": "object:1685",
"expr": "sum(container_memory_working_set_bytes{namespace=\"$namespace\",pod_name=~\"$primary.*\", container_name!~\"POD|istio-proxy\"}) by (pod_name)",
"format": "time_series",
"hide": false,
@@ -811,6 +821,7 @@
],
"thresholds": [],
"timeFrom": null,
+ "timeRegions": [],
"timeShift": null,
"title": "Primary: Memory Usage by Pod",
"tooltip": {
@@ -828,7 +839,6 @@
},
"yaxes": [
{
- "$$hashKey": "object:1845",
"decimals": null,
"format": "bytes",
"label": "",
@@ -838,7 +848,6 @@
"show": true
},
{
- "$$hashKey": "object:1846",
"format": "short",
"label": null,
"logBase": 1,
@@ -846,7 +855,11 @@
"min": null,
"show": false
}
- ]
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
},
{
"aliasColors": {},
@@ -889,7 +902,6 @@
"steppedLine": false,
"targets": [
{
- "$$hashKey": "object:1685",
"expr": "sum(container_memory_working_set_bytes{namespace=\"$namespace\",pod_name=~\"$canary.*\", pod_name!~\"$primary.*\", container_name!~\"POD|istio-proxy\"}) by (pod_name)",
"format": "time_series",
"hide": false,
@@ -901,6 +913,7 @@
],
"thresholds": [],
"timeFrom": null,
+ "timeRegions": [],
"timeShift": null,
"title": "Canary: Memory Usage by Pod",
"tooltip": {
@@ -918,7 +931,6 @@
},
"yaxes": [
{
- "$$hashKey": "object:1845",
"decimals": null,
"format": "bytes",
"label": "",
@@ -928,7 +940,6 @@
"show": true
},
{
- "$$hashKey": "object:1846",
"format": "short",
"label": null,
"logBase": 1,
@@ -936,7 +947,11 @@
"min": null,
"show": false
}
- ]
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
},
{
"aliasColors": {},
@@ -975,12 +990,10 @@
"renderer": "flot",
"seriesOverrides": [
{
- "$$hashKey": "object:3641",
"alias": "received",
"color": "#f9d9f9"
},
{
- "$$hashKey": "object:3649",
"alias": "transmited",
"color": "#f29191"
}
@@ -990,7 +1003,6 @@
"steppedLine": false,
"targets": [
{
- "$$hashKey": "object:2598",
"expr": "sum(rate (container_network_receive_bytes_total{namespace=\"$namespace\",pod_name=~\"$primary.*\"}[1m])) ",
"format": "time_series",
"intervalFactor": 1,
@@ -998,7 +1010,6 @@
"refId": "A"
},
{
- "$$hashKey": "object:3245",
"expr": "-sum (rate (container_network_transmit_bytes_total{namespace=\"$namespace\",pod_name=~\"$primary.*\"}[1m]))",
"format": "time_series",
"intervalFactor": 1,
@@ -1008,6 +1019,7 @@
],
"thresholds": [],
"timeFrom": null,
+ "timeRegions": [],
"timeShift": null,
"title": "Primary: Network I/O",
"tooltip": {
@@ -1025,7 +1037,6 @@
},
"yaxes": [
{
- "$$hashKey": "object:1845",
"decimals": null,
"format": "Bps",
"label": "",
@@ -1035,7 +1046,6 @@
"show": true
},
{
- "$$hashKey": "object:1846",
"format": "short",
"label": null,
"logBase": 1,
@@ -1043,7 +1053,11 @@
"min": null,
"show": false
}
- ]
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
},
{
"aliasColors": {},
@@ -1082,12 +1096,10 @@
"renderer": "flot",
"seriesOverrides": [
{
- "$$hashKey": "object:3641",
"alias": "received",
"color": "#f9d9f9"
},
{
- "$$hashKey": "object:3649",
"alias": "transmited",
"color": "#f29191"
}
@@ -1097,7 +1109,6 @@
"steppedLine": false,
"targets": [
{
- "$$hashKey": "object:2598",
"expr": "sum(rate (container_network_receive_bytes_total{namespace=\"$namespace\",pod_name=~\"$canary.*\",pod_name!~\"$primary.*\"}[1m])) ",
"format": "time_series",
"intervalFactor": 1,
@@ -1105,7 +1116,6 @@
"refId": "A"
},
{
- "$$hashKey": "object:3245",
"expr": "-sum (rate (container_network_transmit_bytes_total{namespace=\"$namespace\",pod_name=~\"$canary.*\",pod_name!~\"$primary.*\"}[1m]))",
"format": "time_series",
"intervalFactor": 1,
@@ -1115,6 +1125,7 @@
],
"thresholds": [],
"timeFrom": null,
+ "timeRegions": [],
"timeShift": null,
"title": "Canary: Network I/O",
"tooltip": {
@@ -1132,7 +1143,6 @@
},
"yaxes": [
{
- "$$hashKey": "object:1845",
"decimals": null,
"format": "Bps",
"label": "",
@@ -1142,7 +1152,6 @@
"show": true
},
{
- "$$hashKey": "object:1846",
"format": "short",
"label": null,
"logBase": 1,
@@ -1150,7 +1159,11 @@
"min": null,
"show": false
}
- ]
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
},
{
"content": "",
@@ -1205,7 +1218,6 @@
"steppedLine": false,
"targets": [
{
- "$$hashKey": "object:1953",
"expr": "round(sum(irate(istio_requests_total{connection_security_policy=\"mutual_tls\", destination_workload_namespace=~\"$namespace\", destination_workload=~\"$primary\", reporter=\"destination\"}[30s])) by (source_workload, source_workload_namespace, response_code), 0.001)",
"format": "time_series",
"hide": false,
@@ -1215,7 +1227,6 @@
"step": 2
},
{
- "$$hashKey": "object:1954",
"expr": "round(sum(irate(istio_requests_total{connection_security_policy!=\"mutual_tls\", destination_workload_namespace=~\"$namespace\", destination_workload=~\"$primary\", reporter=\"destination\"}[30s])) by (source_workload, source_workload_namespace, response_code), 0.001)",
"format": "time_series",
"hide": false,
@@ -1227,6 +1238,7 @@
],
"thresholds": [],
"timeFrom": null,
+ "timeRegions": [],
"timeShift": null,
"title": "Primary: Incoming Requests by Source And Response Code",
"tooltip": {
@@ -1246,7 +1258,6 @@
},
"yaxes": [
{
- "$$hashKey": "object:1999",
"format": "ops",
"label": null,
"logBase": 1,
@@ -1255,7 +1266,6 @@
"show": true
},
{
- "$$hashKey": "object:2000",
"format": "short",
"label": null,
"logBase": 1,
@@ -1263,7 +1273,11 @@
"min": null,
"show": false
}
- ]
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
},
{
"aliasColors": {},
@@ -1323,6 +1337,7 @@
],
"thresholds": [],
"timeFrom": null,
+ "timeRegions": [],
"timeShift": null,
"title": "Canary: Incoming Requests by Source And Response Code",
"tooltip": {
@@ -1357,7 +1372,11 @@
"min": null,
"show": false
}
- ]
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
},
{
"aliasColors": {},
@@ -1416,6 +1435,7 @@
],
"thresholds": [],
"timeFrom": null,
+ "timeRegions": [],
"timeShift": null,
"title": "Primary: Outgoing Requests by Destination And Response Code",
"tooltip": {
@@ -1450,7 +1470,11 @@
"min": null,
"show": false
}
- ]
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
},
{
"aliasColors": {},
@@ -1509,6 +1533,7 @@
],
"thresholds": [],
"timeFrom": null,
+ "timeRegions": [],
"timeShift": null,
"title": "Canary: Outgoing Requests by Destination And Response Code",
"tooltip": {
@@ -1543,7 +1568,11 @@
"min": null,
"show": false
}
- ]
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
}
],
"refresh": "10s",
@@ -1555,10 +1584,12 @@
{
"allValue": null,
"current": {
- "text": "demo",
- "value": "demo"
+ "selected": true,
+ "text": "test",
+ "value": "test"
},
"datasource": "prometheus",
+ "definition": "",
"hide": 0,
"includeAll": false,
"label": "Namespace",
@@ -1568,6 +1599,7 @@
"query": "query_result(sum(istio_requests_total) by (destination_workload_namespace) or sum(istio_tcp_sent_bytes_total) by (destination_workload_namespace))",
"refresh": 1,
"regex": "/.*_namespace=\"([^\"]*).*/",
+ "skipUrlSync": false,
"sort": 0,
"tagValuesQuery": "",
"tags": [],
@@ -1578,10 +1610,12 @@
{
"allValue": null,
"current": {
- "text": "primary",
- "value": "primary"
+ "selected": false,
+ "text": "backend-primary",
+ "value": "backend-primary"
},
"datasource": "prometheus",
+ "definition": "",
"hide": 0,
"includeAll": false,
"label": "Primary",
@@ -1591,6 +1625,7 @@
"query": "query_result(sum(istio_requests_total{destination_workload_namespace=~\"$namespace\"}) by (destination_service_name))",
"refresh": 1,
"regex": "/.*destination_service_name=\"([^\"]*).*/",
+ "skipUrlSync": false,
"sort": 1,
"tagValuesQuery": "",
"tags": [],
@@ -1601,10 +1636,12 @@
{
"allValue": null,
"current": {
- "text": "canary",
- "value": "canary"
+ "selected": true,
+ "text": "backend",
+ "value": "backend"
},
"datasource": "prometheus",
+ "definition": "",
"hide": 0,
"includeAll": false,
"label": "Canary",
@@ -1614,6 +1651,7 @@
"query": "query_result(sum(istio_requests_total{destination_workload_namespace=~\"$namespace\"}) by (destination_service_name))",
"refresh": 1,
"regex": "/.*destination_service_name=\"([^\"]*).*/",
+ "skipUrlSync": false,
"sort": 1,
"tagValuesQuery": "",
"tags": [],
@@ -1653,7 +1691,7 @@
]
},
"timezone": "",
- "title": "Canary analysis",
+ "title": "Flagger canary",
"uid": "RdykD7tiz",
- "version": 2
-}
+ "version": 3
+}
\ No newline at end of file
diff --git a/charts/grafana/templates/deployment.yaml b/charts/grafana/templates/deployment.yaml
index 756da994..f3235011 100644
--- a/charts/grafana/templates/deployment.yaml
+++ b/charts/grafana/templates/deployment.yaml
@@ -38,12 +38,21 @@ spec:
# path: /
# port: http
env:
+ - name: GF_PATHS_PROVISIONING
+ value: /etc/grafana/provisioning/
+ {{- if .Values.password }}
- name: GF_SECURITY_ADMIN_USER
value: {{ .Values.user }}
- name: GF_SECURITY_ADMIN_PASSWORD
value: {{ .Values.password }}
- - name: GF_PATHS_PROVISIONING
- value: /etc/grafana/provisioning/
+ {{- else }}
+ - name: GF_AUTH_BASIC_ENABLED
+ value: "false"
+ - name: GF_AUTH_ANONYMOUS_ENABLED
+ value: "true"
+ - name: GF_AUTH_ANONYMOUS_ORG_ROLE
+ value: Admin
+ {{- end }}
volumeMounts:
- name: grafana
mountPath: /var/lib/grafana
diff --git a/charts/grafana/values.yaml b/charts/grafana/values.yaml
index 37ff1a1f..d6a4a3f5 100644
--- a/charts/grafana/values.yaml
+++ b/charts/grafana/values.yaml
@@ -6,7 +6,7 @@ replicaCount: 1
image:
repository: grafana/grafana
- tag: 5.4.2
+ tag: 5.4.3
pullPolicy: IfNotPresent
service:
@@ -28,7 +28,7 @@ tolerations: []
affinity: {}
user: admin
-password: admin
+password:
# Istio Prometheus instance
url: http://prometheus:9090
diff --git a/charts/podinfo/.helmignore b/charts/podinfo/.helmignore
new file mode 100644
index 00000000..f0c13194
--- /dev/null
+++ b/charts/podinfo/.helmignore
@@ -0,0 +1,21 @@
+# 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
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
diff --git a/charts/podinfo/Chart.yaml b/charts/podinfo/Chart.yaml
new file mode 100644
index 00000000..d54ecf63
--- /dev/null
+++ b/charts/podinfo/Chart.yaml
@@ -0,0 +1,12 @@
+apiVersion: v1
+version: 2.0.0
+appVersion: 1.4.0
+name: podinfo
+engine: gotpl
+description: Flagger canary deployment demo chart
+home: https://github.com/stefanprodan/flagger
+maintainers:
+- email: stefanprodan@users.noreply.github.com
+ name: stefanprodan
+sources:
+- https://github.com/stefanprodan/flagger
diff --git a/charts/podinfo/README.md b/charts/podinfo/README.md
new file mode 100644
index 00000000..6a626022
--- /dev/null
+++ b/charts/podinfo/README.md
@@ -0,0 +1,79 @@
+# Podinfo
+
+Podinfo is a tiny web application made with Go
+that showcases best practices of running canary deployments with Flagger and Istio.
+
+## Installing the Chart
+
+Add Flagger Helm repository:
+
+```console
+helm repo add flagger https://flagger.app
+```
+
+To install the chart with the release name `frontend`:
+
+```console
+helm upgrade -i frontend flagger/podinfo \
+--namespace test \
+--set nameOverride=frontend \
+--set backend=http://backend.test:9898/echo \
+--set canary.enabled=true \
+--set canary.istioIngress.enabled=true \
+--set canary.istioIngress.gateway=public-gateway.istio-system.svc.cluster.local \
+--set canary.istioIngress.host=frontend.istio.example.com
+```
+
+To install the chart as `backend`:
+
+```console
+helm upgrade -i backend flagger/podinfo \
+--namespace test \
+--set nameOverride=backend \
+--set canary.enabled=true
+```
+
+## Uninstalling the Chart
+
+To uninstall/delete the `frontend` deployment:
+
+```console
+$ helm delete --purge frontend
+```
+
+The command removes all the Kubernetes components associated with the chart and deletes the release.
+
+## Configuration
+
+The following tables lists the configurable parameters of the podinfo chart and their default values.
+
+Parameter | Description | Default
+--- | --- | ---
+`image.repository` | image repository | `quay.io/stefanprodan/podinfo`
+`image.tag` | image tag | ``
+`image.pullPolicy` | image pull policy | `IfNotPresent`
+`hpa.enabled` | enables HPA | `true`
+`hpa.cpu` | target CPU usage per pod | `80`
+`hpa.memory` | target memory usage per pod | `512Mi`
+`hpa.minReplicas` | maximum pod replicas | `2`
+`hpa.maxReplicas` | maximum pod replicas | `4`
+`resources.requests/cpu` | pod CPU request | `1m`
+`resources.requests/memory` | pod memory request | `16Mi`
+`backend` | backend URL | None
+`faults.delay` | random HTTP response delays between 0 and 5 seconds | `false`
+`faults.error` | 1/3 chances of a random HTTP response error | `false`
+
+Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example,
+
+```console
+$ helm install flagger/podinfo --name frontend \
+ --set=image.tag=1.4.1,hpa.enabled=false
+```
+
+Alternatively, a YAML file that specifies the values for the above parameters can be provided while installing the chart. For example,
+
+```console
+$ helm install flagger/podinfo --name frontend -f values.yaml
+```
+
+
diff --git a/charts/podinfo/templates/NOTES.txt b/charts/podinfo/templates/NOTES.txt
new file mode 100644
index 00000000..92c7709d
--- /dev/null
+++ b/charts/podinfo/templates/NOTES.txt
@@ -0,0 +1 @@
+podinfo {{ .Release.Name }} deployed!
\ No newline at end of file
diff --git a/charts/podinfo/templates/_helpers.tpl b/charts/podinfo/templates/_helpers.tpl
new file mode 100644
index 00000000..3fe7a70f
--- /dev/null
+++ b/charts/podinfo/templates/_helpers.tpl
@@ -0,0 +1,43 @@
+{{/* vim: set filetype=mustache: */}}
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "podinfo.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "podinfo.fullname" -}}
+{{- if .Values.fullnameOverride -}}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
+{{- else -}}
+{{- $name := default .Chart.Name .Values.nameOverride -}}
+{{- if contains $name .Release.Name -}}
+{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
+{{- else -}}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+{{- end -}}
+{{- end -}}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "podinfo.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+
+{{/*
+Create chart name suffix.
+*/}}
+{{- define "podinfo.suffix" -}}
+{{- if .Values.canary.enabled -}}
+{{- "-primary" -}}
+{{- else -}}
+{{- "" -}}
+{{- end -}}
+{{- end -}}
\ No newline at end of file
diff --git a/charts/podinfo/templates/canary.yaml b/charts/podinfo/templates/canary.yaml
new file mode 100644
index 00000000..92cc5865
--- /dev/null
+++ b/charts/podinfo/templates/canary.yaml
@@ -0,0 +1,54 @@
+{{- if .Values.canary.enabled }}
+apiVersion: flagger.app/v1alpha3
+kind: Canary
+metadata:
+ name: {{ template "podinfo.fullname" . }}
+ labels:
+ app: {{ template "podinfo.name" . }}
+ chart: {{ template "podinfo.chart" . }}
+ release: {{ .Release.Name }}
+ heritage: {{ .Release.Service }}
+spec:
+ targetRef:
+ apiVersion: apps/v1
+ kind: Deployment
+ name: {{ template "podinfo.fullname" . }}
+ progressDeadlineSeconds: 60
+ autoscalerRef:
+ apiVersion: autoscaling/v2beta1
+ kind: HorizontalPodAutoscaler
+ name: {{ template "podinfo.fullname" . }}
+ service:
+ port: {{ .Values.service.port }}
+ {{- if .Values.canary.istioIngress.enabled }}
+ gateways:
+ - {{ .Values.canary.istioIngress.gateway }}
+ hosts:
+ - {{ .Values.canary.istioIngress.host }}
+ {{- end }}
+ canaryAnalysis:
+ interval: {{ .Values.canary.analysis.interval }}
+ threshold: {{ .Values.canary.analysis.threshold }}
+ maxWeight: {{ .Values.canary.analysis.maxWeight }}
+ stepWeight: {{ .Values.canary.analysis.stepWeight }}
+ metrics:
+ - name: istio_requests_total
+ threshold: {{ .Values.canary.thresholds.successRate }}
+ interval: 1m
+ - name: istio_request_duration_seconds_bucket
+ threshold: {{ .Values.canary.thresholds.latency }}
+ interval: 1m
+ {{- if .Values.canary.loadtest.enabled }}
+ webhooks:
+ - name: load-test-get
+ url: {{ .Values.canary.loadtest.url }}
+ timeout: 5s
+ metadata:
+ cmd: "hey -z 1m -q 5 -c 2 http://{{ template "podinfo.fullname" . }}.{{ .Release.Namespace }}:{{ .Values.service.port }}"
+ - name: load-test-post
+ url: {{ .Values.canary.loadtest.url }}
+ timeout: 5s
+ metadata:
+ cmd: "hey -z 1m -q 5 -c 2 -m POST -d '{\"test\": true}' http://{{ template "podinfo.fullname" . }}.{{ .Release.Namespace }}:{{ .Values.service.port }}/echo"
+ {{- end }}
+{{- end }}
\ No newline at end of file
diff --git a/charts/podinfo/templates/deployment.yaml b/charts/podinfo/templates/deployment.yaml
new file mode 100644
index 00000000..20a6751e
--- /dev/null
+++ b/charts/podinfo/templates/deployment.yaml
@@ -0,0 +1,86 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: {{ template "podinfo.fullname" . }}
+ labels:
+ app: {{ template "podinfo.name" . }}
+ chart: {{ template "podinfo.chart" . }}
+ release: {{ .Release.Name }}
+ heritage: {{ .Release.Service }}
+spec:
+ strategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxUnavailable: 1
+ selector:
+ matchLabels:
+ app: {{ template "podinfo.fullname" . }}
+ template:
+ metadata:
+ labels:
+ app: {{ template "podinfo.fullname" . }}
+ annotations:
+ prometheus.io/scrape: 'true'
+ spec:
+ terminationGracePeriodSeconds: 30
+ containers:
+ - name: {{ .Chart.Name }}
+ image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
+ imagePullPolicy: {{ .Values.image.pullPolicy }}
+ command:
+ - ./podinfo
+ - --port={{ .Values.service.port }}
+ - --level={{ .Values.logLevel }}
+ - --random-delay={{ .Values.faults.delay }}
+ - --random-error={{ .Values.faults.error }}
+ env:
+ {{- if .Values.message }}
+ - name: PODINFO_UI_MESSAGE
+ value: {{ .Values.message }}
+ {{- end }}
+ {{- if .Values.backend }}
+ - name: PODINFO_BACKEND_URL
+ value: {{ .Values.backend }}
+ {{- end }}
+ ports:
+ - name: http
+ containerPort: {{ .Values.service.port }}
+ protocol: TCP
+ livenessProbe:
+ exec:
+ command:
+ - podcli
+ - check
+ - http
+ - localhost:{{ .Values.service.port }}/healthz
+ initialDelaySeconds: 5
+ timeoutSeconds: 5
+ readinessProbe:
+ exec:
+ command:
+ - podcli
+ - check
+ - http
+ - localhost:{{ .Values.service.port }}/readyz
+ initialDelaySeconds: 5
+ timeoutSeconds: 5
+ volumeMounts:
+ - name: data
+ mountPath: /data
+ resources:
+{{ toYaml .Values.resources | indent 12 }}
+ {{- with .Values.nodeSelector }}
+ nodeSelector:
+{{ toYaml . | indent 8 }}
+ {{- end }}
+ {{- with .Values.affinity }}
+ affinity:
+{{ toYaml . | indent 8 }}
+ {{- end }}
+ {{- with .Values.tolerations }}
+ tolerations:
+{{ toYaml . | indent 8 }}
+ {{- end }}
+ volumes:
+ - name: data
+ emptyDir: {}
diff --git a/charts/podinfo/templates/hpa.yaml b/charts/podinfo/templates/hpa.yaml
new file mode 100644
index 00000000..dcb36d3a
--- /dev/null
+++ b/charts/podinfo/templates/hpa.yaml
@@ -0,0 +1,32 @@
+{{- if .Values.hpa.enabled -}}
+apiVersion: autoscaling/v2beta1
+kind: HorizontalPodAutoscaler
+metadata:
+ name: {{ template "podinfo.fullname" . }}
+spec:
+ scaleTargetRef:
+ apiVersion: apps/v1beta2
+ kind: Deployment
+ name: {{ template "podinfo.fullname" . }}
+ minReplicas: {{ .Values.hpa.minReplicas }}
+ maxReplicas: {{ .Values.hpa.maxReplicas }}
+ metrics:
+ {{- if .Values.hpa.cpu }}
+ - type: Resource
+ resource:
+ name: cpu
+ targetAverageUtilization: {{ .Values.hpa.cpu }}
+ {{- end }}
+ {{- if .Values.hpa.memory }}
+ - type: Resource
+ resource:
+ name: memory
+ targetAverageValue: {{ .Values.hpa.memory }}
+ {{- end }}
+ {{- if .Values.hpa.requests }}
+ - type: Pod
+ pods:
+ metricName: http_requests
+ targetAverageValue: {{ .Values.hpa.requests }}
+ {{- end }}
+{{- end }}
diff --git a/charts/podinfo/templates/service.yaml b/charts/podinfo/templates/service.yaml
new file mode 100644
index 00000000..82b9451c
--- /dev/null
+++ b/charts/podinfo/templates/service.yaml
@@ -0,0 +1,20 @@
+{{- if not .Values.canary.enabled }}
+apiVersion: v1
+kind: Service
+metadata:
+ name: {{ template "podinfo.fullname" . }}
+ labels:
+ app: {{ template "podinfo.name" . }}
+ chart: {{ template "podinfo.chart" . }}
+ release: {{ .Release.Name }}
+ heritage: {{ .Release.Service }}
+spec:
+ type: {{ .Values.service.type }}
+ ports:
+ - port: {{ .Values.service.port }}
+ targetPort: http
+ protocol: TCP
+ name: http
+ selector:
+ app: {{ template "podinfo.fullname" . }}
+{{- end }}
\ No newline at end of file
diff --git a/charts/podinfo/templates/tests/test-config.yaml b/charts/podinfo/templates/tests/test-config.yaml
new file mode 100755
index 00000000..49a95554
--- /dev/null
+++ b/charts/podinfo/templates/tests/test-config.yaml
@@ -0,0 +1,22 @@
+{{- $url := printf "%s%s.%s:%v" (include "podinfo.fullname" .) (include "podinfo.suffix" .) .Release.Namespace .Values.service.port -}}
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: {{ template "podinfo.fullname" . }}-tests
+ labels:
+ heritage: {{ .Release.Service }}
+ release: {{ .Release.Name }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version }}
+ app: {{ template "podinfo.name" . }}
+data:
+ run.sh: |-
+ @test "HTTP POST /echo" {
+ run curl --retry 3 --connect-timeout 2 -sSX POST -d 'test' {{ $url }}/echo
+ [ $output = "test" ]
+ }
+ @test "HTTP POST /store" {
+ curl --retry 3 --connect-timeout 2 -sSX POST -d 'test' {{ $url }}/store
+ }
+ @test "HTTP GET /" {
+ curl --retry 3 --connect-timeout 2 -sS {{ $url }} | grep hostname
+ }
diff --git a/charts/podinfo/templates/tests/test-pod.yaml b/charts/podinfo/templates/tests/test-pod.yaml
new file mode 100755
index 00000000..5105ef4b
--- /dev/null
+++ b/charts/podinfo/templates/tests/test-pod.yaml
@@ -0,0 +1,43 @@
+apiVersion: v1
+kind: Pod
+metadata:
+ name: {{ template "podinfo.fullname" . }}-tests-{{ randAlphaNum 5 | lower }}
+ annotations:
+ "helm.sh/hook": test-success
+ sidecar.istio.io/inject: "false"
+ labels:
+ heritage: {{ .Release.Service }}
+ release: {{ .Release.Name }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version }}
+ app: {{ template "podinfo.name" . }}
+spec:
+ initContainers:
+ - name: "test-framework"
+ image: "dduportal/bats:0.4.0"
+ command:
+ - "bash"
+ - "-c"
+ - |
+ set -ex
+ # copy bats to tools dir
+ cp -R /usr/local/libexec/ /tools/bats/
+ volumeMounts:
+ - mountPath: /tools
+ name: tools
+ containers:
+ - name: {{ .Release.Name }}-ui-test
+ image: dduportal/bats:0.4.0
+ command: ["/tools/bats/bats", "-t", "/tests/run.sh"]
+ volumeMounts:
+ - mountPath: /tests
+ name: tests
+ readOnly: true
+ - mountPath: /tools
+ name: tools
+ volumes:
+ - name: tests
+ configMap:
+ name: {{ template "podinfo.fullname" . }}-tests
+ - name: tools
+ emptyDir: {}
+ restartPolicy: Never
diff --git a/charts/podinfo/values.yaml b/charts/podinfo/values.yaml
new file mode 100644
index 00000000..3768d618
--- /dev/null
+++ b/charts/podinfo/values.yaml
@@ -0,0 +1,70 @@
+# Default values for podinfo.
+image:
+ repository: quay.io/stefanprodan/podinfo
+ tag: 1.4.0
+ pullPolicy: IfNotPresent
+
+service:
+ type: ClusterIP
+ port: 9898
+
+hpa:
+ enabled: true
+ minReplicas: 2
+ maxReplicas: 4
+ cpu: 80
+ memory: 512Mi
+
+canary:
+ enabled: false
+ istioIngress:
+ enabled: false
+ # Istio ingress gateway name
+ gateway: public-gateway.istio-system.svc.cluster.local
+ # external host name eg. podinfo.example.com
+ host:
+ analysis:
+ # schedule interval (default 60s)
+ interval: 15s
+ # max number of failed metric checks before rollback
+ threshold: 10
+ # max traffic percentage routed to canary
+ # percentage (0-100)
+ maxWeight: 50
+ # canary increment step
+ # percentage (0-100)
+ stepWeight: 2
+ thresholds:
+ # minimum req success rate (non 5xx responses)
+ # percentage (0-100)
+ successRate: 99
+ # maximum req duration P99
+ # milliseconds
+ latency: 500
+ loadtest:
+ enabled: true
+ # load tester address
+ url: http://flagger-loadtester.test/
+
+resources:
+ limits:
+ requests:
+ cpu: 100m
+ memory: 32Mi
+
+nodeSelector: {}
+
+tolerations: []
+
+affinity: {}
+
+nameOverride: ""
+fullnameOverride: ""
+
+logLevel: info
+backend: #http://backend-podinfo:9898/echo
+message: #UI greetings
+
+faults:
+ delay: false
+ error: false
\ No newline at end of file
diff --git a/docs/gitbook/install/flagger-install-on-google-cloud.md b/docs/gitbook/install/flagger-install-on-google-cloud.md
index ed3604ba..52631a4d 100644
--- a/docs/gitbook/install/flagger-install-on-google-cloud.md
+++ b/docs/gitbook/install/flagger-install-on-google-cloud.md
@@ -54,6 +54,7 @@ gcloud beta container clusters create istio \
--machine-type=n1-highcpu-4 \
--preemptible \
--no-enable-cloud-logging \
+--no-enable-cloud-monitoring \
--disk-size=30 \
--enable-autorepair \
--addons=HorizontalPodAutoscaling,Istio \
diff --git a/docs/screens/flagger-grafana-dashboard.png b/docs/screens/flagger-grafana-dashboard.png
new file mode 100644
index 00000000..3d65a7d7
Binary files /dev/null and b/docs/screens/flagger-grafana-dashboard.png differ
diff --git a/docs/screens/grafana-canary-analysis.png b/docs/screens/grafana-canary-analysis.png
index 6055b334..a40de5c3 100644
Binary files a/docs/screens/grafana-canary-analysis.png and b/docs/screens/grafana-canary-analysis.png differ