diff --git a/Chart.yaml b/Chart.yaml new file mode 100644 index 0000000..95925f0 --- /dev/null +++ b/Chart.yaml @@ -0,0 +1,36 @@ +annotations: + artifacthub.io/images: | + - name: collabora + image: docker.io/collabora/code:23.05.5.4.1 + - name: nginx + image: docker.io/nginx:1.25 + - name: twostoryrobot/simple-file-upload + image: docker.io/twostoryrobot/simple-file-upload@sha256:547fc4360b31d8604b7a26202914e87cd13609cc938fd83f412c77eb44aa1cc4 +apiVersion: v2 +appVersion: 23.05.5.4.1 +description: Collabora Online helm chart +home: https://www.collaboraoffice.com/code/ +icon: https://avatars0.githubusercontent.com/u/22418908?s=200&v=4 +keywords: +- collabora-online +- collabora +- code +- nextcloud +- office +maintainers: +- email: k.erber@erber-freelance.de + name: Klaus Erber + url: https://www.erber-freelance.de/ +- email: martin.mueller@dataport.de + name: Martin Müller + url: https://dphoenixsuite.de +- email: geno+dev@fireorbit.de + name: Geno + url: https://fireorbit.de +name: collabora-online +sources: +- https://github.com/CollaboraOnline/online +- https://github.com/CollaboraOnline/online/tree/master/docker +- https://github.com/CollaboraOnline/online/tree/master/kubernetes/helm/collabora-online +type: application +version: 1.1.5 diff --git a/README.md b/README.md index b7748f7..4743770 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,330 @@ -# collabora-online +# Collabora Online for Kubernetes -helm collabora kubernetes \ No newline at end of file +In order for Collaborative Editing and copy/paste to function correctly on kubernetes, it is vital to ensure that all users editing the same document and all the clipboard request end up being served by the same pod. Using the WOPI protocol, the https URL includes a unique identifier (WOPISrc) for use with this document. Thus load balancing can be done by using WOPISrc -- ensuring that all URLs that contain the same WOPISrc are sent to the same pod. + +## Deploying Collabora Online in Kubernetes + +1. Install [helm](https://helm.sh/docs/intro/install/) + +2. Setting up Kubernetes Ingress Controller + + A. Nginx: + + Install [Nginx Ingress + Controller](https://kubernetes.github.io/ingress-nginx/deploy/) + + B. HAProxy: + + Install [HAProxy Ingress + Controller](https://www.haproxy.com/documentation/kubernetes-ingress/) + + --- + + **Note:** + + **Openshift** uses minimized version of HAproxy called + [Router](https://docs.openshift.com/container-platform/3.11/install_config/router) that doesn\'t support all functionality of HAProxy but for COOL we need advance annotations Therefore it is recommended deploy [HAproxy Kubernetes Ingress](https://artifacthub.io/packages/helm/haproxytech/kubernetes-ingress) in `collabora` namespace + + --- + +3. Create an `my_values.yaml` (if your setup differs e.g. take an look in then `values.yaml ./collabora-online/values.yaml`) of the + helmchart + + A. HAproxy: + + ``` yaml + replicaCount: 3 + + ingress: + enabled: true + className: "haproxy" + annotations: + haproxy.org/timeout-tunnel: "3600s" + haproxy.org/backend-config-snippet: | + balance url_param WOPISrc check_post + hash-type consistent + hosts: + - host: chart-example.local + paths: + - path: / + pathType: ImplementationSpecific + + image: + tag: "latest" + + autoscaling: + enabled: false + + collabora: + aliasgroups: + - host: "https://example.integrator.com:443" + extra_params: --o:ssl.enable=false --o:ssl.termination=true + + resources: + limits: + cpu: "1800m" + memory: "2000Mi" + requests: + cpu: "1800m" + memory: "2000Mi" + ``` + + B. Nginx: + + ``` yaml + replicaCount: 3 + + ingress: + enabled: true + className: "nginx" + annotations: + nginx.ingress.kubernetes.io/upstream-hash-by: "$arg_WOPISrc" + nginx.ingress.kubernetes.io/proxy-body-size: "0" + nginx.ingress.kubernetes.io/proxy-read-timeout: "600" + nginx.ingress.kubernetes.io/proxy-send-timeout: "600" + hosts: + - host: chart-example.local + paths: + - path: / + pathType: ImplementationSpecific + + image: + tag: "latest" + + autoscaling: + enabled: false + + collabora: + aliasgroups: + - host: "https://example.integrator.com:443" + extra_params: --o:ssl.enable=false --o:ssl.termination=true + + resources: + limits: + cpu: "1800m" + memory: "2000Mi" + requests: + cpu: "1800m" + memory: "2000Mi" + ``` + + --- + + **Note:** + + - **Horizontal Pod Autoscaling(HPA) is disabled for now. Because after scaling it breaks the collaborative editing and copy/paste + Therefore please set replicaCount as per your needs** + - If you have multiple host and aliases setup set aliasgroups in `my_values.yaml`: + + ``` yaml + collabora: + - host: "://:" + # if there are no aliases you can ignore the below line + aliases: ["://:, ://:"] + # more host and aliases list is possible + ``` + + - Specify `server_name` when the hostname is not reachable directly for example behind reverse-proxy + + ``` yaml + collabora: + server_name: : + ``` + + - In **Openshift** , it is recommended to use HAproxy deployment instead of default router. And add `className` in ingress block + so that Openshift uses HAProxy Ingress Controller instead of `Router`: + + ``` yaml + ingress: + className: "haproxy" + ``` + --- + +4. Install helm-chart using below command, it should deploy the collabora-online + + ``` bash + helm repo add collabora https://collaboraonline.github.io/online/ + helm install --create-namespace --namespace collabora collabora-online collabora/collabora-online -f my_values.yaml + ``` + +5. Follow only if you are using `NodePort` service type in HAProxy and/or using minikube to setup, otherwise skip + + A. HAProxy service is deployed as NodePort so we can access it with node's ip address. To get node ip + ```bash + minikube ip + ``` + Example output: + ``` + 192.168.0.106 + ``` + B. Each container port is mapped to a `NodePort` port via the `Service` object. To find those ports + ``` + kubectl get svc --namespace=haproxy-controller + ``` + Example output: + + ``` + |----------------|---------|--------------|------------|------------------------------------------| + |NAME |TYPE |CLUSTER-IP |EXTERNAL-IP |PORT(S) | + |----------------|---------|--------------|------------|------------------------------------------| + |haproxy-ingress |NodePort |10.108.214.98 | |80:30536/TCP,443:31821/TCP,1024:30480/TCP | + |----------------|---------|--------------|------------|------------------------------------------| + ``` + In this instance, the following ports were mapped: + - Container port 80 to NodePort 30536 + - Container port 443 to NodePort 31821 + - Container port 1024 to NodePort 30480 + +6. Additional step if deploying on minikube for testing: + + 1. Get minikube ip: + + ``` bash + minikube ip + ``` + + Example output: + + ``` bash + 192.168.0.106 + ``` + + 2. Add hostname to `/etc/hosts` + + ``` bash + 192.168.0.106 chart-example.local + ``` + + 3. To check if everything is setup correctly you can run: + + ``` bash + curl -I -H 'Host: chart-example.local' 'http://192.168.0.106:30536/' + ``` + + It should return a similar output as below: + + ``` bash + HTTP/1.1 200 OK + last-modified: Tue, 18 May 2021 10:46:29 + user-agent: COOLWSD WOPI Agent 6.4.8 + content-length: 2 + content-type: text/plain + ``` + +## Kubernetes cluster monitoring + +1. Install [kube-prometheus-stack](https://artifacthub.io/packages/helm/prometheus-community/kube-prometheus-stack), a collection of [Grafana](http://grafana.com/) dashboards, and [Prometheus rules](https://prometheus.io/docs/prometheus/latest/configuration/recording_rules/) combined with documentation and scripts to provide easy to operate end-to-end Kubernetes cluster monitoring with +[Prometheus](https://prometheus.io/) using the [Prometheus Operator](https://prometheus-operator.dev/). + +2. Enable prometheus service monitor, rules and grafana in your + + `my_values.yaml` + + ``` yaml + prometheus: + servicemonitor: + enabled: true + labels: + release: "kube-prometheus-stack" + rules: + enabled: true # will deploy alert rules + additionalLabels: + release: "kube-prometheus-stack" + grafana: + dashboards: + enabled: true # will deploy default dashboards + ``` + --- + **Note:** + + Use `kube-prometheus-stack` as release name when installing [kube-prometheus-stack](https://artifacthub.io/packages/helm/prometheus-community/kube-prometheus-stack) helm chart because we have passed `release=kube-prometheus-stack` label in our `my_values.yaml`. For Grafana Dashboards you may need to enable scan in correct namespaces (or ALL), enabled by `sidecar.dashboards.searchNamespace` in [Helmchart of grafana](https://artifacthub.io/packages/helm/grafana/grafana) (which is part of PrometheusOperator, so `grafana.sidecar.dashboards.searchNamespace`) + + --- + +## Dynamic/Remote configuration in kubernetes + +For big setups, you may not want to restart every pod to modify WOPI +hosts, therefore it is possible to setup an additional webserver to +serve a ConfigMap for using [Remote/Dynamic +Configuration](https://sdk.collaboraonline.com/docs/installation/Configuration.html#remote-dynamic-configuration) + +``` yaml +collabora: + env: + - name: remoteconfigurl + value: https://dynconfig.public.example.com/config/config.json + +dynamicConfig: + enabled: true + + ingress: + enabled: true + annotations: + "cert-manager.io/issuer": letsencrypt-zprod + hosts: + - host: "dynconfig.public.example.com" + tls: + - secretName: "collabora-online-dynconfig-tls" + hosts: + - "dynconfig.public.example.com" + + configuration: + kind: "configuration" + storage: + wopi: + alias_groups: + groups: + - host: "https://domain1\\.xyz\\.abc\\.com/" + allow: true + - host: "https://domain2\\.pqr\\.def\\.com/" + allow: true + aliases: + - "https://domain2\\.ghi\\.leno\\.de/" +``` +--- + +**Note:** + +In current state of COOL remoteconfigurl for [Remote/DynamicConfiguration](https://sdk.collaboraonline.com/docs/installation/Configuration.html#remote-dynamic-configuration) only uses HTTPS. see [here in wsd/COOLWSD.cpp](https://github.com/CollaboraOnline/online/blob/8591d323c6db99e592ac8ac8ebef0e3a95f2e6ba/wsd/COOLWSD.cpp#L1069-L1096) + +--- + +## Useful commands to check what is happening + +Where is this pods, are they ready? + +``` bash +kubectl -n collabora get pod +``` + +example output : + +``` bash +NAME READY STATUS RESTARTS AGE +collabora-online-5fb4869564-dnzmk 1/1 Running 0 28h +collabora-online-5fb4869564-fb4cf 1/1 Running 0 28h +collabora-online-5fb4869564-wbrv2 1/1 Running 0 28h +``` + +What is the outside host that multiple coolwsd servers actually +answering? + +``` bash +kubectl get ingress -n collabora +``` + +example output : + +``` bash +|-----------|------------------|--------------------------|------------------------|-------| +| NAMESPACE | NAME | HOSTS | ADDRESS | PORTS | +|-----------|------------------|--------------------------|------------------------|-------| +| collabora | collabora-online |chart-example.local | | 80 | +|-----------|------------------|--------------------------|------------------------|-------| +``` + +To uninstall the helm chart + +``` bash +helm uninstall collabora-online -n collabora +``` diff --git a/grafana_dashboards/ha-allocation.json b/grafana_dashboards/ha-allocation.json new file mode 100644 index 0000000..8d217be --- /dev/null +++ b/grafana_dashboards/ha-allocation.json @@ -0,0 +1,2902 @@ +{ + "__inputs": [ + ], + "__elements": {}, + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "9.0.5" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "stat", + "name": "Stat", + "version": "" + }, + { + "type": "panel", + "id": "timeseries", + "name": "Time series", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": null, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "dark-blue", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 5, + "x": 0, + "y": 0 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.0.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum(kubelet_node_name{env_id=~\"$env_id\"})by(env_id)", + "legendFormat": "{{env_id}}", + "range": true, + "refId": "A" + } + ], + "title": "Nodes", + "transparent": true, + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 5, + "x": 5, + "y": 0 + }, + "id": 19, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.0.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "count(coolwsd_count{env_id=~\"$env_id\"})by(env_id)", + "legendFormat": "{{env_id}}", + "range": true, + "refId": "A" + } + ], + "title": "Pods", + "transparent": true, + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "purple", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 5, + "x": 10, + "y": 0 + }, + "id": 5, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.0.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum(kit_assigned_count{env_id=~\"$env_id\"})by(env_id)", + "legendFormat": "{{env_id}}", + "range": true, + "refId": "A" + } + ], + "title": "Open Docs", + "transparent": true, + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "purple", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 15, + "y": 0 + }, + "id": 28, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.0.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "max(doc_views_active{env_id=~\"$env_id\"})by(env_id)", + "legendFormat": "{{env_id}}", + "range": true, + "refId": "A" + } + ], + "title": "Highes Viewer on Doc", + "transparent": true, + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 5, + "x": 19, + "y": 0 + }, + "id": 17, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.0.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum(document_active_views_active_count_total{env_id=~\"$env_id\"})by(env_id)", + "legendFormat": "{{env_id}}", + "range": true, + "refId": "A" + } + ], + "title": "Viewers", + "transparent": true, + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 4 + }, + "id": 44, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "mean", + "max" + ], + "displayMode": "table", + "placement": "right" + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "count(doc_pid{env_id=~\"$env_id\"})by(host,env_id)", + "legendFormat": "{{env_id}}: {{host}}", + "range": true, + "refId": "A" + } + ], + "title": "Documents per WOPI-Host", + "transparent": true, + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds", + "seriesBy": "last" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 50, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineStyle": { + "fill": "solid" + }, + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Pods" + }, + "properties": [ + { + "id": "custom.axisPlacement", + "value": "right" + }, + { + "id": "color", + "value": { + "mode": "fixed" + } + }, + { + "id": "custom.gradientMode", + "value": "opacity" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": " Viewers on Pod" + }, + "properties": [ + { + "id": "custom.fillBelowTo", + "value": "Docs" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Docs" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "purple", + "mode": "fixed" + } + }, + { + "id": "custom.lineWidth", + "value": 1 + }, + { + "id": "custom.fillOpacity", + "value": 0 + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 12 + }, + "id": 10, + "maxPerRow": 2, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "mean", + "max" + ], + "displayMode": "table", + "placement": "right" + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "repeat": "env_id", + "repeatDirection": "h", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "count(coolwsd_count{env_id=~\"$env_id\"})by(env_id)", + "legendFormat": "Pods", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum(kit_assigned_count{env_id=~\"$env_id\"})by(env_id)", + "hide": false, + "legendFormat": "Docs", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum(document_active_views_active_count_total{env_id=~\"$env_id\"})by(env_id)", + "hide": false, + "legendFormat": " Viewers on Pod", + "range": true, + "refId": "C" + } + ], + "title": "Usage Overview (ENV_ID: $env_id)", + "transparent": true, + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "green", + "mode": "fixed", + "seriesBy": "last" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "opacity", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineStyle": { + "fill": "solid" + }, + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Max" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#e02f4459", + "mode": "fixed" + } + }, + { + "id": "custom.fillBelowTo", + "value": "Avg" + }, + { + "id": "custom.gradientMode", + "value": "opacity" + }, + { + "id": "custom.lineWidth", + "value": 1 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Avg" + }, + "properties": [ + { + "id": "custom.fillOpacity", + "value": 45 + } + ] + } + ] + }, + "gridPos": { + "h": 6, + "w": 8, + "x": 0, + "y": 19 + }, + "id": 38, + "maxPerRow": 2, + "options": { + "legend": { + "calcs": [ + "lastNotNull" + ], + "displayMode": "hidden", + "placement": "right" + }, + "tooltip": { + "mode": "multi", + "sort": "asc" + } + }, + "repeat": "env_id", + "repeatDirection": "v", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "avg(kit_assigned_count{env_id=~\"$env_id\"})by(env_id)", + "hide": false, + "legendFormat": "Avg", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "max(kit_assigned_count{env_id=~\"$env_id\"})by(env_id)", + "hide": false, + "legendFormat": "Max", + "range": true, + "refId": "A" + } + ], + "title": "Docs on Pod (ENV_ID: $env_id)", + "transparent": true, + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "green", + "mode": "fixed", + "seriesBy": "last" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "opacity", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineStyle": { + "fill": "solid" + }, + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Max" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#e02f4459", + "mode": "fixed" + } + }, + { + "id": "custom.fillBelowTo", + "value": "Avg" + }, + { + "id": "custom.gradientMode", + "value": "opacity" + }, + { + "id": "custom.lineWidth", + "value": 1 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Avg" + }, + "properties": [ + { + "id": "custom.fillOpacity", + "value": 45 + } + ] + } + ] + }, + "gridPos": { + "h": 6, + "w": 8, + "x": 8, + "y": 19 + }, + "id": 37, + "maxPerRow": 2, + "options": { + "legend": { + "calcs": [ + "lastNotNull" + ], + "displayMode": "hidden", + "placement": "right" + }, + "tooltip": { + "mode": "multi", + "sort": "asc" + } + }, + "repeat": "env_id", + "repeatDirection": "v", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "avg(document_active_views_active_count_total{env_id=~\"$env_id\"})by(env_id)", + "hide": false, + "legendFormat": "Avg", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "max(document_active_views_active_count_total{env_id=~\"$env_id\"})by(env_id)", + "hide": false, + "legendFormat": "Max", + "range": true, + "refId": "A" + } + ], + "title": "Viewer on Pod (ENV_ID: $env_id)", + "transparent": true, + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "green", + "mode": "fixed", + "seriesBy": "last" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "opacity", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineStyle": { + "fill": "solid" + }, + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Max" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#e02f4459", + "mode": "fixed" + } + }, + { + "id": "custom.fillBelowTo", + "value": "Avg" + }, + { + "id": "custom.gradientMode", + "value": "opacity" + }, + { + "id": "custom.lineWidth", + "value": 1 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Avg" + }, + "properties": [ + { + "id": "custom.fillOpacity", + "value": 45 + } + ] + } + ] + }, + "gridPos": { + "h": 6, + "w": 8, + "x": 16, + "y": 19 + }, + "id": 36, + "maxPerRow": 2, + "options": { + "legend": { + "calcs": [ + "lastNotNull" + ], + "displayMode": "hidden", + "placement": "right" + }, + "tooltip": { + "mode": "multi", + "sort": "asc" + } + }, + "repeat": "env_id", + "repeatDirection": "v", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "avg(doc_views_active{env_id=~\"$env_id\"})by(env_id)", + "hide": false, + "legendFormat": "Avg", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "max(doc_views_active{env_id=~\"$env_id\"})by(env_id)", + "hide": false, + "legendFormat": "Max", + "range": true, + "refId": "A" + } + ], + "title": "Viewer on Doc (ENV_ID: $env_id)", + "transparent": true, + "type": "timeseries" + }, + { + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 31 + }, + "id": 16, + "title": "Errors", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "green", + "mode": "fixed", + "seriesBy": "last" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "opacity", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineStyle": { + "fill": "solid" + }, + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Max" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#e02f4459", + "mode": "fixed" + } + }, + { + "id": "custom.fillBelowTo", + "value": "Avg" + }, + { + "id": "custom.gradientMode", + "value": "opacity" + }, + { + "id": "custom.lineWidth", + "value": 1 + }, + { + "id": "custom.axisPlacement", + "value": "right" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Avg" + }, + "properties": [ + { + "id": "custom.fillOpacity", + "value": 45 + }, + { + "id": "custom.axisPlacement", + "value": "right" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Count" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "dark-red", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 32 + }, + "id": 45, + "maxPerRow": 2, + "options": { + "legend": { + "calcs": [ + "lastNotNull" + ], + "displayMode": "table", + "placement": "right" + }, + "tooltip": { + "mode": "multi", + "sort": "asc" + } + }, + "repeatDirection": "v", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "avg(count(doc_pid{env_id=~\"$env_id\"})by(key)>1)", + "hide": false, + "legendFormat": "Avg", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "max(count(doc_pid{env_id=~\"$env_id\"})by(key)>1)", + "hide": false, + "legendFormat": "Max", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "count(count(doc_pid{env_id=~\"$env_id\"})by(key)>1)", + "hide": false, + "legendFormat": "Count", + "range": true, + "refId": "C" + } + ], + "title": "Error: Duplicated Documents (All ENV_ID)", + "transparent": true, + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "yellow", + "value": 1 + }, + { + "color": "orange", + "value": 5 + }, + { + "color": "red", + "value": 10 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 3, + "x": 12, + "y": 32 + }, + "id": 29, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.0.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "count(count(doc_pid{env_id=~\"$env_id\"})by(key,env_id)>1)by(env_id)", + "legendFormat": "{{env_id}}", + "range": true, + "refId": "A" + } + ], + "title": "Duplicated Documents", + "transparent": true, + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 1 + }, + { + "color": "orange", + "value": 5 + }, + { + "color": "red", + "value": 10 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 3, + "x": 15, + "y": 32 + }, + "id": 20, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.0.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum(irate(error_storage_space_low{env_id=~\"$env_id\"}[5m]))by(env_id)", + "legendFormat": "{{env_id}}", + "range": true, + "refId": "A" + } + ], + "title": "Storage SpaceLow", + "transparent": true, + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 1 + }, + { + "color": "#EF843C", + "value": 5 + }, + { + "color": "red", + "value": 10 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 3, + "x": 18, + "y": 32 + }, + "id": 21, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.0.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum(irate(error_storage_connection{env_id=~\"$env_id\"}[5m]))by(env_id)", + "legendFormat": "{{env_id}}", + "range": true, + "refId": "A" + } + ], + "title": "Storage Connection", + "transparent": true, + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 1 + }, + { + "color": "#EF843C", + "value": 5 + }, + { + "color": "red", + "value": 10 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 3, + "x": 21, + "y": 32 + }, + "id": 23, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.0.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum(irate(error_bad_argument{env_id=~\"$env_id\"}[5m]))by(env_id)", + "legendFormat": "{{env_id}}", + "range": true, + "refId": "A" + } + ], + "title": "Bad Argument", + "transparent": true, + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 1 + }, + { + "color": "#EF843C", + "value": 5 + }, + { + "color": "red", + "value": 10 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 3, + "x": 12, + "y": 36 + }, + "id": 24, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.0.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum(irate(error_unauthorized_request{env_id=~\"$env_id\"}[5m]))by(env_id)", + "legendFormat": "{{env_id}}", + "range": true, + "refId": "A" + } + ], + "title": "Unauthorized Request", + "transparent": true, + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 1 + }, + { + "color": "#EF843C", + "value": 5 + }, + { + "color": "red", + "value": 10 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 3, + "x": 15, + "y": 36 + }, + "id": 25, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.0.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum(irate(error_service_unavailable{env_id=~\"$env_id\"}[5m]))by(env_id)", + "legendFormat": "{{env_id}}", + "range": true, + "refId": "A" + } + ], + "title": "Service Unavailable", + "transparent": true, + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 1 + }, + { + "color": "#EF843C", + "value": 5 + }, + { + "color": "red", + "value": 10 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 3, + "x": 18, + "y": 36 + }, + "id": 26, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.0.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum(irate(error_parse_error{env_id=~\"$env_id\"}[5m]))by(env_id)", + "legendFormat": "{{env_id}}", + "range": true, + "refId": "A" + } + ], + "title": "Parse Error", + "transparent": true, + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 1 + }, + { + "color": "#EF843C", + "value": 5 + }, + { + "color": "red", + "value": 10 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 3, + "x": 21, + "y": 36 + }, + "id": 22, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.0.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum(irate(error_bad_request{env_id=~\"$env_id\"}[5m]))by(env_id)", + "legendFormat": "{{env_id}}", + "range": true, + "refId": "A" + } + ], + "title": "Bad Request", + "transparent": true, + "type": "stat" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 40 + }, + "id": 13, + "panels": [], + "title": "HA - Allocation", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "percent" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "max": 1, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 9, + "x": 0, + "y": 41 + }, + "id": 6, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom" + }, + "tooltip": { + "mode": "multi", + "sort": "asc" + } + }, + "pluginVersion": "9.0.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "count(kit_assigned_count{env_id=~\"$env_id\"}==0)", + "format": "time_series", + "hide": false, + "instant": false, + "legendFormat": "Viewers: 0", + "range": true, + "refId": "0" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "count(kit_assigned_count{env_id=~\"$env_id\"}==1)", + "format": "time_series", + "hide": false, + "instant": false, + "legendFormat": "Viewers: 1", + "range": true, + "refId": "1" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "count(kit_assigned_count{env_id=~\"$env_id\"}==2)", + "format": "time_series", + "hide": false, + "instant": false, + "legendFormat": "Viewers: 2", + "range": true, + "refId": "2" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "count(kit_assigned_count{env_id=~\"$env_id\"}==3)", + "format": "time_series", + "hide": false, + "instant": false, + "legendFormat": "Viewers: 3", + "range": true, + "refId": "3" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "count(kit_assigned_count{env_id=~\"$env_id\"}==4)", + "format": "time_series", + "hide": false, + "instant": false, + "legendFormat": "Viewers: 3", + "range": true, + "refId": "4" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "count(kit_assigned_count{env_id=~\"$env_id\"}==5)", + "format": "time_series", + "hide": false, + "instant": false, + "legendFormat": "Viewers: 5", + "range": true, + "refId": "5" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "count(5 + + + + + + + + + false + + + de_DE en_GB en_US es_ES fr_FR it nl pt_BR pt_PT ru + + + + + + + + + + + + + false + + + + + + + + + + + + true + + + 1 + + + 4 + 5 + false + 96 + 3600 + 30 + 300 + false + 0 + 8000 + 0 + 0 + 100 + 5 + 100 + 500 + 5000 + + 10000 + 60 + 300 + 3072 + 85 + 120 + + + + + true + 120 + 900 + + + + + + true + + warning + trace + notice + fatal + false + + -INFO-WARN + + + /var/log/coolwsd.log + never + timestamp + true + 10 days + 10 + true + false + + + false + 82589933 + + false + false + + + + + /var/log/coolwsd.trace.json + + + false + + + + + + + + false + + + + + + all + any + + + + 192\.168\.[0-9]{1,3}\.[0-9]{1,3} + ::ffff:192\.168\.[0-9]{1,3}\.[0-9]{1,3} + 127\.0\.0\.1 + ::ffff:127\.0\.0\.1 + ::1 + 172\.1[6789]\.[0-9]{1,3}\.[0-9]{1,3} + ::ffff:172\.1[6789]\.[0-9]{1,3}\.[0-9]{1,3} + 172\.2[0-9]\.[0-9]{1,3}\.[0-9]{1,3} + ::ffff:172\.2[0-9]\.[0-9]{1,3}\.[0-9]{1,3} + 172\.3[01]\.[0-9]{1,3}\.[0-9]{1,3} + ::ffff:172\.3[01]\.[0-9]{1,3}\.[0-9]{1,3} + 10\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3} + ::ffff:10\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3} + + + + + + + + + + + + false + + false + /etc/coolwsd/cert.pem + /etc/coolwsd/key.pem + /etc/coolwsd/ca-chain.cert.pem + + + 1000 + + + + + + + false + 31536000 + + + + + true + true + 1800 + false + 1 + false + false + + + + + + + + + + + + + + default + true + + + + + + 0 + + 900 + + + + https://amogha.labnetwork.in:443 + + + + + false + + + + + + + + + + true + false + + + + true + true + true + true + + + + + + + + + + + + + + + + + + + false + + + + + + + false + + + + log + + + + + + + + + + + + true + + + https://help.collaboraoffice.com/help.html? + + + true + + + diff --git a/templates/configmap_grafana_dashboards.yaml b/templates/configmap_grafana_dashboards.yaml new file mode 100644 index 0000000..2a975df --- /dev/null +++ b/templates/configmap_grafana_dashboards.yaml @@ -0,0 +1,16 @@ +{{- if .Values.grafana.dashboards.enabled }} +{{- range $path, $bytes := .Files.Glob "grafana_dashboards/*.json" }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "collabora-online.fullname" $ }}-grafana-dashboards-{{ base $path }} + labels: + {{- include "collabora-online.labels" $ | nindent 4 }} + {{- toYaml $.Values.grafana.dashboards.labels | nindent 4 }} + annotations: + {{- toYaml $.Values.grafana.dashboards.annotations | nindent 4 }} +data: + {{- ($.Files.Glob $path ).AsConfig | nindent 2 }} +{{- end }} +{{- end }} diff --git a/templates/deployment.yaml b/templates/deployment.yaml new file mode 100644 index 0000000..63235b1 --- /dev/null +++ b/templates/deployment.yaml @@ -0,0 +1,140 @@ +{{- if eq .Values.deployment.kind "Deployment" -}} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "collabora-online.fullname" . }} + labels: + {{- include "collabora-online.labels" . | nindent 4 }} +spec: + minReadySeconds: {{ .Values.deployment.minReadySeconds }} + {{- if not .Values.autoscaling.enabled }} + replicas: {{ .Values.replicaCount }} + {{- end }} + strategy: + type: {{ .Values.deployment.type }} + {{- if eq .Values.deployment.type "RollingUpdate"}} + rollingUpdate: + maxSurge: {{ .Values.deployment.maxSurge }} + maxUnavailable: {{ .Values.deployment.maxUnavailable }} + {{- end}} + selector: + matchLabels: + {{- include "collabora-online.selectorLabels" . | nindent 6 }} + type: main + template: + metadata: + annotations: + {{- with .Values.podAnnotations }} + {{- toYaml . | nindent 8 }} + {{- end }} + confighash: config-{{ .Values.collabora | toYaml | sha256sum | trunc 32 }} + cluster-autoscaler.kubernetes.io/safe-to-evict: "true" + labels: + {{- include "collabora-online.selectorLabels" . | nindent 8 }} + type: main + spec: + {{- with .Values.deployment.hostAliases }} + hostAliases: + {{- toYaml . | nindent 8 }} + {{- end }} + terminationGracePeriodSeconds: {{ .Values.terminationGracePeriodSeconds }} + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "collabora-online.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: {{ .Values.deployment.containerPort }} + protocol: TCP + {{- if .Values.probes.startup.enabled }} + startupProbe: + httpGet: + path: / + port: {{ .Values.deployment.containerPort }} + scheme: HTTP + failureThreshold: {{ .Values.probes.startup.failureThreshold }} + periodSeconds: {{ .Values.probes.startup.periodSeconds }} + {{- end }} + {{- if .Values.probes.liveness.enabled }} + livenessProbe: + httpGet: + path: / + port: {{ .Values.deployment.containerPort }} + scheme: HTTP + initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }} + periodSeconds: {{ .Values.probes.liveness.periodSeconds }} + timeoutSeconds: {{ .Values.probes.liveness.timeoutSeconds }} + successThreshold: {{ .Values.probes.liveness.successThreshold }} + failureThreshold: {{ .Values.probes.liveness.failureThreshold }} + {{- end }} + {{- if .Values.probes.readiness.enabled }} + readinessProbe: + httpGet: + path: / + port: {{ .Values.deployment.containerPort }} + scheme: HTTP + initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }} + periodSeconds: {{ .Values.probes.readiness.periodSeconds }} + timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }} + successThreshold: {{ .Values.probes.readiness.successThreshold }} + failureThreshold: {{ .Values.probes.readiness.failureThreshold }} + {{- end }} + + envFrom: + - configMapRef: + name: {{ include "collabora-online.fullname" . }} + env: + - name: username + valueFrom: + secretKeyRef: + {{- if (.Values.collabora.existingSecret).enabled }} + name: {{ .Values.collabora.existingSecret.secretName | quote }} + key: {{ .Values.collabora.existingSecret.usernameKey | quote }} + {{- else }} + name: {{ include "collabora-online.fullname" . }} + key: username + {{- end }} + - name: password + valueFrom: + secretKeyRef: + {{- if (.Values.collabora.existingSecret).enabled }} + name: {{ .Values.collabora.existingSecret.secretName | quote }} + key: {{ .Values.collabora.existingSecret.passwordKey | quote }} + {{- else }} + name: {{ include "collabora-online.fullname" . }} + key: password + {{- end }} + {{- with .Values.collabora.env }} + {{ toYaml . | nindent 12 }} + {{- end }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + volumeMounts: + - name: tmp + mountPath: /tmp + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + volumes: + - name: tmp + emptyDir: {} +{{- end }} diff --git a/templates/dynamicConfig/configmap.yaml b/templates/dynamicConfig/configmap.yaml new file mode 100644 index 0000000..7174124 --- /dev/null +++ b/templates/dynamicConfig/configmap.yaml @@ -0,0 +1,12 @@ +{{- if and .Values.dynamicConfig.enabled (not .Values.dynamicConfig.upload.enabled) (not .Values.dynamicConfig.existingConfigMap.enabled) -}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "collabora-online.fullname" . }}-dynconfig + labels: + {{- include "collabora-online.labels" . | nindent 4 }} +data: + config.json: | + {{- .Values.dynamicConfig.configuration | nindent 4 }} + +{{- end}} diff --git a/templates/dynamicConfig/deployment.yaml b/templates/dynamicConfig/deployment.yaml new file mode 100644 index 0000000..65ac13b --- /dev/null +++ b/templates/dynamicConfig/deployment.yaml @@ -0,0 +1,103 @@ +{{- if and .Values.dynamicConfig.enabled (not .Values.dynamicConfig.upload.enabled) -}} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "collabora-online.fullname" . }}-dynconfig + labels: + {{- include "collabora-online.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.dynamicConfig.replicaCount }} + selector: + matchLabels: + {{- include "collabora-online.selectorLabels" . | nindent 6 }} + type: dynconfig + template: + metadata: + {{- with .Values.dynamicConfig.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "collabora-online.selectorLabels" . | nindent 8 }} + type: dynconfig + # confighash: config-{{ .Values | toYaml | sha256sum | trunc 32 }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "collabora-online.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.dynamicConfig.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }}-dynconfig + securityContext: + {{- toYaml .Values.dynamicConfig.securityContext | nindent 12 }} + image: "{{ .Values.dynamicConfig.image.repository }}:{{ .Values.dynamicConfig.image.tag }}" + imagePullPolicy: {{ .Values.dynamicConfig.image.pullPolicy }} + ports: + - name: http + containerPort: {{ .Values.dynamicConfig.containerPort }} + protocol: TCP + {{- if .Values.probes.startup.enabled }} + startupProbe: + httpGet: + path: / + port: {{ .Values.dynamicConfig.containerPort }} + scheme: HTTP + failureThreshold: {{ .Values.dynamicConfig.probes.startup.failureThreshold }} + periodSeconds: {{ .Values.dynamicConfig.probes.startup.periodSeconds }} + {{- end }} + {{- if .Values.dynamicConfig.probes.liveness.enabled }} + livenessProbe: + httpGet: + path: / + port: {{ .Values.dynamicConfig.containerPort }} + scheme: HTTP + initialDelaySeconds: {{ .Values.dynamicConfig.probes.liveness.initialDelaySeconds }} + periodSeconds: {{ .Values.dynamicConfig.probes.liveness.periodSeconds }} + timeoutSeconds: {{ .Values.dynamicConfig.probes.liveness.timeoutSeconds }} + successThreshold: {{ .Values.dynamicConfig.probes.liveness.successThreshold }} + failureThreshold: {{ .Values.dynamicConfig.probes.liveness.failureThreshold }} + {{- end }} + {{- if .Values.dynamicConfig.probes.readiness.enabled }} + readinessProbe: + httpGet: + path: / + port: {{ .Values.dynamicConfig.containerPort }} + scheme: HTTP + initialDelaySeconds: {{ .Values.dynamicConfig.probes.readiness.initialDelaySeconds }} + periodSeconds: {{ .Values.dynamicConfig.probes.readiness.periodSeconds }} + timeoutSeconds: {{ .Values.dynamicConfig.probes.readiness.timeoutSeconds }} + successThreshold: {{ .Values.dynamicConfig.probes.readiness.successThreshold }} + failureThreshold: {{ .Values.dynamicConfig.probes.readiness.failureThreshold }} + {{- end }} + {{- with .Values.dynamicConfig.env }} + {{ toYaml . | nindent 12 }} + {{- end }} + resources: + {{- toYaml .Values.dynamicConfig.resources | nindent 12 }} + volumeMounts: + - name: config + mountPath: /usr/share/nginx/html/config + {{- with .Values.dynamicConfig.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.dynamicConfig.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.dynamicConfig.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + volumes: + - name: config + configMap: + {{- if .Values.dynamicConfig.existingConfigMap.enabled }} + name: {{ .Values.dynamicConfig.existingConfigMap.name }} + {{- else }} + name: {{ include "collabora-online.fullname" . }}-dynconfig + {{- end }} +{{- end }} diff --git a/templates/dynamicConfig/ingress.yaml b/templates/dynamicConfig/ingress.yaml new file mode 100644 index 0000000..6c75fc0 --- /dev/null +++ b/templates/dynamicConfig/ingress.yaml @@ -0,0 +1,43 @@ +{{- if and .Values.dynamicConfig.enabled .Values.dynamicConfig.ingress.enabled -}} +{{- $fullName := include "collabora-online.fullname" . -}} +{{- $svcPort := .Values.dynamicConfig.service.port -}} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ $fullName }}-dynconfig + labels: + {{- include "collabora-online.labels" . | nindent 4 }} + {{- with .Values.dynamicConfig.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if .Values.dynamicConfig.ingress.className }} + ingressClassName: {{ .Values.dynamicConfig.ingress.className }} + {{- end }} + {{- if .Values.dynamicConfig.ingress.tls }} + tls: + {{- range .Values.dynamicConfig.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- range .Values.dynamicConfig.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + pathType: {{ .pathType }} + backend: + service: + name: {{ $fullName }}-dynconfig + port: + number: {{ $svcPort }} + {{- end }} + {{- end }} +{{- end }} diff --git a/templates/dynamicConfig/ingress_upload.yaml b/templates/dynamicConfig/ingress_upload.yaml new file mode 100644 index 0000000..a22beea --- /dev/null +++ b/templates/dynamicConfig/ingress_upload.yaml @@ -0,0 +1,43 @@ +{{- if and .Values.dynamicConfig.enabled .Values.dynamicConfig.upload.enabled .Values.dynamicConfig.upload.ingress.enabled -}} +{{- $fullName := include "collabora-online.fullname" . -}} +{{- $svcPort := .Values.dynamicConfig.upload.service.port -}} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ $fullName }}-dynconfig-upload + labels: + {{- include "collabora-online.labels" . | nindent 4 }} + {{- with .Values.dynamicConfig.upload.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if .Values.dynamicConfig.upload.ingress.className }} + ingressClassName: {{ .Values.dynamicConfig.upload.ingress.className }} + {{- end }} + {{- if .Values.dynamicConfig.upload.ingress.tls }} + tls: + {{- range .Values.dynamicConfig.upload.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- range .Values.dynamicConfig.upload.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + pathType: {{ .pathType }} + backend: + service: + name: {{ $fullName }}-dynconfig + port: + number: {{ $svcPort }} + {{- end }} + {{- end }} +{{- end }} diff --git a/templates/dynamicConfig/service.yaml b/templates/dynamicConfig/service.yaml new file mode 100644 index 0000000..2050cd4 --- /dev/null +++ b/templates/dynamicConfig/service.yaml @@ -0,0 +1,25 @@ +{{- if .Values.dynamicConfig.enabled -}} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "collabora-online.fullname" . }}-dynconfig + labels: + {{- include "collabora-online.labels" . | nindent 4 }} + type: dynconfig +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.dynamicConfig.service.port }} + targetPort: http + protocol: TCP + name: http + {{- if .Values.dynamicConfig.upload.enabled }} + - port: {{ .Values.dynamicConfig.upload.service.port }} + targetPort: upload-http + protocol: TCP + name: upload-http + {{- end }} + selector: + {{- include "collabora-online.selectorLabels" . | nindent 4 }} + type: dynconfig +{{- end }} diff --git a/templates/dynamicConfig/statefulset.yaml b/templates/dynamicConfig/statefulset.yaml new file mode 100644 index 0000000..4b53abb --- /dev/null +++ b/templates/dynamicConfig/statefulset.yaml @@ -0,0 +1,118 @@ +{{- if and .Values.dynamicConfig.enabled .Values.dynamicConfig.upload.enabled -}} +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ include "collabora-online.fullname" . }}-dynconfig + labels: + {{- include "collabora-online.labels" . | nindent 4 }} +spec: + serviceName: {{ include "collabora-online.fullname" . }}-dynconfig + replicas: 1 + selector: + matchLabels: + {{- include "collabora-online.selectorLabels" . | nindent 6 }} + type: dynconfig + template: + metadata: + {{- with .Values.dynamicConfig.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "collabora-online.selectorLabels" . | nindent 8 }} + type: dynconfig + # confighash: config-{{ .Values | toYaml | sha256sum | trunc 32 }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "collabora-online.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.dynamicConfig.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }}-dynconfig + securityContext: + {{- toYaml .Values.dynamicConfig.securityContext | nindent 12 }} + image: "{{ .Values.dynamicConfig.image.repository }}:{{ .Values.dynamicConfig.image.tag }}" + imagePullPolicy: {{ .Values.dynamicConfig.image.pullPolicy }} + ports: + - name: http + containerPort: {{ .Values.dynamicConfig.containerPort }} + protocol: TCP + {{- if .Values.probes.startup.enabled }} + startupProbe: + httpGet: + path: / + port: {{ .Values.dynamicConfig.containerPort }} + scheme: HTTP + failureThreshold: {{ .Values.dynamicConfig.probes.startup.failureThreshold }} + periodSeconds: {{ .Values.dynamicConfig.probes.startup.periodSeconds }} + {{- end }} + {{- if .Values.dynamicConfig.probes.liveness.enabled }} + livenessProbe: + httpGet: + path: / + port: {{ .Values.dynamicConfig.containerPort }} + scheme: HTTP + initialDelaySeconds: {{ .Values.dynamicConfig.probes.liveness.initialDelaySeconds }} + periodSeconds: {{ .Values.dynamicConfig.probes.liveness.periodSeconds }} + timeoutSeconds: {{ .Values.dynamicConfig.probes.liveness.timeoutSeconds }} + successThreshold: {{ .Values.dynamicConfig.probes.liveness.successThreshold }} + failureThreshold: {{ .Values.dynamicConfig.probes.liveness.failureThreshold }} + {{- end }} + {{- if .Values.dynamicConfig.probes.readiness.enabled }} + readinessProbe: + httpGet: + path: / + port: {{ .Values.dynamicConfig.containerPort }} + scheme: HTTP + initialDelaySeconds: {{ .Values.dynamicConfig.probes.readiness.initialDelaySeconds }} + periodSeconds: {{ .Values.dynamicConfig.probes.readiness.periodSeconds }} + timeoutSeconds: {{ .Values.dynamicConfig.probes.readiness.timeoutSeconds }} + successThreshold: {{ .Values.dynamicConfig.probes.readiness.successThreshold }} + failureThreshold: {{ .Values.dynamicConfig.probes.readiness.failureThreshold }} + {{- end }} + {{- with .Values.dynamicConfig.env }} + {{ toYaml . | nindent 12 }} + {{- end }} + resources: + {{- toYaml .Values.dynamicConfig.resources | nindent 12 }} + volumeMounts: + - name: config + mountPath: /usr/share/nginx/html/config + - name: {{ .Chart.Name }}-dynconfig-upload + image: {{ .Values.dynamicConfig.upload.image.repository }}@sha256:{{ .Values.dynamicConfig.upload.image.digest }} + envFrom: + - secretRef: + name: {{ include "collabora-online.fullname" . }}-upload-env + ports: + - name: "upload-http" + containerPort: 3000 + volumeMounts: + - name: "config" + mountPath: "/config" + {{- with .Values.dynamicConfig.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.dynamicConfig.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.dynamicConfig.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + volumeClaimTemplates: + - metadata: + name: config + spec: + accessModes: [ "{{ .Values.dynamicConfig.upload.pvc.accessMode }}" ] + resources: + requests: + storage: {{ .Values.dynamicConfig.upload.pvc.size }} + {{- if .Values.dynamicConfig.upload.pvc.storageClassName }} + storageClassName: {{ .Values.dynamicConfig.upload.pvc.storageClassName }} + {{- end }} +{{- end }} diff --git a/templates/dynamicConfig/upload_secret.yaml b/templates/dynamicConfig/upload_secret.yaml new file mode 100644 index 0000000..d59d526 --- /dev/null +++ b/templates/dynamicConfig/upload_secret.yaml @@ -0,0 +1,11 @@ +{{- if .Values.dynamicConfig.upload.enabled }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "collabora-online.fullname" . }}-upload-env + labels: + {{- include "collabora-online.labels" . | nindent 4 }} +data: + KEY_{{ .Values.dynamicConfig.upload.key }}: {{ "/config/config.json" | b64enc }} + +{{- end }} diff --git a/templates/hpa.yaml b/templates/hpa.yaml new file mode 100644 index 0000000..4981bb4 --- /dev/null +++ b/templates/hpa.yaml @@ -0,0 +1,32 @@ +{{- if .Values.autoscaling.enabled }} +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "collabora-online.fullname" . }} + labels: + {{- include "collabora-online.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: {{ .Values.deployment.kind }} + name: {{ include "collabora-online.fullname" . }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + {{- with .Values.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ . }} + {{- end }} + {{- with .Values.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: {{ . }} + {{- end }} +{{- end }} diff --git a/templates/ingress.yaml b/templates/ingress.yaml new file mode 100644 index 0000000..2bba7fb --- /dev/null +++ b/templates/ingress.yaml @@ -0,0 +1,43 @@ +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "collabora-online.fullname" . -}} +{{- $svcPort := .Values.service.port -}} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + {{- include "collabora-online.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if .Values.ingress.className }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + {{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + pathType: {{ .pathType }} + backend: + service: + name: {{ $fullName }} + port: + number: {{ $svcPort }} + {{- end }} + {{- end }} +{{- end }} diff --git a/templates/prometheus-rules.yaml b/templates/prometheus-rules.yaml new file mode 100644 index 0000000..079b70f --- /dev/null +++ b/templates/prometheus-rules.yaml @@ -0,0 +1,163 @@ +{{- if and ( .Values.prometheus.rules.enabled ) ( .Capabilities.APIVersions.Has "monitoring.coreos.com/v1" ) }} +apiVersion: monitoring.coreos.com/v1 +kind: PrometheusRule +metadata: + name: {{ include "collabora-online.fullname" . }} + {{- with .Values.prometheus.rules.namespace }} + namespace: {{ . | quote }} + {{- end }} + labels: + {{- include "collabora-online.labels" . | nindent 4 }} + {{- toYaml .Values.prometheus.rules.additionalLabels | nindent 4 }} +spec: + groups: + {{- if .Values.prometheus.rules.defaults.enabled }} + - name: {{ template "collabora-online.name" . }}-Defaults + rules: + - alert: "Collabora NoProcess" + expr: 'coolwsd_count < 1' + for: "1m" + labels: + severity: "critical" + {{` + annotations: + summary: "no coolwsd process running: in namespace {{ $labels.namespace }}" + `}} + {{- range $key, $value := .Values.prometheus.rules.defaults.docs.pod }} + - alert: "Collabora Open Docs by Pod" + expr: 'kit_assigned_count > {{ $value }}' + for: "1m" + labels: + severity: "{{ $key }}" + {{` + annotations: + summary: "Too many Docs are open on a pod in namespace: {{ $labels.namespace }}" + `}} + {{- end }} + {{- range $key, $value := .Values.prometheus.rules.defaults.docs.sum }} + - alert: "Collabora Open Docs by Namespace" + expr: 'sum(kit_assigned_count) without (instance, pod) > {{ $value }}' + for: "1m" + labels: + severity: "{{ $key }}" + {{` + annotations: + summary: "Too many Docs are open on namespace" + `}} + {{- end }} + {{- range $key, $value := .Values.prometheus.rules.defaults.viewers.pod }} + - alert: "Collabora Viewers by Pod" + expr: 'document_active_views_active_count_total > {{ $value }}' + for: "1m" + labels: + severity: "{{ $key }}" + {{` + annotations: + summary: "Too many Viewers on a pod in namespace: {{ $labels.namespace }}" + `}} + {{- end }} + {{- range $key, $value := .Values.prometheus.rules.defaults.viewers.doc }} + - alert: "Collabora Viewers by Document" + expr: 'doc_views_active > {{ $value }}' + for: "1m" + labels: + severity: "{{ $key }}" + {{` + annotations: + summary: "Too many Viewers on a document in namespace: {{ $labels.namespace }}" + `}} + {{- end }} + {{- range $key, $value := .Values.prometheus.rules.defaults.viewers.sum }} + - alert: "Collabora Viewers by Namespace" + expr: 'sum(document_active_views_active_count_total) without (instance, pod) > {{ $value }}' + for: "1m" + labels: + severity: "{{ $key }}" + {{` + annotations: + summary: "Too many Viewers on namespace" + `}} + {{- end }} + - alert: "Collabora same Document open Multiple time" + expr: 'count(doc_pid) by (key) > 1' + labels: + severity: "warning" + {{` + annotations: + summary: "a key/document is open multiple times in namespace: {{ $labels.namespace }}" + `}} + - alert: "Collabora same Document open Multiple time" + expr: 'count(count(doc_pid)by(key)>1) > {{ .Values.prometheus.rules.defaults.docs.duplicated }}' + labels: + severity: "critical" + {{` + annotations: + summary: "too many document are open multiple times in namespace: {{ $labels.namespace }}" + `}} + - alert: "Collabora Error StorageSpaceLow" + expr: 'increase(error_storage_space_low[1m]) > 0' + labels: + severity: "warning" + {{` + annotations: + summary: "local storage space too low to operate in namespace: {{ $labels.namespace }}" + `}} + {{- range $key, $value := .Values.prometheus.rules.defaults.errorStorageConnections }} + - alert: "Collabora Error StorageConnection" + expr: 'increase(error_storage_connection[1m]) > {{ $value }}' + labels: + severity: "{{ $key }}" + {{` + annotations: + summary: "unable to connect to storage in namespace {{ $labels.namespace }} on pod {{ $labels.pod }}." + `}} + {{- end }} + + - alert: "Collabora Error BadRequest" + expr: 'increase(error_bad_request[1m]) > 0' + labels: + severity: "warning" + {{` + annotations: + summary: "we returned an HTTP bad request to a caller in namespace: {{ $labels.namespace }}" + `}} + - alert: "Collabora Error BadArgument" + expr: 'increase(error_bad_argument[1m]) > 0' + labels: + severity: "warning" + {{` + annotations: + summary: "we returned an HTTP bad argument to a caller in namespace: {{ $labels.namespace }}" + `}} + - alert: "Collabora Error UnauthorizedRequest" + expr: 'increase(error_unauthorized_request[1m]) > 0' + labels: + severity: "warning" + {{` + annotations: + summary: "an authorization exception usually on CheckFileInfo in namespace: {{ $labels.namespace }}" + `}} + {{- range $key, $value := .Values.prometheus.rules.defaults.errorServiceUnavailable }} + - alert: "Collabora Error ServiceUnavailable" + expr: 'increase(error_service_unavailable[1m]) > {{ $value }}' + labels: + severity: "{{ $key }}" + {{` + annotations: + summary: "internal error, service is unavailable in namespace {{ $labels.namespace }} on pod {{ $labels.pod }}." + `}} + {{- end }} + - alert: "Collabora Error ParseError" + expr: 'increase(error_parse_error[1m]) > 0' + labels: + severity: "warning" + {{` + annotations: + summary: "badly formed data provided for us to parse in namespace: {{ $labels.namespace }}" + `}} + {{- end }} + {{- if .Values.prometheus.rules.additionalRules }} + - name: {{ template "collabora-online.name" . }}-Additional + rules: {{- toYaml .Values.prometheus.rules.additionalRules | nindent 4 }} + {{- end }} +{{- end }} diff --git a/templates/secret.yaml b/templates/secret.yaml new file mode 100644 index 0000000..8508d6e --- /dev/null +++ b/templates/secret.yaml @@ -0,0 +1,12 @@ +{{- if not (.Values.collabora.existingSecret).enabled }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "collabora-online.fullname" . }} + labels: + {{- include "collabora-online.labels" . | nindent 4 }} +data: + username: {{ .Values.collabora.username | b64enc }} + password: {{ .Values.collabora.password | b64enc }} + +{{- end }} diff --git a/templates/service.yaml b/templates/service.yaml new file mode 100644 index 0000000..943183f --- /dev/null +++ b/templates/service.yaml @@ -0,0 +1,21 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "collabora-online.fullname" . }} + labels: + {{- include "collabora-online.labels" . | nindent 4 }} + type: main + {{- with .Values.service.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "collabora-online.selectorLabels" . | nindent 4 }} + type: main diff --git a/templates/serviceaccount.yaml b/templates/serviceaccount.yaml new file mode 100644 index 0000000..c8df905 --- /dev/null +++ b/templates/serviceaccount.yaml @@ -0,0 +1,12 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "collabora-online.serviceAccountName" . }} + labels: + {{- include "collabora-online.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/templates/servicemonitor.yaml b/templates/servicemonitor.yaml new file mode 100644 index 0000000..c2c98d4 --- /dev/null +++ b/templates/servicemonitor.yaml @@ -0,0 +1,36 @@ +{{- if .Values.prometheus.servicemonitor.enabled }} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ include "collabora-online.fullname" . }} + labels: + {{- include "collabora-online.labels" . | nindent 4 }} + {{- with .Values.prometheus.servicemonitor.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + endpoints: + - port: http + path: "/cool/getMetrics" + basicAuth: + username: + {{- if (.Values.collabora.existingSecret).enabled }} + name: {{ .Values.collabora.existingSecret.secretName | quote }} + key: {{ .Values.collabora.existingSecret.usernameKey | quote }} + {{- else }} + name: {{ include "collabora-online.fullname" . }} + key: username + {{- end }} + password: + {{- if (.Values.collabora.existingSecret).enabled }} + name: {{ .Values.collabora.existingSecret.secretName | quote }} + key: {{ .Values.collabora.existingSecret.passwordKey | quote }} + {{- else }} + name: {{ include "collabora-online.fullname" . }} + key: password + {{- end }} + selector: + matchLabels: + {{- include "collabora-online.selectorLabels" . | nindent 6 }} + type: main +{{- end }} diff --git a/templates/statefulset.yaml b/templates/statefulset.yaml new file mode 100644 index 0000000..8f15e7f --- /dev/null +++ b/templates/statefulset.yaml @@ -0,0 +1,133 @@ +{{- if eq .Values.deployment.kind "StatefulSet" -}} +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ include "collabora-online.fullname" . }} + labels: + {{- include "collabora-online.labels" . | nindent 4 }} +spec: + serviceName: {{ include "collabora-online.fullname" . }} + minReadySeconds: {{ .Values.deployment.minReadySeconds }} + {{- if not .Values.autoscaling.enabled }} + replicas: {{ .Values.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "collabora-online.selectorLabels" . | nindent 6 }} + type: main + template: + metadata: + annotations: + {{- with .Values.podAnnotations }} + {{- toYaml . | nindent 8 }} + {{- end }} + confighash: config-{{ .Values.collabora | toYaml | sha256sum | trunc 32 }} + cluster-autoscaler.kubernetes.io/safe-to-evict: "true" + labels: + {{- include "collabora-online.selectorLabels" . | nindent 8 }} + type: main + spec: + terminationGracePeriodSeconds: {{ .Values.terminationGracePeriodSeconds }} + {{- with .Values.deployment.hostAliases }} + hostAliases: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "collabora-online.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: {{ .Values.deployment.containerPort }} + protocol: TCP + {{- if .Values.probes.startup.enabled }} + startupProbe: + httpGet: + path: / + port: {{ .Values.deployment.containerPort }} + scheme: HTTP + failureThreshold: {{ .Values.probes.startup.failureThreshold }} + periodSeconds: {{ .Values.probes.startup.periodSeconds }} + {{- end }} + {{- if .Values.probes.liveness.enabled }} + livenessProbe: + httpGet: + path: / + port: {{ .Values.deployment.containerPort }} + scheme: HTTP + initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }} + periodSeconds: {{ .Values.probes.liveness.periodSeconds }} + timeoutSeconds: {{ .Values.probes.liveness.timeoutSeconds }} + successThreshold: {{ .Values.probes.liveness.successThreshold }} + failureThreshold: {{ .Values.probes.liveness.failureThreshold }} + {{- end }} + {{- if .Values.probes.readiness.enabled }} + readinessProbe: + httpGet: + path: / + port: {{ .Values.deployment.containerPort }} + scheme: HTTP + initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }} + periodSeconds: {{ .Values.probes.readiness.periodSeconds }} + timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }} + successThreshold: {{ .Values.probes.readiness.successThreshold }} + failureThreshold: {{ .Values.probes.readiness.failureThreshold }} + {{- end }} + + envFrom: + - configMapRef: + name: {{ include "collabora-online.fullname" . }} + env: + - name: username + valueFrom: + secretKeyRef: + {{- if (.Values.collabora.existingSecret).enabled }} + name: {{ .Values.collabora.existingSecret.secretName | quote }} + key: {{ .Values.collabora.existingSecret.usernameKey | quote }} + {{- else }} + name: {{ include "collabora-online.fullname" . }} + key: username + {{- end }} + - name: password + valueFrom: + secretKeyRef: + {{- if (.Values.collabora.existingSecret).enabled }} + name: {{ .Values.collabora.existingSecret.secretName | quote }} + key: {{ .Values.collabora.existingSecret.passwordKey | quote }} + {{- else }} + name: {{ include "collabora-online.fullname" . }} + key: password + {{- end }} + {{- with .Values.collabora.env }} + {{ toYaml . | nindent 12 }} + {{- end }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + volumeMounts: + - name: tmp + mountPath: /tmp + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + volumes: + - name: tmp + emptyDir: {} +{{- end }} diff --git a/values.yaml b/values.yaml new file mode 100644 index 0000000..f9a526f --- /dev/null +++ b/values.yaml @@ -0,0 +1,316 @@ +--- +# Default values for newchart. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +image: + repository: collabora/code + pullPolicy: IfNotPresent + # Overrides the image tag whose default is the chart appVersion. + tag: "" + +imagePullSecrets: [] +nameOverride: "" +fullnameOverride: "" +terminationGracePeriodSeconds: 60 + +serviceAccount: + # Specifies whether a service account should be created + create: false + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + +collabora: + # example to add aliasgroups + # - host: "://:" + # aliases: ["://:, ://:"] + aliasgroups: [] + + extra_params: --o:ssl.enable=false + + # External hostname:port of the server running coolwsd. + # If empty, it's derived from the request (please set it if this doesn't work). + # May be specified when behind a reverse-proxy or when the hostname is not reachable directly. + server_name: null + + existingSecret: + enabled: false + secretName: "" + usernameKey: "username" + passwordKey: "password" + password: examplepass + username: admin + env: [] + +prometheus: + servicemonitor: + enabled: false + labels: {} + rules: + enabled: false + additionalLabels: {} + defaults: + enabled: true + errorServiceUnavailable: + critical: 50 + warning: 2 + info: 0 + errorStorageConnections: + critical: 50 + warning: 2 + info: 0 + docs: + duplicated: 50 + pod: + critical: 10 + warning: 8 + info: 5 + sum: + critical: 500 + warning: 200 + info: 50 + viewers: + pod: + critical: 100 + warning: 80 + info: 60 + doc: + critical: 50 + warning: 40 + info: 30 + sum: + critical: 15000 + warning: 12000 + info: 5000 + additionalRules: [] + +grafana: + dashboards: + enabled: false + labels: + grafana_dashboard: "1" + annotations: {} + + +podAnnotations: {} + +podSecurityContext: {} +# fsGroup: 2000 + +securityContext: {} +# readOnlyRootFilesystem: false +# privileged: true +# capabilities: +# drop: +# - ALL +# readOnlyRootFilesystem: true +# runAsNonRoot: true +# runAsUser: 1000 + +service: + type: ClusterIP + port: 9980 + annotations: {} + +deployment: + # Use StatefulSet or Deployment + kind: Deployment + containerPort: 9980 + type: RollingUpdate + minReadySeconds: 0 + maxUnavailable: 0 + maxSurge: 1 + # info on how to use hostAliases: https://kubernetes.io/docs/tasks/network/customize-hosts-file-for-pods/ + # note: different from aliasgroups + hostAliases: null + +probes: + startup: + enabled: true + failureThreshold: 30 + periodSeconds: 3 + + readiness: + enabled: true + initialDelaySeconds: 0 + periodSeconds: 10 + timeoutSeconds: 30 + successThreshold: 1 + failureThreshold: 2 + liveness: + enabled: true + initialDelaySeconds: 0 + periodSeconds: 10 + timeoutSeconds: 30 + successThreshold: 1 + failureThreshold: 4 + +ingress: + enabled: false + className: "" + annotations: {} + # # nginx + # nginx.ingress.kubernetes.io/upstream-hash-by: "$arg_WOPISrc" + # # block admin urls from outside + # nginx.ingress.kubernetes.io/server-snippet: | + # location /cool/getMetrics { deny all; return 403; } + # location /cool/adminws/ { deny all; return 403; } + # location /browser/dist/admin/admin.html { deny all; return 403; } + # + # # HAProxy + # haproxy.org/timeout-tunnel: "3600s" + # haproxy.org/backend-config-snippet: | + # mode http + # balance leastconn + # stick-table type string len 2048 size 1k store conn_cur + # http-request set-var(txn.wopisrcconns) url_param(WOPISrc),table_conn_cur() + # http-request track-sc1 url_param(WOPISrc) + # stick match url_param(WOPISrc) if { var(txn.wopisrcconns) -m int gt 0 } + # stick store-request url_param(WOPISrc) + # + # # HAProxy - Community: https://haproxy-ingress.github.io/ + # haproxy-ingress.github.io/timeout-tunnel: 3600s + # haproxy-ingress.github.io/balance-algorithm: url_param WOPISrc check_post + # haproxy-ingress.github.io/config-backend: + # hash-type consistent + # # block admin urls from outside + # acl admin_url path_beg /cool/getMetrics + # acl admin_url path_beg /cool/adminws/ + # acl admin_url path_beg /browser/dist/admin/admin.html + # http-request deny if admin_url + # + hosts: + - host: chart-example.local + paths: + - path: / + pathType: ImplementationSpecific + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + +# We usually recommend not to specify default resources and to leave this as a conscious +# choice for the user. This also increases chances charts run on environments with little +# resources, such as Minikube. If you do want to specify resources, uncomment the following +# lines, adjust them as necessary, and remove the curly braces after 'resources:'. +resources: {} +# limits: +# cpu: 100m +# memory: 128Mi +# requests: +# cpu: 100m +# memory: 128Mi + +replicaCount: 1 + +autoscaling: + enabled: true + minReplicas: 2 + maxReplicas: 100 + targetCPUUtilizationPercentage: 70 + targetMemoryUtilizationPercentage: 50 + +dynamicConfig: + enabled: false + image: + repository: nginx + tag: 1.25 + pullPolicy: IfNotPresent + + replicaCount: 1 + podAnnotations: [] + podSecurityContext: {} + securityContext: {} + + # configVolumeType: configMap # configMap or pvc + + existingConfigMap: + enabled: false + name: "" + + upload: + enabled: false + image: + repository: "twostoryrobot/simple-file-upload" + digest: 547fc4360b31d8604b7a26202914e87cd13609cc938fd83f412c77eb44aa1cc4 + key: TESTKEY + pvc: + size: 1Gi + accessMode: "ReadWriteOnce" + # storageClassName: "" + service: + port: 8090 + ingress: + enabled: false + className: "" + annotations: {} + hosts: + - host: chart-example.local + paths: + - path: / + pathType: ImplementationSpecific + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + + containerPort: 80 + + probes: + startup: + enabled: true + failureThreshold: 30 + periodSeconds: 2 + readiness: + enabled: true + initialDelaySeconds: 0 + periodSeconds: 10 + timeoutSeconds: 30 + successThreshold: 1 + failureThreshold: 2 + liveness: + enabled: true + initialDelaySeconds: 0 + periodSeconds: 10 + timeoutSeconds: 30 + successThreshold: 1 + failureThreshold: 4 + + env: [] + resources: {} + nodeSelector: {} + tolerations: [] + affinity: {} + + service: + port: 8080 + + ingress: + enabled: false + className: "" + annotations: {} + hosts: + - host: chart-example.local + paths: + - path: / + pathType: ImplementationSpecific + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + + configuration: | + {} + +trusted_certs_install: + enabled: false + trusted_certs: [] + +nodeSelector: {} + +tolerations: [] + +affinity: {}